Skip to content

Commit

Permalink
Introduce ImageData::get_rect
Browse files Browse the repository at this point in the history
We use that to send only the pixels that will be actually drawn to the
canvas thread in CanvasRenderingContext2d::PutImageData.

We also make the canvas thread byte swap and premultiply colours in-place.
  • Loading branch information
nox committed Oct 5, 2018
1 parent 784fbb2 commit 19f40cd
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 55 deletions.
45 changes: 13 additions & 32 deletions components/canvas/canvas_data.rs
Expand Up @@ -13,6 +13,7 @@ use cssparser::RGBA;
use euclid::{Transform2D, Point2D, Vector2D, Rect, Size2D};
use ipc_channel::ipc::{IpcBytesSender, IpcSender};
use num_traits::ToPrimitive;
use pixels;
use serde_bytes::ByteBuf;
use std::mem;
use std::sync::Arc;
Expand Down Expand Up @@ -451,42 +452,22 @@ impl<'a> CanvasData<'a> {
// https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata
pub fn put_image_data(
&mut self,
imagedata: Vec<u8>,
mut imagedata: Vec<u8>,
offset: Vector2D<i32>,
image_data_size: Size2D<i32>,
dest_rect: Rect<i32>,
imagedata_size: Size2D<i32>,
) {
assert_eq!(image_data_size.width * image_data_size.height * 4, imagedata.len() as i32);

let image_size = image_data_size;

let first_pixel = dest_rect.origin;
let mut src_line = (first_pixel.y * (image_size.width * 4) + first_pixel.x * 4) as usize;

let mut dest =
Vec::with_capacity((dest_rect.size.width * dest_rect.size.height * 4) as usize);

for _ in 0 .. dest_rect.size.height {
let mut src_offset = src_line;
for _ in 0 .. dest_rect.size.width {
let alpha = imagedata[src_offset + 3] as u16;
// add 127 before dividing for more accurate rounding
let premultiply_channel = |channel: u8| (((channel as u16 * alpha) + 127) / 255) as u8;
dest.push(premultiply_channel(imagedata[src_offset + 2]));
dest.push(premultiply_channel(imagedata[src_offset + 1]));
dest.push(premultiply_channel(imagedata[src_offset + 0]));
dest.push(imagedata[src_offset + 3]);
src_offset += 4;
}
src_line += (image_size.width * 4) as usize;
}

assert_eq!(imagedata_size.area() * 4, imagedata.len() as i32);
pixels::byte_swap_and_premultiply_inplace(&mut imagedata);
if let Some(source_surface) = self.drawtarget.create_source_surface_from_data(
&dest,
dest_rect.size,
dest_rect.size.width * 4,
&imagedata,
imagedata_size,
imagedata_size.width * 4,
SurfaceFormat::B8G8R8A8) {
self.drawtarget.copy_surface(source_surface, Rect::from_size(dest_rect.size), offset.to_point());
self.drawtarget.copy_surface(
source_surface,
Rect::from_size(imagedata_size),
offset.to_point(),
);
}
}

Expand Down
14 changes: 2 additions & 12 deletions components/canvas/canvas_paint_thread.rs
Expand Up @@ -241,18 +241,8 @@ impl<'a> CanvasPaintThread <'a> {
Canvas2dMsg::GetImageData(dest_rect, canvas_size, chan) => {
self.canvas(canvas_id).image_data(dest_rect, canvas_size, chan)
},
Canvas2dMsg::PutImageData(
imagedata,
offset,
image_data_size,
dirty_rect,
) => {
self.canvas(canvas_id).put_image_data(
imagedata.into(),
offset,
image_data_size,
dirty_rect,
)
Canvas2dMsg::PutImageData(imagedata, offset, imagedata_size) => {
self.canvas(canvas_id).put_image_data(imagedata.into(), offset, imagedata_size)
},
Canvas2dMsg::SetShadowOffsetX(value) => {
self.canvas(canvas_id).set_shadow_offset_x(value)
Expand Down
2 changes: 1 addition & 1 deletion components/canvas_traits/canvas.rs
Expand Up @@ -54,7 +54,7 @@ pub enum Canvas2dMsg {
IsPointInPath(f64, f64, FillRule, IpcSender<bool>),
LineTo(Point2D<f32>),
MoveTo(Point2D<f32>),
PutImageData(ByteBuf, Vector2D<i32>, Size2D<i32>, Rect<i32>),
PutImageData(ByteBuf, Vector2D<i32>, Size2D<i32>),
QuadraticCurveTo(Point2D<f32>, Point2D<f32>),
Rect(Rect<f32>),
RestoreContext,
Expand Down
13 changes: 4 additions & 9 deletions components/script/dom/canvasrenderingcontext2d.rs
Expand Up @@ -1277,19 +1277,14 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
return;
}

// FIXME(nox): There is no need to make a Vec<u8> of all the pixels
// if we didn't want to put the entire image.
let buffer = imagedata.get_data_array();
let dirty_size = Size2D::new(dirty_width, dirty_height);
let dirty_rect = Rect::new(Point2D::new(dirty_x, dirty_y), dirty_size);

// Step 7.
self.send_canvas_2d_msg(Canvas2dMsg::PutImageData(
buffer.into(),
imagedata.get_rect(dirty_rect.try_cast().unwrap()).into(),
origin.to_vector(),
imagedata_size,
Rect::new(
Point2D::new(dirty_x, dirty_y),
Size2D::new(dirty_width, dirty_height),
),
dirty_size,
));
self.mark_as_dirty();
}
Expand Down
33 changes: 32 additions & 1 deletion components/script/dom/imagedata.rs
Expand Up @@ -9,7 +9,7 @@ use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::root::DomRoot;
use dom::globalscope::GlobalScope;
use dom_struct::dom_struct;
use euclid::Size2D;
use euclid::{Rect, Size2D};
use js::jsapi::{Heap, JSContext, JSObject};
use js::rust::Runtime;
use js::typedarray::{Uint8ClampedArray, CreateWith};
Expand Down Expand Up @@ -149,9 +149,40 @@ impl ImageData {
}
}

#[allow(unsafe_code)]
pub fn get_rect(&self, rect: Rect<u32>) -> Vec<u8> {
unsafe {
assert!(!rect.is_empty());
assert!(self.rect().contains_rect(&rect));
assert!(!self.data.get().is_null());
let cx = Runtime::get();
assert!(!cx.is_null());
typedarray!(in(cx) let array: Uint8ClampedArray = self.data.get());
let slice = array.as_ref().unwrap().as_slice();
let area = rect.size.area() as usize;
let first_column_start = rect.origin.x as usize * 4;
let row_length = self.width as usize * 4;
let first_row_start = rect.origin.y as usize * row_length;
if rect.origin.x == 0 && rect.size.width == self.width || rect.size.height == 1 {
let start = first_column_start + first_row_start;
// FIXME(nox): This should be a borrow.
return slice[start..start + area * 4].into();
}
let mut data = Vec::with_capacity(area * 4);
for row in slice[first_row_start..].chunks(row_length).take(rect.size.height as usize) {
data.extend_from_slice(&row[first_column_start..][..rect.size.width as usize * 4]);
}
data
}
}

pub fn get_size(&self) -> Size2D<u32> {
Size2D::new(self.Width(), self.Height())
}

pub fn rect(&self) -> Rect<u32> {
Rect::from_size(self.get_size())
}
}

impl ImageDataMethods for ImageData {
Expand Down

0 comments on commit 19f40cd

Please sign in to comment.