1use 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);