Skip to content

Commit

Permalink
Implemented paint worklet arguments.
Browse files Browse the repository at this point in the history
  • Loading branch information
Alan Jeffrey committed Jun 28, 2017
1 parent 41ba333 commit 8094f68
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 12 deletions.
9 changes: 9 additions & 0 deletions components/canvas/canvas_paint_thread.rs
Expand Up @@ -543,9 +543,15 @@ impl<'a> CanvasPaintThread<'a> {
}

fn recreate(&mut self, size: Size2D<i32>) {
// TODO: clear the thread state. https://github.com/servo/servo/issues/17533
self.drawtarget = CanvasPaintThread::create(size);
// Webrender doesn't let images change size, so we clear the webrender image key.
// TODO: there is an annying race condition here: the display list builder
// might still be using the old image key. Really, we should be scheduling the image
// for later deletion, not deleting it immediately.
// https://github.com/servo/servo/issues/17534
if let Some(image_key) = self.image_key.take() {
debug!("No longer using image {:?}.", image_key);
self.webrender_api.delete_image(image_key);
}
}
Expand All @@ -572,13 +578,15 @@ impl<'a> CanvasPaintThread<'a> {

match self.image_key {
Some(image_key) => {
debug!("Updating image {:?}.", image_key);
self.webrender_api.update_image(image_key,
descriptor,
data,
None);
}
None => {
self.image_key = Some(self.webrender_api.generate_image_key());
debug!("New image {:?}.", self.image_key);
self.webrender_api.add_image(self.image_key.unwrap(),
descriptor,
data,
Expand Down Expand Up @@ -744,6 +752,7 @@ impl<'a> CanvasPaintThread<'a> {
impl<'a> Drop for CanvasPaintThread<'a> {
fn drop(&mut self) {
if let Some(image_key) = self.image_key {
debug!("Deleting image key {:?}.", image_key);
self.webrender_api.delete_image(image_key);
}
}
Expand Down
10 changes: 9 additions & 1 deletion components/layout/display_list_builder.rs
Expand Up @@ -68,6 +68,7 @@ use style::values::generics::image::PaintWorklet;
use style::values::specified::length::Percentage;
use style::values::specified::position::{X, Y};
use style_traits::CSSPixel;
use style_traits::ToCss;
use style_traits::cursor::Cursor;
use table_cell::CollapsedBordersForCell;
use webrender_helpers::{ToMixBlendMode, ToTransformStyle};
Expand Down Expand Up @@ -1162,9 +1163,15 @@ impl FragmentDisplayListBuilding for Fragment {
// https://github.com/w3c/css-houdini-drafts/issues/417
let unbordered_box = self.border_box - style.logical_border_width();
let size = unbordered_box.size.to_physical(style.writing_mode);

// TODO: less copying.
let name = paint_worklet.name.clone();
let arguments = paint_worklet.arguments.iter()
.map(|argument| argument.to_css_string())
.collect();

// Get the painter, and the computed values for its properties.
// TODO: less copying.
let (properties, painter) = match state.layout_context.registered_painters.read().get(&name) {
Some(registered_painter) => (
registered_painter.properties
Expand All @@ -1181,11 +1188,12 @@ impl FragmentDisplayListBuilding for Fragment {
// https://github.com/servo/servo/issues/17369
debug!("Drawing a paint image {}({},{}).", name, size.width.to_px(), size.height.to_px());
let (sender, receiver) = ipc::channel().unwrap();
painter.draw_a_paint_image(size, properties, sender);
painter.draw_a_paint_image(size, properties, arguments, sender);

// TODO: timeout
let webrender_image = match receiver.recv() {
Ok(CanvasData::Image(canvas_data)) => {
debug!("Got image {:?}.", canvas_data.image_key);
WebRenderImageInfo {
// TODO: it would be nice to get this data back from the canvas
width: size.width.to_px().abs() as u32,
Expand Down
58 changes: 51 additions & 7 deletions components/script/dom/paintworkletglobalscope.rs
Expand Up @@ -19,6 +19,7 @@ use dom::bindings::js::JS;
use dom::bindings::js::Root;
use dom::bindings::reflector::DomObject;
use dom::bindings::str::DOMString;
use dom::cssstylevalue::CSSStyleValue;
use dom::paintrenderingcontext2d::PaintRenderingContext2D;
use dom::paintsize::PaintSize;
use dom::stylepropertymapreadonly::StylePropertyMapReadOnly;
Expand All @@ -40,6 +41,7 @@ use js::jsapi::IsConstructor;
use js::jsapi::JSAutoCompartment;
use js::jsapi::JS_ClearPendingException;
use js::jsapi::JS_IsExceptionPending;
use js::jsapi::JS_NewArrayObject;
use js::jsval::JSVal;
use js::jsval::ObjectValue;
use js::jsval::UndefinedValue;
Expand Down Expand Up @@ -94,9 +96,9 @@ impl PaintWorkletGlobalScope {

pub fn perform_a_worklet_task(&self, task: PaintWorkletTask) {
match task {
PaintWorkletTask::DrawAPaintImage(name, size, properties, sender) => {
PaintWorkletTask::DrawAPaintImage(name, size, properties, arguments, sender) => {
let properties = StylePropertyMapReadOnly::from_iter(self.upcast(), properties);
self.draw_a_paint_image(name, size, &*properties, sender);
self.draw_a_paint_image(name, size, &*properties, arguments, sender);
}
}
}
Expand All @@ -106,10 +108,33 @@ impl PaintWorkletGlobalScope {
name: Atom,
size: Size2D<Au>,
properties: &StylePropertyMapReadOnly,
arguments: Vec<String>,
sender: IpcSender<CanvasData>)
{
// TODO: Steps 1-5.

// TODO: document paint definitions.
self.invoke_a_paint_callback(name, size, properties, sender);
let input_arguments_len = match self.paint_definitions.borrow().get(&name) {
None => {
// Step 6.
warn!("Drawing un-registered paint definition {}.", name);
return self.send_invalid_image(size, sender);
},
Some(definition) => {
// Steps 7-9.
definition.input_arguments_len
},
};

// Steps 10-11.
// TODO: check syntax, not just length
if arguments.len() != input_arguments_len {
debug!("Incorrect paint arguments length.");
return self.send_invalid_image(size, sender);
}

// Steps 12-13.
self.invoke_a_paint_callback(name, size, properties, arguments, sender);
}

/// https://drafts.css-houdini.org/css-paint-api/#invoke-a-paint-callback
Expand All @@ -119,6 +144,7 @@ impl PaintWorkletGlobalScope {
name: Atom,
size: Size2D<Au>,
properties: &StylePropertyMapReadOnly,
mut arguments: Vec<String>,
sender: IpcSender<CanvasData>)
{
let width = size.width.to_px().abs() as u32;
Expand Down Expand Up @@ -189,10 +215,19 @@ impl PaintWorkletGlobalScope {
// TODO: Step 10
// Steps 11-12
debug!("Invoking paint function {}.", name);
rooted_vec!(let arguments_values <- arguments.drain(..)
.map(|argument| CSSStyleValue::new(self.upcast(), argument)));
let arguments_value_vec: Vec<JSVal> = arguments_values.iter()
.map(|argument| ObjectValue(argument.reflector().get_jsobject().get()))
.collect();
let arguments_value_array = unsafe { HandleValueArray::from_rooted_slice(&*arguments_value_vec) };
rooted!(in(cx) let argument_object = unsafe { JS_NewArrayObject(cx, &arguments_value_array) });

let args = unsafe { HandleValueArray::from_rooted_slice(&[
ObjectValue(rendering_context.reflector().get_jsobject().get()),
ObjectValue(paint_size.reflector().get_jsobject().get()),
ObjectValue(properties.reflector().get_jsobject().get()),
ObjectValue(argument_object.get()),
]) };
rooted!(in(cx) let mut result = UndefinedValue());
unsafe { Call(cx, paint_instance.handle(), paint_function.handle(), &args, result.handle_mut()); }
Expand Down Expand Up @@ -235,10 +270,11 @@ impl PaintWorkletGlobalScope {
fn draw_a_paint_image(&self,
size: Size2D<Au>,
properties: Vec<(Atom, String)>,
arguments: Vec<String>,
sender: IpcSender<CanvasData>)
{
let name = self.0.clone();
let task = PaintWorkletTask::DrawAPaintImage(name, size, properties, sender);
let task = PaintWorkletTask::DrawAPaintImage(name, size, properties, arguments, sender);
self.1.lock().expect("Locking a painter.")
.schedule_a_worklet_task(WorkletTask::Paint(task));
}
Expand Down Expand Up @@ -276,7 +312,7 @@ impl PaintWorkletGlobalScopeMethods for PaintWorkletGlobalScope {
let properties = property_names.drain(..).map(Atom::from).collect();

// Step 7-9.
let _argument_names: Vec<String> =
let input_arguments: Vec<String> =
unsafe { get_property(cx, paint_obj.handle(), "inputArguments", ()) }?
.unwrap_or_default();

Expand Down Expand Up @@ -309,7 +345,11 @@ impl PaintWorkletGlobalScopeMethods for PaintWorkletGlobalScope {

// Step 19.
let context = PaintRenderingContext2D::new(self);
let definition = PaintDefinition::new(paint_val.handle(), paint_function.handle(), alpha, &*context);
let definition = PaintDefinition::new(paint_val.handle(),
paint_function.handle(),
alpha,
input_arguments.len(),
&*context);

// Step 20.
debug!("Registering definition {}.", name);
Expand All @@ -329,7 +369,7 @@ impl PaintWorkletGlobalScopeMethods for PaintWorkletGlobalScope {

/// Tasks which can be peformed by a paint worklet
pub enum PaintWorkletTask {
DrawAPaintImage(Atom, Size2D<Au>, Vec<(Atom, String)>, IpcSender<CanvasData>)
DrawAPaintImage(Atom, Size2D<Au>, Vec<(Atom, String)>, Vec<String>, IpcSender<CanvasData>)
}

/// A paint definition
Expand All @@ -343,6 +383,8 @@ struct PaintDefinition {
paint_function: Heap<JSVal>,
constructor_valid_flag: Cell<bool>,
context_alpha_flag: bool,
// TODO: this should be a list of CSS syntaxes.
input_arguments_len: usize,
// TODO: the spec calls for fresh rendering contexts each time a paint image is drawn,
// but to avoid having the primary worklet thread create a new renering context,
// we recycle them.
Expand All @@ -353,6 +395,7 @@ impl PaintDefinition {
fn new(class_constructor: HandleValue,
paint_function: HandleValue,
alpha: bool,
input_arguments_len: usize,
context: &PaintRenderingContext2D)
-> Box<PaintDefinition>
{
Expand All @@ -361,6 +404,7 @@ impl PaintDefinition {
paint_function: Heap::default(),
constructor_valid_flag: Cell::new(true),
context_alpha_flag: alpha,
input_arguments_len: input_arguments_len,
context: JS::from_ref(context),
});
result.class_constructor.set(class_constructor.get());
Expand Down
1 change: 1 addition & 0 deletions components/script_traits/lib.rs
Expand Up @@ -833,6 +833,7 @@ pub trait Painter: Sync + Send {
fn draw_a_paint_image(&self,
concrete_object_size: Size2D<Au>,
properties: Vec<(Atom, String)>,
arguments: Vec<String>,
sender: IpcSender<CanvasData>);
}

12 changes: 11 additions & 1 deletion components/style/values/generics/image.rs
Expand Up @@ -8,6 +8,7 @@

use Atom;
use cssparser::serialize_identifier;
use custom_properties::SpecifiedValue;
use std::fmt;
use style_traits::{HasViewportPercentage, ToCss};
use values::computed::ComputedValueAsSpecified;
Expand Down Expand Up @@ -134,17 +135,26 @@ pub struct ColorStop<Color, LengthOrPercentage> {

/// Specified values for a paint worklet.
/// https://drafts.css-houdini.org/css-paint-api/
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct PaintWorklet {
/// The name the worklet was registered with.
pub name: Atom,
/// The arguments for the worklet.
/// TODO: store a parsed representation of the arguments.
pub arguments: Vec<SpecifiedValue>,
}

impl ComputedValueAsSpecified for PaintWorklet {}

impl ToCss for PaintWorklet {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
dest.write_str("paint(")?;
serialize_identifier(&*self.name.to_string(), dest)?;
for argument in &self.arguments {
dest.write_str(", ")?;
argument.to_css(dest)?;
}
dest.write_str(")")
}
}
Expand Down
12 changes: 9 additions & 3 deletions components/style/values/specified/image.rs
Expand Up @@ -9,6 +9,7 @@

use Atom;
use cssparser::{Parser, Token, BasicParseError};
use custom_properties::SpecifiedValue;
use parser::{Parse, ParserContext};
use selectors::parser::SelectorParseError;
#[cfg(feature = "servo")]
Expand Down Expand Up @@ -695,12 +696,17 @@ impl Parse for ColorStop {
}

impl Parse for PaintWorklet {
fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
input.expect_function_matching("paint")?;
input.parse_nested_block(|i| {
let name = i.expect_ident()?;
input.parse_nested_block(|input| {
let name = input.expect_ident()?;
let arguments = input.try(|input| {
input.expect_comma()?;
input.parse_comma_separated(|input| Ok(*SpecifiedValue::parse(context, input)?))
}).unwrap_or(vec![]);
Ok(PaintWorklet {
name: Atom::from(Cow::from(name)),
arguments: arguments,
})
})
}
Expand Down

0 comments on commit 8094f68

Please sign in to comment.