mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
Implement drawing an image from a CSS style value into a canvas.
This commit is contained in:
parent
992c647f76
commit
2318caf002
8 changed files with 232 additions and 125 deletions
|
@ -1173,7 +1173,6 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
let device_pixel_ratio = state.layout_context.style_context.device_pixel_ratio();
|
let device_pixel_ratio = state.layout_context.style_context.device_pixel_ratio();
|
||||||
let size_in_au = unbordered_box.size.to_physical(style.writing_mode);
|
let size_in_au = unbordered_box.size.to_physical(style.writing_mode);
|
||||||
let size_in_px = TypedSize2D::new(size_in_au.width.to_f32_px(), size_in_au.height.to_f32_px());
|
let size_in_px = TypedSize2D::new(size_in_au.width.to_f32_px(), size_in_au.height.to_f32_px());
|
||||||
let size_in_dpx = size_in_px * device_pixel_ratio;
|
|
||||||
let name = paint_worklet.name.clone();
|
let name = paint_worklet.name.clone();
|
||||||
|
|
||||||
// Get the painter, and the computed values for its properties.
|
// Get the painter, and the computed values for its properties.
|
||||||
|
@ -1192,24 +1191,19 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
// TODO: add a one-place cache to avoid drawing the paint image every time.
|
// TODO: add a one-place cache to avoid drawing the paint image every time.
|
||||||
// https://github.com/servo/servo/issues/17369
|
// https://github.com/servo/servo/issues/17369
|
||||||
debug!("Drawing a paint image {}({},{}).", name, size_in_px.width, size_in_px.height);
|
debug!("Drawing a paint image {}({},{}).", name, size_in_px.width, size_in_px.height);
|
||||||
let (sender, receiver) = ipc::channel().unwrap();
|
let mut draw_result = painter.draw_a_paint_image(size_in_px, device_pixel_ratio, properties);
|
||||||
painter.draw_a_paint_image(size_in_px, device_pixel_ratio, properties, sender);
|
let webrender_image = WebRenderImageInfo {
|
||||||
|
width: draw_result.width,
|
||||||
// TODO: timeout
|
height: draw_result.height,
|
||||||
let webrender_image = match receiver.recv() {
|
format: draw_result.format,
|
||||||
Ok(CanvasData::Image(canvas_data)) => {
|
key: draw_result.image_key,
|
||||||
WebRenderImageInfo {
|
|
||||||
// TODO: it would be nice to get this data back from the canvas
|
|
||||||
width: size_in_dpx.width as u32,
|
|
||||||
height: size_in_dpx.height as u32,
|
|
||||||
format: PixelFormat::BGRA8,
|
|
||||||
key: Some(canvas_data.image_key),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Ok(CanvasData::WebGL(_)) => return warn!("Paint worklet generated WebGL."),
|
|
||||||
Err(err) => return warn!("Paint worklet recv generated error ({}).", err),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for url in draw_result.missing_image_urls.drain(..) {
|
||||||
|
debug!("Requesting missing image URL {}.", url);
|
||||||
|
state.layout_context.get_webrender_image_for_url(self.node, url, UsePlaceholder::No);
|
||||||
|
}
|
||||||
|
|
||||||
self.build_display_list_for_webrender_image(state,
|
self.build_display_list_for_webrender_image(state,
|
||||||
style,
|
style,
|
||||||
display_list_section,
|
display_list_section,
|
||||||
|
|
|
@ -12,12 +12,12 @@ use dom::bindings::cell::DOMRefCell;
|
||||||
use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods;
|
use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods;
|
||||||
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding;
|
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding;
|
||||||
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasFillRule;
|
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasFillRule;
|
||||||
|
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasImageSource;
|
||||||
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineCap;
|
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineCap;
|
||||||
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineJoin;
|
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineJoin;
|
||||||
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods;
|
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods;
|
||||||
use dom::bindings::codegen::Bindings::ImageDataBinding::ImageDataMethods;
|
use dom::bindings::codegen::Bindings::ImageDataBinding::ImageDataMethods;
|
||||||
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||||
use dom::bindings::codegen::UnionTypes::HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D;
|
|
||||||
use dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern;
|
use dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern;
|
||||||
use dom::bindings::error::{Error, ErrorResult, Fallible};
|
use dom::bindings::error::{Error, ErrorResult, Fallible};
|
||||||
use dom::bindings::inheritance::Castable;
|
use dom::bindings::inheritance::Castable;
|
||||||
|
@ -29,21 +29,25 @@ use dom::canvasgradient::{CanvasGradient, CanvasGradientStyle, ToFillOrStrokeSty
|
||||||
use dom::canvaspattern::CanvasPattern;
|
use dom::canvaspattern::CanvasPattern;
|
||||||
use dom::globalscope::GlobalScope;
|
use dom::globalscope::GlobalScope;
|
||||||
use dom::htmlcanvaselement::HTMLCanvasElement;
|
use dom::htmlcanvaselement::HTMLCanvasElement;
|
||||||
use dom::htmlcanvaselement::utils as canvas_utils;
|
|
||||||
use dom::htmlimageelement::HTMLImageElement;
|
|
||||||
use dom::imagedata::ImageData;
|
use dom::imagedata::ImageData;
|
||||||
use dom::node::{document_from_node, Node, NodeDamage, window_from_node};
|
use dom::node::{document_from_node, Node, NodeDamage, window_from_node};
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use euclid::{Transform2D, Point2D, Vector2D, Rect, Size2D, vec2};
|
use euclid::{Transform2D, Point2D, Vector2D, Rect, Size2D, vec2};
|
||||||
use ipc_channel::ipc::{self, IpcSender};
|
use ipc_channel::ipc::{self, IpcSender};
|
||||||
use net_traits::image::base::PixelFormat;
|
use net_traits::image::base::PixelFormat;
|
||||||
|
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::ImageResponse;
|
||||||
|
use net_traits::image_cache::ImageState;
|
||||||
|
use net_traits::image_cache::UsePlaceholder;
|
||||||
use num_traits::ToPrimitive;
|
use num_traits::ToPrimitive;
|
||||||
use script_traits::ScriptMsg as ConstellationMsg;
|
use script_traits::ScriptMsg as ConstellationMsg;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use std::{cmp, fmt};
|
use std::{cmp, fmt, mem};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::sync::Arc;
|
||||||
use unpremultiplytable::UNPREMULTIPLY_TABLE;
|
use unpremultiplytable::UNPREMULTIPLY_TABLE;
|
||||||
|
|
||||||
#[must_root]
|
#[must_root]
|
||||||
|
@ -61,9 +65,16 @@ pub struct CanvasRenderingContext2D {
|
||||||
reflector_: Reflector,
|
reflector_: Reflector,
|
||||||
#[ignore_heap_size_of = "Defined in ipc-channel"]
|
#[ignore_heap_size_of = "Defined in ipc-channel"]
|
||||||
ipc_renderer: IpcSender<CanvasMsg>,
|
ipc_renderer: IpcSender<CanvasMsg>,
|
||||||
// For rendering contexts created by an HTML canvas element, this is Some,
|
/// For rendering contexts created by an HTML canvas element, this is Some,
|
||||||
// for ones created by a paint worklet, this is None.
|
/// for ones created by a paint worklet, this is None.
|
||||||
canvas: Option<JS<HTMLCanvasElement>>,
|
canvas: Option<JS<HTMLCanvasElement>>,
|
||||||
|
#[ignore_heap_size_of = "Arc"]
|
||||||
|
image_cache: Arc<ImageCache>,
|
||||||
|
/// Any missing image URLs.
|
||||||
|
missing_image_urls: DOMRefCell<Vec<ServoUrl>>,
|
||||||
|
/// The base URL for resolving CSS image URL values.
|
||||||
|
/// Needed because of https://github.com/servo/servo/issues/17625
|
||||||
|
base_url: ServoUrl,
|
||||||
state: DOMRefCell<CanvasContextState>,
|
state: DOMRefCell<CanvasContextState>,
|
||||||
saved_states: DOMRefCell<Vec<CanvasContextState>>,
|
saved_states: DOMRefCell<Vec<CanvasContextState>>,
|
||||||
origin_clean: Cell<bool>,
|
origin_clean: Cell<bool>,
|
||||||
|
@ -113,6 +124,8 @@ impl CanvasContextState {
|
||||||
impl CanvasRenderingContext2D {
|
impl CanvasRenderingContext2D {
|
||||||
pub fn new_inherited(global: &GlobalScope,
|
pub fn new_inherited(global: &GlobalScope,
|
||||||
canvas: Option<&HTMLCanvasElement>,
|
canvas: Option<&HTMLCanvasElement>,
|
||||||
|
image_cache: Arc<ImageCache>,
|
||||||
|
base_url: ServoUrl,
|
||||||
size: Size2D<i32>)
|
size: Size2D<i32>)
|
||||||
-> CanvasRenderingContext2D {
|
-> CanvasRenderingContext2D {
|
||||||
debug!("Creating new canvas rendering context.");
|
debug!("Creating new canvas rendering context.");
|
||||||
|
@ -126,6 +139,9 @@ impl CanvasRenderingContext2D {
|
||||||
reflector_: Reflector::new(),
|
reflector_: Reflector::new(),
|
||||||
ipc_renderer: ipc_renderer,
|
ipc_renderer: ipc_renderer,
|
||||||
canvas: canvas.map(JS::from_ref),
|
canvas: canvas.map(JS::from_ref),
|
||||||
|
image_cache: image_cache,
|
||||||
|
missing_image_urls: DOMRefCell::new(Vec::new()),
|
||||||
|
base_url: base_url,
|
||||||
state: DOMRefCell::new(CanvasContextState::new()),
|
state: DOMRefCell::new(CanvasContextState::new()),
|
||||||
saved_states: DOMRefCell::new(Vec::new()),
|
saved_states: DOMRefCell::new(Vec::new()),
|
||||||
origin_clean: Cell::new(true),
|
origin_clean: Cell::new(true),
|
||||||
|
@ -136,9 +152,11 @@ impl CanvasRenderingContext2D {
|
||||||
canvas: &HTMLCanvasElement,
|
canvas: &HTMLCanvasElement,
|
||||||
size: Size2D<i32>)
|
size: Size2D<i32>)
|
||||||
-> Root<CanvasRenderingContext2D> {
|
-> Root<CanvasRenderingContext2D> {
|
||||||
reflect_dom_object(box CanvasRenderingContext2D::new_inherited(global, Some(canvas), size),
|
let window = window_from_node(canvas);
|
||||||
global,
|
let image_cache = window.image_cache();
|
||||||
CanvasRenderingContext2DBinding::Wrap)
|
let base_url = window.get_url();
|
||||||
|
let boxed = box CanvasRenderingContext2D::new_inherited(global, Some(canvas), image_cache, base_url, size);
|
||||||
|
reflect_dom_object(boxed, global, CanvasRenderingContext2DBinding::Wrap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#concept-canvas-set-bitmap-dimensions
|
// https://html.spec.whatwg.org/multipage/#concept-canvas-set-bitmap-dimensions
|
||||||
|
@ -224,15 +242,15 @@ impl CanvasRenderingContext2D {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#the-image-argument-is-not-origin-clean
|
// https://html.spec.whatwg.org/multipage/#the-image-argument-is-not-origin-clean
|
||||||
fn is_origin_clean(&self,
|
fn is_origin_clean(&self,
|
||||||
image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D)
|
image: CanvasImageSource)
|
||||||
-> bool {
|
-> bool {
|
||||||
match image {
|
match image {
|
||||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLCanvasElement(canvas) => {
|
CanvasImageSource::HTMLCanvasElement(canvas) => {
|
||||||
canvas.origin_is_clean()
|
canvas.origin_is_clean()
|
||||||
}
|
}
|
||||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::CanvasRenderingContext2D(image) =>
|
CanvasImageSource::CanvasRenderingContext2D(image) =>
|
||||||
image.origin_is_clean(),
|
image.origin_is_clean(),
|
||||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLImageElement(image) => {
|
CanvasImageSource::HTMLImageElement(image) => {
|
||||||
let canvas = match self.canvas {
|
let canvas = match self.canvas {
|
||||||
Some(ref canvas) => canvas,
|
Some(ref canvas) => canvas,
|
||||||
None => return false,
|
None => return false,
|
||||||
|
@ -241,6 +259,7 @@ impl CanvasRenderingContext2D {
|
||||||
let document = document_from_node(&**canvas);
|
let document = document_from_node(&**canvas);
|
||||||
document.url().clone().origin() == image_origin
|
document.url().clone().origin() == image_origin
|
||||||
}
|
}
|
||||||
|
CanvasImageSource::CSSStyleValue(_) => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,7 +285,7 @@ impl CanvasRenderingContext2D {
|
||||||
//
|
//
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
|
// https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
|
||||||
fn draw_image(&self,
|
fn draw_image(&self,
|
||||||
image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
|
image: CanvasImageSource,
|
||||||
sx: f64,
|
sx: f64,
|
||||||
sy: f64,
|
sy: f64,
|
||||||
sw: Option<f64>,
|
sw: Option<f64>,
|
||||||
|
@ -277,38 +296,30 @@ impl CanvasRenderingContext2D {
|
||||||
dh: Option<f64>)
|
dh: Option<f64>)
|
||||||
-> ErrorResult {
|
-> ErrorResult {
|
||||||
let result = match image {
|
let result = match image {
|
||||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLCanvasElement(ref canvas) => {
|
CanvasImageSource::HTMLCanvasElement(ref canvas) => {
|
||||||
self.draw_html_canvas_element(&canvas,
|
self.draw_html_canvas_element(&canvas,
|
||||||
sx, sy, sw, sh,
|
sx, sy, sw, sh,
|
||||||
dx, dy, dw, dh)
|
dx, dy, dw, dh)
|
||||||
}
|
}
|
||||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::CanvasRenderingContext2D(ref image) => {
|
CanvasImageSource::CanvasRenderingContext2D(ref image) => {
|
||||||
self.draw_html_canvas_element(&image.Canvas(),
|
self.draw_html_canvas_element(&image.Canvas(),
|
||||||
sx, sy, sw, sh,
|
sx, sy, sw, sh,
|
||||||
dx, dy, dw, dh)
|
dx, dy, dw, dh)
|
||||||
}
|
}
|
||||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLImageElement(ref image) => {
|
CanvasImageSource::HTMLImageElement(ref image) => {
|
||||||
// https://html.spec.whatwg.org/multipage/#img-error
|
// https://html.spec.whatwg.org/multipage/#img-error
|
||||||
// If the image argument is an HTMLImageElement object that is in the broken state,
|
// If the image argument is an HTMLImageElement object that is in the broken state,
|
||||||
// then throw an InvalidStateError exception
|
// then throw an InvalidStateError exception
|
||||||
let (image_data, image_size) = match self.fetch_image_data(image) {
|
let url = image.get_url().ok_or(Error::InvalidState)?;
|
||||||
Some((mut data, size)) => {
|
self.fetch_and_draw_image_data(url,
|
||||||
// Pixels come from cache in BGRA order and drawImage expects RGBA so we
|
sx, sy, sw, sh,
|
||||||
// have to swap the color values
|
dx, dy, dw, dh)
|
||||||
byte_swap_and_premultiply(&mut data);
|
}
|
||||||
let size = Size2D::new(size.width as f64, size.height as f64);
|
CanvasImageSource::CSSStyleValue(ref value) => {
|
||||||
(data, size)
|
let url = value.get_url(self.base_url.clone()).ok_or(Error::InvalidState)?;
|
||||||
},
|
self.fetch_and_draw_image_data(url,
|
||||||
None => return Err(Error::InvalidState),
|
sx, sy, sw, sh,
|
||||||
};
|
dx, dy, dw, dh)
|
||||||
let dw = dw.unwrap_or(image_size.width);
|
|
||||||
let dh = dh.unwrap_or(image_size.height);
|
|
||||||
let sw = sw.unwrap_or(image_size.width);
|
|
||||||
let sh = sh.unwrap_or(image_size.height);
|
|
||||||
self.draw_image_data(image_data,
|
|
||||||
image_size,
|
|
||||||
sx, sy, sw, sh,
|
|
||||||
dx, dy, dw, dh)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -386,6 +397,41 @@ impl CanvasRenderingContext2D {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fetch_and_draw_image_data(&self,
|
||||||
|
url: ServoUrl,
|
||||||
|
sx: f64,
|
||||||
|
sy: f64,
|
||||||
|
sw: Option<f64>,
|
||||||
|
sh: Option<f64>,
|
||||||
|
dx: f64,
|
||||||
|
dy: f64,
|
||||||
|
dw: Option<f64>,
|
||||||
|
dh: Option<f64>)
|
||||||
|
-> ErrorResult {
|
||||||
|
debug!("Fetching image {}.", url);
|
||||||
|
// https://html.spec.whatwg.org/multipage/#img-error
|
||||||
|
// If the image argument is an HTMLImageElement object that is in the broken state,
|
||||||
|
// then throw an InvalidStateError exception
|
||||||
|
let (image_data, image_size) = match self.fetch_image_data(url) {
|
||||||
|
Some((mut data, size)) => {
|
||||||
|
// Pixels come from cache in BGRA order and drawImage expects RGBA so we
|
||||||
|
// have to swap the color values
|
||||||
|
byte_swap_and_premultiply(&mut data);
|
||||||
|
let size = Size2D::new(size.width as f64, size.height as f64);
|
||||||
|
(data, size)
|
||||||
|
},
|
||||||
|
None => return Err(Error::InvalidState),
|
||||||
|
};
|
||||||
|
let dw = dw.unwrap_or(image_size.width);
|
||||||
|
let dh = dh.unwrap_or(image_size.height);
|
||||||
|
let sw = sw.unwrap_or(image_size.width);
|
||||||
|
let sh = sh.unwrap_or(image_size.height);
|
||||||
|
self.draw_image_data(image_data,
|
||||||
|
image_size,
|
||||||
|
sx, sy, sw, sh,
|
||||||
|
dx, dy, dw, dh)
|
||||||
|
}
|
||||||
|
|
||||||
fn draw_image_data(&self,
|
fn draw_image_data(&self,
|
||||||
image_data: Vec<u8>,
|
image_data: Vec<u8>,
|
||||||
image_size: Size2D<f64>,
|
image_size: Size2D<f64>,
|
||||||
|
@ -425,12 +471,7 @@ impl CanvasRenderingContext2D {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_image_data(&self, image_element: &HTMLImageElement) -> Option<(Vec<u8>, Size2D<i32>)> {
|
fn fetch_image_data(&self, url: ServoUrl) -> Option<(Vec<u8>, Size2D<i32>)> {
|
||||||
let url = match image_element.get_url() {
|
|
||||||
Some(url) => url,
|
|
||||||
None => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let img = match self.request_image_from_cache(url) {
|
let img = match self.request_image_from_cache(url) {
|
||||||
ImageResponse::Loaded(img, _) => img,
|
ImageResponse::Loaded(img, _) => img,
|
||||||
ImageResponse::PlaceholderLoaded(_, _) |
|
ImageResponse::PlaceholderLoaded(_, _) |
|
||||||
|
@ -453,10 +494,26 @@ impl CanvasRenderingContext2D {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn request_image_from_cache(&self, url: ServoUrl) -> ImageResponse {
|
fn request_image_from_cache(&self, url: ServoUrl) -> ImageResponse {
|
||||||
self.canvas.as_ref()
|
let response = self.image_cache
|
||||||
.map(|canvas| window_from_node(&**canvas))
|
.find_image_or_metadata(url.clone(),
|
||||||
.map(|window| canvas_utils::request_image_from_cache(&window, url))
|
UsePlaceholder::No,
|
||||||
.unwrap_or(ImageResponse::None)
|
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
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_missing_image_urls(&self) -> Vec<ServoUrl> {
|
||||||
|
mem::replace(&mut self.missing_image_urls.borrow_mut(), vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_drawable_rect(&self, x: f64, y: f64, w: f64, h: f64) -> Option<Rect<f32>> {
|
fn create_drawable_rect(&self, x: f64, y: f64, w: f64, h: f64) -> Option<Rect<f32>> {
|
||||||
|
@ -749,7 +806,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
|
// https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
|
||||||
fn DrawImage(&self,
|
fn DrawImage(&self,
|
||||||
image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
|
image: CanvasImageSource,
|
||||||
dx: f64,
|
dx: f64,
|
||||||
dy: f64)
|
dy: f64)
|
||||||
-> ErrorResult {
|
-> ErrorResult {
|
||||||
|
@ -762,7 +819,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
|
// https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
|
||||||
fn DrawImage_(&self,
|
fn DrawImage_(&self,
|
||||||
image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
|
image: CanvasImageSource,
|
||||||
dx: f64,
|
dx: f64,
|
||||||
dy: f64,
|
dy: f64,
|
||||||
dw: f64,
|
dw: f64,
|
||||||
|
@ -777,7 +834,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
|
// https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
|
||||||
fn DrawImage__(&self,
|
fn DrawImage__(&self,
|
||||||
image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
|
image: CanvasImageSource,
|
||||||
sx: f64,
|
sx: f64,
|
||||||
sy: f64,
|
sy: f64,
|
||||||
sw: f64,
|
sw: f64,
|
||||||
|
@ -1152,27 +1209,34 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-context-2d-createpattern
|
// https://html.spec.whatwg.org/multipage/#dom-context-2d-createpattern
|
||||||
fn CreatePattern(&self,
|
fn CreatePattern(&self,
|
||||||
image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
|
image: CanvasImageSource,
|
||||||
mut repetition: DOMString)
|
mut repetition: DOMString)
|
||||||
-> Fallible<Root<CanvasPattern>> {
|
-> Fallible<Root<CanvasPattern>> {
|
||||||
let (image_data, image_size) = match image {
|
let (image_data, image_size) = match image {
|
||||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLImageElement(ref image) => {
|
CanvasImageSource::HTMLImageElement(ref image) => {
|
||||||
// https://html.spec.whatwg.org/multipage/#img-error
|
// https://html.spec.whatwg.org/multipage/#img-error
|
||||||
// If the image argument is an HTMLImageElement object that is in the broken state,
|
// If the image argument is an HTMLImageElement object that is in the broken state,
|
||||||
// then throw an InvalidStateError exception
|
// then throw an InvalidStateError exception
|
||||||
self.fetch_image_data(image).ok_or(Error::InvalidState)?
|
image.get_url()
|
||||||
|
.and_then(|url| self.fetch_image_data(url))
|
||||||
|
.ok_or(Error::InvalidState)?
|
||||||
},
|
},
|
||||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLCanvasElement(ref canvas) => {
|
CanvasImageSource::HTMLCanvasElement(ref canvas) => {
|
||||||
let _ = canvas.get_or_init_2d_context();
|
let _ = canvas.get_or_init_2d_context();
|
||||||
|
|
||||||
canvas.fetch_all_data().ok_or(Error::InvalidState)?
|
canvas.fetch_all_data().ok_or(Error::InvalidState)?
|
||||||
},
|
},
|
||||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::CanvasRenderingContext2D(ref context) => {
|
CanvasImageSource::CanvasRenderingContext2D(ref context) => {
|
||||||
let canvas = context.Canvas();
|
let canvas = context.Canvas();
|
||||||
let _ = canvas.get_or_init_2d_context();
|
let _ = canvas.get_or_init_2d_context();
|
||||||
|
|
||||||
canvas.fetch_all_data().ok_or(Error::InvalidState)?
|
canvas.fetch_all_data().ok_or(Error::InvalidState)?
|
||||||
}
|
}
|
||||||
|
CanvasImageSource::CSSStyleValue(ref value) => {
|
||||||
|
value.get_url(self.base_url.clone())
|
||||||
|
.and_then(|url| self.fetch_image_data(url))
|
||||||
|
.ok_or(Error::InvalidState)?
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if repetition.is_empty() {
|
if repetition.is_empty() {
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use cssparser::Parser;
|
||||||
|
use cssparser::ParserInput;
|
||||||
use dom::bindings::codegen::Bindings::CSSStyleValueBinding::CSSStyleValueMethods;
|
use dom::bindings::codegen::Bindings::CSSStyleValueBinding::CSSStyleValueMethods;
|
||||||
use dom::bindings::codegen::Bindings::CSSStyleValueBinding::Wrap;
|
use dom::bindings::codegen::Bindings::CSSStyleValueBinding::Wrap;
|
||||||
use dom::bindings::js::Root;
|
use dom::bindings::js::Root;
|
||||||
|
@ -10,6 +12,7 @@ use dom::bindings::reflector::reflect_dom_object;
|
||||||
use dom::bindings::str::DOMString;
|
use dom::bindings::str::DOMString;
|
||||||
use dom::globalscope::GlobalScope;
|
use dom::globalscope::GlobalScope;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
|
use servo_url::ServoUrl;
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub struct CSSStyleValue {
|
pub struct CSSStyleValue {
|
||||||
|
@ -44,3 +47,16 @@ impl CSSStyleValueMethods for CSSStyleValue {
|
||||||
self.Stringifier()
|
self.Stringifier()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CSSStyleValue {
|
||||||
|
/// Parse the value as a `url()`.
|
||||||
|
/// TODO: This should really always be an absolute URL, but we currently
|
||||||
|
/// return relative URLs for computed values, so we pass in a base.
|
||||||
|
/// https://github.com/servo/servo/issues/17625
|
||||||
|
pub fn get_url(&self, base_url: ServoUrl) -> Option<ServoUrl> {
|
||||||
|
let mut input = ParserInput::new(&*self.value);
|
||||||
|
let mut parser = Parser::new(&mut input);
|
||||||
|
parser.expect_url().ok()
|
||||||
|
.and_then(|string| base_url.join(&*string).ok())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLin
|
||||||
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods;
|
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods;
|
||||||
use dom::bindings::codegen::Bindings::PaintRenderingContext2DBinding;
|
use dom::bindings::codegen::Bindings::PaintRenderingContext2DBinding;
|
||||||
use dom::bindings::codegen::Bindings::PaintRenderingContext2DBinding::PaintRenderingContext2DMethods;
|
use dom::bindings::codegen::Bindings::PaintRenderingContext2DBinding::PaintRenderingContext2DMethods;
|
||||||
use dom::bindings::codegen::UnionTypes::HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D;
|
use dom::bindings::codegen::UnionTypes::HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2DOrCSSStyleValue;
|
||||||
use dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern;
|
use dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern;
|
||||||
use dom::bindings::error::ErrorResult;
|
use dom::bindings::error::ErrorResult;
|
||||||
use dom::bindings::error::Fallible;
|
use dom::bindings::error::Fallible;
|
||||||
|
@ -24,11 +24,13 @@ use dom::canvasgradient::CanvasGradient;
|
||||||
use dom::canvaspattern::CanvasPattern;
|
use dom::canvaspattern::CanvasPattern;
|
||||||
use dom::canvasrenderingcontext2d::CanvasRenderingContext2D;
|
use dom::canvasrenderingcontext2d::CanvasRenderingContext2D;
|
||||||
use dom::paintworkletglobalscope::PaintWorkletGlobalScope;
|
use dom::paintworkletglobalscope::PaintWorkletGlobalScope;
|
||||||
|
use dom::workletglobalscope::WorkletGlobalScope;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use euclid::ScaleFactor;
|
use euclid::ScaleFactor;
|
||||||
use euclid::Size2D;
|
use euclid::Size2D;
|
||||||
use euclid::TypedSize2D;
|
use euclid::TypedSize2D;
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::IpcSender;
|
||||||
|
use servo_url::ServoUrl;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use style_traits::CSSPixel;
|
use style_traits::CSSPixel;
|
||||||
use style_traits::DevicePixel;
|
use style_traits::DevicePixel;
|
||||||
|
@ -42,8 +44,10 @@ pub struct PaintRenderingContext2D {
|
||||||
impl PaintRenderingContext2D {
|
impl PaintRenderingContext2D {
|
||||||
fn new_inherited(global: &PaintWorkletGlobalScope) -> PaintRenderingContext2D {
|
fn new_inherited(global: &PaintWorkletGlobalScope) -> PaintRenderingContext2D {
|
||||||
let size = Size2D::zero();
|
let size = Size2D::zero();
|
||||||
|
let image_cache = global.image_cache();
|
||||||
|
let base_url = global.upcast::<WorkletGlobalScope>().base_url();
|
||||||
PaintRenderingContext2D {
|
PaintRenderingContext2D {
|
||||||
context: CanvasRenderingContext2D::new_inherited(global.upcast(), None, size),
|
context: CanvasRenderingContext2D::new_inherited(global.upcast(), None, image_cache, base_url, size),
|
||||||
device_pixel_ratio: Cell::new(ScaleFactor::new(1.0)),
|
device_pixel_ratio: Cell::new(ScaleFactor::new(1.0)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,6 +63,10 @@ impl PaintRenderingContext2D {
|
||||||
let _ = self.context.ipc_renderer().send(msg);
|
let _ = self.context.ipc_renderer().send(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn take_missing_image_urls(&self) -> Vec<ServoUrl> {
|
||||||
|
self.context.take_missing_image_urls()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_bitmap_dimensions(&self,
|
pub fn set_bitmap_dimensions(&self,
|
||||||
size: TypedSize2D<f32, CSSPixel>,
|
size: TypedSize2D<f32, CSSPixel>,
|
||||||
device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>)
|
device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>)
|
||||||
|
@ -187,7 +195,7 @@ impl PaintRenderingContext2DMethods for PaintRenderingContext2D {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
|
// https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
|
||||||
fn DrawImage(&self,
|
fn DrawImage(&self,
|
||||||
image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
|
image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2DOrCSSStyleValue,
|
||||||
dx: f64,
|
dx: f64,
|
||||||
dy: f64)
|
dy: f64)
|
||||||
-> ErrorResult {
|
-> ErrorResult {
|
||||||
|
@ -196,7 +204,7 @@ impl PaintRenderingContext2DMethods for PaintRenderingContext2D {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
|
// https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
|
||||||
fn DrawImage_(&self,
|
fn DrawImage_(&self,
|
||||||
image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
|
image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2DOrCSSStyleValue,
|
||||||
dx: f64,
|
dx: f64,
|
||||||
dy: f64,
|
dy: f64,
|
||||||
dw: f64,
|
dw: f64,
|
||||||
|
@ -207,7 +215,7 @@ impl PaintRenderingContext2DMethods for PaintRenderingContext2D {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
|
// https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
|
||||||
fn DrawImage__(&self,
|
fn DrawImage__(&self,
|
||||||
image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
|
image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2DOrCSSStyleValue,
|
||||||
sx: f64,
|
sx: f64,
|
||||||
sy: f64,
|
sy: f64,
|
||||||
sw: f64,
|
sw: f64,
|
||||||
|
@ -309,7 +317,7 @@ impl PaintRenderingContext2DMethods for PaintRenderingContext2D {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-context-2d-createpattern
|
// https://html.spec.whatwg.org/multipage/#dom-context-2d-createpattern
|
||||||
fn CreatePattern(&self,
|
fn CreatePattern(&self,
|
||||||
image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
|
image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2DOrCSSStyleValue,
|
||||||
repetition: DOMString)
|
repetition: DOMString)
|
||||||
-> Fallible<Root<CanvasPattern>> {
|
-> Fallible<Root<CanvasPattern>> {
|
||||||
self.context.CreatePattern(image, repetition)
|
self.context.CreatePattern(image, repetition)
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use canvas_traits::CanvasData;
|
use canvas_traits::CanvasData;
|
||||||
use canvas_traits::CanvasImageData;
|
|
||||||
use dom::bindings::callback::CallbackContainer;
|
use dom::bindings::callback::CallbackContainer;
|
||||||
use dom::bindings::cell::DOMRefCell;
|
use dom::bindings::cell::DOMRefCell;
|
||||||
use dom::bindings::codegen::Bindings::PaintWorkletGlobalScopeBinding;
|
use dom::bindings::codegen::Bindings::PaintWorkletGlobalScopeBinding;
|
||||||
|
@ -28,8 +27,7 @@ use dom::workletglobalscope::WorkletTask;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use euclid::ScaleFactor;
|
use euclid::ScaleFactor;
|
||||||
use euclid::TypedSize2D;
|
use euclid::TypedSize2D;
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc;
|
||||||
use ipc_channel::ipc::IpcSharedMemory;
|
|
||||||
use js::jsapi::Call;
|
use js::jsapi::Call;
|
||||||
use js::jsapi::Construct1;
|
use js::jsapi::Construct1;
|
||||||
use js::jsapi::HandleValue;
|
use js::jsapi::HandleValue;
|
||||||
|
@ -45,10 +43,10 @@ use js::jsval::ObjectValue;
|
||||||
use js::jsval::UndefinedValue;
|
use js::jsval::UndefinedValue;
|
||||||
use js::rust::Runtime;
|
use js::rust::Runtime;
|
||||||
use msg::constellation_msg::PipelineId;
|
use msg::constellation_msg::PipelineId;
|
||||||
use net_traits::image::base::Image;
|
|
||||||
use net_traits::image::base::PixelFormat;
|
use net_traits::image::base::PixelFormat;
|
||||||
use net_traits::image_cache::ImageCache;
|
use net_traits::image_cache::ImageCache;
|
||||||
use script_layout_interface::message::Msg;
|
use script_layout_interface::message::Msg;
|
||||||
|
use script_traits::DrawAPaintImageResult;
|
||||||
use script_traits::Painter;
|
use script_traits::Painter;
|
||||||
use servo_atoms::Atom;
|
use servo_atoms::Atom;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
|
@ -59,6 +57,8 @@ use std::ptr::null_mut;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use std::sync::mpsc;
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
use style_traits::CSSPixel;
|
use style_traits::CSSPixel;
|
||||||
use style_traits::DevicePixel;
|
use style_traits::DevicePixel;
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ use style_traits::DevicePixel;
|
||||||
pub struct PaintWorkletGlobalScope {
|
pub struct PaintWorkletGlobalScope {
|
||||||
/// The worklet global for this object
|
/// The worklet global for this object
|
||||||
worklet_global: WorkletGlobalScope,
|
worklet_global: WorkletGlobalScope,
|
||||||
/// The image cache (used for generating invalid images).
|
/// The image cache
|
||||||
#[ignore_heap_size_of = "Arc"]
|
#[ignore_heap_size_of = "Arc"]
|
||||||
image_cache: Arc<ImageCache>,
|
image_cache: Arc<ImageCache>,
|
||||||
/// https://drafts.css-houdini.org/css-paint-api/#paint-definitions
|
/// https://drafts.css-houdini.org/css-paint-api/#paint-definitions
|
||||||
|
@ -94,11 +94,16 @@ impl PaintWorkletGlobalScope {
|
||||||
unsafe { PaintWorkletGlobalScopeBinding::Wrap(runtime.cx(), global) }
|
unsafe { PaintWorkletGlobalScopeBinding::Wrap(runtime.cx(), global) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn image_cache(&self) -> Arc<ImageCache> {
|
||||||
|
self.image_cache.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn perform_a_worklet_task(&self, task: PaintWorkletTask) {
|
pub fn perform_a_worklet_task(&self, task: PaintWorkletTask) {
|
||||||
match task {
|
match task {
|
||||||
PaintWorkletTask::DrawAPaintImage(name, size, device_pixel_ratio, properties, sender) => {
|
PaintWorkletTask::DrawAPaintImage(name, size_in_px, device_pixel_ratio, properties, sender) => {
|
||||||
let properties = StylePropertyMapReadOnly::from_iter(self.upcast(), properties);
|
let properties = StylePropertyMapReadOnly::from_iter(self.upcast(), properties);
|
||||||
self.draw_a_paint_image(name, size, device_pixel_ratio, &*properties, sender);
|
let result = self.draw_a_paint_image(name, size_in_px, device_pixel_ratio, &*properties);
|
||||||
|
let _ = sender.send(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,11 +113,11 @@ impl PaintWorkletGlobalScope {
|
||||||
name: Atom,
|
name: Atom,
|
||||||
size_in_px: TypedSize2D<f32, CSSPixel>,
|
size_in_px: TypedSize2D<f32, CSSPixel>,
|
||||||
device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>,
|
device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>,
|
||||||
properties: &StylePropertyMapReadOnly,
|
properties: &StylePropertyMapReadOnly)
|
||||||
sender: IpcSender<CanvasData>)
|
-> DrawAPaintImageResult
|
||||||
{
|
{
|
||||||
// TODO: document paint definitions.
|
// TODO: document paint definitions.
|
||||||
self.invoke_a_paint_callback(name, size_in_px, device_pixel_ratio, properties, sender);
|
self.invoke_a_paint_callback(name, size_in_px, device_pixel_ratio, properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// https://drafts.css-houdini.org/css-paint-api/#invoke-a-paint-callback
|
/// https://drafts.css-houdini.org/css-paint-api/#invoke-a-paint-callback
|
||||||
|
@ -121,8 +126,8 @@ impl PaintWorkletGlobalScope {
|
||||||
name: Atom,
|
name: Atom,
|
||||||
size_in_px: TypedSize2D<f32, CSSPixel>,
|
size_in_px: TypedSize2D<f32, CSSPixel>,
|
||||||
device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>,
|
device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>,
|
||||||
properties: &StylePropertyMapReadOnly,
|
properties: &StylePropertyMapReadOnly)
|
||||||
sender: IpcSender<CanvasData>)
|
-> DrawAPaintImageResult
|
||||||
{
|
{
|
||||||
let size_in_dpx = size_in_px * device_pixel_ratio;
|
let size_in_dpx = size_in_px * device_pixel_ratio;
|
||||||
let size_in_dpx = TypedSize2D::new(size_in_dpx.width.abs() as u32, size_in_dpx.height.abs() as u32);
|
let size_in_dpx = TypedSize2D::new(size_in_dpx.width.abs() as u32, size_in_dpx.height.abs() as u32);
|
||||||
|
@ -140,13 +145,13 @@ impl PaintWorkletGlobalScope {
|
||||||
None => {
|
None => {
|
||||||
// Step 2.2.
|
// Step 2.2.
|
||||||
warn!("Drawing un-registered paint definition {}.", name);
|
warn!("Drawing un-registered paint definition {}.", name);
|
||||||
return self.send_invalid_image(size_in_dpx, sender);
|
return self.invalid_image(size_in_dpx, vec![]);
|
||||||
}
|
}
|
||||||
Some(definition) => {
|
Some(definition) => {
|
||||||
// Step 5.1
|
// Step 5.1
|
||||||
if !definition.constructor_valid_flag.get() {
|
if !definition.constructor_valid_flag.get() {
|
||||||
debug!("Drawing invalid paint definition {}.", name);
|
debug!("Drawing invalid paint definition {}.", name);
|
||||||
return self.send_invalid_image(size_in_dpx, sender);
|
return self.invalid_image(size_in_dpx, vec![]);
|
||||||
}
|
}
|
||||||
class_constructor.set(definition.class_constructor.get());
|
class_constructor.set(definition.class_constructor.get());
|
||||||
paint_function.set(definition.paint_function.get());
|
paint_function.set(definition.paint_function.get());
|
||||||
|
@ -174,7 +179,7 @@ impl PaintWorkletGlobalScope {
|
||||||
self.paint_definitions.borrow_mut().get_mut(&name)
|
self.paint_definitions.borrow_mut().get_mut(&name)
|
||||||
.expect("Vanishing paint definition.")
|
.expect("Vanishing paint definition.")
|
||||||
.constructor_valid_flag.set(false);
|
.constructor_valid_flag.set(false);
|
||||||
return self.send_invalid_image(size_in_dpx, sender);
|
return self.invalid_image(size_in_dpx, vec![]);
|
||||||
}
|
}
|
||||||
// Step 5.4
|
// Step 5.4
|
||||||
entry.insert(Box::new(Heap::default())).set(paint_instance.get());
|
entry.insert(Box::new(Heap::default())).set(paint_instance.get());
|
||||||
|
@ -202,39 +207,42 @@ impl PaintWorkletGlobalScope {
|
||||||
|
|
||||||
rooted!(in(cx) let mut result = UndefinedValue());
|
rooted!(in(cx) let mut result = UndefinedValue());
|
||||||
unsafe { Call(cx, paint_instance.handle(), paint_function.handle(), &args, result.handle_mut()); }
|
unsafe { Call(cx, paint_instance.handle(), paint_function.handle(), &args, result.handle_mut()); }
|
||||||
|
let missing_image_urls = rendering_context.take_missing_image_urls();
|
||||||
|
|
||||||
// Step 13.
|
// Step 13.
|
||||||
if unsafe { JS_IsExceptionPending(cx) } {
|
if unsafe { JS_IsExceptionPending(cx) } {
|
||||||
debug!("Paint function threw an exception {}.", name);
|
debug!("Paint function threw an exception {}.", name);
|
||||||
unsafe { JS_ClearPendingException(cx); }
|
unsafe { JS_ClearPendingException(cx); }
|
||||||
return self.send_invalid_image(size_in_dpx, sender);
|
return self.invalid_image(size_in_dpx, missing_image_urls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let (sender, receiver) = ipc::channel().expect("IPC channel creation.");
|
||||||
rendering_context.send_data(sender);
|
rendering_context.send_data(sender);
|
||||||
|
let image_key = match receiver.recv() {
|
||||||
|
Ok(CanvasData::Image(data)) => Some(data.image_key),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
DrawAPaintImageResult {
|
||||||
|
width: size_in_dpx.width,
|
||||||
|
height: size_in_dpx.height,
|
||||||
|
format: PixelFormat::BGRA8,
|
||||||
|
image_key: image_key,
|
||||||
|
missing_image_urls: missing_image_urls,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_invalid_image(&self,
|
// https://drafts.csswg.org/css-images-4/#invalid-image
|
||||||
size: TypedSize2D<u32, DevicePixel>,
|
fn invalid_image(&self, size: TypedSize2D<u32, DevicePixel>, missing_image_urls: Vec<ServoUrl>)
|
||||||
sender: IpcSender<CanvasData>)
|
-> DrawAPaintImageResult {
|
||||||
{
|
debug!("Returning an invalid image.");
|
||||||
debug!("Sending an invalid image.");
|
DrawAPaintImageResult {
|
||||||
let width = size.width as u32;
|
width: size.width as u32,
|
||||||
let height = size.height as u32;
|
height: size.height as u32,
|
||||||
let len = (width as usize) * (height as usize) * 4;
|
|
||||||
let pixel = [0x00, 0x00, 0x00, 0x00];
|
|
||||||
let bytes: Vec<u8> = pixel.iter().cloned().cycle().take(len).collect();
|
|
||||||
let mut image = Image {
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
format: PixelFormat::BGRA8,
|
format: PixelFormat::BGRA8,
|
||||||
bytes: IpcSharedMemory::from_bytes(&*bytes),
|
image_key: None,
|
||||||
id: None,
|
missing_image_urls: missing_image_urls,
|
||||||
};
|
}
|
||||||
self.image_cache.set_webrender_image_key(&mut image);
|
|
||||||
let image_key = image.id.expect("Image cache should set image key.");
|
|
||||||
let image_data = CanvasImageData { image_key: image_key };
|
|
||||||
let canvas_data = CanvasData::Image(image_data);
|
|
||||||
let _ = sender.send(canvas_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn painter(&self, name: Atom) -> Arc<Painter> {
|
fn painter(&self, name: Atom) -> Arc<Painter> {
|
||||||
|
@ -244,13 +252,15 @@ impl PaintWorkletGlobalScope {
|
||||||
fn draw_a_paint_image(&self,
|
fn draw_a_paint_image(&self,
|
||||||
size: TypedSize2D<f32, CSSPixel>,
|
size: TypedSize2D<f32, CSSPixel>,
|
||||||
device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>,
|
device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>,
|
||||||
properties: Vec<(Atom, String)>,
|
properties: Vec<(Atom, String)>)
|
||||||
sender: IpcSender<CanvasData>)
|
-> DrawAPaintImageResult
|
||||||
{
|
{
|
||||||
let name = self.0.clone();
|
let name = self.0.clone();
|
||||||
|
let (sender, receiver) = mpsc::channel();
|
||||||
let task = PaintWorkletTask::DrawAPaintImage(name, size, device_pixel_ratio, properties, sender);
|
let task = PaintWorkletTask::DrawAPaintImage(name, size, device_pixel_ratio, properties, sender);
|
||||||
self.1.lock().expect("Locking a painter.")
|
self.1.lock().expect("Locking a painter.")
|
||||||
.schedule_a_worklet_task(WorkletTask::Paint(task));
|
.schedule_a_worklet_task(WorkletTask::Paint(task));
|
||||||
|
receiver.recv().expect("Worklet thread died?")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Arc::new(WorkletPainter(name, Mutex::new(self.worklet_global.executor())))
|
Arc::new(WorkletPainter(name, Mutex::new(self.worklet_global.executor())))
|
||||||
|
@ -346,7 +356,7 @@ pub enum PaintWorkletTask {
|
||||||
TypedSize2D<f32, CSSPixel>,
|
TypedSize2D<f32, CSSPixel>,
|
||||||
ScaleFactor<f32, CSSPixel, DevicePixel>,
|
ScaleFactor<f32, CSSPixel, DevicePixel>,
|
||||||
Vec<(Atom, String)>,
|
Vec<(Atom, String)>,
|
||||||
IpcSender<CanvasData>)
|
Sender<DrawAPaintImageResult>)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A paint definition
|
/// A paint definition
|
||||||
|
|
|
@ -8,8 +8,11 @@ enum CanvasFillRule { "nonzero", "evenodd" };
|
||||||
typedef (HTMLImageElement or
|
typedef (HTMLImageElement or
|
||||||
/* HTMLVideoElement or */
|
/* HTMLVideoElement or */
|
||||||
HTMLCanvasElement or
|
HTMLCanvasElement or
|
||||||
CanvasRenderingContext2D /* or
|
CanvasRenderingContext2D or
|
||||||
ImageBitmap */) CanvasImageSource;
|
/* ImageBitmap or */
|
||||||
|
// This should probably be a CSSImageValue
|
||||||
|
// https://github.com/w3c/css-houdini-drafts/issues/416
|
||||||
|
CSSStyleValue) CanvasImageSource;
|
||||||
|
|
||||||
//[Constructor(optional unsigned long width, unsigned long height)]
|
//[Constructor(optional unsigned long width, unsigned long height)]
|
||||||
interface CanvasRenderingContext2D {
|
interface CanvasRenderingContext2D {
|
||||||
|
|
|
@ -39,7 +39,6 @@ mod script_msg;
|
||||||
pub mod webdriver_msg;
|
pub mod webdriver_msg;
|
||||||
|
|
||||||
use bluetooth_traits::BluetoothRequest;
|
use bluetooth_traits::BluetoothRequest;
|
||||||
use canvas_traits::CanvasData;
|
|
||||||
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId};
|
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId};
|
||||||
use euclid::{Size2D, Length, Point2D, Vector2D, Rect, ScaleFactor, TypedSize2D};
|
use euclid::{Size2D, Length, Point2D, Vector2D, Rect, ScaleFactor, TypedSize2D};
|
||||||
use gfx_traits::Epoch;
|
use gfx_traits::Epoch;
|
||||||
|
@ -52,6 +51,7 @@ use msg::constellation_msg::{BrowsingContextId, TopLevelBrowsingContextId, Frame
|
||||||
use msg::constellation_msg::{PipelineId, PipelineNamespaceId, TraversalDirection};
|
use msg::constellation_msg::{PipelineId, PipelineNamespaceId, TraversalDirection};
|
||||||
use net_traits::{FetchResponseMsg, ReferrerPolicy, ResourceThreads};
|
use net_traits::{FetchResponseMsg, ReferrerPolicy, ResourceThreads};
|
||||||
use net_traits::image::base::Image;
|
use net_traits::image::base::Image;
|
||||||
|
use net_traits::image::base::PixelFormat;
|
||||||
use net_traits::image_cache::ImageCache;
|
use net_traits::image_cache::ImageCache;
|
||||||
use net_traits::response::HttpsState;
|
use net_traits::response::HttpsState;
|
||||||
use net_traits::storage_thread::StorageType;
|
use net_traits::storage_thread::StorageType;
|
||||||
|
@ -69,6 +69,7 @@ use style_traits::CSSPixel;
|
||||||
use style_traits::DevicePixel;
|
use style_traits::DevicePixel;
|
||||||
use webdriver_msg::{LoadStatus, WebDriverScriptCommand};
|
use webdriver_msg::{LoadStatus, WebDriverScriptCommand};
|
||||||
use webrender_api::ClipId;
|
use webrender_api::ClipId;
|
||||||
|
use webrender_api::ImageKey;
|
||||||
use webvr_traits::{WebVREvent, WebVRMsg};
|
use webvr_traits::{WebVREvent, WebVRMsg};
|
||||||
|
|
||||||
pub use script_msg::{LayoutMsg, ScriptMsg, EventResult, LogEntry};
|
pub use script_msg::{LayoutMsg, ScriptMsg, EventResult, LogEntry};
|
||||||
|
@ -823,7 +824,22 @@ pub trait Painter: Sync + Send {
|
||||||
fn draw_a_paint_image(&self,
|
fn draw_a_paint_image(&self,
|
||||||
size: TypedSize2D<f32, CSSPixel>,
|
size: TypedSize2D<f32, CSSPixel>,
|
||||||
zoom: ScaleFactor<f32, CSSPixel, DevicePixel>,
|
zoom: ScaleFactor<f32, CSSPixel, DevicePixel>,
|
||||||
properties: Vec<(Atom, String)>,
|
properties: Vec<(Atom, String)>)
|
||||||
sender: IpcSender<CanvasData>);
|
-> DrawAPaintImageResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The result of executing paint code: the image together with any image URLs that need to be loaded.
|
||||||
|
/// TODO: this should return a WR display list. https://github.com/servo/servo/issues/17497
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub struct DrawAPaintImageResult {
|
||||||
|
/// The image height
|
||||||
|
pub width: u32,
|
||||||
|
/// The image width
|
||||||
|
pub height: u32,
|
||||||
|
/// The image format
|
||||||
|
pub format: PixelFormat,
|
||||||
|
/// The image drawn, or None if an invalid paint image was drawn
|
||||||
|
pub image_key: Option<ImageKey>,
|
||||||
|
/// Drawing the image might have requested loading some image URLs.
|
||||||
|
pub missing_image_urls: Vec<ServoUrl>,
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[paint2d-image.html]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
||||||
bug: https://github.com/servo/servo/issues/17432
|
|
Loading…
Add table
Add a link
Reference in a new issue