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