diff --git a/Cargo.lock b/Cargo.lock index 910b903..3171c01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1353,7 +1353,7 @@ dependencies = [ [[package]] name = "yuvutils-rs" -version = "0.5.1" +version = "0.5.2" dependencies = [ "num-traits", "rayon", diff --git a/Cargo.toml b/Cargo.toml index c9037f9..58b129c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ workspace = { members = ["app"] } [package] name = "yuvutils-rs" -version = "0.5.1" +version = "0.5.2" edition = "2021" description = "High performance utilities for YUV format handling and conversion." readme = "README.md" diff --git a/app/src/main.rs b/app/src/main.rs index b1e3158..081b425 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -31,11 +31,7 @@ use image::{ColorType, EncodableLayout, GenericImageView, ImageReader}; use std::fs::File; use std::io::Read; use std::time::Instant; -use yuvutils_rs::{ - rgb_to_sharp_yuv422, rgb_to_yuv_nv12_p16, yuv422_to_rgb, yuv_nv12_to_rgb_p16, - SharpYuvGammaTransfer, YuvBiPlanarImageMut, YuvBytesPacking, YuvChromaSubsampling, - YuvEndianness, YuvPlanarImageMut, YuvRange, YuvStandardMatrix, -}; +use yuvutils_rs::{ab30_to_rgb8, ar30_to_rgb8, ra30_to_rgb8, rgb_to_sharp_yuv422, rgb_to_yuv420_p16, rgb_to_yuv422_p16, rgb_to_yuv_nv12_p16, yuv422_p16_to_ab30, yuv422_p16_to_ar30, yuv422_p16_to_ra30, yuv422_to_rgb, yuv444_p16_to_ar30, yuv_nv12_to_rgb_p16, Rgb30ByteOrder, SharpYuvGammaTransfer, YuvBiPlanarImageMut, YuvBytesPacking, YuvChromaSubsampling, YuvEndianness, YuvPlanarImageMut, YuvRange, YuvStandardMatrix}; fn read_file_bytes(file_path: &str) -> Result, String> { // Open the file @@ -93,13 +89,13 @@ fn main() { ); let mut planar_image = - YuvPlanarImageMut::::alloc(width as u32, height as u32, YuvChromaSubsampling::Yuv422); + YuvPlanarImageMut::::alloc(width as u32, height as u32, YuvChromaSubsampling::Yuv422); let mut bytes_16: Vec = src_bytes.iter().map(|&x| (x as u16) << 2).collect(); let start_time = Instant::now(); - rgb_to_yuv_nv12_p16( - &mut bi_planar_image, + rgb_to_yuv422_p16( + &mut planar_image, &bytes_16, rgba_stride as u32, 10, @@ -218,10 +214,13 @@ fn main() { // bytes_16.resize(width as usize * height as usize * 4, 0u16); // rgba.resize(width as usize * height as usize * 4, 0u8); - yuv_nv12_to_rgb_p16( - &fixed_biplanar, - &mut bytes_16, - rgba_stride as u32, + let mut ar30 = vec![0u32; width as usize * height as usize]; + + yuv422_p16_to_ab30( + &fixed_planar, + &mut ar30, + width, + Rgb30ByteOrder::Host, 10, YuvRange::Limited, YuvStandardMatrix::Bt601, @@ -229,13 +228,27 @@ fn main() { YuvBytesPacking::LeastSignificantBytes, ) .unwrap(); + rgba.fill(0); + ab30_to_rgb8(&ar30, width, Rgb30ByteOrder::Host, &mut rgba, rgba_stride as u32, width, height).unwrap(); + + // yuv_nv12_to_rgb_p16( + // &fixed_biplanar, + // &mut bytes_16, + // rgba_stride as u32, + // 10, + // YuvRange::Limited, + // YuvStandardMatrix::Bt601, + // YuvEndianness::LittleEndian, + // YuvBytesPacking::LeastSignificantBytes, + // ) + // .unwrap(); println!("Backward time: {:?}", start_time.elapsed()); - rgba = bytes_16.iter().map(|&x| (x >> 2) as u8).collect(); + // rgba = bytes_16.iter().map(|&x| (x >> 2) as u8).collect(); image::save_buffer( - "converted_sharp15.png", + "converted_sharp15.jpg", rgba.as_bytes(), dimensions.0, dimensions.1, diff --git a/src/lib.rs b/src/lib.rs index bccea5f..799e335 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,6 +47,7 @@ mod internals; #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] mod neon; mod numerics; +mod rgb_ar30; mod rgb_to_nv_p16; mod rgb_to_y; mod rgb_to_ycgco; @@ -73,6 +74,7 @@ mod yuv_nv_p10_to_rgba; mod yuv_nv_p16_to_rgb; mod yuv_nv_to_rgba; mod yuv_p10_rgba; +mod yuv_p16_ar30; mod yuv_p16_rgba; mod yuv_p16_rgba16_alpha; mod yuv_p16_rgba_alpha; @@ -88,7 +90,8 @@ mod yuy2_to_yuv; mod yuy2_to_yuv_p16; pub use yuv_support::{ - YuvBytesPacking, YuvChromaSubsampling, YuvEndianness, YuvRange, YuvStandardMatrix, + Rgb30ByteOrder, YuvBytesPacking, YuvChromaSubsampling, YuvEndianness, YuvRange, + YuvStandardMatrix, }; pub use yuv_nv_p10_to_rgba::yuv_nv12_p10_to_bgr; @@ -436,3 +439,10 @@ pub use yuv_p16_rgba::*; pub use yuv_p16_rgba16_alpha::*; pub use yuv_p16_rgba_alpha::*; pub use yuv_p16_rgba_p16::*; + +pub use rgb_ar30::{ab30_to_rgb8, ar30_to_rgb8, ba30_to_rgb8, ra30_to_rgb8}; +pub use yuv_p16_ar30::{ + yuv420_p16_to_ab30, yuv420_p16_to_ar30, yuv420_p16_to_ra30, yuv422_p16_to_ab30, + yuv422_p16_to_ar30, yuv422_p16_to_ra30, yuv444_p16_to_ab30, yuv444_p16_to_ar30, + yuv444_p16_to_ra30, +}; diff --git a/src/rgb_ar30.rs b/src/rgb_ar30.rs new file mode 100644 index 0000000..5b8bace --- /dev/null +++ b/src/rgb_ar30.rs @@ -0,0 +1,219 @@ +/* + * Copyright (c) Radzivon Bartoshyk, 11/2024. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +use crate::yuv_error::check_rgba_destination; +use crate::yuv_support::{Rgb30, YuvSourceChannels}; +use crate::{Rgb30ByteOrder, YuvError}; + +fn ar30_to_rgb8_impl< + const AR30_LAYOUT: usize, + const AR30_BYTE_ORDER: usize, + const RGBA_LAYOUT: u8, +>( + ar30: &[u32], + ar30_stride: u32, + rgba: &mut [u8], + rgba_stride: u32, + width: u32, + height: u32, +) -> Result<(), YuvError> { + let rgba_layout: YuvSourceChannels = RGBA_LAYOUT.into(); + let ar30_layout: Rgb30 = AR30_LAYOUT.into(); + check_rgba_destination(ar30, ar30_stride, width, height, 1)?; + check_rgba_destination( + rgba, + rgba_stride, + width, + height, + rgba_layout.get_channels_count(), + )?; + + for (dst, src) in rgba + .chunks_exact_mut(rgba_stride as usize) + .zip(ar30.chunks_exact(ar30_stride as usize)) + { + for (dst, &src) in dst + .chunks_exact_mut(rgba_layout.get_channels_count()) + .zip(src.iter()) + { + let unpacked = ar30_layout.unpack::(src); + let r = unpacked.0 >> 2; + let g = unpacked.1 >> 2; + let b = unpacked.2 >> 2; + dst[rgba_layout.get_r_channel_offset()] = r as u8; + dst[rgba_layout.get_g_channel_offset()] = g as u8; + dst[rgba_layout.get_b_channel_offset()] = b as u8; + if rgba_layout.has_alpha() { + let expanded_a = + (unpacked.3 << 6) | (unpacked.3 << 4) | (unpacked.3 << 2) | unpacked.3; + dst[rgba_layout.get_a_channel_offset()] = expanded_a as u8; + } + } + } + Ok(()) +} + +/// Converts RGBA2101010 to RGB 8 bit depth +/// +/// # Arguments +/// +/// * `ar30`: Source AR30 data +/// * `ar30_stride`: Source AR30 stride +/// * `byte_order`: See [Rgb30ByteOrder] for more info +/// * `rgb`: Destination RGB data +/// * `rgb_stride`: Destination RGB stride +/// * `width`: Image width +/// * `height`: Image height +/// +pub fn ar30_to_rgb8( + ar30: &[u32], + ar30_stride: u32, + byte_order: Rgb30ByteOrder, + rgb: &mut [u8], + rgb_stride: u32, + width: u32, + height: u32, +) -> Result<(), YuvError> { + match byte_order { + Rgb30ByteOrder::Host => ar30_to_rgb8_impl::< + { Rgb30::Ar30 as usize }, + { Rgb30ByteOrder::Host as usize }, + { YuvSourceChannels::Rgb as u8 }, + >(ar30, ar30_stride, rgb, rgb_stride, width, height), + Rgb30ByteOrder::Network => ar30_to_rgb8_impl::< + { Rgb30::Ar30 as usize }, + { Rgb30ByteOrder::Network as usize }, + { YuvSourceChannels::Rgb as u8 }, + >(ar30, ar30_stride, rgb, rgb_stride, width, height), + } +} + +/// Converts BGBA2101010 to RGB 8 bit depth +/// +/// # Arguments +/// +/// * `ab30`: Source AR30 data +/// * `ab30_stride`: Source AR30 stride +/// * `byte_order`: See [Rgb30ByteOrder] for more info +/// * `rgb`: Destination RGB data +/// * `rgb_stride`: Destination RGB stride +/// * `width`: Image width +/// * `height`: Image height +/// +pub fn ab30_to_rgb8( + ab30: &[u32], + ab30_stride: u32, + byte_order: Rgb30ByteOrder, + rgb: &mut [u8], + rgb_stride: u32, + width: u32, + height: u32, +) -> Result<(), YuvError> { + match byte_order { + Rgb30ByteOrder::Host => ar30_to_rgb8_impl::< + { Rgb30::Ab30 as usize }, + { Rgb30ByteOrder::Host as usize }, + { YuvSourceChannels::Rgb as u8 }, + >(ab30, ab30_stride, rgb, rgb_stride, width, height), + Rgb30ByteOrder::Network => ar30_to_rgb8_impl::< + { Rgb30::Ab30 as usize }, + { Rgb30ByteOrder::Network as usize }, + { YuvSourceChannels::Rgb as u8 }, + >(ab30, ab30_stride, rgb, rgb_stride, width, height), + } +} + +/// Converts RGBA1010102 to RGB 8 bit depth +/// +/// # Arguments +/// +/// * `ar30`: Source RA30 data +/// * `ar30_stride`: Source RA30 stride +/// * `byte_order`: See [Rgb30ByteOrder] for more info +/// * `rgb`: Destination RGB data +/// * `rgb_stride`: Destination RGB stride +/// * `width`: Image width +/// * `height`: Image height +/// +pub fn ra30_to_rgb8( + ar30: &[u32], + ar30_stride: u32, + byte_order: Rgb30ByteOrder, + rgb: &mut [u8], + rgb_stride: u32, + width: u32, + height: u32, +) -> Result<(), YuvError> { + match byte_order { + Rgb30ByteOrder::Host => ar30_to_rgb8_impl::< + { Rgb30::Ra30 as usize }, + { Rgb30ByteOrder::Host as usize }, + { YuvSourceChannels::Rgb as u8 }, + >(ar30, ar30_stride, rgb, rgb_stride, width, height), + Rgb30ByteOrder::Network => ar30_to_rgb8_impl::< + { Rgb30::Ra30 as usize }, + { Rgb30ByteOrder::Network as usize }, + { YuvSourceChannels::Rgb as u8 }, + >(ar30, ar30_stride, rgb, rgb_stride, width, height), + } +} + +/// Converts BGRA1010102 to RGB 8 bit depth +/// +/// # Arguments +/// +/// * `ar30`: Source RA30 data +/// * `ar30_stride`: Source RA30 stride +/// * `byte_order`: See [Rgb30ByteOrder] for more info +/// * `rgb`: Destination RGB data +/// * `rgb_stride`: Destination RGB stride +/// * `width`: Image width +/// * `height`: Image height +/// +pub fn ba30_to_rgb8( + ar30: &[u32], + ar30_stride: u32, + byte_order: Rgb30ByteOrder, + rgb: &mut [u8], + rgb_stride: u32, + width: u32, + height: u32, +) -> Result<(), YuvError> { + match byte_order { + Rgb30ByteOrder::Host => ar30_to_rgb8_impl::< + { Rgb30::Ba30 as usize }, + { Rgb30ByteOrder::Host as usize }, + { YuvSourceChannels::Rgb as u8 }, + >(ar30, ar30_stride, rgb, rgb_stride, width, height), + Rgb30ByteOrder::Network => ar30_to_rgb8_impl::< + { Rgb30::Ba30 as usize }, + { Rgb30ByteOrder::Network as usize }, + { YuvSourceChannels::Rgb as u8 }, + >(ar30, ar30_stride, rgb, rgb_stride, width, height), + } +} diff --git a/src/yuv_p16_ar30.rs b/src/yuv_p16_ar30.rs new file mode 100644 index 0000000..1047a8b --- /dev/null +++ b/src/yuv_p16_ar30.rs @@ -0,0 +1,1056 @@ +/* + * Copyright (c) Radzivon Bartoshyk, 10/2024. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +use crate::numerics::{qrshr, to_ne}; +use crate::yuv_error::check_rgba_destination; +use crate::yuv_support::{ + get_inverse_transform, get_yuv_range, Rgb30, YuvBytesPacking, YuvChromaSubsampling, + YuvEndianness, YuvRange, YuvStandardMatrix, +}; +use crate::{Rgb30ByteOrder, YuvError, YuvPlanarImage}; +#[cfg(feature = "rayon")] +use rayon::iter::{IndexedParallelIterator, ParallelIterator}; +#[cfg(feature = "rayon")] +use rayon::prelude::{ParallelSlice, ParallelSliceMut}; + +fn yuv_p16_to_image_ar30< + const AR30_LAYOUT: usize, + const AR30_STORE: usize, + const SAMPLING: u8, + const ENDIANNESS: u8, + const BYTES_POSITION: u8, + const BIT_DEPTH: usize, +>( + image: &YuvPlanarImage, + rgba: &mut [u32], + rgba_stride: u32, + range: YuvRange, + matrix: YuvStandardMatrix, +) -> Result<(), YuvError> { + let ar30_layout: Rgb30 = AR30_LAYOUT.into(); + + let chroma_subsampling: YuvChromaSubsampling = SAMPLING.into(); + let range = get_yuv_range(BIT_DEPTH as u32, range); + + image.check_constraints(chroma_subsampling)?; + check_rgba_destination(rgba, rgba_stride, image.width, image.height, 1)?; + + let kr_kb = matrix.get_kr_kb(); + const AR30_DEPTH: usize = 10; + let max_range_p10 = ((1u32 << AR30_DEPTH as u32) - 1) as i32; + const PRECISION: i32 = 12; + let transform = get_inverse_transform( + max_range_p10 as u32, + range.range_y, + range.range_uv, + kr_kb.kr, + kr_kb.kb, + ); + let i_transform = transform.to_integers(PRECISION as u32); + let cr_coef = i_transform.cr_coef; + let cb_coef = i_transform.cb_coef; + let y_coef = i_transform.y_coef; + let g_coef_1 = i_transform.g_coeff_1; + let g_coef_2 = i_transform.g_coeff_2; + + let bias_y = range.bias_y as i32; + let bias_uv = range.bias_uv as i32; + + let msb_shift = (16 - BIT_DEPTH) as i32; + + let process_halved_chroma_row = |y_plane: &[u16], + u_plane: &[u16], + v_plane: &[u16], + rgba: &mut [u32]| { + for (((rgba, y_src), &u_src), &v_src) in rgba + .chunks_exact_mut(2) + .zip(y_plane.chunks_exact(2)) + .zip(u_plane.iter()) + .zip(v_plane.iter()) + { + let y_value0 = + (to_ne::(y_src[0], msb_shift) as i32 - bias_y) * y_coef; + let cb_value = to_ne::(u_src, msb_shift) as i32 - bias_uv; + let cr_value = to_ne::(v_src, msb_shift) as i32 - bias_uv; + + let r0 = qrshr::(y_value0 + cr_coef * cr_value); + let b0 = qrshr::(y_value0 + cb_coef * cb_value); + let g0 = qrshr::( + y_value0 - g_coef_1 * cr_value - g_coef_2 * cb_value, + ); + + let rgba_2 = &mut rgba[0..2]; + + let pixel0 = ar30_layout.pack::(r0, g0, b0); + rgba_2[0] = pixel0; + + let y_value1 = + (to_ne::(y_src[1], msb_shift) as i32 - bias_y) * y_coef; + + let r1 = qrshr::(y_value1 + cr_coef * cr_value); + let b1 = qrshr::(y_value1 + cb_coef * cb_value); + let g1 = + qrshr::(y_value1 - g_coef_1 * cr_value - g_coef_2 * cb_value); + + let pixel1 = ar30_layout.pack::(r1, g1, b1); + rgba_2[1] = pixel1; + } + + if image.width & 1 != 0 { + let y_value0 = (to_ne::(*y_plane.last().unwrap(), msb_shift) + as i32 + - bias_y) + * y_coef; + let cb_value = to_ne::(*u_plane.last().unwrap(), msb_shift) + as i32 + - bias_uv; + let cr_value = to_ne::(*v_plane.last().unwrap(), msb_shift) + as i32 + - bias_uv; + let rgba = rgba.chunks_exact_mut(2).last().unwrap(); + + let r0 = qrshr::(y_value0 + cr_coef * cr_value); + let b0 = qrshr::(y_value0 + cb_coef * cb_value); + let g0 = + qrshr::(y_value0 - g_coef_1 * cr_value - g_coef_2 * cb_value); + let pixel0 = ar30_layout.pack::(r0, g0, b0); + rgba[0] = pixel0; + } + }; + + if chroma_subsampling == YuvChromaSubsampling::Yuv444 { + let iter; + #[cfg(feature = "rayon")] + { + iter = rgba + .par_chunks_exact_mut(rgba_stride as usize) + .zip(image.y_plane.par_chunks_exact(image.y_stride as usize)) + .zip(image.u_plane.par_chunks_exact(image.u_stride as usize)) + .zip(image.v_plane.par_chunks_exact(image.v_stride as usize)); + } + #[cfg(not(feature = "rayon"))] + { + iter = rgba + .chunks_exact_mut(rgba_stride as usize) + .zip(image.y_plane.chunks_exact(image.y_stride as usize)) + .zip(image.u_plane.chunks_exact(image.u_stride as usize)) + .zip(image.v_plane.chunks_exact(image.v_stride as usize)); + } + iter.for_each(|(((rgba, y_plane), u_plane), v_plane)| { + for (((rgba, &y_src), &u_src), &v_src) in rgba + .iter_mut() + .zip(y_plane.iter()) + .zip(u_plane.iter()) + .zip(v_plane.iter()) + { + let y_value = (to_ne::(y_src, msb_shift) as i32 + - bias_y) + * y_coef; + let cb_value = + to_ne::(u_src, msb_shift) as i32 - bias_uv; + let cr_value = + to_ne::(v_src, msb_shift) as i32 - bias_uv; + + let r = qrshr::(y_value + cr_coef * cr_value); + let b = qrshr::(y_value + cb_coef * cb_value); + let g = qrshr::( + y_value - g_coef_1 * cr_value - g_coef_2 * cb_value, + ); + + let pixel0 = ar30_layout.pack::(r, g, b); + *rgba = pixel0; + } + }); + } else if chroma_subsampling == YuvChromaSubsampling::Yuv422 { + let iter; + #[cfg(feature = "rayon")] + { + iter = rgba + .par_chunks_exact_mut(rgba_stride as usize) + .zip(image.y_plane.par_chunks_exact(image.y_stride as usize)) + .zip(image.u_plane.par_chunks_exact(image.u_stride as usize)) + .zip(image.v_plane.par_chunks_exact(image.v_stride as usize)); + } + #[cfg(not(feature = "rayon"))] + { + iter = rgba + .chunks_exact_mut(rgba_stride as usize) + .zip(image.y_plane.chunks_exact(image.y_stride as usize)) + .zip(image.u_plane.chunks_exact(image.u_stride as usize)) + .zip(image.v_plane.chunks_exact(image.v_stride as usize)); + } + iter.for_each(|(((rgba, y_plane), u_plane), v_plane)| { + process_halved_chroma_row(y_plane, u_plane, v_plane, rgba); + }); + } else if chroma_subsampling == YuvChromaSubsampling::Yuv420 { + let iter; + #[cfg(feature = "rayon")] + { + iter = rgba + .par_chunks_exact_mut(rgba_stride as usize * 2) + .zip(image.y_plane.par_chunks_exact(image.y_stride as usize * 2)) + .zip(image.u_plane.par_chunks_exact(image.u_stride as usize)) + .zip(image.v_plane.par_chunks_exact(image.v_stride as usize)); + } + #[cfg(not(feature = "rayon"))] + { + iter = rgba + .chunks_exact_mut(rgba_stride as usize * 2) + .zip(image.y_plane.chunks_exact(image.y_stride as usize * 2)) + .zip(image.u_plane.chunks_exact(image.u_stride as usize)) + .zip(image.v_plane.chunks_exact(image.v_stride as usize)); + } + iter.for_each(|(((rgba, y_plane), u_plane), v_plane)| { + for (rgba, y_plane) in rgba + .chunks_exact_mut(rgba_stride as usize) + .zip(y_plane.chunks_exact(image.y_stride as usize)) + { + process_halved_chroma_row(y_plane, u_plane, v_plane, rgba); + } + }); + + if image.height & 1 != 0 { + let rgba = rgba.chunks_exact_mut(rgba_stride as usize).last().unwrap(); + let u_plane = image + .u_plane + .chunks_exact(image.u_stride as usize) + .last() + .unwrap(); + let v_plane = image + .v_plane + .chunks_exact(image.v_stride as usize) + .last() + .unwrap(); + let y_plane = image + .y_plane + .chunks_exact(image.y_stride as usize) + .last() + .unwrap(); + process_halved_chroma_row(y_plane, u_plane, v_plane, rgba); + } + } else { + unreachable!(); + } + + Ok(()) +} + +pub(crate) fn yuv_p16_to_image_ar30_impl< + const AR30_LAYOUT: usize, + const SAMPLING: u8, + const ENDIANNESS: u8, + const BYTES_POSITION: u8, +>( + planar_image: &YuvPlanarImage, + rgba: &mut [u32], + rgba_stride: u32, + store_type: Rgb30ByteOrder, + range: YuvRange, + matrix: YuvStandardMatrix, + bit_depth: usize, +) -> Result<(), YuvError> { + if bit_depth == 10 { + match store_type { + Rgb30ByteOrder::Host => yuv_p16_to_image_ar30::< + AR30_LAYOUT, + { Rgb30ByteOrder::Host as usize }, + SAMPLING, + ENDIANNESS, + BYTES_POSITION, + 10, + >(planar_image, rgba, rgba_stride, range, matrix), + Rgb30ByteOrder::Network => yuv_p16_to_image_ar30::< + AR30_LAYOUT, + { Rgb30ByteOrder::Network as usize }, + SAMPLING, + ENDIANNESS, + BYTES_POSITION, + 10, + >(planar_image, rgba, rgba_stride, range, matrix), + } + } else if bit_depth == 12 { + match store_type { + Rgb30ByteOrder::Host => yuv_p16_to_image_ar30::< + AR30_LAYOUT, + { Rgb30ByteOrder::Host as usize }, + SAMPLING, + ENDIANNESS, + BYTES_POSITION, + 12, + >(planar_image, rgba, rgba_stride, range, matrix), + Rgb30ByteOrder::Network => yuv_p16_to_image_ar30::< + AR30_LAYOUT, + { Rgb30ByteOrder::Network as usize }, + SAMPLING, + ENDIANNESS, + BYTES_POSITION, + 12, + >(planar_image, rgba, rgba_stride, range, matrix), + } + } else { + unimplemented!("Only 10 and 12 bit is implemented on YUV16 -> AR30") + } +} + +/// Convert YUV 420 planar format with 8+ bit pixel format to AR30 (RGBA2101010) format +/// +/// This function takes YUV 420 planar data with 8+ bit precision. +/// and converts it to AR30 image format +/// +/// # Arguments +/// +/// * `planar_image` - Source YUV planar image. +/// * `ar30` - A mutable slice to store the converted AR30 data. +/// * `ar30_stride` - The stride (components per row) for AR30 data. +/// * `byte_order` - see [Rgb30ByteOrder] for more info +/// * `range` - The YUV range (limited or full). +/// * `matrix` - The YUV standard matrix (BT.601 or BT.709 or BT.2020 or other). +/// * `endianness` - The endianness of stored bytes +/// * `bytes_packing` - position of significant bytes ( most significant or least significant ) if it in most significant it should be stated as per Apple *kCVPixelFormatType_422YpCbCr10BiPlanarFullRange/kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange* +/// * `bit_depth` - Bit depth of source YUV planes +/// +/// # Error +/// +/// This function panics if the lengths of the planes or the input RGBX1010102 data are not valid based +/// on the specified width, height, and strides, or if invalid YUV range or matrix is provided. +/// +pub fn yuv420_p16_to_ar30( + planar_image: &YuvPlanarImage, + ar30: &mut [u32], + ar30_stride: u32, + byte_order: Rgb30ByteOrder, + bit_depth: usize, + range: YuvRange, + matrix: YuvStandardMatrix, + endianness: YuvEndianness, + bytes_packing: YuvBytesPacking, +) -> Result<(), YuvError> { + let dispatcher = match endianness { + YuvEndianness::BigEndian => match bytes_packing { + YuvBytesPacking::MostSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ar30 as usize }, + { YuvChromaSubsampling::Yuv420 as u8 }, + { YuvEndianness::BigEndian as u8 }, + { YuvBytesPacking::MostSignificantBytes as u8 }, + > + } + YuvBytesPacking::LeastSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ar30 as usize }, + { YuvChromaSubsampling::Yuv420 as u8 }, + { YuvEndianness::BigEndian as u8 }, + { YuvBytesPacking::LeastSignificantBytes as u8 }, + > + } + }, + YuvEndianness::LittleEndian => match bytes_packing { + YuvBytesPacking::MostSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ar30 as usize }, + { YuvChromaSubsampling::Yuv420 as u8 }, + { YuvEndianness::LittleEndian as u8 }, + { YuvBytesPacking::MostSignificantBytes as u8 }, + > + } + YuvBytesPacking::LeastSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ar30 as usize }, + { YuvChromaSubsampling::Yuv420 as u8 }, + { YuvEndianness::LittleEndian as u8 }, + { YuvBytesPacking::LeastSignificantBytes as u8 }, + > + } + }, + }; + dispatcher( + planar_image, + ar30, + ar30_stride, + byte_order, + range, + matrix, + bit_depth, + ) +} + +/// Convert YUV 422 planar format with 8+ bit pixel format to AR30 (RGBA2101010) format +/// +/// This function takes YUV 422 planar data with 8+ bit precision. +/// and converts it to AR30 image format +/// +/// # Arguments +/// +/// * `planar_image` - Source YUV planar image. +/// * `ar30` - A mutable slice to store the converted AR30 data. +/// * `ar30_stride` - The stride (components per row) for AR30 data. +/// * `byte_order` - see [Rgb30ByteOrder] for more info +/// * `range` - The YUV range (limited or full). +/// * `matrix` - The YUV standard matrix (BT.601 or BT.709 or BT.2020 or other). +/// * `endianness` - The endianness of stored bytes +/// * `bytes_packing` - position of significant bytes ( most significant or least significant ) if it in most significant it should be stated as per Apple *kCVPixelFormatType_422YpCbCr10BiPlanarFullRange/kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange* +/// * `bit_depth` - Bit depth of source YUV planes +/// +/// # Error +/// +/// This function panics if the lengths of the planes or the input RGBX1010102 data are not valid based +/// on the specified width, height, and strides, or if invalid YUV range or matrix is provided. +/// +pub fn yuv422_p16_to_ar30( + planar_image: &YuvPlanarImage, + ar30: &mut [u32], + ar30_stride: u32, + byte_order: Rgb30ByteOrder, + bit_depth: usize, + range: YuvRange, + matrix: YuvStandardMatrix, + endianness: YuvEndianness, + bytes_packing: YuvBytesPacking, +) -> Result<(), YuvError> { + let dispatcher = match endianness { + YuvEndianness::BigEndian => match bytes_packing { + YuvBytesPacking::MostSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ar30 as usize }, + { YuvChromaSubsampling::Yuv422 as u8 }, + { YuvEndianness::BigEndian as u8 }, + { YuvBytesPacking::MostSignificantBytes as u8 }, + > + } + YuvBytesPacking::LeastSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ar30 as usize }, + { YuvChromaSubsampling::Yuv422 as u8 }, + { YuvEndianness::BigEndian as u8 }, + { YuvBytesPacking::LeastSignificantBytes as u8 }, + > + } + }, + YuvEndianness::LittleEndian => match bytes_packing { + YuvBytesPacking::MostSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ar30 as usize }, + { YuvChromaSubsampling::Yuv422 as u8 }, + { YuvEndianness::LittleEndian as u8 }, + { YuvBytesPacking::MostSignificantBytes as u8 }, + > + } + YuvBytesPacking::LeastSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ar30 as usize }, + { YuvChromaSubsampling::Yuv422 as u8 }, + { YuvEndianness::LittleEndian as u8 }, + { YuvBytesPacking::LeastSignificantBytes as u8 }, + > + } + }, + }; + dispatcher( + planar_image, + ar30, + ar30_stride, + byte_order, + range, + matrix, + bit_depth, + ) +} + +/// Convert YUV 444 planar format with 8+ bit pixel format to AR30 (RGBA2101010) format +/// +/// This function takes YUV 444 planar data with 8+ bit precision. +/// and converts it to AR30 image format +/// +/// # Arguments +/// +/// * `planar_image` - Source YUV 4:4:4 planar image. +/// * `ar30` - A mutable slice to store the converted AR30 data. +/// * `ar30_stride` - The stride (components per row) for AR30 data. +/// * `byte_order` - see [Rgb30ByteOrder] for more info +/// * `range` - The YUV range (limited or full). +/// * `matrix` - The YUV standard matrix (BT.601 or BT.709 or BT.2020 or other). +/// * `endianness` - The endianness of stored bytes +/// * `bytes_packing` - position of significant bytes ( most significant or least significant ) if it in most significant it should be stated as per Apple *kCVPixelFormatType_422YpCbCr10BiPlanarFullRange/kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange* +/// * `bit_depth` - Bit depth of source YUV planes +/// +/// # Error +/// +/// This function panics if the lengths of the planes or the input RGBX1010102 data are not valid based +/// on the specified width, height, and strides, or if invalid YUV range or matrix is provided. +/// +pub fn yuv444_p16_to_ar30( + planar_image: &YuvPlanarImage, + ar30: &mut [u32], + ar30_stride: u32, + byte_order: Rgb30ByteOrder, + bit_depth: usize, + range: YuvRange, + matrix: YuvStandardMatrix, + endianness: YuvEndianness, + bytes_packing: YuvBytesPacking, +) -> Result<(), YuvError> { + let dispatcher = match endianness { + YuvEndianness::BigEndian => match bytes_packing { + YuvBytesPacking::MostSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ar30 as usize }, + { YuvChromaSubsampling::Yuv444 as u8 }, + { YuvEndianness::BigEndian as u8 }, + { YuvBytesPacking::MostSignificantBytes as u8 }, + > + } + YuvBytesPacking::LeastSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ar30 as usize }, + { YuvChromaSubsampling::Yuv444 as u8 }, + { YuvEndianness::BigEndian as u8 }, + { YuvBytesPacking::LeastSignificantBytes as u8 }, + > + } + }, + YuvEndianness::LittleEndian => match bytes_packing { + YuvBytesPacking::MostSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ar30 as usize }, + { YuvChromaSubsampling::Yuv444 as u8 }, + { YuvEndianness::LittleEndian as u8 }, + { YuvBytesPacking::MostSignificantBytes as u8 }, + > + } + YuvBytesPacking::LeastSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ar30 as usize }, + { YuvChromaSubsampling::Yuv444 as u8 }, + { YuvEndianness::LittleEndian as u8 }, + { YuvBytesPacking::LeastSignificantBytes as u8 }, + > + } + }, + }; + dispatcher( + planar_image, + ar30, + ar30_stride, + byte_order, + range, + matrix, + bit_depth, + ) +} + +/// Convert YUV 420 planar format with 8+ bit pixel format to AB30 (BGRA2101010) format +/// +/// This function takes YUV 420 planar data with 8+ bit precision. +/// and converts it to AB30 image format +/// +/// # Arguments +/// +/// * `planar_image` - Source YUV planar image. +/// * `ab30` - A mutable slice to store the converted AB30 data. +/// * `ab30_stride` - The stride (components per row) for AB30 data. +/// * `byte_order` - see [Rgb30ByteOrder] for more info +/// * `range` - The YUV range (limited or full). +/// * `matrix` - The YUV standard matrix (BT.601 or BT.709 or BT.2020 or other). +/// * `endianness` - The endianness of stored bytes +/// * `bytes_packing` - position of significant bytes ( most significant or least significant ) if it in most significant it should be stated as per Apple *kCVPixelFormatType_422YpCbCr10BiPlanarFullRange/kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange* +/// * `bit_depth` - Bit depth of source YUV planes +/// +/// # Error +/// +/// This function panics if the lengths of the planes or the input BGRA2101010 data are not valid based +/// on the specified width, height, and strides, or if invalid YUV range or matrix is provided. +/// +pub fn yuv420_p16_to_ab30( + planar_image: &YuvPlanarImage, + ab30: &mut [u32], + ab30_stride: u32, + byte_order: Rgb30ByteOrder, + bit_depth: usize, + range: YuvRange, + matrix: YuvStandardMatrix, + endianness: YuvEndianness, + bytes_packing: YuvBytesPacking, +) -> Result<(), YuvError> { + let dispatcher = match endianness { + YuvEndianness::BigEndian => match bytes_packing { + YuvBytesPacking::MostSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ab30 as usize }, + { YuvChromaSubsampling::Yuv420 as u8 }, + { YuvEndianness::BigEndian as u8 }, + { YuvBytesPacking::MostSignificantBytes as u8 }, + > + } + YuvBytesPacking::LeastSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ab30 as usize }, + { YuvChromaSubsampling::Yuv420 as u8 }, + { YuvEndianness::BigEndian as u8 }, + { YuvBytesPacking::LeastSignificantBytes as u8 }, + > + } + }, + YuvEndianness::LittleEndian => match bytes_packing { + YuvBytesPacking::MostSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ab30 as usize }, + { YuvChromaSubsampling::Yuv420 as u8 }, + { YuvEndianness::LittleEndian as u8 }, + { YuvBytesPacking::MostSignificantBytes as u8 }, + > + } + YuvBytesPacking::LeastSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ab30 as usize }, + { YuvChromaSubsampling::Yuv420 as u8 }, + { YuvEndianness::LittleEndian as u8 }, + { YuvBytesPacking::LeastSignificantBytes as u8 }, + > + } + }, + }; + dispatcher( + planar_image, + ab30, + ab30_stride, + byte_order, + range, + matrix, + bit_depth, + ) +} + +/// Convert YUV 422 planar format with 8+ bit pixel format to AB30 (BGRA2101010) format +/// +/// This function takes YUV 422 planar data with 8+ bit precision. +/// and converts it to AB30 image format +/// +/// # Arguments +/// +/// * `planar_image` - Source YUV planar image. +/// * `ab30` - A mutable slice to store the converted AB30 data. +/// * `ab30_stride` - The stride (components per row) for AB30 data. +/// * `byte_order` - see [Rgb30ByteOrder] for more info +/// * `range` - The YUV range (limited or full). +/// * `matrix` - The YUV standard matrix (BT.601 or BT.709 or BT.2020 or other). +/// * `endianness` - The endianness of stored bytes +/// * `bytes_packing` - position of significant bytes ( most significant or least significant ) if it in most significant it should be stated as per Apple *kCVPixelFormatType_422YpCbCr10BiPlanarFullRange/kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange* +/// * `bit_depth` - Bit depth of source YUV planes +/// +/// # Error +/// +/// This function panics if the lengths of the planes or the input BGRA2101010 data are not valid based +/// on the specified width, height, and strides, or if invalid YUV range or matrix is provided. +/// +pub fn yuv422_p16_to_ab30( + planar_image: &YuvPlanarImage, + ab30: &mut [u32], + ab30_stride: u32, + byte_order: Rgb30ByteOrder, + bit_depth: usize, + range: YuvRange, + matrix: YuvStandardMatrix, + endianness: YuvEndianness, + bytes_packing: YuvBytesPacking, +) -> Result<(), YuvError> { + let dispatcher = match endianness { + YuvEndianness::BigEndian => match bytes_packing { + YuvBytesPacking::MostSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ab30 as usize }, + { YuvChromaSubsampling::Yuv422 as u8 }, + { YuvEndianness::BigEndian as u8 }, + { YuvBytesPacking::MostSignificantBytes as u8 }, + > + } + YuvBytesPacking::LeastSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ab30 as usize }, + { YuvChromaSubsampling::Yuv422 as u8 }, + { YuvEndianness::BigEndian as u8 }, + { YuvBytesPacking::LeastSignificantBytes as u8 }, + > + } + }, + YuvEndianness::LittleEndian => match bytes_packing { + YuvBytesPacking::MostSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ab30 as usize }, + { YuvChromaSubsampling::Yuv422 as u8 }, + { YuvEndianness::LittleEndian as u8 }, + { YuvBytesPacking::MostSignificantBytes as u8 }, + > + } + YuvBytesPacking::LeastSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ab30 as usize }, + { YuvChromaSubsampling::Yuv422 as u8 }, + { YuvEndianness::LittleEndian as u8 }, + { YuvBytesPacking::LeastSignificantBytes as u8 }, + > + } + }, + }; + dispatcher( + planar_image, + ab30, + ab30_stride, + byte_order, + range, + matrix, + bit_depth, + ) +} + +/// Convert YUV 444 planar format with 8+ bit pixel format to AB30 (BGRA2101010) format +/// +/// This function takes YUV 444 planar data with 8+ bit precision. +/// and converts it to AB30 image format +/// +/// # Arguments +/// +/// * `planar_image` - Source YUV planar image. +/// * `ab30` - A mutable slice to store the converted AB30 data. +/// * `ab30_stride` - The stride (components per row) for AB30 data. +/// * `byte_order` - see [Rgb30ByteOrder] for more info +/// * `range` - The YUV range (limited or full). +/// * `matrix` - The YUV standard matrix (BT.601 or BT.709 or BT.2020 or other). +/// * `endianness` - The endianness of stored bytes +/// * `bytes_packing` - position of significant bytes ( most significant or least significant ) if it in most significant it should be stated as per Apple *kCVPixelFormatType_422YpCbCr10BiPlanarFullRange/kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange* +/// * `bit_depth` - Bit depth of source YUV planes +/// +/// # Error +/// +/// This function panics if the lengths of the planes or the input BGRA2101010 data are not valid based +/// on the specified width, height, and strides, or if invalid YUV range or matrix is provided. +/// +pub fn yuv444_p16_to_ab30( + planar_image: &YuvPlanarImage, + ab30: &mut [u32], + ab30_stride: u32, + byte_order: Rgb30ByteOrder, + bit_depth: usize, + range: YuvRange, + matrix: YuvStandardMatrix, + endianness: YuvEndianness, + bytes_packing: YuvBytesPacking, +) -> Result<(), YuvError> { + let dispatcher = match endianness { + YuvEndianness::BigEndian => match bytes_packing { + YuvBytesPacking::MostSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ab30 as usize }, + { YuvChromaSubsampling::Yuv444 as u8 }, + { YuvEndianness::BigEndian as u8 }, + { YuvBytesPacking::MostSignificantBytes as u8 }, + > + } + YuvBytesPacking::LeastSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ab30 as usize }, + { YuvChromaSubsampling::Yuv444 as u8 }, + { YuvEndianness::BigEndian as u8 }, + { YuvBytesPacking::LeastSignificantBytes as u8 }, + > + } + }, + YuvEndianness::LittleEndian => match bytes_packing { + YuvBytesPacking::MostSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ab30 as usize }, + { YuvChromaSubsampling::Yuv444 as u8 }, + { YuvEndianness::LittleEndian as u8 }, + { YuvBytesPacking::MostSignificantBytes as u8 }, + > + } + YuvBytesPacking::LeastSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ab30 as usize }, + { YuvChromaSubsampling::Yuv444 as u8 }, + { YuvEndianness::LittleEndian as u8 }, + { YuvBytesPacking::LeastSignificantBytes as u8 }, + > + } + }, + }; + dispatcher( + planar_image, + ab30, + ab30_stride, + byte_order, + range, + matrix, + bit_depth, + ) +} + +/// Convert YUV 420 planar format with 8+ bit pixel format to AR30 (RGBA1010102) format +/// +/// This function takes YUV 420 planar data with 8+ bit precision. +/// and converts it to RA30 image format +/// +/// # Arguments +/// +/// * `planar_image` - Source YUV planar image. +/// * `ra30` - A mutable slice to store the converted RA30 data. +/// * `ra30_stride` - The stride (components per row) for RA30 data. +/// * `byte_order` - see [Rgb30ByteOrder] for more info +/// * `range` - The YUV range (limited or full). +/// * `matrix` - The YUV standard matrix (BT.601 or BT.709 or BT.2020 or other). +/// * `endianness` - The endianness of stored bytes +/// * `bytes_packing` - position of significant bytes ( most significant or least significant ) if it in most significant it should be stated as per Apple *kCVPixelFormatType_422YpCbCr10BiPlanarFullRange/kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange* +/// * `bit_depth` - Bit depth of source YUV planes +/// +/// # Error +/// +/// This function panics if the lengths of the planes or the input RGBA1010102 data are not valid based +/// on the specified width, height, and strides, or if invalid YUV range or matrix is provided. +/// +pub fn yuv420_p16_to_ra30( + planar_image: &YuvPlanarImage, + ra30: &mut [u32], + ra30_stride: u32, + byte_order: Rgb30ByteOrder, + bit_depth: usize, + range: YuvRange, + matrix: YuvStandardMatrix, + endianness: YuvEndianness, + bytes_packing: YuvBytesPacking, +) -> Result<(), YuvError> { + let dispatcher = match endianness { + YuvEndianness::BigEndian => match bytes_packing { + YuvBytesPacking::MostSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ra30 as usize }, + { YuvChromaSubsampling::Yuv420 as u8 }, + { YuvEndianness::BigEndian as u8 }, + { YuvBytesPacking::MostSignificantBytes as u8 }, + > + } + YuvBytesPacking::LeastSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ra30 as usize }, + { YuvChromaSubsampling::Yuv420 as u8 }, + { YuvEndianness::BigEndian as u8 }, + { YuvBytesPacking::LeastSignificantBytes as u8 }, + > + } + }, + YuvEndianness::LittleEndian => match bytes_packing { + YuvBytesPacking::MostSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ra30 as usize }, + { YuvChromaSubsampling::Yuv420 as u8 }, + { YuvEndianness::LittleEndian as u8 }, + { YuvBytesPacking::MostSignificantBytes as u8 }, + > + } + YuvBytesPacking::LeastSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ra30 as usize }, + { YuvChromaSubsampling::Yuv420 as u8 }, + { YuvEndianness::LittleEndian as u8 }, + { YuvBytesPacking::LeastSignificantBytes as u8 }, + > + } + }, + }; + dispatcher( + planar_image, + ra30, + ra30_stride, + byte_order, + range, + matrix, + bit_depth, + ) +} + +/// Convert YUV 422 planar format with 8+ bit pixel format to AR30 (RGBA1010102) format +/// +/// This function takes YUV 422 planar data with 8+ bit precision. +/// and converts it to RA30 image format +/// +/// # Arguments +/// +/// * `planar_image` - Source YUV planar image. +/// * `ra30` - A mutable slice to store the converted RA30 data. +/// * `ra30_stride` - The stride (components per row) for RA30 data. +/// * `byte_order` - see [Rgb30ByteOrder] for more info +/// * `range` - The YUV range (limited or full). +/// * `matrix` - The YUV standard matrix (BT.601 or BT.709 or BT.2020 or other). +/// * `endianness` - The endianness of stored bytes +/// * `bytes_packing` - position of significant bytes ( most significant or least significant ) if it in most significant it should be stated as per Apple *kCVPixelFormatType_422YpCbCr10BiPlanarFullRange/kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange* +/// * `bit_depth` - Bit depth of source YUV planes +/// +/// # Error +/// +/// This function panics if the lengths of the planes or the input RGBA1010102 data are not valid based +/// on the specified width, height, and strides, or if invalid YUV range or matrix is provided. +/// +pub fn yuv422_p16_to_ra30( + planar_image: &YuvPlanarImage, + ra30: &mut [u32], + ra30_stride: u32, + byte_order: Rgb30ByteOrder, + bit_depth: usize, + range: YuvRange, + matrix: YuvStandardMatrix, + endianness: YuvEndianness, + bytes_packing: YuvBytesPacking, +) -> Result<(), YuvError> { + let dispatcher = match endianness { + YuvEndianness::BigEndian => match bytes_packing { + YuvBytesPacking::MostSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ra30 as usize }, + { YuvChromaSubsampling::Yuv422 as u8 }, + { YuvEndianness::BigEndian as u8 }, + { YuvBytesPacking::MostSignificantBytes as u8 }, + > + } + YuvBytesPacking::LeastSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ra30 as usize }, + { YuvChromaSubsampling::Yuv422 as u8 }, + { YuvEndianness::BigEndian as u8 }, + { YuvBytesPacking::LeastSignificantBytes as u8 }, + > + } + }, + YuvEndianness::LittleEndian => match bytes_packing { + YuvBytesPacking::MostSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ra30 as usize }, + { YuvChromaSubsampling::Yuv422 as u8 }, + { YuvEndianness::LittleEndian as u8 }, + { YuvBytesPacking::MostSignificantBytes as u8 }, + > + } + YuvBytesPacking::LeastSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ra30 as usize }, + { YuvChromaSubsampling::Yuv422 as u8 }, + { YuvEndianness::LittleEndian as u8 }, + { YuvBytesPacking::LeastSignificantBytes as u8 }, + > + } + }, + }; + dispatcher( + planar_image, + ra30, + ra30_stride, + byte_order, + range, + matrix, + bit_depth, + ) +} + +/// Convert YUV 444 planar format with 8+ bit pixel format to AR30 (RGBA1010102) format +/// +/// This function takes YUV 444 planar data with 8+ bit precision. +/// and converts it to RA30 image format +/// +/// # Arguments +/// +/// * `planar_image` - Source YUV planar image. +/// * `ra30` - A mutable slice to store the converted RA30 data. +/// * `ra30_stride` - The stride (components per row) for RA30 data. +/// * `byte_order` - see [Rgb30ByteOrder] for more info +/// * `range` - The YUV range (limited or full). +/// * `matrix` - The YUV standard matrix (BT.601 or BT.709 or BT.2020 or other). +/// * `endianness` - The endianness of stored bytes +/// * `bytes_packing` - position of significant bytes ( most significant or least significant ) if it in most significant it should be stated as per Apple *kCVPixelFormatType_422YpCbCr10BiPlanarFullRange/kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange* +/// * `bit_depth` - Bit depth of source YUV planes +/// +/// # Error +/// +/// This function panics if the lengths of the planes or the input RGBA1010102 data are not valid based +/// on the specified width, height, and strides, or if invalid YUV range or matrix is provided. +/// +pub fn yuv444_p16_to_ra30( + planar_image: &YuvPlanarImage, + ra30: &mut [u32], + ra30_stride: u32, + byte_order: Rgb30ByteOrder, + bit_depth: usize, + range: YuvRange, + matrix: YuvStandardMatrix, + endianness: YuvEndianness, + bytes_packing: YuvBytesPacking, +) -> Result<(), YuvError> { + let dispatcher = match endianness { + YuvEndianness::BigEndian => match bytes_packing { + YuvBytesPacking::MostSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ra30 as usize }, + { YuvChromaSubsampling::Yuv444 as u8 }, + { YuvEndianness::BigEndian as u8 }, + { YuvBytesPacking::MostSignificantBytes as u8 }, + > + } + YuvBytesPacking::LeastSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ra30 as usize }, + { YuvChromaSubsampling::Yuv444 as u8 }, + { YuvEndianness::BigEndian as u8 }, + { YuvBytesPacking::LeastSignificantBytes as u8 }, + > + } + }, + YuvEndianness::LittleEndian => match bytes_packing { + YuvBytesPacking::MostSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ra30 as usize }, + { YuvChromaSubsampling::Yuv444 as u8 }, + { YuvEndianness::LittleEndian as u8 }, + { YuvBytesPacking::MostSignificantBytes as u8 }, + > + } + YuvBytesPacking::LeastSignificantBytes => { + yuv_p16_to_image_ar30_impl::< + { Rgb30::Ra30 as usize }, + { YuvChromaSubsampling::Yuv444 as u8 }, + { YuvEndianness::LittleEndian as u8 }, + { YuvBytesPacking::LeastSignificantBytes as u8 }, + > + } + }, + }; + dispatcher( + planar_image, + ra30, + ra30_stride, + byte_order, + range, + matrix, + bit_depth, + ) +} diff --git a/src/yuv_support.rs b/src/yuv_support.rs index 55e4cce..cb70fc6 100644 --- a/src/yuv_support.rs +++ b/src/yuv_support.rs @@ -500,3 +500,110 @@ impl Yuy2Description { } } } + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub(crate) enum Rgb30 { + Ar30 = 0, + Ab30 = 1, + Ra30 = 2, + Ba30 = 3, +} + +impl From for Rgb30 { + fn from(value: usize) -> Self { + match value { + 0 => Rgb30::Ar30, + 1 => Rgb30::Ab30, + 2 => Rgb30::Ra30, + 3 => Rgb30::Ba30, + _ => { + unimplemented!("Rgb30 is not implemented for value {}", value) + } + } + } +} + +/// Converts a value from host byte order to network byte order. +#[inline] +const fn htonl(hostlong: u32) -> u32 { + hostlong.to_be() +} + +/// Converts a value from network byte order to host byte order. +#[inline] +const fn ntohl(netlong: u32) -> u32 { + u32::from_be(netlong) +} + +impl Rgb30 { + #[inline(always)] + pub(crate) const fn pack(self, r: i32, g: i32, b: i32) -> u32 { + let value: u32 = match self { + Rgb30::Ar30 => (3 << 30 | (b << 20) | (g << 10) | r) as u32, + Rgb30::Ab30 => (3 << 30 | (r << 20) | (g << 10) | b) as u32, + Rgb30::Ra30 => ((r << 22) | (g << 12) | (b << 2) | 3) as u32, + Rgb30::Ba30 => ((b << 22) | (g << 12) | (r << 2) | 3) as u32, + }; + if STORE == 0 { + value + } else { + htonl(value) + } + } + + #[inline(always)] + pub(crate) const fn unpack(self, value: u32) -> (u32, u32, u32, u32) { + let pixel = if STORE == 0 { value } else { ntohl(value) }; + match self { + Rgb30::Ar30 => { + let r10 = pixel & 0x3ff; + let g10 = (pixel >> 10) & 0x3ff; + let b10 = (pixel >> 20) & 0x3ff; + let a10 = pixel >> 30; + (r10, g10, b10, a10) + } + Rgb30::Ab30 => { + let b10 = pixel & 0x3ff; + let g10 = (pixel >> 10) & 0x3ff; + let r10 = (pixel >> 20) & 0x3ff; + let a10 = pixel >> 30; + (r10, g10, b10, a10) + } + Rgb30::Ra30 => { + let a2 = pixel & 0x3; + let r10 = (pixel >> 22) & 0x3ff; + let g10 = (pixel >> 12) & 0x3ff; + let b10 = (pixel >> 2) & 0x3ff; + (r10, g10, b10, a2) + } + Rgb30::Ba30 => { + let a2 = pixel & 0x3; + let b10 = (pixel >> 22) & 0x3ff; + let g10 = (pixel >> 12) & 0x3ff; + let r10 = (pixel >> 2) & 0x3ff; + (r10, g10, b10, a2) + } + } + } +} + +#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +/// Defines storage byte order for RGBA1010102 or RGBA2101010 +/// +/// Some systems require to be bytes in network byte order instead of host. +pub enum Rgb30ByteOrder { + Host = 0, + Network = 1, +} + +impl From for Rgb30ByteOrder { + fn from(value: usize) -> Self { + match value { + 0 => Rgb30ByteOrder::Host, + 1 => Rgb30ByteOrder::Network, + _ => { + unimplemented!("Rgb30ByteOrder is not implemented for value {}", value) + } + } + } +}