yuvutils_rs/
rgb_to_nv_p16.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::yuv_error::check_rgba_destination;
30use crate::yuv_support::{
31    get_forward_transform, get_yuv_range, ToIntegerTransform, YuvChromaSubsampling, YuvNVOrder,
32    YuvSourceChannels,
33};
34use crate::{
35    YuvBiPlanarImageMut, YuvBytesPacking, YuvEndianness, YuvError, YuvRange, YuvStandardMatrix,
36};
37use num_traits::AsPrimitive;
38#[cfg(feature = "rayon")]
39use rayon::iter::{IndexedParallelIterator, ParallelIterator};
40#[cfg(feature = "rayon")]
41use rayon::prelude::{ParallelSlice, ParallelSliceMut};
42
43#[inline(always)]
44fn transform_integer<const ENDIANNESS: u8, const BYTES_POSITION: u8, const BIT_DEPTH: u8>(
45    v: i32,
46) -> u16 {
47    let endianness: YuvEndianness = ENDIANNESS.into();
48    let bytes_position: YuvBytesPacking = BYTES_POSITION.into();
49    let packing: i32 = 16 - BIT_DEPTH as i32;
50    let packed_bytes = match bytes_position {
51        YuvBytesPacking::MostSignificantBytes => v << packing,
52        YuvBytesPacking::LeastSignificantBytes => v,
53    } as u16;
54    match endianness {
55        #[cfg(feature = "big_endian")]
56        YuvEndianness::BigEndian => packed_bytes.to_be(),
57        YuvEndianness::LittleEndian => packed_bytes.to_le(),
58    }
59}
60
61fn rgbx_to_yuv_bi_planar_10_impl<
62    J: AsPrimitive<i32> + Copy + Send + Sync,
63    const ORIGIN_CHANNELS: u8,
64    const NV_ORDER: u8,
65    const SAMPLING: u8,
66    const ENDIANNESS: u8,
67    const BYTES_POSITION: u8,
68    const BIT_DEPTH: u8,
69>(
70    image: &mut YuvBiPlanarImageMut<u16>,
71    rgba: &[u16],
72    rgba_stride: u32,
73    range: YuvRange,
74    matrix: YuvStandardMatrix,
75) -> Result<(), YuvError>
76where
77    i32: AsPrimitive<J>,
78{
79    let nv_order: YuvNVOrder = NV_ORDER.into();
80    let chroma_subsampling: YuvChromaSubsampling = SAMPLING.into();
81    let src_chans: YuvSourceChannels = ORIGIN_CHANNELS.into();
82    let channels = src_chans.get_channels_count();
83
84    image.check_constraints(chroma_subsampling)?;
85    check_rgba_destination(rgba, rgba_stride, image.width, image.height, channels)?;
86
87    let range = get_yuv_range(BIT_DEPTH as u32, range);
88    let kr_kb = matrix.get_kr_kb();
89    let max_range = (1u32 << BIT_DEPTH as u32) - 1u32;
90
91    const PRECISION: i32 = 15;
92
93    let transform_precise =
94        get_forward_transform(max_range, range.range_y, range.range_uv, kr_kb.kr, kr_kb.kb);
95    let transform = transform_precise.to_integers(PRECISION as u32).cast::<J>();
96    const ROUNDING_CONST_BIAS: i32 = (1 << (PRECISION - 1)) - 1;
97    let bias_y = range.bias_y as i32 * (1 << PRECISION) + ROUNDING_CONST_BIAS;
98    let bias_uv = range.bias_uv as i32 * (1 << PRECISION) + ROUNDING_CONST_BIAS;
99
100    let width = image.width;
101
102    let process_double_row = |y_dst0: &mut [u16],
103                              y_dst1: &mut [u16],
104                              uv_dst: &mut [u16],
105                              rgba0: &[u16],
106                              rgba1: &[u16]| {
107        for ((((y_dst0, y_dst1), uv_dst), rgba0), rgba1) in y_dst0
108            .chunks_exact_mut(2)
109            .zip(y_dst1.chunks_exact_mut(2))
110            .zip(uv_dst.chunks_exact_mut(2))
111            .zip(rgba0.chunks_exact(channels * 2))
112            .zip(rgba1.chunks_exact(channels * 2))
113        {
114            let rgba00 = &rgba0[0..channels];
115
116            let r00 = rgba00[src_chans.get_r_channel_offset()] as i32;
117            let g00 = rgba00[src_chans.get_g_channel_offset()] as i32;
118            let b00 = rgba00[src_chans.get_b_channel_offset()] as i32;
119
120            let y_00 = (r00 * transform.yr.as_()
121                + g00 * transform.yg.as_()
122                + b00 * transform.yb.as_()
123                + bias_y)
124                >> PRECISION;
125            y_dst0[0] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_00);
126
127            let rgba01 = &rgba0[channels..channels * 2];
128
129            let r01 = rgba01[src_chans.get_r_channel_offset()] as i32;
130            let g01 = rgba01[src_chans.get_g_channel_offset()] as i32;
131            let b01 = rgba01[src_chans.get_b_channel_offset()] as i32;
132
133            let y_01 = (r01 * transform.yr.as_()
134                + g01 * transform.yg.as_()
135                + b01 * transform.yb.as_()
136                + bias_y)
137                >> PRECISION;
138            y_dst0[1] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_01);
139
140            let rgba01 = &rgba1[0..channels];
141            let r10 = rgba01[src_chans.get_r_channel_offset()] as i32;
142            let g10 = rgba01[src_chans.get_g_channel_offset()] as i32;
143            let b10 = rgba01[src_chans.get_b_channel_offset()] as i32;
144
145            let y_10 = (r10 * transform.yr.as_()
146                + g10 * transform.yg.as_()
147                + b10 * transform.yb.as_()
148                + bias_y)
149                >> PRECISION;
150            y_dst1[0] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_10);
151
152            let rgba11 = &rgba1[channels..channels * 2];
153
154            let r11 = rgba11[src_chans.get_r_channel_offset()] as i32;
155            let g11 = rgba11[src_chans.get_g_channel_offset()] as i32;
156            let b11 = rgba11[src_chans.get_b_channel_offset()] as i32;
157
158            let y_11 = (r11 * transform.yr.as_()
159                + g11 * transform.yg.as_()
160                + b11 * transform.yb.as_()
161                + bias_y)
162                >> PRECISION;
163            y_dst1[1] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_11);
164
165            let r = (r00 + r01 + r10 + r11 + 2) >> 2;
166            let g = (g00 + g01 + g10 + g11 + 2) >> 2;
167            let b = (b00 + b01 + b10 + b11 + 2) >> 2;
168
169            let cb = (r * transform.cb_r.as_()
170                + g * transform.cb_g.as_()
171                + b * transform.cb_b.as_()
172                + bias_uv)
173                >> PRECISION;
174            let cr = (r * transform.cr_r.as_()
175                + g * transform.cr_g.as_()
176                + b * transform.cr_b.as_()
177                + bias_uv)
178                >> PRECISION;
179            uv_dst[nv_order.get_u_position()] =
180                transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cb);
181            uv_dst[nv_order.get_v_position()] =
182                transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cr);
183        }
184
185        if width & 1 != 0 {
186            let rgba0 = rgba0.chunks_exact(channels * 2).remainder();
187            let rgba0 = &rgba0[0..channels];
188            let rgba1 = rgba1.chunks_exact(channels * 2).remainder();
189            let rgba1 = &rgba1[0..channels];
190            let uv_dst = uv_dst.chunks_exact_mut(2).last().unwrap();
191            let y_dst0 = y_dst0.chunks_exact_mut(2).into_remainder();
192
193            let r0 = rgba0[src_chans.get_r_channel_offset()] as i32;
194            let g0 = rgba0[src_chans.get_g_channel_offset()] as i32;
195            let b0 = rgba0[src_chans.get_b_channel_offset()] as i32;
196
197            let r1 = rgba1[src_chans.get_r_channel_offset()] as i32;
198            let g1 = rgba1[src_chans.get_g_channel_offset()] as i32;
199            let b1 = rgba1[src_chans.get_b_channel_offset()] as i32;
200
201            let r = (r0 + r1 + 1) >> 1;
202            let g = (g0 + g1 + 1) >> 1;
203            let b = (b0 + b1 + 1) >> 1;
204
205            let y_0 = (r0 * transform.yr.as_()
206                + g0 * transform.yg.as_()
207                + b0 * transform.yb.as_()
208                + bias_y)
209                >> PRECISION;
210            y_dst0[0] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_0);
211
212            let y_1 = (r1 * transform.yr.as_()
213                + g1 * transform.yg.as_()
214                + b1 * transform.yb.as_()
215                + bias_y)
216                >> PRECISION;
217            y_dst1[0] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_1);
218
219            let cb = (r * transform.cb_r.as_()
220                + g * transform.cb_g.as_()
221                + b * transform.cb_b.as_()
222                + bias_uv)
223                >> PRECISION;
224            let cr = (r * transform.cr_r.as_()
225                + g * transform.cr_g.as_()
226                + b * transform.cr_b.as_()
227                + bias_uv)
228                >> PRECISION;
229            uv_dst[nv_order.get_u_position()] =
230                transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cb);
231            uv_dst[nv_order.get_v_position()] =
232                transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cr);
233        }
234    };
235
236    let process_halved_row = |y_dst: &mut [u16], uv_dst: &mut [u16], rgba: &[u16]| {
237        for ((y_dst, uv_dst), rgba) in y_dst
238            .chunks_exact_mut(2)
239            .zip(uv_dst.chunks_exact_mut(2))
240            .zip(rgba.chunks_exact(channels * 2))
241        {
242            let rgba0 = &rgba[0..channels];
243            let r0 = rgba0[src_chans.get_r_channel_offset()] as i32;
244            let g0 = rgba0[src_chans.get_g_channel_offset()] as i32;
245            let b0 = rgba0[src_chans.get_b_channel_offset()] as i32;
246            let y_0 = (r0 * transform.yr.as_()
247                + g0 * transform.yg.as_()
248                + b0 * transform.yb.as_()
249                + bias_y)
250                >> PRECISION;
251            y_dst[0] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_0);
252
253            let rgba1 = &rgba[channels..channels * 2];
254
255            let r1 = rgba1[src_chans.get_r_channel_offset()] as i32;
256            let g1 = rgba1[src_chans.get_g_channel_offset()] as i32;
257            let b1 = rgba1[src_chans.get_b_channel_offset()] as i32;
258
259            let y_1 = (r1 * transform.yr.as_()
260                + g1 * transform.yg.as_()
261                + b1 * transform.yb.as_()
262                + bias_y)
263                >> PRECISION;
264            y_dst[1] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_1);
265
266            let r = (r0 + r1 + 1) >> 1;
267            let g = (g0 + g1 + 1) >> 1;
268            let b = (b0 + b1 + 1) >> 1;
269
270            let cb = (r * transform.cb_r.as_()
271                + g * transform.cb_g.as_()
272                + b * transform.cb_b.as_()
273                + bias_uv)
274                >> PRECISION;
275            let cr = (r * transform.cr_r.as_()
276                + g * transform.cr_g.as_()
277                + b * transform.cr_b.as_()
278                + bias_uv)
279                >> PRECISION;
280            uv_dst[nv_order.get_u_position()] =
281                transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cb);
282            uv_dst[nv_order.get_v_position()] =
283                transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cr);
284        }
285
286        if width & 1 != 0 {
287            let rgba = rgba.chunks_exact(channels * 2).remainder();
288            let rgba = &rgba[0..channels];
289            let uv_dst = uv_dst.chunks_exact_mut(2).last().unwrap();
290            let y_dst = y_dst.chunks_exact_mut(2).into_remainder();
291
292            let r0 = rgba[src_chans.get_r_channel_offset()] as i32;
293            let g0 = rgba[src_chans.get_g_channel_offset()] as i32;
294            let b0 = rgba[src_chans.get_b_channel_offset()] as i32;
295            let y_0 = (r0 * transform.yr.as_()
296                + g0 * transform.yg.as_()
297                + b0 * transform.yb.as_()
298                + bias_y)
299                >> PRECISION;
300            y_dst[0] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_0);
301
302            let cb = (r0 * transform.cb_r.as_()
303                + g0 * transform.cb_g.as_()
304                + b0 * transform.cb_b.as_()
305                + bias_uv)
306                >> PRECISION;
307            let cr = (r0 * transform.cr_r.as_()
308                + g0 * transform.cr_g.as_()
309                + b0 * transform.cr_b.as_()
310                + bias_uv)
311                >> PRECISION;
312            uv_dst[nv_order.get_u_position()] =
313                transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cb);
314            uv_dst[nv_order.get_v_position()] =
315                transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cr);
316        }
317    };
318
319    let y_plane = image.y_plane.borrow_mut();
320    let uv_plane = image.uv_plane.borrow_mut();
321    let y_stride = image.y_stride;
322    let uv_stride = image.uv_stride;
323
324    if chroma_subsampling == YuvChromaSubsampling::Yuv444 {
325        let iter;
326        #[cfg(feature = "rayon")]
327        {
328            iter = y_plane
329                .par_chunks_exact_mut(y_stride as usize)
330                .zip(uv_plane.par_chunks_exact_mut(uv_stride as usize))
331                .zip(rgba.par_chunks_exact(rgba_stride as usize));
332        }
333        #[cfg(not(feature = "rayon"))]
334        {
335            iter = y_plane
336                .chunks_exact_mut(y_stride as usize)
337                .zip(uv_plane.chunks_exact_mut(uv_stride as usize))
338                .zip(rgba.chunks_exact(rgba_stride as usize));
339        }
340        iter.for_each(|((y_dst, uv_dst), rgba)| {
341            let y_dst = &mut y_dst[0..image.width as usize];
342            for ((y_dst, uv_dst), rgba) in y_dst
343                .iter_mut()
344                .zip(uv_dst.chunks_exact_mut(2))
345                .zip(rgba.chunks_exact(channels))
346            {
347                let r0 = rgba[src_chans.get_r_channel_offset()] as i32;
348                let g0 = rgba[src_chans.get_g_channel_offset()] as i32;
349                let b0 = rgba[src_chans.get_b_channel_offset()] as i32;
350                let y_0 = (r0 * transform.yr.as_()
351                    + g0 * transform.yg.as_()
352                    + b0 * transform.yb.as_()
353                    + bias_y)
354                    >> PRECISION;
355                *y_dst = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_0);
356                let cb = (r0 * transform.cb_r.as_()
357                    + g0 * transform.cb_g.as_()
358                    + b0 * transform.cb_b.as_()
359                    + bias_uv)
360                    >> PRECISION;
361                let cr = (r0 * transform.cr_r.as_()
362                    + g0 * transform.cr_g.as_()
363                    + b0 * transform.cr_b.as_()
364                    + bias_uv)
365                    >> PRECISION;
366                uv_dst[nv_order.get_u_position()] =
367                    transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cb);
368                uv_dst[nv_order.get_v_position()] =
369                    transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cr);
370            }
371        });
372    } else if chroma_subsampling == YuvChromaSubsampling::Yuv422 {
373        let iter;
374        #[cfg(feature = "rayon")]
375        {
376            iter = y_plane
377                .par_chunks_exact_mut(y_stride as usize)
378                .zip(uv_plane.par_chunks_exact_mut(uv_stride as usize))
379                .zip(rgba.par_chunks_exact(rgba_stride as usize));
380        }
381        #[cfg(not(feature = "rayon"))]
382        {
383            iter = y_plane
384                .chunks_exact_mut(y_stride as usize)
385                .zip(uv_plane.chunks_exact_mut(uv_stride as usize))
386                .zip(rgba.chunks_exact(rgba_stride as usize));
387        }
388        iter.for_each(|((y_dst, uv_dst), rgba)| {
389            process_halved_row(
390                &mut y_dst[0..image.width as usize],
391                &mut uv_dst[0..(image.width as usize).div_ceil(2) * 2],
392                &rgba[0..image.width as usize * channels],
393            );
394        });
395    } else {
396        let iter;
397        #[cfg(feature = "rayon")]
398        {
399            iter = y_plane
400                .par_chunks_exact_mut(y_stride as usize * 2)
401                .zip(uv_plane.par_chunks_exact_mut(uv_stride as usize))
402                .zip(rgba.par_chunks_exact(rgba_stride as usize * 2));
403        }
404        #[cfg(not(feature = "rayon"))]
405        {
406            iter = y_plane
407                .chunks_exact_mut(y_stride as usize * 2)
408                .zip(uv_plane.chunks_exact_mut(uv_stride as usize))
409                .zip(rgba.chunks_exact(rgba_stride as usize * 2));
410        }
411        iter.for_each(|((y_dst, uv_dst), rgba)| {
412            let (y_dst0, y_dst1) = y_dst.split_at_mut(y_stride as usize);
413            let (rgba0, rgba1) = rgba.split_at(rgba_stride as usize);
414            process_double_row(
415                &mut y_dst0[0..image.width as usize],
416                &mut y_dst1[0..image.width as usize],
417                &mut uv_dst[0..(image.width as usize).div_ceil(2) * 2],
418                &rgba0[0..image.width as usize * channels],
419                &rgba1[0..image.width as usize * channels],
420            );
421        });
422
423        if image.height & 1 != 0 {
424            let y_dst = y_plane
425                .chunks_exact_mut(y_stride as usize * 2)
426                .into_remainder();
427            let uv_dst = uv_plane
428                .chunks_exact_mut(uv_stride as usize)
429                .last()
430                .unwrap();
431            let rgba = rgba.chunks_exact(rgba_stride as usize * 2).remainder();
432            process_halved_row(
433                &mut y_dst[0..image.width as usize],
434                &mut uv_dst[0..(image.width as usize).div_ceil(2) * 2],
435                &rgba[0..image.width as usize * channels],
436            );
437        }
438    }
439
440    Ok(())
441}
442
443macro_rules! d_cnv {
444    ($method:ident, $px_fmt: expr, $subsampling: expr, $yuv_name: expr, $rgb_name: expr, $bit_depth: expr, $intermediate: ident) => {
445        #[doc = concat!("Convert ",$rgb_name, stringify!($bit_depth)," image data to ", $yuv_name, " format.
446
447This function performs ",$rgb_name, stringify!($bit_depth)," to ",$yuv_name," conversion and stores the result in ", $yuv_name, " format,
448with separate planes for Y (luminance), UV (chrominance) components.
449
450# Arguments
451
452* `bi_planar_image` - Target Bi-Planar ", $yuv_name," image.
453* `dst` - The input ", $rgb_name, stringify!($bit_depth)," image data slice.
454* `dst_stride` - The stride (components per row) for the ", $rgb_name, stringify!($bit_depth)," image data.
455* `range` - The YUV range (limited or full).
456* `matrix` - The YUV standard matrix (BT.601 or BT.709 or BT.2020 or other).
457
458# Panics
459
460This function panics if the lengths of the planes or the input ", $rgb_name," data are not valid based
461on the specified width, height, and strides, or if invalid YUV range or matrix is provided.")]
462        pub fn $method(
463            bi_planar_image: &mut YuvBiPlanarImageMut<u16>,
464            dst: &[u16],
465            dst_stride: u32,
466            range: YuvRange,
467            matrix: YuvStandardMatrix,
468        ) -> Result<(), YuvError> {
469            rgbx_to_yuv_bi_planar_10_impl::<
470                $intermediate,
471                { $px_fmt as u8 },
472                { YuvNVOrder::UV as u8 },
473                { $subsampling as u8 },
474                { YuvEndianness::LittleEndian as u8 },
475                { YuvBytesPacking::MostSignificantBytes as u8 },
476                $bit_depth,
477            >(bi_planar_image, dst, dst_stride, range, matrix)
478        }
479    };
480}
481
482d_cnv!(
483    rgba10_to_p010,
484    YuvSourceChannels::Rgba,
485    YuvChromaSubsampling::Yuv420,
486    "P010",
487    "RGBA",
488    10,
489    i16
490);
491d_cnv!(
492    rgb10_to_p010,
493    YuvSourceChannels::Rgb,
494    YuvChromaSubsampling::Yuv420,
495    "P010",
496    "RGB",
497    10,
498    i16
499);
500d_cnv!(
501    rgba10_to_p210,
502    YuvSourceChannels::Rgba,
503    YuvChromaSubsampling::Yuv422,
504    "P210",
505    "RGBA",
506    10,
507    i16
508);
509d_cnv!(
510    rgb10_to_p210,
511    YuvSourceChannels::Rgb,
512    YuvChromaSubsampling::Yuv422,
513    "P210",
514    "RGB",
515    10,
516    i16
517);
518d_cnv!(
519    rgba10_to_p410,
520    YuvSourceChannels::Rgba,
521    YuvChromaSubsampling::Yuv444,
522    "P410",
523    "RGBA",
524    10,
525    i16
526);
527d_cnv!(
528    rgb10_to_p410,
529    YuvSourceChannels::Rgb,
530    YuvChromaSubsampling::Yuv444,
531    "P410",
532    "RGB",
533    10,
534    i16
535);
536
537d_cnv!(
538    rgba12_to_p012,
539    YuvSourceChannels::Rgba,
540    YuvChromaSubsampling::Yuv420,
541    "P012",
542    "RGBA",
543    12,
544    i16
545);
546d_cnv!(
547    rgb12_to_p012,
548    YuvSourceChannels::Rgb,
549    YuvChromaSubsampling::Yuv420,
550    "P012",
551    "RGB",
552    12,
553    i16
554);
555d_cnv!(
556    rgba12_to_p212,
557    YuvSourceChannels::Rgba,
558    YuvChromaSubsampling::Yuv422,
559    "P212",
560    "RGBA",
561    12,
562    i16
563);
564d_cnv!(
565    rgb12_to_p212,
566    YuvSourceChannels::Rgb,
567    YuvChromaSubsampling::Yuv422,
568    "P212",
569    "RGB",
570    12,
571    i16
572);
573d_cnv!(
574    rgba12_to_p412,
575    YuvSourceChannels::Rgba,
576    YuvChromaSubsampling::Yuv444,
577    "P412",
578    "RGBA",
579    12,
580    i16
581);
582d_cnv!(
583    rgb12_to_p412,
584    YuvSourceChannels::Rgb,
585    YuvChromaSubsampling::Yuv444,
586    "P412",
587    "RGB",
588    12,
589    i16
590);
591
592d_cnv!(
593    rgba16_to_p016,
594    YuvSourceChannels::Rgba,
595    YuvChromaSubsampling::Yuv420,
596    "P016",
597    "RGBA",
598    16,
599    i32
600);
601d_cnv!(
602    rgb16_to_p016,
603    YuvSourceChannels::Rgb,
604    YuvChromaSubsampling::Yuv420,
605    "P016",
606    "RGB",
607    16,
608    i32
609);
610
611d_cnv!(
612    rgba16_to_p216,
613    YuvSourceChannels::Rgba,
614    YuvChromaSubsampling::Yuv420,
615    "P216",
616    "RGBA",
617    16,
618    i32
619);
620d_cnv!(
621    rgb16_to_p216,
622    YuvSourceChannels::Rgb,
623    YuvChromaSubsampling::Yuv420,
624    "P216",
625    "RGB",
626    16,
627    i32
628);