diff options
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/canvasrenderingcontext2d.rs | 231 | ||||
-rw-r--r-- | components/script/dom/htmlcanvaselement.rs | 28 | ||||
-rw-r--r-- | components/script/dom/imagedata.rs | 37 | ||||
-rw-r--r-- | components/script/dom/webglrenderingcontext.rs | 85 | ||||
-rw-r--r-- | components/script/dom/webidls/CanvasRenderingContext2D.webidl | 4 |
5 files changed, 169 insertions, 216 deletions
diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 9adbaa3a734..dacabcccce5 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -5,7 +5,7 @@ use canvas_traits::canvas::{Canvas2dMsg, CanvasMsg, CanvasId}; use canvas_traits::canvas::{CompositionOrBlending, FillOrStrokeStyle, FillRule}; use canvas_traits::canvas::{LineCapStyle, LineJoinStyle, LinearGradientStyle}; -use canvas_traits::canvas::{RadialGradientStyle, RepetitionStyle, byte_swap_and_premultiply}; +use canvas_traits::canvas::{RadialGradientStyle, RepetitionStyle}; use cssparser::{Parser, ParserInput, RGBA}; use cssparser::Color as CSSColor; use dom::bindings::cell::DomRefCell; @@ -40,11 +40,11 @@ use net_traits::image_cache::ImageOrMetadataAvailable; use net_traits::image_cache::ImageResponse; use net_traits::image_cache::ImageState; use net_traits::image_cache::UsePlaceholder; -use num_traits::ToPrimitive; +use pixels; use profile_traits::ipc as profiled_ipc; use script_traits::ScriptMsg; use servo_url::ServoUrl; -use std::{cmp, fmt, mem}; +use std::{fmt, mem}; use std::cell::Cell; use std::str::FromStr; use std::sync::Arc; @@ -410,7 +410,7 @@ impl CanvasRenderingContext2D { Some((mut data, size)) => { // Pixels come from cache in BGRA order and drawImage expects RGBA so we // have to swap the color values - byte_swap_and_premultiply(&mut data); + pixels::byte_swap_and_premultiply_inplace(&mut data); let size = Size2D::new(size.width as f64, size.height as f64); (data, size) }, @@ -575,6 +575,28 @@ impl CanvasRenderingContext2D { fn set_origin_unclean(&self) { self.origin_clean.set(false) } + + pub fn get_rect(&self, rect: Rect<u32>) -> Vec<u8> { + assert!(self.origin_is_clean()); + + // 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()); + assert!(Rect::from_size(canvas_size).contains_rect(&rect)); + + let (sender, receiver) = ipc::bytes_channel().unwrap(); + self.send_canvas_2d_msg(Canvas2dMsg::GetImageData(rect, canvas_size, sender)); + let mut pixels = receiver.recv().unwrap().to_vec(); + + 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]; + } + + pixels + } } pub trait LayoutCanvasRenderingContext2DHelpers { @@ -1117,14 +1139,11 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { } // https://html.spec.whatwg.org/multipage/#dom-context-2d-createimagedata - fn CreateImageData(&self, sw: Finite<f64>, sh: Finite<f64>) -> Fallible<DomRoot<ImageData>> { - if *sw == 0.0 || *sh == 0.0 { + fn CreateImageData(&self, sw: i32, sh: i32) -> Fallible<DomRoot<ImageData>> { + if sw == 0 || sh == 0 { return Err(Error::IndexSize); } - - let sw = cmp::max(1, sw.abs().to_u32().unwrap()); - let sh = cmp::max(1, sh.abs().to_u32().unwrap()); - ImageData::new(&self.global(), sw, sh, None) + ImageData::new(&self.global(), sw.abs() as u32, sh.abs() as u32, None) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-createimagedata @@ -1133,61 +1152,31 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { } // https://html.spec.whatwg.org/multipage/#dom-context-2d-getimagedata - fn GetImageData( - &self, - sx: Finite<f64>, - sy: Finite<f64>, - sw: Finite<f64>, - sh: Finite<f64>, - ) -> Fallible<DomRoot<ImageData>> { - if !self.origin_is_clean() { - return Err(Error::Security); - } - - let mut sx = *sx; - let mut sy = *sy; - let mut sw = *sw; - let mut sh = *sh; + fn GetImageData(&self, sx: i32, sy: i32, sw: i32, 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.0 || sh == 0.0 { + if sw == 0 || sh == 0 { return Err(Error::IndexSize); } - if sw < 0.0 { - sw = -sw; - sx -= sw; - } - if sh < 0.0 { - sh = -sh; - sy -= sh; + if !self.origin_is_clean() { + return Err(Error::Security); } - let sh = cmp::max(1, sh.to_u32().unwrap()); - let sw = cmp::max(1, sw.to_u32().unwrap()); - - let (sender, receiver) = ipc::bytes_channel().unwrap(); - let dest_rect = Rect::new( - Point2D::new(sx.to_i32().unwrap(), sy.to_i32().unwrap()), - Size2D::new(sw as i32, sh as i32), - ); - let canvas_size = self - .canvas - .as_ref() - .map(|c| c.get_size()) - .unwrap_or(Size2D::zero()); - let canvas_size = Size2D::new(canvas_size.width as f64, canvas_size.height as f64); - self.send_canvas_2d_msg(Canvas2dMsg::GetImageData(dest_rect, canvas_size, sender)); - let mut data = receiver.recv().unwrap(); - - // Byte swap and unmultiply alpha. - for chunk in data.chunks_mut(4) { - let (b, g, r, a) = (chunk[0], chunk[1], chunk[2], chunk[3]); - chunk[0] = UNPREMULTIPLY_TABLE[256 * (a as usize) + r as usize]; - chunk[1] = UNPREMULTIPLY_TABLE[256 * (a as usize) + g as usize]; - chunk[2] = UNPREMULTIPLY_TABLE[256 * (a as usize) + b as usize]; - } + let (origin, size) = adjust_size_sign(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()); + let read_rect = match pixels::clip(origin, size, canvas_size) { + Some(rect) => rect, + None => { + // All the pixels are outside the canvas surface. + return ImageData::new(&self.global(), size.width, size.height, None); + }, + }; - ImageData::new(&self.global(), sw, sh, Some(data.to_vec())) + ImageData::new(&self.global(), size.width, size.height, Some(self.get_rect(read_rect))) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata @@ -1196,21 +1185,23 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { } // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata + #[allow(unsafe_code)] fn PutImageData_( &self, imagedata: &ImageData, dx: i32, dy: i32, - mut dirty_x: i32, - mut dirty_y: i32, - mut dirty_width: i32, - mut dirty_height: i32, + dirty_x: i32, + dirty_y: i32, + dirty_width: i32, + dirty_height: i32, ) { // 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 { + + let imagedata_size = Size2D::new(imagedata.Width(), imagedata.Height()); + if imagedata_size.area() == 0 { return; } @@ -1220,76 +1211,37 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { // Step 2. // TODO: throw InvalidState if buffer is detached. - // Step 3. - if dirty_width < 0 { - dirty_x += dirty_width; - dirty_width = -dirty_width; - } - if dirty_height < 0 { - dirty_y += dirty_height; - dirty_height = -dirty_height; - } - - // Ignore any pixel that would be drawn before the beginning of the - // canvas surface. - let mut dest_x = dx + dirty_x; - let mut dest_y = dy + dirty_y; - if dest_x < 0 { - dirty_x -= dest_x; - dirty_width += dest_x; - dest_x = 0; - } - if dest_y < 0 { - dirty_y -= dest_y; - dirty_height += dest_y; - dest_y = 0; - } - - // Step 4. - if dirty_x < 0 { - dirty_width += dirty_x; - dirty_x = 0; - } - if dirty_y < 0 { - dirty_height += dirty_y; - dirty_y = 0; - } - - // Step 5. - if dirty_x + dirty_width > imagedata_size.width { - dirty_width = imagedata_size.width - dirty_x; - } - if dirty_y + dirty_height > imagedata_size.height { - dirty_height = imagedata_size.height - dirty_y; - } + // 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()); - // We take care of ignoring any pixel that would be drawn after the end - // of the canvas surface. - let canvas_size = self.canvas.as_ref().map_or(Size2D::zero(), |c| c.get_size()).to_i32(); - let origin = Point2D::new(dest_x, dest_y); - let drawable_size = (origin - canvas_size.to_vector().to_point()).to_size().abs(); - dirty_width = dirty_width.min(drawable_size.width); - dirty_height = dirty_height.min(drawable_size.height); - - // Step 6. - if dirty_width <= 0 || dirty_height <= 0 { - 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(); + // Steps 3-6. + let (src_origin, src_size) = adjust_size_sign( + Point2D::new(dirty_x, dirty_y), + Size2D::new(dirty_width, dirty_height), + ); + let src_rect = match pixels::clip(src_origin, src_size, imagedata_size) { + Some(rect) => rect, + None => return, + }; + let (dst_origin, _) = adjust_size_sign( + Point2D::new(dirty_x.saturating_add(dx), dirty_y.saturating_add(dy)), + Size2D::new(dirty_width, dirty_height), + ); + // By clipping to the canvas surface, we avoid sending any pixel + // that would fall outside it. + let dst_rect = match pixels::clip(dst_origin, src_rect.size, canvas_size) { + Some(rect) => rect, + None => return, + }; // Step 7. - self.send_canvas_2d_msg(Canvas2dMsg::PutImageData( - buffer.into(), - origin.to_vector(), - imagedata_size, - Rect::new( - Point2D::new(dirty_x, dirty_y), - Size2D::new(dirty_width, dirty_height), - ), - )); + let (sender, receiver) = ipc::bytes_channel().unwrap(); + let pixels = unsafe { + &imagedata.get_rect(Rect::new(src_rect.origin, dst_rect.size)) + }; + self.send_canvas_2d_msg(Canvas2dMsg::PutImageData(dst_rect, receiver)); + sender.send(pixels).unwrap(); self.mark_as_dirty(); } @@ -1539,7 +1491,7 @@ fn is_rect_valid(rect: Rect<f64>) -> bool { rect.size.width > 0.0 && rect.size.height > 0.0 } -// https://html.spec.whatwg.org/multipage/#serialisation-of-a-colour +// https://html.spec.whatwg.org/multipage/#serialisation-of-a-color fn serialize<W>(color: &RGBA, dest: &mut W) -> fmt::Result where W: fmt::Write, @@ -1570,3 +1522,18 @@ where ) } } + +fn adjust_size_sign( + mut origin: Point2D<i32>, + mut size: Size2D<i32>, +) -> (Point2D<i32>, Size2D<u32>) { + if size.width < 0 { + size.width = -size.width; + origin.x = origin.x.saturating_sub(size.width); + } + if size.height < 0 { + size.height = -size.height; + origin.y = origin.y.saturating_sub(size.height); + } + (origin, size.to_u32()) +} diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index 09114a95d0a..8f572336573 100644 --- a/components/script/dom/htmlcanvaselement.rs +++ b/components/script/dom/htmlcanvaselement.rs @@ -7,14 +7,12 @@ use canvas_traits::canvas::{CanvasMsg, CanvasId, FromScriptMsg}; use canvas_traits::webgl::WebGLVersion; use dom::attr::Attr; use dom::bindings::cell::DomRefCell; -use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods; use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding; use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::{HTMLCanvasElementMethods, RenderingContext}; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes; use dom::bindings::conversions::ConversionResult; use dom::bindings::error::{Error, Fallible}; use dom::bindings::inheritance::Castable; -use dom::bindings::num::Finite; use dom::bindings::reflector::DomObject; use dom::bindings::root::{Dom, DomRoot, LayoutDom}; use dom::bindings::str::{DOMString, USVString}; @@ -28,7 +26,7 @@ use dom::virtualmethods::VirtualMethods; use dom::webgl2renderingcontext::WebGL2RenderingContext; use dom::webglrenderingcontext::{LayoutCanvasWebGLRenderingContextHelpers, WebGLRenderingContext}; use dom_struct::dom_struct; -use euclid::Size2D; +use euclid::{Rect, Size2D}; use html5ever::{LocalName, Prefix}; use image::ColorType; use image::png::PNGEncoder; @@ -355,10 +353,8 @@ impl HTMLCanvasElementMethods for HTMLCanvasElement { _quality: HandleValue, ) -> Fallible<USVString> { // Step 1. - if let Some(CanvasContext::Context2d(ref context)) = *self.context.borrow() { - if !context.origin_is_clean() { - return Err(Error::Security); - } + if !self.origin_is_clean() { + return Err(Error::Security); } // Step 2. @@ -367,24 +363,18 @@ impl HTMLCanvasElementMethods for HTMLCanvasElement { } // Step 3. - let raw_data = match *self.context.borrow() { + let file = match *self.context.borrow() { Some(CanvasContext::Context2d(ref context)) => { - let image_data = context.GetImageData( - Finite::wrap(0f64), - Finite::wrap(0f64), - Finite::wrap(self.Width() as f64), - Finite::wrap(self.Height() as f64), - )?; - image_data.get_data_array() + context.get_rect(Rect::from_size(self.get_size())) }, Some(CanvasContext::WebGL(ref context)) => { - match context.get_image_data(self.Width(), self.Height()) { + match context.get_image_data(self.get_size()) { Some(data) => data, None => return Ok(USVString("data:,".into())), } }, Some(CanvasContext::WebGL2(ref context)) => { - match context.base_context().get_image_data(self.Width(), self.Height()) { + match context.base_context().get_image_data(self.get_size()) { Some(data) => data, None => return Ok(USVString("data:,".into())), } @@ -397,8 +387,10 @@ impl HTMLCanvasElementMethods for HTMLCanvasElement { // FIXME: Only handle image/png for now. let mut png = Vec::new(); + // FIXME(nox): https://github.com/PistonDevelopers/image-png/issues/86 + // FIXME(nox): https://github.com/PistonDevelopers/image-png/issues/87 PNGEncoder::new(&mut png) - .encode(&raw_data, self.Width(), self.Height(), ColorType::RGBA(8)) + .encode(&file, self.Width(), self.Height(), ColorType::RGBA(8)) .unwrap(); let mut url = "data:image/png;base64,".to_owned(); // FIXME(nox): Should this use base64::URL_SAFE? diff --git a/components/script/dom/imagedata.rs b/components/script/dom/imagedata.rs index ea65d533ffe..f72ea49c0ff 100644 --- a/components/script/dom/imagedata.rs +++ b/components/script/dom/imagedata.rs @@ -9,10 +9,12 @@ 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}; +use pixels; +use std::borrow::Cow; use std::default::Default; use std::ptr; use std::ptr::NonNull; @@ -137,16 +139,31 @@ impl ImageData { Self::new_with_jsobject(global, width, opt_height, Some(jsobject)) } + /// Nothing must change the array on the JS side while the slice is live. #[allow(unsafe_code)] - pub fn get_data_array(&self) -> Vec<u8> { - unsafe { - assert!(!self.data.get().is_null()); - let cx = Runtime::get(); - assert!(!cx.is_null()); - typedarray!(in(cx) let array: Uint8ClampedArray = self.data.get()); - let vec = array.unwrap().as_slice().to_vec(); - vec - } + pub unsafe fn as_slice(&self) -> &[u8] { + assert!(!self.data.get().is_null()); + let cx = Runtime::get(); + assert!(!cx.is_null()); + typedarray!(in(cx) let array: Uint8ClampedArray = self.data.get()); + let array = array.as_ref().unwrap(); + // NOTE(nox): This is just as unsafe as `as_slice` itself even though we + // are extending the lifetime of the slice, because the data in + // this ImageData instance will never change. The method is thus unsafe + // because the array may be manipulated from JS while the reference + // is live. + let ptr = array.as_slice() as *const _; + &*ptr + } + + #[allow(unsafe_code)] + pub fn to_vec(&self) -> Vec<u8> { + unsafe { self.as_slice().into() } + } + + #[allow(unsafe_code)] + pub unsafe fn get_rect(&self, rect: Rect<u32>) -> Cow<[u8]> { + pixels::get_rect(self.as_slice(), self.get_size(), rect) } pub fn get_size(&self) -> Size2D<u32> { diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index d832776230d..c8f2b1fce4f 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -5,7 +5,6 @@ #[cfg(feature = "webgl_backtrace")] use backtrace::Backtrace; use byteorder::{ByteOrder, NativeEndian, WriteBytesExt}; -use canvas_traits::canvas::{byte_swap, multiply_u8_pixel}; use canvas_traits::webgl::{DOMToTextureCommand, Parameter, WebGLCommandBacktrace}; use canvas_traits::webgl::{TexParameter, WebGLCommand, WebGLContextShareMode, WebGLError}; use canvas_traits::webgl::{WebGLFramebufferBindingRequest, WebGLMsg, WebGLMsgSender}; @@ -53,7 +52,7 @@ use dom::webgluniformlocation::WebGLUniformLocation; use dom::webglvertexarrayobjectoes::WebGLVertexArrayObjectOES; use dom::window::Window; use dom_struct::dom_struct; -use euclid::Size2D; +use euclid::{Point2D, Rect, Size2D}; use half::f16; use ipc_channel::ipc; use js::jsapi::{JSContext, JSObject, Type}; @@ -65,6 +64,7 @@ use js::typedarray::{TypedArray, TypedArrayElementCreator}; use net_traits::image::base::PixelFormat; use net_traits::image_cache::ImageResponse; use offscreen_gl_context::{GLContextAttributes, GLLimits}; +use pixels; use script_layout_interface::HTMLCanvasDataSource; use serde::{Deserialize, Serialize}; use servo_config::prefs::PREFS; @@ -524,7 +524,7 @@ impl WebGLRenderingContext { ) -> Fallible<Option<(Vec<u8>, Size2D<u32>, bool)>> { Ok(Some(match source { TexImageSource::ImageData(image_data) => { - (image_data.get_data_array(), image_data.get_size(), false) + (image_data.to_vec(), image_data.get_size(), false) }, TexImageSource::HTMLImageElement(image) => { let document = document_from_node(&*self.canvas); @@ -554,7 +554,7 @@ impl WebGLRenderingContext { _ => unimplemented!(), }; - byte_swap(&mut data); + pixels::byte_swap_colors_inplace(&mut data); (data, size, false) }, @@ -567,7 +567,7 @@ impl WebGLRenderingContext { } if let Some((mut data, size)) = canvas.fetch_all_data() { // Pixels got from Canvas have already alpha premultiplied - byte_swap(&mut data); + pixels::byte_swap_colors_inplace(&mut data); (data, size, true) } else { return Ok(None); @@ -683,15 +683,11 @@ impl WebGLRenderingContext { match (format, data_type) { (TexFormat::RGBA, TexDataType::UnsignedByte) => { - for rgba in pixels.chunks_mut(4) { - rgba[0] = multiply_u8_pixel(rgba[0], rgba[3]); - rgba[1] = multiply_u8_pixel(rgba[1], rgba[3]); - rgba[2] = multiply_u8_pixel(rgba[2], rgba[3]); - } + pixels::premultiply_inplace(pixels); }, (TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => { for la in pixels.chunks_mut(2) { - la[0] = multiply_u8_pixel(la[0], la[1]); + la[0] = pixels::multiply_u8_color(la[0], la[1]); } }, (TexFormat::RGBA, TexDataType::UnsignedShort5551) => { @@ -711,9 +707,9 @@ impl WebGLRenderingContext { let a = extend_to_8_bits(pix & 0x0f); NativeEndian::write_u16( rgba, - ((multiply_u8_pixel(r, a) & 0xf0) as u16) << 8 | - ((multiply_u8_pixel(g, a) & 0xf0) as u16) << 4 | - ((multiply_u8_pixel(b, a) & 0xf0) as u16) | + ((pixels::multiply_u8_color(r, a) & 0xf0) as u16) << 8 | + ((pixels::multiply_u8_color(g, a) & 0xf0) as u16) << 4 | + ((pixels::multiply_u8_color(b, a) & 0xf0) as u16) | ((a & 0x0f) as u16), ); } @@ -1078,7 +1074,7 @@ impl WebGLRenderingContext { // can fail and that it is UB what happens in that case. // // https://www.khronos.org/registry/webgl/specs/latest/1.0/#2.2 - pub fn get_image_data(&self, width: u32, height: u32) -> Option<Vec<u8>> { + pub fn get_image_data(&self, mut size: Size2D<u32>) -> Option<Vec<u8>> { handle_potential_webgl_error!(self, self.validate_framebuffer(), return None); let (fb_width, fb_height) = handle_potential_webgl_error!( @@ -1086,15 +1082,12 @@ impl WebGLRenderingContext { self.get_current_framebuffer_size().ok_or(InvalidOperation), return None ); - let width = cmp::min(width, fb_width as u32); - let height = cmp::min(height, fb_height as u32); + size.width = cmp::min(size.width, fb_width as u32); + size.height = cmp::min(size.height, fb_height as u32); let (sender, receiver) = ipc::bytes_channel().unwrap(); self.send_command(WebGLCommand::ReadPixels( - 0, - 0, - width as i32, - height as i32, + Rect::from_size(size), constants::RGBA, constants::UNSIGNED_BYTE, sender, @@ -2887,45 +2880,29 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { return self.webgl_error(InvalidOperation); } - let mut src_x = x; - let mut src_y = y; - let mut src_width = width; - let mut src_height = height; - let mut dest_offset = 0; - - if src_x < 0 { - if src_width <= -src_x { - return; - } - dest_offset += bytes_per_pixel * -src_x; - src_width += src_x; - src_x = 0; - } - if src_y < 0 { - if src_height <= -src_y { - return; - } - dest_offset += row_len * -src_y; - src_height += src_y; - src_y = 0; - } + let src_origin = Point2D::new(x, y); + let src_size = Size2D::new(width as u32, height as u32); + let fb_size = Size2D::new(fb_width as u32, fb_height as u32); + let src_rect = match pixels::clip(src_origin, src_size, fb_size) { + Some(rect) => rect, + None => return, + }; - if src_x + src_width > fb_width { - src_width = fb_width - src_x; + let mut dest_offset = 0; + if x < 0 { + dest_offset += -x * bytes_per_pixel; } - if src_y + src_height > fb_height { - src_height = fb_height - src_y; + if y < 0 { + dest_offset += -y * row_len; } let (sender, receiver) = ipc::bytes_channel().unwrap(); - self.send_command(WebGLCommand::ReadPixels( - src_x, src_y, src_width, src_height, format, pixel_type, sender, - )); - + self.send_command(WebGLCommand::ReadPixels(src_rect, format, pixel_type, sender)); let src = receiver.recv().unwrap(); - let src_row_len = (src_width * bytes_per_pixel) as usize; - for i in 0..src_height { - let dest_start = (dest_offset + i * dest_stride) as usize; + + let src_row_len = src_rect.size.width as usize * bytes_per_pixel as usize; + for i in 0..src_rect.size.height { + let dest_start = dest_offset as usize + i as usize * dest_stride as usize; let dest_end = dest_start + src_row_len; let src_start = i as usize * src_row_len; let src_end = src_start + src_row_len; diff --git a/components/script/dom/webidls/CanvasRenderingContext2D.webidl b/components/script/dom/webidls/CanvasRenderingContext2D.webidl index f772bd2e8db..82001645924 100644 --- a/components/script/dom/webidls/CanvasRenderingContext2D.webidl +++ b/components/script/dom/webidls/CanvasRenderingContext2D.webidl @@ -175,11 +175,11 @@ interface CanvasDrawImage { interface CanvasImageData { // pixel manipulation [Throws] - ImageData createImageData(double sw, double sh); + ImageData createImageData(long sw, long sh); [Throws] ImageData createImageData(ImageData imagedata); [Throws] - ImageData getImageData(double sx, double sy, double sw, double sh); + ImageData getImageData(long sx, long sy, long sw, long sh); void putImageData(ImageData imagedata, long dx, long dy); void putImageData(ImageData imagedata, long dx, long dy, |