canvas: Fully stateless backend (#38214)

I think this simplifies canvas backends greatly. Before there were many
(needless) hoops from abstract to concrete (backend) and back, now we
can do most stuff on abstract types.

Testing: Existing WPT tests
Fixes: #38022 

try run: https://github.com/sagudev/servo/actions/runs/16450978211

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
sagudev 2025-07-24 20:11:29 +02:00 committed by GitHub
parent 86d8317460
commit d39e701b46
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 650 additions and 806 deletions

View file

@ -17,6 +17,7 @@ use serde::{Deserialize, Serialize};
use strum::{Display, EnumString};
use style::color::AbsoluteColor;
use style::properties::style_structs::Font as FontStyleStruct;
use style::servo_arc::Arc as ServoArc;
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Path(pub BezPath);
@ -400,7 +401,7 @@ pub enum FillRule {
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
pub struct CanvasId(pub u64);
#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, Serialize)]
pub struct CompositionOptions {
pub alpha: f64,
pub composition_operation: CompositionOrBlending,
@ -414,6 +415,18 @@ pub struct ShadowOptions {
pub color: AbsoluteColor,
}
impl ShadowOptions {
/// <https://html.spec.whatwg.org/multipage/#when-shadows-are-drawn>
pub fn need_to_draw_shadow(&self) -> bool {
// Shadows are only drawn if the opacity component of the alpha component of the shadow color is nonzero
self.color.alpha != 0.0 &&
// and either the shadowBlur is nonzero, or the shadowOffsetX is nonzero, or the shadowOffsetY is nonzero.
(self.offset_x != 0.0 ||
self.offset_y != 0.0 ||
self.blur != 0.0)
}
}
#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
pub struct LineOptions {
pub width: f64,
@ -426,7 +439,8 @@ pub struct LineOptions {
#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
pub struct TextOptions {
pub font: Option<FontStyleStruct>,
#[ignore_malloc_size_of = "Arc"]
pub font: Option<ServoArc<FontStyleStruct>>,
pub align: TextAlign,
pub baseline: TextBaseline,
}
@ -471,6 +485,7 @@ pub enum Canvas2dMsg {
),
ClearRect(Rect<f32>, Transform2D<f32>),
ClipPath(Path, Transform2D<f32>),
PopClip,
FillPath(
FillOrStrokeStyle,
Path,
@ -500,8 +515,6 @@ pub enum Canvas2dMsg {
GetImageData(Rect<u32>, Size2D<u32>, IpcSender<IpcSnapshot>),
MeasureText(String, IpcSender<TextMetrics>, TextOptions),
PutImageData(Rect<u32>, IpcSnapshot),
RestoreContext,
SaveContext,
StrokeRect(
Rect<f32>,
FillOrStrokeStyle,
@ -627,6 +640,48 @@ pub enum FillOrStrokeStyle {
Surface(SurfaceStyle),
}
impl FillOrStrokeStyle {
pub fn is_zero_size_gradient(&self) -> bool {
match self {
Self::RadialGradient(pattern) => {
let centers_equal = (pattern.x0, pattern.y0) == (pattern.x1, pattern.y1);
let radii_equal = pattern.r0 == pattern.r1;
(centers_equal && radii_equal) || pattern.stops.is_empty()
},
Self::LinearGradient(pattern) => {
(pattern.x0, pattern.y0) == (pattern.x1, pattern.y1) || pattern.stops.is_empty()
},
Self::Color(..) | Self::Surface(..) => false,
}
}
pub fn x_bound(&self) -> Option<u32> {
match self {
Self::Surface(pattern) => {
if pattern.repeat_x {
None
} else {
Some(pattern.surface_size.width)
}
},
Self::Color(..) | Self::LinearGradient(..) | Self::RadialGradient(..) => None,
}
}
pub fn y_bound(&self) -> Option<u32> {
match self {
Self::Surface(pattern) => {
if pattern.repeat_y {
None
} else {
Some(pattern.surface_size.height)
}
},
Self::Color(..) | Self::LinearGradient(..) | Self::RadialGradient(..) => None,
}
}
}
#[derive(
Clone, Copy, Debug, Display, Deserialize, EnumString, MallocSizeOf, PartialEq, Serialize,
)]