diff options
Diffstat (limited to 'components/script/dom/canvasrenderingcontext2d.rs')
-rw-r--r-- | components/script/dom/canvasrenderingcontext2d.rs | 1403 |
1 files changed, 354 insertions, 1049 deletions
diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 432dbe800aa..3ebf8f130d9 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -1,527 +1,157 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * 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/. */ - -use canvas_traits::{Canvas2dMsg, CanvasCommonMsg, CanvasMsg}; -use canvas_traits::{CompositionOrBlending, FillOrStrokeStyle, FillRule}; -use canvas_traits::{LineCapStyle, LineJoinStyle, LinearGradientStyle}; -use canvas_traits::{RadialGradientStyle, RepetitionStyle, byte_swap_and_premultiply}; -use cssparser::{Parser, RGBA}; -use cssparser::Color as CSSColor; -use dom::bindings::cell::DOMRefCell; -use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods; -use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding; -use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasFillRule; -use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineCap; -use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineJoin; -use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods; -use dom::bindings::codegen::Bindings::ImageDataBinding::ImageDataMethods; -use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; -use dom::bindings::codegen::UnionTypes::HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D; -use dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern; -use dom::bindings::error::{Error, ErrorResult, Fallible}; -use dom::bindings::inheritance::Castable; -use dom::bindings::js::{JS, LayoutJS, Root}; -use dom::bindings::num::Finite; -use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object}; -use dom::bindings::str::DOMString; -use dom::canvasgradient::{CanvasGradient, CanvasGradientStyle, ToFillOrStrokeStyle}; -use dom::canvaspattern::CanvasPattern; -use dom::globalscope::GlobalScope; -use dom::htmlcanvaselement::HTMLCanvasElement; -use dom::htmlcanvaselement::utils as canvas_utils; -use dom::htmlimageelement::HTMLImageElement; -use dom::imagedata::ImageData; -use dom::node::{Node, NodeDamage, window_from_node}; + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::canvas_state::CanvasState; +use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasDirection; +use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasFillRule; +use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasImageSource; +use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineCap; +use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineJoin; +use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods; +use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasTextAlign; +use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasTextBaseline; +use crate::dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern; +use crate::dom::bindings::error::{ErrorResult, Fallible}; +use crate::dom::bindings::num::Finite; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; +use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom}; +use crate::dom::bindings::str::DOMString; +use crate::dom::canvasgradient::CanvasGradient; +use crate::dom::canvaspattern::CanvasPattern; +use crate::dom::dommatrix::DOMMatrix; +use crate::dom::globalscope::GlobalScope; +use crate::dom::htmlcanvaselement::HTMLCanvasElement; +use crate::dom::imagedata::ImageData; +use crate::dom::textmetrics::TextMetrics; +use canvas_traits::canvas::{Canvas2dMsg, CanvasId, CanvasMsg}; use dom_struct::dom_struct; -use euclid::matrix2d::Matrix2D; -use euclid::point::Point2D; -use euclid::rect::Rect; -use euclid::size::Size2D; -use ipc_channel::ipc::{self, IpcSender}; -use net_traits::image::base::PixelFormat; -use net_traits::image_cache::ImageResponse; -use num_traits::ToPrimitive; -use script_traits::ScriptMsg as ConstellationMsg; +use euclid::default::{Point2D, Rect, Size2D}; +use ipc_channel::ipc::IpcSender; use servo_url::ServoUrl; -use std::{cmp, fmt}; -use std::cell::Cell; -use std::str::FromStr; -use unpremultiplytable::UNPREMULTIPLY_TABLE; - -#[must_root] -#[derive(JSTraceable, Clone, HeapSizeOf)] -#[allow(dead_code)] -enum CanvasFillOrStrokeStyle { - Color(RGBA), - Gradient(JS<CanvasGradient>), - Pattern(JS<CanvasPattern>), -} +use std::mem; // https://html.spec.whatwg.org/multipage/#canvasrenderingcontext2d #[dom_struct] pub struct CanvasRenderingContext2D { reflector_: Reflector, - #[ignore_heap_size_of = "Defined in ipc-channel"] - ipc_renderer: IpcSender<CanvasMsg>, - canvas: JS<HTMLCanvasElement>, - state: DOMRefCell<CanvasContextState>, - saved_states: DOMRefCell<Vec<CanvasContextState>>, - origin_clean: Cell<bool>, -} - -#[must_root] -#[derive(JSTraceable, Clone, HeapSizeOf)] -struct CanvasContextState { - global_alpha: f64, - global_composition: CompositionOrBlending, - image_smoothing_enabled: bool, - fill_style: CanvasFillOrStrokeStyle, - stroke_style: CanvasFillOrStrokeStyle, - line_width: f64, - line_cap: LineCapStyle, - line_join: LineJoinStyle, - miter_limit: f64, - transform: Matrix2D<f32>, - shadow_offset_x: f64, - shadow_offset_y: f64, - shadow_blur: f64, - shadow_color: RGBA, -} - -impl CanvasContextState { - fn new() -> CanvasContextState { - let black = RGBA::new(0, 0, 0, 255); - CanvasContextState { - global_alpha: 1.0, - global_composition: CompositionOrBlending::default(), - image_smoothing_enabled: true, - fill_style: CanvasFillOrStrokeStyle::Color(black), - stroke_style: CanvasFillOrStrokeStyle::Color(black), - line_width: 1.0, - line_cap: LineCapStyle::Butt, - line_join: LineJoinStyle::Miter, - miter_limit: 10.0, - transform: Matrix2D::identity(), - shadow_offset_x: 0.0, - shadow_offset_y: 0.0, - shadow_blur: 0.0, - shadow_color: RGBA::transparent(), - } - } + /// For rendering contexts created by an HTML canvas element, this is Some, + /// for ones created by a paint worklet, this is None. + canvas: Option<Dom<HTMLCanvasElement>>, + canvas_state: CanvasState, } impl CanvasRenderingContext2D { - fn new_inherited(global: &GlobalScope, - canvas: &HTMLCanvasElement, - size: Size2D<i32>) - -> CanvasRenderingContext2D { - let (sender, receiver) = ipc::channel().unwrap(); - let constellation_chan = global.constellation_chan(); - constellation_chan.send(ConstellationMsg::CreateCanvasPaintThread(size, sender)).unwrap(); - let ipc_renderer = receiver.recv().unwrap(); + pub fn new_inherited( + global: &GlobalScope, + canvas: Option<&HTMLCanvasElement>, + size: Size2D<u32>, + ) -> CanvasRenderingContext2D { CanvasRenderingContext2D { reflector_: Reflector::new(), - ipc_renderer: ipc_renderer, - canvas: JS::from_ref(canvas), - state: DOMRefCell::new(CanvasContextState::new()), - saved_states: DOMRefCell::new(Vec::new()), - origin_clean: Cell::new(true), + canvas: canvas.map(Dom::from_ref), + canvas_state: CanvasState::new( + global, + Size2D::new(size.width as u64, size.height as u64), + ), } } - pub fn new(global: &GlobalScope, - canvas: &HTMLCanvasElement, - size: Size2D<i32>) - -> Root<CanvasRenderingContext2D> { - reflect_dom_object(box CanvasRenderingContext2D::new_inherited(global, canvas, size), - global, - CanvasRenderingContext2DBinding::Wrap) + pub fn new( + global: &GlobalScope, + canvas: &HTMLCanvasElement, + size: Size2D<u32>, + ) -> DomRoot<CanvasRenderingContext2D> { + let boxed = Box::new(CanvasRenderingContext2D::new_inherited( + global, + Some(canvas), + size, + )); + reflect_dom_object(boxed, global) } // https://html.spec.whatwg.org/multipage/#concept-canvas-set-bitmap-dimensions - pub fn set_bitmap_dimensions(&self, size: Size2D<i32>) { + pub fn set_bitmap_dimensions(&self, size: Size2D<u32>) { self.reset_to_initial_state(); - self.ipc_renderer - .send(CanvasMsg::Common(CanvasCommonMsg::Recreate(size))) + self.canvas_state + .get_ipc_renderer() + .send(CanvasMsg::Recreate( + size.to_u64(), + self.canvas_state.get_canvas_id(), + )) .unwrap(); } // https://html.spec.whatwg.org/multipage/#reset-the-rendering-context-to-its-default-state fn reset_to_initial_state(&self) { - self.saved_states.borrow_mut().clear(); - *self.state.borrow_mut() = CanvasContextState::new(); - } - - pub fn ipc_renderer(&self) -> IpcSender<CanvasMsg> { - self.ipc_renderer.clone() - } - - fn mark_as_dirty(&self) { - self.canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage); - } - - fn update_transform(&self) { - self.ipc_renderer - .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetTransform(self.state.borrow().transform))) - .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, - image_size: Size2D<f64>, - sx: f64, - sy: f64, - sw: f64, - sh: f64, - dx: f64, - dy: f64, - dw: f64, - dh: f64) - -> (Rect<f64>, Rect<f64>) { - let image_rect = Rect::new(Point2D::new(0f64, 0f64), - Size2D::new(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::new(Point2D::new(sx.min(sx + sw), sy.min(sy + sh)), - Size2D::new(sw.abs(), sh.abs())); - - // 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::new(Point2D::new(dx.min(dx + dest_rect_width_scaled), - dy.min(dy + dest_rect_height_scaled)), - Size2D::new(dest_rect_width_scaled.abs(), - dest_rect_height_scaled.abs())); - - let source_rect = Rect::new(Point2D::new(source_rect_clipped.origin.x, - source_rect_clipped.origin.y), - Size2D::new(source_rect_clipped.size.width, - source_rect_clipped.size.height)); - - (source_rect, dest_rect) - } - - // https://html.spec.whatwg.org/multipage/#the-image-argument-is-not-origin-clean - fn is_origin_clean(&self, - image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D) - -> bool { - match image { - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLCanvasElement(canvas) => { - canvas.origin_is_clean() - } - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::CanvasRenderingContext2D(image) => - image.origin_is_clean(), - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLImageElement(image) => - match image.get_url() { - None => true, - Some(url) => { - // TODO(zbarsky): we should check the origin of the image against - // the entry settings object, but for now check it against the canvas' doc. - let node: &Node = &*self.canvas.upcast(); - url.origin() == node.owner_doc().url().origin() - } - } - } + self.canvas_state.reset_to_initial_state(); } - // - // 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/#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>) - -> ErrorResult { - let result = match image { - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLCanvasElement(ref canvas) => { - self.draw_html_canvas_element(&canvas, - sx, sy, sw, sh, - dx, dy, dw, dh) - } - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::CanvasRenderingContext2D(ref image) => { - self.draw_html_canvas_element(&image.Canvas(), - sx, sy, sw, sh, - dx, dy, dw, dh) - } - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLImageElement(ref image) => { - // 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) { - 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); - let size = Size2D::new(size.width as f64, size.height as f64); - (data, size) - }, - None => return Err(Error::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) - } - }; - - if result.is_ok() && !self.is_origin_clean(image) { - self.set_origin_unclean() - } - result - } - - fn draw_html_canvas_element(&self, - canvas: &HTMLCanvasElement, - sx: f64, - sy: f64, - sw: Option<f64>, - sh: Option<f64>, - dx: f64, - dy: f64, - dw: Option<f64>, - dh: Option<f64>) - -> ErrorResult { - // 1. Check the usability of the image argument - if !canvas.is_valid() { - return Err(Error::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); - - if !is_rect_valid(source_rect) || !is_rect_valid(dest_rect) { - return Err(Error::IndexSize); - } - - let smoothing_enabled = self.state.borrow().image_smoothing_enabled; - - if &*self.canvas == canvas { - let msg = CanvasMsg::Canvas2d(Canvas2dMsg::DrawImageSelf( - image_size, dest_rect, source_rect, smoothing_enabled)); - self.ipc_renderer.send(msg).unwrap(); - } else { - let context = match canvas.get_or_init_2d_context() { - Some(context) => context, - None => return Err(Error::InvalidState), - }; - - let (sender, receiver) = ipc::channel().unwrap(); - let msg = CanvasMsg::Canvas2d(Canvas2dMsg::DrawImageInOther( - self.ipc_renderer.clone(), - image_size, - dest_rect, - source_rect, - smoothing_enabled, - sender)); - - let renderer = context.get_ipc_renderer(); - renderer.send(msg).unwrap(); - receiver.recv().unwrap(); - }; - - self.mark_as_dirty(); - Ok(()) - } - - fn draw_image_data(&self, - image_data: Vec<u8>, - image_size: Size2D<f64>, - sx: f64, - sy: f64, - sw: f64, - sh: f64, - dx: f64, - dy: f64, - dw: f64, - dh: f64) - -> ErrorResult { - // 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); - - if !is_rect_valid(source_rect) || !is_rect_valid(dest_rect) { - return Err(Error::IndexSize); - } - - let smoothing_enabled = self.state.borrow().image_smoothing_enabled; - self.ipc_renderer - .send(CanvasMsg::Canvas2d(Canvas2dMsg::DrawImage(image_data, - image_size, - dest_rect, - source_rect, - smoothing_enabled))) - .unwrap(); - self.mark_as_dirty(); - Ok(()) + pub fn set_canvas_bitmap_dimensions(&self, size: Size2D<u64>) { + self.canvas_state.set_bitmap_dimensions(size); } - fn fetch_image_data(&self, image_element: &HTMLImageElement) -> Option<(Vec<u8>, Size2D<i32>)> { - let url = match image_element.get_url() { - Some(url) => url, - None => return None, - }; - - let img = match self.request_image_from_cache(url) { - ImageResponse::Loaded(img) => img, - ImageResponse::PlaceholderLoaded(_) | - ImageResponse::None | - ImageResponse::MetadataLoaded(_) => { - return None; - } - }; - - let image_size = Size2D::new(img.width as i32, img.height as i32); - let image_data = match img.format { - PixelFormat::RGBA8 => img.bytes.to_vec(), - PixelFormat::K8 => panic!("K8 color type not supported"), - PixelFormat::RGB8 => panic!("RGB8 color type not supported"), - PixelFormat::KA8 => panic!("KA8 color type not supported"), - }; - - Some((image_data, image_size)) + pub fn mark_as_dirty(&self) { + self.canvas_state + .mark_as_dirty(self.canvas.as_ref().map(|c| &**c)) } - #[inline] - fn request_image_from_cache(&self, url: ServoUrl) -> ImageResponse { - let window = window_from_node(&*self.canvas); - canvas_utils::request_image_from_cache(&window, url) + pub fn take_missing_image_urls(&self) -> Vec<ServoUrl> { + mem::replace( + &mut self.canvas_state.get_missing_image_urls().borrow_mut(), + vec![], + ) } - fn create_drawable_rect(&self, x: f64, y: f64, w: f64, h: f64) -> Option<Rect<f32>> { - if !([x, y, w, h].iter().all(|val| val.is_finite())) { - return None; - } - - if w == 0.0 && h == 0.0 { - return None; - } + pub fn get_canvas_id(&self) -> CanvasId { + self.canvas_state.get_canvas_id() + } - Some(Rect::new(Point2D::new(x as f32, y as f32), - Size2D::new(w as f32, h as f32))) - } - - fn parse_color(&self, string: &str) -> Result<RGBA, ()> { - let mut parser = Parser::new(&string); - let color = CSSColor::parse(&mut parser); - if parser.is_exhausted() { - match color { - Ok(CSSColor::RGBA(rgba)) => Ok(rgba), - Ok(CSSColor::CurrentColor) => { - // TODO: https://github.com/whatwg/html/issues/1099 - // Reconsider how to calculate currentColor in a display:none canvas - - // TODO: will need to check that the context bitmap mode is fixed - // once we implement CanvasProxy - let window = window_from_node(&*self.canvas); - - let style = window.GetComputedStyle(&*self.canvas.upcast(), None); - - let element_not_rendered = - !self.canvas.upcast::<Node>().is_in_doc() || - style.GetPropertyValue(DOMString::from("display")) == "none"; - - if element_not_rendered { - Ok(RGBA::new(0, 0, 0, 255)) - } else { - self.parse_color(&style.GetPropertyValue(DOMString::from("color"))) - } - }, - _ => Err(()) - } - } else { - Err(()) - } + pub fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) { + self.canvas_state.send_canvas_2d_msg(msg) } + // TODO: Remove this pub fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg> { - self.ipc_renderer.clone() + self.canvas_state.get_ipc_renderer().clone() } pub fn origin_is_clean(&self) -> bool { - self.origin_clean.get() + self.canvas_state.origin_is_clean() } - fn set_origin_unclean(&self) { - self.origin_clean.set(false) + pub fn get_rect(&self, rect: Rect<u32>) -> Vec<u8> { + let rect = Rect::new( + Point2D::new(rect.origin.x as u64, rect.origin.y as u64), + Size2D::new(rect.size.width as u64, rect.size.height as u64), + ); + self.canvas_state.get_rect( + self.canvas + .as_ref() + .map_or(Size2D::zero(), |c| c.get_size().to_u64()), + rect, + ) } } pub trait LayoutCanvasRenderingContext2DHelpers { #[allow(unsafe_code)] - unsafe fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg>; + unsafe fn get_ipc_renderer(self) -> IpcSender<CanvasMsg>; + fn get_canvas_id(self) -> CanvasId; } -impl LayoutCanvasRenderingContext2DHelpers for LayoutJS<CanvasRenderingContext2D> { +impl LayoutCanvasRenderingContext2DHelpers for LayoutDom<'_, CanvasRenderingContext2D> { + #[allow(unsafe_code)] + unsafe fn get_ipc_renderer(self) -> IpcSender<CanvasMsg> { + (*self.unsafe_get()).canvas_state.get_ipc_renderer().clone() + } + #[allow(unsafe_code)] - unsafe fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg> { - (*self.unsafe_get()).ipc_renderer.clone() + fn get_canvas_id(self) -> CanvasId { + // FIXME(nox): This relies on the fact that CanvasState::get_canvas_id + // does nothing fancy but it would be easier to trust a + // LayoutDom<_>-like type that would wrap the &CanvasState. + unsafe { self.unsafe_get().canvas_state.get_canvas_id() } } } @@ -536,826 +166,501 @@ impl LayoutCanvasRenderingContext2DHelpers for LayoutJS<CanvasRenderingContext2D // FIXME: this behavior should might be generated by some annotattions to idl. impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { // https://html.spec.whatwg.org/multipage/#dom-context-2d-canvas - fn Canvas(&self) -> Root<HTMLCanvasElement> { - Root::from_ref(&*self.canvas) + fn Canvas(&self) -> DomRoot<HTMLCanvasElement> { + // This method is not called from a paint worklet rendering context, + // so it's OK to panic if self.canvas is None. + DomRoot::from_ref(self.canvas.as_ref().expect("No canvas.")) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-save fn Save(&self) { - self.saved_states.borrow_mut().push(self.state.borrow().clone()); - self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SaveContext)).unwrap(); + self.canvas_state.save() } #[allow(unrooted_must_root)] // https://html.spec.whatwg.org/multipage/#dom-context-2d-restore fn Restore(&self) { - let mut saved_states = self.saved_states.borrow_mut(); - if let Some(state) = saved_states.pop() { - self.state.borrow_mut().clone_from(&state); - self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::RestoreContext)).unwrap(); - } + self.canvas_state.restore() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-scale fn Scale(&self, x: f64, y: f64) { - if !(x.is_finite() && y.is_finite()) { - return; - } - - let transform = self.state.borrow().transform; - self.state.borrow_mut().transform = transform.pre_scaled(x as f32, y as f32); - self.update_transform() + self.canvas_state.scale(x, y) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-rotate fn Rotate(&self, angle: f64) { - if angle == 0.0 || !angle.is_finite() { - return; - } - - let (sin, cos) = (angle.sin(), angle.cos()); - let transform = self.state.borrow().transform; - self.state.borrow_mut().transform = transform.pre_mul( - &Matrix2D::row_major(cos as f32, sin as f32, - -sin as f32, cos as f32, - 0.0, 0.0)); - self.update_transform() + self.canvas_state.rotate(angle) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-translate fn Translate(&self, x: f64, y: f64) { - if !(x.is_finite() && y.is_finite()) { - return; - } - - let transform = self.state.borrow().transform; - self.state.borrow_mut().transform = transform.pre_translated(x as f32, y as f32); - self.update_transform() + self.canvas_state.translate(x, y) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-transform fn Transform(&self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) { - if !(a.is_finite() && b.is_finite() && c.is_finite() && - d.is_finite() && e.is_finite() && f.is_finite()) { - return; - } + self.canvas_state.transform(a, b, c, d, e, f) + } - let transform = self.state.borrow().transform; - self.state.borrow_mut().transform = transform.pre_mul( - &Matrix2D::row_major(a as f32, b as f32, c as f32, d as f32, e as f32, f as f32)); - self.update_transform() + // https://html.spec.whatwg.org/multipage/#dom-context-2d-gettransform + fn GetTransform(&self) -> DomRoot<DOMMatrix> { + self.canvas_state.get_transform(&self.global()) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-settransform fn SetTransform(&self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) { - if !(a.is_finite() && b.is_finite() && c.is_finite() && - d.is_finite() && e.is_finite() && f.is_finite()) { - return; - } - - self.state.borrow_mut().transform = - Matrix2D::row_major(a as f32, b as f32, c as f32, d as f32, e as f32, f as f32); - self.update_transform() + self.canvas_state.set_transform(a, b, c, d, e, f) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-resettransform fn ResetTransform(&self) { - self.state.borrow_mut().transform = Matrix2D::identity(); - self.update_transform() + self.canvas_state.reset_transform() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalalpha fn GlobalAlpha(&self) -> f64 { - let state = self.state.borrow(); - state.global_alpha + self.canvas_state.global_alpha() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalalpha fn SetGlobalAlpha(&self, alpha: f64) { - if !alpha.is_finite() || alpha > 1.0 || alpha < 0.0 { - return; - } - - self.state.borrow_mut().global_alpha = alpha; - self.ipc_renderer - .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetGlobalAlpha(alpha as f32))) - .unwrap() + self.canvas_state.set_global_alpha(alpha) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalcompositeoperation fn GlobalCompositeOperation(&self) -> DOMString { - let state = self.state.borrow(); - match state.global_composition { - CompositionOrBlending::Composition(op) => DOMString::from(op.to_str()), - CompositionOrBlending::Blending(op) => DOMString::from(op.to_str()), - } + self.canvas_state.global_composite_operation() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalcompositeoperation fn SetGlobalCompositeOperation(&self, op_str: DOMString) { - if let Ok(op) = CompositionOrBlending::from_str(&op_str) { - self.state.borrow_mut().global_composition = op; - self.ipc_renderer - .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetGlobalComposition(op))) - .unwrap() - } + self.canvas_state.set_global_composite_operation(op_str) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-fillrect fn FillRect(&self, x: f64, y: f64, width: f64, height: f64) { - if let Some(rect) = self.create_drawable_rect(x, y, width, height) { - self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::FillRect(rect))).unwrap(); - self.mark_as_dirty(); - } + self.canvas_state.fill_rect(x, y, width, height); + self.mark_as_dirty(); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-clearrect fn ClearRect(&self, x: f64, y: f64, width: f64, height: f64) { - if let Some(rect) = self.create_drawable_rect(x, y, width, height) { - self.ipc_renderer - .send(CanvasMsg::Canvas2d(Canvas2dMsg::ClearRect(rect))) - .unwrap(); - self.mark_as_dirty(); - } + self.canvas_state.clear_rect(x, y, width, height); + self.mark_as_dirty(); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokerect fn StrokeRect(&self, x: f64, y: f64, width: f64, height: f64) { - if let Some(rect) = self.create_drawable_rect(x, y, width, height) { - self.ipc_renderer - .send(CanvasMsg::Canvas2d(Canvas2dMsg::StrokeRect(rect))) - .unwrap(); - self.mark_as_dirty(); - } + self.canvas_state.stroke_rect(x, y, width, height); + self.mark_as_dirty(); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-beginpath fn BeginPath(&self) { - self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::BeginPath)).unwrap(); + self.canvas_state.begin_path() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-closepath fn ClosePath(&self) { - self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::ClosePath)).unwrap(); + self.canvas_state.close_path() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-fill - fn Fill(&self, _: CanvasFillRule) { - // TODO: Process fill rule - self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::Fill)).unwrap(); + fn Fill(&self, fill_rule: CanvasFillRule) { + self.canvas_state.fill(fill_rule); self.mark_as_dirty(); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-stroke fn Stroke(&self) { - self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::Stroke)).unwrap(); + self.canvas_state.stroke(); self.mark_as_dirty(); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-clip - fn Clip(&self, _: CanvasFillRule) { - // TODO: Process fill rule - self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::Clip)).unwrap(); + fn Clip(&self, fill_rule: CanvasFillRule) { + self.canvas_state.clip(fill_rule) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-ispointinpath fn IsPointInPath(&self, x: f64, y: f64, fill_rule: CanvasFillRule) -> bool { - let fill_rule = match fill_rule { - CanvasFillRule::Nonzero => FillRule::Nonzero, - CanvasFillRule::Evenodd => FillRule::Evenodd, - }; - let (sender, receiver) = ipc::channel::<bool>().unwrap(); - self.ipc_renderer - .send(CanvasMsg::Canvas2d(Canvas2dMsg::IsPointInPath(x, y, fill_rule, sender))) - .unwrap(); - receiver.recv().unwrap() + self.canvas_state + .is_point_in_path(&self.global(), x, y, fill_rule) } - // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage - fn DrawImage(&self, - image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D, - dx: f64, - dy: f64) - -> ErrorResult { - if !(dx.is_finite() && dy.is_finite()) { - return Ok(()); - } + // https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext + fn FillText(&self, text: DOMString, x: f64, y: f64, max_width: Option<f64>) { + self.canvas_state + .fill_text(self.canvas.as_ref().map(|c| &**c), text, x, y, max_width); + self.mark_as_dirty(); + } - self.draw_image(image, 0f64, 0f64, None, None, dx, dy, None, None) + // https://html.spec.whatwg.org/multipage/#textmetrics + fn MeasureText(&self, text: DOMString) -> DomRoot<TextMetrics> { + self.canvas_state.measure_text(&self.global(), text) } - // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage - fn DrawImage_(&self, - image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D, - dx: f64, - dy: f64, - dw: f64, - dh: f64) - -> ErrorResult { - if !(dx.is_finite() && dy.is_finite() && dw.is_finite() && dh.is_finite()) { - return Ok(()); - } + // https://html.spec.whatwg.org/multipage/#dom-context-2d-font + fn Font(&self) -> DOMString { + self.canvas_state.font() + } - self.draw_image(image, 0f64, 0f64, None, None, dx, dy, Some(dw), Some(dh)) + // https://html.spec.whatwg.org/multipage/#dom-context-2d-font + fn SetFont(&self, value: DOMString) { + self.canvas_state + .set_font(self.canvas.as_ref().map(|c| &**c), value) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-textalign + fn TextAlign(&self) -> CanvasTextAlign { + self.canvas_state.text_align() + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-textalign + fn SetTextAlign(&self, value: CanvasTextAlign) { + self.canvas_state.set_text_align(value) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-textbaseline + fn TextBaseline(&self) -> CanvasTextBaseline { + self.canvas_state.text_baseline() + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-textbaseline + fn SetTextBaseline(&self, value: CanvasTextBaseline) { + self.canvas_state.set_text_baseline(value) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-direction + fn Direction(&self) -> CanvasDirection { + self.canvas_state.direction() + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-direction + fn SetDirection(&self, value: CanvasDirection) { + self.canvas_state.set_direction(value) } // 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) - -> ErrorResult { - 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(()); - } + fn DrawImage(&self, image: CanvasImageSource, dx: f64, dy: f64) -> ErrorResult { + self.canvas_state + .draw_image(self.canvas.as_ref().map(|c| &**c), image, dx, dy) + } - 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-drawimage + fn DrawImage_( + &self, + image: CanvasImageSource, + dx: f64, + dy: f64, + dw: f64, + dh: f64, + ) -> ErrorResult { + self.canvas_state + .draw_image_(self.canvas.as_ref().map(|c| &**c), image, dx, dy, dw, dh) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage + fn DrawImage__( + &self, + image: CanvasImageSource, + sx: f64, + sy: f64, + sw: f64, + sh: f64, + dx: f64, + dy: f64, + dw: f64, + dh: f64, + ) -> ErrorResult { + self.canvas_state.draw_image__( + self.canvas.as_ref().map(|c| &**c), + image, + sx, + sy, + sw, + sh, + dx, + dy, + dw, + dh, + ) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-moveto fn MoveTo(&self, x: f64, y: f64) { - if !(x.is_finite() && y.is_finite()) { - return; - } - - let msg = CanvasMsg::Canvas2d(Canvas2dMsg::MoveTo(Point2D::new(x as f32, y as f32))); - self.ipc_renderer.send(msg).unwrap(); + self.canvas_state.move_to(x, y) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-lineto fn LineTo(&self, x: f64, y: f64) { - if !(x.is_finite() && y.is_finite()) { - return; - } - - let msg = CanvasMsg::Canvas2d(Canvas2dMsg::LineTo(Point2D::new(x as f32, y as f32))); - self.ipc_renderer.send(msg).unwrap(); + self.canvas_state.line_to(x, y) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-rect fn Rect(&self, x: f64, y: f64, width: f64, height: f64) { - if [x, y, width, height].iter().all(|val| val.is_finite()) { - let rect = Rect::new(Point2D::new(x as f32, y as f32), - Size2D::new(width as f32, height as f32)); - let msg = CanvasMsg::Canvas2d(Canvas2dMsg::Rect(rect)); - self.ipc_renderer.send(msg).unwrap(); - } + self.canvas_state.rect(x, y, width, height) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-quadraticcurveto fn QuadraticCurveTo(&self, cpx: f64, cpy: f64, x: f64, y: f64) { - if !(cpx.is_finite() && cpy.is_finite() && x.is_finite() && y.is_finite()) { - return; - } - - let msg = CanvasMsg::Canvas2d(Canvas2dMsg::QuadraticCurveTo(Point2D::new(cpx as f32, - cpy as f32), - Point2D::new(x as f32, - y as f32))); - self.ipc_renderer.send(msg).unwrap(); + self.canvas_state.quadratic_curve_to(cpx, cpy, x, y) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-beziercurveto fn BezierCurveTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) { - if !(cp1x.is_finite() && cp1y.is_finite() && cp2x.is_finite() && cp2y.is_finite() && - x.is_finite() && y.is_finite()) { - return; - } - - let msg = CanvasMsg::Canvas2d(Canvas2dMsg::BezierCurveTo(Point2D::new(cp1x as f32, - cp1y as f32), - Point2D::new(cp2x as f32, - cp2y as f32), - Point2D::new(x as f32, y as f32))); - self.ipc_renderer.send(msg).unwrap(); + self.canvas_state + .bezier_curve_to(cp1x, cp1y, cp2x, cp2y, x, y) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-arc fn Arc(&self, x: f64, y: f64, r: f64, start: f64, end: f64, ccw: bool) -> ErrorResult { - if !([x, y, r, start, end].iter().all(|x| x.is_finite())) { - return Ok(()); - } - - if r < 0.0 { - return Err(Error::IndexSize); - } - - let msg = CanvasMsg::Canvas2d(Canvas2dMsg::Arc(Point2D::new(x as f32, y as f32), - r as f32, - start as f32, - end as f32, - ccw)); - - self.ipc_renderer.send(msg).unwrap(); - Ok(()) + self.canvas_state.arc(x, y, r, start, end, ccw) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-arcto fn ArcTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, r: f64) -> ErrorResult { - if !([cp1x, cp1y, cp2x, cp2y, r].iter().all(|x| x.is_finite())) { - return Ok(()); - } - if r < 0.0 { - return Err(Error::IndexSize); - } + self.canvas_state.arc_to(cp1x, cp1y, cp2x, cp2y, r) + } - let msg = CanvasMsg::Canvas2d(Canvas2dMsg::ArcTo(Point2D::new(cp1x as f32, cp1y as f32), - Point2D::new(cp2x as f32, cp2y as f32), - r as f32)); - self.ipc_renderer.send(msg).unwrap(); - Ok(()) + // https://html.spec.whatwg.org/multipage/#dom-context-2d-ellipse + fn Ellipse( + &self, + x: f64, + y: f64, + rx: f64, + ry: f64, + rotation: f64, + start: f64, + end: f64, + ccw: bool, + ) -> ErrorResult { + self.canvas_state + .ellipse(x, y, rx, ry, rotation, start, end, ccw) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-imagesmoothingenabled fn ImageSmoothingEnabled(&self) -> bool { - let state = self.state.borrow(); - state.image_smoothing_enabled + self.canvas_state.image_smoothing_enabled() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-imagesmoothingenabled fn SetImageSmoothingEnabled(&self, value: bool) { - self.state.borrow_mut().image_smoothing_enabled = value; + self.canvas_state.set_image_smoothing_enabled(value) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle fn StrokeStyle(&self) -> StringOrCanvasGradientOrCanvasPattern { - match self.state.borrow().stroke_style { - CanvasFillOrStrokeStyle::Color(ref rgba) => { - let mut result = String::new(); - serialize(rgba, &mut result).unwrap(); - StringOrCanvasGradientOrCanvasPattern::String(DOMString::from(result)) - }, - CanvasFillOrStrokeStyle::Gradient(ref gradient) => { - StringOrCanvasGradientOrCanvasPattern::CanvasGradient(Root::from_ref(&*gradient)) - }, - CanvasFillOrStrokeStyle::Pattern(ref pattern) => { - StringOrCanvasGradientOrCanvasPattern::CanvasPattern(Root::from_ref(&*pattern)) - } - } + self.canvas_state.stroke_style() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) { - match value { - StringOrCanvasGradientOrCanvasPattern::String(string) => { - if let Ok(rgba) = self.parse_color(&string) { - self.state.borrow_mut().stroke_style = CanvasFillOrStrokeStyle::Color(rgba); - self.ipc_renderer - .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetStrokeStyle( - FillOrStrokeStyle::Color(rgba)))) - .unwrap(); - } - }, - StringOrCanvasGradientOrCanvasPattern::CanvasGradient(gradient) => { - self.state.borrow_mut().stroke_style = - CanvasFillOrStrokeStyle::Gradient(JS::from_ref(&*gradient)); - let msg = CanvasMsg::Canvas2d( - Canvas2dMsg::SetStrokeStyle(gradient.to_fill_or_stroke_style())); - self.ipc_renderer.send(msg).unwrap(); - }, - StringOrCanvasGradientOrCanvasPattern::CanvasPattern(pattern) => { - self.state.borrow_mut().stroke_style = - CanvasFillOrStrokeStyle::Pattern(JS::from_ref(&*pattern)); - let msg = CanvasMsg::Canvas2d( - Canvas2dMsg::SetStrokeStyle(pattern.to_fill_or_stroke_style())); - self.ipc_renderer.send(msg).unwrap(); - if !pattern.origin_is_clean() { - self.set_origin_unclean(); - } - } - } + self.canvas_state + .set_stroke_style(self.canvas.as_ref().map(|c| &**c), value) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle fn FillStyle(&self) -> StringOrCanvasGradientOrCanvasPattern { - match self.state.borrow().fill_style { - CanvasFillOrStrokeStyle::Color(ref rgba) => { - let mut result = String::new(); - serialize(rgba, &mut result).unwrap(); - StringOrCanvasGradientOrCanvasPattern::String(DOMString::from(result)) - }, - CanvasFillOrStrokeStyle::Gradient(ref gradient) => { - StringOrCanvasGradientOrCanvasPattern::CanvasGradient(Root::from_ref(&*gradient)) - }, - CanvasFillOrStrokeStyle::Pattern(ref pattern) => { - StringOrCanvasGradientOrCanvasPattern::CanvasPattern(Root::from_ref(&*pattern)) - } - } + self.canvas_state.fill_style() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle fn SetFillStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) { - match value { - StringOrCanvasGradientOrCanvasPattern::String(string) => { - if let Ok(rgba) = self.parse_color(&string) { - self.state.borrow_mut().fill_style = CanvasFillOrStrokeStyle::Color(rgba); - self.ipc_renderer - .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetFillStyle( - FillOrStrokeStyle::Color(rgba)))) - .unwrap() - } - } - StringOrCanvasGradientOrCanvasPattern::CanvasGradient(gradient) => { - self.state.borrow_mut().fill_style = - CanvasFillOrStrokeStyle::Gradient(JS::from_ref(&*gradient)); - let msg = CanvasMsg::Canvas2d( - Canvas2dMsg::SetFillStyle(gradient.to_fill_or_stroke_style())); - self.ipc_renderer.send(msg).unwrap(); - } - StringOrCanvasGradientOrCanvasPattern::CanvasPattern(pattern) => { - self.state.borrow_mut().fill_style = - CanvasFillOrStrokeStyle::Pattern(JS::from_ref(&*pattern)); - let msg = CanvasMsg::Canvas2d( - Canvas2dMsg::SetFillStyle(pattern.to_fill_or_stroke_style())); - self.ipc_renderer.send(msg).unwrap(); - if !pattern.origin_is_clean() { - self.set_origin_unclean(); - } - } - } + self.canvas_state + .set_fill_style(self.canvas.as_ref().map(|c| &**c), value) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-createimagedata - fn CreateImageData(&self, sw: Finite<f64>, sh: Finite<f64>) -> Fallible<Root<ImageData>> { - if *sw == 0.0 || *sh == 0.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) + fn CreateImageData(&self, sw: i32, sh: i32) -> Fallible<DomRoot<ImageData>> { + self.canvas_state.create_image_data(&self.global(), sw, sh) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-createimagedata - fn CreateImageData_(&self, imagedata: &ImageData) -> Fallible<Root<ImageData>> { - ImageData::new(&self.global(), - imagedata.Width(), - imagedata.Height(), - None) + fn CreateImageData_(&self, imagedata: &ImageData) -> Fallible<DomRoot<ImageData>> { + self.canvas_state + .create_image_data_(&self.global(), imagedata) } // 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<Root<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; - - if sw == 0.0 || sh == 0.0 { - return Err(Error::IndexSize); - } - - if sw < 0.0 { - sw = -sw; - sx -= sw; - } - if sh < 0.0 { - sh = -sh; - sy -= sh; - } - - let sh = cmp::max(1, sh.to_u32().unwrap()); - let sw = cmp::max(1, sw.to_u32().unwrap()); - - let (sender, receiver) = ipc::channel::<Vec<u8>>().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.get_size(); - let canvas_size = Size2D::new(canvas_size.width as f64, canvas_size.height as f64); - self.ipc_renderer - .send(CanvasMsg::Canvas2d(Canvas2dMsg::GetImageData(dest_rect, canvas_size, sender))) - .unwrap(); - let mut data = receiver.recv().unwrap(); - - // Un-premultiply alpha - for chunk in data.chunks_mut(4) { - let alpha = chunk[3] as usize; - chunk[0] = UNPREMULTIPLY_TABLE[256 * alpha + chunk[0] as usize]; - chunk[1] = UNPREMULTIPLY_TABLE[256 * alpha + chunk[1] as usize]; - chunk[2] = UNPREMULTIPLY_TABLE[256 * alpha + chunk[2] as usize]; - } - - ImageData::new(&self.global(), sw, sh, Some(data)) + fn GetImageData(&self, sx: i32, sy: i32, sw: i32, sh: i32) -> Fallible<DomRoot<ImageData>> { + self.canvas_state.get_image_data( + self.canvas + .as_ref() + .map_or(Size2D::zero(), |c| c.get_size().to_u64()), + &self.global(), + sx, + sy, + sw, + sh, + ) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata - fn PutImageData(&self, imagedata: &ImageData, dx: Finite<f64>, dy: Finite<f64>) { - self.PutImageData_(imagedata, - dx, - dy, - Finite::wrap(0f64), - Finite::wrap(0f64), - Finite::wrap(imagedata.Width() as f64), - Finite::wrap(imagedata.Height() as f64)) + fn PutImageData(&self, imagedata: &ImageData, dx: i32, dy: i32) { + self.canvas_state.put_image_data( + self.canvas + .as_ref() + .map_or(Size2D::zero(), |c| c.get_size().to_u64()), + imagedata, + dx, + dy, + ) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata - fn PutImageData_(&self, - imagedata: &ImageData, - dx: Finite<f64>, - dy: Finite<f64>, - dirty_x: Finite<f64>, - dirty_y: Finite<f64>, - dirty_width: Finite<f64>, - dirty_height: Finite<f64>) { - let data = imagedata.get_data_array(); - let offset = Point2D::new(*dx, *dy); - let image_data_size = Size2D::new(imagedata.Width() as f64, imagedata.Height() as f64); - - let dirty_rect = Rect::new(Point2D::new(*dirty_x, *dirty_y), - Size2D::new(*dirty_width, *dirty_height)); - let msg = CanvasMsg::Canvas2d(Canvas2dMsg::PutImageData(data, - offset, - image_data_size, - dirty_rect)); - self.ipc_renderer.send(msg).unwrap(); + #[allow(unsafe_code)] + fn PutImageData_( + &self, + imagedata: &ImageData, + dx: i32, + dy: i32, + dirty_x: i32, + dirty_y: i32, + dirty_width: i32, + dirty_height: i32, + ) { + self.canvas_state.put_image_data_( + self.canvas + .as_ref() + .map_or(Size2D::zero(), |c| c.get_size().to_u64()), + imagedata, + dx, + dy, + dirty_x, + dirty_y, + dirty_width, + dirty_height, + ); self.mark_as_dirty(); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-createlineargradient - fn CreateLinearGradient(&self, - x0: Finite<f64>, - y0: Finite<f64>, - x1: Finite<f64>, - y1: Finite<f64>) - -> Root<CanvasGradient> { - CanvasGradient::new(&self.global(), - CanvasGradientStyle::Linear(LinearGradientStyle::new(*x0, - *y0, - *x1, - *y1, - Vec::new()))) + fn CreateLinearGradient( + &self, + x0: Finite<f64>, + y0: Finite<f64>, + x1: Finite<f64>, + y1: Finite<f64>, + ) -> DomRoot<CanvasGradient> { + self.canvas_state + .create_linear_gradient(&self.global(), x0, y0, x1, y1) } // 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>) - -> Fallible<Root<CanvasGradient>> { - if *r0 < 0. || *r1 < 0. { - return Err(Error::IndexSize); - } - - Ok(CanvasGradient::new(&self.global(), - CanvasGradientStyle::Radial(RadialGradientStyle::new(*x0, - *y0, - *r0, - *x1, - *y1, - *r1, - Vec::new())))) + fn CreateRadialGradient( + &self, + x0: Finite<f64>, + y0: Finite<f64>, + r0: Finite<f64>, + x1: Finite<f64>, + y1: Finite<f64>, + r1: Finite<f64>, + ) -> Fallible<DomRoot<CanvasGradient>> { + self.canvas_state + .create_radial_gradient(&self.global(), x0, y0, r0, x1, y1, r1) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-createpattern - fn CreatePattern(&self, - image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D, - mut repetition: DOMString) - -> Fallible<Root<CanvasPattern>> { - let (image_data, image_size) = match image { - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLImageElement(ref image) => { - // 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 - try!(self.fetch_image_data(image).ok_or(Error::InvalidState)) - }, - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLCanvasElement(ref canvas) => { - let _ = canvas.get_or_init_2d_context(); - - try!(canvas.fetch_all_data().ok_or(Error::InvalidState)) - }, - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::CanvasRenderingContext2D(ref context) => { - let canvas = context.Canvas(); - let _ = canvas.get_or_init_2d_context(); - - try!(canvas.fetch_all_data().ok_or(Error::InvalidState)) - } - }; - - if repetition.is_empty() { - repetition.push_str("repeat"); - } - - if let Ok(rep) = RepetitionStyle::from_str(&repetition) { - Ok(CanvasPattern::new(&self.global(), - image_data, - image_size, - rep, - self.is_origin_clean(image))) - } else { - Err(Error::Syntax) - } + fn CreatePattern( + &self, + image: CanvasImageSource, + repetition: DOMString, + ) -> Fallible<Option<DomRoot<CanvasPattern>>> { + self.canvas_state + .create_pattern(&self.global(), image, repetition) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth fn LineWidth(&self) -> f64 { - let state = self.state.borrow(); - state.line_width + self.canvas_state.line_width() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth fn SetLineWidth(&self, width: f64) { - if !width.is_finite() || width <= 0.0 { - return; - } - - self.state.borrow_mut().line_width = width; - self.ipc_renderer - .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetLineWidth(width as f32))) - .unwrap() + self.canvas_state.set_line_width(width) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linecap fn LineCap(&self) -> CanvasLineCap { - match self.state.borrow().line_cap { - LineCapStyle::Butt => CanvasLineCap::Butt, - LineCapStyle::Round => CanvasLineCap::Round, - LineCapStyle::Square => CanvasLineCap::Square, - } + self.canvas_state.line_cap() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linecap fn SetLineCap(&self, cap: CanvasLineCap) { - let line_cap = match cap { - CanvasLineCap::Butt => LineCapStyle::Butt, - CanvasLineCap::Round => LineCapStyle::Round, - CanvasLineCap::Square => LineCapStyle::Square, - }; - self.state.borrow_mut().line_cap = line_cap; - self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetLineCap(line_cap))).unwrap(); + self.canvas_state.set_line_cap(cap) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linejoin fn LineJoin(&self) -> CanvasLineJoin { - match self.state.borrow().line_join { - LineJoinStyle::Round => CanvasLineJoin::Round, - LineJoinStyle::Bevel => CanvasLineJoin::Bevel, - LineJoinStyle::Miter => CanvasLineJoin::Miter, - } + self.canvas_state.line_join() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linejoin fn SetLineJoin(&self, join: CanvasLineJoin) { - let line_join = match join { - CanvasLineJoin::Round => LineJoinStyle::Round, - CanvasLineJoin::Bevel => LineJoinStyle::Bevel, - CanvasLineJoin::Miter => LineJoinStyle::Miter, - }; - self.state.borrow_mut().line_join = line_join; - self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetLineJoin(line_join))).unwrap(); + self.canvas_state.set_line_join(join) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-miterlimit fn MiterLimit(&self) -> f64 { - let state = self.state.borrow(); - state.miter_limit + self.canvas_state.miter_limit() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-miterlimit fn SetMiterLimit(&self, limit: f64) { - if !limit.is_finite() || limit <= 0.0 { - return; - } - - self.state.borrow_mut().miter_limit = limit; - self.ipc_renderer - .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetMiterLimit(limit as f32))) - .unwrap() + self.canvas_state.set_miter_limit(limit) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx fn ShadowOffsetX(&self) -> f64 { - self.state.borrow().shadow_offset_x + self.canvas_state.shadow_offset_x() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx fn SetShadowOffsetX(&self, value: f64) { - if !value.is_finite() || value == self.state.borrow().shadow_offset_x { - return; - } - self.state.borrow_mut().shadow_offset_x = value; - self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetShadowOffsetX(value))).unwrap() + self.canvas_state.set_shadow_offset_x(value) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsety fn ShadowOffsetY(&self) -> f64 { - self.state.borrow().shadow_offset_y + self.canvas_state.shadow_offset_y() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsety fn SetShadowOffsetY(&self, value: f64) { - if !value.is_finite() || value == self.state.borrow().shadow_offset_y { - return; - } - self.state.borrow_mut().shadow_offset_y = value; - self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetShadowOffsetY(value))).unwrap() + self.canvas_state.set_shadow_offset_y(value) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowblur fn ShadowBlur(&self) -> f64 { - self.state.borrow().shadow_blur + self.canvas_state.shadow_blur() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowblur fn SetShadowBlur(&self, value: f64) { - if !value.is_finite() || value < 0f64 || value == self.state.borrow().shadow_blur { - return; - } - self.state.borrow_mut().shadow_blur = value; - self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetShadowBlur(value))).unwrap() + self.canvas_state.set_shadow_blur(value) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor fn ShadowColor(&self) -> DOMString { - let mut result = String::new(); - serialize(&self.state.borrow().shadow_color, &mut result).unwrap(); - DOMString::from(result) + self.canvas_state.shadow_color() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor fn SetShadowColor(&self, value: DOMString) { - if let Ok(color) = parse_color(&value) { - self.state.borrow_mut().shadow_color = color; - self.ipc_renderer - .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetShadowColor(color))) - .unwrap() - } + self.canvas_state.set_shadow_color(value) } } impl Drop for CanvasRenderingContext2D { fn drop(&mut self) { - if let Err(err) = self.ipc_renderer.send(CanvasMsg::Common(CanvasCommonMsg::Close)) { + if let Err(err) = self + .canvas_state + .get_ipc_renderer() + .send(CanvasMsg::Close(self.canvas_state.get_canvas_id())) + { warn!("Could not close canvas: {}", err) } } } - -pub fn parse_color(string: &str) -> Result<RGBA, ()> { - let mut parser = Parser::new(&string); - match CSSColor::parse(&mut parser) { - Ok(CSSColor::RGBA(rgba)) => { - if parser.is_exhausted() { - Ok(rgba) - } else { - Err(()) - } - }, - _ => Err(()), - } -} - -// 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<f64>) -> bool { - rect.size.width > 0.0 && rect.size.height > 0.0 -} - -// https://html.spec.whatwg.org/multipage/#serialisation-of-a-colour -fn serialize<W>(color: &RGBA, dest: &mut W) -> fmt::Result - where W: fmt::Write -{ - let red = color.red; - let green = color.green; - let blue = color.blue; - - if color.alpha == 255 { - write!(dest, - "#{:x}{:x}{:x}{:x}{:x}{:x}", - red >> 4, - red & 0xF, - green >> 4, - green & 0xF, - blue >> 4, - blue & 0xF) - } else { - write!(dest, "rgba({}, {}, {}, {})", red, green, blue, color.alpha_f32()) - } -} |