Skip to content

Commit 0e18531

Browse files
Remove functions that attempt to extract/isolate dirty regions from the image buffer for rendering. After updating to the tip of IronRDP, this introduced some strange visual artifacts. Though I narrowed it down to these functions, I could never quite figure out what exactly the root cause is. As a workaround, we'll skip this step and simply provide the javascript side with the entire image buffer and coordinates for the dirty region. While we incur the penality of copying the entire image buffer from rust -> js, at least we're still only rendering the dirty region.
1 parent 40eb5a4 commit 0e18531

File tree

4 files changed

+67
-93
lines changed

4 files changed

+67
-93
lines changed

web/packages/shared/components/CanvasRenderer.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,15 @@ function makeBitmapFrameRenderer(
238238
for (let i = 0; i < bitmapBuffer.length; i++) {
239239
if (bitmapBuffer[i].image_data.data.length != 0) {
240240
const bmpFrame = bitmapBuffer[i];
241-
ctx.putImageData(bmpFrame.image_data, bmpFrame.left, bmpFrame.top);
241+
ctx.putImageData(
242+
bmpFrame.image_data,
243+
bmpFrame.left,
244+
bmpFrame.top,
245+
bmpFrame.dirty_x,
246+
bmpFrame.dirty_y,
247+
bmpFrame.dirty_width,
248+
bmpFrame.dirty_height
249+
);
242250
}
243251
}
244252
bitmapBuffer = [];

web/packages/shared/components/DesktopSession/DesktopSession.story.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ function emitFrame(client: TdpClient, spec: ClientScreenSpec) {
212212
const frame: BitmapFrame = {
213213
left: 0,
214214
top: 0,
215+
dirty_x: 0,
216+
dirty_y: 0,
217+
dirty_width: imageData.width,
218+
dirty_height: imageData.height,
215219
image_data: imageData,
216220
};
217221

web/packages/shared/libs/ironrdp/src/lib.rs

Lines changed: 50 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ pub fn init_wasm_log(log_level: &str) {
7373
pub struct BitmapFrame {
7474
top: u16,
7575
left: u16,
76+
dirty_x: u16,
77+
dirty_y: u16,
78+
dirty_width: u16,
79+
dirty_height: u16,
7680
image_data: ImageData,
7781
}
7882

@@ -88,6 +92,26 @@ impl BitmapFrame {
8892
self.left
8993
}
9094

95+
#[wasm_bindgen(getter)]
96+
pub fn dirty_x(&self) -> u16 {
97+
self.dirty_x
98+
}
99+
100+
#[wasm_bindgen(getter)]
101+
pub fn dirty_y(&self) -> u16 {
102+
self.dirty_y
103+
}
104+
105+
#[wasm_bindgen(getter)]
106+
pub fn dirty_width(&self) -> u16 {
107+
self.dirty_width
108+
}
109+
110+
#[wasm_bindgen(getter)]
111+
pub fn dirty_height(&self) -> u16 {
112+
self.dirty_height
113+
}
114+
91115
#[wasm_bindgen(getter)]
92116
pub fn image_data(&self) -> ImageData {
93117
self.image_data.clone() // TODO(isaiah): can we remove this clone?
@@ -204,9 +228,14 @@ impl FastPathProcessor {
204228
match output {
205229
ActiveStageOutput::GraphicsUpdate(updated_region) => {
206230
// Apply the updated region to the canvas.
207-
let (image_location, image_data) =
208-
extract_partial_image(&self.image, updated_region);
209-
self.apply_image_to_canvas(image_data, image_location, cb_context, draw_cb)?;
231+
self.apply_image_to_canvas(
232+
self.image.data(),
233+
updated_region,
234+
cb_context,
235+
draw_cb,
236+
self.image.width(),
237+
self.image.height(),
238+
)?;
210239
}
211240
ActiveStageOutput::ResponseFrame(frame) => {
212241
// Send the response frame back to the server.
@@ -280,18 +309,30 @@ impl FastPathProcessor {
280309

281310
fn apply_image_to_canvas(
282311
&self,
283-
image_data: Vec<u8>,
312+
image_data: &[u8],
284313
image_location: InclusiveRectangle,
285314
cb_context: &JsValue,
286315
callback: &js_sys::Function,
316+
image_width: u16,
317+
image_height: u16,
287318
) -> Result<(), JsValue> {
288-
let top = image_location.top;
289-
let left = image_location.left;
319+
let image_data = create_image_data_from_image_and_region(
320+
image_data,
321+
InclusiveRectangle {
322+
left: 0,
323+
top: 0,
324+
right: image_width - 1,
325+
bottom: image_height - 1,
326+
},
327+
)?;
290328

291-
let image_data = create_image_data_from_image_and_region(&image_data, image_location)?;
292329
let bitmap_frame = BitmapFrame {
293-
top,
294-
left,
330+
top: 0,
331+
left: 0,
332+
dirty_x: image_location.left,
333+
dirty_y: image_location.top,
334+
dirty_width: image_location.width(),
335+
dirty_height: image_location.height(),
295336
image_data,
296337
};
297338

@@ -302,86 +343,3 @@ impl FastPathProcessor {
302343
Ok(())
303344
}
304345
}
305-
306-
/// Taken from https://github.com/Devolutions/IronRDP/blob/35839459aa58c5c42cd686b39b63a7944285c0de/crates/ironrdp-web/src/image.rs#L6
307-
pub fn extract_partial_image(
308-
image: &DecodedImage,
309-
region: InclusiveRectangle,
310-
) -> (InclusiveRectangle, Vec<u8>) {
311-
// PERF: needs actual benchmark to find a better heuristic
312-
if region.height() > 64 || region.width() > 512 {
313-
extract_whole_rows(image, region)
314-
} else {
315-
extract_smallest_rectangle(image, region)
316-
}
317-
}
318-
319-
/// Faster for low-height and smaller images
320-
///
321-
/// https://github.com/Devolutions/IronRDP/blob/35839459aa58c5c42cd686b39b63a7944285c0de/crates/ironrdp-web/src/image.rs#L16
322-
fn extract_smallest_rectangle(
323-
image: &DecodedImage,
324-
region: InclusiveRectangle,
325-
) -> (InclusiveRectangle, Vec<u8>) {
326-
let pixel_size = usize::from(image.pixel_format().bytes_per_pixel());
327-
328-
let image_width = usize::from(image.width());
329-
let image_stride = image_width * pixel_size;
330-
331-
let region_top = usize::from(region.top);
332-
let region_left = usize::from(region.left);
333-
let region_width = usize::from(region.width());
334-
let region_height = usize::from(region.height());
335-
let region_stride = region_width * pixel_size;
336-
337-
let dst_buf_size = region_width * region_height * pixel_size;
338-
let mut dst = vec![0; dst_buf_size];
339-
340-
let src = image.data();
341-
342-
for row in 0..region_height {
343-
let src_begin = image_stride * (region_top + row) + region_left * pixel_size;
344-
let src_end = src_begin + region_stride;
345-
let src_slice = &src[src_begin..src_end];
346-
347-
let target_begin = region_stride * row;
348-
let target_end = target_begin + region_stride;
349-
let target_slice = &mut dst[target_begin..target_end];
350-
351-
target_slice.copy_from_slice(src_slice);
352-
}
353-
354-
(region, dst)
355-
}
356-
357-
/// Faster for high-height and bigger images
358-
///
359-
/// https://github.com/Devolutions/IronRDP/blob/35839459aa58c5c42cd686b39b63a7944285c0de/crates/ironrdp-web/src/image.rs#L49
360-
fn extract_whole_rows(
361-
image: &DecodedImage,
362-
region: InclusiveRectangle,
363-
) -> (InclusiveRectangle, Vec<u8>) {
364-
let pixel_size = usize::from(image.pixel_format().bytes_per_pixel());
365-
366-
let image_width = usize::from(image.width());
367-
let image_stride = image_width * pixel_size;
368-
369-
let region_top = usize::from(region.top);
370-
let region_bottom = usize::from(region.bottom);
371-
372-
let src = image.data();
373-
374-
let src_begin = region_top * image_stride;
375-
let src_end = (region_bottom + 1) * image_stride;
376-
377-
let dst = src[src_begin..src_end].to_vec();
378-
379-
let wider_region = InclusiveRectangle {
380-
left: 0,
381-
top: region.top,
382-
right: image.width() - 1,
383-
bottom: region.bottom,
384-
};
385-
386-
(wider_region, dst)
387-
}

web/packages/shared/libs/tdp/client.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,10 @@ export class TdpClient extends EventEmitter<EventMap> {
889889
export type BitmapFrame = {
890890
top: number;
891891
left: number;
892+
dirty_x: number;
893+
dirty_y: number;
894+
dirty_width: number;
895+
dirty_height: number;
892896
image_data: ImageData;
893897
};
894898

0 commit comments

Comments
 (0)