Skip to content

Commit

Permalink
Implemented paint worklet rendering context.
Browse files Browse the repository at this point in the history
  • Loading branch information
Alan Jeffrey committed Jun 22, 2017
1 parent d89851c commit ab8024d
Show file tree
Hide file tree
Showing 14 changed files with 545 additions and 107 deletions.
25 changes: 17 additions & 8 deletions components/layout/display_list_builder.rs
Expand Up @@ -1173,21 +1173,30 @@ impl FragmentDisplayListBuilding for Fragment {
// TODO: add a one-place cache to avoid drawing the paint image every time.
// https://github.com/servo/servo/issues/17369
debug!("Drawing a paint image {}({},{}).", name, size.width.to_px(), size.height.to_px());
let mut image = match executor.draw_a_paint_image(name, size) {
Ok(image) => image,
Err(err) => return warn!("Error running paint worklet ({:?}).", err),
let (sender, receiver) = ipc::channel().unwrap();
executor.draw_a_paint_image(name, size, sender);

// TODO: timeout
let webrender_image = match receiver.recv() {
Ok(CanvasData::Image(canvas_data)) => {
WebRenderImageInfo {
// TODO: it would be nice to get this data back from the canvas
width: size.width.to_px().abs() as u32,
height: size.height.to_px().abs() 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),
};

// Make sure the image has a webrender key.
state.layout_context.image_cache.set_webrender_image_key(&mut image);

debug!("Drew a paint image ({},{}).", image.width, image.height);
self.build_display_list_for_webrender_image(state,
style,
display_list_section,
absolute_bounds,
clip,
WebRenderImageInfo::from_image(&image),
webrender_image,
index);
}

Expand Down
2 changes: 2 additions & 0 deletions components/script/dom/bindings/refcounted.rs
Expand Up @@ -127,6 +127,7 @@ impl TrustedPromise {
struct RejectPromise(TrustedPromise, Error);
impl Runnable for RejectPromise {
fn main_thread_handler(self: Box<Self>, script_thread: &ScriptThread) {
debug!("Rejecting promise.");
let this = *self;
let cx = script_thread.get_cx();
let promise = this.0.root();
Expand All @@ -145,6 +146,7 @@ impl TrustedPromise {
struct ResolvePromise<T>(TrustedPromise, T);
impl<T: ToJSValConvertible> Runnable for ResolvePromise<T> {
fn main_thread_handler(self: Box<Self>, script_thread: &ScriptThread) {
debug!("Resolving promise.");
let this = *self;
let cx = script_thread.get_cx();
let promise = this.0.root();
Expand Down
57 changes: 40 additions & 17 deletions components/script/dom/canvasrenderingcontext2d.rs
Expand Up @@ -61,7 +61,9 @@ pub struct CanvasRenderingContext2D {
reflector_: Reflector,
#[ignore_heap_size_of = "Defined in ipc-channel"]
ipc_renderer: IpcSender<CanvasMsg>,
canvas: JS<HTMLCanvasElement>,
// For rendering contexts created by an HTML canvas element, this is Some,
// for ones created by a paint worklet, this is None.
canvas: Option<JS<HTMLCanvasElement>>,
state: DOMRefCell<CanvasContextState>,
saved_states: DOMRefCell<Vec<CanvasContextState>>,
origin_clean: Cell<bool>,
Expand Down Expand Up @@ -109,18 +111,21 @@ impl CanvasContextState {
}

impl CanvasRenderingContext2D {
fn new_inherited(global: &GlobalScope,
canvas: &HTMLCanvasElement,
size: Size2D<i32>)
-> CanvasRenderingContext2D {
pub fn new_inherited(global: &GlobalScope,
canvas: Option<&HTMLCanvasElement>,
size: Size2D<i32>)
-> CanvasRenderingContext2D {
debug!("Creating new canvas rendering context.");
let (sender, receiver) = ipc::channel().unwrap();
let constellation_chan = global.constellation_chan();
debug!("Asking constellation to create new canvas thread.");
constellation_chan.send(ConstellationMsg::CreateCanvasPaintThread(size, sender)).unwrap();
let ipc_renderer = receiver.recv().unwrap();
debug!("Done.");
CanvasRenderingContext2D {
reflector_: Reflector::new(),
ipc_renderer: ipc_renderer,
canvas: JS::from_ref(canvas),
canvas: canvas.map(JS::from_ref),
state: DOMRefCell::new(CanvasContextState::new()),
saved_states: DOMRefCell::new(Vec::new()),
origin_clean: Cell::new(true),
Expand All @@ -131,7 +136,7 @@ impl CanvasRenderingContext2D {
canvas: &HTMLCanvasElement,
size: Size2D<i32>)
-> Root<CanvasRenderingContext2D> {
reflect_dom_object(box CanvasRenderingContext2D::new_inherited(global, canvas, size),
reflect_dom_object(box CanvasRenderingContext2D::new_inherited(global, Some(canvas), size),
global,
CanvasRenderingContext2DBinding::Wrap)
}
Expand All @@ -155,7 +160,9 @@ impl CanvasRenderingContext2D {
}

fn mark_as_dirty(&self) {
self.canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
if let Some(ref canvas) = self.canvas {
canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
}
}

fn update_transform(&self) {
Expand Down Expand Up @@ -226,8 +233,12 @@ impl CanvasRenderingContext2D {
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::CanvasRenderingContext2D(image) =>
image.origin_is_clean(),
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLImageElement(image) => {
let canvas = match self.canvas {
Some(ref canvas) => canvas,
None => return false,
};
let image_origin = image.get_origin().expect("Image's origin is missing");
let document = document_from_node(&*self.canvas);
let document = document_from_node(&**canvas);
document.url().clone().origin() == image_origin
}
}
Expand Down Expand Up @@ -347,7 +358,7 @@ impl CanvasRenderingContext2D {

let smoothing_enabled = self.state.borrow().image_smoothing_enabled;

if &*self.canvas == canvas {
if self.canvas.as_ref().map(|c| &**c == canvas).unwrap_or(false) {
let msg = CanvasMsg::Canvas2d(Canvas2dMsg::DrawImageSelf(
image_size, dest_rect, source_rect, smoothing_enabled));
self.ipc_renderer.send(msg).unwrap();
Expand Down Expand Up @@ -442,8 +453,10 @@ impl CanvasRenderingContext2D {

#[inline]
fn request_image_from_cache(&self, url: ServoUrl) -> ImageResponse {
let window = window_from_node(&*self.canvas);
canvas_utils::request_image_from_cache(&window, url)
self.canvas.as_ref()
.map(|canvas| window_from_node(&**canvas))
.map(|window| canvas_utils::request_image_from_cache(&window, url))
.unwrap_or(ImageResponse::None)
}

fn create_drawable_rect(&self, x: f64, y: f64, w: f64, h: f64) -> Option<Rect<f32>> {
Expand Down Expand Up @@ -472,12 +485,20 @@ impl CanvasRenderingContext2D {

// TODO: will need to check that the context bitmap mode is fixed
// once we implement CanvasProxy
let window = window_from_node(&*self.canvas);
let canvas = match self.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 window = window_from_node(canvas);

let style = window.GetComputedStyle(&*self.canvas.upcast(), None);
let style = window.GetComputedStyle(canvas.upcast(), None);

let element_not_rendered =
!self.canvas.upcast::<Node>().is_in_doc() ||
!canvas.upcast::<Node>().is_in_doc() ||
style.GetPropertyValue(DOMString::from("display")) == "none";

if element_not_rendered {
Expand Down Expand Up @@ -530,7 +551,9 @@ impl LayoutCanvasRenderingContext2DHelpers for LayoutJS<CanvasRenderingContext2D
impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
// https://html.spec.whatwg.org/multipage/#dom-context-2d-canvas
fn Canvas(&self) -> Root<HTMLCanvasElement> {
Root::from_ref(&*self.canvas)
// This method is not called from a paint worklet rendering context,
// so it's OK to panic if self.canvas is None.
Root::from_ref(self.canvas.as_ref().expect("No canvas."))
}

// https://html.spec.whatwg.org/multipage/#dom-context-2d-save
Expand Down Expand Up @@ -1037,7 +1060,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
let (sender, receiver) = ipc::channel::<Vec<u8>>().unwrap();
let dest_rect = Rect::new(Point2D::new(sx.to_i32().unwrap(), sy.to_i32().unwrap()),
Size2D::new(sw as i32, sh as i32));
let canvas_size = self.canvas.get_size();
let canvas_size = self.canvas.as_ref().map(|c| c.get_size()).unwrap_or(Size2D::zero());
let canvas_size = Size2D::new(canvas_size.width as f64, canvas_size.height as f64);
self.ipc_renderer
.send(CanvasMsg::Canvas2d(Canvas2dMsg::GetImageData(dest_rect, canvas_size, sender)))
Expand Down

0 comments on commit ab8024d

Please sign in to comment.