From de69040e47e94c00c244d9f2343d7b87662dee3e Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Wed, 27 Aug 2025 05:19:27 -0700 Subject: [PATCH] canvas: Remove the `raqote` backend (#38962) In general, `raqote` is essentially umaintained and has issues with quality (for instance text rendering has lots of issues) and removing it finally lets us remove our dependency on `font-kit`. Although, `vello_cpu` performance is not yet equal to raqote, rendering quality is a lot better. It's expected that `vello` and `vello_cpu` performance will keep improving. Testing: This is covered by existing WPT tests. Signed-off-by: Martin Robinson --- Cargo.lock | 99 --- components/canvas/Cargo.toml | 3 - components/canvas/backend.rs | 4 +- components/canvas/canvas_data.rs | 4 +- components/canvas/canvas_paint_thread.rs | 80 --- components/canvas/lib.rs | 3 - components/canvas/raqote_backend.rs | 780 ----------------------- components/canvas/vello_backend.rs | 5 +- components/canvas/vello_cpu_backend.rs | 5 +- components/config/prefs.rs | 1 - components/constellation/Cargo.toml | 1 - components/servo/Cargo.toml | 1 - ports/servoshell/Cargo.toml | 1 - 13 files changed, 5 insertions(+), 982 deletions(-) delete mode 100644 components/canvas/raqote_backend.rs diff --git a/Cargo.lock b/Cargo.lock index 703b61a8f7e..92eba3cd870 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1143,7 +1143,6 @@ dependencies = [ "crossbeam-channel", "cssparser", "euclid", - "font-kit", "fonts", "futures-intrusive", "ipc-channel", @@ -1154,7 +1153,6 @@ dependencies = [ "pixels", "pollster", "range", - "raqote", "servo-tracing", "servo_arc", "servo_config", @@ -2677,12 +2675,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" -[[package]] -name = "float-ord" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" - [[package]] name = "fnv" version = "1.0.7" @@ -2695,31 +2687,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" -[[package]] -name = "font-kit" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c7e611d49285d4c4b2e1727b72cf05353558885cc5252f93707b845dfcaf3d3" -dependencies = [ - "bitflags 2.9.3", - "byteorder", - "core-foundation 0.9.4", - "core-graphics", - "core-text", - "dirs", - "dwrote", - "float-ord", - "freetype-sys", - "lazy_static", - "libc", - "log", - "pathfinder_geometry", - "pathfinder_simd", - "walkdir", - "winapi", - "yeslogic-fontconfig-sys", -] - [[package]] name = "font-types" version = "0.9.0" @@ -5166,17 +5133,6 @@ dependencies = [ "imgref", ] -[[package]] -name = "lyon_geom" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8af69edc087272df438b3ee436c4bb6d7c04aa8af665cfd398feae627dbd8570" -dependencies = [ - "arrayvec", - "euclid", - "num-traits", -] - [[package]] name = "mac" version = "0.1.1" @@ -6402,25 +6358,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "pathfinder_geometry" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" -dependencies = [ - "log", - "pathfinder_simd", -] - -[[package]] -name = "pathfinder_simd" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf9027960355bf3afff9841918474a81a5f972ac6d226d518060bba758b5ad57" -dependencies = [ - "rustc_version", -] - [[package]] name = "peek-poke" version = "0.3.0" @@ -6980,21 +6917,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" -[[package]] -name = "raqote" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324990858d2a5df9ccd30b2e8b474030bd503297378a3fc0944c37af00eb8903" -dependencies = [ - "euclid", - "font-kit", - "lyon_geom", - "pathfinder_geometry", - "png", - "sw-composite", - "typed-arena", -] - [[package]] name = "rav1e" version = "0.7.1" @@ -7260,15 +7182,6 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - [[package]] name = "rustix" version = "0.38.44" @@ -8623,12 +8536,6 @@ dependencies = [ "siphasher", ] -[[package]] -name = "sw-composite" -version = "0.7.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac8fb7895b4afa060ad731a32860db8755da3449a47e796d5ecf758db2671d4" - [[package]] name = "swapper" version = "0.1.0" @@ -9238,12 +9145,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "typed-arena" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" - [[package]] name = "typeid" version = "1.0.3" diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index ef0c84977f2..1401fc1c000 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -14,7 +14,6 @@ path = "lib.rs" [features] vello = ["dep:vello", "dep:pollster", "dep:futures-intrusive", "dep:peniko"] vello_cpu = ["dep:vello_cpu", "dep:peniko"] -raqote = ["dep:raqote", "dep:font-kit"] tracing = ["dep:tracing"] [dependencies] @@ -25,7 +24,6 @@ compositing_traits = { workspace = true } crossbeam-channel = { workspace = true } cssparser = { workspace = true } euclid = { workspace = true } -font-kit = { version = "0.14", optional = true } fonts = { path = "../fonts" } ipc-channel = { workspace = true } kurbo = { workspace = true } @@ -34,7 +32,6 @@ net_traits = { workspace = true } peniko = { workspace = true, optional = true } pixels = { path = "../pixels" } range = { path = "../range" } -raqote = { version = "0.8.5", optional = true } servo_arc = { workspace = true } stylo = { workspace = true } unicode-script = { workspace = true } diff --git a/components/canvas/backend.rs b/components/canvas/backend.rs index 0a89ad7a0c3..f4f50f77337 100644 --- a/components/canvas/backend.rs +++ b/components/canvas/backend.rs @@ -12,8 +12,8 @@ use webrender_api::ImageDescriptor; use crate::canvas_data::{Filter, TextRun}; -// This defines required methods for a DrawTarget (currently only implemented for raqote). The -// prototypes are derived from the now-removed Azure backend's methods. +// This defines required methods for a DrawTarget. The prototypes are derived from the now-removed +// Azure backend's methods. pub(crate) trait GenericDrawTarget { type SourceSurface; diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index e4e9685cea8..221ff38c42a 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -169,7 +169,6 @@ impl CanvasData { Size2D::new(dest_rect.size.width as f32, dest_rect.size.height as f32), ); - // TODO(pylbrecht) pass another closure for raqote self.draw_with_shadow( &rect, shadow_options, @@ -411,8 +410,7 @@ impl CanvasData { let mut current_text_run_start_index = 0; for (index, character) in text.char_indices() { - // TODO: This should ultimately handle emoji variation selectors, but raqote does not yet - // have support for color glyphs. + // TODO: This should ultimately handle emoji variation selectors. let script = Script::from(character); let font = font_group.find_by_codepoint(&self.font_context, character, None, None); diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs index e4a8962d7eb..910f6d303d2 100644 --- a/components/canvas/canvas_paint_thread.rs +++ b/components/canvas/canvas_paint_thread.rs @@ -295,8 +295,6 @@ impl CanvasPaintThread { #[allow(clippy::large_enum_variant)] enum Canvas { - #[cfg(feature = "raqote")] - Raqote(CanvasData), #[cfg(feature = "vello")] Vello(CanvasData), #[cfg(feature = "vello_cpu")] @@ -319,12 +317,6 @@ impl Canvas { compositor_api, font_context, ))), - #[cfg(feature = "raqote")] - "" | "auto" | "raqote" => Some(Self::Raqote(CanvasData::new( - size, - compositor_api, - font_context, - ))), #[cfg(feature = "vello")] "" | "auto" | "vello" => Some(Self::Vello(CanvasData::new( size, @@ -340,8 +332,6 @@ impl Canvas { fn image_key(&self) -> ImageKey { match self { - #[cfg(feature = "raqote")] - Canvas::Raqote(canvas_data) => canvas_data.image_key(), #[cfg(feature = "vello")] Canvas::Vello(canvas_data) => canvas_data.image_key(), #[cfg(feature = "vello_cpu")] @@ -351,8 +341,6 @@ impl Canvas { fn pop_clips(&mut self, clips: usize) { match self { - #[cfg(feature = "raqote")] - Canvas::Raqote(canvas_data) => canvas_data.pop_clips(clips), #[cfg(feature = "vello")] Canvas::Vello(canvas_data) => canvas_data.pop_clips(clips), #[cfg(feature = "vello_cpu")] @@ -375,19 +363,6 @@ impl Canvas { transform: Transform2D, ) { match self { - #[cfg(feature = "raqote")] - Canvas::Raqote(canvas_data) => canvas_data.fill_text( - text, - x, - y, - max_width, - is_rtl, - style, - text_options, - shadow_options, - composition_options, - transform, - ), #[cfg(feature = "vello")] Canvas::Vello(canvas_data) => canvas_data.fill_text( text, @@ -426,10 +401,6 @@ impl Canvas { transform: Transform2D, ) { match self { - #[cfg(feature = "raqote")] - Canvas::Raqote(canvas_data) => { - 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) @@ -451,15 +422,6 @@ impl Canvas { transform: Transform2D, ) { match self { - #[cfg(feature = "raqote")] - Canvas::Raqote(canvas_data) => canvas_data.stroke_rect( - rect, - style, - line_options, - shadow_options, - composition_options, - transform, - ), #[cfg(feature = "vello")] Canvas::Vello(canvas_data) => canvas_data.stroke_rect( rect, @@ -491,15 +453,6 @@ impl Canvas { transform: Transform2D, ) { match self { - #[cfg(feature = "raqote")] - Canvas::Raqote(canvas_data) => canvas_data.fill_path( - path, - fill_rule, - style, - shadow_options, - composition_options, - transform, - ), #[cfg(feature = "vello")] Canvas::Vello(canvas_data) => canvas_data.fill_path( path, @@ -531,15 +484,6 @@ impl Canvas { transform: Transform2D, ) { match self { - #[cfg(feature = "raqote")] - Canvas::Raqote(canvas_data) => canvas_data.stroke_path( - path, - style, - line_options, - shadow_options, - composition_options, - transform, - ), #[cfg(feature = "vello")] Canvas::Vello(canvas_data) => canvas_data.stroke_path( path, @@ -563,8 +507,6 @@ impl Canvas { fn clear_rect(&mut self, rect: &Rect, transform: Transform2D) { match self { - #[cfg(feature = "raqote")] - Canvas::Raqote(canvas_data) => canvas_data.clear_rect(rect, transform), #[cfg(feature = "vello")] Canvas::Vello(canvas_data) => canvas_data.clear_rect(rect, transform), #[cfg(feature = "vello_cpu")] @@ -584,16 +526,6 @@ impl Canvas { transform: Transform2D, ) { match self { - #[cfg(feature = "raqote")] - Canvas::Raqote(canvas_data) => canvas_data.draw_image( - snapshot, - dest_rect, - source_rect, - smoothing_enabled, - shadow_options, - composition_options, - transform, - ), #[cfg(feature = "vello")] Canvas::Vello(canvas_data) => canvas_data.draw_image( snapshot, @@ -619,8 +551,6 @@ impl Canvas { fn read_pixels(&mut self, read_rect: Option>) -> Snapshot { match self { - #[cfg(feature = "raqote")] - Canvas::Raqote(canvas_data) => canvas_data.read_pixels(read_rect), #[cfg(feature = "vello")] Canvas::Vello(canvas_data) => canvas_data.read_pixels(read_rect), #[cfg(feature = "vello_cpu")] @@ -630,8 +560,6 @@ impl Canvas { fn measure_text(&mut self, text: String, text_options: TextOptions) -> TextMetrics { match self { - #[cfg(feature = "raqote")] - 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), #[cfg(feature = "vello_cpu")] @@ -641,8 +569,6 @@ impl Canvas { fn clip_path(&mut self, path: &Path, fill_rule: FillRule, transform: Transform2D) { match self { - #[cfg(feature = "raqote")] - Canvas::Raqote(canvas_data) => canvas_data.clip_path(path, fill_rule, transform), #[cfg(feature = "vello")] Canvas::Vello(canvas_data) => canvas_data.clip_path(path, fill_rule, transform), #[cfg(feature = "vello_cpu")] @@ -652,8 +578,6 @@ impl Canvas { fn put_image_data(&mut self, snapshot: Snapshot, rect: Rect) { match self { - #[cfg(feature = "raqote")] - 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), #[cfg(feature = "vello_cpu")] @@ -663,8 +587,6 @@ impl Canvas { fn update_image_rendering(&mut self) { match self { - #[cfg(feature = "raqote")] - Canvas::Raqote(canvas_data) => canvas_data.update_image_rendering(), #[cfg(feature = "vello")] Canvas::Vello(canvas_data) => canvas_data.update_image_rendering(), #[cfg(feature = "vello_cpu")] @@ -674,8 +596,6 @@ impl Canvas { fn recreate(&mut self, size: Option>) { match self { - #[cfg(feature = "raqote")] - Canvas::Raqote(canvas_data) => canvas_data.recreate(size), #[cfg(feature = "vello")] Canvas::Vello(canvas_data) => canvas_data.recreate(size), #[cfg(feature = "vello_cpu")] diff --git a/components/canvas/lib.rs b/components/canvas/lib.rs index 6ca631a9b85..cbfe89b04a1 100644 --- a/components/canvas/lib.rs +++ b/components/canvas/lib.rs @@ -6,9 +6,6 @@ mod backend; -#[cfg(feature = "raqote")] -mod raqote_backend; - #[cfg(any(feature = "vello", feature = "vello_cpu"))] mod peniko_conversions; diff --git a/components/canvas/raqote_backend.rs b/components/canvas/raqote_backend.rs deleted file mode 100644 index 87ed0e3ff8b..00000000000 --- a/components/canvas/raqote_backend.rs +++ /dev/null @@ -1,780 +0,0 @@ -/* 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/. */ - -use std::cell::{Cell, RefCell}; -use std::collections::HashMap; - -use canvas_traits::canvas::*; -use compositing_traits::SerializableImageData; -use cssparser::color::clamp_unit_f32; -use euclid::default::{Point2D, Rect, Size2D, Transform2D}; -use font_kit::font::Font; -use fonts::{ByteIndex, FontIdentifier, FontTemplateRefMethods}; -use ipc_channel::ipc::IpcSharedMemory; -use log::warn; -use pixels::{Snapshot, SnapshotAlphaMode, SnapshotPixelFormat}; -use range::Range; -use raqote::{DrawOptions, PathBuilder, StrokeStyle}; -use style::color::AbsoluteColor; -use webrender_api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat}; - -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> = RefCell::default(); -} - -#[derive(Clone)] -pub enum Pattern { - // argb - Color(u8, u8, u8, u8), - LinearGradient(LinearGradientPattern), - RadialGradient(RadialGradientPattern), - Surface(SurfacePattern), -} - -#[derive(Clone)] -pub struct LinearGradientPattern { - gradient: raqote::Gradient, - start: Point2D, - end: Point2D, -} - -impl LinearGradientPattern { - fn new(start: Point2D, end: Point2D, stops: Vec) -> Self { - LinearGradientPattern { - gradient: raqote::Gradient { stops }, - start, - end, - } - } -} - -#[derive(Clone)] -pub struct RadialGradientPattern { - gradient: raqote::Gradient, - center1: Point2D, - radius1: f32, - center2: Point2D, - radius2: f32, -} - -impl RadialGradientPattern { - fn new( - center1: Point2D, - radius1: f32, - center2: Point2D, - radius2: f32, - stops: Vec, - ) -> Self { - RadialGradientPattern { - gradient: raqote::Gradient { stops }, - center1, - radius1, - center2, - radius2, - } - } -} - -#[derive(Clone)] -pub struct SurfacePattern { - image: Snapshot, - filter: raqote::FilterMode, - extend: raqote::ExtendMode, - transform: Transform2D, -} - -impl SurfacePattern { - fn new( - image: Snapshot, - filter: raqote::FilterMode, - repeat: Repetition, - transform: Transform2D, - ) -> Self { - let extend = match repeat { - Repetition::NoRepeat => raqote::ExtendMode::Pad, - Repetition::RepeatX | Repetition::RepeatY | Repetition::Repeat => { - raqote::ExtendMode::Repeat - }, - }; - SurfacePattern { - image, - filter, - extend, - transform, - } - } -} - -#[derive(Clone, Copy, Debug)] -pub enum Repetition { - Repeat, - RepeatX, - RepeatY, - NoRepeat, -} - -impl Repetition { - fn from_xy(repeat_x: bool, repeat_y: bool) -> Self { - if repeat_x && repeat_y { - Repetition::Repeat - } else if repeat_x { - Repetition::RepeatX - } else if repeat_y { - Repetition::RepeatY - } else { - Repetition::NoRepeat - } - } -} - -pub fn source(pattern: &Pattern) -> raqote::Source<'_> { - match pattern { - Pattern::Color(a, r, g, b) => raqote::Source::Solid( - raqote::SolidSource::from_unpremultiplied_argb(*a, *r, *g, *b), - ), - Pattern::LinearGradient(pattern) => raqote::Source::new_linear_gradient( - pattern.gradient.clone(), - pattern.start, - pattern.end, - raqote::Spread::Pad, - ), - Pattern::RadialGradient(pattern) => raqote::Source::new_two_circle_radial_gradient( - pattern.gradient.clone(), - pattern.center1, - pattern.radius1, - pattern.center2, - pattern.radius2, - raqote::Spread::Pad, - ), - Pattern::Surface(pattern) => raqote::Source::Image( - raqote::Image { - width: pattern.image.size().width as i32, - height: pattern.image.size().height as i32, - data: bytemuck::cast_slice(pattern.image.as_raw_bytes()), - }, - pattern.extend, - pattern.filter, - pattern.transform, - ), - } -} - -fn create_gradient_stops(gradient_stops: Vec) -> Vec { - let mut stops = gradient_stops - .into_iter() - .map(|item| item.to_raqote()) - .collect::>(); - // https://www.w3.org/html/test/results/2dcontext/annotated-spec/canvas.html#testrefs.2d.gradient.interpolate.overlap - stops.sort_by(|a, b| a.position.partial_cmp(&b.position).unwrap()); - stops -} - -impl GenericDrawTarget for raqote::DrawTarget { - type SourceSurface = Vec; // TODO: See if we can avoid the alloc (probably?) - - fn new(size: Size2D) -> Self { - raqote::DrawTarget::new(size.width as i32, size.height as i32) - } - - fn clear_rect(&mut self, rect: &Rect, transform: Transform2D) { - ::fill_rect( - self, - rect, - FillOrStrokeStyle::Color(AbsoluteColor::TRANSPARENT_BLACK), - CompositionOptions { - alpha: 1.0, - composition_operation: CompositionOrBlending::Composition(CompositionStyle::Clear), - }, - transform, - ); - } - fn copy_surface( - &mut self, - surface: Self::SourceSurface, - source: Rect, - destination: Point2D, - ) { - let dt = raqote::DrawTarget::from_vec(source.size.width, source.size.height, surface); - raqote::DrawTarget::copy_surface(self, &dt, source.to_box2d(), destination); - } - - fn create_similar_draw_target(&self, size: &Size2D) -> Self { - raqote::DrawTarget::new(size.width, size.height) - } - fn create_source_surface_from_data(&self, data: Snapshot) -> Option { - Some( - bytemuck::try_cast_vec( - data.to_vec( - Some(SnapshotAlphaMode::Transparent { - premultiplied: true, - }), - Some(SnapshotPixelFormat::BGRA), - ) - .0, - ) - .unwrap_or_else(|(_, surface)| bytemuck::pod_collect_to_vec(&surface)), - ) - } - fn draw_surface( - &mut self, - surface: Self::SourceSurface, - dest: Rect, - src: Rect, - filter: Filter, - composition_options: CompositionOptions, - transform: Transform2D, - ) { - let paint_transform = - raqote::Transform::translation(-dest.origin.x as f32, -dest.origin.y as f32) - .then_scale( - src.size.width as f32 / dest.size.width as f32, - src.size.height as f32 / dest.size.height as f32, - ); - - self.set_transform(&transform.cast()); - let dest = dest.cast(); - let mut pb = raqote::PathBuilder::new(); - pb.rect( - dest.origin.x, - dest.origin.y, - dest.size.width, - dest.size.height, - ); - let size = src.size.cast(); - fill_draw_target( - self, - draw_options(composition_options), - &raqote::Source::Image( - raqote::Image { - width: size.width, - height: size.height, - data: &surface, - }, - raqote::ExtendMode::Pad, - filter.to_raqote(), - paint_transform, - ), - pb.finish(), - ); - } - fn draw_surface_with_shadow( - &self, - _surface: Self::SourceSurface, - _dest: &Point2D, - _shadow_options: ShadowOptions, - _composition_options: CompositionOptions, - ) { - warn!("no support for drawing shadows"); - } - fn fill( - &mut self, - path: &canvas_traits::canvas::Path, - fill_rule: FillRule, - style: FillOrStrokeStyle, - composition_options: CompositionOptions, - transform: Transform2D, - ) { - self.set_transform(&transform.cast()); - let draw_options = draw_options(composition_options); - let pattern = style.to_raqote_pattern(); - let mut path = to_path(path); - path.winding = match fill_rule { - FillRule::Nonzero => raqote::Winding::NonZero, - FillRule::Evenodd => raqote::Winding::EvenOdd, - }; - fill_draw_target(self, draw_options, &source(&pattern), path); - } - - fn fill_text( - &mut self, - text_runs: Vec, - start: Point2D, - style: FillOrStrokeStyle, - composition_options: CompositionOptions, - transform: Transform2D, - ) { - self.set_transform(&transform.cast()); - let draw_options = draw_options(composition_options); - let pattern = style.to_raqote_pattern(); - let mut advance = 0.; - for run in text_runs.iter() { - let mut positions = Vec::new(); - let glyphs = &run.glyphs; - let ids: Vec<_> = glyphs - .iter_glyphs_for_byte_range(&Range::new(ByteIndex(0), glyphs.len())) - .map(|glyph| { - let glyph_offset = glyph.offset().unwrap_or(Point2D::zero()); - positions.push(Point2D::new( - advance + start.x + glyph_offset.x.to_f32_px(), - start.y + glyph_offset.y.to_f32_px(), - )); - advance += glyph.advance().to_f32_px(); - glyph.id() - }) - .collect(); - - // TODO: raqote uses font-kit to rasterize glyphs, but font-kit fails an assertion when - // using color bitmap fonts in the FreeType backend. For now, simply do not render these - // type of fonts. - if run.font.has_color_bitmap_or_colr_table() { - continue; - } - - let template = &run.font.template; - - SHARED_FONT_CACHE.with(|font_cache| { - let identifier = template.identifier(); - if !font_cache.borrow().contains_key(&identifier) { - let Ok(font_data_and_index) = run.font.font_data_and_index() else { - return; - }; - let data = std::sync::Arc::new(font_data_and_index.data.as_ref().to_vec()); - let Ok(font) = Font::from_bytes(data, font_data_and_index.index) else { - return; - }; - font_cache.borrow_mut().insert(identifier.clone(), font); - } - - let font_cache = font_cache.borrow(); - let Some(font) = font_cache.get(&identifier) else { - return; - }; - - self.draw_glyphs( - font, - run.font.descriptor.pt_size.to_f32_px(), - &ids, - &positions, - &source(&pattern), - &draw_options, - ); - }) - } - } - - fn fill_rect( - &mut self, - rect: &Rect, - style: FillOrStrokeStyle, - composition_options: CompositionOptions, - transform: Transform2D, - ) { - let rect = rect.cast(); - let mut pb = canvas_traits::canvas::Path::new(); - pb.rect( - rect.origin.x, - rect.origin.y, - rect.size.width, - rect.size.height, - ); - - ::fill( - self, - &pb, - FillRule::Nonzero, - style, - composition_options, - transform, - ); - } - fn get_size(&self) -> Size2D { - Size2D::new(self.width(), self.height()) - } - fn pop_clip(&mut self) { - self.pop_clip(); - } - fn push_clip( - &mut self, - path: &canvas_traits::canvas::Path, - fill_rule: FillRule, - transform: Transform2D, - ) { - self.set_transform(&transform.cast()); - let mut path = to_path(path); - path.winding = match fill_rule { - FillRule::Nonzero => raqote::Winding::NonZero, - FillRule::Evenodd => raqote::Winding::EvenOdd, - }; - self.push_clip(&path); - } - fn push_clip_rect(&mut self, rect: &Rect) { - self.push_clip_rect(rect.to_box2d()); - } - fn surface(&mut self) -> Self::SourceSurface { - self.get_data().to_vec() - } - fn stroke( - &mut self, - path: &canvas_traits::canvas::Path, - style: FillOrStrokeStyle, - line_options: LineOptions, - composition_options: CompositionOptions, - transform: Transform2D, - ) { - let pattern = style.to_raqote_pattern(); - let options = draw_options(composition_options); - self.set_transform(&transform.cast()); - self.stroke( - &to_path(path), - &source(&pattern), - &line_options.to_raqote_style(), - &options, - ); - } - fn stroke_rect( - &mut self, - rect: &Rect, - style: FillOrStrokeStyle, - line_options: LineOptions, - composition_options: CompositionOptions, - transform: Transform2D, - ) { - self.set_transform(&transform.cast()); - let pattern = style.to_raqote_pattern(); - let options = draw_options(composition_options); - let mut pb = raqote::PathBuilder::new(); - pb.rect( - rect.origin.x, - rect.origin.y, - rect.size.width, - rect.size.height, - ); - - self.stroke( - &pb.finish(), - &source(&pattern), - &line_options.to_raqote_style(), - &options, - ); - } - - fn image_descriptor_and_serializable_data( - &mut self, - ) -> ( - webrender_api::ImageDescriptor, - compositing_traits::SerializableImageData, - ) { - let descriptor = ImageDescriptor { - size: self.get_size().cast_unit(), - stride: None, - format: ImageFormat::BGRA8, - offset: 0, - flags: ImageDescriptorFlags::empty(), - }; - let data = SerializableImageData::Raw(IpcSharedMemory::from_bytes(self.get_data_u8())); - (descriptor, data) - } - - fn snapshot(&mut self) -> Snapshot { - Snapshot::from_vec( - self.get_size().cast(), - SnapshotPixelFormat::BGRA, - SnapshotAlphaMode::Transparent { - premultiplied: true, - }, - self.get_data_u8().to_vec(), - ) - } -} - -fn fill_draw_target( - draw_target: &mut raqote::DrawTarget, - draw_options: DrawOptions, - source: &raqote::Source<'_>, - path: raqote::Path, -) { - match draw_options.blend_mode { - raqote::BlendMode::Src => { - draw_target.clear(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0)); - draw_target.fill(&path, source, &draw_options); - }, - raqote::BlendMode::Clear | - raqote::BlendMode::SrcAtop | - raqote::BlendMode::DstOut | - raqote::BlendMode::Add | - raqote::BlendMode::Xor | - raqote::BlendMode::DstOver | - raqote::BlendMode::SrcOver => { - draw_target.fill(&path, source, &draw_options); - }, - raqote::BlendMode::SrcIn | - raqote::BlendMode::SrcOut | - raqote::BlendMode::DstIn | - raqote::BlendMode::DstAtop => { - let mut options = draw_options; - draw_target.push_layer_with_blend(1., options.blend_mode); - options.blend_mode = raqote::BlendMode::SrcOver; - draw_target.fill(&path, source, &options); - draw_target.pop_layer(); - }, - _ => warn!("unrecognized blend mode: {:?}", draw_options.blend_mode), - } -} - -impl Filter { - fn to_raqote(self) -> raqote::FilterMode { - match self { - Filter::Bilinear => raqote::FilterMode::Bilinear, - Filter::Nearest => raqote::FilterMode::Nearest, - } - } -} - -pub(crate) struct Path(Cell); - -impl From for Path { - fn from(path_builder: raqote::PathBuilder) -> Self { - Self(Cell::new(path_builder)) - } -} - -impl From<&Path> for raqote::Path { - fn from(path: &Path) -> Self { - path.clone().0.into_inner().finish() - } -} - -impl Clone for Path { - fn clone(&self) -> Self { - let path_builder = self.0.replace(raqote::PathBuilder::new()); - let path = path_builder.finish(); - self.0.set(path.clone().into()); - Self(Cell::new(path.into())) - } -} - -pub trait ToRaqoteStyle { - type Target; - - fn to_raqote_style(self) -> Self::Target; -} - -impl ToRaqoteStyle for LineOptions { - type Target = StrokeStyle; - - fn to_raqote_style(self) -> Self::Target { - let LineOptions { - width, - cap_style, - join_style, - miter_limit, - dash, - dash_offset, - } = self; - StrokeStyle { - width: width as f32, - cap: cap_style.to_raqote_style(), - join: join_style.to_raqote_style(), - miter_limit: miter_limit as f32, - dash_array: dash, - dash_offset: dash_offset as f32, - } - } -} - -impl ToRaqoteStyle for LineJoinStyle { - type Target = raqote::LineJoin; - - fn to_raqote_style(self) -> raqote::LineJoin { - match self { - LineJoinStyle::Round => raqote::LineJoin::Round, - LineJoinStyle::Bevel => raqote::LineJoin::Bevel, - LineJoinStyle::Miter => raqote::LineJoin::Miter, - } - } -} - -impl ToRaqoteStyle for LineCapStyle { - type Target = raqote::LineCap; - - fn to_raqote_style(self) -> raqote::LineCap { - match self { - LineCapStyle::Butt => raqote::LineCap::Butt, - LineCapStyle::Round => raqote::LineCap::Round, - LineCapStyle::Square => raqote::LineCap::Square, - } - } -} - -pub trait ToRaqotePattern { - fn to_raqote_pattern(self) -> Pattern; -} - -pub trait ToRaqoteGradientStop { - fn to_raqote(self) -> raqote::GradientStop; -} - -impl ToRaqoteGradientStop for CanvasGradientStop { - fn to_raqote(self) -> raqote::GradientStop { - let srgb = self.color.into_srgb_legacy(); - let color = raqote::Color::new( - clamp_unit_f32(srgb.alpha), - clamp_unit_f32(srgb.components.0), - clamp_unit_f32(srgb.components.1), - clamp_unit_f32(srgb.components.2), - ); - let position = self.offset as f32; - raqote::GradientStop { position, color } - } -} - -impl ToRaqotePattern for FillOrStrokeStyle { - fn to_raqote_pattern(self) -> Pattern { - use canvas_traits::canvas::FillOrStrokeStyle::*; - - match self { - Color(color) => { - let srgb = color.into_srgb_legacy(); - Pattern::Color( - clamp_unit_f32(srgb.alpha), - clamp_unit_f32(srgb.components.0), - clamp_unit_f32(srgb.components.1), - clamp_unit_f32(srgb.components.2), - ) - }, - LinearGradient(style) => { - let start = Point2D::new(style.x0 as f32, style.y0 as f32); - let end = Point2D::new(style.x1 as f32, style.y1 as f32); - let stops = create_gradient_stops(style.stops); - Pattern::LinearGradient(LinearGradientPattern::new(start, end, stops)) - }, - RadialGradient(style) => { - let center1 = Point2D::new(style.x0 as f32, style.y0 as f32); - let center2 = Point2D::new(style.x1 as f32, style.y1 as f32); - let stops = create_gradient_stops(style.stops); - Pattern::RadialGradient(RadialGradientPattern::new( - center1, - style.r0 as f32, - center2, - style.r1 as f32, - stops, - )) - }, - Surface(style) => { - let repeat = Repetition::from_xy(style.repeat_x, style.repeat_y); - let mut snapshot = style.surface_data.to_owned(); - snapshot.transform( - SnapshotAlphaMode::Transparent { - premultiplied: true, - }, - SnapshotPixelFormat::BGRA, - ); - Pattern::Surface(SurfacePattern::new( - snapshot, - raqote::FilterMode::Nearest, - repeat, - style.transform, - )) - }, - } - } -} - -impl ToRaqoteStyle for AbsoluteColor { - type Target = raqote::SolidSource; - - fn to_raqote_style(self) -> Self::Target { - let srgb = self.into_srgb_legacy(); - raqote::SolidSource::from_unpremultiplied_argb( - clamp_unit_f32(srgb.alpha), - clamp_unit_f32(srgb.components.0), - clamp_unit_f32(srgb.components.1), - clamp_unit_f32(srgb.components.2), - ) - } -} - -impl ToRaqoteStyle for CompositionOrBlending { - type Target = raqote::BlendMode; - - fn to_raqote_style(self) -> Self::Target { - match self { - CompositionOrBlending::Composition(op) => op.to_raqote_style(), - CompositionOrBlending::Blending(op) => op.to_raqote_style(), - } - } -} - -impl ToRaqoteStyle for BlendingStyle { - type Target = raqote::BlendMode; - - fn to_raqote_style(self) -> Self::Target { - match self { - BlendingStyle::Multiply => raqote::BlendMode::Multiply, - BlendingStyle::Screen => raqote::BlendMode::Screen, - BlendingStyle::Overlay => raqote::BlendMode::Overlay, - BlendingStyle::Darken => raqote::BlendMode::Darken, - BlendingStyle::Lighten => raqote::BlendMode::Lighten, - BlendingStyle::ColorDodge => raqote::BlendMode::ColorDodge, - BlendingStyle::HardLight => raqote::BlendMode::HardLight, - BlendingStyle::SoftLight => raqote::BlendMode::SoftLight, - BlendingStyle::Difference => raqote::BlendMode::Difference, - BlendingStyle::Exclusion => raqote::BlendMode::Exclusion, - BlendingStyle::Hue => raqote::BlendMode::Hue, - BlendingStyle::Saturation => raqote::BlendMode::Saturation, - BlendingStyle::Color => raqote::BlendMode::Color, - BlendingStyle::Luminosity => raqote::BlendMode::Luminosity, - BlendingStyle::ColorBurn => raqote::BlendMode::ColorBurn, - } - } -} - -impl ToRaqoteStyle for CompositionStyle { - type Target = raqote::BlendMode; - - fn to_raqote_style(self) -> Self::Target { - match self { - CompositionStyle::SourceIn => raqote::BlendMode::SrcIn, - CompositionStyle::SourceOut => raqote::BlendMode::SrcOut, - CompositionStyle::SourceOver => raqote::BlendMode::SrcOver, - CompositionStyle::SourceAtop => raqote::BlendMode::SrcAtop, - CompositionStyle::DestinationIn => raqote::BlendMode::DstIn, - CompositionStyle::DestinationOut => raqote::BlendMode::DstOut, - CompositionStyle::DestinationOver => raqote::BlendMode::DstOver, - CompositionStyle::DestinationAtop => raqote::BlendMode::DstAtop, - CompositionStyle::Copy => raqote::BlendMode::Src, - CompositionStyle::Lighter => raqote::BlendMode::Add, - CompositionStyle::Xor => raqote::BlendMode::Xor, - CompositionStyle::Clear => raqote::BlendMode::Clear, - } - } -} - -fn to_path(path: &canvas_traits::canvas::Path) -> raqote::Path { - let mut pb = PathBuilder::new(); - for cmd in &path.0 { - match cmd { - kurbo::PathEl::MoveTo(kurbo::Point { x, y }) => pb.move_to(x as f32, y as f32), - kurbo::PathEl::LineTo(kurbo::Point { x, y }) => pb.line_to(x as f32, y as f32), - kurbo::PathEl::QuadTo(cp, p) => { - pb.quad_to(cp.x as f32, cp.y as f32, p.x as f32, p.y as f32) - }, - kurbo::PathEl::CurveTo(cp1, cp2, p) => pb.cubic_to( - cp1.x as f32, - cp1.y as f32, - cp2.x as f32, - cp2.y as f32, - p.x as f32, - p.y as f32, - ), - kurbo::PathEl::ClosePath => pb.close(), - } - } - pb.finish() -} - -fn draw_options(composition_options: CompositionOptions) -> DrawOptions { - DrawOptions { - blend_mode: composition_options.composition_operation.to_raqote_style(), - alpha: composition_options.alpha as f32, - ..Default::default() - } -} diff --git a/components/canvas/vello_backend.rs b/components/canvas/vello_backend.rs index 7ef8572c422..6a603c3e3dd 100644 --- a/components/canvas/vello_backend.rs +++ b/components/canvas/vello_backend.rs @@ -41,10 +41,7 @@ use crate::backend::{Convert as _, 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. + /// The shared font cache used by all canvases that render on a thread. static SHARED_FONT_CACHE: RefCell> = RefCell::default(); } diff --git a/components/canvas/vello_cpu_backend.rs b/components/canvas/vello_cpu_backend.rs index 97074778922..138befb7070 100644 --- a/components/canvas/vello_cpu_backend.rs +++ b/components/canvas/vello_cpu_backend.rs @@ -24,10 +24,7 @@ use crate::backend::{Convert, 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. + /// The shared font cache used by all canvases that render on a thread. static SHARED_FONT_CACHE: RefCell> = RefCell::default(); } diff --git a/components/config/prefs.rs b/components/config/prefs.rs index eb5a9e3f9e4..8b3c5e86788 100644 --- a/components/config/prefs.rs +++ b/components/config/prefs.rs @@ -100,7 +100,6 @@ pub struct Preferences { /// /// Available values: /// - ` `/`auto` - /// - raqote /// - vello /// - vello_cpu pub dom_canvas_backend: String, diff --git a/components/constellation/Cargo.toml b/components/constellation/Cargo.toml index e149947f2f1..5aaebc3c004 100644 --- a/components/constellation/Cargo.toml +++ b/components/constellation/Cargo.toml @@ -18,7 +18,6 @@ tracing = ["dep:tracing", "canvas/tracing"] webgpu = ["script_traits/webgpu"] vello = ["canvas/vello"] vello_cpu = ["canvas/vello_cpu"] -raqote = ["canvas/raqote"] [lints.clippy] unwrap_used = "deny" diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml index 6fd7eb12c1c..6308dd8b3a0 100644 --- a/components/servo/Cargo.toml +++ b/components/servo/Cargo.toml @@ -63,7 +63,6 @@ webgpu = [ ] vello = ["constellation/vello"] vello_cpu = ["constellation/vello_cpu"] -raqote = ["constellation/raqote"] [dependencies] background_hang_monitor = { path = "../background_hang_monitor" } diff --git a/ports/servoshell/Cargo.toml b/ports/servoshell/Cargo.toml index 93199cb4917..20d14a6df23 100644 --- a/ports/servoshell/Cargo.toml +++ b/ports/servoshell/Cargo.toml @@ -53,7 +53,6 @@ webgpu = ["libservo/webgpu"] webxr = ["libservo/webxr"] vello = ["libservo/vello"] vello_cpu = ["libservo/vello_cpu"] -raqote = ["libservo/raqote"] [dependencies] cfg-if = { workspace = true }