defmodule ExYUV do @moduledoc File.read!("README.md") alias ExYUV.NIF @type width :: pos_integer() @type height :: pos_integer() @type y_plane :: binary() @type u_plane :: binary() @type v_plane :: binary() @type argb_16 :: binary() @type argb_32 :: binary() @type rgb_16 :: binary() @type rgb_24 :: binary() @type i420_data :: binary() | {y_plane(), u_plane(), v_plane()} | [binary()] @type yuv_planes :: {y_plane(), u_plane(), v_plane()} @type filter_mode :: :none | :linear | :bilinear | :box @filter_modes [:none, :linear, :bilinear, :box] @doc """ Converts from I420 to RGB24 format. The input should be the planes (Y, U and V), in case a binary is provided, it'll try to get the planes from it. The memory layout of the result is `<>` """ @spec i420_to_raw!(i420_data(), width(), height()) :: rgb_24() def i420_to_raw!(data, width, height) do convert_from_i420(data, width, height, :RAW) end @doc """ Convert I420 to BGR24. > ### Note {: .info} > > Even when the function name hints that the output format is RGB24, it's > actually BGR24. > We kept the same naming convention used in `libyuv`. """ @spec i420_to_rgb24!(i420_data(), width(), height()) :: rgb_24() def i420_to_rgb24!(data, width, height) do convert_from_i420(data, width, height, :RGB24) end @doc """ Convert I420 to ARGB. The memory layout of the result is `<>` """ @spec i420_to_argb!(i420_data(), width(), height()) :: argb_32() def i420_to_argb!(data, width, height) do convert_from_i420(data, width, height, :ARGB) end @doc """ Convert I420 to ABGR. The memory layout of the result is `<>` """ @spec i420_to_abgr!(i420_data(), width(), height()) :: argb_32() def i420_to_abgr!(data, width, height) do convert_from_i420(data, width, height, :ABGR) end @doc """ Convert I420 to RGBA. The memory layout of the result is `<>` """ @spec i420_to_rgba!(i420_data(), width(), height()) :: argb_32() def i420_to_rgba!(data, width, height) do convert_from_i420(data, width, height, :RGBA) end @doc """ Convert I420 to BGRA. The memory layout of the result is `<>` """ @spec i420_to_bgra!(i420_data(), width(), height()) :: argb_32() def i420_to_bgra!(data, width, height) do convert_from_i420(data, width, height, :BGRA) end @doc """ Convert I420 to RGB565. The memory layout of the result is `<>` """ @spec i420_to_rgb565!(i420_data(), width(), height()) :: rgb_16() def i420_to_rgb565!(data, width, height) do convert_from_i420(data, width, height, :RGB565) end @doc """ Convert I420 to ARGB1555. The memory layout of the result is `<>` """ @spec i420_to_argb1555!(i420_data(), width(), height()) :: argb_16() def i420_to_argb1555!(data, width, height) do convert_from_i420(data, width, height, :ARGB1555) end @doc """ Convert I420 to ARGB4444. The memory layout of the result is `<>` """ @spec i420_to_argb4444!(i420_data(), width(), height()) :: argb_16() def i420_to_argb4444!(data, width, height) do convert_from_i420(data, width, height, :ARGB4444) end @doc """ Convert I420 to AR30. The memory layout of the result is `<>` """ @spec i420_to_ar30!(i420_data(), width(), height()) :: argb_32() def i420_to_ar30!(data, width, height) do convert_from_i420(data, width, height, :AR30) end @doc """ Convert I420 to AB30. The memory layout of the result is `<>` """ @spec i420_to_ab30!(i420_data(), width(), height()) :: argb_32() def i420_to_ab30!(data, width, height) do convert_from_i420(data, width, height, :AB30) end @doc """ Converts from RGB24 to I420 format. """ @spec raw_to_i420!(rgb_24(), width(), height()) :: yuv_planes() def raw_to_i420!(data, width, height) do NIF.raw_to_i420(data, width, height) end defp convert_from_i420(data, width, height, out_format) do {y_plane, u_plane, v_plane} = yuv_planes(data, width, height) NIF.convert_from_i420(y_plane, u_plane, v_plane, width, height, out_format) end @doc """ Scale I420 image. The following filters are supported: `none`, `linear`, `bilinear`, and `box`. Check [libyuv](https://chromium.googlesource.com/libyuv/libyuv/+/refs/heads/main/docs/filtering.md) documentation for more details. """ @spec scale_i420!(i420_data(), width(), height(), width(), height()) :: yuv_planes() @spec scale_i420!(i420_data(), width(), height(), width(), height(), filter_mode()) :: yuv_planes() def scale_i420!(data, width, height, out_width, out_height, filter_mode \\ :none) when filter_mode in @filter_modes do {y_plane, u_plane, v_plane} = yuv_planes(data, width, height) NIF.scale_i420(y_plane, u_plane, v_plane, width, height, out_width, out_height, filter_mode) end @doc """ Scale ARGB/ABGR/RGBA/BGRA image. The following filters are supported: `none`, `linear`, `bilinear`, and `box`. Check [libyuv](https://chromium.googlesource.com/libyuv/libyuv/+/refs/heads/main/docs/filtering.md) documentation for more details. """ @spec scale_argb!(argb_32(), width(), height(), width(), height()) :: argb_32() @spec scale_argb!(argb_32(), width(), height(), width(), height(), filter_mode()) :: argb_32() def scale_argb!(data, width, height, out_width, out_height, filter_mode \\ :none) when filter_mode in @filter_modes do NIF.scale_argb(data, width, height, out_width, out_height, filter_mode, :ARGB) end @doc """ Scale RAW/RGB24 image. The following filters are supported: `none`, `linear`, `bilinear`, and `box`. Check [libyuv](https://chromium.googlesource.com/libyuv/libyuv/+/refs/heads/main/docs/filtering.md) documentation for more details. """ @spec scale_rgb!(rgb_24(), width(), height(), width(), height()) :: rgb_24() @spec scale_rgb!(rgb_24(), width(), height(), width(), height(), filter_mode()) :: rgb_24() def scale_rgb!(data, width, height, out_width, out_height, filter_mode \\ :none) when filter_mode in @filter_modes do NIF.scale_argb(data, width, height, out_width, out_height, filter_mode, :RGB24) end defp yuv_planes(data, width, height) do case data do [y, u, v] -> {y, u, v} {y, u, v} -> {y, u, v} data -> planes_from_binary(data, width, height, :I420) end end defp planes_from_binary(data, width, height, :I420) when is_binary(data) do y_plane_size = width * height u_width = div(width, 2) + rem(width, 2) u_height = div(height, 2) + rem(height, 2) u_plane_size = u_height * u_width <> = data {y, u, v} end end