canvas: Remove Backend trait (#38262)

After #38214 `Backend` trait is only used as DrawTarget builder and as
type holder. By moving types and DrawTarget creation into
`GenericDrawTarget` trait, we can completely remove `Backend` trait.

Testing: Just refactor, but code is covered by WPT tests.

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
sagudev 2025-07-25 14:05:03 +02:00 committed by GitHub
parent 5cd57f9dba
commit 420a5a64a7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 46 additions and 77 deletions

View file

@ -12,28 +12,25 @@ use webrender_api::ImageDescriptor;
use crate::canvas_data::{Filter, TextRun};
pub(crate) trait Backend: Clone + Sized {
type DrawTarget: GenericDrawTarget<Self>;
type SourceSurface;
fn create_drawtarget(&self, size: Size2D<u64>) -> Self::DrawTarget;
}
// This defines required methods for a DrawTarget (currently only implemented for raqote). The
// prototypes are derived from the now-removed Azure backend's methods.
pub(crate) trait GenericDrawTarget<B: Backend> {
pub(crate) trait GenericDrawTarget {
type SourceSurface;
fn new(size: Size2D<u32>) -> Self;
fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Self;
fn clear_rect(&mut self, rect: &Rect<f32>, transform: Transform2D<f32>);
fn copy_surface(
&mut self,
surface: B::SourceSurface,
surface: Self::SourceSurface,
source: Rect<i32>,
destination: Point2D<i32>,
);
fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Self;
fn create_source_surface_from_data(&self, data: Snapshot) -> Option<B::SourceSurface>;
fn create_source_surface_from_data(&self, data: Snapshot) -> Option<Self::SourceSurface>;
fn draw_surface(
&mut self,
surface: B::SourceSurface,
surface: Self::SourceSurface,
dest: Rect<f64>,
source: Rect<f64>,
filter: Filter,
@ -42,7 +39,7 @@ pub(crate) trait GenericDrawTarget<B: Backend> {
);
fn draw_surface_with_shadow(
&self,
surface: B::SourceSurface,
surface: Self::SourceSurface,
dest: &Point2D<f32>,
shadow_options: ShadowOptions,
composition_options: CompositionOptions,
@ -89,7 +86,7 @@ pub(crate) trait GenericDrawTarget<B: Backend> {
composition_options: CompositionOptions,
transform: Transform2D<f32>,
);
fn surface(&self) -> B::SourceSurface;
fn surface(&self) -> Self::SourceSurface;
fn image_descriptor_and_serializable_data(&self) -> (ImageDescriptor, SerializableImageData);
fn snapshot(&self) -> Snapshot;
}

View file

@ -20,7 +20,7 @@ use range::Range;
use unicode_script::Script;
use webrender_api::ImageKey;
use crate::backend::{Backend, GenericDrawTarget as _};
use crate::backend::GenericDrawTarget;
// Asserts on WR texture cache update for zero sized image with raw data.
// https://github.com/servo/webrender/blob/main/webrender/src/texture_cache.rs#L1475
@ -102,28 +102,25 @@ pub(crate) enum Filter {
Nearest,
}
pub(crate) struct CanvasData<B: Backend> {
backend: B,
drawtarget: B::DrawTarget,
pub(crate) struct CanvasData<DrawTarget: GenericDrawTarget> {
drawtarget: DrawTarget,
compositor_api: CrossProcessCompositorApi,
image_key: ImageKey,
font_context: Arc<FontContext>,
}
impl<B: Backend> CanvasData<B> {
impl<DrawTarget: GenericDrawTarget> CanvasData<DrawTarget> {
pub(crate) fn new(
size: Size2D<u64>,
compositor_api: CrossProcessCompositorApi,
font_context: Arc<FontContext>,
backend: B,
) -> CanvasData<B> {
) -> CanvasData<DrawTarget> {
let size = size.max(MIN_WR_IMAGE_SIZE);
let draw_target = backend.create_drawtarget(size);
let draw_target = DrawTarget::new(size.cast());
let image_key = compositor_api.generate_image_key_blocking().unwrap();
let (descriptor, data) = draw_target.image_descriptor_and_serializable_data();
compositor_api.add_image(image_key, descriptor, data);
CanvasData {
backend,
drawtarget: draw_target,
compositor_api,
image_key,
@ -155,8 +152,8 @@ impl<B: Backend> CanvasData<B> {
snapshot
};
let writer = |draw_target: &mut B::DrawTarget, transform| {
write_image::<B>(
let writer = |draw_target: &mut DrawTarget, transform| {
write_image::<DrawTarget>(
draw_target,
snapshot,
dest_rect,
@ -496,7 +493,7 @@ impl<B: Backend> CanvasData<B> {
shadow_options,
composition_options,
transform,
|new_draw_target: &mut B::DrawTarget, transform| {
|new_draw_target, transform| {
new_draw_target.fill_rect(rect, style, composition_options, transform);
},
);
@ -538,7 +535,7 @@ impl<B: Backend> CanvasData<B> {
shadow_options,
composition_options,
transform,
|new_draw_target: &mut B::DrawTarget, transform| {
|new_draw_target, transform| {
new_draw_target.stroke_rect(
rect,
style,
@ -629,9 +626,7 @@ impl<B: Backend> CanvasData<B> {
.max(MIN_WR_IMAGE_SIZE);
// Step 1. Clear canvas's bitmap to transparent black.
self.drawtarget = self
.backend
.create_drawtarget(Size2D::new(size.width, size.height));
self.drawtarget = DrawTarget::new(Size2D::new(size.width, size.height).cast());
self.update_image_rendering();
}
@ -658,7 +653,7 @@ impl<B: Backend> CanvasData<B> {
);
}
fn create_draw_target_for_shadow(&self, source_rect: &Rect<f32>) -> B::DrawTarget {
fn create_draw_target_for_shadow(&self, source_rect: &Rect<f32>) -> DrawTarget {
self.drawtarget.create_similar_draw_target(&Size2D::new(
source_rect.size.width as i32,
source_rect.size.height as i32,
@ -673,7 +668,7 @@ impl<B: Backend> CanvasData<B> {
transform: Transform2D<f32>,
draw_shadow_source: F,
) where
F: FnOnce(&mut B::DrawTarget, Transform2D<f32>),
F: FnOnce(&mut DrawTarget, Transform2D<f32>),
{
let shadow_src_rect = transform.outer_transformed_rect(rect);
let mut new_draw_target = self.create_draw_target_for_shadow(&shadow_src_rect);
@ -755,7 +750,7 @@ impl<B: Backend> CanvasData<B> {
}
}
impl<B: Backend> Drop for CanvasData<B> {
impl<D: GenericDrawTarget> Drop for CanvasData<D> {
fn drop(&mut self) {
self.compositor_api.delete_image(self.image_key);
}
@ -771,8 +766,8 @@ const IDEOGRAPHIC_BASELINE_DEFAULT: f32 = 0.5;
/// dest_rect: Area of the destination target where the pixels will be copied
/// smoothing_enabled: It determines if smoothing is applied to the image result
/// premultiply: Determines whenever the image data should be premultiplied or not
fn write_image<B: Backend>(
draw_target: &mut B::DrawTarget,
fn write_image<DrawTarget: GenericDrawTarget>(
draw_target: &mut DrawTarget,
snapshot: Snapshot,
dest_rect: Rect<f64>,
smoothing_enabled: bool,

View file

@ -23,7 +23,6 @@ use pixels::Snapshot;
use webrender_api::ImageKey;
use crate::canvas_data::*;
use crate::raqote_backend::RaqoteBackend;
pub struct CanvasPaintThread {
canvases: HashMap<CanvasId, Canvas>,
@ -120,12 +119,8 @@ impl CanvasPaintThread {
let canvas_id = self.next_canvas_id;
self.next_canvas_id.0 += 1;
let canvas_data = CanvasData::new(
size,
self.compositor_api.clone(),
self.font_context.clone(),
RaqoteBackend,
);
let canvas_data =
CanvasData::new(size, self.compositor_api.clone(), self.font_context.clone());
let image_key = canvas_data.image_key();
self.canvases.insert(canvas_id, Canvas::Raqote(canvas_data));
@ -301,7 +296,7 @@ impl CanvasPaintThread {
}
enum Canvas {
Raqote(CanvasData<RaqoteBackend>),
Raqote(CanvasData<raqote::DrawTarget>),
}
impl Canvas {

View file

@ -19,7 +19,7 @@ use raqote::{DrawOptions, PathBuilder, StrokeStyle};
use style::color::AbsoluteColor;
use webrender_api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat};
use crate::backend::{Backend, GenericDrawTarget};
use crate::backend::GenericDrawTarget;
use crate::canvas_data::{Filter, TextRun};
thread_local! {
@ -30,18 +30,6 @@ thread_local! {
static SHARED_FONT_CACHE: RefCell<HashMap<FontIdentifier, Font>> = RefCell::default();
}
#[derive(Clone, Default)]
pub(crate) struct RaqoteBackend;
impl Backend for RaqoteBackend {
type DrawTarget = raqote::DrawTarget;
type SourceSurface = Vec<u8>; // TODO: See if we can avoid the alloc (probably?)
fn create_drawtarget(&self, size: Size2D<u64>) -> Self::DrawTarget {
raqote::DrawTarget::new(size.width as i32, size.height as i32)
}
}
#[derive(Clone)]
pub enum Pattern {
// argb
@ -199,9 +187,15 @@ fn create_gradient_stops(gradient_stops: Vec<CanvasGradientStop>) -> Vec<raqote:
stops
}
impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
impl GenericDrawTarget for raqote::DrawTarget {
type SourceSurface = Vec<u8>; // TODO: See if we can avoid the alloc (probably?)
fn new(size: Size2D<u32>) -> Self {
raqote::DrawTarget::new(size.width as i32, size.height as i32)
}
fn clear_rect(&mut self, rect: &Rect<f32>, transform: Transform2D<f32>) {
<Self as GenericDrawTarget<RaqoteBackend>>::fill_rect(
<Self as GenericDrawTarget>::fill_rect(
self,
rect,
FillOrStrokeStyle::Color(AbsoluteColor::TRANSPARENT_BLACK),
@ -215,7 +209,7 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
#[allow(unsafe_code)]
fn copy_surface(
&mut self,
surface: <RaqoteBackend as Backend>::SourceSurface,
surface: Self::SourceSurface,
source: Rect<i32>,
destination: Point2D<i32>,
) {
@ -226,16 +220,10 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
raqote::DrawTarget::copy_surface(self, &dt, source.to_box2d(), destination);
}
fn create_similar_draw_target(
&self,
size: &Size2D<i32>,
) -> <RaqoteBackend as Backend>::DrawTarget {
fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Self {
raqote::DrawTarget::new(size.width, size.height)
}
fn create_source_surface_from_data(
&self,
data: Snapshot,
) -> Option<<RaqoteBackend as Backend>::SourceSurface> {
fn create_source_surface_from_data(&self, data: Snapshot) -> Option<Self::SourceSurface> {
Some(
data.to_vec(
Some(SnapshotAlphaMode::Transparent {
@ -249,7 +237,7 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
#[allow(unsafe_code)]
fn draw_surface(
&mut self,
surface: <RaqoteBackend as Backend>::SourceSurface,
surface: Self::SourceSurface,
dest: Rect<f64>,
src: Rect<f64>,
filter: Filter,
@ -297,7 +285,7 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
}
fn draw_surface_with_shadow(
&self,
_surface: <RaqoteBackend as Backend>::SourceSurface,
_surface: Self::SourceSurface,
_dest: &Point2D<f32>,
_shadow_options: ShadowOptions,
_composition_options: CompositionOptions,
@ -398,13 +386,7 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
rect.size.height,
);
<Self as GenericDrawTarget<RaqoteBackend>>::fill(
self,
&pb,
style,
composition_options,
transform,
);
<Self as GenericDrawTarget>::fill(self, &pb, style, composition_options, transform);
}
fn get_size(&self) -> Size2D<i32> {
Size2D::new(self.width(), self.height())
@ -419,7 +401,7 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
fn push_clip_rect(&mut self, rect: &Rect<i32>) {
self.push_clip_rect(rect.to_box2d());
}
fn surface(&self) -> <RaqoteBackend as Backend>::SourceSurface {
fn surface(&self) -> Self::SourceSurface {
self.get_data_u8().to_vec()
}
fn stroke(