diff options
Diffstat (limited to 'components/script/dom/canvasrenderingcontext2d.rs')
-rw-r--r-- | components/script/dom/canvasrenderingcontext2d.rs | 223 |
1 files changed, 77 insertions, 146 deletions
diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 834e9606c49..c9f8da05bff 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -42,6 +42,7 @@ use num::{Float, ToPrimitive}; use std::borrow::ToOwned; use std::cell::RefCell; use std::fmt; +use std::cmp; use std::sync::mpsc::channel; use util::str::DOMString; @@ -219,16 +220,62 @@ impl CanvasRenderingContext2D { // is copied on the rectangle (dx, dy, dh, dw) of the destination canvas // // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage + fn draw_image(&self, image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D, + sx: f64, sy: f64, sw: Option<f64>, sh: Option<f64>, + dx: f64, dy: f64, dw: Option<f64>, dh: Option<f64>) -> Fallible<()> { + match image { + HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(canvas) => + self.draw_html_canvas_element(canvas.r(), + sx, sy, sw, sh, + dx, dy, dw, dh), + HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => { + let context = image.r(); + let canvas = context.Canvas(); + self.draw_html_canvas_element(canvas.r(), + sx, sy, sw, sh, + dx, dy, dw, dh) + } + HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(image) => { + let image_element = image.r(); + // https://html.spec.whatwg.org/multipage/#img-error + // If the image argument is an HTMLImageElement object that is in the broken state, + // then throw an InvalidStateError exception + let (image_data, image_size) = match self.fetch_image_data(&image_element) { + 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(&mut data); + (data, size) + }, + None => return Err(InvalidState), + }; + let dw = dw.unwrap_or(image_size.width); + let dh = dh.unwrap_or(image_size.height); + let sw = sw.unwrap_or(image_size.width); + let sh = sh.unwrap_or(image_size.height); + self.draw_image_data(image_data, + image_size, + sx, sy, sw, sh, + dx, dy, dw, dh) + } + } + } + fn draw_html_canvas_element(&self, canvas: &HTMLCanvasElement, - sx: f64, sy: f64, sw: f64, sh: f64, - dx: f64, dy: f64, dw: f64, dh: f64) -> Fallible<()> { + sx: f64, sy: f64, sw: Option<f64>, sh: Option<f64>, + dx: f64, dy: f64, dw: Option<f64>, dh: Option<f64>) -> Fallible<()> { // 1. Check the usability of the image argument if !canvas.is_valid() { return Err(InvalidState) } let canvas_size = canvas.get_size(); + let dw = dw.unwrap_or(canvas_size.width as f64); + let dh = dh.unwrap_or(canvas_size.height as f64); + let sw = sw.unwrap_or(canvas_size.width as f64); + let sh = sh.unwrap_or(canvas_size.height as f64); + let image_size = Size2D::new(canvas_size.width as f64, canvas_size.height as f64); // 2. Establish the source and destination rectangles let (source_rect, dest_rect) = self.adjust_source_dest_rects(image_size, sx, sy, sw, sh, dx, dy, dw, dh); @@ -597,61 +644,7 @@ impl<'a> CanvasRenderingContext2DMethods for &'a CanvasRenderingContext2D { return Ok(()); } - // 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 { - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(canvas) => { - 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) - } - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => { - let context = image.r(); - let canvas = context.Canvas(); - 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) - } - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(image) => { - let image_element = image.r(); - // https://html.spec.whatwg.org/multipage/#img-error - // If the image argument is an HTMLImageElement object that is in the broken state, - // then throw an InvalidStateError exception - let (image_data, image_size) = match self.fetch_image_data(&image_element) { - 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(&mut data); - (data, size) - }, - None => return Err(InvalidState), - }; - let dw: f64 = image_size.width as f64; - let dh: f64 = image_size.height as f64; - let sw: f64 = dw; - let sh: f64 = dh; - return self.draw_image_data(image_data, - image_size, - sx, sy, sw, sh, - dx, dy, dw, dh) - } - - } + self.draw_image(image, 0f64, 0f64, None, None, dx, dy, None, None) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage @@ -662,98 +655,19 @@ impl<'a> CanvasRenderingContext2DMethods for &'a CanvasRenderingContext2D { return Ok(()); } - // 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 { - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(canvas) => { - 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) - } - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => { - let context = image.r(); - let canvas = context.Canvas(); - 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) - } - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(image) => { - let image_element = image.r(); - // https://html.spec.whatwg.org/multipage/#img-error - // If the image argument is an HTMLImageElement object that is in the broken state, - // then throw an InvalidStateError exception - let (image_data, image_size) = match self.fetch_image_data(&image_element) { - 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(&mut data); - (data, size) - }, - None => return Err(InvalidState), - }; - let sw: f64 = image_size.width as f64; - let sh: f64 = image_size.height as f64; - return self.draw_image_data(image_data, - image_size, - sx, sy, sw, sh, - dx, dy, dw, dh) - } - } + self.draw_image(image, 0f64, 0f64, None, None, dx, dy, Some(dw), Some(dh)) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage fn DrawImage__(self, image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D, - sx: f64, sy: f64, sw: f64, sh: f64, - dx: f64, dy: f64, dw: f64, dh: f64) -> Fallible<()> { + sx: f64, sy: f64, sw: f64, sh: f64, + dx: f64, dy: f64, dw: f64, dh: f64) -> Fallible<()> { if !(sx.is_finite() && sy.is_finite() && sw.is_finite() && sh.is_finite() && dx.is_finite() && dy.is_finite() && dw.is_finite() && dh.is_finite()) { return Ok(()); } - match image { - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(image) => { - return self.draw_html_canvas_element(image.r(), - sx, sy, sw, sh, - dx, dy, dw, dh) - } - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => { - let context = image.r(); - let canvas = context.Canvas(); - return self.draw_html_canvas_element(canvas.r(), - sx, sy, sw, sh, - dx, dy, dw, dh) - } - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(image) => { - let image_element = image.r(); - // https://html.spec.whatwg.org/multipage/#img-error - // If the image argument is an HTMLImageElement object that is in the broken state, - // then throw an InvalidStateError exception - let (image_data, image_size) = match self.fetch_image_data(&image_element) { - 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(&mut data); - (data, size) - }, - None => return Err(InvalidState), - }; - return self.draw_image_data(image_data, - image_size, - sx, sy, sw, sh, - dx, dy, dw, dh) - } - } + self.draw_image(image, sx, sy, Some(sw), Some(sh), dx, dy, Some(dw), Some(dh)) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-moveto @@ -954,7 +868,9 @@ impl<'a> CanvasRenderingContext2DMethods for &'a CanvasRenderingContext2D { return Err(IndexSize) } - Ok(ImageData::new(self.global.root().r(), sw.abs().to_u32().unwrap(), sh.abs().to_u32().unwrap(), None)) + let sw = cmp::max(1, sw.abs().to_u32().unwrap()); + let sh = cmp::max(1, sh.abs().to_u32().unwrap()); + Ok(ImageData::new(self.global.root().r(), sw, sh, None)) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-createimagedata @@ -985,7 +901,18 @@ impl<'a> CanvasRenderingContext2DMethods for &'a CanvasRenderingContext2D { self.ipc_renderer .send(CanvasMsg::Canvas2d(Canvas2dMsg::GetImageData(dest_rect, canvas_size, sender))) .unwrap(); - let data = receiver.recv().unwrap(); + let mut data = receiver.recv().unwrap(); + + // Un-premultiply alpha + // TODO: may want a precomputed un-premultiply table to make this fast. + // https://github.com/servo/servo/issues/6969 + for chunk in data.chunks_mut(4) { + let alpha = chunk[3] as f32 / 255.; + chunk[0] = (chunk[0] as f32 / alpha) as u8; + chunk[1] = (chunk[1] as f32 / alpha) as u8; + chunk[2] = (chunk[2] as f32 / alpha) as u8; + } + Ok(ImageData::new(self.global.root().r(), sw.abs().to_u32().unwrap(), sh.abs().to_u32().unwrap(), Some(data))) } @@ -1019,10 +946,14 @@ impl<'a> CanvasRenderingContext2DMethods for &'a CanvasRenderingContext2D { // https://html.spec.whatwg.org/multipage/#dom-context-2d-createradialgradient fn CreateRadialGradient(self, x0: Finite<f64>, y0: Finite<f64>, r0: Finite<f64>, x1: Finite<f64>, y1: Finite<f64>, r1: Finite<f64>) - -> Root<CanvasGradient> { - CanvasGradient::new(self.global.root().r(), - CanvasGradientStyle::Radial( - RadialGradientStyle::new(*x0, *y0, *r0, *x1, *y1, *r1, Vec::new()))) + -> Fallible<Root<CanvasGradient>> { + if *r0 < 0. || *r1 < 0. { + return Err(IndexSize); + } + + Ok(CanvasGradient::new(self.global.root().r(), + CanvasGradientStyle::Radial( + RadialGradientStyle::new(*x0, *y0, *r0, *x1, *y1, *r1, Vec::new())))) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-createpattern |