From ec39390c23cd46a115bb0528abdb2c5527f1272a Mon Sep 17 00:00:00 2001 From: Vlad-Stefan Harbuz Date: Fri, 21 Jun 2024 10:41:17 +0100 Subject: [PATCH 1/2] Add stroke_rectangle This method should be able to leverage performance improvements in lyon's `tessellate_rectangle` over `tessellate_path`. --- graphics/src/geometry/frame.rs | 24 +++++++++++++++++++++ renderer/src/fallback.rs | 13 ++++++++++++ tiny_skia/src/geometry.rs | 25 ++++++++++++++++++++++ wgpu/src/geometry.rs | 38 ++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+) diff --git a/graphics/src/geometry/frame.rs b/graphics/src/geometry/frame.rs index b5f2f1398c..3dee7e757f 100644 --- a/graphics/src/geometry/frame.rs +++ b/graphics/src/geometry/frame.rs @@ -65,6 +65,17 @@ where self.raw.stroke(path, stroke); } + /// Draws the stroke of an axis-aligned rectangle with the provided style + /// given its top-left corner coordinate and its `Size` on the [`Frame`] . + pub fn stroke_rectangle<'a>( + &mut self, + top_left: Point, + size: Size, + stroke: impl Into>, + ) { + self.raw.stroke_rectangle(top_left, size, stroke); + } + /// Draws the characters of the given [`Text`] on the [`Frame`], filling /// them with the given color. /// @@ -200,6 +211,12 @@ pub trait Backend: Sized { fn paste(&mut self, frame: Self); fn stroke<'a>(&mut self, path: &Path, stroke: impl Into>); + fn stroke_rectangle<'a>( + &mut self, + top_left: Point, + size: Size, + stroke: impl Into>, + ); fn fill(&mut self, path: &Path, fill: impl Into); fn fill_text(&mut self, text: impl Into); @@ -248,6 +265,13 @@ impl Backend for () { fn paste(&mut self, _frame: Self) {} fn stroke<'a>(&mut self, _path: &Path, _stroke: impl Into>) {} + fn stroke_rectangle<'a>( + &mut self, + _top_left: Point, + _size: Size, + _stroke: impl Into>, + ) { + } fn fill(&mut self, _path: &Path, _fill: impl Into) {} fn fill_text(&mut self, _text: impl Into) {} diff --git a/renderer/src/fallback.rs b/renderer/src/fallback.rs index fbd285db9e..8cb18bdea0 100644 --- a/renderer/src/fallback.rs +++ b/renderer/src/fallback.rs @@ -540,6 +540,19 @@ mod geometry { delegate!(self, frame, frame.stroke(path, stroke)); } + fn stroke_rectangle<'a>( + &mut self, + top_left: Point, + size: Size, + stroke: impl Into>, + ) { + delegate!( + self, + frame, + frame.stroke_rectangle(top_left, size, stroke) + ); + } + fn fill_text(&mut self, text: impl Into) { delegate!(self, frame, frame.fill_text(text)); } diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index 659612d1db..532a53cd41 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -168,6 +168,31 @@ impl geometry::frame::Backend for Frame { }); } + fn stroke_rectangle<'a>( + &mut self, + top_left: Point, + size: Size, + stroke: impl Into>, + ) { + let Some(path) = convert_path(&Path::rectangle(top_left, size)) + .and_then(|path| path.transform(self.transform)) + else { + return; + }; + + let stroke = stroke.into(); + let skia_stroke = into_stroke(&stroke); + + let mut paint = into_paint(stroke.style); + paint.shader.transform(self.transform); + + self.primitives.push(Primitive::Stroke { + path, + paint, + stroke: skia_stroke, + }); + } + fn fill_text(&mut self, text: impl Into) { let text = text.into(); diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index be65ba3656..8e6f77d762 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -253,6 +253,44 @@ impl geometry::frame::Backend for Frame { .expect("Stroke path"); } + fn stroke_rectangle<'a>( + &mut self, + top_left: Point, + size: Size, + stroke: impl Into>, + ) { + let stroke = stroke.into(); + + let mut buffer = self + .buffers + .get_stroke(&self.transforms.current.transform_style(stroke.style)); + + let top_left = self + .transforms + .current + .0 + .transform_point(lyon::math::Point::new(top_left.x, top_left.y)); + + let size = + self.transforms.current.0.transform_vector( + lyon::math::Vector::new(size.width, size.height), + ); + + let mut options = tessellation::StrokeOptions::default(); + options.line_width = stroke.width; + options.start_cap = into_line_cap(stroke.line_cap); + options.end_cap = into_line_cap(stroke.line_cap); + options.line_join = into_line_join(stroke.line_join); + + self.stroke_tessellator + .tessellate_rectangle( + &lyon::math::Box2D::new(top_left, top_left + size), + &options, + buffer.as_mut(), + ) + .expect("Stroke rectangle"); + } + fn fill_text(&mut self, text: impl Into) { let text = text.into(); From fe8f41278dc922e12ffeb7a50bfb17a47b4bf956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 10 Sep 2024 23:45:33 +0200 Subject: [PATCH 2/2] Leverage `stroke` for `stroke_rectangle` in `tiny-skia` backend --- tiny_skia/src/geometry.rs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index 532a53cd41..0d5fff62fd 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -174,23 +174,7 @@ impl geometry::frame::Backend for Frame { size: Size, stroke: impl Into>, ) { - let Some(path) = convert_path(&Path::rectangle(top_left, size)) - .and_then(|path| path.transform(self.transform)) - else { - return; - }; - - let stroke = stroke.into(); - let skia_stroke = into_stroke(&stroke); - - let mut paint = into_paint(stroke.style); - paint.shader.transform(self.transform); - - self.primitives.push(Primitive::Stroke { - path, - paint, - stroke: skia_stroke, - }); + self.stroke(&Path::rectangle(top_left, size), stroke); } fn fill_text(&mut self, text: impl Into) {