1use crate::internals::ProcessedOffset;
30use crate::numerics::qrshr;
31use crate::yuv_error::check_rgba_destination;
32use crate::yuv_support::*;
33use crate::{YuvError, YuvPlanarImage};
34use num_traits::AsPrimitive;
35#[cfg(feature = "rayon")]
36use rayon::iter::{IndexedParallelIterator, ParallelIterator};
37#[cfg(feature = "rayon")]
38use rayon::prelude::{ParallelSlice, ParallelSliceMut};
39use std::fmt::Debug;
40use std::ops::Sub;
41
42trait CgCoWideRowInversionHandler<V> {
43 fn handle_row(
44 &self,
45 y_plane: &[V],
46 u_plane: &[V],
47 v_plane: &[V],
48 rgba: &mut [V],
49 width: u32,
50 chroma_range: YuvChromaRange,
51 ) -> ProcessedOffset;
52}
53
54trait CgCoWideRowInversionHandler420<V> {
55 fn handle_row420(
56 &self,
57 y_plane0: &[V],
58 y_plane1: &[V],
59 u_plane: &[V],
60 v_plane: &[V],
61 rgba0: &mut [V],
62 rgba1: &mut [V],
63 width: u32,
64 chroma_range: YuvChromaRange,
65 ) -> ProcessedOffset;
66}
67
68type RgbHandler = unsafe fn(
69 y_plane: &[u8],
70 u_plane: &[u8],
71 v_plane: &[u8],
72 rgba: &mut [u8],
73 width: usize,
74 chroma_range: YuvChromaRange,
75) -> ProcessedOffset;
76
77type RgbHandler420 = unsafe fn(
78 y_plane0: &[u8],
79 y_plane1: &[u8],
80 u_plane: &[u8],
81 v_plane: &[u8],
82 rgba0: &mut [u8],
83 rgba1: &mut [u8],
84 width: u32,
85 chroma_range: YuvChromaRange,
86) -> ProcessedOffset;
87
88struct Rgb8Converter<const DESTINATION_CHANNELS: u8, const SAMPLING: u8> {
89 handler: Option<RgbHandler>,
90}
91
92struct Rgb8Converter420<const DESTINATION_CHANNELS: u8, const SAMPLING: u8> {
93 handler: Option<RgbHandler420>,
94}
95
96impl<const DESTINATION_CHANNELS: u8, const SAMPLING: u8>
97 Rgb8Converter<DESTINATION_CHANNELS, SAMPLING>
98{
99 fn new(range: YuvRange) -> Self {
100 if range == YuvRange::Full {
101 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
102 {
103 use crate::neon::neon_ycgco_full_range_to_rgb;
104 return Rgb8Converter {
105 handler: Some(neon_ycgco_full_range_to_rgb::<DESTINATION_CHANNELS, SAMPLING>),
106 };
107 }
108 }
109 Self { handler: None }
110 }
111}
112
113impl<const DESTINATION_CHANNELS: u8, const SAMPLING: u8>
114 Rgb8Converter420<DESTINATION_CHANNELS, SAMPLING>
115{
116 fn new(range: YuvRange) -> Self {
117 let sampling: YuvChromaSubsampling = SAMPLING.into();
118 if sampling != YuvChromaSubsampling::Yuv420 {
119 return Self { handler: None };
120 }
121 assert_eq!(sampling, YuvChromaSubsampling::Yuv420);
122 if range == YuvRange::Full {
123 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
124 {
125 use crate::neon::neon_ycgco420_to_rgba_row;
126 return Rgb8Converter420 {
127 handler: Some(neon_ycgco420_to_rgba_row::<DESTINATION_CHANNELS>),
128 };
129 }
130 }
131 Self { handler: None }
132 }
133}
134
135struct Rgb16Converter<const DESTINATION_CHANNELS: u8, const SAMPLING: u8> {}
136
137impl<const DESTINATION_CHANNELS: u8, const SAMPLING: u8> CgCoWideRowInversionHandler<u16>
138 for Rgb16Converter<DESTINATION_CHANNELS, SAMPLING>
139{
140 fn handle_row(
141 &self,
142 _: &[u16],
143 _: &[u16],
144 _: &[u16],
145 _: &mut [u16],
146 _: u32,
147 _: YuvChromaRange,
148 ) -> ProcessedOffset {
149 ProcessedOffset { cx: 0, ux: 0 }
150 }
151}
152
153struct Rgb16Converter420<const DESTINATION_CHANNELS: u8, const SAMPLING: u8> {}
154
155impl<const DESTINATION_CHANNELS: u8, const SAMPLING: u8> CgCoWideRowInversionHandler420<u16>
156 for Rgb16Converter420<DESTINATION_CHANNELS, SAMPLING>
157{
158 fn handle_row420(
159 &self,
160 _: &[u16],
161 _: &[u16],
162 _: &[u16],
163 _: &[u16],
164 _: &mut [u16],
165 _: &mut [u16],
166 _: u32,
167 _: YuvChromaRange,
168 ) -> ProcessedOffset {
169 ProcessedOffset { cx: 0, ux: 0 }
170 }
171}
172
173impl<const DESTINATION_CHANNELS: u8, const SAMPLING: u8> CgCoWideRowInversionHandler<u8>
174 for Rgb8Converter<DESTINATION_CHANNELS, SAMPLING>
175{
176 fn handle_row(
177 &self,
178 y_plane: &[u8],
179 u_plane: &[u8],
180 v_plane: &[u8],
181 rgba: &mut [u8],
182 width: u32,
183 chroma_range: YuvChromaRange,
184 ) -> ProcessedOffset {
185 if let Some(handler) = self.handler {
186 unsafe {
187 return handler(
188 y_plane,
189 u_plane,
190 v_plane,
191 rgba,
192 width as usize,
193 chroma_range,
194 );
195 }
196 }
197 ProcessedOffset { cx: 0, ux: 0 }
198 }
199}
200
201impl<const DESTINATION_CHANNELS: u8, const SAMPLING: u8> CgCoWideRowInversionHandler420<u8>
202 for Rgb8Converter420<DESTINATION_CHANNELS, SAMPLING>
203{
204 fn handle_row420(
205 &self,
206 y_plane0: &[u8],
207 y_plane1: &[u8],
208 u_plane: &[u8],
209 v_plane: &[u8],
210 rgba0: &mut [u8],
211 rgba1: &mut [u8],
212 width: u32,
213 chroma_range: YuvChromaRange,
214 ) -> ProcessedOffset {
215 if let Some(handler) = self.handler {
216 unsafe {
217 return handler(
218 y_plane0,
219 y_plane1,
220 u_plane,
221 v_plane,
222 rgba0,
223 rgba1,
224 width,
225 chroma_range,
226 );
227 }
228 }
229 ProcessedOffset { cx: 0, ux: 0 }
230 }
231}
232
233trait YCgCoConverterFactory<V> {
234 fn make_converter<const DESTINATION_CHANNELS: u8, const SAMPLING: u8>(
235 range: YuvRange,
236 bit_depth: usize,
237 ) -> Box<dyn CgCoWideRowInversionHandler<V> + Sync + Send>;
238
239 fn make_converter420<const DESTINATION_CHANNELS: u8, const SAMPLING: u8>(
240 range: YuvRange,
241 bit_depth: usize,
242 ) -> Box<dyn CgCoWideRowInversionHandler420<V> + Sync + Send>;
243}
244
245impl YCgCoConverterFactory<u8> for u8 {
246 fn make_converter<const DESTINATION_CHANNELS: u8, const SAMPLING: u8>(
247 range: YuvRange,
248 _: usize,
249 ) -> Box<dyn CgCoWideRowInversionHandler<u8> + Sync + Send> {
250 Box::new(Rgb8Converter::<DESTINATION_CHANNELS, SAMPLING>::new(range))
251 }
252
253 fn make_converter420<const DESTINATION_CHANNELS: u8, const SAMPLING: u8>(
254 range: YuvRange,
255 _: usize,
256 ) -> Box<dyn CgCoWideRowInversionHandler420<u8> + Sync + Send> {
257 Box::new(Rgb8Converter420::<DESTINATION_CHANNELS, SAMPLING>::new(
258 range,
259 ))
260 }
261}
262
263impl YCgCoConverterFactory<u16> for u16 {
264 fn make_converter<const DESTINATION_CHANNELS: u8, const SAMPLING: u8>(
265 _: YuvRange,
266 _: usize,
267 ) -> Box<dyn CgCoWideRowInversionHandler<u16> + Sync + Send> {
268 Box::new(Rgb16Converter::<DESTINATION_CHANNELS, SAMPLING> {})
269 }
270
271 fn make_converter420<const DESTINATION_CHANNELS: u8, const SAMPLING: u8>(
272 _: YuvRange,
273 _: usize,
274 ) -> Box<dyn CgCoWideRowInversionHandler420<u16> + Sync + Send> {
275 Box::new(Rgb16Converter420::<DESTINATION_CHANNELS, SAMPLING> {})
276 }
277}
278
279fn ycgco_ro_rgbx<
280 V: AsPrimitive<J> + 'static + Default + Debug + Sync + Send + YCgCoConverterFactory<V>,
281 J: Copy + AsPrimitive<i32> + 'static + Sub<Output = J> + Send + Sync,
282 const DESTINATION_CHANNELS: u8,
283 const SAMPLING: u8,
284 const BIT_DEPTH: usize,
285>(
286 image: &YuvPlanarImage<V>,
287 rgba: &mut [V],
288 rgba_stride: u32,
289 range: YuvRange,
290) -> Result<(), YuvError>
291where
292 i32: AsPrimitive<V>,
293 u32: AsPrimitive<J>,
294{
295 let chroma_subsampling: YuvChromaSubsampling = SAMPLING.into();
296 let dst_chans: YuvSourceChannels = DESTINATION_CHANNELS.into();
297 let channels = dst_chans.get_channels_count();
298
299 check_rgba_destination(rgba, rgba_stride, image.width, image.height, channels)?;
300 image.check_constraints(chroma_subsampling)?;
301
302 let chroma_range = get_yuv_range(BIT_DEPTH as u32, range);
303 let bias_y: J = chroma_range.bias_y.as_();
304 let bias_uv: J = chroma_range.bias_uv.as_();
305
306 const PRECISION: i32 = 13;
307
308 let max_colors = (1 << BIT_DEPTH) - 1i32;
309 let precision_scale = (1 << PRECISION) as f32;
310
311 let range_reduction_y =
312 (max_colors as f32 / chroma_range.range_y as f32 * precision_scale).round() as i16;
313 let range_reduction_uv =
314 (max_colors as f32 / chroma_range.range_uv as f32 * precision_scale).round() as i16;
315
316 let converter = V::make_converter::<DESTINATION_CHANNELS, SAMPLING>(range, BIT_DEPTH);
317 let converter420 = V::make_converter420::<DESTINATION_CHANNELS, SAMPLING>(range, BIT_DEPTH);
318
319 let process_halved_chroma_row =
320 |y_plane: &[V], u_plane: &[V], v_plane: &[V], rgba: &mut [V]| {
321 let processed_offset =
322 converter.handle_row(y_plane, u_plane, v_plane, rgba, image.width, chroma_range);
323 if processed_offset.cx != image.width as usize {
324 for (((rgba, y_src), &u_src), &v_src) in rgba
325 .chunks_exact_mut(channels * 2)
326 .zip(y_plane.chunks_exact(2))
327 .zip(u_plane.iter())
328 .zip(v_plane.iter())
329 .skip(processed_offset.cx)
330 {
331 let y_value0 = (y_src[0].as_() - bias_y).as_() * range_reduction_y as i32;
332 let cb_value = (u_src.as_() - bias_uv).as_() * range_reduction_uv as i32;
333 let cr_value = (v_src.as_() - bias_uv).as_() * range_reduction_uv as i32;
334
335 let t0 = y_value0 - cb_value;
336
337 let r0 = qrshr::<PRECISION, BIT_DEPTH>(t0 + cr_value);
338 let b0 = qrshr::<PRECISION, BIT_DEPTH>(t0 - cr_value);
339 let g0 = qrshr::<PRECISION, BIT_DEPTH>(y_value0 + cb_value);
340
341 let rgba0 = &mut rgba[0..channels];
342
343 rgba0[dst_chans.get_r_channel_offset()] = r0.as_();
344 rgba0[dst_chans.get_g_channel_offset()] = g0.as_();
345 rgba0[dst_chans.get_b_channel_offset()] = b0.as_();
346 if dst_chans.has_alpha() {
347 rgba0[dst_chans.get_a_channel_offset()] = max_colors.as_();
348 }
349
350 let y_value1 = (y_src[1].as_() - bias_y).as_() * range_reduction_y as i32;
351
352 let t1 = y_value1 - cb_value;
353
354 let r1 = qrshr::<PRECISION, BIT_DEPTH>(t1 + cr_value);
355 let b1 = qrshr::<PRECISION, BIT_DEPTH>(t1 - cr_value);
356 let g1 = qrshr::<PRECISION, BIT_DEPTH>(y_value1 + cb_value);
357
358 let rgba1 = &mut rgba[channels..channels * 2];
359
360 rgba1[dst_chans.get_r_channel_offset()] = r1.as_();
361 rgba1[dst_chans.get_g_channel_offset()] = g1.as_();
362 rgba1[dst_chans.get_b_channel_offset()] = b1.as_();
363 if dst_chans.has_alpha() {
364 rgba1[dst_chans.get_a_channel_offset()] = max_colors.as_();
365 }
366 }
367
368 if image.width & 1 != 0 {
369 let y_value0 =
370 (y_plane.last().unwrap().as_() - bias_y).as_() * range_reduction_y as i32;
371 let cb_value =
372 (u_plane.last().unwrap().as_() - bias_uv).as_() * range_reduction_uv as i32;
373 let cr_value =
374 (v_plane.last().unwrap().as_() - bias_uv).as_() * range_reduction_uv as i32;
375 let rgba = rgba.chunks_exact_mut(channels).last().unwrap();
376 let rgba0 = &mut rgba[0..channels];
377
378 let t0 = y_value0 - cb_value;
379
380 let r0 = qrshr::<PRECISION, BIT_DEPTH>(t0 + cr_value);
381 let b0 = qrshr::<PRECISION, BIT_DEPTH>(t0 - cr_value);
382 let g0 = qrshr::<PRECISION, BIT_DEPTH>(y_value0 + cb_value);
383 rgba0[dst_chans.get_r_channel_offset()] = r0.as_();
384 rgba0[dst_chans.get_g_channel_offset()] = g0.as_();
385 rgba0[dst_chans.get_b_channel_offset()] = b0.as_();
386 if dst_chans.has_alpha() {
387 rgba0[dst_chans.get_a_channel_offset()] = max_colors.as_();
388 }
389 }
390 }
391 };
392
393 let process_doubled_chroma_row = |y_plane0: &[V],
394 y_plane1: &[V],
395 u_plane: &[V],
396 v_plane: &[V],
397 rgba0: &mut [V],
398 rgba1: &mut [V]| {
399 let processed_offset420 = converter420
400 .handle_row420(
401 y_plane0,
402 y_plane1,
403 u_plane,
404 v_plane,
405 rgba0,
406 rgba1,
407 image.width,
408 chroma_range,
409 )
410 .cx;
411 if processed_offset420 != image.width as usize {
412 for (((((rgba0, rgba1), y_src0), y_src1), &u_src), &v_src) in rgba0
413 .chunks_exact_mut(channels * 2)
414 .zip(rgba1.chunks_exact_mut(channels * 2))
415 .zip(y_plane0.chunks_exact(2))
416 .zip(y_plane1.chunks_exact(2))
417 .zip(u_plane.iter())
418 .zip(v_plane.iter())
419 {
420 let y_value0 = (y_src0[0].as_() - bias_y).as_() * range_reduction_y as i32;
421 let y_value1 = (y_src0[1].as_() - bias_y).as_() * range_reduction_y as i32;
422
423 let cb_value = (u_src.as_() - bias_uv).as_() * range_reduction_uv as i32;
424 let cr_value = (v_src.as_() - bias_uv).as_() * range_reduction_uv as i32;
425
426 let y_value10 = (y_src1[0].as_() - bias_y).as_() * range_reduction_y as i32;
427 let y_value11 = (y_src1[1].as_() - bias_y).as_() * range_reduction_y as i32;
428
429 let t0 = y_value0 - cb_value;
430
431 let r0 = qrshr::<PRECISION, BIT_DEPTH>(t0 + cr_value);
432 let b0 = qrshr::<PRECISION, BIT_DEPTH>(t0 - cr_value);
433 let g0 = qrshr::<PRECISION, BIT_DEPTH>(y_value0 + cb_value);
434
435 let rgba00 = &mut rgba0[0..channels];
436
437 rgba00[dst_chans.get_r_channel_offset()] = r0.as_();
438 rgba00[dst_chans.get_g_channel_offset()] = g0.as_();
439 rgba00[dst_chans.get_b_channel_offset()] = b0.as_();
440 if dst_chans.has_alpha() {
441 rgba00[dst_chans.get_a_channel_offset()] = max_colors.as_();
442 }
443
444 let t1 = y_value1 - cb_value;
445
446 let r1 = qrshr::<PRECISION, BIT_DEPTH>(t1 + cr_value);
447 let b1 = qrshr::<PRECISION, BIT_DEPTH>(t1 - cr_value);
448 let g1 = qrshr::<PRECISION, BIT_DEPTH>(y_value1 + cb_value);
449
450 let rgba01 = &mut rgba0[channels..channels * 2];
451
452 rgba01[dst_chans.get_r_channel_offset()] = r1.as_();
453 rgba01[dst_chans.get_g_channel_offset()] = g1.as_();
454 rgba01[dst_chans.get_b_channel_offset()] = b1.as_();
455 if dst_chans.has_alpha() {
456 rgba01[dst_chans.get_a_channel_offset()] = max_colors.as_();
457 }
458
459 let t10 = y_value10 - cb_value;
460
461 let r10 = qrshr::<PRECISION, BIT_DEPTH>(t10 + cr_value);
462 let b10 = qrshr::<PRECISION, BIT_DEPTH>(t10 - cr_value);
463 let g10 = qrshr::<PRECISION, BIT_DEPTH>(y_value10 + cb_value);
464
465 let rgba10 = &mut rgba1[0..channels];
466
467 rgba10[dst_chans.get_r_channel_offset()] = r10.as_();
468 rgba10[dst_chans.get_g_channel_offset()] = g10.as_();
469 rgba10[dst_chans.get_b_channel_offset()] = b10.as_();
470 if dst_chans.has_alpha() {
471 rgba10[dst_chans.get_a_channel_offset()] = max_colors.as_();
472 }
473
474 let t11 = y_value11 - cb_value;
475
476 let r11 = qrshr::<PRECISION, BIT_DEPTH>(t11 + cr_value);
477 let b11 = qrshr::<PRECISION, BIT_DEPTH>(t11 - cr_value);
478 let g11 = qrshr::<PRECISION, BIT_DEPTH>(y_value11 + cb_value);
479
480 let rgba11 = &mut rgba1[channels..channels * 2];
481
482 rgba11[dst_chans.get_r_channel_offset()] = r11.as_();
483 rgba11[dst_chans.get_g_channel_offset()] = g11.as_();
484 rgba11[dst_chans.get_b_channel_offset()] = b11.as_();
485 if dst_chans.has_alpha() {
486 rgba11[dst_chans.get_a_channel_offset()] = max_colors.as_();
487 }
488 }
489
490 if image.width & 1 != 0 {
491 let y_value0 =
492 (y_plane0.last().unwrap().as_() - bias_y).as_() * range_reduction_y as i32;
493 let y_value1 =
494 (y_plane1.last().unwrap().as_() - bias_y).as_() * range_reduction_y as i32;
495 let cb_value =
496 (u_plane.last().unwrap().as_() - bias_uv).as_() * range_reduction_uv as i32;
497 let cr_value =
498 (v_plane.last().unwrap().as_() - bias_uv).as_() * range_reduction_uv as i32;
499 let rgba = rgba0.chunks_exact_mut(channels).last().unwrap();
500 let rgba0 = &mut rgba[0..channels];
501
502 let t0 = y_value0 - cb_value;
503
504 let r0 = qrshr::<PRECISION, BIT_DEPTH>(t0 + cr_value);
505 let b0 = qrshr::<PRECISION, BIT_DEPTH>(t0 - cr_value);
506 let g0 = qrshr::<PRECISION, BIT_DEPTH>(y_value0 + cb_value);
507
508 rgba0[dst_chans.get_r_channel_offset()] = r0.as_();
509 rgba0[dst_chans.get_g_channel_offset()] = g0.as_();
510 rgba0[dst_chans.get_b_channel_offset()] = b0.as_();
511 if dst_chans.has_alpha() {
512 rgba0[dst_chans.get_a_channel_offset()] = max_colors.as_();
513 }
514
515 let t1 = y_value1 - cb_value;
516
517 let r1 = qrshr::<PRECISION, BIT_DEPTH>(t1 + cr_value);
518 let b1 = qrshr::<PRECISION, BIT_DEPTH>(t1 - cr_value);
519 let g1 = qrshr::<PRECISION, BIT_DEPTH>(y_value1 + cb_value);
520
521 let rgba = rgba1.chunks_exact_mut(channels).last().unwrap();
522 let rgba1 = &mut rgba[0..channels];
523 rgba1[dst_chans.get_r_channel_offset()] = r1.as_();
524 rgba1[dst_chans.get_g_channel_offset()] = g1.as_();
525 rgba1[dst_chans.get_b_channel_offset()] = b1.as_();
526 if dst_chans.has_alpha() {
527 rgba1[dst_chans.get_a_channel_offset()] = max_colors.as_();
528 }
529 }
530 }
531 };
532
533 if chroma_subsampling == YuvChromaSubsampling::Yuv444 {
534 let iter;
535 #[cfg(feature = "rayon")]
536 {
537 iter = rgba
538 .par_chunks_exact_mut(rgba_stride as usize)
539 .zip(image.y_plane.par_chunks_exact(image.y_stride as usize))
540 .zip(image.u_plane.par_chunks_exact(image.u_stride as usize))
541 .zip(image.v_plane.par_chunks_exact(image.v_stride as usize));
542 }
543 #[cfg(not(feature = "rayon"))]
544 {
545 iter = rgba
546 .chunks_exact_mut(rgba_stride as usize)
547 .zip(image.y_plane.chunks_exact(image.y_stride as usize))
548 .zip(image.u_plane.chunks_exact(image.u_stride as usize))
549 .zip(image.v_plane.chunks_exact(image.v_stride as usize));
550 }
551 iter.for_each(|(((rgba, y_plane), u_plane), v_plane)| {
552 let y_plane = &y_plane[0..image.width as usize];
553 let processed_offset =
554 converter.handle_row(y_plane, u_plane, v_plane, rgba, image.width, chroma_range);
555 if processed_offset.cx != image.width as usize {
556 for (((rgba, &y_src), &u_src), &v_src) in rgba
557 .chunks_exact_mut(channels)
558 .zip(y_plane.iter())
559 .zip(u_plane.iter())
560 .zip(v_plane.iter())
561 {
562 let y_value = (y_src.as_() - bias_y).as_() * range_reduction_y as i32;
563 let cb_value = (u_src.as_() - bias_uv).as_() * range_reduction_uv as i32;
564 let cr_value = (v_src.as_() - bias_uv).as_() * range_reduction_uv as i32;
565
566 let t0 = y_value - cb_value;
567
568 let r = qrshr::<PRECISION, BIT_DEPTH>(t0 + cr_value);
569 let b = qrshr::<PRECISION, BIT_DEPTH>(t0 - cr_value);
570 let g = qrshr::<PRECISION, BIT_DEPTH>(y_value + cb_value);
571
572 rgba[dst_chans.get_r_channel_offset()] = r.as_();
573 rgba[dst_chans.get_g_channel_offset()] = g.as_();
574 rgba[dst_chans.get_b_channel_offset()] = b.as_();
575 if dst_chans.has_alpha() {
576 rgba[dst_chans.get_a_channel_offset()] = max_colors.as_();
577 }
578 }
579 }
580 });
581 } else if chroma_subsampling == YuvChromaSubsampling::Yuv422 {
582 let iter;
583 #[cfg(feature = "rayon")]
584 {
585 iter = rgba
586 .par_chunks_exact_mut(rgba_stride as usize)
587 .zip(image.y_plane.par_chunks_exact(image.y_stride as usize))
588 .zip(image.u_plane.par_chunks_exact(image.u_stride as usize))
589 .zip(image.v_plane.par_chunks_exact(image.v_stride as usize));
590 }
591 #[cfg(not(feature = "rayon"))]
592 {
593 iter = rgba
594 .chunks_exact_mut(rgba_stride as usize)
595 .zip(image.y_plane.chunks_exact(image.y_stride as usize))
596 .zip(image.u_plane.chunks_exact(image.u_stride as usize))
597 .zip(image.v_plane.chunks_exact(image.v_stride as usize));
598 }
599 iter.for_each(|(((rgba, y_plane), u_plane), v_plane)| {
600 process_halved_chroma_row(
601 &y_plane[0..image.width as usize],
602 &u_plane[0..(image.width as usize).div_ceil(2)],
603 &v_plane[0..(image.width as usize).div_ceil(2)],
604 &mut rgba[0..image.width as usize * channels],
605 );
606 });
607 } else if chroma_subsampling == YuvChromaSubsampling::Yuv420 {
608 let iter;
609 #[cfg(feature = "rayon")]
610 {
611 iter = rgba
612 .par_chunks_exact_mut(rgba_stride as usize * 2)
613 .zip(image.y_plane.par_chunks_exact(image.y_stride as usize * 2))
614 .zip(image.u_plane.par_chunks_exact(image.u_stride as usize))
615 .zip(image.v_plane.par_chunks_exact(image.v_stride as usize));
616 }
617 #[cfg(not(feature = "rayon"))]
618 {
619 iter = rgba
620 .chunks_exact_mut(rgba_stride as usize * 2)
621 .zip(image.y_plane.chunks_exact(image.y_stride as usize * 2))
622 .zip(image.u_plane.chunks_exact(image.u_stride as usize))
623 .zip(image.v_plane.chunks_exact(image.v_stride as usize));
624 }
625 iter.for_each(|(((rgba, y_plane), u_plane), v_plane)| {
626 let (rgba0, rgba1) = rgba.split_at_mut(rgba_stride as usize);
627 let (y_plane0, y_plane1) = y_plane.split_at(image.y_stride as usize);
628 process_doubled_chroma_row(
629 &y_plane0[0..image.width as usize],
630 &y_plane1[0..image.width as usize],
631 &u_plane[0..(image.width as usize).div_ceil(2)],
632 &v_plane[0..(image.width as usize).div_ceil(2)],
633 &mut rgba0[0..image.width as usize * channels],
634 &mut rgba1[0..image.width as usize * channels],
635 );
636 });
637
638 if image.height & 1 != 0 {
639 let rgba = rgba.chunks_exact_mut(rgba_stride as usize).last().unwrap();
640 let u_plane = image
641 .u_plane
642 .chunks_exact(image.u_stride as usize)
643 .last()
644 .unwrap();
645 let v_plane = image
646 .v_plane
647 .chunks_exact(image.v_stride as usize)
648 .last()
649 .unwrap();
650 let y_plane = image
651 .y_plane
652 .chunks_exact(image.y_stride as usize)
653 .last()
654 .unwrap();
655 process_halved_chroma_row(
656 &y_plane[0..image.width as usize],
657 &u_plane[0..(image.width as usize).div_ceil(2)],
658 &v_plane[0..(image.width as usize).div_ceil(2)],
659 &mut rgba[0..image.width as usize * channels],
660 );
661 }
662 } else {
663 unreachable!();
664 }
665
666 Ok(())
667}
668
669macro_rules! d_cnv {
670 ($method: ident, $clazz: ident, $bp: expr, $cn: expr, $subsampling: expr, $rgb_name: expr, $yuv_name: expr, $intermediate: ident) => {
671 #[doc = concat!("Convert ", $yuv_name," planar format to ", $rgb_name, stringify!($bp)," format.
672
673This function takes ", $yuv_name," planar format data with ", stringify!($bp),"-bit precision,
674and converts it to ", $rgb_name, stringify!($bp)," format with ", stringify!($bp),"-bit per channel precision.
675
676# Arguments
677
678* `image` - Source ",$yuv_name," image.
679* `dst` - A mutable slice to store the converted ", $rgb_name, stringify!($bp)," data.
680* `dst_stride` - Elements per row.
681* `range` - The YUV range (limited or full).
682
683# Panics
684
685This function panics if the lengths of the planes or the input ", $rgb_name, stringify!($bp)," data are not valid based
686on the specified width, height, and strides, or if invalid YUV range or matrix is provided.")]
687 pub fn $method(
688 image: &YuvPlanarImage<$clazz>,
689 dst: &mut [$clazz],
690 dst_stride: u32,
691 range: YuvRange,
692 ) -> Result<(), YuvError> {
693 ycgco_ro_rgbx::<$clazz, $intermediate, { $cn as u8 }, { $subsampling as u8 }, $bp>(
694 image,
695 dst,
696 dst_stride,
697 range,
698 )
699 }
700 };
701}
702
703d_cnv!(
704 ycgco420_to_rgb,
705 u8,
706 8,
707 YuvSourceChannels::Rgb,
708 YuvChromaSubsampling::Yuv420,
709 "RGB",
710 "YCgCo 420",
711 i16
712);
713d_cnv!(
714 ycgco420_to_bgr,
715 u8,
716 8,
717 YuvSourceChannels::Bgr,
718 YuvChromaSubsampling::Yuv420,
719 "BGR",
720 "YCgCo 420",
721 i16
722);
723d_cnv!(
724 ycgco420_to_rgba,
725 u8,
726 8,
727 YuvSourceChannels::Rgba,
728 YuvChromaSubsampling::Yuv420,
729 "RGBA",
730 "YCgCo 420",
731 i16
732);
733d_cnv!(
734 ycgco420_to_bgra,
735 u8,
736 8,
737 YuvSourceChannels::Bgra,
738 YuvChromaSubsampling::Yuv420,
739 "BGRA",
740 "YCgCo 420",
741 i16
742);
743
744d_cnv!(
745 ycgco422_to_rgb,
746 u8,
747 8,
748 YuvSourceChannels::Rgb,
749 YuvChromaSubsampling::Yuv422,
750 "RGB",
751 "YCgCo 422",
752 i16
753);
754d_cnv!(
755 ycgco422_to_bgr,
756 u8,
757 8,
758 YuvSourceChannels::Bgr,
759 YuvChromaSubsampling::Yuv422,
760 "BGR",
761 "YCgCo 422",
762 i16
763);
764d_cnv!(
765 ycgco422_to_rgba,
766 u8,
767 8,
768 YuvSourceChannels::Rgba,
769 YuvChromaSubsampling::Yuv422,
770 "RGBA",
771 "YCgCo 422",
772 i16
773);
774d_cnv!(
775 ycgco422_to_bgra,
776 u8,
777 8,
778 YuvSourceChannels::Bgra,
779 YuvChromaSubsampling::Yuv422,
780 "BGRA",
781 "YCgCo 422",
782 i16
783);
784
785d_cnv!(
786 ycgco444_to_rgb,
787 u8,
788 8,
789 YuvSourceChannels::Rgb,
790 YuvChromaSubsampling::Yuv444,
791 "RGB",
792 "YCgCo 444",
793 i16
794);
795d_cnv!(
796 ycgco444_to_bgr,
797 u8,
798 8,
799 YuvSourceChannels::Bgr,
800 YuvChromaSubsampling::Yuv444,
801 "BGR",
802 "YCgCo 444",
803 i16
804);
805d_cnv!(
806 ycgco444_to_rgba,
807 u8,
808 8,
809 YuvSourceChannels::Rgba,
810 YuvChromaSubsampling::Yuv444,
811 "RGBA",
812 "YCgCo 444",
813 i16
814);
815d_cnv!(
816 ycgco444_to_bgra,
817 u8,
818 8,
819 YuvSourceChannels::Bgra,
820 YuvChromaSubsampling::Yuv444,
821 "BGRA",
822 "YCgCo 444",
823 i16
824);
825
826d_cnv!(
827 icgc010_to_rgb10,
828 u16,
829 10,
830 YuvSourceChannels::Rgb,
831 YuvChromaSubsampling::Yuv420,
832 "RGB",
833 "YCgCo 4:2:0 10-bit",
834 i16
835);
836d_cnv!(
837 icgc010_to_rgba10,
838 u16,
839 10,
840 YuvSourceChannels::Rgba,
841 YuvChromaSubsampling::Yuv420,
842 "RGBA",
843 "YCgCo 4:2:0 10-bit",
844 i16
845);
846d_cnv!(
847 icgc210_to_rgb10,
848 u16,
849 10,
850 YuvSourceChannels::Rgb,
851 YuvChromaSubsampling::Yuv422,
852 "RGB",
853 "YCgCo 4:2:2 10-bit",
854 i16
855);
856d_cnv!(
857 icgc210_to_rgba10,
858 u16,
859 10,
860 YuvSourceChannels::Rgba,
861 YuvChromaSubsampling::Yuv422,
862 "RGBA",
863 "YCgCo 4:2:2 10-bit",
864 i16
865);
866d_cnv!(
867 icgc410_to_rgb10,
868 u16,
869 10,
870 YuvSourceChannels::Rgb,
871 YuvChromaSubsampling::Yuv444,
872 "RGB",
873 "YCgCo 4:4:4 10-bit",
874 i16
875);
876d_cnv!(
877 icgc410_to_rgba10,
878 u16,
879 10,
880 YuvSourceChannels::Rgba,
881 YuvChromaSubsampling::Yuv444,
882 "RGBA",
883 "YCgCo 4:4:4 10-bit",
884 i16
885);
886
887d_cnv!(
890 icgc012_to_rgb12,
891 u16,
892 12,
893 YuvSourceChannels::Rgb,
894 YuvChromaSubsampling::Yuv420,
895 "RGB",
896 "YCgCo 4:2:0 12-bit",
897 i16
898);
899d_cnv!(
900 icgc012_to_rgba12,
901 u16,
902 12,
903 YuvSourceChannels::Rgba,
904 YuvChromaSubsampling::Yuv420,
905 "RGBA",
906 "YCgCo 4:2:0 12-bit",
907 i16
908);
909d_cnv!(
910 icgc212_to_rgb12,
911 u16,
912 12,
913 YuvSourceChannels::Rgb,
914 YuvChromaSubsampling::Yuv422,
915 "RGB",
916 "YCgCo 4:2:2 12-bit",
917 i16
918);
919d_cnv!(
920 icgc212_to_rgba12,
921 u16,
922 12,
923 YuvSourceChannels::Rgba,
924 YuvChromaSubsampling::Yuv422,
925 "RGBA",
926 "YCgCo 4:2:2 12-bit",
927 i16
928);
929d_cnv!(
930 icgc412_to_rgb12,
931 u16,
932 12,
933 YuvSourceChannels::Rgb,
934 YuvChromaSubsampling::Yuv444,
935 "RGB",
936 "YCgCo 4:4:4 12-bit",
937 i16
938);
939d_cnv!(
940 icgc412_to_rgba12,
941 u16,
942 12,
943 YuvSourceChannels::Rgba,
944 YuvChromaSubsampling::Yuv444,
945 "RGBA",
946 "YCgCo 4:4:4 12-bit",
947 i16
948);