yuvutils_rs/
yuv_p16_ar30.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::numerics::{qrshr, to_ne};
30use crate::yuv_error::check_rgba_destination;
31use crate::yuv_support::{
32    get_yuv_range, search_inverse_transform, Rgb30, YuvBytesPacking, YuvChromaSubsampling,
33    YuvEndianness, YuvRange, YuvStandardMatrix,
34};
35use crate::{Rgb30ByteOrder, YuvError, YuvPlanarImage};
36#[cfg(feature = "rayon")]
37use rayon::iter::{IndexedParallelIterator, ParallelIterator};
38#[cfg(feature = "rayon")]
39use rayon::prelude::{ParallelSlice, ParallelSliceMut};
40
41fn yuv_p16_to_image_ar30<
42    const AR30_LAYOUT: usize,
43    const AR30_STORE: usize,
44    const SAMPLING: u8,
45    const ENDIANNESS: u8,
46    const BYTES_POSITION: u8,
47    const BIT_DEPTH: usize,
48>(
49    image: &YuvPlanarImage<u16>,
50    rgba: &mut [u8],
51    rgba_stride: u32,
52    range: YuvRange,
53    matrix: YuvStandardMatrix,
54) -> Result<(), YuvError> {
55    let ar30_layout: Rgb30 = AR30_LAYOUT.into();
56
57    let chroma_subsampling: YuvChromaSubsampling = SAMPLING.into();
58    let chroma_range = get_yuv_range(BIT_DEPTH as u32, range);
59
60    image.check_constraints(chroma_subsampling)?;
61    check_rgba_destination(rgba, rgba_stride, image.width, image.height, 4)?;
62
63    let kr_kb = matrix.get_kr_kb();
64    const AR30_DEPTH: usize = 10;
65    const PRECISION: i32 = 14;
66    let i_transform = search_inverse_transform(
67        PRECISION,
68        BIT_DEPTH as u32,
69        range,
70        matrix,
71        chroma_range,
72        kr_kb,
73    );
74    let cr_coef = i_transform.cr_coef;
75    let cb_coef = i_transform.cb_coef;
76    let y_coef = i_transform.y_coef;
77    let g_coef_1 = i_transform.g_coeff_1;
78    let g_coef_2 = i_transform.g_coeff_2;
79
80    let bias_y = chroma_range.bias_y as i32;
81    let bias_uv = chroma_range.bias_uv as i32;
82
83    let msb_shift = (16 - BIT_DEPTH) as i32;
84
85    let process_halved_chroma_row = |y_plane: &[u16],
86                                     u_plane: &[u16],
87                                     v_plane: &[u16],
88                                     rgba: &mut [u8]| {
89        for (((rgba, y_src), &u_src), &v_src) in rgba
90            .chunks_exact_mut(2 * 4)
91            .zip(y_plane.chunks_exact(2))
92            .zip(u_plane.iter())
93            .zip(v_plane.iter())
94        {
95            let y_value0 =
96                (to_ne::<ENDIANNESS, BYTES_POSITION>(y_src[0], msb_shift) as i32 - bias_y) * y_coef;
97            let cb_value = to_ne::<ENDIANNESS, BYTES_POSITION>(u_src, msb_shift) as i32 - bias_uv;
98            let cr_value = to_ne::<ENDIANNESS, BYTES_POSITION>(v_src, msb_shift) as i32 - bias_uv;
99
100            let r0 = qrshr::<PRECISION, AR30_DEPTH>(y_value0 + cr_coef * cr_value);
101            let b0 = qrshr::<PRECISION, AR30_DEPTH>(y_value0 + cb_coef * cb_value);
102            let g0 = qrshr::<PRECISION, AR30_DEPTH>(
103                y_value0 - g_coef_1 * cr_value - g_coef_2 * cb_value,
104            );
105
106            let rgba_2 = &mut rgba[0..8];
107
108            let pixel0 = ar30_layout.pack::<AR30_STORE>(r0, g0, b0).to_ne_bytes();
109            rgba_2[0] = pixel0[0];
110            rgba_2[1] = pixel0[1];
111            rgba_2[2] = pixel0[2];
112            rgba_2[3] = pixel0[3];
113
114            let y_value1 =
115                (to_ne::<ENDIANNESS, BYTES_POSITION>(y_src[1], msb_shift) as i32 - bias_y) * y_coef;
116
117            let r1 = qrshr::<PRECISION, BIT_DEPTH>(y_value1 + cr_coef * cr_value);
118            let b1 = qrshr::<PRECISION, BIT_DEPTH>(y_value1 + cb_coef * cb_value);
119            let g1 =
120                qrshr::<PRECISION, BIT_DEPTH>(y_value1 - g_coef_1 * cr_value - g_coef_2 * cb_value);
121
122            let pixel1 = ar30_layout.pack::<AR30_STORE>(r1, g1, b1).to_ne_bytes();
123            rgba_2[4] = pixel1[0];
124            rgba_2[5] = pixel1[1];
125            rgba_2[6] = pixel1[2];
126            rgba_2[7] = pixel1[3];
127        }
128
129        if image.width & 1 != 0 {
130            let y_value0 = (to_ne::<ENDIANNESS, BYTES_POSITION>(*y_plane.last().unwrap(), msb_shift)
131                as i32
132                - bias_y)
133                * y_coef;
134            let cb_value = to_ne::<ENDIANNESS, BYTES_POSITION>(*u_plane.last().unwrap(), msb_shift)
135                as i32
136                - bias_uv;
137            let cr_value = to_ne::<ENDIANNESS, BYTES_POSITION>(*v_plane.last().unwrap(), msb_shift)
138                as i32
139                - bias_uv;
140            let rgba = rgba.chunks_exact_mut(4).last().unwrap();
141
142            let r0 = qrshr::<PRECISION, BIT_DEPTH>(y_value0 + cr_coef * cr_value);
143            let b0 = qrshr::<PRECISION, BIT_DEPTH>(y_value0 + cb_coef * cb_value);
144            let g0 =
145                qrshr::<PRECISION, BIT_DEPTH>(y_value0 - g_coef_1 * cr_value - g_coef_2 * cb_value);
146            let pixel0 = ar30_layout.pack::<AR30_STORE>(r0, g0, b0).to_ne_bytes();
147            rgba[0] = pixel0[0];
148            rgba[1] = pixel0[1];
149            rgba[2] = pixel0[2];
150            rgba[3] = pixel0[3];
151        }
152    };
153
154    if chroma_subsampling == YuvChromaSubsampling::Yuv444 {
155        let iter;
156        #[cfg(feature = "rayon")]
157        {
158            iter = rgba
159                .par_chunks_exact_mut(rgba_stride as usize)
160                .zip(image.y_plane.par_chunks_exact(image.y_stride as usize))
161                .zip(image.u_plane.par_chunks_exact(image.u_stride as usize))
162                .zip(image.v_plane.par_chunks_exact(image.v_stride as usize));
163        }
164        #[cfg(not(feature = "rayon"))]
165        {
166            iter = rgba
167                .chunks_exact_mut(rgba_stride as usize)
168                .zip(image.y_plane.chunks_exact(image.y_stride as usize))
169                .zip(image.u_plane.chunks_exact(image.u_stride as usize))
170                .zip(image.v_plane.chunks_exact(image.v_stride as usize));
171        }
172        iter.for_each(|(((rgba, y_plane), u_plane), v_plane)| {
173            for (((rgba, &y_src), &u_src), &v_src) in rgba
174                .chunks_exact_mut(4)
175                .zip(y_plane.iter())
176                .zip(u_plane.iter())
177                .zip(v_plane.iter())
178            {
179                let y_value = (to_ne::<ENDIANNESS, BYTES_POSITION>(y_src, msb_shift) as i32
180                    - bias_y)
181                    * y_coef;
182                let cb_value =
183                    to_ne::<ENDIANNESS, BYTES_POSITION>(u_src, msb_shift) as i32 - bias_uv;
184                let cr_value =
185                    to_ne::<ENDIANNESS, BYTES_POSITION>(v_src, msb_shift) as i32 - bias_uv;
186
187                let r = qrshr::<PRECISION, BIT_DEPTH>(y_value + cr_coef * cr_value);
188                let b = qrshr::<PRECISION, BIT_DEPTH>(y_value + cb_coef * cb_value);
189                let g = qrshr::<PRECISION, BIT_DEPTH>(
190                    y_value - g_coef_1 * cr_value - g_coef_2 * cb_value,
191                );
192
193                let pixel0 = ar30_layout.pack::<AR30_STORE>(r, g, b).to_ne_bytes();
194                rgba[0] = pixel0[0];
195                rgba[1] = pixel0[1];
196                rgba[2] = pixel0[2];
197                rgba[3] = pixel0[3];
198            }
199        });
200    } else if chroma_subsampling == YuvChromaSubsampling::Yuv422 {
201        let iter;
202        #[cfg(feature = "rayon")]
203        {
204            iter = rgba
205                .par_chunks_exact_mut(rgba_stride as usize)
206                .zip(image.y_plane.par_chunks_exact(image.y_stride as usize))
207                .zip(image.u_plane.par_chunks_exact(image.u_stride as usize))
208                .zip(image.v_plane.par_chunks_exact(image.v_stride as usize));
209        }
210        #[cfg(not(feature = "rayon"))]
211        {
212            iter = rgba
213                .chunks_exact_mut(rgba_stride as usize)
214                .zip(image.y_plane.chunks_exact(image.y_stride as usize))
215                .zip(image.u_plane.chunks_exact(image.u_stride as usize))
216                .zip(image.v_plane.chunks_exact(image.v_stride as usize));
217        }
218        iter.for_each(|(((rgba, y_plane), u_plane), v_plane)| {
219            process_halved_chroma_row(
220                &y_plane[0..image.width as usize],
221                &u_plane[0..(image.width as usize).div_ceil(2)],
222                &v_plane[0..(image.width as usize).div_ceil(2)],
223                &mut rgba[0..image.width as usize * 4],
224            );
225        });
226    } else if chroma_subsampling == YuvChromaSubsampling::Yuv420 {
227        let iter;
228        #[cfg(feature = "rayon")]
229        {
230            iter = rgba
231                .par_chunks_exact_mut(rgba_stride as usize * 2)
232                .zip(image.y_plane.par_chunks_exact(image.y_stride as usize * 2))
233                .zip(image.u_plane.par_chunks_exact(image.u_stride as usize))
234                .zip(image.v_plane.par_chunks_exact(image.v_stride as usize));
235        }
236        #[cfg(not(feature = "rayon"))]
237        {
238            iter = rgba
239                .chunks_exact_mut(rgba_stride as usize * 2)
240                .zip(image.y_plane.chunks_exact(image.y_stride as usize * 2))
241                .zip(image.u_plane.chunks_exact(image.u_stride as usize))
242                .zip(image.v_plane.chunks_exact(image.v_stride as usize));
243        }
244        iter.for_each(|(((rgba, y_plane), u_plane), v_plane)| {
245            for (rgba, y_plane) in rgba
246                .chunks_exact_mut(rgba_stride as usize)
247                .zip(y_plane.chunks_exact(image.y_stride as usize))
248            {
249                process_halved_chroma_row(
250                    &y_plane[0..image.width as usize],
251                    &u_plane[0..(image.width as usize).div_ceil(2)],
252                    &v_plane[0..(image.width as usize).div_ceil(2)],
253                    &mut rgba[0..image.width as usize * 4],
254                );
255            }
256        });
257
258        if image.height & 1 != 0 {
259            let rgba = rgba.chunks_exact_mut(rgba_stride as usize).last().unwrap();
260            let u_plane = image
261                .u_plane
262                .chunks_exact(image.u_stride as usize)
263                .last()
264                .unwrap();
265            let v_plane = image
266                .v_plane
267                .chunks_exact(image.v_stride as usize)
268                .last()
269                .unwrap();
270            let y_plane = image
271                .y_plane
272                .chunks_exact(image.y_stride as usize)
273                .last()
274                .unwrap();
275            process_halved_chroma_row(
276                &y_plane[0..image.width as usize],
277                &u_plane[0..(image.width as usize).div_ceil(2)],
278                &v_plane[0..(image.width as usize).div_ceil(2)],
279                &mut rgba[0..image.width as usize * 4],
280            );
281        }
282    } else {
283        unreachable!();
284    }
285
286    Ok(())
287}
288
289macro_rules! build_cnv {
290    ($method: ident, $ar_fmt: expr, $subsampling: expr,
291    $bit_depth: expr, $sampling_written: expr,
292    $px_written: expr, $px_written_small: expr) => {
293        #[doc = concat!("
294Convert ",$sampling_written, " planar format with ", $bit_depth," bit pixel format to ", $px_written," format.
295
296This function takes ", $sampling_written, " planar data with ",$bit_depth," bit precision.
297and converts it to ", $px_written," format.
298
299# Arguments
300
301* `planar_image` - Source ",$sampling_written," planar image.
302* `", $px_written_small, "` - A mutable slice to store the converted ", $px_written," format.
303* `", $px_written_small, "_stride` - The stride (components per row) for ", $px_written," format.
304* `range` - The YUV range (limited or full).
305* `matrix` - The YUV standard matrix (BT.601 or BT.709 or BT.2020 or other).
306
307# Panics
308
309This function panics if the lengths of the planes or the input ", $px_written," data are not valid based
310on the specified width, height, and strides, or if invalid YUV range or matrix is provided.")]
311        pub fn $method(
312            planar_image: &YuvPlanarImage<u16>,
313            dst: &mut [u8],
314            dst_stride: u32,
315            byte_order: Rgb30ByteOrder,
316            range: YuvRange,
317            matrix: YuvStandardMatrix,
318        ) -> Result<(), YuvError> {
319               match byte_order {
320                    Rgb30ByteOrder::Host => yuv_p16_to_image_ar30::<
321                        { $ar_fmt as usize },
322                        { Rgb30ByteOrder::Host as usize },
323                        { $subsampling as u8 },
324                        { YuvEndianness::LittleEndian as u8 },
325                        { YuvBytesPacking::LeastSignificantBytes as u8 },
326                        $bit_depth,
327                    >(planar_image, dst, dst_stride, range, matrix),
328                    Rgb30ByteOrder::Network => yuv_p16_to_image_ar30::<
329                        { $ar_fmt as usize },
330                        { Rgb30ByteOrder::Network as usize },
331                        { $subsampling as u8 },
332                        { YuvEndianness::LittleEndian as u8 },
333                        { YuvBytesPacking::LeastSignificantBytes as u8 },
334                        $bit_depth,
335                    >(planar_image, dst, dst_stride, range, matrix),
336            }
337        }
338    };
339}
340
341build_cnv!(
342    i010_to_ar30,
343    Rgb30::Ar30,
344    YuvChromaSubsampling::Yuv420,
345    10,
346    "I010",
347    "AR30",
348    "ar30"
349);
350build_cnv!(
351    i012_to_ar30,
352    Rgb30::Ar30,
353    YuvChromaSubsampling::Yuv420,
354    12,
355    "I012",
356    "AR30",
357    "ar30"
358);
359build_cnv!(
360    i014_to_ar30,
361    Rgb30::Ar30,
362    YuvChromaSubsampling::Yuv420,
363    14,
364    "I014",
365    "AR30",
366    "ar30"
367);
368build_cnv!(
369    i010_to_ra30,
370    Rgb30::Ra30,
371    YuvChromaSubsampling::Yuv420,
372    10,
373    "I010",
374    "RA30",
375    "ra30"
376);
377build_cnv!(
378    i012_to_ra30,
379    Rgb30::Ra30,
380    YuvChromaSubsampling::Yuv420,
381    12,
382    "I012",
383    "RA30",
384    "ra30"
385);
386build_cnv!(
387    i014_to_ra30,
388    Rgb30::Ra30,
389    YuvChromaSubsampling::Yuv420,
390    14,
391    "I014",
392    "RA30",
393    "ra30"
394);
395
396build_cnv!(
397    i210_to_ar30,
398    Rgb30::Ar30,
399    YuvChromaSubsampling::Yuv422,
400    10,
401    "I210",
402    "AR30",
403    "ar30"
404);
405build_cnv!(
406    i212_to_ar30,
407    Rgb30::Ar30,
408    YuvChromaSubsampling::Yuv422,
409    12,
410    "I212",
411    "AR30",
412    "ar30"
413);
414build_cnv!(
415    i214_to_ar30,
416    Rgb30::Ar30,
417    YuvChromaSubsampling::Yuv422,
418    14,
419    "I214",
420    "AR30",
421    "ar30"
422);
423build_cnv!(
424    i210_to_ra30,
425    Rgb30::Ra30,
426    YuvChromaSubsampling::Yuv422,
427    10,
428    "I210",
429    "RA30",
430    "ra30"
431);
432build_cnv!(
433    i212_to_ra30,
434    Rgb30::Ra30,
435    YuvChromaSubsampling::Yuv422,
436    12,
437    "I212",
438    "RA30",
439    "ra30"
440);
441build_cnv!(
442    i214_to_ra30,
443    Rgb30::Ra30,
444    YuvChromaSubsampling::Yuv422,
445    14,
446    "I214",
447    "RA30",
448    "ra30"
449);
450
451build_cnv!(
452    i410_to_ar30,
453    Rgb30::Ar30,
454    YuvChromaSubsampling::Yuv444,
455    10,
456    "I410",
457    "AR30",
458    "ar30"
459);
460build_cnv!(
461    i412_to_ar30,
462    Rgb30::Ar30,
463    YuvChromaSubsampling::Yuv444,
464    12,
465    "I412",
466    "AR30",
467    "ar30"
468);
469build_cnv!(
470    i414_to_ar30,
471    Rgb30::Ar30,
472    YuvChromaSubsampling::Yuv444,
473    14,
474    "I414",
475    "AR30",
476    "ar30"
477);
478build_cnv!(
479    i410_to_ra30,
480    Rgb30::Ra30,
481    YuvChromaSubsampling::Yuv444,
482    10,
483    "I410",
484    "RA30",
485    "ra30"
486);
487build_cnv!(
488    i412_to_ra30,
489    Rgb30::Ra30,
490    YuvChromaSubsampling::Yuv444,
491    12,
492    "I412",
493    "RA30",
494    "ra30"
495);
496build_cnv!(
497    i414_to_ra30,
498    Rgb30::Ra30,
499    YuvChromaSubsampling::Yuv444,
500    14,
501    "I414",
502    "RA30",
503    "ra30"
504);