Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support OffscreenCanvas as CanvasImageSource #25087

Merged
merged 1 commit into from Dec 5, 2019
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
The table of contents is too big for display.

Always

Just for now

Addresses Issue: Support OffscreenCanvas as CanvasImageSource #24269

Added methods to canvas_data to support drawing an offscreen canvas onto another canvas
Bug fix: Swapped OffscreenCanvas width and height parameters to match Mozilla spec
Tests: Updated metadata for 866 tests
  • Loading branch information
bblanke committed Dec 4, 2019
commit deae103042a14db2f62aff418fe08221a6a093ec
@@ -22,6 +22,7 @@ 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::offscreencanvas::{OffscreenCanvas, OffscreenCanvasContext};
use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
use crate::dom::textmetrics::TextMetrics;
use crate::euclidext::Size2DExt;
@@ -222,6 +223,7 @@ impl CanvasState {
fn is_origin_clean(&self, image: CanvasImageSource) -> bool {
match image {
CanvasImageSource::HTMLCanvasElement(canvas) => canvas.origin_is_clean(),
CanvasImageSource::OffscreenCanvas(canvas) => canvas.origin_is_clean(),
CanvasImageSource::HTMLImageElement(image) => {
image.same_origin(GlobalScope::entry().origin())
},
@@ -372,6 +374,9 @@ impl CanvasState {
CanvasImageSource::HTMLCanvasElement(ref canvas) => {
self.draw_html_canvas_element(&canvas, htmlcanvas, sx, sy, sw, sh, dx, dy, dw, dh)
},
CanvasImageSource::OffscreenCanvas(ref canvas) => {
self.draw_offscreen_canvas(&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,
@@ -408,9 +413,9 @@ impl CanvasState {
result
}

fn draw_html_canvas_element(
fn draw_offscreen_canvas(
&self,
canvas: &HTMLCanvasElement,
canvas: &OffscreenCanvas,
htmlcanvas: Option<&HTMLCanvasElement>,
sx: f64,
sy: f64,
@@ -420,6 +425,62 @@ impl CanvasState {
dy: f64,
dw: Option<f64>,
dh: Option<f64>,
) -> ErrorResult {
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 {
OffscreenCanvasContext::OffscreenContext2d(ref context) => {
context.send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther(
self.get_canvas_id(),
image_size,
dest_rect,
source_rect,
smoothing_enabled,
));
},
}
} else {
self.send_canvas_2d_msg(Canvas2dMsg::DrawImage(
None,
image_size,
dest_rect,
source_rect,
smoothing_enabled,
));
}

self.mark_as_dirty(htmlcanvas);
Ok(())
}

fn draw_html_canvas_element(
&self,
canvas: &HTMLCanvasElement, // source canvas
htmlcanvas: Option<&HTMLCanvasElement>, // destination canvas
sx: f64,
sy: f64,
sw: Option<f64>,
sh: Option<f64>,
dx: f64,
dy: f64,
dw: Option<f64>,
dh: Option<f64>,
) -> ErrorResult {
// 1. Check the usability of the image argument
if !canvas.is_valid() {
@@ -842,6 +903,13 @@ impl CanvasState {
.unwrap_or_else(|| vec![0; size.area() as usize * 4]);
(data, size)
},
CanvasImageSource::OffscreenCanvas(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))
@@ -16,9 +16,12 @@ use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlcanvaselement::HTMLCanvasElement;
use crate::dom::offscreencanvasrenderingcontext2d::OffscreenCanvasRenderingContext2D;
use crate::script_runtime::JSContext;
use canvas_traits::canvas::{CanvasMsg, FromScriptMsg};
use dom_struct::dom_struct;
use euclid::default::Size2D;
use ipc_channel::ipc::IpcSharedMemory;
use js::rust::HandleValue;
use profile_traits::ipc;
use ref_filter_map;
use std::cell::Cell;
use std::cell::Ref;
@@ -34,57 +37,91 @@ pub enum OffscreenCanvasContext {
#[dom_struct]
pub struct OffscreenCanvas {
eventtarget: EventTarget,
height: Cell<u64>,
width: Cell<u64>,
height: Cell<u64>,
context: DomRefCell<Option<OffscreenCanvasContext>>,
placeholder: Option<Dom<HTMLCanvasElement>>,
}

impl OffscreenCanvas {
pub fn new_inherited(
height: u64,
width: u64,
height: u64,
placeholder: Option<&HTMLCanvasElement>,
) -> OffscreenCanvas {
OffscreenCanvas {
eventtarget: EventTarget::new_inherited(),
height: Cell::new(height),
width: Cell::new(width),
height: Cell::new(height),
context: DomRefCell::new(None),
placeholder: placeholder.map(Dom::from_ref),
}
}

pub fn new(
global: &GlobalScope,
height: u64,
width: u64,
height: u64,
placeholder: Option<&HTMLCanvasElement>,
) -> DomRoot<OffscreenCanvas> {
reflect_dom_object(
Box::new(OffscreenCanvas::new_inherited(height, width, placeholder)),
Box::new(OffscreenCanvas::new_inherited(width, height, placeholder)),
global,
OffscreenCanvasWrap,
)
}

pub fn Constructor(
global: &GlobalScope,
height: u64,
width: u64,
height: u64,
) -> Fallible<DomRoot<OffscreenCanvas>> {
let offscreencanvas = OffscreenCanvas::new(global, height, width, None);
let offscreencanvas = OffscreenCanvas::new(global, width, height, None);
Ok(offscreencanvas)
}

pub fn get_size(&self) -> Size2D<u64> {
Size2D::new(self.Width(), self.Height())
}

pub fn origin_is_clean(&self) -> bool {
match *self.context.borrow() {
Some(OffscreenCanvasContext::OffscreenContext2d(ref context)) => {
context.origin_is_clean()
},
_ => true,
}
}

pub fn context(&self) -> Option<Ref<OffscreenCanvasContext>> {
ref_filter_map::ref_filter_map(self.context.borrow(), |ctx| ctx.as_ref())
}

pub fn fetch_all_data(&self) -> Option<(Option<IpcSharedMemory>, Size2D<u32>)> {
let size = self.get_size();

if size.width == 0 || size.height == 0 {
return None;
}

let data = match self.context.borrow().as_ref() {
Some(&OffscreenCanvasContext::OffscreenContext2d(ref context)) => {
let (sender, receiver) =
ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
let msg = CanvasMsg::FromScript(
FromScriptMsg::SendPixels(sender),
context.get_canvas_id(),
);
context.get_ipc_renderer().send(msg).unwrap();

Some(receiver.recv().unwrap())
},
None => None,
};

Some((data, size.to_u32()))
}

#[allow(unsafe_code)]
fn get_or_init_2d_context(&self) -> Option<DomRoot<OffscreenCanvasRenderingContext2D>> {
if let Some(ctx) = self.context() {
@@ -24,8 +24,10 @@ use crate::dom::htmlcanvaselement::HTMLCanvasElement;
use crate::dom::imagedata::ImageData;
use crate::dom::offscreencanvas::OffscreenCanvas;
use crate::dom::textmetrics::TextMetrics;
use canvas_traits::canvas::{Canvas2dMsg, CanvasId, CanvasMsg};
use dom_struct::dom_struct;
use euclid::default::Size2D;
use ipc_channel::ipc::IpcSender;

#[dom_struct]
pub struct OffscreenCanvasRenderingContext2D {
@@ -72,6 +74,22 @@ impl OffscreenCanvasRenderingContext2D {
pub fn set_canvas_bitmap_dimensions(&self, size: Size2D<u64>) {
self.canvas_state.borrow().set_bitmap_dimensions(size);
}

pub fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) {
self.canvas_state.borrow().send_canvas_2d_msg(msg)
}

pub fn origin_is_clean(&self) -> bool {
self.canvas_state.borrow().origin_is_clean()
}

pub fn get_canvas_id(&self) -> CanvasId {
self.canvas_state.borrow().get_canvas_id()
}

pub fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg> {
self.canvas_state.borrow().get_ipc_renderer().clone()
}
}

impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContext2D {
@@ -12,7 +12,7 @@ typedef (HTMLOrSVGImageElement or
/*HTMLVideoElement or*/
HTMLCanvasElement or
/*ImageBitmap or*/
/*OffscreenCanvas or*/
OffscreenCanvas or
/*CSSImageValue*/ CSSStyleValue) CanvasImageSource;

enum CanvasFillRule { "nonzero", "evenodd" };

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.