1use crate::internals::{ProcessedOffset, WideRowForward420Handler, WideRowForwardHandler};
30
31use crate::yuv_error::check_rgba_destination;
32use crate::yuv_support::{
33 get_forward_transform, get_yuv_range, CbCrForwardTransform, ToIntegerTransform, YuvChromaRange,
34 YuvChromaSubsampling, YuvSourceChannels,
35};
36use crate::{
37 YuvBytesPacking, YuvEndianness, YuvError, YuvPlanarImageMut, YuvRange, YuvStandardMatrix,
38};
39#[cfg(feature = "rayon")]
40use rayon::iter::{IndexedParallelIterator, ParallelIterator};
41#[cfg(feature = "rayon")]
42use rayon::prelude::{ParallelSlice, ParallelSliceMut};
43
44#[inline(always)]
45fn transform_integer<const ENDIANNESS: u8, const BYTES_POSITION: u8, const BIT_DEPTH: usize>(
46 v: i32,
47) -> u16 {
48 let endianness: YuvEndianness = ENDIANNESS.into();
49 let bytes_position: YuvBytesPacking = BYTES_POSITION.into();
50 let packing: i32 = 16 - BIT_DEPTH as i32;
51 let packed_bytes = match bytes_position {
52 YuvBytesPacking::MostSignificantBytes => v << packing,
53 YuvBytesPacking::LeastSignificantBytes => v,
54 } as u16;
55 match endianness {
56 #[cfg(feature = "big_endian")]
57 YuvEndianness::BigEndian => packed_bytes.to_be(),
58 YuvEndianness::LittleEndian => packed_bytes.to_le(),
59 }
60}
61
62type RgbEncoderHandler = Option<
63 unsafe fn(
64 transform: &CbCrForwardTransform<i32>,
65 range: &YuvChromaRange,
66 y_plane: &mut [u16],
67 u_plane: &mut [u16],
68 v_plane: &mut [u16],
69 rgba: &[u16],
70 start_cx: usize,
71 start_ux: usize,
72 width: usize,
73 ) -> ProcessedOffset,
74>;
75
76type RgbEncoder420Handler = Option<
77 unsafe fn(
78 transform: &CbCrForwardTransform<i32>,
79 range: &YuvChromaRange,
80 y_plane0: &mut [u16],
81 y_plane1: &mut [u16],
82 u_plane: &mut [u16],
83 v_plane: &mut [u16],
84 rgba0: &[u16],
85 rgba1: &[u16],
86 start_cx: usize,
87 start_ux: usize,
88 width: usize,
89 ) -> ProcessedOffset,
90>;
91
92struct RgbEncoder<
93 const ORIGIN_CHANNELS: u8,
94 const SAMPLING: u8,
95 const ENDIANNESS: u8,
96 const BYTES_POSITION: u8,
97 const BIT_DEPTH: usize,
98 const PRECISION: i32,
99> {
100 handler: RgbEncoderHandler,
101}
102
103struct RgbEncoder420<
104 const ORIGIN_CHANNELS: u8,
105 const SAMPLING: u8,
106 const ENDIANNESS: u8,
107 const BYTES_POSITION: u8,
108 const BIT_DEPTH: usize,
109 const PRECISION: i32,
110> {
111 handler: RgbEncoder420Handler,
112}
113
114impl<
115 const ORIGIN_CHANNELS: u8,
116 const SAMPLING: u8,
117 const ENDIANNESS: u8,
118 const BYTES_POSITION: u8,
119 const BIT_DEPTH: usize,
120 const PRECISION: i32,
121 > Default
122 for RgbEncoder<ORIGIN_CHANNELS, SAMPLING, ENDIANNESS, BYTES_POSITION, BIT_DEPTH, PRECISION>
123{
124 fn default() -> Self {
125 if PRECISION != 15 {
126 return RgbEncoder { handler: None };
127 }
128 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
129 {
130 #[cfg(feature = "rdm")]
131 if BIT_DEPTH == 10 {
132 let is_rdm_available = std::arch::is_aarch64_feature_detected!("rdm");
133 if is_rdm_available {
134 use crate::neon::neon_rgba_to_yuv_p16_rdm;
135 return RgbEncoder {
136 handler: Some(
137 neon_rgba_to_yuv_p16_rdm::<
138 ORIGIN_CHANNELS,
139 SAMPLING,
140 ENDIANNESS,
141 BYTES_POSITION,
142 PRECISION,
143 BIT_DEPTH,
144 >,
145 ),
146 };
147 }
148 }
149
150 use crate::neon::neon_rgba_to_yuv_p16;
151 RgbEncoder {
152 handler: Some(
153 neon_rgba_to_yuv_p16::<
154 ORIGIN_CHANNELS,
155 SAMPLING,
156 ENDIANNESS,
157 BYTES_POSITION,
158 PRECISION,
159 BIT_DEPTH,
160 >,
161 ),
162 }
163 }
164 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
165 {
166 #[cfg(feature = "nightly_avx512")]
167 {
168 let use_avx512 = std::arch::is_x86_feature_detected!("avx512bw");
169 if use_avx512 && BIT_DEPTH <= 15 {
170 use crate::avx512bw::avx512_rgba_to_yuv_p16;
171 return RgbEncoder {
172 handler: Some(
173 avx512_rgba_to_yuv_p16::<
174 ORIGIN_CHANNELS,
175 SAMPLING,
176 ENDIANNESS,
177 BYTES_POSITION,
178 PRECISION,
179 BIT_DEPTH,
180 >,
181 ),
182 };
183 }
184 }
185 #[cfg(feature = "avx")]
186 {
187 let use_avx = std::arch::is_x86_feature_detected!("avx2");
188 if use_avx {
189 if BIT_DEPTH <= 15 {
190 use crate::avx2::avx_rgba_to_yuv_p16;
191 return RgbEncoder {
192 handler: Some(
193 avx_rgba_to_yuv_p16::<
194 ORIGIN_CHANNELS,
195 SAMPLING,
196 ENDIANNESS,
197 BYTES_POSITION,
198 PRECISION,
199 BIT_DEPTH,
200 >,
201 ),
202 };
203 } else {
204 use crate::avx2::avx_rgba_to_yuv_p16_d16;
205 return RgbEncoder {
206 handler: Some(
207 avx_rgba_to_yuv_p16_d16::<
208 ORIGIN_CHANNELS,
209 SAMPLING,
210 ENDIANNESS,
211 BYTES_POSITION,
212 PRECISION,
213 BIT_DEPTH,
214 >,
215 ),
216 };
217 }
218 }
219 }
220 #[cfg(feature = "sse")]
221 {
222 let use_sse = std::arch::is_x86_feature_detected!("sse4.1");
223 if use_sse && BIT_DEPTH <= 15 {
224 use crate::sse::sse_rgba_to_yuv_p16;
225 return RgbEncoder {
226 handler: Some(
227 sse_rgba_to_yuv_p16::<
228 ORIGIN_CHANNELS,
229 SAMPLING,
230 ENDIANNESS,
231 BYTES_POSITION,
232 PRECISION,
233 BIT_DEPTH,
234 >,
235 ),
236 };
237 }
238 }
239 }
240 #[cfg(not(all(target_arch = "aarch64", target_feature = "neon")))]
241 RgbEncoder { handler: None }
242 }
243}
244
245macro_rules! define_handler_impl {
246 ($struct_name:ident) => {
247 impl<
248 const ORIGIN_CHANNELS: u8,
249 const SAMPLING: u8,
250 const ENDIANNESS: u8,
251 const BYTES_POSITION: u8,
252 const BIT_DEPTH: usize,
253 const PRECISION: i32,
254 > WideRowForwardHandler<u16, i32>
255 for $struct_name<
256 ORIGIN_CHANNELS,
257 SAMPLING,
258 ENDIANNESS,
259 BYTES_POSITION,
260 BIT_DEPTH,
261 PRECISION,
262 >
263 {
264 fn handle_row(
265 &self,
266 y_plane: &mut [u16],
267 u_plane: &mut [u16],
268 v_plane: &mut [u16],
269 rgba: &[u16],
270 width: u32,
271 chroma: YuvChromaRange,
272 transform: &CbCrForwardTransform<i32>,
273 ) -> ProcessedOffset {
274 if let Some(handler) = self.handler {
275 unsafe {
276 return handler(
277 transform,
278 &chroma,
279 y_plane,
280 u_plane,
281 v_plane,
282 rgba,
283 0,
284 0,
285 width as usize,
286 );
287 }
288 }
289 ProcessedOffset { cx: 0, ux: 0 }
290 }
291 }
292 };
293}
294
295define_handler_impl!(RgbEncoder);
296
297impl<
298 const ORIGIN_CHANNELS: u8,
299 const SAMPLING: u8,
300 const ENDIANNESS: u8,
301 const BYTES_POSITION: u8,
302 const BIT_DEPTH: usize,
303 const PRECISION: i32,
304 > Default
305 for RgbEncoder420<ORIGIN_CHANNELS, SAMPLING, ENDIANNESS, BYTES_POSITION, BIT_DEPTH, PRECISION>
306{
307 fn default() -> Self {
308 if PRECISION != 15 {
309 return RgbEncoder420 { handler: None };
310 }
311 assert_eq!(PRECISION, 15);
312 let sampling: YuvChromaSubsampling = SAMPLING.into();
313 if sampling != YuvChromaSubsampling::Yuv420 {
314 return RgbEncoder420 { handler: None };
315 }
316 assert_eq!(sampling, YuvChromaSubsampling::Yuv420);
317 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
318 {
319 #[cfg(feature = "rdm")]
320 {
321 let is_rdm_available = std::arch::is_aarch64_feature_detected!("rdm");
322 if is_rdm_available && BIT_DEPTH == 10 {
323 use crate::neon::neon_rgba_to_yuv_p16_rdm_420;
324 return RgbEncoder420 {
325 handler: Some(
326 neon_rgba_to_yuv_p16_rdm_420::<
327 ORIGIN_CHANNELS,
328 ENDIANNESS,
329 BYTES_POSITION,
330 PRECISION,
331 BIT_DEPTH,
332 >,
333 ),
334 };
335 }
336 }
337 use crate::neon::neon_rgba_to_yuv_p16_420;
338 RgbEncoder420 {
339 handler: Some(
340 neon_rgba_to_yuv_p16_420::<
341 ORIGIN_CHANNELS,
342 ENDIANNESS,
343 BYTES_POSITION,
344 PRECISION,
345 BIT_DEPTH,
346 >,
347 ),
348 }
349 }
350 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
351 {
352 #[cfg(feature = "nightly_avx512")]
353 {
354 let use_avx512 = std::arch::is_x86_feature_detected!("avx512bw");
355 if use_avx512 && BIT_DEPTH <= 15 {
356 use crate::avx512bw::avx512_rgba_to_yuv_p16_420;
357 return RgbEncoder420 {
358 handler: Some(
359 avx512_rgba_to_yuv_p16_420::<
360 ORIGIN_CHANNELS,
361 ENDIANNESS,
362 BYTES_POSITION,
363 PRECISION,
364 BIT_DEPTH,
365 >,
366 ),
367 };
368 }
369 }
370 #[cfg(feature = "avx")]
371 {
372 let use_avx = std::arch::is_x86_feature_detected!("avx2");
373 if use_avx {
374 if BIT_DEPTH <= 15 {
375 use crate::avx2::avx_rgba_to_yuv_p16_420;
376 return RgbEncoder420 {
377 handler: Some(
378 avx_rgba_to_yuv_p16_420::<
379 ORIGIN_CHANNELS,
380 ENDIANNESS,
381 BYTES_POSITION,
382 PRECISION,
383 BIT_DEPTH,
384 >,
385 ),
386 };
387 } else if BIT_DEPTH <= 16 {
388 use crate::avx2::avx_rgba_to_yuv_p16_420_d16;
389 return RgbEncoder420 {
390 handler: Some(
391 avx_rgba_to_yuv_p16_420_d16::<
392 ORIGIN_CHANNELS,
393 ENDIANNESS,
394 BYTES_POSITION,
395 PRECISION,
396 BIT_DEPTH,
397 >,
398 ),
399 };
400 }
401 }
402 }
403 #[cfg(feature = "sse")]
404 {
405 let use_sse = std::arch::is_x86_feature_detected!("sse4.1");
406 if use_sse && BIT_DEPTH <= 15 {
407 use crate::sse::sse_rgba_to_yuv_p16_420;
408 return RgbEncoder420 {
409 handler: Some(
410 sse_rgba_to_yuv_p16_420::<
411 ORIGIN_CHANNELS,
412 ENDIANNESS,
413 BYTES_POSITION,
414 PRECISION,
415 BIT_DEPTH,
416 >,
417 ),
418 };
419 }
420 }
421 }
422 #[cfg(not(all(target_arch = "aarch64", target_feature = "neon")))]
423 RgbEncoder420 { handler: None }
424 }
425}
426
427macro_rules! impl_wide_row_forward_handler {
428 ($struct_name:ident) => {
429 impl<
430 const ORIGIN_CHANNELS: u8,
431 const SAMPLING: u8,
432 const ENDIANNESS: u8,
433 const BYTES_POSITION: u8,
434 const BIT_DEPTH: usize,
435 const PRECISION: i32,
436 > WideRowForward420Handler<u16, i32>
437 for $struct_name<
438 ORIGIN_CHANNELS,
439 SAMPLING,
440 ENDIANNESS,
441 BYTES_POSITION,
442 BIT_DEPTH,
443 PRECISION,
444 >
445 {
446 fn handle_row(
447 &self,
448 y_plane0: &mut [u16],
449 y_plane1: &mut [u16],
450 u_plane: &mut [u16],
451 v_plane: &mut [u16],
452 rgba0: &[u16],
453 rgba1: &[u16],
454 width: u32,
455 chroma: YuvChromaRange,
456 transform: &CbCrForwardTransform<i32>,
457 ) -> ProcessedOffset {
458 if let Some(handler) = self.handler {
459 unsafe {
460 return handler(
461 transform,
462 &chroma,
463 y_plane0,
464 y_plane1,
465 u_plane,
466 v_plane,
467 rgba0,
468 rgba1,
469 0,
470 0,
471 width as usize,
472 );
473 }
474 }
475 ProcessedOffset { cx: 0, ux: 0 }
476 }
477 }
478 };
479}
480
481impl_wide_row_forward_handler!(RgbEncoder420);
482
483fn rgbx_to_yuv_ant<
484 const ORIGIN_CHANNELS: u8,
485 const SAMPLING: u8,
486 const ENDIANNESS: u8,
487 const BYTES_POSITION: u8,
488 const BIT_DEPTH: usize,
489 const PRECISION: i32,
490>(
491 image: &mut YuvPlanarImageMut<u16>,
492 rgba: &[u16],
493 rgba_stride: u32,
494 range: YuvRange,
495 matrix: YuvStandardMatrix,
496 handler: impl WideRowForwardHandler<u16, i32> + Send + Sync,
497 handler420: impl WideRowForward420Handler<u16, i32> + Send + Sync,
498) -> Result<(), YuvError> {
499 let chroma_subsampling: YuvChromaSubsampling = SAMPLING.into();
500 let src_chans: YuvSourceChannels = ORIGIN_CHANNELS.into();
501 let channels = src_chans.get_channels_count();
502
503 image.check_constraints(chroma_subsampling)?;
504 check_rgba_destination(rgba, rgba_stride, image.width, image.height, channels)?;
505
506 let range = get_yuv_range(BIT_DEPTH as u32, range);
507 let kr_kb = matrix.get_kr_kb();
508 let max_range = (1u32 << BIT_DEPTH) - 1u32;
509 let transform_precise =
510 get_forward_transform(max_range, range.range_y, range.range_uv, kr_kb.kr, kr_kb.kb);
511
512 let transform = transform_precise.to_integers(PRECISION as u32);
513 let rnd_const: i32 = (1 << (PRECISION - 1)) - 1;
514 let bias_y = range.bias_y as i32 * (1 << PRECISION) + rnd_const;
515 let bias_uv = range.bias_uv as i32 * (1 << PRECISION) + rnd_const;
516
517 let process_halved_chroma_row = |y_plane: &mut [u16],
518 u_plane: &mut [u16],
519 v_plane: &mut [u16],
520 rgba| {
521 let processed_offset = handler.handle_row(
522 y_plane,
523 u_plane,
524 v_plane,
525 rgba,
526 image.width,
527 range,
528 &transform,
529 );
530 let cx = processed_offset.cx;
531 if cx != image.width as usize {
532 for (((y_dst, u_dst), v_dst), rgba) in y_plane
533 .chunks_exact_mut(2)
534 .zip(u_plane.iter_mut())
535 .zip(v_plane.iter_mut())
536 .zip(rgba.chunks_exact(channels * 2))
537 .skip(cx / 2)
538 {
539 let r0 = rgba[src_chans.get_r_channel_offset()] as i32;
540 let g0 = rgba[src_chans.get_g_channel_offset()] as i32;
541 let b0 = rgba[src_chans.get_b_channel_offset()] as i32;
542 let y_0 = (r0 * transform.yr + g0 * transform.yg + b0 * transform.yb + bias_y)
543 >> PRECISION;
544 y_dst[0] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_0);
545
546 let r1 = rgba[channels + src_chans.get_r_channel_offset()] as i32;
547 let g1 = rgba[channels + src_chans.get_g_channel_offset()] as i32;
548 let b1 = rgba[channels + src_chans.get_b_channel_offset()] as i32;
549 let y_1 = (r1 * transform.yr + g1 * transform.yg + b1 * transform.yb + bias_y)
550 >> PRECISION;
551 y_dst[1] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_1);
552
553 let r = (r0 + r1 + 1) >> 1;
554 let g = (g0 + g1 + 1) >> 1;
555 let b = (b0 + b1 + 1) >> 1;
556
557 let cb = (r * transform.cb_r + g * transform.cb_g + b * transform.cb_b + bias_uv)
558 >> PRECISION;
559 let cr = (r * transform.cr_r + g * transform.cr_g + b * transform.cr_b + bias_uv)
560 >> PRECISION;
561 *u_dst = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cb);
562 *v_dst = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cr);
563 }
564
565 if image.width & 1 != 0 {
566 let rgb_last = rgba.chunks_exact(channels * 2).remainder();
567 let r0 = rgb_last[src_chans.get_r_channel_offset()] as i32;
568 let g0 = rgb_last[src_chans.get_g_channel_offset()] as i32;
569 let b0 = rgb_last[src_chans.get_b_channel_offset()] as i32;
570
571 let y_last = y_plane.last_mut().unwrap();
572 let u_last = u_plane.last_mut().unwrap();
573 let v_last = v_plane.last_mut().unwrap();
574
575 let y_0 = (r0 * transform.yr + g0 * transform.yg + b0 * transform.yb + bias_y)
576 >> PRECISION;
577 *y_last = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_0);
578
579 let cb =
580 (r0 * transform.cb_r + g0 * transform.cb_g + b0 * transform.cb_b + bias_uv)
581 >> PRECISION;
582 let cr =
583 (r0 * transform.cr_r + g0 * transform.cr_g + b0 * transform.cr_b + bias_uv)
584 >> PRECISION;
585 *u_last = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cb);
586 *v_last = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cr);
587 }
588 }
589 };
590
591 let process_double_chroma_row = |y_plane0: &mut [u16],
592 y_plane1: &mut [u16],
593 u_plane: &mut [u16],
594 v_plane: &mut [u16],
595 rgba0: &[u16],
596 rgba1: &[u16]| {
597 let processed_offset = handler420.handle_row(
598 y_plane0,
599 y_plane1,
600 u_plane,
601 v_plane,
602 rgba0,
603 rgba1,
604 image.width,
605 range,
606 &transform,
607 );
608 let cx = processed_offset.cx;
609
610 for (((((y_dst0, y_dst1), u_dst), v_dst), rgba0), rgba1) in y_plane0
611 .chunks_exact_mut(2)
612 .zip(y_plane1.chunks_exact_mut(2))
613 .zip(u_plane.iter_mut())
614 .zip(v_plane.iter_mut())
615 .zip(rgba0.chunks_exact(channels * 2))
616 .zip(rgba1.chunks_exact(channels * 2))
617 .skip(cx / 2)
618 {
619 let r00 = rgba0[src_chans.get_r_channel_offset()] as i32;
620 let g00 = rgba0[src_chans.get_g_channel_offset()] as i32;
621 let b00 = rgba0[src_chans.get_b_channel_offset()] as i32;
622 let y_00 = (r00 * transform.yr + g00 * transform.yg + b00 * transform.yb + bias_y)
623 >> PRECISION;
624 y_dst0[0] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_00);
625
626 let rgba01 = &rgba0[channels..channels * 2];
627 let r01 = rgba01[src_chans.get_r_channel_offset()] as i32;
628 let g01 = rgba01[src_chans.get_g_channel_offset()] as i32;
629 let b01 = rgba01[src_chans.get_b_channel_offset()] as i32;
630 let y_01 = (r01 * transform.yr + g01 * transform.yg + b01 * transform.yb + bias_y)
631 >> PRECISION;
632 y_dst0[1] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_01);
633
634 let r10 = rgba1[src_chans.get_r_channel_offset()] as i32;
635 let g10 = rgba1[src_chans.get_g_channel_offset()] as i32;
636 let b10 = rgba1[src_chans.get_b_channel_offset()] as i32;
637 let y_10 = (r10 * transform.yr + g10 * transform.yg + b10 * transform.yb + bias_y)
638 >> PRECISION;
639 y_dst1[0] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_10);
640
641 let rgba11 = &rgba1[channels..channels * 2];
642 let r11 = rgba11[src_chans.get_r_channel_offset()] as i32;
643 let g11 = rgba11[src_chans.get_g_channel_offset()] as i32;
644 let b11 = rgba11[src_chans.get_b_channel_offset()] as i32;
645 let y_11 = (r01 * transform.yr + g01 * transform.yg + b01 * transform.yb + bias_y)
646 >> PRECISION;
647 y_dst1[1] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_11);
648
649 let r = (r00 + r01 + r10 + r11 + 2) >> 2;
650 let g = (g00 + g01 + g10 + g11 + 2) >> 2;
651 let b = (b00 + b01 + b10 + b11 + 2) >> 2;
652
653 let cb = (r * transform.cb_r + g * transform.cb_g + b * transform.cb_b + bias_uv)
654 >> PRECISION;
655 let cr = (r * transform.cr_r + g * transform.cr_g + b * transform.cr_b + bias_uv)
656 >> PRECISION;
657 *u_dst = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cb);
658 *v_dst = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cr);
659 }
660
661 if image.width & 1 != 0 {
662 let rgb_last0 = rgba0.chunks_exact(channels * 2).remainder();
663 let r0 = rgb_last0[src_chans.get_r_channel_offset()] as i32;
664 let g0 = rgb_last0[src_chans.get_g_channel_offset()] as i32;
665 let b0 = rgb_last0[src_chans.get_b_channel_offset()] as i32;
666
667 let rgb_last1 = rgba1.chunks_exact(channels * 2).remainder();
668 let r1 = rgb_last1[src_chans.get_r_channel_offset()] as i32;
669 let g1 = rgb_last1[src_chans.get_g_channel_offset()] as i32;
670 let b1 = rgb_last1[src_chans.get_b_channel_offset()] as i32;
671
672 let y0_last = y_plane0.last_mut().unwrap();
673 let y1_last = y_plane1.last_mut().unwrap();
674 let u_last = u_plane.last_mut().unwrap();
675 let v_last = v_plane.last_mut().unwrap();
676
677 let y_0 =
678 (r0 * transform.yr + g0 * transform.yg + b0 * transform.yb + bias_y) >> PRECISION;
679 *y0_last = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_0);
680
681 let y_1 =
682 (r1 * transform.yr + g1 * transform.yg + b1 * transform.yb + bias_y) >> PRECISION;
683 *y1_last = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_1);
684
685 let r = (r0 + r1 + 1) >> 1;
686 let g = (g0 + g1 + 1) >> 1;
687 let b = (b0 + b1 + 1) >> 1;
688
689 let cb = (r * transform.cb_r + g * transform.cb_g + b * transform.cb_b + bias_uv)
690 >> PRECISION;
691 let cr = (r * transform.cr_r + g * transform.cr_g + b * transform.cr_b + bias_uv)
692 >> PRECISION;
693 *u_last = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cb);
694 *v_last = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cr);
695 }
696 };
697
698 let y_plane = image.y_plane.borrow_mut();
699 let u_plane = image.u_plane.borrow_mut();
700 let v_plane = image.v_plane.borrow_mut();
701 let y_stride = image.y_stride as usize;
702 let u_stride = image.u_stride as usize;
703 let v_stride = image.v_stride as usize;
704
705 if chroma_subsampling == YuvChromaSubsampling::Yuv444 {
706 let iter;
707 #[cfg(feature = "rayon")]
708 {
709 iter = y_plane
710 .par_chunks_exact_mut(y_stride)
711 .zip(u_plane.par_chunks_exact_mut(u_stride))
712 .zip(v_plane.par_chunks_exact_mut(v_stride))
713 .zip(rgba.par_chunks_exact(rgba_stride as usize));
714 }
715 #[cfg(not(feature = "rayon"))]
716 {
717 iter = y_plane
718 .chunks_exact_mut(y_stride)
719 .zip(u_plane.chunks_exact_mut(u_stride))
720 .zip(v_plane.chunks_exact_mut(v_stride))
721 .zip(rgba.chunks_exact(rgba_stride as usize));
722 }
723 iter.for_each(|(((y_dst, u_plane), v_plane), rgba)| {
724 let y_dst = &mut y_dst[0..image.width as usize];
725 let processed_offset = handler.handle_row(
726 y_dst,
727 u_plane,
728 v_plane,
729 rgba,
730 image.width,
731 range,
732 &transform,
733 );
734
735 let cx = processed_offset.cx;
736
737 if cx != image.width as usize {
738 for (((y_dst, u_dst), v_dst), rgba) in y_dst
739 .iter_mut()
740 .zip(u_plane.iter_mut())
741 .zip(v_plane.iter_mut())
742 .zip(rgba.chunks_exact(channels))
743 .skip(cx)
744 {
745 let r0 = rgba[src_chans.get_r_channel_offset()] as i32;
746 let g0 = rgba[src_chans.get_g_channel_offset()] as i32;
747 let b0 = rgba[src_chans.get_b_channel_offset()] as i32;
748 let y_0 = (r0 * transform.yr + g0 * transform.yg + b0 * transform.yb + bias_y)
749 >> PRECISION;
750 *y_dst = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_0);
751
752 let cb =
753 (r0 * transform.cb_r + g0 * transform.cb_g + b0 * transform.cb_b + bias_uv)
754 >> PRECISION;
755 let cr =
756 (r0 * transform.cr_r + g0 * transform.cr_g + b0 * transform.cr_b + bias_uv)
757 >> PRECISION;
758 *u_dst = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cb);
759 *v_dst = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cr);
760 }
761 }
762 });
763 } else if chroma_subsampling == YuvChromaSubsampling::Yuv422 {
764 let iter;
765 #[cfg(feature = "rayon")]
766 {
767 iter = y_plane
768 .par_chunks_exact_mut(y_stride)
769 .zip(u_plane.par_chunks_exact_mut(u_stride))
770 .zip(v_plane.par_chunks_exact_mut(v_stride))
771 .zip(rgba.par_chunks_exact(rgba_stride as usize));
772 }
773 #[cfg(not(feature = "rayon"))]
774 {
775 iter = y_plane
776 .chunks_exact_mut(y_stride)
777 .zip(u_plane.chunks_exact_mut(u_stride))
778 .zip(v_plane.chunks_exact_mut(v_stride))
779 .zip(rgba.chunks_exact(rgba_stride as usize));
780 }
781
782 iter.for_each(|(((y_plane, u_plane), v_plane), rgba)| {
783 process_halved_chroma_row(y_plane, u_plane, v_plane, rgba);
784 });
785 } else if chroma_subsampling == YuvChromaSubsampling::Yuv420 {
786 let iter;
787 #[cfg(feature = "rayon")]
788 {
789 iter = y_plane
790 .par_chunks_exact_mut(y_stride * 2)
791 .zip(u_plane.par_chunks_exact_mut(u_stride))
792 .zip(v_plane.par_chunks_exact_mut(v_stride))
793 .zip(rgba.par_chunks_exact(rgba_stride as usize * 2));
794 }
795 #[cfg(not(feature = "rayon"))]
796 {
797 iter = y_plane
798 .chunks_exact_mut(y_stride * 2)
799 .zip(u_plane.chunks_exact_mut(u_stride))
800 .zip(v_plane.chunks_exact_mut(v_stride))
801 .zip(rgba.chunks_exact(rgba_stride as usize * 2));
802 }
803 iter.for_each(|(((y_plane, u_plane), v_plane), rgba)| {
804 let (y_plane0, y_plane1) = y_plane.split_at_mut(y_stride);
805 let (rgba0, rgba1) = rgba.split_at(rgba_stride as usize);
806 process_double_chroma_row(
807 &mut y_plane0[0..image.width as usize],
808 &mut y_plane1[0..image.width as usize],
809 &mut u_plane[0..(image.width as usize).div_ceil(2)],
810 &mut v_plane[0..(image.width as usize).div_ceil(2)],
811 &rgba0[0..image.width as usize * channels],
812 &rgba1[0..image.width as usize * channels],
813 );
814 });
815
816 if image.height & 1 != 0 {
817 let remainder_y_plane = y_plane.chunks_exact_mut(y_stride * 2).into_remainder();
818 let remainder_rgba = rgba.chunks_exact(rgba_stride as usize * 2).remainder();
819 let u_plane = u_plane.chunks_exact_mut(u_stride).last().unwrap();
820 let v_plane = v_plane.chunks_exact_mut(v_stride).last().unwrap();
821 process_halved_chroma_row(
822 &mut remainder_y_plane[0..image.width as usize],
823 &mut u_plane[0..(image.width as usize).div_ceil(2)],
824 &mut v_plane[0..(image.width as usize).div_ceil(2)],
825 &remainder_rgba[0..image.width as usize * channels],
826 );
827 }
828 } else {
829 unreachable!();
830 }
831
832 Ok(())
833}
834
835macro_rules! d_cvn {
836 ($method: ident, $px_fmt: expr,
837 $sampling: expr,
838 $yuv_name: expr, $rgb_name: expr,
839 $rgb_small: expr, $bit_depth: expr,
840 $endianness: expr) => {
841 #[doc = concat!("Convert ", $rgb_name, " image data to ", $yuv_name, " format with ", $bit_depth, " bit depth.
842
843This function performs ", $rgb_name, stringify!($bit_depth), " to ",$yuv_name," conversion and stores the result in ", $yuv_name," format,
844with separate planes for Y (luminance), U (chrominance), and V (chrominance) components.
845
846# Arguments
847
848* `planar_image` - Target planar image.
849* `",$rgb_small,"` - The input ", $rgb_name," image data slice.
850* `",$rgb_small,"_stride` - The stride (components per row) for the ", $rgb_name ," image data.
851* `range` - The YUV range (limited or full).
852* `matrix` - The YUV standard matrix (BT.601 or BT.709 or BT.2020 or other).
853
854# Panics
855
856This function panics if the lengths of the planes or the input RGBA data are not valid based
857on the specified width, height, and strides, or if invalid YUV range or matrix is provided.
858")]
859 pub fn $method(
860 planar_image: &mut YuvPlanarImageMut<u16>,
861 rgba: &[u16],
862 rgba_stride: u32,
863 range: YuvRange,
864 matrix: YuvStandardMatrix,
865 ) -> Result<(), YuvError> {
866 rgbx_to_yuv_ant::<
867 { $px_fmt as u8 },
868 { $sampling as u8 },
869 { $endianness as u8 },
870 { YuvBytesPacking::LeastSignificantBytes as u8 },
871 $bit_depth,
872 15,
873 >(planar_image, rgba, rgba_stride, range, matrix,
874 RgbEncoder::<{ $px_fmt as u8 }, { $sampling as u8 }, { $endianness as u8 },
875 { YuvBytesPacking::LeastSignificantBytes as u8 }, $bit_depth, 15>::default(),
876 RgbEncoder420::<{ $px_fmt as u8 }, { $sampling as u8 }, { $endianness as u8 },
877 { YuvBytesPacking::LeastSignificantBytes as u8 }, $bit_depth, 15>::default())
878 }
879 };
880}
881
882d_cvn!(
883 rgba10_to_i010,
884 YuvSourceChannels::Rgba,
885 YuvChromaSubsampling::Yuv420,
886 "I010",
887 "RGBA10",
888 "rgba10",
889 10,
890 YuvEndianness::LittleEndian
891);
892#[cfg(feature = "big_endian")]
893d_cvn!(
894 rgba10_to_i010_be,
895 YuvSourceChannels::Rgba,
896 YuvChromaSubsampling::Yuv420,
897 "I010",
898 "RGBA10",
899 "rgba10",
900 10,
901 YuvEndianness::BigEndian
902);
903
904d_cvn!(
905 rgb10_to_i010,
906 YuvSourceChannels::Rgb,
907 YuvChromaSubsampling::Yuv420,
908 "I010",
909 "RGB10",
910 "rgb10",
911 10,
912 YuvEndianness::LittleEndian
913);
914#[cfg(feature = "big_endian")]
915d_cvn!(
916 rgb10_to_i010_be,
917 YuvSourceChannels::Rgb,
918 YuvChromaSubsampling::Yuv420,
919 "I010",
920 "RGB10",
921 "rgb10",
922 10,
923 YuvEndianness::BigEndian
924);
925
926d_cvn!(
927 rgba10_to_i210,
928 YuvSourceChannels::Rgba,
929 YuvChromaSubsampling::Yuv422,
930 "I210",
931 "RGBA10",
932 "rgba10",
933 10,
934 YuvEndianness::LittleEndian
935);
936#[cfg(feature = "big_endian")]
937d_cvn!(
938 rgba10_to_i210_be,
939 YuvSourceChannels::Rgba,
940 YuvChromaSubsampling::Yuv422,
941 "I210",
942 "RGBA10",
943 "rgba10",
944 10,
945 YuvEndianness::BigEndian
946);
947
948d_cvn!(
949 rgb10_to_i210,
950 YuvSourceChannels::Rgb,
951 YuvChromaSubsampling::Yuv422,
952 "I210",
953 "RGB10",
954 "rgb10",
955 10,
956 YuvEndianness::LittleEndian
957);
958#[cfg(feature = "big_endian")]
959d_cvn!(
960 rgb10_to_i210_be,
961 YuvSourceChannels::Rgb,
962 YuvChromaSubsampling::Yuv422,
963 "I210",
964 "RGB10",
965 "rgb10",
966 10,
967 YuvEndianness::BigEndian
968);
969
970d_cvn!(
971 rgba10_to_i410,
972 YuvSourceChannels::Rgba,
973 YuvChromaSubsampling::Yuv444,
974 "I410",
975 "RGBA10",
976 "rgba10",
977 10,
978 YuvEndianness::LittleEndian
979);
980#[cfg(feature = "big_endian")]
981d_cvn!(
982 rgba10_to_i410_be,
983 YuvSourceChannels::Rgba,
984 YuvChromaSubsampling::Yuv444,
985 "I410",
986 "RGBA10",
987 "rgba10",
988 10,
989 YuvEndianness::BigEndian
990);
991
992d_cvn!(
993 rgb10_to_i410,
994 YuvSourceChannels::Rgb,
995 YuvChromaSubsampling::Yuv444,
996 "I410",
997 "RGB10",
998 "rgb10",
999 10,
1000 YuvEndianness::LittleEndian
1001);
1002#[cfg(feature = "big_endian")]
1003d_cvn!(
1004 rgb10_to_i410_be,
1005 YuvSourceChannels::Rgb,
1006 YuvChromaSubsampling::Yuv444,
1007 "I410",
1008 "RGB10",
1009 "rgb10",
1010 10,
1011 YuvEndianness::BigEndian
1012);
1013
1014d_cvn!(
1015 rgba12_to_i012,
1016 YuvSourceChannels::Rgba,
1017 YuvChromaSubsampling::Yuv420,
1018 "I012",
1019 "RGBA12",
1020 "rgba12",
1021 12,
1022 YuvEndianness::LittleEndian
1023);
1024#[cfg(feature = "big_endian")]
1025d_cvn!(
1026 rgba12_to_i012_be,
1027 YuvSourceChannels::Rgba,
1028 YuvChromaSubsampling::Yuv420,
1029 "I012",
1030 "RGBA12",
1031 "rgba12",
1032 12,
1033 YuvEndianness::BigEndian
1034);
1035
1036d_cvn!(
1037 rgb12_to_i012,
1038 YuvSourceChannels::Rgb,
1039 YuvChromaSubsampling::Yuv420,
1040 "I012",
1041 "RGB12",
1042 "rgb12",
1043 12,
1044 YuvEndianness::LittleEndian
1045);
1046#[cfg(feature = "big_endian")]
1047d_cvn!(
1048 rgb12_to_i012_be,
1049 YuvSourceChannels::Rgb,
1050 YuvChromaSubsampling::Yuv420,
1051 "I012",
1052 "RGB12",
1053 "rgb12",
1054 12,
1055 YuvEndianness::BigEndian
1056);
1057
1058d_cvn!(
1059 rgba12_to_i212,
1060 YuvSourceChannels::Rgba,
1061 YuvChromaSubsampling::Yuv422,
1062 "I212",
1063 "RGBA12",
1064 "rgba12",
1065 12,
1066 YuvEndianness::LittleEndian
1067);
1068#[cfg(feature = "big_endian")]
1069d_cvn!(
1070 rgba12_to_i212_be,
1071 YuvSourceChannels::Rgba,
1072 YuvChromaSubsampling::Yuv420,
1073 "I212",
1074 "RGBA12",
1075 "rgba12",
1076 12,
1077 YuvEndianness::BigEndian
1078);
1079
1080d_cvn!(
1081 rgb12_to_i212,
1082 YuvSourceChannels::Rgb,
1083 YuvChromaSubsampling::Yuv422,
1084 "I212",
1085 "RGB12",
1086 "rgb12",
1087 12,
1088 YuvEndianness::LittleEndian
1089);
1090#[cfg(feature = "big_endian")]
1091d_cvn!(
1092 rgb12_to_i212_be,
1093 YuvSourceChannels::Rgb,
1094 YuvChromaSubsampling::Yuv422,
1095 "I212",
1096 "RGB12",
1097 "rgb12",
1098 12,
1099 YuvEndianness::BigEndian
1100);
1101
1102d_cvn!(
1103 rgba12_to_i412,
1104 YuvSourceChannels::Rgba,
1105 YuvChromaSubsampling::Yuv444,
1106 "I412",
1107 "RGBA12",
1108 "rgba12",
1109 12,
1110 YuvEndianness::LittleEndian
1111);
1112#[cfg(feature = "big_endian")]
1113d_cvn!(
1114 rgba12_to_i412_be,
1115 YuvSourceChannels::Rgba,
1116 YuvChromaSubsampling::Yuv444,
1117 "I412",
1118 "RGBA12",
1119 "rgba12",
1120 12,
1121 YuvEndianness::BigEndian
1122);
1123
1124d_cvn!(
1125 rgb12_to_i412,
1126 YuvSourceChannels::Rgb,
1127 YuvChromaSubsampling::Yuv444,
1128 "I412",
1129 "RGB12",
1130 "rgb12",
1131 12,
1132 YuvEndianness::LittleEndian
1133);
1134#[cfg(feature = "big_endian")]
1135d_cvn!(
1136 rgb12_to_i412_be,
1137 YuvSourceChannels::Rgb,
1138 YuvChromaSubsampling::Yuv444,
1139 "I412",
1140 "RGB12",
1141 "rgb12",
1142 12,
1143 YuvEndianness::BigEndian
1144);
1145d_cvn!(
1147 rgba14_to_i014,
1148 YuvSourceChannels::Rgba,
1149 YuvChromaSubsampling::Yuv420,
1150 "I014",
1151 "RGBA14",
1152 "rgba14",
1153 14,
1154 YuvEndianness::LittleEndian
1155);
1156#[cfg(feature = "big_endian")]
1157d_cvn!(
1158 rgba14_to_i014_be,
1159 YuvSourceChannels::Rgba,
1160 YuvChromaSubsampling::Yuv420,
1161 "I014",
1162 "RGBA14",
1163 "rgba14",
1164 14,
1165 YuvEndianness::BigEndian
1166);
1167
1168d_cvn!(
1169 rgb14_to_i014,
1170 YuvSourceChannels::Rgb,
1171 YuvChromaSubsampling::Yuv420,
1172 "I014",
1173 "RGB14",
1174 "rgb14",
1175 14,
1176 YuvEndianness::LittleEndian
1177);
1178#[cfg(feature = "big_endian")]
1179d_cvn!(
1180 rgb14_to_i014_be,
1181 YuvSourceChannels::Rgb,
1182 YuvChromaSubsampling::Yuv420,
1183 "I014",
1184 "RGB14",
1185 "rgb14",
1186 14,
1187 YuvEndianness::BigEndian
1188);
1189
1190d_cvn!(
1191 rgba14_to_i214,
1192 YuvSourceChannels::Rgba,
1193 YuvChromaSubsampling::Yuv422,
1194 "I214",
1195 "RGBA14",
1196 "rgba14",
1197 14,
1198 YuvEndianness::LittleEndian
1199);
1200#[cfg(feature = "big_endian")]
1201d_cvn!(
1202 rgba14_to_i214_be,
1203 YuvSourceChannels::Rgba,
1204 YuvChromaSubsampling::Yuv422,
1205 "I214",
1206 "RGBA14",
1207 "rgba14",
1208 14,
1209 YuvEndianness::BigEndian
1210);
1211d_cvn!(
1212 rgb14_to_i214,
1213 YuvSourceChannels::Rgb,
1214 YuvChromaSubsampling::Yuv422,
1215 "I214",
1216 "RGB14",
1217 "rgb14",
1218 14,
1219 YuvEndianness::LittleEndian
1220);
1221#[cfg(feature = "big_endian")]
1222d_cvn!(
1223 rgb14_to_i214_be,
1224 YuvSourceChannels::Rgb,
1225 YuvChromaSubsampling::Yuv422,
1226 "I214",
1227 "RGB14",
1228 "rgb14",
1229 14,
1230 YuvEndianness::BigEndian
1231);
1232
1233d_cvn!(
1234 rgba14_to_i414,
1235 YuvSourceChannels::Rgba,
1236 YuvChromaSubsampling::Yuv444,
1237 "I414",
1238 "RGBA14",
1239 "rgba14",
1240 14,
1241 YuvEndianness::LittleEndian
1242);
1243#[cfg(feature = "big_endian")]
1244d_cvn!(
1245 rgba14_to_i414_be,
1246 YuvSourceChannels::Rgba,
1247 YuvChromaSubsampling::Yuv444,
1248 "I414",
1249 "RGBA14",
1250 "rgba14",
1251 14,
1252 YuvEndianness::BigEndian
1253);
1254
1255d_cvn!(
1256 rgb14_to_i414,
1257 YuvSourceChannels::Rgb,
1258 YuvChromaSubsampling::Yuv444,
1259 "I414",
1260 "RGB14",
1261 "rgb14",
1262 14,
1263 YuvEndianness::LittleEndian
1264);
1265#[cfg(feature = "big_endian")]
1266d_cvn!(
1267 rgb14_to_i414_be,
1268 YuvSourceChannels::Rgb,
1269 YuvChromaSubsampling::Yuv444,
1270 "I414",
1271 "RGB14",
1272 "rgb14",
1273 14,
1274 YuvEndianness::BigEndian
1275);
1276d_cvn!(
1278 rgba16_to_i016,
1279 YuvSourceChannels::Rgba,
1280 YuvChromaSubsampling::Yuv420,
1281 "I016",
1282 "RGBA16",
1283 "rgba16",
1284 16,
1285 YuvEndianness::LittleEndian
1286);
1287#[cfg(feature = "big_endian")]
1288d_cvn!(
1289 rgba16_to_i016_be,
1290 YuvSourceChannels::Rgba,
1291 YuvChromaSubsampling::Yuv420,
1292 "I016",
1293 "RGBA16",
1294 "rgba16",
1295 16,
1296 YuvEndianness::BigEndian
1297);
1298
1299d_cvn!(
1300 rgb16_to_i016,
1301 YuvSourceChannels::Rgb,
1302 YuvChromaSubsampling::Yuv420,
1303 "I016",
1304 "RGB16",
1305 "rgb16",
1306 16,
1307 YuvEndianness::LittleEndian
1308);
1309#[cfg(feature = "big_endian")]
1310d_cvn!(
1311 rgb16_to_i016_be,
1312 YuvSourceChannels::Rgb,
1313 YuvChromaSubsampling::Yuv420,
1314 "I016",
1315 "RGB16",
1316 "rgb16",
1317 16,
1318 YuvEndianness::BigEndian
1319);
1320
1321d_cvn!(
1322 rgba16_to_i216,
1323 YuvSourceChannels::Rgba,
1324 YuvChromaSubsampling::Yuv422,
1325 "I216",
1326 "RGBA16",
1327 "rgba16",
1328 16,
1329 YuvEndianness::LittleEndian
1330);
1331#[cfg(feature = "big_endian")]
1332d_cvn!(
1333 rgba16_to_i216_be,
1334 YuvSourceChannels::Rgba,
1335 YuvChromaSubsampling::Yuv422,
1336 "I216",
1337 "RGBA16",
1338 "rgba16",
1339 16,
1340 YuvEndianness::BigEndian
1341);
1342d_cvn!(
1343 rgb16_to_i216,
1344 YuvSourceChannels::Rgb,
1345 YuvChromaSubsampling::Yuv422,
1346 "I216",
1347 "RGB16",
1348 "rgb16",
1349 16,
1350 YuvEndianness::LittleEndian
1351);
1352#[cfg(feature = "big_endian")]
1353d_cvn!(
1354 rgb16_to_i216_be,
1355 YuvSourceChannels::Rgb,
1356 YuvChromaSubsampling::Yuv422,
1357 "I216",
1358 "RGB16",
1359 "rgb16",
1360 16,
1361 YuvEndianness::BigEndian
1362);
1363
1364d_cvn!(
1365 rgba16_to_i416,
1366 YuvSourceChannels::Rgba,
1367 YuvChromaSubsampling::Yuv444,
1368 "I416",
1369 "RGBA16",
1370 "rgba16",
1371 16,
1372 YuvEndianness::LittleEndian
1373);
1374#[cfg(feature = "big_endian")]
1375d_cvn!(
1376 rgba16_to_i416_be,
1377 YuvSourceChannels::Rgba,
1378 YuvChromaSubsampling::Yuv444,
1379 "I416",
1380 "RGBA16",
1381 "rgba16",
1382 16,
1383 YuvEndianness::BigEndian
1384);
1385
1386d_cvn!(
1387 rgb16_to_i416,
1388 YuvSourceChannels::Rgb,
1389 YuvChromaSubsampling::Yuv444,
1390 "I416",
1391 "RGB16",
1392 "rgb16",
1393 16,
1394 YuvEndianness::LittleEndian
1395);
1396#[cfg(feature = "big_endian")]
1397d_cvn!(
1398 rgb16_to_i416_be,
1399 YuvSourceChannels::Rgb,
1400 YuvChromaSubsampling::Yuv444,
1401 "I416",
1402 "RGB16",
1403 "rgb16",
1404 16,
1405 YuvEndianness::BigEndian
1406);