diff options
author | bors-servo <metajack+bors@gmail.com> | 2015-03-24 14:01:06 -0600 |
---|---|---|
committer | bors-servo <metajack+bors@gmail.com> | 2015-03-24 14:01:06 -0600 |
commit | 2ab1ece765a50e26c2908bcbe5463ff1fda0b085 (patch) | |
tree | 44409f57d9174202890610385d9aa0d1813fadca /components/script | |
parent | 2c51d0ef53d25892be4c992fdbe131c1b641bf74 (diff) | |
parent | e3f5a76baacfdd7b865bd0e01c31f4dd741b4976 (diff) | |
download | servo-2ab1ece765a50e26c2908bcbe5463ff1fda0b085.tar.gz servo-2ab1ece765a50e26c2908bcbe5463ff1fda0b085.zip |
auto merge of #5231 : dmarcos/servo/issue4784, r=jdm
...ce
Diffstat (limited to 'components/script')
-rw-r--r-- | components/script/dom/canvasrenderingcontext2d.rs | 234 | ||||
-rw-r--r-- | components/script/dom/htmlcanvaselement.rs | 10 | ||||
-rw-r--r-- | components/script/dom/webidls/CanvasRenderingContext2D.webidl | 19 |
3 files changed, 254 insertions, 9 deletions
diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 4c2e05b4b9e..58f3b51ab05 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -6,6 +6,7 @@ use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding; use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods; use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasWindingRule; use dom::bindings::codegen::Bindings::ImageDataBinding::ImageDataMethods; +use dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrCanvasRenderingContext2D; use dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern; use dom::bindings::error::Error::{IndexSize, TypeError}; use dom::bindings::error::Fallible; @@ -37,6 +38,7 @@ pub struct CanvasRenderingContext2D { global: GlobalField, renderer: Sender<CanvasMsg>, canvas: JS<HTMLCanvasElement>, + image_smoothing_enabled: Cell<bool>, stroke_color: Cell<RGBA>, fill_color: Cell<RGBA>, transform: Cell<Matrix2D<f32>>, @@ -56,6 +58,7 @@ impl CanvasRenderingContext2D { global: GlobalField::from_rooted(&global), renderer: CanvasPaintTask::start(size), canvas: JS::from_rooted(canvas), + image_smoothing_enabled: Cell::new(true), stroke_color: Cell::new(black), fill_color: Cell::new(black), transform: Cell::new(Matrix2D::identity()), @@ -75,6 +78,120 @@ impl CanvasRenderingContext2D { fn update_transform(&self) { self.renderer.send(CanvasMsg::SetTransform(self.transform.get())).unwrap() } + + // It is used by DrawImage to calculate the size of the source and destination rectangles based + // on the drawImage call arguments + // source rectangle = area of the original image to be copied + // destination rectangle = area of the destination canvas where the source image is going to be drawn + fn adjust_source_dest_rects(&self, + canvas: JSRef<HTMLCanvasElement>, + sx: f64, sy: f64, sw: f64, sh: f64, + dx: f64, dy: f64, dw: f64, dh: f64) -> (Rect<i32>, Rect<i32>) { + let image_size = canvas.get_size(); + let image_rect = Rect(Point2D(0f64, 0f64), + Size2D(image_size.width as f64, image_size.height as f64)); + + // The source rectangle is the rectangle whose corners are the four points (sx, sy), + // (sx+sw, sy), (sx+sw, sy+sh), (sx, sy+sh). + let source_rect = Rect(Point2D(sx, sy), + Size2D(sw, sh)); + + // When the source rectangle is outside the source image, + // the source rectangle must be clipped to the source image + let source_rect_clipped = source_rect.intersection(&image_rect).unwrap_or(Rect::zero()); + + // Width and height ratios between the non clipped and clipped source rectangles + let width_ratio: f64 = source_rect_clipped.size.width / source_rect.size.width; + let height_ratio: f64 = source_rect_clipped.size.height / source_rect.size.height; + + // When the source rectangle is outside the source image, + // the destination rectangle must be clipped in the same proportion. + let dest_rect_width_scaled: f64 = dw * width_ratio; + let dest_rect_height_scaled: f64 = dh * height_ratio; + + // The destination rectangle is the rectangle whose corners are the four points (dx, dy), + // (dx+dw, dy), (dx+dw, dy+dh), (dx, dy+dh). + let dest_rect = Rect(Point2D(dx.to_i32().unwrap(), + dy.to_i32().unwrap()), + Size2D(dest_rect_width_scaled.to_i32().unwrap(), + dest_rect_height_scaled.to_i32().unwrap())); + + let source_rect = Rect(Point2D(source_rect_clipped.origin.x.to_i32().unwrap(), + source_rect_clipped.origin.y.to_i32().unwrap()), + Size2D(source_rect_clipped.size.width.to_i32().unwrap(), + source_rect_clipped.size.height.to_i32().unwrap())); + + return (source_rect, dest_rect) + } + + // + // drawImage coordinates explained + // + // Source Image Destination Canvas + // +-------------+ +-------------+ + // | | | | + // |(sx,sy) | |(dx,dy) | + // | +----+ | | +----+ | + // | | | | | | | | + // | | |sh |---->| | |dh | + // | | | | | | | | + // | +----+ | | +----+ | + // | sw | | dw | + // | | | | + // +-------------+ +-------------+ + // + // + // The rectangle (sx, sy, sw, sh) from the source image + // is copied on the rectangle (dx, dy, dh, dw) of the destination canvas + // + // https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-drawimage + fn draw_html_canvas_element(&self, + canvas: JSRef<HTMLCanvasElement>, + sx: f64, sy: f64, sw: f64, sh: f64, + dx: f64, dy: f64, dw: f64, dh: f64) -> Fallible<()> { + + // 1. Check the usability of the image argument + if !canvas.is_valid() { + return Ok(()) + } + + // 2. Establish the source and destination rectangles + let (source_rect, dest_rect) = self.adjust_source_dest_rects(canvas, sx, sy, sw, sh, dx, dy, dw, dh); + + if !is_rect_valid(source_rect) || !is_rect_valid(dest_rect) { + return Err(IndexSize) + } + + let smoothing_enabled = self.image_smoothing_enabled.get(); + let canvas_size = canvas.get_size(); + + // If the source and target canvas are the same + let msg = if self.canvas.root().r() == canvas { + CanvasMsg::DrawImageSelf(canvas_size, dest_rect, source_rect, smoothing_enabled) + } else { // Source and target canvases are different + let context = canvas.get_2d_context().root(); + let renderer = context.r().get_renderer(); + let (sender, receiver) = channel::<Vec<u8>>(); + // Reads pixels from source image + renderer.send(CanvasMsg::GetImageData(source_rect, canvas_size, sender)).unwrap(); + let imagedata = receiver.recv().unwrap(); + // Writes pixels to destination canvas + CanvasMsg::DrawImage(imagedata, dest_rect, source_rect, smoothing_enabled) + }; + + self.renderer.send(msg).unwrap(); + Ok(()) + } +} + +pub trait CanvasRenderingContext2DHelpers { + fn get_renderer(&self) -> Sender<CanvasMsg>; +} + +impl CanvasRenderingContext2DHelpers for CanvasRenderingContext2D { + fn get_renderer(&self) -> Sender<CanvasMsg> { + self.renderer.clone() + } } pub trait LayoutCanvasRenderingContext2DHelpers { @@ -151,6 +268,102 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D> self.renderer.send(CanvasMsg::Fill).unwrap(); } + // https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-drawimage + fn DrawImage(self, image: HTMLCanvasElementOrCanvasRenderingContext2D, + dx: f64, dy: f64) -> Fallible<()> { + + // From rules described in the spec: + // If the sx, sy, sw, and sh arguments are omitted, they must default to 0, 0, + // the image's intrinsic width in image pixels, + // and the image's intrinsic height in image pixels, respectively + let sx: f64 = 0f64; + let sy: f64 = 0f64; + + match image { + HTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(image) => { + let canvas = image.root(); + let canvas_size = canvas.r().get_size(); + let dw: f64 = canvas_size.width as f64; + let dh: f64 = canvas_size.height as f64; + let sw: f64 = dw; + let sh: f64 = dh; + return self.draw_html_canvas_element(canvas.r(), + sx, sy, sw, sh, + dx, dy, dw, dh) + } + HTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => { + let image = image.root(); + let context = image.r(); + let canvas = context.Canvas().root(); + let canvas_size = canvas.r().get_size(); + let dw: f64 = canvas_size.width as f64; + let dh: f64 = canvas_size.height as f64; + let sw: f64 = dw; + let sh: f64 = dh; + return self.draw_html_canvas_element(canvas.r(), + sx, sy, sw, sh, + dx, dy, dw, dh) + } + } + } + + // https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-drawimage + fn DrawImage_(self, image: HTMLCanvasElementOrCanvasRenderingContext2D, + dx: f64, dy: f64, dw: f64, dh: f64) -> Fallible<()> { + + // From rules described in the spec: + // If the sx, sy, sw, and sh arguments are omitted, they must default to 0, 0, + // the image's intrinsic width in image pixels, + // and the image's intrinsic height in image pixels, respectively + let sx: f64 = 0f64; + let sy: f64 = 0f64; + + match image { + HTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(image) => { + let canvas = image.root(); + let canvas_size = canvas.r().get_size(); + let sw: f64 = canvas_size.width as f64; + let sh: f64 = canvas_size.height as f64; + return self.draw_html_canvas_element(canvas.r(), + sx, sy, sw, sh, + dx, dy, dw, dh) + } + HTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => { + let image = image.root(); + let context = image.r(); + let canvas = context.Canvas().root(); + let canvas_size = canvas.r().get_size(); + let sw: f64 = canvas_size.width as f64; + let sh: f64 = canvas_size.height as f64; + return self.draw_html_canvas_element(canvas.r(), + sx, sy, sw, sh, + dx, dy, dw, dh) + } + } + } + + // https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-drawimage + fn DrawImage__(self, image: HTMLCanvasElementOrCanvasRenderingContext2D, + sx: f64, sy: f64, sw: f64, sh: f64, + dx: f64, dy: f64, dw: f64, dh: f64) -> Fallible<()> { + match image { + HTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(image) => { + let canvas = image.root(); + return self.draw_html_canvas_element(canvas.r(), + sx, sy, sw, sh, + dx, dy, dw, dh) + } + HTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => { + let image = image.root(); + let context = image.r(); + let canvas = context.Canvas().root(); + return self.draw_html_canvas_element(canvas.r(), + sx, sy, sw, sh, + dx, dy, dw, dh) + } + } + } + fn MoveTo(self, x: f64, y: f64) { self.renderer.send(CanvasMsg::MoveTo(Point2D(x as f32, y as f32))).unwrap(); } @@ -175,6 +388,16 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D> start as f32, end as f32, ccw)).unwrap(); } + // https://html.spec.whatwg.org/#dom-context-2d-imagesmoothingenabled + fn ImageSmoothingEnabled(self) -> bool { + self.image_smoothing_enabled.get() + } + + // https://html.spec.whatwg.org/#dom-context-2d-imagesmoothingenabled + fn SetImageSmoothingEnabled(self, value: bool) -> () { + self.image_smoothing_enabled.set(value); + } + fn StrokeStyle(self) -> StringOrCanvasGradientOrCanvasPattern { // FIXME(pcwalton, #4761): This is not spec-compliant. See: // @@ -261,8 +484,7 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D> let data = imagedata.get_data_array(&self.global.root().r()); let image_data_rect = Rect(Point2D(dx.to_i32().unwrap(), dy.to_i32().unwrap()), imagedata.get_size()); let dirty_rect = None; - let canvas_size = self.canvas.root().r().get_size(); - self.renderer.send(CanvasMsg::PutImageData(data, image_data_rect, dirty_rect, canvas_size)).unwrap() + self.renderer.send(CanvasMsg::PutImageData(data, image_data_rect, dirty_rect)).unwrap() } fn PutImageData_(self, imagedata: JSRef<ImageData>, dx: f64, dy: f64, @@ -274,8 +496,7 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D> let dirty_rect = Some(Rect(Point2D(dirtyX.to_i32().unwrap(), dirtyY.to_i32().unwrap()), Size2D(dirtyWidth.to_i32().unwrap(), dirtyHeight.to_i32().unwrap()))); - let canvas_size = self.canvas.root().r().get_size(); - self.renderer.send(CanvasMsg::PutImageData(data, image_data_rect, dirty_rect, canvas_size)).unwrap() + self.renderer.send(CanvasMsg::PutImageData(data, image_data_rect, dirty_rect)).unwrap() } fn CreateLinearGradient(self, x0: f64, y0: f64, x1: f64, y1: f64) -> Fallible<Temporary<CanvasGradient>> { @@ -309,3 +530,8 @@ pub fn parse_color(string: &str) -> Result<RGBA,()> { } } +// Used by drawImage to determine if a source or destination rectangle is valid +// Origin coordinates and size cannot be negative. Size has to be greater than zero +fn is_rect_valid(rect: Rect<i32>) -> bool { + rect.origin.x >= 0 && rect.origin.y >= 0 && rect.size.width > 0 && rect.size.height > 0 +} diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index 9c3c11d5dbc..e4fc68565a3 100644 --- a/components/script/dom/htmlcanvaselement.rs +++ b/components/script/dom/htmlcanvaselement.rs @@ -91,12 +91,22 @@ impl LayoutHTMLCanvasElementHelpers for LayoutJS<HTMLCanvasElement> { pub trait HTMLCanvasElementHelpers { fn get_size(&self) -> Size2D<i32>; + fn get_2d_context(self) -> Temporary<CanvasRenderingContext2D>; + fn is_valid(self) -> bool; } impl<'a> HTMLCanvasElementHelpers for JSRef<'a, HTMLCanvasElement> { fn get_size(&self) -> Size2D<i32> { Size2D(self.Width() as i32, self.Height() as i32) } + + fn get_2d_context(self) -> Temporary<CanvasRenderingContext2D> { + self.GetContext(String::from_str("2d")).unwrap() + } + + fn is_valid(self) -> bool { + self.height.get() != 0 && self.width.get() != 0 + } } impl<'a> HTMLCanvasElementMethods for JSRef<'a, HTMLCanvasElement> { diff --git a/components/script/dom/webidls/CanvasRenderingContext2D.webidl b/components/script/dom/webidls/CanvasRenderingContext2D.webidl index 1cc40eb0f3f..0218600bba1 100644 --- a/components/script/dom/webidls/CanvasRenderingContext2D.webidl +++ b/components/script/dom/webidls/CanvasRenderingContext2D.webidl @@ -6,6 +6,12 @@ enum CanvasWindingRule { "nonzero", "evenodd" }; // http://www.whatwg.org/html/#2dcontext +typedef (/* HTMLImageElement or + HTMLVideoElement or */ + HTMLCanvasElement or + CanvasRenderingContext2D /* or + ImageBitmap */) CanvasImageSource; + //[Constructor(optional unsigned long width, unsigned long height), Exposed=Window,Worker] interface CanvasRenderingContext2D { @@ -47,7 +53,7 @@ interface CanvasRenderingContext2D { // attribute DOMString globalCompositeOperation; // (default source-over) // image smoothing - // attribute boolean imageSmoothingEnabled; // (default true) + attribute boolean imageSmoothingEnabled; // (default true) // colours and styles (see also the CanvasDrawingStyles interface) attribute (DOMString or CanvasGradient or CanvasPattern) strokeStyle; // (default black) @@ -101,9 +107,12 @@ interface CanvasRenderingContext2D { //TextMetrics measureText(DOMString text); // drawing images - //void drawImage(CanvasImageSource image, unrestricted double dx, unrestricted double dy); - //void drawImage(CanvasImageSource image, unrestricted double dx, unrestricted double dy, unrestricted double dw, unrestricted double dh); - //void drawImage(CanvasImageSource image, unrestricted double sx, unrestricted double sy, unrestricted double sw, unrestricted double sh, unrestricted double dx, unrestricted double dy, unrestricted double dw, unrestricted double dh); + [Throws] + void drawImage(CanvasImageSource image, /* unrestricted */ double dx, /* unrestricted */ double dy); + [Throws] + void drawImage(CanvasImageSource image, /* unrestricted */ double dx, /* unrestricted */ double dy, /* unrestricted */ double dw, /* unrestricted */ double dh); + [Throws] + void drawImage(CanvasImageSource image, /* unrestricted */ double sx, /* unrestricted */ double sy, /* unrestricted */ double sw, /* unrestricted */ double sh, /* unrestricted */ double dx, /* unrestricted */ double dy, /* unrestricted */ double dw, /* unrestricted */ double dh); // hit regions //void addHitRegion(optional HitRegionOptions options); @@ -135,7 +144,7 @@ interface CanvasPathMethods { /*unrestricted*/ double x, /*unrestricted*/ double y); - //void arcTo(double x1, double y1, double x2, double y2, double radius); + //void arcTo(double x1, double y1, double x2, double y2, double radius); // NOT IMPLEMENTED [LenientFloat] void arcTo(double x1, double y1, double x2, double y2, double radiusX, double radiusY, double rotation); //void rect(double x, double y, double w, double h); |