canvas: Move CompositionOrBlending and ellipse() from RaqoteBackend to Backend (#36790)

Small fixes of abstraction.

We do not need to generalize `CompositionOrBlending`, because it's from
canvas_traits.
Ellipse impl that uses arc is not backend specific, so it serves as good
trait default.

Reviewable per commit.


Testing: Only refactoring, but there are WPT tests

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
sagudev 2025-05-01 11:57:20 +02:00 committed by GitHub
parent cfc7115867
commit d1f7a90619
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 58 additions and 66 deletions

View file

@ -2,8 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use canvas_traits::canvas::{FillOrStrokeStyle, LineCapStyle, LineJoinStyle}; use canvas_traits::canvas::{
CompositionOrBlending, FillOrStrokeStyle, LineCapStyle, LineJoinStyle,
};
use euclid::Angle;
use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D};
use lyon_geom::Arc;
use style::color::AbsoluteColor; use style::color::AbsoluteColor;
use crate::canvas_data::{CanvasPaintState, Filter, TextRun}; use crate::canvas_data::{CanvasPaintState, Filter, TextRun};
@ -14,7 +18,6 @@ pub(crate) trait Backend: Clone + Sized {
type Color: Clone; type Color: Clone;
type DrawOptions: DrawOptionsHelpers + Clone; type DrawOptions: DrawOptionsHelpers + Clone;
type CompositionOp; type CompositionOp;
type CompositionOrBlending;
type DrawTarget: GenericDrawTarget<Self>; type DrawTarget: GenericDrawTarget<Self>;
type PathBuilder: GenericPathBuilder<Self>; type PathBuilder: GenericPathBuilder<Self>;
type SourceSurface; type SourceSurface;
@ -39,7 +42,7 @@ pub(crate) trait Backend: Clone + Sized {
); );
fn set_global_composition( fn set_global_composition(
&mut self, &mut self,
op: Self::CompositionOrBlending, op: CompositionOrBlending,
state: &mut CanvasPaintState<'_, Self>, state: &mut CanvasPaintState<'_, Self>,
); );
fn create_drawtarget(&self, size: Size2D<u64>) -> Self::DrawTarget; fn create_drawtarget(&self, size: Size2D<u64>) -> Self::DrawTarget;
@ -148,7 +151,54 @@ pub(crate) trait GenericPathBuilder<B: Backend> {
start_angle: f32, start_angle: f32,
end_angle: f32, end_angle: f32,
anticlockwise: bool, anticlockwise: bool,
); ) {
let mut start = Angle::radians(start_angle);
let mut end = Angle::radians(end_angle);
// Wrap angles mod 2 * PI if necessary
if !anticlockwise && start > end + Angle::two_pi() ||
anticlockwise && end > start + Angle::two_pi()
{
start = start.positive();
end = end.positive();
}
// Calculate the total arc we're going to sweep.
let sweep = match anticlockwise {
true => {
if end - start == Angle::two_pi() {
-Angle::two_pi()
} else if end > start {
-(Angle::two_pi() - (end - start))
} else {
-(start - end)
}
},
false => {
if start - end == Angle::two_pi() {
Angle::two_pi()
} else if start > end {
Angle::two_pi() - (start - end)
} else {
end - start
}
},
};
let arc: Arc<f32> = Arc {
center: origin,
radii: Vector2D::new(radius_x, radius_y),
start_angle: start,
sweep_angle: sweep,
x_rotation: Angle::radians(rotation_angle),
};
self.line_to(arc.from());
arc.for_each_quadratic_bezier(&mut |q| {
self.quadratic_curve_to(&q.ctrl, &q.to);
});
}
fn get_current_point(&mut self) -> Option<Point2D<f32>>; fn get_current_point(&mut self) -> Option<Point2D<f32>>;
fn line_to(&mut self, point: Point2D<f32>); fn line_to(&mut self, point: Point2D<f32>);
fn move_to(&mut self, point: Point2D<f32>); fn move_to(&mut self, point: Point2D<f32>);

View file

@ -1183,7 +1183,7 @@ impl<'a, B: Backend> CanvasData<'a, B> {
self.state.draw_options.set_alpha(alpha); self.state.draw_options.set_alpha(alpha);
} }
pub(crate) fn set_global_composition(&mut self, op: B::CompositionOrBlending) { pub(crate) fn set_global_composition(&mut self, op: CompositionOrBlending) {
self.backend.set_global_composition(op, &mut self.state); self.backend.set_global_composition(op, &mut self.state);
} }

View file

@ -7,12 +7,10 @@ use std::collections::HashMap;
use canvas_traits::canvas::*; use canvas_traits::canvas::*;
use cssparser::color::clamp_unit_f32; use cssparser::color::clamp_unit_f32;
use euclid::Angle;
use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D};
use font_kit::font::Font; use font_kit::font::Font;
use fonts::{ByteIndex, FontIdentifier, FontTemplateRefMethods}; use fonts::{ByteIndex, FontIdentifier, FontTemplateRefMethods};
use log::warn; use log::warn;
use lyon_geom::Arc;
use range::Range; use range::Range;
use raqote::PathOp; use raqote::PathOp;
use style::color::AbsoluteColor; use style::color::AbsoluteColor;
@ -40,7 +38,6 @@ impl Backend for RaqoteBackend {
type Color = raqote::SolidSource; type Color = raqote::SolidSource;
type DrawOptions = raqote::DrawOptions; type DrawOptions = raqote::DrawOptions;
type CompositionOp = raqote::BlendMode; type CompositionOp = raqote::BlendMode;
type CompositionOrBlending = CompositionOrBlending;
type DrawTarget = raqote::DrawTarget; type DrawTarget = raqote::DrawTarget;
type PathBuilder = PathBuilder; type PathBuilder = PathBuilder;
type SourceSurface = Vec<u8>; // TODO: See if we can avoid the alloc (probably?) type SourceSurface = Vec<u8>; // TODO: See if we can avoid the alloc (probably?)
@ -84,7 +81,7 @@ impl Backend for RaqoteBackend {
fn set_global_composition( fn set_global_composition(
&mut self, &mut self,
op: Self::CompositionOrBlending, op: CompositionOrBlending,
state: &mut CanvasPaintState<'_, Self>, state: &mut CanvasPaintState<'_, Self>,
) { ) {
state.draw_options.blend_mode = op.to_raqote_style(); state.draw_options.blend_mode = op.to_raqote_style();
@ -701,6 +698,7 @@ impl GenericPathBuilder<RaqoteBackend> for PathBuilder {
anticlockwise, anticlockwise,
); );
} }
fn bezier_curve_to( fn bezier_curve_to(
&mut self, &mut self,
control_point1: &Point2D<f32>, control_point1: &Point2D<f32>,
@ -716,66 +714,10 @@ impl GenericPathBuilder<RaqoteBackend> for PathBuilder {
control_point3.y, control_point3.y,
); );
} }
fn close(&mut self) { fn close(&mut self) {
self.0.as_mut().unwrap().close(); self.0.as_mut().unwrap().close();
} }
fn ellipse(
&mut self,
origin: Point2D<f32>,
radius_x: f32,
radius_y: f32,
rotation_angle: f32,
start_angle: f32,
end_angle: f32,
anticlockwise: bool,
) {
let mut start = Angle::radians(start_angle);
let mut end = Angle::radians(end_angle);
// Wrap angles mod 2 * PI if necessary
if !anticlockwise && start > end + Angle::two_pi() ||
anticlockwise && end > start + Angle::two_pi()
{
start = start.positive();
end = end.positive();
}
// Calculate the total arc we're going to sweep.
let sweep = match anticlockwise {
true => {
if end - start == Angle::two_pi() {
-Angle::two_pi()
} else if end > start {
-(Angle::two_pi() - (end - start))
} else {
-(start - end)
}
},
false => {
if start - end == Angle::two_pi() {
Angle::two_pi()
} else if start > end {
Angle::two_pi() - (start - end)
} else {
end - start
}
},
};
let arc: Arc<f32> = Arc {
center: origin,
radii: Vector2D::new(radius_x, radius_y),
start_angle: start,
sweep_angle: sweep,
x_rotation: Angle::radians(rotation_angle),
};
self.line_to(arc.from());
arc.for_each_quadratic_bezier(&mut |q| {
self.quadratic_curve_to(&q.ctrl, &q.to);
});
}
fn svg_arc( fn svg_arc(
&mut self, &mut self,