diff options
author | Anthony Ramine <n.oxyde@gmail.com> | 2018-10-07 02:52:06 +0200 |
---|---|---|
committer | Anthony Ramine <n.oxyde@gmail.com> | 2018-10-07 02:52:06 +0200 |
commit | e62dbabb46b8c5b6a5fc3dc6188976cbf2039d75 (patch) | |
tree | 39f62d51bec93921aae3790d472c71eaaa5d1306 | |
parent | 241dba064ded04b3b2f97b098db637ce58cf9e19 (diff) | |
download | servo-e62dbabb46b8c5b6a5fc3dc6188976cbf2039d75.tar.gz servo-e62dbabb46b8c5b6a5fc3dc6188976cbf2039d75.zip |
Handle some transparent black cases in ctx.getImageData
-rw-r--r-- | Cargo.lock | 3 | ||||
-rw-r--r-- | components/canvas/canvas_data.rs | 17 | ||||
-rw-r--r-- | components/pixels/Cargo.toml | 3 | ||||
-rw-r--r-- | components/pixels/lib.rs | 25 | ||||
-rw-r--r-- | components/script/dom/canvasrenderingcontext2d.rs | 51 | ||||
-rw-r--r-- | components/script/dom/imagedata.rs | 22 |
6 files changed, 72 insertions, 49 deletions
diff --git a/Cargo.lock b/Cargo.lock index e80bb0441e1..c999b84bb5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2663,6 +2663,9 @@ dependencies = [ [[package]] name = "pixels" version = "0.0.1" +dependencies = [ + "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "pkg-config" diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index e7841d1a53c..cadce6ad941 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -526,22 +526,7 @@ impl<'a> CanvasData<'a> { } let data_surface = self.drawtarget.snapshot().get_data_surface(); - let src_data = unsafe { data_surface.data() }; - let stride = data_surface.stride(); - - //start offset of the copyable rectangle - let mut src = (src_read_rect.origin.y * stride + src_read_rect.origin.x * 4) as usize; - let mut image_data = Vec::with_capacity( - (src_read_rect.size.width * src_read_rect.size.height * 4) as usize, - ); - //copy the data to the destination vector - for _ in 0..src_read_rect.size.height { - let row = &src_data[src .. src + (4 * src_read_rect.size.width) as usize]; - image_data.extend_from_slice(row); - src += stride as usize; - } - - image_data + pixels::get_rect(unsafe { data_surface.data() }, canvas_size.to_u32(), read_rect.to_u32()).into_owned() } } diff --git a/components/pixels/Cargo.toml b/components/pixels/Cargo.toml index 49dcc3833cf..0c2e47b07bb 100644 --- a/components/pixels/Cargo.toml +++ b/components/pixels/Cargo.toml @@ -8,3 +8,6 @@ publish = false [lib] name = "pixels" path = "lib.rs" + +[dependencies] +euclid = "0.19" diff --git a/components/pixels/lib.rs b/components/pixels/lib.rs index d91b14cb650..dc6285ebaab 100644 --- a/components/pixels/lib.rs +++ b/components/pixels/lib.rs @@ -2,6 +2,31 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +extern crate euclid; + +use euclid::{Rect, Size2D}; +use std::borrow::Cow; + +pub fn get_rect(pixels: &[u8], size: Size2D<u32>, rect: Rect<u32>) -> Cow<[u8]> { + assert!(!rect.is_empty()); + assert!(Rect::from_size(size).contains_rect(&rect)); + assert_eq!(pixels.len() % 4, 0); + assert_eq!(size.area() as usize, pixels.len() / 4); + let area = rect.size.area() as usize; + let first_column_start = rect.origin.x as usize * 4; + let row_length = size.width as usize * 4; + let first_row_start = rect.origin.y as usize * row_length; + if rect.origin.x == 0 && rect.size.width == size.width || rect.size.height == 1 { + let start = first_column_start + first_row_start; + return Cow::Borrowed(&pixels[start..start + area * 4]); + } + let mut data = Vec::with_capacity(area * 4); + for row in pixels[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.into() +} + // TODO(pcwalton): Speed up with SIMD, or better yet, find some way to not do this. pub fn byte_swap_colors_inplace(pixels: &mut [u8]) { assert!(pixels.len() % 4 == 0); diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 633fbbc3f14..f98da6f9a5e 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -1137,6 +1137,9 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { mut sw: i32, mut sh: i32, ) -> Fallible<DomRoot<ImageData>> { + // FIXME(nox): There are many arithmetic operations here that can + // overflow or underflow, this should probably be audited. + if sw == 0 || sh == 0 { return Err(Error::IndexSize); } @@ -1154,27 +1157,48 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { sy -= sh; } + let data_width = sw; + let data_height = sh; + + if sx < 0 { + sw += sx; + sx = 0; + } + if sy < 0 { + sh += sy; + sy = 0; + } + + if sw <= 0 || sh <= 0 { + // All the pixels are before the start of the canvas surface. + return ImageData::new(&self.global(), data_width as u32, data_height as u32, None); + } + let (sender, receiver) = ipc::bytes_channel().unwrap(); - let dest_rect = Rect::new(Point2D::new(sx, sy), Size2D::new(sw, sh)); + let src_rect = Rect::new(Point2D::new(sx, sy), Size2D::new(sw, sh)); // FIXME(nox): This is probably wrong when this is a context for an // offscreen canvas. - let canvas_size = self.canvas.as_ref().map_or(Size2D::zero(), |c| c.get_size()); - self.send_canvas_2d_msg(Canvas2dMsg::GetImageData( - dest_rect, - canvas_size.to_i32(), - sender, - )); - let mut data = receiver.recv().unwrap(); - - // Byte swap and unmultiply alpha. - for chunk in data.chunks_mut(4) { + let canvas_size = self.canvas + .as_ref() + .map_or(Size2D::zero(), |c| c.get_size()) + .try_cast().unwrap(); + let canvas_rect = Rect::from_size(canvas_size); + let read_rect = match src_rect.intersection(&canvas_rect) { + Some(rect) if !rect.is_empty() => rect, + _ => { + // All the pixels are past the end of the canvas surface. + return ImageData::new(&self.global(), data_width as u32, data_height as u32, None); + } + }; + self.send_canvas_2d_msg(Canvas2dMsg::GetImageData(read_rect, canvas_size, sender)); + let mut pixels = receiver.recv().unwrap(); + for chunk in pixels.chunks_mut(4) { let b = chunk[0]; chunk[0] = UNPREMULTIPLY_TABLE[256 * (chunk[3] as usize) + chunk[2] as usize]; chunk[1] = UNPREMULTIPLY_TABLE[256 * (chunk[3] as usize) + chunk[1] as usize]; chunk[2] = UNPREMULTIPLY_TABLE[256 * (chunk[3] as usize) + b as usize]; } - - ImageData::new(&self.global(), sw as u32, sh as u32, Some(data.to_vec())) + ImageData::new(&self.global(), sw as u32, sh as u32, Some(pixels.to_vec())) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata @@ -1197,6 +1221,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { // FIXME(nox): There are many arithmetic operations here that can // overflow or underflow, this should probably be audited. + let imagedata_size = Size2D::new(imagedata.Width() as i32, imagedata.Height() as i32); if imagedata_size.width <= 0 || imagedata_size.height <= 0 { return; diff --git a/components/script/dom/imagedata.rs b/components/script/dom/imagedata.rs index 7e615c0e5da..f72ea49c0ff 100644 --- a/components/script/dom/imagedata.rs +++ b/components/script/dom/imagedata.rs @@ -13,6 +13,7 @@ use euclid::{Rect, Size2D}; use js::jsapi::{Heap, JSContext, JSObject}; use js::rust::Runtime; use js::typedarray::{Uint8ClampedArray, CreateWith}; +use pixels; use std::borrow::Cow; use std::default::Default; use std::ptr; @@ -162,31 +163,12 @@ impl ImageData { #[allow(unsafe_code)] pub unsafe fn get_rect(&self, rect: Rect<u32>) -> Cow<[u8]> { - assert!(!rect.is_empty()); - assert!(self.rect().contains_rect(&rect)); - let slice = self.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; - return Cow::Borrowed(&slice[start..start + area * 4]); - } - 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.into() + pixels::get_rect(self.as_slice(), self.get_size(), rect) } 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 { |