Skip to content

Commit 5f1c440

Browse files
elmarcoCBenoit
authored andcommitted
feat(graphics): replace hand-coded yuv/rgb with yuvutils
cargo bench: to_ycbcr time: [2.2988 µs 2.3251 µs 2.3517 µs] change: [-83.643% -83.534% -83.421%] (p = 0.00 < 0.05) Performance has improved. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
1 parent 05c0c97 commit 5f1c440

File tree

2 files changed

+49
-76
lines changed

2 files changed

+49
-76
lines changed

crates/ironrdp-graphics/src/color_conversion.rs

Lines changed: 39 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
1-
use core::cmp::min;
2-
use std::io::{self, Write};
1+
use std::io;
32

4-
use crate::image_processing::PixelFormat;
5-
6-
const ALPHA: u8 = 255;
7-
8-
pub fn ycbcr_to_bgra(input: YCbCrBuffer<'_>, mut output: &mut [u8]) -> io::Result<()> {
9-
for ycbcr in input {
10-
let pixel = Rgb::from(ycbcr);
3+
use yuvutils_rs::{
4+
rdp_abgr_to_yuv444, rdp_argb_to_yuv444, rdp_bgra_to_yuv444, rdp_rgba_to_yuv444, rdp_yuv444_to_bgra, BufferStoreMut,
5+
YuvPlanarImage, YuvPlanarImageMut,
6+
};
117

12-
output.write_all(&[pixel.b, pixel.g, pixel.r, ALPHA])?;
13-
}
8+
use crate::image_processing::PixelFormat;
149

15-
Ok(())
10+
pub fn ycbcr_to_bgra(input: YCbCrBuffer<'_>, output: &mut [u8]) -> io::Result<()> {
11+
let len = u32::try_from(output.len()).map_err(io::Error::other)?;
12+
let width = len / 4;
13+
let planar = YuvPlanarImage {
14+
y_plane: input.y,
15+
y_stride: width,
16+
u_plane: input.cb,
17+
u_stride: width,
18+
v_plane: input.cr,
19+
v_stride: width,
20+
width,
21+
height: 1,
22+
};
23+
rdp_yuv444_to_bgra(&planar, output, len).map_err(io::Error::other)
1624
}
1725

1826
fn iter_to_ycbcr<'a, I, C>(input: I, y: &mut [i16], cb: &mut [i16], cr: &mut [i16], conv: C)
@@ -93,63 +101,6 @@ pub fn to_ycbcr(
93101
}
94102
}
95103

96-
struct TileIterator<'a> {
97-
slice: &'a [u8],
98-
x: usize,
99-
y: usize,
100-
width: usize,
101-
height: usize,
102-
stride: usize,
103-
bpp: usize,
104-
}
105-
106-
impl<'a> TileIterator<'a> {
107-
fn new(slice: &'a [u8], width: usize, height: usize, stride: usize, bpp: usize) -> Self {
108-
assert!(width >= 1);
109-
assert!(height >= 1);
110-
111-
Self {
112-
slice,
113-
x: 0,
114-
y: 0,
115-
width,
116-
height,
117-
stride,
118-
bpp,
119-
}
120-
}
121-
}
122-
123-
impl<'a> Iterator for TileIterator<'a> {
124-
type Item = &'a [u8];
125-
126-
fn next(&mut self) -> Option<Self::Item> {
127-
// create 64x64 tiles
128-
if self.y >= 64 {
129-
return None;
130-
}
131-
132-
// repeat the last column & line if necessary
133-
let y = min(self.y, self.height - 1);
134-
let x = min(self.x, self.width - 1);
135-
let pos = y * self.stride + x * self.bpp;
136-
137-
self.x += 1;
138-
if self.x >= 64 {
139-
self.x = 0;
140-
self.y += 1;
141-
}
142-
143-
Some(&self.slice[pos..pos + self.bpp])
144-
}
145-
}
146-
147-
impl ExactSizeIterator for TileIterator<'_> {
148-
fn len(&self) -> usize {
149-
64 * 64
150-
}
151-
}
152-
153104
#[allow(clippy::too_many_arguments)]
154105
pub fn to_64x64_ycbcr_tile(
155106
input: &[u8],
@@ -164,15 +115,27 @@ pub fn to_64x64_ycbcr_tile(
164115
assert!(width <= 64);
165116
assert!(height <= 64);
166117

167-
let bpp = format.bytes_per_pixel() as usize;
118+
let y_plane = BufferStoreMut::Borrowed(y);
119+
let u_plane = BufferStoreMut::Borrowed(cb);
120+
let v_plane = BufferStoreMut::Borrowed(cr);
121+
let mut plane = YuvPlanarImageMut {
122+
y_plane,
123+
y_stride: 64,
124+
u_plane,
125+
u_stride: 64,
126+
v_plane,
127+
v_stride: 64,
128+
width: width.try_into().unwrap(),
129+
height: height.try_into().unwrap(),
130+
};
168131

169-
let input = TileIterator::new(input, width, height, stride, bpp);
170-
match format {
171-
PixelFormat::ARgb32 | PixelFormat::XRgb32 => iter_to_ycbcr(input, y, cb, cr, xrgb_to_rgb),
172-
PixelFormat::ABgr32 | PixelFormat::XBgr32 => iter_to_ycbcr(input, y, cb, cr, xbgr_to_rgb),
173-
PixelFormat::BgrA32 | PixelFormat::BgrX32 => iter_to_ycbcr(input, y, cb, cr, bgrx_to_rgb),
174-
PixelFormat::RgbA32 | PixelFormat::RgbX32 => iter_to_ycbcr(input, y, cb, cr, rgbx_to_rgb),
132+
let res = match format {
133+
PixelFormat::RgbA32 | PixelFormat::RgbX32 => rdp_rgba_to_yuv444(&mut plane, input, stride.try_into().unwrap()),
134+
PixelFormat::ARgb32 | PixelFormat::XRgb32 => rdp_argb_to_yuv444(&mut plane, input, stride.try_into().unwrap()),
135+
PixelFormat::BgrA32 | PixelFormat::BgrX32 => rdp_bgra_to_yuv444(&mut plane, input, stride.try_into().unwrap()),
136+
PixelFormat::ABgr32 | PixelFormat::XBgr32 => rdp_abgr_to_yuv444(&mut plane, input, stride.try_into().unwrap()),
175137
};
138+
res.unwrap();
176139
}
177140

178141
/// Convert a 16-bit RDP color to RGB representation. Input value should be represented in

fuzz/Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)