diff --git a/components/script/canvas_state.rs b/components/script/canvas_state.rs new file mode 100644 index 00000000000..5dd32c61c0b --- /dev/null +++ b/components/script/canvas_state.rs @@ -0,0 +1,1536 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::cell::DomRefCell; +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::ImageDataBinding::ImageDataMethods; +use crate::dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern; +use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::num::Finite; +use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::bindings::str::DOMString; +use crate::dom::canvasgradient::{CanvasGradient, CanvasGradientStyle, ToFillOrStrokeStyle}; +use crate::dom::canvaspattern::CanvasPattern; +use crate::dom::element::cors_setting_for_element; +use crate::dom::element::Element; +use crate::dom::globalscope::GlobalScope; +use crate::dom::htmlcanvaselement::{CanvasContext, HTMLCanvasElement}; +use crate::dom::imagedata::ImageData; +use crate::dom::node::{Node, NodeDamage}; +use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope; +use crate::dom::textmetrics::TextMetrics; +use crate::unpremultiplytable::UNPREMULTIPLY_TABLE; +use canvas_traits::canvas::{Canvas2dMsg, CanvasId, CanvasMsg}; +use canvas_traits::canvas::{CompositionOrBlending, FillOrStrokeStyle, FillRule}; +use canvas_traits::canvas::{LineCapStyle, LineJoinStyle, LinearGradientStyle}; +use canvas_traits::canvas::{RadialGradientStyle, RepetitionStyle}; +use cssparser::Color as CSSColor; +use cssparser::{Parser, ParserInput, RGBA}; +use euclid::{ + default::{Point2D, Rect, Size2D, Transform2D}, + vec2, +}; +use ipc_channel::ipc::{self, IpcSender}; +use net_traits::image_cache::CanRequestImages; +use net_traits::image_cache::ImageCache; +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 net_traits::request::CorsSettings; +use pixels::PixelFormat; +use profile_traits::ipc as profiled_ipc; +use script_traits::ScriptMsg; +use servo_url::{ImmutableOrigin, ServoUrl}; +use std::cell::Cell; +use std::fmt; +use std::str::FromStr; +use std::sync::Arc; + +#[must_root] +#[derive(Clone, JSTraceable, MallocSizeOf)] +#[allow(dead_code)] +pub(crate) enum CanvasFillOrStrokeStyle { + Color(RGBA), + Gradient(Dom), + Pattern(Dom), +} + +#[must_root] +#[derive(Clone, JSTraceable, MallocSizeOf)] +pub(crate) 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: Transform2D, + shadow_offset_x: f64, + shadow_offset_y: f64, + shadow_blur: f64, + shadow_color: RGBA, +} + +impl CanvasContextState { + pub(crate) 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: Transform2D::identity(), + shadow_offset_x: 0.0, + shadow_offset_y: 0.0, + shadow_blur: 0.0, + shadow_color: RGBA::transparent(), + } + } +} + +#[must_root] +#[derive(JSTraceable, MallocSizeOf)] +pub(crate) struct CanvasState { + #[ignore_malloc_size_of = "Defined in ipc-channel"] + ipc_renderer: IpcSender, + canvas_id: CanvasId, + state: DomRefCell, + origin_clean: Cell, + #[ignore_malloc_size_of = "Arc"] + image_cache: Arc, + /// The base URL for resolving CSS image URL values. + /// Needed because of https://github.com/servo/servo/issues/17625 + base_url: ServoUrl, + origin: ImmutableOrigin, + /// Any missing image URLs. + missing_image_urls: DomRefCell>, + saved_states: DomRefCell>, +} + +impl CanvasState { + pub(crate) fn new(global: &GlobalScope, size: Size2D) -> CanvasState { + debug!("Creating new canvas rendering context."); + let (sender, receiver) = + profiled_ipc::channel(global.time_profiler_chan().clone()).unwrap(); + let script_to_constellation_chan = global.script_to_constellation_chan(); + debug!("Asking constellation to create new canvas thread."); + script_to_constellation_chan + .send(ScriptMsg::CreateCanvasPaintThread(size, sender)) + .unwrap(); + let (ipc_renderer, canvas_id) = receiver.recv().unwrap(); + debug!("Done."); + // Worklets always receive a unique origin. This messes with fetching + // cached images in the case of paint worklets, since the image cache + // is keyed on the origin requesting the image data. + let origin = if global.is::() { + global.api_base_url().origin() + } else { + global.origin().immutable().clone() + }; + CanvasState { + ipc_renderer: ipc_renderer, + canvas_id: canvas_id, + state: DomRefCell::new(CanvasContextState::new()), + origin_clean: Cell::new(true), + image_cache: global.image_cache(), + base_url: global.api_base_url(), + missing_image_urls: DomRefCell::new(Vec::new()), + saved_states: DomRefCell::new(Vec::new()), + origin, + } + } + + pub fn get_ipc_renderer(&self) -> &IpcSender { + &self.ipc_renderer + } + + pub fn get_missing_image_urls(&self) -> &DomRefCell> { + &self.missing_image_urls + } + + pub fn get_state(&self) -> &DomRefCell { + &self.state + } + + pub fn get_saved_state(&self) -> &DomRefCell> { + &self.saved_states + } + + pub fn get_canvas_id(&self) -> CanvasId { + self.canvas_id.clone() + } + + pub fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) { + self.ipc_renderer + .send(CanvasMsg::Canvas2d(msg, self.get_canvas_id())) + .unwrap() + } + + fn create_drawable_rect(&self, x: f64, y: f64, w: f64, h: f64) -> Option> { + if !([x, y, w, h].iter().all(|val| val.is_finite())) { + return None; + } + + if w == 0.0 && h == 0.0 { + return None; + } + + Some(Rect::new( + Point2D::new(x as f32, y as f32), + Size2D::new(w as f32, h as f32), + )) + } + + pub fn origin_is_clean(&self) -> bool { + self.origin_clean.get() + } + + fn set_origin_unclean(&self) { + self.origin_clean.set(false) + } + + // https://html.spec.whatwg.org/multipage/#the-image-argument-is-not-origin-clean + fn is_origin_clean(&self, image: CanvasImageSource) -> bool { + match image { + CanvasImageSource::HTMLCanvasElement(canvas) => canvas.origin_is_clean(), + CanvasImageSource::HTMLImageElement(image) => { + image.same_origin(GlobalScope::entry().origin()) + }, + CanvasImageSource::CSSStyleValue(_) => true, + } + } + + fn fetch_image_data( + &self, + url: ServoUrl, + cors_setting: Option, + ) -> Option<(Vec, Size2D)> { + let img = match self.request_image_from_cache(url, cors_setting) { + ImageResponse::Loaded(img, _) => img, + ImageResponse::PlaceholderLoaded(_, _) | + ImageResponse::None | + ImageResponse::MetadataLoaded(_) => { + return None; + }, + }; + + let image_size = Size2D::new(img.width, img.height); + let image_data = match img.format { + PixelFormat::BGRA8 => img.bytes.to_vec(), + pixel_format => unimplemented!("unsupported pixel format ({:?})", pixel_format), + }; + + Some((image_data, image_size)) + } + + fn request_image_from_cache( + &self, + url: ServoUrl, + cors_setting: Option, + ) -> ImageResponse { + let response = self.image_cache.find_image_or_metadata( + url.clone(), + self.origin.clone(), + cors_setting, + UsePlaceholder::No, + CanRequestImages::No, + ); + match response { + Ok(ImageOrMetadataAvailable::ImageAvailable(image, url)) => { + ImageResponse::Loaded(image, url) + }, + Err(ImageState::Pending(_)) => ImageResponse::None, + _ => { + // Rather annoyingly, we get the same response back from + // A load which really failed and from a load which hasn't started yet. + self.missing_image_urls.borrow_mut().push(url); + ImageResponse::None + }, + } + } + + fn parse_color(&self, canvas: Option<&HTMLCanvasElement>, string: &str) -> Result { + let mut input = ParserInput::new(string); + let mut parser = Parser::new(&mut input); + 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 canvas = match canvas { + // https://drafts.css-houdini.org/css-paint-api/#2d-rendering-context + // Whenever "currentColor" is used as a color in the PaintRenderingContext2D API, + // it is treated as opaque black. + None => return Ok(RGBA::new(0, 0, 0, 255)), + Some(ref canvas) => &**canvas, + }; + + let canvas_element = canvas.upcast::(); + + match canvas_element.style() { + Some(ref s) if canvas_element.has_css_layout_box() => { + Ok(s.get_inherited_text().color) + }, + _ => Ok(RGBA::new(0, 0, 0, 255)), + } + }, + _ => Err(()), + } + } else { + Err(()) + } + } + + pub fn get_rect(&self, canvas_size: Size2D, rect: Rect) -> Vec { + assert!(self.origin_is_clean()); + + 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 + } + + // + // 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_internal( + &self, + htmlcanvas: Option<&HTMLCanvasElement>, + image: CanvasImageSource, + sx: f64, + sy: f64, + sw: Option, + sh: Option, + dx: f64, + dy: f64, + dw: Option, + dh: Option, + ) -> ErrorResult { + let result = match image { + CanvasImageSource::HTMLCanvasElement(ref canvas) => { + self.draw_html_canvas_element(&canvas, htmlcanvas, sx, sy, sw, sh, dx, dy, dw, dh) + }, + CanvasImageSource::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 url = image.get_url().ok_or(Error::InvalidState)?; + let cors_setting = cors_setting_for_element(image.upcast()); + self.fetch_and_draw_image_data( + htmlcanvas, + url, + cors_setting, + sx, + sy, + sw, + sh, + dx, + dy, + dw, + dh, + ) + }, + CanvasImageSource::CSSStyleValue(ref value) => { + let url = value + .get_url(self.base_url.clone()) + .ok_or(Error::InvalidState)?; + self.fetch_and_draw_image_data( + htmlcanvas, url, None, 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, + htmlcanvas: Option<&HTMLCanvasElement>, + sx: f64, + sy: f64, + sw: Option, + sh: Option, + dx: f64, + dy: f64, + dw: Option, + dh: Option, + ) -> 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 Ok(()); + } + + let smoothing_enabled = self.state.borrow().image_smoothing_enabled; + + if let Some(context) = canvas.context() { + match *context { + CanvasContext::Context2d(ref context) => { + context.send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther( + self.get_canvas_id(), + image_size, + dest_rect, + source_rect, + smoothing_enabled, + )); + }, + _ => return Err(Error::InvalidState), + } + } else { + self.send_canvas_2d_msg(Canvas2dMsg::DrawImage( + None, + image_size, + dest_rect, + source_rect, + smoothing_enabled, + )); + } + + self.mark_as_dirty(htmlcanvas); + Ok(()) + } + + fn fetch_and_draw_image_data( + &self, + canvas: Option<&HTMLCanvasElement>, + url: ServoUrl, + cors_setting: Option, + sx: f64, + sy: f64, + sw: Option, + sh: Option, + dx: f64, + dy: f64, + dw: Option, + dh: Option, + ) -> ErrorResult { + debug!("Fetching image {}.", url); + let (mut image_data, image_size) = self + .fetch_image_data(url, cors_setting) + .ok_or(Error::InvalidState)?; + pixels::rgba8_premultiply_inplace(&mut image_data); + let image_size = image_size.to_f64(); + + 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); + + // 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 Ok(()); + } + + let smoothing_enabled = self.state.borrow().image_smoothing_enabled; + self.send_canvas_2d_msg(Canvas2dMsg::DrawImage( + Some(image_data.into()), + image_size, + dest_rect, + source_rect, + smoothing_enabled, + )); + self.mark_as_dirty(canvas); + Ok(()) + } + + pub fn mark_as_dirty(&self, canvas: Option<&HTMLCanvasElement>) { + if let Some(ref canvas) = canvas { + canvas.upcast::().dirty(NodeDamage::OtherNodeDamage); + } + } + + // 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, + sx: f64, + sy: f64, + sw: f64, + sh: f64, + dx: f64, + dy: f64, + dw: f64, + dh: f64, + ) -> (Rect, Rect) { + 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) + } + + fn update_transform(&self) { + self.send_canvas_2d_msg(Canvas2dMsg::SetTransform(self.state.borrow().transform)) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-fillrect + pub fn fill_rect(&self, x: f64, y: f64, width: f64, height: f64) { + if let Some(rect) = self.create_drawable_rect(x, y, width, height) { + self.send_canvas_2d_msg(Canvas2dMsg::FillRect(rect)); + } + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-clearrect + pub fn clear_rect(&self, x: f64, y: f64, width: f64, height: f64) { + if let Some(rect) = self.create_drawable_rect(x, y, width, height) { + self.send_canvas_2d_msg(Canvas2dMsg::ClearRect(rect)); + } + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokerect + pub fn stroke_rect(&self, x: f64, y: f64, width: f64, height: f64) { + if let Some(rect) = self.create_drawable_rect(x, y, width, height) { + self.send_canvas_2d_msg(Canvas2dMsg::StrokeRect(rect)); + } + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx + pub fn shadow_offset_x(&self) -> f64 { + self.state.borrow().shadow_offset_x + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx + pub fn set_shadow_offset_x(&self, value: f64) { + if !value.is_finite() || value == self.state.borrow().shadow_offset_x { + return; + } + self.state.borrow_mut().shadow_offset_x = value; + self.send_canvas_2d_msg(Canvas2dMsg::SetShadowOffsetX(value)) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsety + pub fn shadow_offset_y(&self) -> f64 { + self.state.borrow().shadow_offset_y + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsety + pub fn set_shadow_offset_y(&self, value: f64) { + if !value.is_finite() || value == self.state.borrow().shadow_offset_y { + return; + } + self.state.borrow_mut().shadow_offset_y = value; + self.send_canvas_2d_msg(Canvas2dMsg::SetShadowOffsetY(value)) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowblur + pub fn shadow_blur(&self) -> f64 { + self.state.borrow().shadow_blur + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowblur + pub fn set_shadow_blur(&self, value: f64) { + if !value.is_finite() || value < 0f64 || value == self.state.borrow().shadow_blur { + return; + } + self.state.borrow_mut().shadow_blur = value; + self.send_canvas_2d_msg(Canvas2dMsg::SetShadowBlur(value)) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor + pub fn shadow_color(&self) -> DOMString { + let mut result = String::new(); + serialize(&self.state.borrow().shadow_color, &mut result).unwrap(); + DOMString::from(result) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor + pub fn set_shadow_color(&self, value: DOMString) { + if let Ok(color) = parse_color(&value) { + self.state.borrow_mut().shadow_color = color; + self.send_canvas_2d_msg(Canvas2dMsg::SetShadowColor(color)) + } + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle + pub fn stroke_style(&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(DomRoot::from_ref(&*gradient)) + }, + CanvasFillOrStrokeStyle::Pattern(ref pattern) => { + StringOrCanvasGradientOrCanvasPattern::CanvasPattern(DomRoot::from_ref(&*pattern)) + }, + } + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle + pub fn set_stroke_style( + &self, + canvas: Option<&HTMLCanvasElement>, + value: StringOrCanvasGradientOrCanvasPattern, + ) { + match value { + StringOrCanvasGradientOrCanvasPattern::String(string) => { + if let Ok(rgba) = self.parse_color(canvas, &string) { + self.state.borrow_mut().stroke_style = CanvasFillOrStrokeStyle::Color(rgba); + self.send_canvas_2d_msg(Canvas2dMsg::SetStrokeStyle(FillOrStrokeStyle::Color( + rgba, + ))); + } + }, + StringOrCanvasGradientOrCanvasPattern::CanvasGradient(gradient) => { + self.state.borrow_mut().stroke_style = + CanvasFillOrStrokeStyle::Gradient(Dom::from_ref(&*gradient)); + self.send_canvas_2d_msg(Canvas2dMsg::SetStrokeStyle( + gradient.to_fill_or_stroke_style(), + )); + }, + StringOrCanvasGradientOrCanvasPattern::CanvasPattern(pattern) => { + self.state.borrow_mut().stroke_style = + CanvasFillOrStrokeStyle::Pattern(Dom::from_ref(&*pattern)); + self.send_canvas_2d_msg(Canvas2dMsg::SetStrokeStyle( + pattern.to_fill_or_stroke_style(), + )); + if !pattern.origin_is_clean() { + self.set_origin_unclean(); + } + }, + } + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle + pub fn fill_style(&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(DomRoot::from_ref(&*gradient)) + }, + CanvasFillOrStrokeStyle::Pattern(ref pattern) => { + StringOrCanvasGradientOrCanvasPattern::CanvasPattern(DomRoot::from_ref(&*pattern)) + }, + } + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle + pub fn set_fill_style( + &self, + canvas: Option<&HTMLCanvasElement>, + value: StringOrCanvasGradientOrCanvasPattern, + ) { + match value { + StringOrCanvasGradientOrCanvasPattern::String(string) => { + if let Ok(rgba) = self.parse_color(canvas, &string) { + self.state.borrow_mut().fill_style = CanvasFillOrStrokeStyle::Color(rgba); + self.send_canvas_2d_msg(Canvas2dMsg::SetFillStyle(FillOrStrokeStyle::Color( + rgba, + ))) + } + }, + StringOrCanvasGradientOrCanvasPattern::CanvasGradient(gradient) => { + self.state.borrow_mut().fill_style = + CanvasFillOrStrokeStyle::Gradient(Dom::from_ref(&*gradient)); + self.send_canvas_2d_msg(Canvas2dMsg::SetFillStyle( + gradient.to_fill_or_stroke_style(), + )); + }, + StringOrCanvasGradientOrCanvasPattern::CanvasPattern(pattern) => { + self.state.borrow_mut().fill_style = + CanvasFillOrStrokeStyle::Pattern(Dom::from_ref(&*pattern)); + self.send_canvas_2d_msg(Canvas2dMsg::SetFillStyle( + pattern.to_fill_or_stroke_style(), + )); + if !pattern.origin_is_clean() { + self.set_origin_unclean(); + } + }, + } + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-createlineargradient + pub fn create_linear_gradient( + &self, + global: &GlobalScope, + x0: Finite, + y0: Finite, + x1: Finite, + y1: Finite, + ) -> DomRoot { + CanvasGradient::new( + global, + CanvasGradientStyle::Linear(LinearGradientStyle::new(*x0, *y0, *x1, *y1, Vec::new())), + ) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-createradialgradient + pub fn create_radial_gradient( + &self, + global: &GlobalScope, + x0: Finite, + y0: Finite, + r0: Finite, + x1: Finite, + y1: Finite, + r1: Finite, + ) -> Fallible> { + if *r0 < 0. || *r1 < 0. { + return Err(Error::IndexSize); + } + + Ok(CanvasGradient::new( + global, + CanvasGradientStyle::Radial(RadialGradientStyle::new( + *x0, + *y0, + *r0, + *x1, + *y1, + *r1, + Vec::new(), + )), + )) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-createpattern + pub fn create_pattern( + &self, + global: &GlobalScope, + image: CanvasImageSource, + mut repetition: DOMString, + ) -> Fallible> { + let (image_data, image_size) = match image { + CanvasImageSource::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 + image + .get_url() + .and_then(|url| { + self.fetch_image_data(url, cors_setting_for_element(image.upcast())) + }) + .ok_or(Error::InvalidState)? + }, + CanvasImageSource::HTMLCanvasElement(ref canvas) => { + let (data, size) = canvas.fetch_all_data().ok_or(Error::InvalidState)?; + let data = data + .map(|data| data.to_vec()) + .unwrap_or_else(|| vec![0; size.area() as usize * 4]); + (data, size) + }, + CanvasImageSource::CSSStyleValue(ref value) => value + .get_url(self.base_url.clone()) + .and_then(|url| self.fetch_image_data(url, None)) + .ok_or(Error::InvalidState)?, + }; + + if repetition.is_empty() { + repetition.push_str("repeat"); + } + + if let Ok(rep) = RepetitionStyle::from_str(&repetition) { + Ok(CanvasPattern::new( + global, + image_data, + image_size, + rep, + self.is_origin_clean(image), + )) + } else { + Err(Error::Syntax) + } + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-save + pub fn save(&self) { + self.saved_states + .borrow_mut() + .push(self.state.borrow().clone()); + self.send_canvas_2d_msg(Canvas2dMsg::SaveContext); + } + + #[allow(unrooted_must_root)] + // https://html.spec.whatwg.org/multipage/#dom-context-2d-restore + pub 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.send_canvas_2d_msg(Canvas2dMsg::RestoreContext); + } + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalalpha + pub fn global_alpha(&self) -> f64 { + self.state.borrow().global_alpha + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalalpha + pub fn set_global_alpha(&self, alpha: f64) { + if !alpha.is_finite() || alpha > 1.0 || alpha < 0.0 { + return; + } + + self.state.borrow_mut().global_alpha = alpha; + self.send_canvas_2d_msg(Canvas2dMsg::SetGlobalAlpha(alpha as f32)) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalcompositeoperation + pub fn global_composite_operation(&self) -> DOMString { + match self.state.borrow().global_composition { + CompositionOrBlending::Composition(op) => DOMString::from(op.to_str()), + CompositionOrBlending::Blending(op) => DOMString::from(op.to_str()), + } + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalcompositeoperation + pub fn set_global_composite_operation(&self, op_str: DOMString) { + if let Ok(op) = CompositionOrBlending::from_str(&op_str) { + self.state.borrow_mut().global_composition = op; + self.send_canvas_2d_msg(Canvas2dMsg::SetGlobalComposition(op)) + } + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-imagesmoothingenabled + pub fn image_smoothing_enabled(&self) -> bool { + self.state.borrow().image_smoothing_enabled + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-imagesmoothingenabled + pub fn set_image_smoothing_enabled(&self, value: bool) { + self.state.borrow_mut().image_smoothing_enabled = value; + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext + pub fn fill_text(&self, text: DOMString, x: f64, y: f64, max_width: Option) { + let parsed_text: String = text.into(); + self.send_canvas_2d_msg(Canvas2dMsg::FillText(parsed_text, x, y, max_width)); + } + + // https://html.spec.whatwg.org/multipage/#textmetrics + pub fn measure_text(&self, global: &GlobalScope, _text: DOMString) -> DomRoot { + // FIXME: for now faking the implementation of MeasureText(). + // See https://github.com/servo/servo/issues/5411#issuecomment-533776291 + TextMetrics::new( + global, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + ) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth + pub fn line_width(&self) -> f64 { + self.state.borrow().line_width + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth + pub fn set_line_width(&self, width: f64) { + if !width.is_finite() || width <= 0.0 { + return; + } + + self.state.borrow_mut().line_width = width; + self.send_canvas_2d_msg(Canvas2dMsg::SetLineWidth(width as f32)) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-linecap + pub fn line_cap(&self) -> CanvasLineCap { + match self.state.borrow().line_cap { + LineCapStyle::Butt => CanvasLineCap::Butt, + LineCapStyle::Round => CanvasLineCap::Round, + LineCapStyle::Square => CanvasLineCap::Square, + } + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-linecap + pub fn set_line_cap(&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.send_canvas_2d_msg(Canvas2dMsg::SetLineCap(line_cap)); + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-linejoin + pub fn line_join(&self) -> CanvasLineJoin { + match self.state.borrow().line_join { + LineJoinStyle::Round => CanvasLineJoin::Round, + LineJoinStyle::Bevel => CanvasLineJoin::Bevel, + LineJoinStyle::Miter => CanvasLineJoin::Miter, + } + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-linejoin + pub fn set_line_join(&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.send_canvas_2d_msg(Canvas2dMsg::SetLineJoin(line_join)); + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-miterlimit + pub fn miter_limit(&self) -> f64 { + self.state.borrow().miter_limit + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-miterlimit + pub fn set_miter_limit(&self, limit: f64) { + if !limit.is_finite() || limit <= 0.0 { + return; + } + + self.state.borrow_mut().miter_limit = limit; + self.send_canvas_2d_msg(Canvas2dMsg::SetMiterLimit(limit as f32)) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-createimagedata + pub fn create_image_data( + &self, + global: &GlobalScope, + sw: i32, + sh: i32, + ) -> Fallible> { + if sw == 0 || sh == 0 { + return Err(Error::IndexSize); + } + ImageData::new(global, sw.abs() as u32, sh.abs() as u32, None) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-createimagedata + pub fn create_image_data_( + &self, + global: &GlobalScope, + imagedata: &ImageData, + ) -> Fallible> { + ImageData::new(global, imagedata.Width(), imagedata.Height(), None) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-getimagedata + pub fn get_image_data( + &self, + canvas_size: Size2D, + global: &GlobalScope, + sx: i32, + sy: i32, + sw: i32, + sh: i32, + ) -> Fallible> { + // FIXME(nox): There are many arithmetic operations here that can + // overflow or underflow, this should probably be audited. + + if sw == 0 || sh == 0 { + return Err(Error::IndexSize); + } + + if !self.origin_is_clean() { + return Err(Error::Security); + } + + let (origin, size) = adjust_size_sign(Point2D::new(sx, sy), Size2D::new(sw, sh)); + 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(global, size.width, size.height, None); + }, + }; + + ImageData::new( + global, + size.width, + size.height, + Some(self.get_rect(canvas_size, read_rect)), + ) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata + pub fn put_image_data( + &self, + canvas_size: Size2D, + imagedata: &ImageData, + dx: i32, + dy: i32, + ) { + self.put_image_data_( + canvas_size, + imagedata, + dx, + dy, + 0, + 0, + imagedata.Width() as i32, + imagedata.Height() as i32, + ) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata + #[allow(unsafe_code)] + pub fn put_image_data_( + &self, + canvas_size: Size2D, + imagedata: &ImageData, + dx: i32, + dy: 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(), imagedata.Height()); + if imagedata_size.area() == 0 { + return; + } + + // Step 1. + // Done later. + + // Step 2. + // TODO: throw InvalidState if buffer is detached. + + // 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. + 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(); + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage + pub fn draw_image( + &self, + canvas: Option<&HTMLCanvasElement>, + image: CanvasImageSource, + dx: f64, + dy: f64, + ) -> ErrorResult { + if !(dx.is_finite() && dy.is_finite()) { + return Ok(()); + } + + self.draw_image_internal(canvas, image, 0f64, 0f64, None, None, dx, dy, None, None) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage + pub fn draw_image_( + &self, + canvas: Option<&HTMLCanvasElement>, + image: CanvasImageSource, + dx: f64, + dy: f64, + dw: f64, + dh: f64, + ) -> ErrorResult { + if !(dx.is_finite() && dy.is_finite() && dw.is_finite() && dh.is_finite()) { + return Ok(()); + } + + self.draw_image_internal( + canvas, + image, + 0f64, + 0f64, + None, + None, + dx, + dy, + Some(dw), + Some(dh), + ) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage + pub fn draw_image__( + &self, + canvas: Option<&HTMLCanvasElement>, + image: CanvasImageSource, + 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(()); + } + + self.draw_image_internal( + canvas, + image, + sx, + sy, + Some(sw), + Some(sh), + dx, + dy, + Some(dw), + Some(dh), + ) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-beginpath + pub fn begin_path(&self) { + self.send_canvas_2d_msg(Canvas2dMsg::BeginPath); + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-fill + pub fn fill(&self, _fill_rule: CanvasFillRule) { + // TODO: Process fill rule + self.send_canvas_2d_msg(Canvas2dMsg::Fill); + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-stroke + pub fn stroke(&self) { + self.send_canvas_2d_msg(Canvas2dMsg::Stroke); + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-clip + pub fn clip(&self, _fill_rule: CanvasFillRule) { + // TODO: Process fill rule + self.send_canvas_2d_msg(Canvas2dMsg::Clip); + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-ispointinpath + pub fn is_point_in_path( + &self, + global: &GlobalScope, + 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) = + profiled_ipc::channel::(global.time_profiler_chan().clone()).unwrap(); + self.send_canvas_2d_msg(Canvas2dMsg::IsPointInPath(x, y, fill_rule, sender)); + receiver.recv().unwrap() + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-scale + pub 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_scale(x as f32, y as f32); + self.update_transform() + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-rotate + pub 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_transform(&Transform2D::row_major( + cos as f32, + sin as f32, + -sin as f32, + cos as f32, + 0.0, + 0.0, + )); + self.update_transform() + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-translate + pub 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_translate(vec2(x as f32, y as f32)); + self.update_transform() + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-transform + pub 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; + } + + let transform = self.state.borrow().transform; + self.state.borrow_mut().transform = transform.pre_transform(&Transform2D::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-settransform + pub fn set_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.state.borrow_mut().transform = + Transform2D::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-resettransform + pub fn reset_transform(&self) { + self.state.borrow_mut().transform = Transform2D::identity(); + self.update_transform() + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-closepath + pub fn close_path(&self) { + self.send_canvas_2d_msg(Canvas2dMsg::ClosePath); + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-moveto + pub fn move_to(&self, x: f64, y: f64) { + if !(x.is_finite() && y.is_finite()) { + return; + } + self.send_canvas_2d_msg(Canvas2dMsg::MoveTo(Point2D::new(x as f32, y as f32))); + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-lineto + pub fn line_to(&self, x: f64, y: f64) { + if !(x.is_finite() && y.is_finite()) { + return; + } + self.send_canvas_2d_msg(Canvas2dMsg::LineTo(Point2D::new(x as f32, y as f32))); + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-rect + pub 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), + ); + self.send_canvas_2d_msg(Canvas2dMsg::Rect(rect)); + } + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-quadraticcurveto + pub fn quadratic_curve_to(&self, cpx: f64, cpy: f64, x: f64, y: f64) { + if !(cpx.is_finite() && cpy.is_finite() && x.is_finite() && y.is_finite()) { + return; + } + self.send_canvas_2d_msg(Canvas2dMsg::QuadraticCurveTo( + Point2D::new(cpx as f32, cpy as f32), + Point2D::new(x as f32, y as f32), + )); + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-beziercurveto + pub fn bezier_curve_to(&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; + } + self.send_canvas_2d_msg(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), + )); + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-arc + pub 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); + } + + self.send_canvas_2d_msg(Canvas2dMsg::Arc( + Point2D::new(x as f32, y as f32), + r as f32, + start as f32, + end as f32, + ccw, + )); + Ok(()) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-arcto + pub fn arc_to(&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.send_canvas_2d_msg(Canvas2dMsg::ArcTo( + Point2D::new(cp1x as f32, cp1y as f32), + Point2D::new(cp2x as f32, cp2y as f32), + r as f32, + )); + Ok(()) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-ellipse + pub fn ellipse( + &self, + x: f64, + y: f64, + rx: f64, + ry: f64, + rotation: f64, + start: f64, + end: f64, + ccw: bool, + ) -> ErrorResult { + if !([x, y, rx, ry, rotation, start, end] + .iter() + .all(|x| x.is_finite())) + { + return Ok(()); + } + if rx < 0.0 || ry < 0.0 { + return Err(Error::IndexSize); + } + + self.send_canvas_2d_msg(Canvas2dMsg::Ellipse( + Point2D::new(x as f32, y as f32), + rx as f32, + ry as f32, + rotation as f32, + start as f32, + end as f32, + ccw, + )); + Ok(()) + } +} + +pub fn parse_color(string: &str) -> Result { + let mut input = ParserInput::new(string); + let mut parser = Parser::new(&mut input); + 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 +pub fn is_rect_valid(rect: Rect) -> bool { + rect.size.width > 0.0 && rect.size.height > 0.0 +} + +// https://html.spec.whatwg.org/multipage/#serialisation-of-a-color +pub fn serialize(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() + ) + } +} + +pub fn adjust_size_sign( + mut origin: Point2D, + mut size: Size2D, +) -> (Point2D, Size2D) { + 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/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 999aab5320c..9f6403bec5c 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use crate::canvas_state::{CanvasContextState, CanvasState}; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasFillRule; @@ -9,61 +10,24 @@ use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::Ca 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::ImageDataBinding::ImageDataMethods; use crate::dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern; -use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; -use crate::dom::bindings::inheritance::Castable; +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, CanvasGradientStyle, ToFillOrStrokeStyle}; +use crate::dom::canvasgradient::CanvasGradient; use crate::dom::canvaspattern::CanvasPattern; -use crate::dom::element::cors_setting_for_element; -use crate::dom::element::Element; use crate::dom::globalscope::GlobalScope; -use crate::dom::htmlcanvaselement::{CanvasContext, HTMLCanvasElement}; +use crate::dom::htmlcanvaselement::HTMLCanvasElement; use crate::dom::imagedata::ImageData; -use crate::dom::node::{Node, NodeDamage}; -use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope; use crate::dom::textmetrics::TextMetrics; -use crate::unpremultiplytable::UNPREMULTIPLY_TABLE; use canvas_traits::canvas::{Canvas2dMsg, CanvasId, CanvasMsg}; -use canvas_traits::canvas::{CompositionOrBlending, FillOrStrokeStyle, FillRule}; -use canvas_traits::canvas::{LineCapStyle, LineJoinStyle, LinearGradientStyle}; -use canvas_traits::canvas::{RadialGradientStyle, RepetitionStyle}; -use cssparser::Color as CSSColor; -use cssparser::{Parser, ParserInput, RGBA}; use dom_struct::dom_struct; -use euclid::{ - default::{Point2D, Rect, Size2D, Transform2D}, - vec2, -}; -use ipc_channel::ipc::{self, IpcSender}; -use net_traits::image_cache::CanRequestImages; -use net_traits::image_cache::ImageCache; -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 net_traits::request::CorsSettings; -use pixels::PixelFormat; -use profile_traits::ipc as profiled_ipc; -use script_traits::ScriptMsg; -use servo_url::{ImmutableOrigin, ServoUrl}; -use std::cell::Cell; -use std::str::FromStr; -use std::sync::Arc; -use std::{fmt, mem}; - -#[must_root] -#[derive(Clone, JSTraceable, MallocSizeOf)] -#[allow(dead_code)] -enum CanvasFillOrStrokeStyle { - Color(RGBA), - Gradient(Dom), - Pattern(Dom), -} +use euclid::default::{Rect, Size2D}; +use ipc_channel::ipc::IpcSender; +use servo_url::ServoUrl; +use std::mem; // https://html.spec.whatwg.org/multipage/#canvasrenderingcontext2d #[dom_struct] @@ -75,1390 +39,6 @@ pub struct CanvasRenderingContext2D { canvas_state: DomRefCell, } -#[must_root] -#[derive(Clone, JSTraceable, MallocSizeOf)] -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: Transform2D, - 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: Transform2D::identity(), - shadow_offset_x: 0.0, - shadow_offset_y: 0.0, - shadow_blur: 0.0, - shadow_color: RGBA::transparent(), - } - } -} - -#[must_root] -#[derive(JSTraceable, MallocSizeOf)] -pub struct CanvasState { - #[ignore_malloc_size_of = "Defined in ipc-channel"] - ipc_renderer: IpcSender, - canvas_id: CanvasId, - state: DomRefCell, - origin_clean: Cell, - #[ignore_malloc_size_of = "Arc"] - image_cache: Arc, - /// The base URL for resolving CSS image URL values. - /// Needed because of https://github.com/servo/servo/issues/17625 - base_url: ServoUrl, - origin: ImmutableOrigin, - /// Any missing image URLs. - missing_image_urls: DomRefCell>, - saved_states: DomRefCell>, -} - -impl CanvasState { - pub fn new(global: &GlobalScope, size: Size2D) -> CanvasState { - debug!("Creating new canvas rendering context."); - let (sender, receiver) = - profiled_ipc::channel(global.time_profiler_chan().clone()).unwrap(); - let script_to_constellation_chan = global.script_to_constellation_chan(); - debug!("Asking constellation to create new canvas thread."); - script_to_constellation_chan - .send(ScriptMsg::CreateCanvasPaintThread(size, sender)) - .unwrap(); - let (ipc_renderer, canvas_id) = receiver.recv().unwrap(); - debug!("Done."); - // Worklets always receive a unique origin. This messes with fetching - // cached images in the case of paint worklets, since the image cache - // is keyed on the origin requesting the image data. - let origin = if global.is::() { - global.api_base_url().origin() - } else { - global.origin().immutable().clone() - }; - CanvasState { - ipc_renderer: ipc_renderer, - canvas_id: canvas_id, - state: DomRefCell::new(CanvasContextState::new()), - origin_clean: Cell::new(true), - image_cache: global.image_cache(), - base_url: global.api_base_url(), - missing_image_urls: DomRefCell::new(Vec::new()), - saved_states: DomRefCell::new(Vec::new()), - origin, - } - } - - pub fn get_canvas_id(&self) -> CanvasId { - self.canvas_id.clone() - } - - pub fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) { - self.ipc_renderer - .send(CanvasMsg::Canvas2d(msg, self.get_canvas_id())) - .unwrap() - } - - fn create_drawable_rect(&self, x: f64, y: f64, w: f64, h: f64) -> Option> { - if !([x, y, w, h].iter().all(|val| val.is_finite())) { - return None; - } - - if w == 0.0 && h == 0.0 { - return None; - } - - Some(Rect::new( - Point2D::new(x as f32, y as f32), - Size2D::new(w as f32, h as f32), - )) - } - - fn origin_is_clean(&self) -> bool { - self.origin_clean.get() - } - - fn set_origin_unclean(&self) { - self.origin_clean.set(false) - } - - // https://html.spec.whatwg.org/multipage/#the-image-argument-is-not-origin-clean - fn is_origin_clean(&self, image: CanvasImageSource) -> bool { - match image { - CanvasImageSource::HTMLCanvasElement(canvas) => canvas.origin_is_clean(), - CanvasImageSource::HTMLImageElement(image) => { - image.same_origin(GlobalScope::entry().origin()) - }, - CanvasImageSource::CSSStyleValue(_) => true, - } - } - - fn fetch_image_data( - &self, - url: ServoUrl, - cors_setting: Option, - ) -> Option<(Vec, Size2D)> { - let img = match self.request_image_from_cache(url, cors_setting) { - ImageResponse::Loaded(img, _) => img, - ImageResponse::PlaceholderLoaded(_, _) | - ImageResponse::None | - ImageResponse::MetadataLoaded(_) => { - return None; - }, - }; - - let image_size = Size2D::new(img.width, img.height); - let image_data = match img.format { - PixelFormat::BGRA8 => img.bytes.to_vec(), - pixel_format => unimplemented!("unsupported pixel format ({:?})", pixel_format), - }; - - Some((image_data, image_size)) - } - - fn request_image_from_cache( - &self, - url: ServoUrl, - cors_setting: Option, - ) -> ImageResponse { - let response = self.image_cache.find_image_or_metadata( - url.clone(), - self.origin.clone(), - cors_setting, - UsePlaceholder::No, - CanRequestImages::No, - ); - match response { - Ok(ImageOrMetadataAvailable::ImageAvailable(image, url)) => { - ImageResponse::Loaded(image, url) - }, - Err(ImageState::Pending(_)) => ImageResponse::None, - _ => { - // Rather annoyingly, we get the same response back from - // A load which really failed and from a load which hasn't started yet. - self.missing_image_urls.borrow_mut().push(url); - ImageResponse::None - }, - } - } - - fn parse_color(&self, canvas: Option<&HTMLCanvasElement>, string: &str) -> Result { - let mut input = ParserInput::new(string); - let mut parser = Parser::new(&mut input); - 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 canvas = match canvas { - // https://drafts.css-houdini.org/css-paint-api/#2d-rendering-context - // Whenever "currentColor" is used as a color in the PaintRenderingContext2D API, - // it is treated as opaque black. - None => return Ok(RGBA::new(0, 0, 0, 255)), - Some(ref canvas) => &**canvas, - }; - - let canvas_element = canvas.upcast::(); - - match canvas_element.style() { - Some(ref s) if canvas_element.has_css_layout_box() => { - Ok(s.get_inherited_text().color) - }, - _ => Ok(RGBA::new(0, 0, 0, 255)), - } - }, - _ => Err(()), - } - } else { - Err(()) - } - } - - pub fn get_rect(&self, canvas_size: Size2D, rect: Rect) -> Vec { - assert!(self.origin_is_clean()); - - 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 - } - - // - // 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, - htmlcanvas: Option<&HTMLCanvasElement>, - image: CanvasImageSource, - sx: f64, - sy: f64, - sw: Option, - sh: Option, - dx: f64, - dy: f64, - dw: Option, - dh: Option, - ) -> ErrorResult { - let result = match image { - CanvasImageSource::HTMLCanvasElement(ref canvas) => { - self.draw_html_canvas_element(&canvas, htmlcanvas, sx, sy, sw, sh, dx, dy, dw, dh) - }, - CanvasImageSource::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 url = image.get_url().ok_or(Error::InvalidState)?; - let cors_setting = cors_setting_for_element(image.upcast()); - self.fetch_and_draw_image_data( - htmlcanvas, - url, - cors_setting, - sx, - sy, - sw, - sh, - dx, - dy, - dw, - dh, - ) - }, - CanvasImageSource::CSSStyleValue(ref value) => { - let url = value - .get_url(self.base_url.clone()) - .ok_or(Error::InvalidState)?; - self.fetch_and_draw_image_data( - htmlcanvas, url, None, 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, - htmlcanvas: Option<&HTMLCanvasElement>, - sx: f64, - sy: f64, - sw: Option, - sh: Option, - dx: f64, - dy: f64, - dw: Option, - dh: Option, - ) -> 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 Ok(()); - } - - let smoothing_enabled = self.state.borrow().image_smoothing_enabled; - - if let Some(context) = canvas.context() { - match *context { - CanvasContext::Context2d(ref context) => { - context.send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther( - self.get_canvas_id(), - image_size, - dest_rect, - source_rect, - smoothing_enabled, - )); - }, - _ => return Err(Error::InvalidState), - } - } else { - self.send_canvas_2d_msg(Canvas2dMsg::DrawImage( - None, - image_size, - dest_rect, - source_rect, - smoothing_enabled, - )); - } - - self.mark_as_dirty(htmlcanvas); - Ok(()) - } - - fn fetch_and_draw_image_data( - &self, - canvas: Option<&HTMLCanvasElement>, - url: ServoUrl, - cors_setting: Option, - sx: f64, - sy: f64, - sw: Option, - sh: Option, - dx: f64, - dy: f64, - dw: Option, - dh: Option, - ) -> ErrorResult { - debug!("Fetching image {}.", url); - let (mut image_data, image_size) = self - .fetch_image_data(url, cors_setting) - .ok_or(Error::InvalidState)?; - pixels::rgba8_premultiply_inplace(&mut image_data); - let image_size = image_size.to_f64(); - - 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); - - // 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 Ok(()); - } - - let smoothing_enabled = self.state.borrow().image_smoothing_enabled; - self.send_canvas_2d_msg(Canvas2dMsg::DrawImage( - Some(image_data.into()), - image_size, - dest_rect, - source_rect, - smoothing_enabled, - )); - self.mark_as_dirty(canvas); - Ok(()) - } - - fn mark_as_dirty(&self, canvas: Option<&HTMLCanvasElement>) { - if let Some(ref canvas) = canvas { - canvas.upcast::().dirty(NodeDamage::OtherNodeDamage); - } - } - - // 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, - sx: f64, - sy: f64, - sw: f64, - sh: f64, - dx: f64, - dy: f64, - dw: f64, - dh: f64, - ) -> (Rect, Rect) { - 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) - } - - fn update_transform(&self) { - self.send_canvas_2d_msg(Canvas2dMsg::SetTransform(self.state.borrow().transform)) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-fillrect - pub fn FillRect(&self, x: f64, y: f64, width: f64, height: f64) { - if let Some(rect) = self.create_drawable_rect(x, y, width, height) { - self.send_canvas_2d_msg(Canvas2dMsg::FillRect(rect)); - } - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-clearrect - pub fn ClearRect(&self, x: f64, y: f64, width: f64, height: f64) { - if let Some(rect) = self.create_drawable_rect(x, y, width, height) { - self.send_canvas_2d_msg(Canvas2dMsg::ClearRect(rect)); - } - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokerect - pub fn StrokeRect(&self, x: f64, y: f64, width: f64, height: f64) { - if let Some(rect) = self.create_drawable_rect(x, y, width, height) { - self.send_canvas_2d_msg(Canvas2dMsg::StrokeRect(rect)); - } - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx - pub fn ShadowOffsetX(&self) -> f64 { - self.state.borrow().shadow_offset_x - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx - pub 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.send_canvas_2d_msg(Canvas2dMsg::SetShadowOffsetX(value)) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsety - pub fn ShadowOffsetY(&self) -> f64 { - self.state.borrow().shadow_offset_y - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsety - pub 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.send_canvas_2d_msg(Canvas2dMsg::SetShadowOffsetY(value)) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowblur - pub fn ShadowBlur(&self) -> f64 { - self.state.borrow().shadow_blur - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowblur - pub 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.send_canvas_2d_msg(Canvas2dMsg::SetShadowBlur(value)) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor - pub fn ShadowColor(&self) -> DOMString { - let mut result = String::new(); - serialize(&self.state.borrow().shadow_color, &mut result).unwrap(); - DOMString::from(result) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor - pub fn SetShadowColor(&self, value: DOMString) { - if let Ok(color) = parse_color(&value) { - self.state.borrow_mut().shadow_color = color; - self.send_canvas_2d_msg(Canvas2dMsg::SetShadowColor(color)) - } - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle - pub 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(DomRoot::from_ref(&*gradient)) - }, - CanvasFillOrStrokeStyle::Pattern(ref pattern) => { - StringOrCanvasGradientOrCanvasPattern::CanvasPattern(DomRoot::from_ref(&*pattern)) - }, - } - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle - pub fn SetStrokeStyle( - &self, - canvas: Option<&HTMLCanvasElement>, - value: StringOrCanvasGradientOrCanvasPattern, - ) { - match value { - StringOrCanvasGradientOrCanvasPattern::String(string) => { - if let Ok(rgba) = self.parse_color(canvas, &string) { - self.state.borrow_mut().stroke_style = CanvasFillOrStrokeStyle::Color(rgba); - self.send_canvas_2d_msg(Canvas2dMsg::SetStrokeStyle(FillOrStrokeStyle::Color( - rgba, - ))); - } - }, - StringOrCanvasGradientOrCanvasPattern::CanvasGradient(gradient) => { - self.state.borrow_mut().stroke_style = - CanvasFillOrStrokeStyle::Gradient(Dom::from_ref(&*gradient)); - self.send_canvas_2d_msg(Canvas2dMsg::SetStrokeStyle( - gradient.to_fill_or_stroke_style(), - )); - }, - StringOrCanvasGradientOrCanvasPattern::CanvasPattern(pattern) => { - self.state.borrow_mut().stroke_style = - CanvasFillOrStrokeStyle::Pattern(Dom::from_ref(&*pattern)); - self.send_canvas_2d_msg(Canvas2dMsg::SetStrokeStyle( - pattern.to_fill_or_stroke_style(), - )); - if !pattern.origin_is_clean() { - self.set_origin_unclean(); - } - }, - } - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle - pub 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(DomRoot::from_ref(&*gradient)) - }, - CanvasFillOrStrokeStyle::Pattern(ref pattern) => { - StringOrCanvasGradientOrCanvasPattern::CanvasPattern(DomRoot::from_ref(&*pattern)) - }, - } - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle - pub fn SetFillStyle( - &self, - canvas: Option<&HTMLCanvasElement>, - value: StringOrCanvasGradientOrCanvasPattern, - ) { - match value { - StringOrCanvasGradientOrCanvasPattern::String(string) => { - if let Ok(rgba) = self.parse_color(canvas, &string) { - self.state.borrow_mut().fill_style = CanvasFillOrStrokeStyle::Color(rgba); - self.send_canvas_2d_msg(Canvas2dMsg::SetFillStyle(FillOrStrokeStyle::Color( - rgba, - ))) - } - }, - StringOrCanvasGradientOrCanvasPattern::CanvasGradient(gradient) => { - self.state.borrow_mut().fill_style = - CanvasFillOrStrokeStyle::Gradient(Dom::from_ref(&*gradient)); - self.send_canvas_2d_msg(Canvas2dMsg::SetFillStyle( - gradient.to_fill_or_stroke_style(), - )); - }, - StringOrCanvasGradientOrCanvasPattern::CanvasPattern(pattern) => { - self.state.borrow_mut().fill_style = - CanvasFillOrStrokeStyle::Pattern(Dom::from_ref(&*pattern)); - self.send_canvas_2d_msg(Canvas2dMsg::SetFillStyle( - pattern.to_fill_or_stroke_style(), - )); - if !pattern.origin_is_clean() { - self.set_origin_unclean(); - } - }, - } - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-createlineargradient - pub fn CreateLinearGradient( - &self, - global: &GlobalScope, - x0: Finite, - y0: Finite, - x1: Finite, - y1: Finite, - ) -> DomRoot { - CanvasGradient::new( - global, - CanvasGradientStyle::Linear(LinearGradientStyle::new(*x0, *y0, *x1, *y1, Vec::new())), - ) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-createradialgradient - pub fn CreateRadialGradient( - &self, - global: &GlobalScope, - x0: Finite, - y0: Finite, - r0: Finite, - x1: Finite, - y1: Finite, - r1: Finite, - ) -> Fallible> { - if *r0 < 0. || *r1 < 0. { - return Err(Error::IndexSize); - } - - Ok(CanvasGradient::new( - global, - CanvasGradientStyle::Radial(RadialGradientStyle::new( - *x0, - *y0, - *r0, - *x1, - *y1, - *r1, - Vec::new(), - )), - )) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-createpattern - pub fn CreatePattern( - &self, - global: &GlobalScope, - image: CanvasImageSource, - mut repetition: DOMString, - ) -> Fallible> { - let (image_data, image_size) = match image { - CanvasImageSource::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 - image - .get_url() - .and_then(|url| { - self.fetch_image_data(url, cors_setting_for_element(image.upcast())) - }) - .ok_or(Error::InvalidState)? - }, - CanvasImageSource::HTMLCanvasElement(ref canvas) => { - let (data, size) = canvas.fetch_all_data().ok_or(Error::InvalidState)?; - let data = data - .map(|data| data.to_vec()) - .unwrap_or_else(|| vec![0; size.area() as usize * 4]); - (data, size) - }, - CanvasImageSource::CSSStyleValue(ref value) => value - .get_url(self.base_url.clone()) - .and_then(|url| self.fetch_image_data(url, None)) - .ok_or(Error::InvalidState)?, - }; - - if repetition.is_empty() { - repetition.push_str("repeat"); - } - - if let Ok(rep) = RepetitionStyle::from_str(&repetition) { - Ok(CanvasPattern::new( - global, - image_data, - image_size, - rep, - self.is_origin_clean(image), - )) - } else { - Err(Error::Syntax) - } - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-save - pub fn Save(&self) { - self.saved_states - .borrow_mut() - .push(self.state.borrow().clone()); - self.send_canvas_2d_msg(Canvas2dMsg::SaveContext); - } - - #[allow(unrooted_must_root)] - // https://html.spec.whatwg.org/multipage/#dom-context-2d-restore - pub 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.send_canvas_2d_msg(Canvas2dMsg::RestoreContext); - } - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalalpha - pub fn GlobalAlpha(&self) -> f64 { - self.state.borrow().global_alpha - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalalpha - pub fn SetGlobalAlpha(&self, alpha: f64) { - if !alpha.is_finite() || alpha > 1.0 || alpha < 0.0 { - return; - } - - self.state.borrow_mut().global_alpha = alpha; - self.send_canvas_2d_msg(Canvas2dMsg::SetGlobalAlpha(alpha as f32)) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalcompositeoperation - pub fn GlobalCompositeOperation(&self) -> DOMString { - match self.state.borrow().global_composition { - CompositionOrBlending::Composition(op) => DOMString::from(op.to_str()), - CompositionOrBlending::Blending(op) => DOMString::from(op.to_str()), - } - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalcompositeoperation - pub fn SetGlobalCompositeOperation(&self, op_str: DOMString) { - if let Ok(op) = CompositionOrBlending::from_str(&op_str) { - self.state.borrow_mut().global_composition = op; - self.send_canvas_2d_msg(Canvas2dMsg::SetGlobalComposition(op)) - } - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-imagesmoothingenabled - pub fn ImageSmoothingEnabled(&self) -> bool { - self.state.borrow().image_smoothing_enabled - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-imagesmoothingenabled - pub fn SetImageSmoothingEnabled(&self, value: bool) { - self.state.borrow_mut().image_smoothing_enabled = value; - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext - pub fn FillText(&self, text: DOMString, x: f64, y: f64, max_width: Option) { - let parsed_text: String = text.into(); - self.send_canvas_2d_msg(Canvas2dMsg::FillText(parsed_text, x, y, max_width)); - } - - // https://html.spec.whatwg.org/multipage/#textmetrics - pub fn MeasureText(&self, global: &GlobalScope, _text: DOMString) -> DomRoot { - // FIXME: for now faking the implementation of MeasureText(). - // See https://github.com/servo/servo/issues/5411#issuecomment-533776291 - TextMetrics::new( - global, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - ) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth - pub fn LineWidth(&self) -> f64 { - self.state.borrow().line_width - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth - pub fn SetLineWidth(&self, width: f64) { - if !width.is_finite() || width <= 0.0 { - return; - } - - self.state.borrow_mut().line_width = width; - self.send_canvas_2d_msg(Canvas2dMsg::SetLineWidth(width as f32)) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-linecap - pub fn LineCap(&self) -> CanvasLineCap { - match self.state.borrow().line_cap { - LineCapStyle::Butt => CanvasLineCap::Butt, - LineCapStyle::Round => CanvasLineCap::Round, - LineCapStyle::Square => CanvasLineCap::Square, - } - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-linecap - pub 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.send_canvas_2d_msg(Canvas2dMsg::SetLineCap(line_cap)); - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-linejoin - pub fn LineJoin(&self) -> CanvasLineJoin { - match self.state.borrow().line_join { - LineJoinStyle::Round => CanvasLineJoin::Round, - LineJoinStyle::Bevel => CanvasLineJoin::Bevel, - LineJoinStyle::Miter => CanvasLineJoin::Miter, - } - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-linejoin - pub 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.send_canvas_2d_msg(Canvas2dMsg::SetLineJoin(line_join)); - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-miterlimit - pub fn MiterLimit(&self) -> f64 { - self.state.borrow().miter_limit - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-miterlimit - pub fn SetMiterLimit(&self, limit: f64) { - if !limit.is_finite() || limit <= 0.0 { - return; - } - - self.state.borrow_mut().miter_limit = limit; - self.send_canvas_2d_msg(Canvas2dMsg::SetMiterLimit(limit as f32)) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-createimagedata - pub fn CreateImageData( - &self, - global: &GlobalScope, - sw: i32, - sh: i32, - ) -> Fallible> { - if sw == 0 || sh == 0 { - return Err(Error::IndexSize); - } - ImageData::new(global, sw.abs() as u32, sh.abs() as u32, None) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-createimagedata - pub fn CreateImageData_( - &self, - global: &GlobalScope, - imagedata: &ImageData, - ) -> Fallible> { - ImageData::new(global, imagedata.Width(), imagedata.Height(), None) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-getimagedata - pub fn GetImageData( - &self, - canvas_size: Size2D, - global: &GlobalScope, - sx: i32, - sy: i32, - sw: i32, - sh: i32, - ) -> Fallible> { - // FIXME(nox): There are many arithmetic operations here that can - // overflow or underflow, this should probably be audited. - - if sw == 0 || sh == 0 { - return Err(Error::IndexSize); - } - - if !self.origin_is_clean() { - return Err(Error::Security); - } - - let (origin, size) = adjust_size_sign(Point2D::new(sx, sy), Size2D::new(sw, sh)); - 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(global, size.width, size.height, None); - }, - }; - - ImageData::new( - global, - size.width, - size.height, - Some(self.get_rect(canvas_size, read_rect)), - ) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata - pub fn PutImageData(&self, canvas_size: Size2D, imagedata: &ImageData, dx: i32, dy: i32) { - self.PutImageData_( - canvas_size, - imagedata, - dx, - dy, - 0, - 0, - imagedata.Width() as i32, - imagedata.Height() as i32, - ) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata - #[allow(unsafe_code)] - pub fn PutImageData_( - &self, - canvas_size: Size2D, - imagedata: &ImageData, - dx: i32, - dy: 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(), imagedata.Height()); - if imagedata_size.area() == 0 { - return; - } - - // Step 1. - // Done later. - - // Step 2. - // TODO: throw InvalidState if buffer is detached. - - // 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. - 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(); - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage - pub fn DrawImage( - &self, - canvas: Option<&HTMLCanvasElement>, - image: CanvasImageSource, - dx: f64, - dy: f64, - ) -> ErrorResult { - if !(dx.is_finite() && dy.is_finite()) { - return Ok(()); - } - - self.draw_image(canvas, image, 0f64, 0f64, None, None, dx, dy, None, None) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage - pub fn DrawImage_( - &self, - canvas: Option<&HTMLCanvasElement>, - image: CanvasImageSource, - dx: f64, - dy: f64, - dw: f64, - dh: f64, - ) -> ErrorResult { - if !(dx.is_finite() && dy.is_finite() && dw.is_finite() && dh.is_finite()) { - return Ok(()); - } - - self.draw_image( - canvas, - image, - 0f64, - 0f64, - None, - None, - dx, - dy, - Some(dw), - Some(dh), - ) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage - pub fn DrawImage__( - &self, - canvas: Option<&HTMLCanvasElement>, - image: CanvasImageSource, - 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(()); - } - - self.draw_image( - canvas, - image, - sx, - sy, - Some(sw), - Some(sh), - dx, - dy, - Some(dw), - Some(dh), - ) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-beginpath - pub fn BeginPath(&self) { - self.send_canvas_2d_msg(Canvas2dMsg::BeginPath); - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-fill - pub fn Fill(&self, _fill_rule: CanvasFillRule) { - // TODO: Process fill rule - self.send_canvas_2d_msg(Canvas2dMsg::Fill); - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-stroke - pub fn Stroke(&self) { - self.send_canvas_2d_msg(Canvas2dMsg::Stroke); - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-clip - pub fn Clip(&self, _fill_rule: CanvasFillRule) { - // TODO: Process fill rule - self.send_canvas_2d_msg(Canvas2dMsg::Clip); - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-ispointinpath - pub fn IsPointInPath( - &self, - global: &GlobalScope, - 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) = - profiled_ipc::channel::(global.time_profiler_chan().clone()).unwrap(); - self.send_canvas_2d_msg(Canvas2dMsg::IsPointInPath(x, y, fill_rule, sender)); - receiver.recv().unwrap() - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-scale - pub 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_scale(x as f32, y as f32); - self.update_transform() - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-rotate - pub 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_transform(&Transform2D::row_major( - cos as f32, - sin as f32, - -sin as f32, - cos as f32, - 0.0, - 0.0, - )); - self.update_transform() - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-translate - pub 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_translate(vec2(x as f32, y as f32)); - self.update_transform() - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-transform - pub 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; - } - - let transform = self.state.borrow().transform; - self.state.borrow_mut().transform = transform.pre_transform(&Transform2D::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-settransform - pub 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 = - Transform2D::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-resettransform - pub fn ResetTransform(&self) { - self.state.borrow_mut().transform = Transform2D::identity(); - self.update_transform() - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-closepath - pub fn ClosePath(&self) { - self.send_canvas_2d_msg(Canvas2dMsg::ClosePath); - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-moveto - pub fn MoveTo(&self, x: f64, y: f64) { - if !(x.is_finite() && y.is_finite()) { - return; - } - self.send_canvas_2d_msg(Canvas2dMsg::MoveTo(Point2D::new(x as f32, y as f32))); - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-lineto - pub fn LineTo(&self, x: f64, y: f64) { - if !(x.is_finite() && y.is_finite()) { - return; - } - self.send_canvas_2d_msg(Canvas2dMsg::LineTo(Point2D::new(x as f32, y as f32))); - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-rect - pub 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), - ); - self.send_canvas_2d_msg(Canvas2dMsg::Rect(rect)); - } - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-quadraticcurveto - pub 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; - } - self.send_canvas_2d_msg(Canvas2dMsg::QuadraticCurveTo( - Point2D::new(cpx as f32, cpy as f32), - Point2D::new(x as f32, y as f32), - )); - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-beziercurveto - pub 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; - } - self.send_canvas_2d_msg(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), - )); - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-arc - pub 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); - } - - self.send_canvas_2d_msg(Canvas2dMsg::Arc( - Point2D::new(x as f32, y as f32), - r as f32, - start as f32, - end as f32, - ccw, - )); - Ok(()) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-arcto - pub 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.send_canvas_2d_msg(Canvas2dMsg::ArcTo( - Point2D::new(cp1x as f32, cp1y as f32), - Point2D::new(cp2x as f32, cp2y as f32), - r as f32, - )); - Ok(()) - } - - // https://html.spec.whatwg.org/multipage/#dom-context-2d-ellipse - pub fn Ellipse( - &self, - x: f64, - y: f64, - rx: f64, - ry: f64, - rotation: f64, - start: f64, - end: f64, - ccw: bool, - ) -> ErrorResult { - if !([x, y, rx, ry, rotation, start, end] - .iter() - .all(|x| x.is_finite())) - { - return Ok(()); - } - if rx < 0.0 || ry < 0.0 { - return Err(Error::IndexSize); - } - - self.send_canvas_2d_msg(Canvas2dMsg::Ellipse( - Point2D::new(x as f32, y as f32), - rx as f32, - ry as f32, - rotation as f32, - start as f32, - end as f32, - ccw, - )); - Ok(()) - } -} - impl CanvasRenderingContext2D { pub fn new_inherited( global: &GlobalScope, @@ -1493,7 +73,7 @@ impl CanvasRenderingContext2D { self.reset_to_initial_state(); self.canvas_state .borrow() - .ipc_renderer + .get_ipc_renderer() .send(CanvasMsg::Recreate( size, self.canvas_state.borrow().get_canvas_id(), @@ -1503,11 +83,15 @@ impl CanvasRenderingContext2D { // https://html.spec.whatwg.org/multipage/#reset-the-rendering-context-to-its-default-state fn reset_to_initial_state(&self) { - self.canvas_state.borrow().saved_states.borrow_mut().clear(); - *self.canvas_state.borrow().state.borrow_mut() = CanvasContextState::new(); + self.canvas_state + .borrow() + .get_saved_state() + .borrow_mut() + .clear(); + *self.canvas_state.borrow().get_state().borrow_mut() = CanvasContextState::new(); } - fn mark_as_dirty(&self) { + pub fn mark_as_dirty(&self) { self.canvas_state .borrow() .mark_as_dirty(self.canvas.as_ref().map(|c| &**c)) @@ -1515,7 +99,11 @@ impl CanvasRenderingContext2D { pub fn take_missing_image_urls(&self) -> Vec { mem::replace( - &mut self.canvas_state.borrow().missing_image_urls.borrow_mut(), + &mut self + .canvas_state + .borrow() + .get_missing_image_urls() + .borrow_mut(), vec![], ) } @@ -1529,7 +117,7 @@ impl CanvasRenderingContext2D { } pub fn get_ipc_renderer(&self) -> IpcSender { - self.canvas_state.borrow().ipc_renderer.clone() + self.canvas_state.borrow().get_ipc_renderer().clone() } pub fn origin_is_clean(&self) -> bool { @@ -1559,7 +147,7 @@ impl LayoutCanvasRenderingContext2DHelpers for LayoutDom f64 { - self.canvas_state.borrow().GlobalAlpha() + self.canvas_state.borrow().global_alpha() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalalpha fn SetGlobalAlpha(&self, alpha: f64) { - self.canvas_state.borrow().SetGlobalAlpha(alpha) + self.canvas_state.borrow().set_global_alpha(alpha) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalcompositeoperation fn GlobalCompositeOperation(&self) -> DOMString { - self.canvas_state.borrow().GlobalCompositeOperation() + self.canvas_state.borrow().global_composite_operation() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalcompositeoperation fn SetGlobalCompositeOperation(&self, op_str: DOMString) { self.canvas_state .borrow() - .SetGlobalCompositeOperation(op_str) + .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) { - self.canvas_state.borrow().FillRect(x, y, width, height); + self.canvas_state.borrow().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) { - self.canvas_state.borrow().ClearRect(x, y, width, height); + self.canvas_state.borrow().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) { - self.canvas_state.borrow().StrokeRect(x, y, width, height); + self.canvas_state.borrow().stroke_rect(x, y, width, height); self.mark_as_dirty(); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-beginpath fn BeginPath(&self) { - self.canvas_state.borrow().BeginPath() + self.canvas_state.borrow().begin_path() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-closepath fn ClosePath(&self) { - self.canvas_state.borrow().ClosePath() + self.canvas_state.borrow().close_path() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-fill fn Fill(&self, fill_rule: CanvasFillRule) { - self.canvas_state.borrow().Fill(fill_rule); + self.canvas_state.borrow().fill(fill_rule); self.mark_as_dirty(); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-stroke fn Stroke(&self) { - self.canvas_state.borrow().Stroke(); + self.canvas_state.borrow().stroke(); self.mark_as_dirty(); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-clip fn Clip(&self, fill_rule: CanvasFillRule) { - self.canvas_state.borrow().Clip(fill_rule) + self.canvas_state.borrow().clip(fill_rule) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-ispointinpath fn IsPointInPath(&self, x: f64, y: f64, fill_rule: CanvasFillRule) -> bool { self.canvas_state .borrow() - .IsPointInPath(&self.global(), x, y, fill_rule) + .is_point_in_path(&self.global(), x, y, fill_rule) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext fn FillText(&self, text: DOMString, x: f64, y: f64, max_width: Option) { - self.canvas_state.borrow().FillText(text, x, y, max_width); + self.canvas_state.borrow().fill_text(text, x, y, max_width); self.mark_as_dirty(); } // https://html.spec.whatwg.org/multipage/#textmetrics fn MeasureText(&self, text: DOMString) -> DomRoot { - self.canvas_state.borrow().MeasureText(&self.global(), text) + self.canvas_state + .borrow() + .measure_text(&self.global(), text) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage fn DrawImage(&self, image: CanvasImageSource, dx: f64, dy: f64) -> ErrorResult { self.canvas_state .borrow() - .DrawImage(self.canvas.as_ref().map(|c| &**c), image, dx, dy) + .draw_image(self.canvas.as_ref().map(|c| &**c), image, dx, dy) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage @@ -1731,7 +321,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { dw: f64, dh: f64, ) -> ErrorResult { - self.canvas_state.borrow().DrawImage_( + self.canvas_state.borrow().draw_image_( self.canvas.as_ref().map(|c| &**c), image, dx, @@ -1754,7 +344,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { dw: f64, dh: f64, ) -> ErrorResult { - self.canvas_state.borrow().DrawImage__( + self.canvas_state.borrow().draw_image__( self.canvas.as_ref().map(|c| &**c), image, sx, @@ -1770,39 +360,41 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { // https://html.spec.whatwg.org/multipage/#dom-context-2d-moveto fn MoveTo(&self, x: f64, y: f64) { - self.canvas_state.borrow().MoveTo(x, y) + self.canvas_state.borrow().move_to(x, y) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-lineto fn LineTo(&self, x: f64, y: f64) { - self.canvas_state.borrow().LineTo(x, y) + self.canvas_state.borrow().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) { - self.canvas_state.borrow().Rect(x, y, width, height) + self.canvas_state.borrow().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) { - self.canvas_state.borrow().QuadraticCurveTo(cpx, cpy, x, y) + self.canvas_state + .borrow() + .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) { self.canvas_state .borrow() - .BezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) + .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 { - self.canvas_state.borrow().Arc(x, y, r, start, end, ccw) + self.canvas_state.borrow().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 { - self.canvas_state.borrow().ArcTo(cp1x, cp1y, cp2x, cp2y, r) + self.canvas_state.borrow().arc_to(cp1x, cp1y, cp2x, cp2y, r) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-ellipse @@ -1819,60 +411,62 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { ) -> ErrorResult { self.canvas_state .borrow() - .Ellipse(x, y, rx, ry, rotation, start, end, ccw) + .ellipse(x, y, rx, ry, rotation, start, end, ccw) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-imagesmoothingenabled fn ImageSmoothingEnabled(&self) -> bool { - self.canvas_state.borrow().ImageSmoothingEnabled() + self.canvas_state.borrow().image_smoothing_enabled() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-imagesmoothingenabled fn SetImageSmoothingEnabled(&self, value: bool) { - self.canvas_state.borrow().SetImageSmoothingEnabled(value) + self.canvas_state + .borrow() + .set_image_smoothing_enabled(value) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle fn StrokeStyle(&self) -> StringOrCanvasGradientOrCanvasPattern { - self.canvas_state.borrow().StrokeStyle() + self.canvas_state.borrow().stroke_style() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) { self.canvas_state .borrow() - .SetStrokeStyle(self.canvas.as_ref().map(|c| &**c), value) + .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 { - self.canvas_state.borrow().FillStyle() + self.canvas_state.borrow().fill_style() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle fn SetFillStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) { self.canvas_state .borrow() - .SetFillStyle(self.canvas.as_ref().map(|c| &**c), value) + .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: i32, sh: i32) -> Fallible> { self.canvas_state .borrow() - .CreateImageData(&self.global(), sw, sh) + .create_image_data(&self.global(), sw, sh) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-createimagedata fn CreateImageData_(&self, imagedata: &ImageData) -> Fallible> { self.canvas_state .borrow() - .CreateImageData_(&self.global(), imagedata) + .create_image_data_(&self.global(), imagedata) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-getimagedata fn GetImageData(&self, sx: i32, sy: i32, sw: i32, sh: i32) -> Fallible> { - self.canvas_state.borrow().GetImageData( + self.canvas_state.borrow().get_image_data( self.canvas .as_ref() .map_or(Size2D::zero(), |c| c.get_size()), @@ -1886,7 +480,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata fn PutImageData(&self, imagedata: &ImageData, dx: i32, dy: i32) { - self.canvas_state.borrow().PutImageData( + self.canvas_state.borrow().put_image_data( self.canvas .as_ref() .map_or(Size2D::zero(), |c| c.get_size()), @@ -1908,7 +502,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { dirty_width: i32, dirty_height: i32, ) { - self.canvas_state.borrow().PutImageData_( + self.canvas_state.borrow().put_image_data_( self.canvas .as_ref() .map_or(Size2D::zero(), |c| c.get_size()), @@ -1933,7 +527,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { ) -> DomRoot { self.canvas_state .borrow() - .CreateLinearGradient(&self.global(), x0, y0, x1, y1) + .create_linear_gradient(&self.global(), x0, y0, x1, y1) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-createradialgradient @@ -1948,7 +542,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { ) -> Fallible> { self.canvas_state .borrow() - .CreateRadialGradient(&self.global(), x0, y0, r0, x1, y1, r1) + .create_radial_gradient(&self.global(), x0, y0, r0, x1, y1, r1) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-createpattern @@ -1959,87 +553,87 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { ) -> Fallible> { self.canvas_state .borrow() - .CreatePattern(&self.global(), image, repetition) + .create_pattern(&self.global(), image, repetition) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth fn LineWidth(&self) -> f64 { - self.canvas_state.borrow().LineWidth() + self.canvas_state.borrow().line_width() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth fn SetLineWidth(&self, width: f64) { - self.canvas_state.borrow().SetLineWidth(width) + self.canvas_state.borrow().set_line_width(width) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linecap fn LineCap(&self) -> CanvasLineCap { - self.canvas_state.borrow().LineCap() + self.canvas_state.borrow().line_cap() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linecap fn SetLineCap(&self, cap: CanvasLineCap) { - self.canvas_state.borrow().SetLineCap(cap) + self.canvas_state.borrow().set_line_cap(cap) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linejoin fn LineJoin(&self) -> CanvasLineJoin { - self.canvas_state.borrow().LineJoin() + self.canvas_state.borrow().line_join() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linejoin fn SetLineJoin(&self, join: CanvasLineJoin) { - self.canvas_state.borrow().SetLineJoin(join) + self.canvas_state.borrow().set_line_join(join) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-miterlimit fn MiterLimit(&self) -> f64 { - self.canvas_state.borrow().MiterLimit() + self.canvas_state.borrow().miter_limit() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-miterlimit fn SetMiterLimit(&self, limit: f64) { - self.canvas_state.borrow().SetMiterLimit(limit) + self.canvas_state.borrow().set_miter_limit(limit) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx fn ShadowOffsetX(&self) -> f64 { - self.canvas_state.borrow().ShadowOffsetX() + self.canvas_state.borrow().shadow_offset_x() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx fn SetShadowOffsetX(&self, value: f64) { - self.canvas_state.borrow().SetShadowOffsetX(value) + self.canvas_state.borrow().set_shadow_offset_x(value) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsety fn ShadowOffsetY(&self) -> f64 { - self.canvas_state.borrow().ShadowOffsetY() + self.canvas_state.borrow().shadow_offset_y() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsety fn SetShadowOffsetY(&self, value: f64) { - self.canvas_state.borrow().SetShadowOffsetY(value) + self.canvas_state.borrow().set_shadow_offset_y(value) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowblur fn ShadowBlur(&self) -> f64 { - self.canvas_state.borrow().ShadowBlur() + self.canvas_state.borrow().shadow_blur() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowblur fn SetShadowBlur(&self, value: f64) { - self.canvas_state.borrow().SetShadowBlur(value) + self.canvas_state.borrow().set_shadow_blur(value) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor fn ShadowColor(&self) -> DOMString { - self.canvas_state.borrow().ShadowColor() + self.canvas_state.borrow().shadow_color() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor fn SetShadowColor(&self, value: DOMString) { - self.canvas_state.borrow().SetShadowColor(value) + self.canvas_state.borrow().set_shadow_color(value) } } @@ -2048,78 +642,10 @@ impl Drop for CanvasRenderingContext2D { if let Err(err) = self .canvas_state .borrow() - .ipc_renderer + .get_ipc_renderer() .send(CanvasMsg::Close(self.canvas_state.borrow().get_canvas_id())) { warn!("Could not close canvas: {}", err) } } } - -pub fn parse_color(string: &str) -> Result { - let mut input = ParserInput::new(string); - let mut parser = Parser::new(&mut input); - 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) -> bool { - rect.size.width > 0.0 && rect.size.height > 0.0 -} - -// https://html.spec.whatwg.org/multipage/#serialisation-of-a-color -fn serialize(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() - ) - } -} - -fn adjust_size_sign( - mut origin: Point2D, - mut size: Size2D, -) -> (Point2D, Size2D) { - 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/offscreencanvasrenderingcontext2d.rs b/components/script/dom/offscreencanvasrenderingcontext2d.rs index bb2d1e73e96..04e0a13032c 100644 --- a/components/script/dom/offscreencanvasrenderingcontext2d.rs +++ b/components/script/dom/offscreencanvasrenderingcontext2d.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use crate::canvas_state::CanvasState; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasFillRule; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasImageSource; @@ -18,7 +19,6 @@ use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::DOMString; use crate::dom::canvasgradient::CanvasGradient; use crate::dom::canvaspattern::CanvasPattern; -use crate::dom::canvasrenderingcontext2d::CanvasState; use crate::dom::globalscope::GlobalScope; use crate::dom::htmlcanvaselement::HTMLCanvasElement; use crate::dom::imagedata::ImageData; @@ -85,81 +85,81 @@ impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContex // https://html.spec.whatwg.org/multipage/#dom-context-2d-fillrect fn FillRect(&self, x: f64, y: f64, width: f64, height: f64) { - self.canvas_state.borrow().FillRect(x, y, width, height); + self.canvas_state.borrow().fill_rect(x, y, width, height); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-clearrect fn ClearRect(&self, x: f64, y: f64, width: f64, height: f64) { - self.canvas_state.borrow().ClearRect(x, y, width, height); + self.canvas_state.borrow().clear_rect(x, y, width, height); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokerect fn StrokeRect(&self, x: f64, y: f64, width: f64, height: f64) { - self.canvas_state.borrow().StrokeRect(x, y, width, height); + self.canvas_state.borrow().stroke_rect(x, y, width, height); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx fn ShadowOffsetX(&self) -> f64 { - self.canvas_state.borrow().ShadowOffsetX() + self.canvas_state.borrow().shadow_offset_x() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx fn SetShadowOffsetX(&self, value: f64) { - self.canvas_state.borrow().SetShadowOffsetX(value) + self.canvas_state.borrow().set_shadow_offset_x(value) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsety fn ShadowOffsetY(&self) -> f64 { - self.canvas_state.borrow().ShadowOffsetY() + self.canvas_state.borrow().shadow_offset_y() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsety fn SetShadowOffsetY(&self, value: f64) { - self.canvas_state.borrow().SetShadowOffsetY(value) + self.canvas_state.borrow().set_shadow_offset_y(value) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowblur fn ShadowBlur(&self) -> f64 { - self.canvas_state.borrow().ShadowBlur() + self.canvas_state.borrow().shadow_blur() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowblur fn SetShadowBlur(&self, value: f64) { - self.canvas_state.borrow().SetShadowBlur(value) + self.canvas_state.borrow().set_shadow_blur(value) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor fn ShadowColor(&self) -> DOMString { - self.canvas_state.borrow().ShadowColor() + self.canvas_state.borrow().shadow_color() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor fn SetShadowColor(&self, value: DOMString) { - self.canvas_state.borrow().SetShadowColor(value) + self.canvas_state.borrow().set_shadow_color(value) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle fn StrokeStyle(&self) -> StringOrCanvasGradientOrCanvasPattern { - self.canvas_state.borrow().StrokeStyle() + self.canvas_state.borrow().stroke_style() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) { self.canvas_state .borrow() - .SetStrokeStyle(self.htmlcanvas.as_ref().map(|c| &**c), value) + .set_stroke_style(self.htmlcanvas.as_ref().map(|c| &**c), value) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle fn FillStyle(&self) -> StringOrCanvasGradientOrCanvasPattern { - self.canvas_state.borrow().FillStyle() + self.canvas_state.borrow().fill_style() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle fn SetFillStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) { self.canvas_state .borrow() - .SetFillStyle(self.htmlcanvas.as_ref().map(|c| &**c), value) + .set_fill_style(self.htmlcanvas.as_ref().map(|c| &**c), value) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-createlineargradient @@ -172,7 +172,7 @@ impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContex ) -> DomRoot { self.canvas_state .borrow() - .CreateLinearGradient(&self.global(), x0, y0, x1, y1) + .create_linear_gradient(&self.global(), x0, y0, x1, y1) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-createradialgradient @@ -187,7 +187,7 @@ impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContex ) -> Fallible> { self.canvas_state .borrow() - .CreateRadialGradient(&self.global(), x0, y0, r0, x1, y1, r1) + .create_radial_gradient(&self.global(), x0, y0, r0, x1, y1, r1) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-createpattern @@ -198,119 +198,123 @@ impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContex ) -> Fallible> { self.canvas_state .borrow() - .CreatePattern(&self.global(), image, repetition) + .create_pattern(&self.global(), image, repetition) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-save fn Save(&self) { - self.canvas_state.borrow().Save() + self.canvas_state.borrow().save() } #[allow(unrooted_must_root)] // https://html.spec.whatwg.org/multipage/#dom-context-2d-restore fn Restore(&self) { - self.canvas_state.borrow().Restore() + self.canvas_state.borrow().restore() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalalpha fn GlobalAlpha(&self) -> f64 { - self.canvas_state.borrow().GlobalAlpha() + self.canvas_state.borrow().global_alpha() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalalpha fn SetGlobalAlpha(&self, alpha: f64) { - self.canvas_state.borrow().SetGlobalAlpha(alpha) + self.canvas_state.borrow().set_global_alpha(alpha) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalcompositeoperation fn GlobalCompositeOperation(&self) -> DOMString { - self.canvas_state.borrow().GlobalCompositeOperation() + self.canvas_state.borrow().global_composite_operation() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalcompositeoperation fn SetGlobalCompositeOperation(&self, op_str: DOMString) { self.canvas_state .borrow() - .SetGlobalCompositeOperation(op_str) + .set_global_composite_operation(op_str) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-imagesmoothingenabled fn ImageSmoothingEnabled(&self) -> bool { - self.canvas_state.borrow().ImageSmoothingEnabled() + self.canvas_state.borrow().image_smoothing_enabled() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-imagesmoothingenabled fn SetImageSmoothingEnabled(&self, value: bool) { - self.canvas_state.borrow().SetImageSmoothingEnabled(value) + self.canvas_state + .borrow() + .set_image_smoothing_enabled(value) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext fn FillText(&self, text: DOMString, x: f64, y: f64, max_width: Option) { - self.canvas_state.borrow().FillText(text, x, y, max_width) + self.canvas_state.borrow().fill_text(text, x, y, max_width) } // https://html.spec.whatwg.org/multipage/#textmetrics fn MeasureText(&self, text: DOMString) -> DomRoot { - self.canvas_state.borrow().MeasureText(&self.global(), text) + self.canvas_state + .borrow() + .measure_text(&self.global(), text) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth fn LineWidth(&self) -> f64 { - self.canvas_state.borrow().LineWidth() + self.canvas_state.borrow().line_width() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth fn SetLineWidth(&self, width: f64) { - self.canvas_state.borrow().SetLineWidth(width) + self.canvas_state.borrow().set_line_width(width) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linecap fn LineCap(&self) -> CanvasLineCap { - self.canvas_state.borrow().LineCap() + self.canvas_state.borrow().line_cap() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linecap fn SetLineCap(&self, cap: CanvasLineCap) { - self.canvas_state.borrow().SetLineCap(cap) + self.canvas_state.borrow().set_line_cap(cap) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linejoin fn LineJoin(&self) -> CanvasLineJoin { - self.canvas_state.borrow().LineJoin() + self.canvas_state.borrow().line_join() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linejoin fn SetLineJoin(&self, join: CanvasLineJoin) { - self.canvas_state.borrow().SetLineJoin(join) + self.canvas_state.borrow().set_line_join(join) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-miterlimit fn MiterLimit(&self) -> f64 { - self.canvas_state.borrow().MiterLimit() + self.canvas_state.borrow().miter_limit() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-miterlimit fn SetMiterLimit(&self, limit: f64) { - self.canvas_state.borrow().SetMiterLimit(limit) + self.canvas_state.borrow().set_miter_limit(limit) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-createimagedata fn CreateImageData(&self, sw: i32, sh: i32) -> Fallible> { self.canvas_state .borrow() - .CreateImageData(&self.global(), sw, sh) + .create_image_data(&self.global(), sw, sh) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-createimagedata fn CreateImageData_(&self, imagedata: &ImageData) -> Fallible> { self.canvas_state .borrow() - .CreateImageData_(&self.global(), imagedata) + .create_image_data_(&self.global(), imagedata) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-getimagedata fn GetImageData(&self, sx: i32, sy: i32, sw: i32, sh: i32) -> Fallible> { - self.canvas_state.borrow().GetImageData( + self.canvas_state.borrow().get_image_data( Size2D::new(self.width, self.height), &self.global(), sx, @@ -322,7 +326,7 @@ impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContex // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata fn PutImageData(&self, imagedata: &ImageData, dx: i32, dy: i32) { - self.canvas_state.borrow().PutImageData( + self.canvas_state.borrow().put_image_data( Size2D::new(self.width, self.height), imagedata, dx, @@ -342,7 +346,7 @@ impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContex dirty_width: i32, dirty_height: i32, ) { - self.canvas_state.borrow().PutImageData_( + self.canvas_state.borrow().put_image_data_( Size2D::new(self.width, self.height), imagedata, dx, @@ -358,7 +362,7 @@ impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContex fn DrawImage(&self, image: CanvasImageSource, dx: f64, dy: f64) -> ErrorResult { self.canvas_state .borrow() - .DrawImage(self.htmlcanvas.as_ref().map(|c| &**c), image, dx, dy) + .draw_image(self.htmlcanvas.as_ref().map(|c| &**c), image, dx, dy) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage @@ -370,7 +374,7 @@ impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContex dw: f64, dh: f64, ) -> ErrorResult { - self.canvas_state.borrow().DrawImage_( + self.canvas_state.borrow().draw_image_( self.htmlcanvas.as_ref().map(|c| &**c), image, dx, @@ -393,7 +397,7 @@ impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContex dw: f64, dh: f64, ) -> ErrorResult { - self.canvas_state.borrow().DrawImage__( + self.canvas_state.borrow().draw_image__( self.htmlcanvas.as_ref().map(|c| &**c), image, sx, @@ -409,101 +413,103 @@ impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContex // https://html.spec.whatwg.org/multipage/#dom-context-2d-beginpath fn BeginPath(&self) { - self.canvas_state.borrow().BeginPath() + self.canvas_state.borrow().begin_path() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-fill fn Fill(&self, fill_rule: CanvasFillRule) { - self.canvas_state.borrow().Fill(fill_rule) + self.canvas_state.borrow().fill(fill_rule) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-stroke fn Stroke(&self) { - self.canvas_state.borrow().Stroke() + self.canvas_state.borrow().stroke() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-clip fn Clip(&self, fill_rule: CanvasFillRule) { - self.canvas_state.borrow().Clip(fill_rule) + self.canvas_state.borrow().clip(fill_rule) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-ispointinpath fn IsPointInPath(&self, x: f64, y: f64, fill_rule: CanvasFillRule) -> bool { self.canvas_state .borrow() - .IsPointInPath(&self.global(), x, y, fill_rule) + .is_point_in_path(&self.global(), x, y, fill_rule) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-scale fn Scale(&self, x: f64, y: f64) { - self.canvas_state.borrow().Scale(x, y) + self.canvas_state.borrow().scale(x, y) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-rotate fn Rotate(&self, angle: f64) { - self.canvas_state.borrow().Rotate(angle) + self.canvas_state.borrow().rotate(angle) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-translate fn Translate(&self, x: f64, y: f64) { - self.canvas_state.borrow().Translate(x, y) + self.canvas_state.borrow().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) { - self.canvas_state.borrow().Transform(a, b, c, d, e, f) + self.canvas_state.borrow().transform(a, b, c, d, e, f) } // 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) { - self.canvas_state.borrow().SetTransform(a, b, c, d, e, f) + self.canvas_state.borrow().set_transform(a, b, c, d, e, f) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-resettransform fn ResetTransform(&self) { - self.canvas_state.borrow().ResetTransform() + self.canvas_state.borrow().reset_transform() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-closepath fn ClosePath(&self) { - self.canvas_state.borrow().ClosePath() + self.canvas_state.borrow().close_path() } // https://html.spec.whatwg.org/multipage/#dom-context-2d-moveto fn MoveTo(&self, x: f64, y: f64) { - self.canvas_state.borrow().MoveTo(x, y) + self.canvas_state.borrow().move_to(x, y) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-lineto fn LineTo(&self, x: f64, y: f64) { - self.canvas_state.borrow().LineTo(x, y) + self.canvas_state.borrow().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) { - self.canvas_state.borrow().Rect(x, y, width, height) + self.canvas_state.borrow().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) { - self.canvas_state.borrow().QuadraticCurveTo(cpx, cpy, x, y) + self.canvas_state + .borrow() + .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) { self.canvas_state .borrow() - .BezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) + .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 { - self.canvas_state.borrow().Arc(x, y, r, start, end, ccw) + self.canvas_state.borrow().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 { - self.canvas_state.borrow().ArcTo(cp1x, cp1y, cp2x, cp2y, r) + self.canvas_state.borrow().arc_to(cp1x, cp1y, cp2x, cp2y, r) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-ellipse @@ -520,6 +526,6 @@ impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContex ) -> ErrorResult { self.canvas_state .borrow() - .Ellipse(x, y, rx, ry, rotation, start, end, ccw) + .ellipse(x, y, rx, ry, rotation, start, end, ccw) } } diff --git a/components/script/lib.rs b/components/script/lib.rs index 773c4eba812..c87e705d1e9 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -55,6 +55,7 @@ mod devtools; pub mod document_loader; #[macro_use] mod dom; +mod canvas_state; mod compartments; pub mod fetch; mod image_listener;