1#![allow(clippy::excessive_precision)]
30
31use crate::internals::ProcessedOffset;
32use crate::yuv_error::check_rgba_destination;
33use crate::yuv_support::{CbCrForwardTransform, CbCrInverseTransform, YuvChromaRange};
34use crate::{YuvChromaSubsampling, YuvError, YuvPlanarImage, YuvPlanarImageMut, YuvRange};
35use std::fmt::{Display, Formatter};
36
37#[repr(u8)]
38#[derive(Debug, Copy, Clone, PartialEq, Eq)]
39pub(crate) enum RdpChannels {
40 Rgb = 0,
41 Rgba = 1,
42 Bgra = 2,
43 Bgr = 3,
44 Abgr = 4,
45 Argb = 5,
46}
47
48impl Display for RdpChannels {
49 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
50 match self {
51 RdpChannels::Rgb => f.write_str("RdpChannels::Rgb"),
52 RdpChannels::Rgba => f.write_str("RdpChannels::Rgba"),
53 RdpChannels::Bgra => f.write_str("RdpChannels::Bgra"),
54 RdpChannels::Bgr => f.write_str("RdpChannels::Bgr"),
55 RdpChannels::Abgr => f.write_str("RdpChannels::Abgr"),
56 RdpChannels::Argb => f.write_str("RdpChannels::Argb"),
57 }
58 }
59}
60
61impl From<u8> for RdpChannels {
62 #[inline(always)]
63 fn from(value: u8) -> Self {
64 match value {
65 0 => RdpChannels::Rgb,
66 1 => RdpChannels::Rgba,
67 2 => RdpChannels::Bgra,
68 3 => RdpChannels::Bgr,
69 4 => RdpChannels::Abgr,
70 5 => RdpChannels::Argb,
71 _ => {
72 unimplemented!("Unknown value")
73 }
74 }
75 }
76}
77
78impl RdpChannels {
79 #[inline(always)]
80 pub const fn get_channels_count(&self) -> usize {
81 match self {
82 RdpChannels::Rgb | RdpChannels::Bgr => 3,
83 RdpChannels::Rgba | RdpChannels::Bgra | RdpChannels::Abgr | RdpChannels::Argb => 4,
84 }
85 }
86
87 #[inline(always)]
88 pub const fn has_alpha(&self) -> bool {
89 match self {
90 RdpChannels::Rgb | RdpChannels::Bgr => false,
91 RdpChannels::Rgba | RdpChannels::Bgra | RdpChannels::Abgr | RdpChannels::Argb => true,
92 }
93 }
94}
95
96impl RdpChannels {
97 #[inline(always)]
98 pub const fn get_r_channel_offset(&self) -> usize {
99 match self {
100 RdpChannels::Rgb => 0,
101 RdpChannels::Rgba => 0,
102 RdpChannels::Bgra => 2,
103 RdpChannels::Bgr => 2,
104 RdpChannels::Abgr => 3,
105 RdpChannels::Argb => 1,
106 }
107 }
108
109 #[inline(always)]
110 pub const fn get_g_channel_offset(&self) -> usize {
111 match self {
112 RdpChannels::Rgb | RdpChannels::Bgr => 1,
113 RdpChannels::Rgba | RdpChannels::Bgra => 1,
114 RdpChannels::Abgr | RdpChannels::Argb => 2,
115 }
116 }
117
118 #[inline(always)]
119 pub const fn get_b_channel_offset(&self) -> usize {
120 match self {
121 RdpChannels::Rgb => 2,
122 RdpChannels::Rgba => 2,
123 RdpChannels::Bgra => 0,
124 RdpChannels::Bgr => 0,
125 RdpChannels::Abgr => 1,
126 RdpChannels::Argb => 3,
127 }
128 }
129 #[inline(always)]
130 pub const fn get_a_channel_offset(&self) -> usize {
131 match self {
132 RdpChannels::Rgb | RdpChannels::Bgr => 0,
133 RdpChannels::Rgba | RdpChannels::Bgra => 3,
134 RdpChannels::Abgr | RdpChannels::Argb => 0,
135 }
136 }
137}
138
139type RgbEncoderHandler = Option<
140 unsafe fn(
141 transform: &CbCrForwardTransform<i32>,
142 y_plane: &mut [i16],
143 u_plane: &mut [i16],
144 v_plane: &mut [i16],
145 rgba: &[u8],
146 width: usize,
147 ) -> ProcessedOffset,
148>;
149
150struct RgbEncoder<const ORIGIN_CHANNELS: u8, const Q: i32> {
151 handler: RgbEncoderHandler,
152}
153
154impl<const ORIGIN_CHANNELS: u8, const Q: i32> Default for RgbEncoder<ORIGIN_CHANNELS, Q> {
155 fn default() -> Self {
156 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
157 {
158 #[cfg(feature = "avx")]
159 {
160 let use_avx = std::arch::is_x86_feature_detected!("avx2");
161 if use_avx {
162 use crate::avx2::rdp_avx2_rgba_to_yuv;
163 return RgbEncoder {
164 handler: Some(rdp_avx2_rgba_to_yuv::<ORIGIN_CHANNELS, Q>),
165 };
166 }
167 }
168 }
169 RgbEncoder { handler: None }
170 }
171}
172
173pub(crate) trait WideRdpRowForwardHandler<V, T, K> {
174 fn handle_row(
175 &self,
176 y_plane: &mut [V],
177 u_plane: &mut [V],
178 v_plane: &mut [V],
179 rgba: &[T],
180 width: u32,
181 chroma: YuvChromaRange,
182 transform: &CbCrForwardTransform<K>,
183 ) -> ProcessedOffset;
184}
185
186impl<const ORIGIN_CHANNELS: u8, const Q: i32> WideRdpRowForwardHandler<i16, u8, i32>
187 for RgbEncoder<ORIGIN_CHANNELS, Q>
188{
189 fn handle_row(
190 &self,
191 y_plane: &mut [i16],
192 u_plane: &mut [i16],
193 v_plane: &mut [i16],
194 rgba: &[u8],
195 width: u32,
196 _: YuvChromaRange,
197 transform: &CbCrForwardTransform<i32>,
198 ) -> ProcessedOffset {
199 if let Some(handler) = self.handler {
200 unsafe {
201 return handler(transform, y_plane, u_plane, v_plane, rgba, width as usize);
202 }
203 }
204 ProcessedOffset { cx: 0, ux: 0 }
205 }
206}
207
208fn to_rdp_yuv<const ORIGIN_CHANNELS: u8>(
209 planar_image: &mut YuvPlanarImageMut<i16>,
210 rgba: &[u8],
211 rgba_stride: u32,
212) -> Result<(), YuvError> {
213 let ch: RdpChannels = ORIGIN_CHANNELS.into();
214 let channels = ch.get_channels_count();
215 planar_image.check_constraints(YuvChromaSubsampling::Yuv444)?;
216 check_rgba_destination(
217 rgba,
218 rgba_stride,
219 planar_image.width,
220 planar_image.height,
221 channels,
222 )?;
223
224 let y_plane = planar_image.y_plane.borrow_mut();
225 let u_plane = planar_image.u_plane.borrow_mut();
226 let v_plane = planar_image.v_plane.borrow_mut();
227
228 const PRECISION: i32 = 15;
229 const SCALE: f32 = (1 << PRECISION) as f32;
230 const Y_R: i32 = (0.299 * SCALE) as i32;
231 const Y_G: i32 = (0.587 * SCALE) as i32;
232 const Y_B: i32 = (0.114 * SCALE) as i32;
233 const CB_R: i32 = -(0.168_935 * SCALE) as i32;
234 const CB_G: i32 = -(0.331_665 * SCALE) as i32;
235 const CB_B: i32 = (0.500_59 * SCALE) as i32;
236 const CR_R: i32 = (0.499_813 * SCALE) as i32;
237 const CR_G: i32 = -(0.418_531 * SCALE) as i32;
238 const CR_B: i32 = -(0.081_282 * SCALE) as i32;
239
240 let b_transform = CbCrForwardTransform {
241 yr: Y_R,
242 yg: Y_G,
243 yb: Y_B,
244 cb_r: CB_R,
245 cb_g: CB_G,
246 cb_b: CB_B,
247 cr_r: CR_R,
248 cr_g: CR_G,
249 cr_b: CR_B,
250 };
251
252 let handler = RgbEncoder::<ORIGIN_CHANNELS, 10>::default();
253
254 let iter = y_plane
255 .chunks_exact_mut(planar_image.y_stride as usize)
256 .zip(u_plane.chunks_exact_mut(planar_image.u_stride as usize))
257 .zip(v_plane.chunks_exact_mut(planar_image.v_stride as usize))
258 .zip(rgba.chunks_exact(rgba_stride as usize));
259
260 iter.for_each(|(((y_dst, u_dst), v_dst), rgba)| {
261 let offset = handler.handle_row(
262 y_dst,
263 u_dst,
264 v_dst,
265 rgba,
266 planar_image.width,
267 YuvChromaRange {
268 bias_y: 4096,
269 bias_uv: 0,
270 range: YuvRange::Full,
271 range_uv: 0,
272 range_y: 0,
273 },
274 &b_transform,
275 );
276
277 for (((y_dst, u_dst), v_dst), rgba) in y_dst
278 .iter_mut()
279 .zip(u_dst.iter_mut())
280 .zip(v_dst.iter_mut())
281 .zip(rgba.chunks_exact(channels))
282 .take(planar_image.width as usize)
283 .skip(offset.cx)
284 {
285 let r = rgba[ch.get_r_channel_offset()] as i32;
286 let g = rgba[ch.get_g_channel_offset()] as i32;
287 let b = rgba[ch.get_b_channel_offset()] as i32;
288
289 const Q: i32 = 10;
290
291 let y0 = ((r * b_transform.yr + g * b_transform.yg + b * b_transform.yb) >> Q) - 4096;
292 let u = (r * b_transform.cb_r + g * b_transform.cb_g + b * b_transform.cb_b) >> Q;
293 let v = (r * b_transform.cr_r + g * b_transform.cr_g + b * b_transform.cr_b) >> Q;
294
295 *y_dst = y0 as i16;
296 *u_dst = u as i16;
297 *v_dst = v as i16;
298 }
299 });
300
301 Ok(())
302}
303
304macro_rules! d_forward {
305 ($method: ident, $cn: expr, $name: ident, $stride_name: ident) => {
306 #[doc = concat!("RemoteFX conversion RGBx to YUV 4:4:4")]
307 pub fn $method(
308 planar_image: &mut YuvPlanarImageMut<i16>,
309 $name: &[u8],
310 $stride_name: u32,
311 ) -> Result<(), YuvError> {
312 to_rdp_yuv::<{ $cn as u8 }>(planar_image, $name, $stride_name)
313 }
314 };
315}
316
317d_forward!(rdp_rgb_to_yuv444, RdpChannels::Rgb, rgb, rgb_stride);
318d_forward!(rdp_rgba_to_yuv444, RdpChannels::Rgba, rgba, rgba_stride);
319d_forward!(rdp_bgra_to_yuv444, RdpChannels::Bgra, bgra, bgra_stride);
320d_forward!(rdp_abgr_to_yuv444, RdpChannels::Abgr, abgr, abgr_stride);
321d_forward!(rdp_bgr_to_yuv444, RdpChannels::Bgr, bgr, bgr_stride);
322d_forward!(rdp_argb_to_yuv444, RdpChannels::Argb, argb, argb_stride);
323
324#[inline(always)]
325fn qrshr_n<const PRECISION: i32, const BIT_DEPTH: usize>(val: i32) -> i32 {
326 let max_value: i32 = (1 << BIT_DEPTH) - 1;
327 ((val) >> PRECISION).min(max_value).max(0)
328}
329
330fn rdp_yuv_to_rgb<const ORIGIN_CHANNELS: u8>(
331 planar_image: &YuvPlanarImage<i16>,
332 rgba: &mut [u8],
333 rgba_stride: u32,
334) -> Result<(), YuvError> {
335 let ch: RdpChannels = ORIGIN_CHANNELS.into();
336 let channels = ch.get_channels_count();
337 planar_image.check_constraints(YuvChromaSubsampling::Yuv444)?;
338 check_rgba_destination(
339 rgba,
340 rgba_stride,
341 planar_image.width,
342 planar_image.height,
343 channels,
344 )?;
345
346 let y_plane = planar_image.y_plane;
347 let u_plane = planar_image.u_plane;
348 let v_plane = planar_image.v_plane;
349
350 const PRECISION: i32 = 16;
351 const Y_SCALE: i32 = 1 << PRECISION;
352 const B_Y: i32 = (1.402525f32 * Y_SCALE as f32) as i32;
353 const B_G_1: i32 = (0.343730f32 * Y_SCALE as f32) as i32;
354 const B_G_2: i32 = (0.714401f32 * Y_SCALE as f32) as i32;
355 const B_B_1: i32 = (1.769905 * Y_SCALE as f32) as i32;
356
357 let b_transform = CbCrInverseTransform::<i32> {
358 y_coef: Y_SCALE,
359 cr_coef: B_Y,
360 cb_coef: B_B_1,
361 g_coeff_1: B_G_1,
362 g_coeff_2: B_G_2,
363 };
364
365 let iter = y_plane
366 .chunks_exact(planar_image.y_stride as usize)
367 .zip(u_plane.chunks_exact(planar_image.u_stride as usize))
368 .zip(v_plane.chunks_exact(planar_image.v_stride as usize))
369 .zip(rgba.chunks_exact_mut(rgba_stride as usize));
370
371 iter.for_each(|(((y_dst, u_dst), v_dst), rgba)| {
372 let mut _cx = 0;
373
374 let mut _offset = ProcessedOffset { cx: 0, ux: 0 };
375
376 _cx = _offset.cx;
377
378 for (((&y_0, &u), &v), rgba) in y_dst
379 .iter()
380 .zip(u_dst.iter())
381 .zip(v_dst.iter())
382 .zip(rgba.chunks_exact_mut(channels))
383 .take(planar_image.width as usize)
384 .skip(_cx)
385 {
386 let y = y_0;
387 let yy = ((y + 4096) as i32) * Y_SCALE;
388 let r = qrshr_n::<21, 8>(yy + b_transform.cr_coef * v as i32);
389 let g = qrshr_n::<21, 8>(
390 yy - b_transform.g_coeff_2 * v as i32 - b_transform.g_coeff_1 * u as i32,
391 );
392 let b = qrshr_n::<21, 8>(yy + b_transform.cb_coef * u as i32);
393
394 rgba[ch.get_r_channel_offset()] = r as u8;
395 rgba[ch.get_g_channel_offset()] = g as u8;
396 rgba[ch.get_b_channel_offset()] = b as u8;
397 if ch.has_alpha() {
398 rgba[ch.get_a_channel_offset()] = 255;
399 }
400 }
401 });
402 Ok(())
403}
404
405macro_rules! d_backward {
406 ($method: ident, $cn: expr, $name: ident, $stride_name: ident, $px_name: expr) => {
407 #[doc = concat!("Converts RemoteFX YUV 4:4:4 to ", $px_name, "with 8 bit-depth precision.")]
408 pub fn $method(
409 planar_image: &YuvPlanarImage<i16>,
410 $name: &mut [u8],
411 $stride_name: u32,
412 ) -> Result<(), YuvError> {
413 rdp_yuv_to_rgb::<{ $cn as u8 }>(planar_image, $name, $stride_name)
414 }
415 };
416}
417
418d_backward!(rdp_yuv444_to_rgb, RdpChannels::Rgb, rgb, rgb_stride, "RGB");
419d_backward!(
420 rdp_yuv444_to_rgba,
421 RdpChannels::Rgba,
422 rgba,
423 rgba_stride,
424 "RGBA"
425);
426d_backward!(
427 rdp_yuv444_to_bgra,
428 RdpChannels::Bgra,
429 bgra,
430 bgra_stride,
431 "BGRA"
432);
433d_backward!(
434 rdp_yuv444_to_abgr,
435 RdpChannels::Abgr,
436 abgr,
437 abgr_stride,
438 "ABGR"
439);
440d_backward!(rdp_yuv444_to_bgr, RdpChannels::Bgr, bgr, bgr_stride, "BGR");
441d_backward!(
442 rdp_yuv444_to_argb,
443 RdpChannels::Argb,
444 argb,
445 argb_stride,
446 "ARGB"
447);
448
449#[cfg(test)]
450mod tests {
451 use super::*;
452 use crate::BufferStoreMut;
453 #[test]
454 fn rgba_to_64x64_yuv() {
455 const WIDTH: usize = 64;
456 const HEIGHT: usize = 64;
457 let mut y = [0i16; WIDTH * HEIGHT];
458 let mut cb = [0i16; WIDTH * HEIGHT];
459 let mut cr = [0i16; WIDTH * HEIGHT];
460 let y_plane = BufferStoreMut::Borrowed(&mut y);
461 let u_plane = BufferStoreMut::Borrowed(&mut cb);
462 let v_plane = BufferStoreMut::Borrowed(&mut cr);
463 let mut plane = YuvPlanarImageMut {
464 y_plane,
465 y_stride: 64,
466 u_plane,
467 u_stride: 64,
468 v_plane,
469 v_stride: 64,
470 width: 10,
471 height: 20,
472 };
473 let stride = 30 * 4;
474 let input = vec![
475 0;
476 (stride * (plane.height - 1) + plane.width * 4)
477 .try_into()
478 .unwrap()
479 ];
480 rdp_rgba_to_yuv444(&mut plane, &input, stride).unwrap();
481 rdp_bgra_to_yuv444(&mut plane, &input, stride).unwrap();
482 rdp_abgr_to_yuv444(&mut plane, &input, stride).unwrap();
483 rdp_argb_to_yuv444(&mut plane, &input, stride).unwrap();
484 }
485}