yuvutils_rs/
yuv_support.rs

1/*
2 * Copyright (c) Radzivon Bartoshyk, 10/2024. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 *
7 * 1.  Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * 2.  Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * 3.  Neither the name of the copyright holder nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29use crate::built_coefficients::{get_built_forward_transform, get_built_inverse_transform};
30use num_traits::AsPrimitive;
31use std::fmt::{Display, Formatter};
32
33#[derive(Debug, Copy, Clone)]
34pub struct CbCrInverseTransform<T> {
35    pub y_coef: T,
36    pub cr_coef: T,
37    pub cb_coef: T,
38    pub g_coeff_1: T,
39    pub g_coeff_2: T,
40}
41
42impl<T> CbCrInverseTransform<T> {
43    pub fn new(
44        y_coef: T,
45        cr_coef: T,
46        cb_coef: T,
47        g_coeff_1: T,
48        g_coeff_2: T,
49    ) -> CbCrInverseTransform<T> {
50        CbCrInverseTransform {
51            y_coef,
52            cr_coef,
53            cb_coef,
54            g_coeff_1,
55            g_coeff_2,
56        }
57    }
58}
59
60impl CbCrInverseTransform<f32> {
61    /// Integral transformation adds an error not less than 1%
62    pub fn to_integers(self, precision: u32) -> CbCrInverseTransform<i32> {
63        let precision_scale: i32 = 1i32 << (precision as i32);
64        let cr_coef = (self.cr_coef * precision_scale as f32).round() as i32;
65        let cb_coef = (self.cb_coef * precision_scale as f32).round() as i32;
66        let y_coef = (self.y_coef * precision_scale as f32).round() as i32;
67        let g_coef_1 = (self.g_coeff_1 * precision_scale as f32).round() as i32;
68        let g_coef_2 = (self.g_coeff_2 * precision_scale as f32).round() as i32;
69        CbCrInverseTransform::<i32> {
70            y_coef,
71            cr_coef,
72            cb_coef,
73            g_coeff_1: g_coef_1,
74            g_coeff_2: g_coef_2,
75        }
76    }
77}
78
79impl<V> CbCrInverseTransform<V> {
80    #[inline]
81    pub(crate) fn cast<T: Copy + 'static>(&self) -> CbCrInverseTransform<T>
82    where
83        V: AsPrimitive<T>,
84    {
85        CbCrInverseTransform {
86            y_coef: self.y_coef.as_(),
87            cb_coef: self.cb_coef.as_(),
88            cr_coef: self.cr_coef.as_(),
89            g_coeff_1: self.g_coeff_1.as_(),
90            g_coeff_2: self.g_coeff_2.as_(),
91        }
92    }
93}
94
95/// Transformation RGB to YUV with coefficients as specified in [ITU-R](https://www.itu.int/rec/T-REC-H.273/en)
96pub fn get_inverse_transform(
97    range_bgra: u32,
98    range_y: u32,
99    range_uv: u32,
100    kr: f32,
101    kb: f32,
102) -> CbCrInverseTransform<f32> {
103    let range_uv = range_bgra as f32 / range_uv as f32;
104    let y_coef = range_bgra as f32 / range_y as f32;
105    let cr_coeff = (2f32 * (1f32 - kr)) * range_uv;
106    let cb_coeff = (2f32 * (1f32 - kb)) * range_uv;
107    let kg = 1.0f32 - kr - kb;
108    assert_ne!(kg, 0., "1.0f - kr - kg must not be 0");
109    let g_coeff_1 = (2f32 * ((1f32 - kr) * kr / kg)) * range_uv;
110    let g_coeff_2 = (2f32 * ((1f32 - kb) * kb / kg)) * range_uv;
111    CbCrInverseTransform::new(y_coef, cr_coeff, cb_coeff, g_coeff_1, g_coeff_2)
112}
113
114#[repr(C)]
115#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
116pub struct CbCrForwardTransform<T> {
117    pub yr: T,
118    pub yg: T,
119    pub yb: T,
120    pub cb_r: T,
121    pub cb_g: T,
122    pub cb_b: T,
123    pub cr_r: T,
124    pub cr_g: T,
125    pub cr_b: T,
126}
127
128impl CbCrForwardTransform<i32> {
129    #[inline]
130    pub(crate) const fn _interleaved_yr_yg(&self) -> i32 {
131        let w0_as_u16 = self.yg.to_ne_bytes();
132        let w1_as_u16 = self.yr.to_ne_bytes();
133        i32::from_ne_bytes([w1_as_u16[0], w1_as_u16[1], w0_as_u16[0], w0_as_u16[1]])
134    }
135
136    #[inline]
137    pub(crate) const fn _interleaved_cbr_cbg(&self) -> i32 {
138        let w0_as_u16 = self.cb_g.to_ne_bytes();
139        let w1_as_u16 = self.cb_r.to_ne_bytes();
140        i32::from_ne_bytes([w1_as_u16[0], w1_as_u16[1], w0_as_u16[0], w0_as_u16[1]])
141    }
142
143    #[inline]
144    pub(crate) const fn _interleaved_crr_crg(&self) -> i32 {
145        let w0_as_u16 = self.cr_g as u16;
146        let w1_as_u16 = self.cr_r as u16;
147        (((w0_as_u16 as u32) << 16) | (w1_as_u16 as u32)) as i32
148    }
149}
150
151pub trait ToIntegerTransform {
152    fn to_integers(&self, precision: u32) -> CbCrForwardTransform<i32>;
153}
154
155impl ToIntegerTransform for CbCrForwardTransform<f32> {
156    fn to_integers(&self, precision: u32) -> CbCrForwardTransform<i32> {
157        let scale = (1 << precision) as f32;
158        CbCrForwardTransform::<i32> {
159            yr: (self.yr * scale).round() as i32,
160            yg: (self.yg * scale).round() as i32,
161            yb: (self.yb * scale).round() as i32,
162            cb_r: (self.cb_r * scale).round() as i32,
163            cb_g: (self.cb_g * scale).round() as i32,
164            cb_b: (self.cb_b * scale).round() as i32,
165            cr_r: (self.cr_r * scale).round() as i32,
166            cr_g: (self.cr_g * scale).round() as i32,
167            cr_b: (self.cr_b * scale).round() as i32,
168        }
169    }
170}
171
172impl<V> CbCrForwardTransform<V> {
173    pub(crate) fn cast<T: Copy + 'static>(&self) -> CbCrForwardTransform<T>
174    where
175        V: AsPrimitive<T>,
176    {
177        CbCrForwardTransform {
178            yr: self.yr.as_(),
179            yg: self.yg.as_(),
180            yb: self.yb.as_(),
181            cb_r: self.cb_r.as_(),
182            cb_g: self.cb_g.as_(),
183            cb_b: self.cb_b.as_(),
184            cr_r: self.cr_r.as_(),
185            cr_g: self.cr_g.as_(),
186            cr_b: self.cr_b.as_(),
187        }
188    }
189}
190
191/// Transformation YUV to RGB with coefficients as specified in [ITU-R](https://www.itu.int/rec/T-REC-H.273/en)
192pub fn get_forward_transform(
193    range_rgba: u32,
194    range_y: u32,
195    range_uv: u32,
196    kr: f32,
197    kb: f32,
198) -> CbCrForwardTransform<f32> {
199    let kg = 1.0f32 - kr - kb;
200
201    let yr = kr * range_y as f32 / range_rgba as f32;
202    let yg = kg * range_y as f32 / range_rgba as f32;
203    let yb = kb * range_y as f32 / range_rgba as f32;
204
205    let cb_r = -0.5f32 * kr / (1f32 - kb) * range_uv as f32 / range_rgba as f32;
206    let cb_g = -0.5f32 * kg / (1f32 - kb) * range_uv as f32 / range_rgba as f32;
207    let cb_b = 0.5f32 * range_uv as f32 / range_rgba as f32;
208
209    let cr_r = 0.5f32 * range_uv as f32 / range_rgba as f32;
210    let cr_g = -0.5f32 * kg / (1f32 - kr) * range_uv as f32 / range_rgba as f32;
211    let cr_b = -0.5f32 * kb / (1f32 - kr) * range_uv as f32 / range_rgba as f32;
212    CbCrForwardTransform {
213        yr,
214        yg,
215        yb,
216        cb_r,
217        cb_g,
218        cb_b,
219        cr_r,
220        cr_g,
221        cr_b,
222    }
223}
224
225#[repr(C)]
226#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
227/// Declares YUV range TV (limited) or Full
228pub enum YuvRange {
229    /// Limited range Y ∈ [16 << (depth - 8), 16 << (depth - 8) + 224 << (depth - 8)], UV ∈ [-1 << (depth - 1), -1 << (depth - 1) + 1 << (depth - 1)]
230    Limited,
231    /// Full range Y ∈ [0, 2^bit_depth - 1], UV ∈ [-1 << (depth - 1), -1 << (depth - 1) + 2^bit_depth - 1]
232    Full,
233}
234
235/// Holds YUV bias values
236#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
237pub struct YuvChromaRange {
238    pub bias_y: u32,
239    pub bias_uv: u32,
240    pub range_y: u32,
241    pub range_uv: u32,
242    pub range: YuvRange,
243}
244
245/// Computes YUV ranges for given bit depth
246pub const fn get_yuv_range(depth: u32, range: YuvRange) -> YuvChromaRange {
247    match range {
248        YuvRange::Limited => YuvChromaRange {
249            bias_y: 16 << (depth - 8),
250            bias_uv: 1 << (depth - 1),
251            range_y: 219 << (depth - 8),
252            range_uv: 224 << (depth - 8),
253            range,
254        },
255        YuvRange::Full => YuvChromaRange {
256            bias_y: 0,
257            bias_uv: 1 << (depth - 1),
258            range_uv: (1 << depth) - 1,
259            range_y: (1 << depth) - 1,
260            range,
261        },
262    }
263}
264
265#[repr(C)]
266#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
267/// Declares standard prebuilt YUV conversion matrices, check [ITU-R](https://www.itu.int/rec/T-REC-H.273/en) information for more info
268/// JPEG YUV Matrix corresponds Bt.601 + Full Range
269pub enum YuvStandardMatrix {
270    /// If you want to encode/decode JPEG YUV use Bt.601 + Full Range
271    Bt601,
272    Bt709,
273    Bt2020,
274    Smpte240,
275    Bt470_6,
276    /// Custom parameters first goes for kr, second for kb.
277    /// Methods will *panic* if 1.0f32 - kr - kb == 0
278    Custom(f32, f32),
279}
280
281#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
282pub struct YuvBias {
283    pub kr: f32,
284    pub kb: f32,
285}
286
287impl YuvStandardMatrix {
288    pub const fn get_kr_kb(self) -> YuvBias {
289        match self {
290            YuvStandardMatrix::Bt601 => YuvBias {
291                kr: 0.299f32,
292                kb: 0.114f32,
293            },
294            YuvStandardMatrix::Bt709 => YuvBias {
295                kr: 0.2126f32,
296                kb: 0.0722f32,
297            },
298            YuvStandardMatrix::Bt2020 => YuvBias {
299                kr: 0.2627f32,
300                kb: 0.0593f32,
301            },
302            YuvStandardMatrix::Smpte240 => YuvBias {
303                kr: 0.087f32,
304                kb: 0.212f32,
305            },
306            YuvStandardMatrix::Bt470_6 => YuvBias {
307                kr: 0.2220f32,
308                kb: 0.0713f32,
309            },
310            YuvStandardMatrix::Custom(kr, kb) => YuvBias { kr, kb },
311        }
312    }
313}
314
315#[repr(u8)]
316#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
317pub enum YuvNVOrder {
318    UV = 0,
319    VU = 1,
320}
321
322impl YuvNVOrder {
323    #[inline]
324    pub const fn get_u_position(&self) -> usize {
325        match self {
326            YuvNVOrder::UV => 0,
327            YuvNVOrder::VU => 1,
328        }
329    }
330    #[inline]
331    pub const fn get_v_position(&self) -> usize {
332        match self {
333            YuvNVOrder::UV => 1,
334            YuvNVOrder::VU => 0,
335        }
336    }
337}
338
339impl From<u8> for YuvNVOrder {
340    #[inline(always)]
341    fn from(value: u8) -> Self {
342        match value {
343            0 => YuvNVOrder::UV,
344            1 => YuvNVOrder::VU,
345            _ => {
346                unimplemented!("Unknown value")
347            }
348        }
349    }
350}
351
352#[repr(u8)]
353#[derive(Debug, Copy, Clone, PartialEq, Eq)]
354pub enum YuvChromaSubsampling {
355    Yuv420 = 0,
356    Yuv422 = 1,
357    Yuv444 = 2,
358}
359
360impl From<u8> for YuvChromaSubsampling {
361    #[inline(always)]
362    fn from(value: u8) -> Self {
363        match value {
364            0 => YuvChromaSubsampling::Yuv420,
365            1 => YuvChromaSubsampling::Yuv422,
366            2 => YuvChromaSubsampling::Yuv444,
367            _ => {
368                unimplemented!("Unknown value")
369            }
370        }
371    }
372}
373
374#[repr(u8)]
375#[derive(Copy, Clone, PartialEq, Eq)]
376/// This controls endianness of YUV storage format
377pub enum YuvEndianness {
378    #[cfg(feature = "big_endian")]
379    BigEndian = 0,
380    LittleEndian = 1,
381}
382
383impl From<u8> for YuvEndianness {
384    #[inline(always)]
385    fn from(value: u8) -> Self {
386        match value {
387            #[cfg(feature = "big_endian")]
388            0 => YuvEndianness::BigEndian,
389            1 => YuvEndianness::LittleEndian,
390            _ => {
391                unimplemented!("Unknown value")
392            }
393        }
394    }
395}
396
397#[repr(u8)]
398#[derive(Debug, Copy, Clone, PartialEq, Eq)]
399/// Most of the cases of storage bytes is least significant whereas b`0000000111111` integers stored in low part.
400///
401/// However most modern hardware encoders (Apple, Android manufacturers) uses most significant bytes
402/// where same number stored as b`111111000000` and need to be shifted right before working with this.
403/// This is not the same and endianness. I never met `big endian` packing with `most significant bytes`
404/// so this case may not work fully correct, however, `little endian` + `most significant bytes`
405/// can be easily derived from HDR camera stream on android and apple platforms.
406/// This may also correspond to either [YCBCR_P010](https://developer.android.com/reference/android/graphics/ImageFormat#YCBCR_P010)
407/// or [YCBCR_P210](https://developer.android.com/reference/android/graphics/ImageFormat#YCBCR_P210)
408/// or [kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange](https://developer.apple.com/documentation/CoreVideo/kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange)
409/// or [kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange](https://developer.apple.com/documentation/CoreVideo/kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange).
410pub enum YuvBytesPacking {
411    MostSignificantBytes = 0,
412    LeastSignificantBytes = 1,
413}
414
415impl From<u8> for YuvBytesPacking {
416    #[inline(always)]
417    fn from(value: u8) -> Self {
418        match value {
419            0 => YuvBytesPacking::MostSignificantBytes,
420            1 => YuvBytesPacking::LeastSignificantBytes,
421            _ => {
422                unimplemented!("Unknown value")
423            }
424        }
425    }
426}
427
428#[repr(u8)]
429#[derive(Debug, Copy, Clone, PartialEq, Eq)]
430pub enum YuvSourceChannels {
431    Rgb = 0,
432    Rgba = 1,
433    Bgra = 2,
434    Bgr = 3,
435}
436
437impl Display for YuvSourceChannels {
438    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
439        match self {
440            YuvSourceChannels::Rgb => f.write_str("YuvSourceChannels::Rgb"),
441            YuvSourceChannels::Rgba => f.write_str("YuvSourceChannels::Rgba"),
442            YuvSourceChannels::Bgra => f.write_str("YuvSourceChannels::Bgra"),
443            YuvSourceChannels::Bgr => f.write_str("YuvSourceChannels::Bgr"),
444        }
445    }
446}
447
448impl From<u8> for YuvSourceChannels {
449    #[inline(always)]
450    fn from(value: u8) -> Self {
451        match value {
452            0 => YuvSourceChannels::Rgb,
453            1 => YuvSourceChannels::Rgba,
454            2 => YuvSourceChannels::Bgra,
455            3 => YuvSourceChannels::Bgr,
456            _ => {
457                unimplemented!("Unknown value")
458            }
459        }
460    }
461}
462
463impl YuvSourceChannels {
464    #[inline(always)]
465    pub const fn get_channels_count(&self) -> usize {
466        match self {
467            YuvSourceChannels::Rgb | YuvSourceChannels::Bgr => 3,
468            YuvSourceChannels::Rgba | YuvSourceChannels::Bgra => 4,
469        }
470    }
471
472    #[inline(always)]
473    pub const fn has_alpha(&self) -> bool {
474        match self {
475            YuvSourceChannels::Rgb | YuvSourceChannels::Bgr => false,
476            YuvSourceChannels::Rgba | YuvSourceChannels::Bgra => true,
477        }
478    }
479}
480
481impl YuvSourceChannels {
482    #[inline(always)]
483    pub const fn get_r_channel_offset(&self) -> usize {
484        match self {
485            YuvSourceChannels::Rgb => 0,
486            YuvSourceChannels::Rgba => 0,
487            YuvSourceChannels::Bgra => 2,
488            YuvSourceChannels::Bgr => 2,
489        }
490    }
491
492    #[inline(always)]
493    pub const fn get_g_channel_offset(&self) -> usize {
494        match self {
495            YuvSourceChannels::Rgb | YuvSourceChannels::Bgr => 1,
496            YuvSourceChannels::Rgba | YuvSourceChannels::Bgra => 1,
497        }
498    }
499
500    #[inline(always)]
501    pub const fn get_b_channel_offset(&self) -> usize {
502        match self {
503            YuvSourceChannels::Rgb => 2,
504            YuvSourceChannels::Rgba => 2,
505            YuvSourceChannels::Bgra => 0,
506            YuvSourceChannels::Bgr => 0,
507        }
508    }
509    #[inline(always)]
510    pub const fn get_a_channel_offset(&self) -> usize {
511        match self {
512            YuvSourceChannels::Rgb | YuvSourceChannels::Bgr => 0,
513            YuvSourceChannels::Rgba | YuvSourceChannels::Bgra => 3,
514        }
515    }
516}
517
518#[repr(usize)]
519#[derive(Debug, Copy, Clone, PartialEq, Eq)]
520#[allow(clippy::upper_case_acronyms)]
521pub(crate) enum Yuy2Description {
522    YUYV = 0,
523    UYVY = 1,
524    YVYU = 2,
525    VYUY = 3,
526}
527
528impl From<usize> for Yuy2Description {
529    fn from(value: usize) -> Self {
530        match value {
531            0 => Yuy2Description::YUYV,
532            1 => Yuy2Description::UYVY,
533            2 => Yuy2Description::YVYU,
534            3 => Yuy2Description::VYUY,
535            _ => {
536                unimplemented!("YUY2 not supported value {}", value)
537            }
538        }
539    }
540}
541
542impl Yuy2Description {
543    #[inline]
544    pub(crate) const fn get_u_position(&self) -> usize {
545        match self {
546            Yuy2Description::YUYV => 1,
547            Yuy2Description::UYVY => 0,
548            Yuy2Description::YVYU => 3,
549            Yuy2Description::VYUY => 2,
550        }
551    }
552
553    #[inline]
554    pub(crate) const fn get_v_position(&self) -> usize {
555        match self {
556            Yuy2Description::YUYV => 3,
557            Yuy2Description::UYVY => 2,
558            Yuy2Description::YVYU => 1,
559            Yuy2Description::VYUY => 0,
560        }
561    }
562
563    #[inline(always)]
564    pub(crate) const fn get_first_y_position(&self) -> usize {
565        match self {
566            Yuy2Description::YUYV => 0,
567            Yuy2Description::UYVY => 1,
568            Yuy2Description::YVYU => 0,
569            Yuy2Description::VYUY => 1,
570        }
571    }
572
573    #[inline]
574    pub(crate) const fn get_second_y_position(&self) -> usize {
575        match self {
576            Yuy2Description::YUYV => 2,
577            Yuy2Description::UYVY => 3,
578            Yuy2Description::YVYU => 2,
579            Yuy2Description::VYUY => 3,
580        }
581    }
582}
583
584#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
585pub(crate) enum YuvPacked444Format {
586    Ayuv = 0,
587    Vuya = 1,
588}
589
590impl From<u8> for YuvPacked444Format {
591    fn from(value: u8) -> Self {
592        match value {
593            0 => YuvPacked444Format::Ayuv,
594            1 => YuvPacked444Format::Vuya,
595            _ => unimplemented!("{} is not implemented on PackedYuv444", value),
596        }
597    }
598}
599
600#[cfg(not(all(target_arch = "aarch64", target_feature = "neon")))]
601impl YuvPacked444Format {
602    pub(crate) const fn get_a_ps(&self) -> usize {
603        match self {
604            YuvPacked444Format::Ayuv => 0,
605            YuvPacked444Format::Vuya => 3,
606        }
607    }
608
609    pub(crate) const fn get_u_ps(&self) -> usize {
610        match self {
611            YuvPacked444Format::Ayuv => 2,
612            YuvPacked444Format::Vuya => 1,
613        }
614    }
615
616    pub(crate) const fn get_v_ps(&self) -> usize {
617        match self {
618            YuvPacked444Format::Ayuv => 3,
619            YuvPacked444Format::Vuya => 0,
620        }
621    }
622
623    pub(crate) const fn get_y_ps(&self) -> usize {
624        match self {
625            YuvPacked444Format::Ayuv => 1,
626            YuvPacked444Format::Vuya => 2,
627        }
628    }
629}
630
631#[derive(Debug, Copy, Clone, PartialEq, Eq)]
632pub(crate) enum Rgb30 {
633    Ar30 = 0,
634    Ab30 = 1,
635    Ra30 = 2,
636    Ba30 = 3,
637}
638
639impl From<usize> for Rgb30 {
640    fn from(value: usize) -> Self {
641        match value {
642            0 => Rgb30::Ar30,
643            1 => Rgb30::Ab30,
644            2 => Rgb30::Ra30,
645            3 => Rgb30::Ba30,
646            _ => {
647                unimplemented!("Rgb30 is not implemented for value {}", value)
648            }
649        }
650    }
651}
652
653/// Converts a value from host byte order to network byte order.
654#[inline]
655const fn htonl(hostlong: u32) -> u32 {
656    hostlong.to_be()
657}
658
659/// Converts a value from network byte order to host byte order.
660#[inline]
661const fn ntohl(netlong: u32) -> u32 {
662    u32::from_be(netlong)
663}
664
665impl Rgb30 {
666    #[inline(always)]
667    pub(crate) const fn pack<const STORE: usize>(self, r: i32, g: i32, b: i32) -> u32 {
668        let value: u32 = match self {
669            Rgb30::Ar30 => (((3 << 30) | (b << 20)) | ((g << 10) | r)) as u32,
670            Rgb30::Ab30 => (((3 << 30) | (r << 20)) | ((g << 10) | b)) as u32,
671            Rgb30::Ra30 => (((r << 22) | (g << 12)) | ((b << 2) | 3)) as u32,
672            Rgb30::Ba30 => (((b << 22) | (g << 12)) | ((r << 2) | 3)) as u32,
673        };
674        if STORE == 0 {
675            value
676        } else {
677            htonl(value)
678        }
679    }
680
681    pub(crate) const fn pack_w_a<const STORE: usize>(self, r: i32, g: i32, b: i32, a: i32) -> u32 {
682        let value: u32 = match self {
683            Rgb30::Ar30 => ((a << 30) | (b << 20) | (g << 10) | r) as u32,
684            Rgb30::Ab30 => ((a << 30) | (r << 20) | (g << 10) | b) as u32,
685            Rgb30::Ra30 => ((r << 22) | (g << 12) | (b << 2) | a) as u32,
686            Rgb30::Ba30 => ((b << 22) | (g << 12) | (r << 2) | a) as u32,
687        };
688        if STORE == 0 {
689            value
690        } else {
691            htonl(value)
692        }
693    }
694
695    #[inline(always)]
696    pub(crate) const fn unpack<const STORE: usize>(self, value: u32) -> (u32, u32, u32, u32) {
697        let pixel = if STORE == 0 { value } else { ntohl(value) };
698        match self {
699            Rgb30::Ar30 => {
700                let r10 = pixel & 0x3ff;
701                let g10 = (pixel >> 10) & 0x3ff;
702                let b10 = (pixel >> 20) & 0x3ff;
703                let a10 = pixel >> 30;
704                (r10, g10, b10, a10)
705            }
706            Rgb30::Ab30 => {
707                let b10 = pixel & 0x3ff;
708                let g10 = (pixel >> 10) & 0x3ff;
709                let r10 = (pixel >> 20) & 0x3ff;
710                let a10 = pixel >> 30;
711                (r10, g10, b10, a10)
712            }
713            Rgb30::Ra30 => {
714                let a2 = pixel & 0x3;
715                let r10 = (pixel >> 22) & 0x3ff;
716                let g10 = (pixel >> 12) & 0x3ff;
717                let b10 = (pixel >> 2) & 0x3ff;
718                (r10, g10, b10, a2)
719            }
720            Rgb30::Ba30 => {
721                let a2 = pixel & 0x3;
722                let b10 = (pixel >> 22) & 0x3ff;
723                let g10 = (pixel >> 12) & 0x3ff;
724                let r10 = (pixel >> 2) & 0x3ff;
725                (r10, g10, b10, a2)
726            }
727        }
728    }
729}
730
731#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
732/// Represents the byte order for storing RGBA data in 30-bit formats such as RGBA1010102 or RGBA2101010.
733///
734/// # Overview
735/// RGBA1010102 and RGBA2101010 are 30-bit color formats where each component (R, G, B, and A)
736/// uses 10 bits for color depth, and the remaining 2 bits are used for additional purposes (e.g., alpha).
737///
738/// In certain systems, the byte order used for storage can differ:
739/// - **Host Byte Order**: Uses the native endianness of the host machine (little-endian or big-endian).
740/// - **Network Byte Order**: Always uses big-endian format, often required for consistent data
741///   transfer across different platforms or network protocols. Used by Apple.
742pub enum Rgb30ByteOrder {
743    Host = 0,
744    Network = 1,
745}
746
747impl From<usize> for Rgb30ByteOrder {
748    fn from(value: usize) -> Self {
749        match value {
750            0 => Rgb30ByteOrder::Host,
751            1 => Rgb30ByteOrder::Network,
752            _ => {
753                unimplemented!("Rgb30ByteOrder is not implemented for value {}", value)
754            }
755        }
756    }
757}
758
759/// Search for prebuilt forward transform, otherwise computes new transform
760pub(crate) fn search_forward_transform(
761    precision: i32,
762    bit_depth: u32,
763    range: YuvRange,
764    matrix: YuvStandardMatrix,
765    chroma_range: YuvChromaRange,
766    kr_kb: YuvBias,
767) -> CbCrForwardTransform<i32> {
768    if let Some(stored_t) = get_built_forward_transform(precision as u32, bit_depth, range, matrix)
769    {
770        stored_t
771    } else {
772        let transform_precise = get_forward_transform(
773            (1 << bit_depth) - 1,
774            chroma_range.range_y,
775            chroma_range.range_uv,
776            kr_kb.kr,
777            kr_kb.kb,
778        );
779        transform_precise.to_integers(precision as u32)
780    }
781}
782
783/// Search for prebuilt inverse transform, otherwise computes new transform
784pub(crate) fn search_inverse_transform(
785    precision: i32,
786    bit_depth: u32,
787    range: YuvRange,
788    matrix: YuvStandardMatrix,
789    chroma_range: YuvChromaRange,
790    kr_kb: YuvBias,
791) -> CbCrInverseTransform<i32> {
792    if let Some(stored) = get_built_inverse_transform(precision as u32, bit_depth, range, matrix) {
793        stored
794    } else {
795        let transform = get_inverse_transform(
796            (1 << bit_depth) - 1,
797            chroma_range.range_y,
798            chroma_range.range_uv,
799            kr_kb.kr,
800            kr_kb.kb,
801        );
802        if precision == 6 {
803            // We can't allow infinite contribution to fastest 6 bit approximation
804            let mut transform = transform.to_integers(precision as u32);
805            transform.cr_coef = transform.cr_coef.min(127);
806            transform.cb_coef = transform.cb_coef.min(127);
807            transform.g_coeff_1 = transform.g_coeff_1.min(127);
808            transform.g_coeff_2 = transform.g_coeff_2.min(127);
809            transform
810        } else {
811            transform.to_integers(precision as u32)
812        }
813    }
814}
815
816/// Declares YUV conversion accuracy mode
817///
818/// In common case, each step for increasing accuracy have at least 30% slowdown.
819#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Default)]
820pub enum YuvConversionMode {
821    /// Minimal precision, but the fastest option. Same as libyuv does use.
822    /// This may encode with notable changes in the image,
823    /// consider using this when you're migrating from libyuv and want same,
824    /// or fastest performance, or you just need the fastest available performance.
825    /// On aarch64 `i8mm` activated feature may be preferred, nightly compiler channel is required,
826    /// when encoding RGBA/BGRA only.
827    /// For `x86` consider activating `avx512` feature ( nightly compiler channel is required ),
828    /// it may significantly increase throughout on some modern CPU's,
829    /// even without AVX-512 available. `avxvnni` may be used instead.
830    #[cfg(feature = "fast_mode")]
831    #[cfg_attr(docsrs, doc(cfg(feature = "fast_mode")))]
832    Fast,
833    /// Mixed, but high precision, very good performance.
834    /// This is still a VERY fast method, with much more precise encoding.
835    /// This option is more suitable for common encoding, where fast speed is critical along the
836    /// high precision.
837    #[default]
838    Balanced,
839    /// Maximizes quality and precision over speed while maintaining reasonable performance.
840    #[cfg(feature = "professional_mode")]
841    #[cfg_attr(docsrs, doc(cfg(feature = "professional_mode")))]
842    Professional,
843}
844
845impl Display for YuvConversionMode {
846    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
847        match self {
848            #[cfg(feature = "fast_mode")]
849            YuvConversionMode::Fast => f.write_str("YuvAccuracy::Fast"),
850            YuvConversionMode::Balanced => f.write_str("YuvAccuracy::Balanced"),
851            #[cfg(feature = "professional_mode")]
852            YuvConversionMode::Professional => f.write_str("YuvAccuracy::Professional"),
853        }
854    }
855}