mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
canvas: Add vello backend (#36821)
Add vello backend by implementing Backend traits in canvas crate (so this lives in canvas_paint_thread - embedded process). Current implementation uses normal wgpu, so we block on GPU work. Vello backend is gated behind `vello` feature and `dom_canvas_vello_enabled` pref. Feature-wise this backend is on on par with raqote (sometimes better sometimes worse), but performance wise it's worse. ## Known vello problems: - image roundtrip does not work (fixed in https://github.com/linebender/vello/pull/974) - https://github.com/linebender/vello/issues/1066 (fixed) - clip layers are not working properly: https://github.com/linebender/vello/issues/1061 - `/html/canvas/element/pixel-manipulation/2d.imageData.put.*` - `/html/canvas/element/path-objects/2d.path.clip.intersect.html` - https://github.com/linebender/vello/issues/1056 - `/html/canvas/element/fill-and-stroke-styles/2d.gradient.interpolate.coloralpha.html` - `kurbo::Cap::Butt` is defect (only visible with big lineWidth) https://github.com/linebender/vello/issues/1063 - `/html/canvas/element/line-styles/2d.line.cross.html` - `/html/canvas/element/line-styles/2d.line.miter.acute.html` - other lack of strong correct problems (https://github.com/linebender/vello/issues/1063#issuecomment-2998084736): - `/html/canvas/element/path-objects/2d.path.rect.selfintersect.html` - There is currently no way to do put image properly in vello as we would need to ignore all clips and other stuff (we try to work around this on best effort basis) https://github.com/linebender/vello/issues/1088 - `/html/canvas/element/pixel-manipulation/2d.imageData.put.*` - precision problems - `/html/canvas/element/path-objects/2d.path.stroke.scale2.html` - `/html/canvas/element/path-objects/2d.path.arc.scale.1.html` ## Known servo problems - bad performance due to blocking on GPU work - some get/put intensive tests `TIMEOUT` - proper shadow support (non-blocker as we already are living without it now) - support for rect shadow is there but unimplemented currently as that's the state in raqote Testing: `mach try vello` will run normal WPT (with raqote) + vello_canvas subsuite that runs only on `/html/canvas/element`. All subsuite expectations are stored separately. Fixes: #36823 Fixes: #35230 --------- Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
parent
c2ed599eb1
commit
d678901122
49 changed files with 1779 additions and 165 deletions
138
Cargo.lock
generated
138
Cargo.lock
generated
|
@ -1071,16 +1071,20 @@ dependencies = [
|
||||||
"euclid",
|
"euclid",
|
||||||
"font-kit",
|
"font-kit",
|
||||||
"fonts",
|
"fonts",
|
||||||
|
"futures-intrusive",
|
||||||
"ipc-channel",
|
"ipc-channel",
|
||||||
"kurbo",
|
"kurbo",
|
||||||
"log",
|
"log",
|
||||||
"net_traits",
|
"net_traits",
|
||||||
"pixels",
|
"pixels",
|
||||||
|
"pollster",
|
||||||
"range",
|
"range",
|
||||||
"raqote",
|
"raqote",
|
||||||
"servo_arc",
|
"servo_arc",
|
||||||
|
"servo_config",
|
||||||
"stylo",
|
"stylo",
|
||||||
"unicode-script",
|
"unicode-script",
|
||||||
|
"vello",
|
||||||
"webrender_api",
|
"webrender_api",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1324,6 +1328,12 @@ dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "color"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ae467d04a8a8aea5d9a49018a6ade2e4221d92968e8ce55a48c0b1164e5f698"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "color_quant"
|
name = "color_quant"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -2798,6 +2808,17 @@ dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-intrusive"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-io"
|
name = "futures-io"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
|
@ -3480,6 +3501,16 @@ dependencies = [
|
||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "guillotiere"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b62d5865c036cb1393e23c50693df631d3f5d7bcca4c04fe4cc0fd592e74a782"
|
||||||
|
dependencies = [
|
||||||
|
"euclid",
|
||||||
|
"svg_fmt",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.27"
|
version = "0.3.27"
|
||||||
|
@ -6181,6 +6212,17 @@ dependencies = [
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "peniko"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1f9529efd019889b2a205193c14ffb6e2839b54ed9d2720674f10f4b04d87ac9"
|
||||||
|
dependencies = [
|
||||||
|
"color",
|
||||||
|
"kurbo",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
|
@ -6386,6 +6428,12 @@ dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pollster"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polyval"
|
name = "polyval"
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
|
@ -6821,6 +6869,12 @@ version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "renderdoc-sys"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "resvg"
|
name = "resvg"
|
||||||
version = "0.45.1"
|
version = "0.45.1"
|
||||||
|
@ -7805,6 +7859,16 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "skrifa"
|
||||||
|
version = "0.31.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dbeb4ca4399663735553a09dd17ce7e49a0a0203f03b706b39628c4d913a8607"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"read-fonts",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.10"
|
version = "0.4.10"
|
||||||
|
@ -9066,6 +9130,48 @@ version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vello"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "git+https://github.com/linebender/vello?rev=ecf6b282dba01e5dc50e9463b87b6baeccdb3094#ecf6b282dba01e5dc50e9463b87b6baeccdb3094"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"futures-intrusive",
|
||||||
|
"log",
|
||||||
|
"peniko",
|
||||||
|
"png",
|
||||||
|
"skrifa",
|
||||||
|
"static_assertions",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
"vello_encoding",
|
||||||
|
"vello_shaders",
|
||||||
|
"wgpu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vello_encoding"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "git+https://github.com/linebender/vello?rev=ecf6b282dba01e5dc50e9463b87b6baeccdb3094#ecf6b282dba01e5dc50e9463b87b6baeccdb3094"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"guillotiere",
|
||||||
|
"peniko",
|
||||||
|
"skrifa",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vello_shaders"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "git+https://github.com/linebender/vello?rev=ecf6b282dba01e5dc50e9463b87b6baeccdb3094#ecf6b282dba01e5dc50e9463b87b6baeccdb3094"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"log",
|
||||||
|
"naga",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
"vello_encoding",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version-compare"
|
name = "version-compare"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -9593,6 +9699,34 @@ version = "0.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3"
|
checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wgpu"
|
||||||
|
version = "25.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec8fb398f119472be4d80bc3647339f56eb63b2a331f6a3d16e25d8144197dd9"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
"bitflags 2.9.1",
|
||||||
|
"cfg_aliases",
|
||||||
|
"document-features",
|
||||||
|
"hashbrown",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"naga",
|
||||||
|
"parking_lot",
|
||||||
|
"portable-atomic",
|
||||||
|
"profiling",
|
||||||
|
"raw-window-handle",
|
||||||
|
"smallvec",
|
||||||
|
"static_assertions",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"web-sys",
|
||||||
|
"wgpu-core",
|
||||||
|
"wgpu-hal",
|
||||||
|
"wgpu-types",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu-core"
|
name = "wgpu-core"
|
||||||
version = "25.0.1"
|
version = "25.0.1"
|
||||||
|
@ -9611,7 +9745,9 @@ dependencies = [
|
||||||
"naga",
|
"naga",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
|
"portable-atomic",
|
||||||
"profiling",
|
"profiling",
|
||||||
|
"raw-window-handle",
|
||||||
"rustc-hash 1.1.0",
|
"rustc-hash 1.1.0",
|
||||||
"serde",
|
"serde",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
|
@ -9683,9 +9819,11 @@ dependencies = [
|
||||||
"objc",
|
"objc",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
|
"portable-atomic",
|
||||||
"profiling",
|
"profiling",
|
||||||
"range-alloc",
|
"range-alloc",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
|
"renderdoc-sys",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
|
|
@ -166,6 +166,7 @@ unicode-segmentation = "1.12.0"
|
||||||
url = "2.5"
|
url = "2.5"
|
||||||
urlpattern = "0.3"
|
urlpattern = "0.3"
|
||||||
uuid = { version = "1.12.1", features = ["v4"] }
|
uuid = { version = "1.12.1", features = ["v4"] }
|
||||||
|
vello = { git = "https://github.com/linebender/vello", rev = "ecf6b282dba01e5dc50e9463b87b6baeccdb3094" }
|
||||||
webdriver = "0.53.0"
|
webdriver = "0.53.0"
|
||||||
webgpu_traits = { path = "components/shared/webgpu" }
|
webgpu_traits = { path = "components/shared/webgpu" }
|
||||||
webpki-roots = "1.0"
|
webpki-roots = "1.0"
|
||||||
|
|
|
@ -11,6 +11,9 @@ rust-version.workspace = true
|
||||||
name = "canvas"
|
name = "canvas"
|
||||||
path = "lib.rs"
|
path = "lib.rs"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
vello = ["dep:vello", "dep:pollster", "dep:futures-intrusive"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
app_units = { workspace = true }
|
app_units = { workspace = true }
|
||||||
canvas_traits = { workspace = true }
|
canvas_traits = { workspace = true }
|
||||||
|
@ -31,3 +34,7 @@ servo_arc = { workspace = true }
|
||||||
stylo = { workspace = true }
|
stylo = { workspace = true }
|
||||||
unicode-script = { workspace = true }
|
unicode-script = { workspace = true }
|
||||||
webrender_api = { workspace = true }
|
webrender_api = { workspace = true }
|
||||||
|
servo_config = { path = "../config" }
|
||||||
|
vello = { workspace = true, optional = true }
|
||||||
|
pollster = { version = "0.4", optional = true }
|
||||||
|
futures-intrusive = { version = "0.5", optional = true }
|
||||||
|
|
|
@ -110,10 +110,9 @@ impl CanvasPaintThread {
|
||||||
let canvas_id = self.next_canvas_id;
|
let canvas_id = self.next_canvas_id;
|
||||||
self.next_canvas_id.0 += 1;
|
self.next_canvas_id.0 += 1;
|
||||||
|
|
||||||
let canvas_data =
|
let canvas = Canvas::new(size, self.compositor_api.clone(), self.font_context.clone());
|
||||||
CanvasData::new(size, self.compositor_api.clone(), self.font_context.clone());
|
let image_key = canvas.image_key();
|
||||||
let image_key = canvas_data.image_key();
|
self.canvases.insert(canvas_id, canvas);
|
||||||
self.canvases.insert(canvas_id, Canvas::Raqote(canvas_data));
|
|
||||||
|
|
||||||
(canvas_id, image_key)
|
(canvas_id, image_key)
|
||||||
}
|
}
|
||||||
|
@ -283,14 +282,39 @@ impl CanvasPaintThread {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
enum Canvas {
|
enum Canvas {
|
||||||
Raqote(CanvasData<raqote::DrawTarget>),
|
Raqote(CanvasData<raqote::DrawTarget>),
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
Vello(CanvasData<crate::vello_backend::VelloDrawTarget>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Canvas {
|
impl Canvas {
|
||||||
|
fn new(
|
||||||
|
size: Size2D<u64>,
|
||||||
|
compositor_api: CrossProcessCompositorApi,
|
||||||
|
font_context: Arc<FontContext>,
|
||||||
|
) -> Self {
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
if servo_config::pref!(dom_canvas_vello_enabled) {
|
||||||
|
return Self::Vello(CanvasData::new(size, compositor_api, font_context));
|
||||||
|
}
|
||||||
|
Self::Raqote(CanvasData::new(size, compositor_api, font_context))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn image_key(&self) -> ImageKey {
|
||||||
|
match self {
|
||||||
|
Canvas::Raqote(canvas_data) => canvas_data.image_key(),
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
Canvas::Vello(canvas_data) => canvas_data.image_key(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn pop_clip(&mut self) {
|
fn pop_clip(&mut self) {
|
||||||
match self {
|
match self {
|
||||||
Canvas::Raqote(canvas_data) => canvas_data.pop_clip(),
|
Canvas::Raqote(canvas_data) => canvas_data.pop_clip(),
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
Canvas::Vello(canvas_data) => canvas_data.pop_clip(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,6 +344,19 @@ impl Canvas {
|
||||||
composition_options,
|
composition_options,
|
||||||
transform,
|
transform,
|
||||||
),
|
),
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
Canvas::Vello(canvas_data) => canvas_data.fill_text(
|
||||||
|
text,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
max_width,
|
||||||
|
is_rtl,
|
||||||
|
style,
|
||||||
|
text_options,
|
||||||
|
shadow_options,
|
||||||
|
composition_options,
|
||||||
|
transform,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,6 +372,10 @@ impl Canvas {
|
||||||
Canvas::Raqote(canvas_data) => {
|
Canvas::Raqote(canvas_data) => {
|
||||||
canvas_data.fill_rect(rect, style, shadow_options, composition_options, transform)
|
canvas_data.fill_rect(rect, style, shadow_options, composition_options, transform)
|
||||||
},
|
},
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
Canvas::Vello(canvas_data) => {
|
||||||
|
canvas_data.fill_rect(rect, style, shadow_options, composition_options, transform)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,6 +397,15 @@ impl Canvas {
|
||||||
composition_options,
|
composition_options,
|
||||||
transform,
|
transform,
|
||||||
),
|
),
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
Canvas::Vello(canvas_data) => canvas_data.stroke_rect(
|
||||||
|
rect,
|
||||||
|
style,
|
||||||
|
line_options,
|
||||||
|
shadow_options,
|
||||||
|
composition_options,
|
||||||
|
transform,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,6 +421,10 @@ impl Canvas {
|
||||||
Canvas::Raqote(canvas_data) => {
|
Canvas::Raqote(canvas_data) => {
|
||||||
canvas_data.fill_path(path, style, shadow_options, composition_options, transform)
|
canvas_data.fill_path(path, style, shadow_options, composition_options, transform)
|
||||||
},
|
},
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
Canvas::Vello(canvas_data) => {
|
||||||
|
canvas_data.fill_path(path, style, shadow_options, composition_options, transform)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,12 +446,23 @@ impl Canvas {
|
||||||
composition_options,
|
composition_options,
|
||||||
transform,
|
transform,
|
||||||
),
|
),
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
Canvas::Vello(canvas_data) => canvas_data.stroke_path(
|
||||||
|
path,
|
||||||
|
style,
|
||||||
|
line_options,
|
||||||
|
shadow_options,
|
||||||
|
composition_options,
|
||||||
|
transform,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_rect(&mut self, rect: &Rect<f32>, transform: Transform2D<f32>) {
|
fn clear_rect(&mut self, rect: &Rect<f32>, transform: Transform2D<f32>) {
|
||||||
match self {
|
match self {
|
||||||
Canvas::Raqote(canvas_data) => canvas_data.clear_rect(rect, transform),
|
Canvas::Raqote(canvas_data) => canvas_data.clear_rect(rect, transform),
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
Canvas::Vello(canvas_data) => canvas_data.clear_rect(rect, transform),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,42 +486,64 @@ impl Canvas {
|
||||||
composition_options,
|
composition_options,
|
||||||
transform,
|
transform,
|
||||||
),
|
),
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
Canvas::Vello(canvas_data) => canvas_data.draw_image(
|
||||||
|
snapshot,
|
||||||
|
dest_rect,
|
||||||
|
source_rect,
|
||||||
|
smoothing_enabled,
|
||||||
|
shadow_options,
|
||||||
|
composition_options,
|
||||||
|
transform,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_pixels(&mut self, read_rect: Option<Rect<u32>>) -> Snapshot {
|
fn read_pixels(&mut self, read_rect: Option<Rect<u32>>) -> Snapshot {
|
||||||
match self {
|
match self {
|
||||||
Canvas::Raqote(canvas_data) => canvas_data.read_pixels(read_rect),
|
Canvas::Raqote(canvas_data) => canvas_data.read_pixels(read_rect),
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
Canvas::Vello(canvas_data) => canvas_data.read_pixels(read_rect),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn measure_text(&mut self, text: String, text_options: TextOptions) -> TextMetrics {
|
fn measure_text(&mut self, text: String, text_options: TextOptions) -> TextMetrics {
|
||||||
match self {
|
match self {
|
||||||
Canvas::Raqote(canvas_data) => canvas_data.measure_text(text, text_options),
|
Canvas::Raqote(canvas_data) => canvas_data.measure_text(text, text_options),
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
Canvas::Vello(canvas_data) => canvas_data.measure_text(text, text_options),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clip_path(&mut self, path: &Path, transform: Transform2D<f32>) {
|
fn clip_path(&mut self, path: &Path, transform: Transform2D<f32>) {
|
||||||
match self {
|
match self {
|
||||||
Canvas::Raqote(canvas_data) => canvas_data.clip_path(path, transform),
|
Canvas::Raqote(canvas_data) => canvas_data.clip_path(path, transform),
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
Canvas::Vello(canvas_data) => canvas_data.clip_path(path, transform),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put_image_data(&mut self, snapshot: Snapshot, rect: Rect<u32>) {
|
fn put_image_data(&mut self, snapshot: Snapshot, rect: Rect<u32>) {
|
||||||
match self {
|
match self {
|
||||||
Canvas::Raqote(canvas_data) => canvas_data.put_image_data(snapshot, rect),
|
Canvas::Raqote(canvas_data) => canvas_data.put_image_data(snapshot, rect),
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
Canvas::Vello(canvas_data) => canvas_data.put_image_data(snapshot, rect),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_image_rendering(&mut self) {
|
fn update_image_rendering(&mut self) {
|
||||||
match self {
|
match self {
|
||||||
Canvas::Raqote(canvas_data) => canvas_data.update_image_rendering(),
|
Canvas::Raqote(canvas_data) => canvas_data.update_image_rendering(),
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
Canvas::Vello(canvas_data) => canvas_data.update_image_rendering(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recreate(&mut self, size: Option<Size2D<u64>>) {
|
fn recreate(&mut self, size: Option<Size2D<u64>>) {
|
||||||
match self {
|
match self {
|
||||||
Canvas::Raqote(canvas_data) => canvas_data.recreate(size),
|
Canvas::Raqote(canvas_data) => canvas_data.recreate(size),
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
Canvas::Vello(canvas_data) => canvas_data.recreate(size),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
mod backend;
|
mod backend;
|
||||||
mod raqote_backend;
|
mod raqote_backend;
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
mod vello_backend;
|
||||||
|
|
||||||
pub mod canvas_data;
|
pub mod canvas_data;
|
||||||
pub mod canvas_paint_thread;
|
pub mod canvas_paint_thread;
|
||||||
|
|
752
components/canvas/vello_backend.rs
Normal file
752
components/canvas/vello_backend.rs
Normal file
|
@ -0,0 +1,752 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
//! Vello implementation of 2D canvas backend.
|
||||||
|
//!
|
||||||
|
//! Vello only encodes commands for GPU, then runs rendering when
|
||||||
|
//! image is explicitly requested. This requires to copy image
|
||||||
|
//! from texture to buffer, then download buffer to CPU
|
||||||
|
//! (where we also need to un pad it).
|
||||||
|
//!
|
||||||
|
//! All Vello images are in no alpha premultiplied RGBA8 pixel format.
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use canvas_traits::canvas::{
|
||||||
|
BlendingStyle, CanvasGradientStop, CompositionOptions, CompositionOrBlending, CompositionStyle,
|
||||||
|
FillOrStrokeStyle, LineCapStyle, LineJoinStyle, LineOptions, Path, ShadowOptions,
|
||||||
|
};
|
||||||
|
use compositing_traits::SerializableImageData;
|
||||||
|
use euclid::default::{Point2D, Rect, Size2D, Transform2D};
|
||||||
|
use fonts::{ByteIndex, FontIdentifier, FontTemplateRefMethods as _};
|
||||||
|
use ipc_channel::ipc::IpcSharedMemory;
|
||||||
|
use pixels::{Snapshot, SnapshotAlphaMode, SnapshotPixelFormat};
|
||||||
|
use range::Range;
|
||||||
|
use style::color::AbsoluteColor;
|
||||||
|
use vello::wgpu::{
|
||||||
|
BackendOptions, Backends, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, Device,
|
||||||
|
Extent3d, Instance, InstanceDescriptor, InstanceFlags, MapMode, Queue, TexelCopyBufferInfo,
|
||||||
|
TexelCopyBufferLayout, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
|
||||||
|
TextureViewDescriptor,
|
||||||
|
};
|
||||||
|
use vello::{kurbo, peniko};
|
||||||
|
use webrender_api::{ImageDescriptor, ImageDescriptorFlags};
|
||||||
|
|
||||||
|
use crate::backend::GenericDrawTarget;
|
||||||
|
use crate::canvas_data::{Filter, TextRun};
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
/// The shared font cache used by all canvases that render on a thread. It would be nicer
|
||||||
|
/// to have a global cache, but it looks like font-kit uses a per-thread FreeType, so
|
||||||
|
/// in order to ensure that fonts are particular to a thread we have to make our own
|
||||||
|
/// cache thread local as well.
|
||||||
|
static SHARED_FONT_CACHE: RefCell<HashMap<FontIdentifier, peniko::Font>> = RefCell::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct VelloDrawTarget {
|
||||||
|
device: Device,
|
||||||
|
queue: Queue,
|
||||||
|
renderer: Rc<RefCell<vello::Renderer>>,
|
||||||
|
scene: vello::Scene,
|
||||||
|
size: Size2D<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn options() -> vello::RendererOptions {
|
||||||
|
vello::RendererOptions {
|
||||||
|
use_cpu: false,
|
||||||
|
num_init_threads: NonZeroUsize::new(1),
|
||||||
|
antialiasing_support: vello::AaSupport::area_only(),
|
||||||
|
pipeline_cache: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VelloDrawTarget {
|
||||||
|
fn with_draw_options<F: FnOnce(&mut Self)>(&mut self, draw_options: &CompositionOptions, f: F) {
|
||||||
|
self.scene.push_layer(
|
||||||
|
draw_options.composition_operation.convert(),
|
||||||
|
1.0,
|
||||||
|
kurbo::Affine::IDENTITY,
|
||||||
|
&kurbo::Rect::ZERO.with_size(self.size.cast()),
|
||||||
|
);
|
||||||
|
f(self);
|
||||||
|
self.scene.pop_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_and_download<F, R>(&self, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(u32, Option<&[u8]>) -> R,
|
||||||
|
{
|
||||||
|
let size = Extent3d {
|
||||||
|
width: self.size.width,
|
||||||
|
height: self.size.height,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
};
|
||||||
|
let target = self.device.create_texture(&TextureDescriptor {
|
||||||
|
label: Some("Target texture"),
|
||||||
|
size,
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: TextureDimension::D2,
|
||||||
|
format: TextureFormat::Rgba8Unorm,
|
||||||
|
usage: TextureUsages::STORAGE_BINDING | TextureUsages::COPY_SRC,
|
||||||
|
view_formats: &[],
|
||||||
|
});
|
||||||
|
let view = target.create_view(&TextureViewDescriptor::default());
|
||||||
|
self.renderer
|
||||||
|
.borrow_mut()
|
||||||
|
.render_to_texture(
|
||||||
|
&self.device,
|
||||||
|
&self.queue,
|
||||||
|
&self.scene,
|
||||||
|
&view,
|
||||||
|
&vello::RenderParams {
|
||||||
|
base_color: peniko::color::AlphaColor::TRANSPARENT,
|
||||||
|
width: self.size.width,
|
||||||
|
height: self.size.height,
|
||||||
|
antialiasing_method: vello::AaConfig::Area,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
// TODO(perf): do a render pass that will multiply with alpha on GPU
|
||||||
|
let padded_byte_width = (self.size.width * 4).next_multiple_of(256);
|
||||||
|
let buffer_size = padded_byte_width as u64 * self.size.height as u64;
|
||||||
|
let buffer = self.device.create_buffer(&BufferDescriptor {
|
||||||
|
label: Some("val"),
|
||||||
|
size: buffer_size,
|
||||||
|
usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
let mut encoder = self
|
||||||
|
.device
|
||||||
|
.create_command_encoder(&CommandEncoderDescriptor {
|
||||||
|
label: Some("Copy out buffer"),
|
||||||
|
});
|
||||||
|
encoder.copy_texture_to_buffer(
|
||||||
|
target.as_image_copy(),
|
||||||
|
TexelCopyBufferInfo {
|
||||||
|
buffer: &buffer,
|
||||||
|
layout: TexelCopyBufferLayout {
|
||||||
|
offset: 0,
|
||||||
|
bytes_per_row: Some(padded_byte_width),
|
||||||
|
rows_per_image: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
size,
|
||||||
|
);
|
||||||
|
self.queue.submit([encoder.finish()]);
|
||||||
|
let result = {
|
||||||
|
let buf_slice = buffer.slice(..);
|
||||||
|
let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel();
|
||||||
|
buf_slice.map_async(MapMode::Read, move |v| sender.send(v).unwrap());
|
||||||
|
if let Err(error) =
|
||||||
|
vello::util::block_on_wgpu(&self.device, receiver.receive()).unwrap()
|
||||||
|
{
|
||||||
|
log::warn!("VELLO WGPU MAP ASYNC ERROR {error}");
|
||||||
|
return f(padded_byte_width, None);
|
||||||
|
}
|
||||||
|
let data = buf_slice.get_mapped_range();
|
||||||
|
f(padded_byte_width, Some(&data))
|
||||||
|
};
|
||||||
|
buffer.unmap();
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericDrawTarget for VelloDrawTarget {
|
||||||
|
type SourceSurface = Vec<u8>; // TODO: this should be texture
|
||||||
|
|
||||||
|
fn new(size: Size2D<u32>) -> Self {
|
||||||
|
// TODO: we should read prefs instead of env
|
||||||
|
|
||||||
|
// we forbid GL because it clashes with servo's GL usage
|
||||||
|
let backends = Backends::from_env().unwrap_or_default() - Backends::GL;
|
||||||
|
let flags = InstanceFlags::from_build_config().with_env();
|
||||||
|
let backend_options = BackendOptions::from_env_or_default();
|
||||||
|
let instance = Instance::new(&InstanceDescriptor {
|
||||||
|
backends,
|
||||||
|
flags,
|
||||||
|
backend_options,
|
||||||
|
});
|
||||||
|
let mut context = vello::util::RenderContext {
|
||||||
|
instance,
|
||||||
|
devices: Vec::new(),
|
||||||
|
};
|
||||||
|
let device_id = pollster::block_on(context.device(None)).unwrap();
|
||||||
|
let device_handle = &mut context.devices[device_id];
|
||||||
|
let device = device_handle.device.clone();
|
||||||
|
let queue = device_handle.queue.clone();
|
||||||
|
let renderer = vello::Renderer::new(&device, options()).unwrap();
|
||||||
|
let scene = vello::Scene::new();
|
||||||
|
device.on_uncaptured_error(Box::new(|error| {
|
||||||
|
log::error!("VELLO WGPU ERROR: {error}");
|
||||||
|
}));
|
||||||
|
Self {
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
renderer: Rc::new(RefCell::new(renderer)),
|
||||||
|
scene,
|
||||||
|
size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_rect(&mut self, rect: &Rect<f32>, transform: Transform2D<f32>) {
|
||||||
|
let rect: kurbo::Rect = rect.cast().into();
|
||||||
|
let transform = transform.cast().into();
|
||||||
|
self.scene
|
||||||
|
.push_layer(peniko::Compose::Clear, 0.0, transform, &rect);
|
||||||
|
self.scene.fill(
|
||||||
|
peniko::Fill::NonZero,
|
||||||
|
transform,
|
||||||
|
peniko::BrushRef::Solid(peniko::color::AlphaColor::TRANSPARENT),
|
||||||
|
None,
|
||||||
|
&rect,
|
||||||
|
);
|
||||||
|
self.scene.pop_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_surface(&mut self, surface: Vec<u8>, source: Rect<i32>, destination: Point2D<i32>) {
|
||||||
|
let destination: kurbo::Point = destination.cast::<f64>().into();
|
||||||
|
let rect = kurbo::Rect::from_origin_size(destination, source.size.cast());
|
||||||
|
|
||||||
|
// TODO: ignore clip from prev layers
|
||||||
|
// this will require creating a stacks of applicable clips
|
||||||
|
// that will be popped and reinserted after
|
||||||
|
// or we could impl this in vello directly
|
||||||
|
|
||||||
|
// then there is also this nasty vello bug where clipping does not work correctly:
|
||||||
|
// https://xi.zulipchat.com/#narrow/channel/197075-vello/topic/Servo.202D.20canvas.20backend/near/525153593
|
||||||
|
|
||||||
|
self.scene
|
||||||
|
.push_layer(peniko::Compose::Copy, 1.0, kurbo::Affine::IDENTITY, &rect);
|
||||||
|
|
||||||
|
self.scene.fill(
|
||||||
|
peniko::Fill::NonZero,
|
||||||
|
kurbo::Affine::IDENTITY,
|
||||||
|
&peniko::Image {
|
||||||
|
data: peniko::Blob::from(surface),
|
||||||
|
format: peniko::ImageFormat::Rgba8,
|
||||||
|
width: source.size.width as u32,
|
||||||
|
height: source.size.height as u32,
|
||||||
|
x_extend: peniko::Extend::Pad,
|
||||||
|
y_extend: peniko::Extend::Pad,
|
||||||
|
quality: peniko::ImageQuality::Low,
|
||||||
|
alpha: 1.0,
|
||||||
|
},
|
||||||
|
Some(kurbo::Affine::translate(destination.to_vec2())),
|
||||||
|
&rect,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.scene.pop_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Self {
|
||||||
|
Self {
|
||||||
|
device: self.device.clone(),
|
||||||
|
queue: self.queue.clone(),
|
||||||
|
renderer: self.renderer.clone(),
|
||||||
|
scene: vello::Scene::new(),
|
||||||
|
size: size.cast(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_surface(
|
||||||
|
&mut self,
|
||||||
|
surface: Vec<u8>,
|
||||||
|
dest: Rect<f64>,
|
||||||
|
source: Rect<f64>,
|
||||||
|
filter: Filter,
|
||||||
|
composition_options: CompositionOptions,
|
||||||
|
transform: Transform2D<f32>,
|
||||||
|
) {
|
||||||
|
let scale_up = dest.size.width > source.size.width || dest.size.height > source.size.height;
|
||||||
|
let shape: kurbo::Rect = dest.into();
|
||||||
|
self.with_draw_options(&composition_options, move |self_| {
|
||||||
|
self_.scene.fill(
|
||||||
|
peniko::Fill::NonZero,
|
||||||
|
transform.cast().into(),
|
||||||
|
&peniko::Image {
|
||||||
|
data: peniko::Blob::from(surface),
|
||||||
|
format: peniko::ImageFormat::Rgba8,
|
||||||
|
width: source.size.width as u32,
|
||||||
|
height: source.size.height as u32,
|
||||||
|
x_extend: peniko::Extend::Pad,
|
||||||
|
y_extend: peniko::Extend::Pad,
|
||||||
|
// we should only do bicubic when scaling up
|
||||||
|
quality: if scale_up {
|
||||||
|
filter.convert()
|
||||||
|
} else {
|
||||||
|
peniko::ImageQuality::Low
|
||||||
|
},
|
||||||
|
alpha: composition_options.alpha as f32,
|
||||||
|
},
|
||||||
|
Some(
|
||||||
|
kurbo::Affine::translate((dest.origin.x, dest.origin.y)).pre_scale_non_uniform(
|
||||||
|
dest.size.width / source.size.width,
|
||||||
|
dest.size.height / source.size.height,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
&shape,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_surface_with_shadow(
|
||||||
|
&self,
|
||||||
|
_surface: Vec<u8>,
|
||||||
|
_dest: &Point2D<f32>,
|
||||||
|
_shadow_options: ShadowOptions,
|
||||||
|
_composition_options: CompositionOptions,
|
||||||
|
) {
|
||||||
|
log::warn!("no support for drawing shadows");
|
||||||
|
/*
|
||||||
|
We will need to do some changes to support drawing shadows with vello, as current abstraction is made for azure.
|
||||||
|
In vello we do not need new draw target (we will use layers) and we need to pass whole rect.
|
||||||
|
offsets will be applied to rect directly. shadow blur will be passed directly to let backend do transforms.
|
||||||
|
*/
|
||||||
|
//self_.scene.draw_blurred_rounded_rect(self_.transform, rect, color, 0.0, sigma);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill(
|
||||||
|
&mut self,
|
||||||
|
path: &Path,
|
||||||
|
style: FillOrStrokeStyle,
|
||||||
|
composition_options: CompositionOptions,
|
||||||
|
transform: Transform2D<f32>,
|
||||||
|
) {
|
||||||
|
self.with_draw_options(&composition_options, |self_| {
|
||||||
|
self_.scene.fill(
|
||||||
|
peniko::Fill::NonZero,
|
||||||
|
transform.cast().into(),
|
||||||
|
&style
|
||||||
|
.convert()
|
||||||
|
.multiply_alpha(composition_options.alpha as f32),
|
||||||
|
None,
|
||||||
|
&path.0,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_text(
|
||||||
|
&mut self,
|
||||||
|
text_runs: Vec<TextRun>,
|
||||||
|
start: Point2D<f32>,
|
||||||
|
style: FillOrStrokeStyle,
|
||||||
|
composition_options: CompositionOptions,
|
||||||
|
transform: Transform2D<f32>,
|
||||||
|
) {
|
||||||
|
let pattern = style
|
||||||
|
.convert()
|
||||||
|
.multiply_alpha(composition_options.alpha as f32);
|
||||||
|
let transform = transform.cast().into();
|
||||||
|
self.with_draw_options(&composition_options, |self_| {
|
||||||
|
let mut advance = 0.;
|
||||||
|
for run in text_runs.iter() {
|
||||||
|
let glyphs = &run.glyphs;
|
||||||
|
|
||||||
|
let template = &run.font.template;
|
||||||
|
|
||||||
|
SHARED_FONT_CACHE.with(|font_cache| {
|
||||||
|
let identifier = template.identifier();
|
||||||
|
if !font_cache.borrow().contains_key(&identifier) {
|
||||||
|
font_cache.borrow_mut().insert(
|
||||||
|
identifier.clone(),
|
||||||
|
peniko::Font::new(
|
||||||
|
peniko::Blob::from(run.font.data().as_ref().to_vec()),
|
||||||
|
identifier.index(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let font_cache = font_cache.borrow();
|
||||||
|
let Some(font) = font_cache.get(&identifier) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
self_
|
||||||
|
.scene
|
||||||
|
.draw_glyphs(font)
|
||||||
|
.transform(transform)
|
||||||
|
.brush(&pattern)
|
||||||
|
.font_size(run.font.descriptor.pt_size.to_f32_px())
|
||||||
|
.draw(
|
||||||
|
peniko::Fill::NonZero,
|
||||||
|
glyphs
|
||||||
|
.iter_glyphs_for_byte_range(&Range::new(ByteIndex(0), glyphs.len()))
|
||||||
|
.map(|glyph| {
|
||||||
|
let glyph_offset = glyph.offset().unwrap_or(Point2D::zero());
|
||||||
|
let x = advance + start.x + glyph_offset.x.to_f32_px();
|
||||||
|
let y = start.y + glyph_offset.y.to_f32_px();
|
||||||
|
advance += glyph.advance().to_f32_px();
|
||||||
|
vello::Glyph {
|
||||||
|
id: glyph.id(),
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_rect(
|
||||||
|
&mut self,
|
||||||
|
rect: &Rect<f32>,
|
||||||
|
style: FillOrStrokeStyle,
|
||||||
|
composition_options: CompositionOptions,
|
||||||
|
transform: Transform2D<f32>,
|
||||||
|
) {
|
||||||
|
let pattern = style
|
||||||
|
.convert()
|
||||||
|
.multiply_alpha(composition_options.alpha as f32);
|
||||||
|
let transform = transform.cast().into();
|
||||||
|
let rect: kurbo::Rect = rect.cast().into();
|
||||||
|
self.with_draw_options(&composition_options, |self_| {
|
||||||
|
self_
|
||||||
|
.scene
|
||||||
|
.fill(peniko::Fill::NonZero, transform, &pattern, None, &rect);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_size(&self) -> Size2D<i32> {
|
||||||
|
self.size.cast()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_clip(&mut self) {
|
||||||
|
self.scene.pop_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_clip(&mut self, path: &Path, transform: Transform2D<f32>) {
|
||||||
|
self.scene
|
||||||
|
.push_layer(peniko::Mix::Clip, 1.0, transform.cast().into(), &path.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_clip_rect(&mut self, rect: &Rect<i32>) {
|
||||||
|
let mut path = Path::new();
|
||||||
|
let rect = rect.cast();
|
||||||
|
path.rect(
|
||||||
|
rect.origin.x,
|
||||||
|
rect.origin.y,
|
||||||
|
rect.size.width,
|
||||||
|
rect.size.height,
|
||||||
|
);
|
||||||
|
self.push_clip(&path, Transform2D::identity());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stroke(
|
||||||
|
&mut self,
|
||||||
|
path: &Path,
|
||||||
|
style: FillOrStrokeStyle,
|
||||||
|
line_options: LineOptions,
|
||||||
|
composition_options: CompositionOptions,
|
||||||
|
transform: Transform2D<f32>,
|
||||||
|
) {
|
||||||
|
self.with_draw_options(&composition_options, |self_| {
|
||||||
|
self_.scene.stroke(
|
||||||
|
&line_options.convert(),
|
||||||
|
transform.cast().into(),
|
||||||
|
&style
|
||||||
|
.convert()
|
||||||
|
.multiply_alpha(composition_options.alpha as f32),
|
||||||
|
None,
|
||||||
|
&path.0,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stroke_rect(
|
||||||
|
&mut self,
|
||||||
|
rect: &Rect<f32>,
|
||||||
|
style: FillOrStrokeStyle,
|
||||||
|
line_options: LineOptions,
|
||||||
|
composition_options: CompositionOptions,
|
||||||
|
transform: Transform2D<f32>,
|
||||||
|
) {
|
||||||
|
let rect: kurbo::Rect = rect.cast().into();
|
||||||
|
self.with_draw_options(&composition_options, |self_| {
|
||||||
|
self_.scene.stroke(
|
||||||
|
&line_options.convert(),
|
||||||
|
transform.cast().into(),
|
||||||
|
&style
|
||||||
|
.convert()
|
||||||
|
.multiply_alpha(composition_options.alpha as f32),
|
||||||
|
None,
|
||||||
|
&rect,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn image_descriptor_and_serializable_data(
|
||||||
|
&mut self,
|
||||||
|
) -> (ImageDescriptor, SerializableImageData) {
|
||||||
|
let size = self.size;
|
||||||
|
self.render_and_download(|stride, data| {
|
||||||
|
let image_desc = ImageDescriptor {
|
||||||
|
format: webrender_api::ImageFormat::RGBA8,
|
||||||
|
size: size.cast().cast_unit(),
|
||||||
|
stride: data.map(|_| stride as i32),
|
||||||
|
offset: 0,
|
||||||
|
flags: ImageDescriptorFlags::empty(),
|
||||||
|
};
|
||||||
|
let data = SerializableImageData::Raw(if let Some(data) = data {
|
||||||
|
let mut data = IpcSharedMemory::from_bytes(data);
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe {
|
||||||
|
pixels::generic_transform_inplace::<1, false, false>(data.deref_mut());
|
||||||
|
};
|
||||||
|
data
|
||||||
|
} else {
|
||||||
|
IpcSharedMemory::from_byte(0, size.area() as usize * 4)
|
||||||
|
});
|
||||||
|
(image_desc, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn snapshot(&mut self) -> pixels::Snapshot {
|
||||||
|
let size = self.size;
|
||||||
|
self.render_and_download(|padded_byte_width, data| {
|
||||||
|
let data = data
|
||||||
|
.map(|data| {
|
||||||
|
let mut result_unpadded = Vec::<u8>::with_capacity(size.area() as usize * 4);
|
||||||
|
for row in 0..self.size.height {
|
||||||
|
let start = (row * padded_byte_width).try_into().unwrap();
|
||||||
|
result_unpadded
|
||||||
|
.extend(&data[start..start + (self.size.width * 4) as usize]);
|
||||||
|
}
|
||||||
|
result_unpadded
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| vec![0; size.area() as usize * 4]);
|
||||||
|
Snapshot::from_vec(
|
||||||
|
size,
|
||||||
|
SnapshotPixelFormat::RGBA,
|
||||||
|
SnapshotAlphaMode::Transparent {
|
||||||
|
premultiplied: false,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn surface(&mut self) -> Vec<u8> {
|
||||||
|
self.snapshot().to_vec(None, None).0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_source_surface_from_data(&self, data: Snapshot) -> Option<Vec<u8>> {
|
||||||
|
let (data, _, _) = data.to_vec(
|
||||||
|
Some(SnapshotAlphaMode::Transparent {
|
||||||
|
premultiplied: false,
|
||||||
|
}),
|
||||||
|
Some(SnapshotPixelFormat::RGBA),
|
||||||
|
);
|
||||||
|
Some(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A version of the `Into<T>` trait from the standard library that can be used
|
||||||
|
/// to convert between two types that are not defined in the canvas crate.
|
||||||
|
pub(crate) trait Convert<T> {
|
||||||
|
fn convert(self) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Convert<kurbo::Join> for LineJoinStyle {
|
||||||
|
fn convert(self) -> kurbo::Join {
|
||||||
|
match self {
|
||||||
|
LineJoinStyle::Round => kurbo::Join::Round,
|
||||||
|
LineJoinStyle::Bevel => kurbo::Join::Bevel,
|
||||||
|
LineJoinStyle::Miter => kurbo::Join::Miter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Convert<kurbo::Cap> for LineCapStyle {
|
||||||
|
fn convert(self) -> kurbo::Cap {
|
||||||
|
match self {
|
||||||
|
LineCapStyle::Butt => kurbo::Cap::Butt,
|
||||||
|
LineCapStyle::Round => kurbo::Cap::Round,
|
||||||
|
LineCapStyle::Square => kurbo::Cap::Square,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Convert<peniko::Color> for AbsoluteColor {
|
||||||
|
fn convert(self) -> peniko::Color {
|
||||||
|
let srgb = self.into_srgb_legacy();
|
||||||
|
peniko::Color::new([
|
||||||
|
srgb.components.0,
|
||||||
|
srgb.components.1,
|
||||||
|
srgb.components.2,
|
||||||
|
srgb.alpha,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Convert<peniko::BlendMode> for CompositionOrBlending {
|
||||||
|
fn convert(self) -> peniko::BlendMode {
|
||||||
|
match self {
|
||||||
|
CompositionOrBlending::Composition(composition_style) => {
|
||||||
|
composition_style.convert().into()
|
||||||
|
},
|
||||||
|
CompositionOrBlending::Blending(blending_style) => blending_style.convert().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Convert<peniko::Compose> for CompositionStyle {
|
||||||
|
fn convert(self) -> peniko::Compose {
|
||||||
|
match self {
|
||||||
|
CompositionStyle::SourceIn => peniko::Compose::SrcIn,
|
||||||
|
CompositionStyle::SourceOut => peniko::Compose::SrcOut,
|
||||||
|
CompositionStyle::SourceOver => peniko::Compose::SrcOver,
|
||||||
|
CompositionStyle::SourceAtop => peniko::Compose::SrcAtop,
|
||||||
|
CompositionStyle::DestinationIn => peniko::Compose::DestIn,
|
||||||
|
CompositionStyle::DestinationOut => peniko::Compose::DestOut,
|
||||||
|
CompositionStyle::DestinationOver => peniko::Compose::DestOver,
|
||||||
|
CompositionStyle::DestinationAtop => peniko::Compose::DestAtop,
|
||||||
|
CompositionStyle::Copy => peniko::Compose::Copy,
|
||||||
|
CompositionStyle::Lighter => peniko::Compose::Plus,
|
||||||
|
CompositionStyle::Xor => peniko::Compose::Xor,
|
||||||
|
CompositionStyle::Clear => peniko::Compose::Clear,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Convert<peniko::Mix> for BlendingStyle {
|
||||||
|
fn convert(self) -> peniko::Mix {
|
||||||
|
match self {
|
||||||
|
BlendingStyle::Multiply => peniko::Mix::Multiply,
|
||||||
|
BlendingStyle::Screen => peniko::Mix::Screen,
|
||||||
|
BlendingStyle::Overlay => peniko::Mix::Overlay,
|
||||||
|
BlendingStyle::Darken => peniko::Mix::Darken,
|
||||||
|
BlendingStyle::Lighten => peniko::Mix::Lighten,
|
||||||
|
BlendingStyle::ColorDodge => peniko::Mix::ColorDodge,
|
||||||
|
BlendingStyle::ColorBurn => peniko::Mix::ColorBurn,
|
||||||
|
BlendingStyle::HardLight => peniko::Mix::HardLight,
|
||||||
|
BlendingStyle::SoftLight => peniko::Mix::SoftLight,
|
||||||
|
BlendingStyle::Difference => peniko::Mix::Difference,
|
||||||
|
BlendingStyle::Exclusion => peniko::Mix::Exclusion,
|
||||||
|
BlendingStyle::Hue => peniko::Mix::Hue,
|
||||||
|
BlendingStyle::Saturation => peniko::Mix::Saturation,
|
||||||
|
BlendingStyle::Color => peniko::Mix::Color,
|
||||||
|
BlendingStyle::Luminosity => peniko::Mix::Luminosity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Convert<kurbo::Stroke> for LineOptions {
|
||||||
|
fn convert(self) -> kurbo::Stroke {
|
||||||
|
let LineOptions {
|
||||||
|
width,
|
||||||
|
cap_style,
|
||||||
|
join_style,
|
||||||
|
miter_limit,
|
||||||
|
dash,
|
||||||
|
dash_offset,
|
||||||
|
} = self;
|
||||||
|
kurbo::Stroke {
|
||||||
|
width,
|
||||||
|
join: join_style.convert(),
|
||||||
|
miter_limit,
|
||||||
|
start_cap: cap_style.convert(),
|
||||||
|
end_cap: cap_style.convert(),
|
||||||
|
dash_pattern: dash.iter().map(|x| *x as f64).collect(),
|
||||||
|
dash_offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Convert<peniko::Brush> for FillOrStrokeStyle {
|
||||||
|
fn convert(self) -> peniko::Brush {
|
||||||
|
use canvas_traits::canvas::FillOrStrokeStyle::*;
|
||||||
|
match self {
|
||||||
|
Color(absolute_color) => peniko::Brush::Solid(absolute_color.convert()),
|
||||||
|
LinearGradient(style) => {
|
||||||
|
let start = kurbo::Point::new(style.x0, style.y0);
|
||||||
|
let end = kurbo::Point::new(style.x1, style.y1);
|
||||||
|
let mut gradient = peniko::Gradient::new_linear(start, end);
|
||||||
|
gradient.stops = style.stops.convert();
|
||||||
|
peniko::Brush::Gradient(gradient)
|
||||||
|
},
|
||||||
|
RadialGradient(style) => {
|
||||||
|
let center1 = kurbo::Point::new(style.x0, style.y0);
|
||||||
|
let center2 = kurbo::Point::new(style.x1, style.y1);
|
||||||
|
let mut gradient = peniko::Gradient::new_two_point_radial(
|
||||||
|
center1,
|
||||||
|
style.r0 as f32,
|
||||||
|
center2,
|
||||||
|
style.r1 as f32,
|
||||||
|
);
|
||||||
|
gradient.stops = style.stops.convert();
|
||||||
|
peniko::Brush::Gradient(gradient)
|
||||||
|
},
|
||||||
|
Surface(surface_style) => {
|
||||||
|
let data = surface_style
|
||||||
|
.surface_data
|
||||||
|
.to_owned()
|
||||||
|
.to_vec(
|
||||||
|
Some(SnapshotAlphaMode::Transparent {
|
||||||
|
premultiplied: false,
|
||||||
|
}),
|
||||||
|
Some(SnapshotPixelFormat::RGBA),
|
||||||
|
)
|
||||||
|
.0;
|
||||||
|
peniko::Brush::Image(peniko::Image {
|
||||||
|
data: peniko::Blob::from(data),
|
||||||
|
format: peniko::ImageFormat::Rgba8,
|
||||||
|
width: surface_style.surface_size.width,
|
||||||
|
height: surface_style.surface_size.height,
|
||||||
|
x_extend: if surface_style.repeat_x {
|
||||||
|
peniko::Extend::Repeat
|
||||||
|
} else {
|
||||||
|
peniko::Extend::Pad
|
||||||
|
},
|
||||||
|
y_extend: if surface_style.repeat_y {
|
||||||
|
peniko::Extend::Repeat
|
||||||
|
} else {
|
||||||
|
peniko::Extend::Pad
|
||||||
|
},
|
||||||
|
quality: peniko::ImageQuality::Low,
|
||||||
|
alpha: 1.0,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Convert<peniko::color::DynamicColor> for AbsoluteColor {
|
||||||
|
fn convert(self) -> peniko::color::DynamicColor {
|
||||||
|
peniko::color::DynamicColor::from_alpha_color(self.convert())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Convert<peniko::ColorStop> for CanvasGradientStop {
|
||||||
|
fn convert(self) -> peniko::ColorStop {
|
||||||
|
peniko::ColorStop {
|
||||||
|
offset: self.offset as f32,
|
||||||
|
color: self.color.convert(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Convert<peniko::ColorStops> for Vec<CanvasGradientStop> {
|
||||||
|
fn convert(self) -> peniko::ColorStops {
|
||||||
|
let mut stops = peniko::ColorStops(self.into_iter().map(|item| item.convert()).collect());
|
||||||
|
// https://www.w3.org/html/test/results/2dcontext/annotated-spec/canvas.html#testrefs.2d.gradient.interpolate.overlap
|
||||||
|
stops
|
||||||
|
.0
|
||||||
|
.sort_by(|a, b| a.offset.partial_cmp(&b.offset).unwrap());
|
||||||
|
stops
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Convert<peniko::ImageQuality> for Filter {
|
||||||
|
fn convert(self) -> peniko::ImageQuality {
|
||||||
|
match self {
|
||||||
|
Filter::Bilinear => peniko::ImageQuality::Medium,
|
||||||
|
Filter::Nearest => peniko::ImageQuality::Low,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -76,6 +76,8 @@ pub struct Preferences {
|
||||||
pub dom_allow_scripts_to_close_windows: bool,
|
pub dom_allow_scripts_to_close_windows: bool,
|
||||||
pub dom_canvas_capture_enabled: bool,
|
pub dom_canvas_capture_enabled: bool,
|
||||||
pub dom_canvas_text_enabled: bool,
|
pub dom_canvas_text_enabled: bool,
|
||||||
|
/// Uses vello as canvas backend
|
||||||
|
pub dom_canvas_vello_enabled: bool,
|
||||||
pub dom_clipboardevent_enabled: bool,
|
pub dom_clipboardevent_enabled: bool,
|
||||||
pub dom_composition_event_enabled: bool,
|
pub dom_composition_event_enabled: bool,
|
||||||
pub dom_cookiestore_enabled: bool,
|
pub dom_cookiestore_enabled: bool,
|
||||||
|
@ -255,6 +257,7 @@ impl Preferences {
|
||||||
dom_bluetooth_testing_enabled: false,
|
dom_bluetooth_testing_enabled: false,
|
||||||
dom_canvas_capture_enabled: false,
|
dom_canvas_capture_enabled: false,
|
||||||
dom_canvas_text_enabled: true,
|
dom_canvas_text_enabled: true,
|
||||||
|
dom_canvas_vello_enabled: false,
|
||||||
dom_clipboardevent_enabled: true,
|
dom_clipboardevent_enabled: true,
|
||||||
dom_composition_event_enabled: false,
|
dom_composition_event_enabled: false,
|
||||||
dom_cookiestore_enabled: false,
|
dom_cookiestore_enabled: false,
|
||||||
|
|
|
@ -16,6 +16,7 @@ bluetooth = ["bluetooth_traits"]
|
||||||
default = []
|
default = []
|
||||||
tracing = ["dep:tracing"]
|
tracing = ["dep:tracing"]
|
||||||
webgpu = ["script_traits/webgpu"]
|
webgpu = ["script_traits/webgpu"]
|
||||||
|
vello = ["canvas/vello"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
background_hang_monitor = { path = "../background_hang_monitor" }
|
background_hang_monitor = { path = "../background_hang_monitor" }
|
||||||
|
|
|
@ -62,6 +62,7 @@ webgpu = [
|
||||||
"constellation/webgpu",
|
"constellation/webgpu",
|
||||||
"constellation_traits/webgpu",
|
"constellation_traits/webgpu",
|
||||||
]
|
]
|
||||||
|
vello = ["constellation/vello"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
background_hang_monitor = { path = "../background_hang_monitor" }
|
background_hang_monitor = { path = "../background_hang_monitor" }
|
||||||
|
|
|
@ -193,4 +193,4 @@ skip = [
|
||||||
|
|
||||||
# github.com organizations to allow git sources for
|
# github.com organizations to allow git sources for
|
||||||
[sources.allow-org]
|
[sources.allow-org]
|
||||||
github = ["pcwalton", "servo"]
|
github = ["pcwalton", "servo", "linebender"]
|
||||||
|
|
|
@ -52,6 +52,7 @@ webdriver = ["libservo/webdriver"]
|
||||||
webgl_backtrace = ["libservo/webgl_backtrace"]
|
webgl_backtrace = ["libservo/webgl_backtrace"]
|
||||||
webgpu = ["libservo/webgpu"]
|
webgpu = ["libservo/webgpu"]
|
||||||
webxr = ["libservo/webxr"]
|
webxr = ["libservo/webxr"]
|
||||||
|
vello = ["libservo/vello"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cfg-if = { workspace = true }
|
cfg-if = { workspace = true }
|
||||||
|
|
|
@ -104,7 +104,7 @@ def handle_preset(s: str) -> Optional[JobConfig]:
|
||||||
return JobConfig(
|
return JobConfig(
|
||||||
"WebGPU CTS",
|
"WebGPU CTS",
|
||||||
Workflow.LINUX,
|
Workflow.LINUX,
|
||||||
wpt=True, # reftests are mode for new layout
|
wpt=True,
|
||||||
wpt_args="_webgpu", # run only webgpu cts
|
wpt_args="_webgpu", # run only webgpu cts
|
||||||
profile="production", # WebGPU works to slow with debug assert
|
profile="production", # WebGPU works to slow with debug assert
|
||||||
unit_tests=False,
|
unit_tests=False,
|
||||||
|
@ -125,6 +125,20 @@ def handle_preset(s: str) -> Optional[JobConfig]:
|
||||||
unit_tests=False,
|
unit_tests=False,
|
||||||
number_of_wpt_chunks=2,
|
number_of_wpt_chunks=2,
|
||||||
)
|
)
|
||||||
|
elif any(word in s for word in ["vello"]):
|
||||||
|
return JobConfig(
|
||||||
|
"Vello WPT",
|
||||||
|
Workflow.LINUX,
|
||||||
|
wpt=True,
|
||||||
|
wpt_args=" ".join(
|
||||||
|
[
|
||||||
|
"--subsuite-file ./tests/wpt/vello_canvas_subsuite.json",
|
||||||
|
"--subsuite vello_canvas",
|
||||||
|
"--processes 1",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
build_args="--features 'vello'",
|
||||||
|
)
|
||||||
elif any(word in s for word in ["lint", "tidy"]):
|
elif any(word in s for word in ["lint", "tidy"]):
|
||||||
return JobConfig("Lint", Workflow.LINT)
|
return JobConfig("Lint", Workflow.LINT)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
[2d.composite.grid.no_filter.no_shadow.drawImage.html]
|
[2d.composite.grid.no_filter.no_shadow.drawImage.html]
|
||||||
expected: FAIL
|
expected:
|
||||||
|
if subsuite == "": FAIL
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
[2d.composite.grid.no_filter.no_shadow.fillRect.html]
|
[2d.composite.grid.no_filter.no_shadow.fillRect.html]
|
||||||
expected: FAIL
|
expected:
|
||||||
|
if subsuite == "": FAIL
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
[2d.composite.grid.no_filter.no_shadow.pattern.html]
|
[2d.composite.grid.no_filter.no_shadow.pattern.html]
|
||||||
expected: FAIL
|
expected:
|
||||||
|
if subsuite == "": FAIL
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
[2d.gradient.interpolate.coloralpha.html]
|
||||||
|
[Canvas test: 2d.gradient.interpolate.coloralpha]
|
||||||
|
bug: https://github.com/linebender/vello/issues/1056
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": FAIL
|
|
@ -1,4 +1,4 @@
|
||||||
[2d.gradient.radial.cone.behind.html]
|
[2d.gradient.radial.cone.behind.html]
|
||||||
[Canvas test: 2d.gradient.radial.cone.behind]
|
[Canvas test: 2d.gradient.radial.cone.behind]
|
||||||
expected: FAIL
|
expected:
|
||||||
|
if subsuite == "": FAIL
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[2d.gradient.radial.cone.shape2.html]
|
[2d.gradient.radial.cone.shape2.html]
|
||||||
[Canvas test: 2d.gradient.radial.cone.shape2]
|
[Canvas test: 2d.gradient.radial.cone.shape2]
|
||||||
expected: FAIL
|
expected:
|
||||||
|
if subsuite == "": FAIL
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[2d.gradient.radial.outside2.html]
|
[2d.gradient.radial.outside2.html]
|
||||||
[Canvas test: 2d.gradient.radial.outside2]
|
[Canvas test: 2d.gradient.radial.outside2]
|
||||||
expected: FAIL
|
expected:
|
||||||
|
if subsuite == "": FAIL
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[2d.gradient.radial.outside3.html]
|
[2d.gradient.radial.outside3.html]
|
||||||
[Canvas test: 2d.gradient.radial.outside3]
|
[Canvas test: 2d.gradient.radial.outside3]
|
||||||
expected: FAIL
|
expected:
|
||||||
|
if subsuite == "": FAIL
|
||||||
|
|
4
tests/wpt/meta/html/canvas/element/layers/2d.layer.globalCompositeOperation.html.ini
vendored
Normal file
4
tests/wpt/meta/html/canvas/element/layers/2d.layer.globalCompositeOperation.html.ini
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[2d.layer.globalCompositeOperation.html]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": TIMEOUT
|
||||||
|
PASS
|
5
tests/wpt/meta/html/canvas/element/line-styles/2d.line.cross.html.ini
vendored
Normal file
5
tests/wpt/meta/html/canvas/element/line-styles/2d.line.cross.html.ini
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[2d.line.cross.html]
|
||||||
|
[Canvas test: 2d.line.cross]
|
||||||
|
bug: https://github.com/linebender/vello/issues/1063
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": FAIL
|
5
tests/wpt/meta/html/canvas/element/line-styles/2d.line.miter.acute.html.ini
vendored
Normal file
5
tests/wpt/meta/html/canvas/element/line-styles/2d.line.miter.acute.html.ini
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[2d.line.miter.acute.html]
|
||||||
|
[Miter joins are drawn correctly with acute angles]
|
||||||
|
bug: https://github.com/linebender/vello/issues/1063
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": FAIL
|
|
@ -1,3 +1,15 @@
|
||||||
[canvas-createImageBitmap-resize.html]
|
[canvas-createImageBitmap-resize.html]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": TIMEOUT
|
||||||
[createImageBitmap from a HTMLImageElement of svg with no specified size with resize option.]
|
[createImageBitmap from a HTMLImageElement of svg with no specified size with resize option.]
|
||||||
expected: FAIL
|
expected:
|
||||||
|
if subsuite == "vello_canvas": TIMEOUT
|
||||||
|
FAIL
|
||||||
|
|
||||||
|
[createImageBitmap from an ImageBitmap with resize option.]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": NOTRUN
|
||||||
|
|
||||||
|
[createImageBitmap from an ImageData with resize option.]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": NOTRUN
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
[createImageBitmap-drawImage.html]
|
[createImageBitmap-drawImage.html]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": TIMEOUT
|
||||||
[createImageBitmap from a vector HTMLImageElement resized, and drawImage on the created ImageBitmap]
|
[createImageBitmap from a vector HTMLImageElement resized, and drawImage on the created ImageBitmap]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -73,3 +75,39 @@
|
||||||
|
|
||||||
[createImageBitmap from a vector HTMLImageElement scaled up, and drawImage on the created ImageBitmap]
|
[createImageBitmap from a vector HTMLImageElement scaled up, and drawImage on the created ImageBitmap]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
[createImageBitmap from an ImageBitmap scaled down, and drawImage on the created ImageBitmap]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": [PASS, TIMEOUT]
|
||||||
|
|
||||||
|
[createImageBitmap from an ImageBitmap scaled up, and drawImage on the created ImageBitmap]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": [TIMEOUT, NOTRUN]
|
||||||
|
|
||||||
|
[createImageBitmap from an ImageBitmap resized, and drawImage on the created ImageBitmap]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": NOTRUN
|
||||||
|
|
||||||
|
[createImageBitmap from an ImageBitmap with negative sw/sh, and drawImage on the created ImageBitmap]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": NOTRUN
|
||||||
|
|
||||||
|
[createImageBitmap from a Blob, and drawImage on the created ImageBitmap]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": NOTRUN
|
||||||
|
|
||||||
|
[createImageBitmap from a Blob scaled down, and drawImage on the created ImageBitmap]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": NOTRUN
|
||||||
|
|
||||||
|
[createImageBitmap from a Blob scaled up, and drawImage on the created ImageBitmap]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": NOTRUN
|
||||||
|
|
||||||
|
[createImageBitmap from a Blob resized, and drawImage on the created ImageBitmap]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": NOTRUN
|
||||||
|
|
||||||
|
[createImageBitmap from a Blob with negative sw/sh, and drawImage on the created ImageBitmap]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": NOTRUN
|
||||||
|
|
3
tests/wpt/meta/html/canvas/element/manual/text/canvas.2d.lang.dynamic.html.ini
vendored
Normal file
3
tests/wpt/meta/html/canvas/element/manual/text/canvas.2d.lang.dynamic.html.ini
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[canvas.2d.lang.dynamic.html]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": [PASS, FAIL]
|
|
@ -1,4 +1,6 @@
|
||||||
[canvas-display-p3-drawImage-ImageBitmap-Blob.html]
|
[canvas-display-p3-drawImage-ImageBitmap-Blob.html]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": TIMEOUT
|
||||||
[sRGB-FF0000FF.png, Context srgb, ImageData display-p3, cropSource=false]
|
[sRGB-FF0000FF.png, Context srgb, ImageData display-p3, cropSource=false]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
[canvas-display-p3-drawImage-ImageBitmap-ImageBitmap.html]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": TIMEOUT
|
|
@ -1,4 +1,6 @@
|
||||||
[canvas-display-p3-drawImage-ImageBitmap-cloned.html]
|
[canvas-display-p3-drawImage-ImageBitmap-cloned.html]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": TIMEOUT
|
||||||
[sRGB-FF0000FF.png, Context srgb, ImageData display-p3, cropSource=false]
|
[sRGB-FF0000FF.png, Context srgb, ImageData display-p3, cropSource=false]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
[canvas-display-p3-drawImage-ImageBitmap-image.html]
|
[canvas-display-p3-drawImage-ImageBitmap-image.html]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": TIMEOUT
|
||||||
[sRGB-FF0000FF.png, Context srgb, ImageData display-p3, cropSource=false]
|
[sRGB-FF0000FF.png, Context srgb, ImageData display-p3, cropSource=false]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,6 @@
|
||||||
[canvas-display-p3-pattern-image.html]
|
[canvas-display-p3-pattern-image.html]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": TIMEOUT
|
||||||
[sRGB-FF0000FF.png, Context srgb, ImageData display-p3]
|
[sRGB-FF0000FF.png, Context srgb, ImageData display-p3]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
4
tests/wpt/meta/html/canvas/element/path-objects/2d.path.arc.scale.1.html.ini
vendored
Normal file
4
tests/wpt/meta/html/canvas/element/path-objects/2d.path.arc.scale.1.html.ini
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[2d.path.arc.scale.1.html]
|
||||||
|
[Non-uniformly scaled arcs are the right shape]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": FAIL
|
|
@ -1,4 +1,4 @@
|
||||||
[2d.path.arc.scale.2.html]
|
[2d.path.arc.scale.2.html]
|
||||||
[Highly scaled arcs are the right shape]
|
[Highly scaled arcs are the right shape]
|
||||||
expected: FAIL
|
expected:
|
||||||
|
if subsuite == "": FAIL
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[2d.path.arc.selfintersect.1.html]
|
[2d.path.arc.selfintersect.1.html]
|
||||||
[arc() with lineWidth > 2*radius is drawn sensibly]
|
[arc() with lineWidth > 2*radius is drawn sensibly]
|
||||||
expected: FAIL
|
expected:
|
||||||
|
if subsuite == "": FAIL
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[2d.path.arc.selfintersect.2.html]
|
[2d.path.arc.selfintersect.2.html]
|
||||||
[arc() with lineWidth > 2*radius is drawn sensibly]
|
[arc() with lineWidth > 2*radius is drawn sensibly]
|
||||||
expected: FAIL
|
expected:
|
||||||
|
if subsuite == "": FAIL
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[2d.path.arc.shape.3.html]
|
[2d.path.arc.shape.3.html]
|
||||||
[arc() from 0 to -pi/2 does not draw anything in the wrong quadrant]
|
[arc() from 0 to -pi/2 does not draw anything in the wrong quadrant]
|
||||||
expected: FAIL
|
expected:
|
||||||
|
if subsuite == "": FAIL
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[2d.path.arc.shape.4.html]
|
[2d.path.arc.shape.4.html]
|
||||||
[arc() from 0 to -pi/2 draws stuff in the right quadrant]
|
[arc() from 0 to -pi/2 draws stuff in the right quadrant]
|
||||||
expected: FAIL
|
expected:
|
||||||
|
if subsuite == "": FAIL
|
||||||
|
|
5
tests/wpt/meta/html/canvas/element/path-objects/2d.path.clip.intersect.html.ini
vendored
Normal file
5
tests/wpt/meta/html/canvas/element/path-objects/2d.path.clip.intersect.html.ini
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[2d.path.clip.intersect.html]
|
||||||
|
[Canvas test: 2d.path.clip.intersect]
|
||||||
|
bug: https://github.com/linebender/vello/issues/1061
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": FAIL
|
5
tests/wpt/meta/html/canvas/element/path-objects/2d.path.rect.selfintersect.html.ini
vendored
Normal file
5
tests/wpt/meta/html/canvas/element/path-objects/2d.path.rect.selfintersect.html.ini
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[2d.path.rect.selfintersect.html]
|
||||||
|
[Canvas test: 2d.path.rect.selfintersect]
|
||||||
|
bug: https://github.com/linebender/vello/issues/1063#issuecomment-2998084736
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": FAIL
|
4
tests/wpt/meta/html/canvas/element/path-objects/2d.path.stroke.scale2.html.ini
vendored
Normal file
4
tests/wpt/meta/html/canvas/element/path-objects/2d.path.stroke.scale2.html.ini
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[2d.path.stroke.scale2.html]
|
||||||
|
[Stroke line widths are scaled by the current transformation matrix]
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": FAIL
|
5
tests/wpt/meta/html/canvas/element/pixel-manipulation/2d.imageData.put.clip.html.ini
vendored
Normal file
5
tests/wpt/meta/html/canvas/element/pixel-manipulation/2d.imageData.put.clip.html.ini
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[2d.imageData.put.clip.html]
|
||||||
|
[putImageData() is not affected by clipping regions]
|
||||||
|
bug: https://github.com/linebender/vello/issues/1088
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": FAIL
|
|
@ -0,0 +1,5 @@
|
||||||
|
[2d.imageData.put.dirty.negative.html]
|
||||||
|
[putImageData() handles negative-sized dirty rectangles correctly]
|
||||||
|
bug: https://github.com/linebender/vello/issues/1061
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": FAIL
|
5
tests/wpt/meta/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.rect1.html.ini
vendored
Normal file
5
tests/wpt/meta/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.rect1.html.ini
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[2d.imageData.put.dirty.rect1.html]
|
||||||
|
[putImageData() only modifies areas inside the dirty rectangle, using width and height]
|
||||||
|
bug: https://github.com/linebender/vello/issues/1061
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": FAIL
|
5
tests/wpt/meta/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.rect2.html.ini
vendored
Normal file
5
tests/wpt/meta/html/canvas/element/pixel-manipulation/2d.imageData.put.dirty.rect2.html.ini
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[2d.imageData.put.dirty.rect2.html]
|
||||||
|
[putImageData() only modifies areas inside the dirty rectangle, using x and y]
|
||||||
|
bug: https://github.com/linebender/vello/issues/1061
|
||||||
|
expected:
|
||||||
|
if subsuite == "vello_canvas": FAIL
|
|
@ -1,4 +1,4 @@
|
||||||
[2d.imageData.put.unchanged.html]
|
[2d.imageData.put.unchanged.html]
|
||||||
[putImageData(getImageData(...), ...) has no effect]
|
[putImageData(getImageData(...), ...) has no effect]
|
||||||
expected: FAIL
|
expected:
|
||||||
|
if subsuite == "": FAIL
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[2d.transformation.scale.large.html]
|
[2d.transformation.scale.large.html]
|
||||||
[scale() with large scale factors works]
|
[scale() with large scale factors works]
|
||||||
expected: FAIL
|
expected:
|
||||||
|
if subsuite == "": FAIL
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
[2d.layer.globalCompositeOperation.html]
|
[2d.layer.globalCompositeOperation.html]
|
||||||
expected: FAIL
|
expected:
|
||||||
|
FAIL
|
||||||
|
|
8
tests/wpt/vello_canvas_subsuite.json
vendored
Normal file
8
tests/wpt/vello_canvas_subsuite.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"vello_canvas": {
|
||||||
|
"config": {
|
||||||
|
"binary_args": ["--pref", "dom_canvas_vello_enabled"]
|
||||||
|
},
|
||||||
|
"include": ["/html/canvas/element"]
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue