0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-15 17:21:17 -05:00

Merge pull request #5518 from penpot/superalex-bugs-wasm-strokes

🐛 Fix wasm render strokes bugs
This commit is contained in:
Belén Albeza 2025-01-09 10:38:09 +01:00 committed by GitHub
commit 4bd1e32462
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 44 additions and 17 deletions

View file

@ -73,6 +73,7 @@ impl TryFrom<RawPathData> for Segment {
pub struct Path { pub struct Path {
segments: Vec<Segment>, segments: Vec<Segment>,
skia_path: skia::Path, skia_path: skia::Path,
open: bool,
} }
fn starts_and_ends_at_same_point(path: &skia::Path) -> bool { fn starts_and_ends_at_same_point(path: &skia::Path) -> bool {
@ -91,6 +92,7 @@ impl TryFrom<Vec<RawPathData>> for Path {
type Error = String; type Error = String;
fn try_from(value: Vec<RawPathData>) -> Result<Self, Self::Error> { fn try_from(value: Vec<RawPathData>) -> Result<Self, Self::Error> {
let mut open = true;
let segments = value let segments = value
.into_iter() .into_iter()
.map(|raw| Segment::try_from(raw)) .map(|raw| Segment::try_from(raw))
@ -110,17 +112,20 @@ impl TryFrom<Vec<RawPathData>> for Path {
} }
Segment::Close => { Segment::Close => {
skia_path.close(); skia_path.close();
open = false;
} }
} }
} }
if !skia_path.is_last_contour_closed() && starts_and_ends_at_same_point(&skia_path) { if !skia_path.is_last_contour_closed() && starts_and_ends_at_same_point(&skia_path) {
skia_path.close(); skia_path.close();
open = false;
} }
Ok(Path { Ok(Path {
segments, segments,
skia_path, skia_path,
open,
}) })
} }
} }
@ -129,4 +134,8 @@ impl Path {
pub fn to_skia_path(&self) -> skia::Path { pub fn to_skia_path(&self) -> skia::Path {
self.skia_path.snapshot() self.skia_path.snapshot()
} }
pub fn is_open(&self) -> bool {
self.open
}
} }

View file

@ -131,7 +131,7 @@ fn render_stroke(
Kind::Rect(rect) => draw_stroke_on_rect(surface.canvas(), stroke, rect, &selrect), Kind::Rect(rect) => draw_stroke_on_rect(surface.canvas(), stroke, rect, &selrect),
Kind::Circle(rect) => draw_stroke_on_circle(surface.canvas(), stroke, rect, &selrect), Kind::Circle(rect) => draw_stroke_on_circle(surface.canvas(), stroke, rect, &selrect),
Kind::Path(path) => { Kind::Path(path) => {
draw_stroke_on_path(surface.canvas(), stroke, path, &selrect, path_transform) draw_stroke_on_path(surface.canvas(), stroke, path, &selrect, path_transform);
} }
} }
} }
@ -165,9 +165,10 @@ fn draw_stroke_on_path(
let mut skia_path = path.to_skia_path(); let mut skia_path = path.to_skia_path();
skia_path.transform(path_transform.unwrap()); skia_path.transform(path_transform.unwrap());
let paint_stroke = stroke.to_stroked_paint(selrect); let kind = stroke.render_kind(path.is_open());
let paint_stroke = stroke.to_stroked_paint(kind.clone(), selrect);
// Draw the different kind of strokes for a path requires different strategies: // Draw the different kind of strokes for a path requires different strategies:
match stroke.kind { match kind {
// For inner stroke we draw a center stroke (with double width) and clip to the original path (that way the extra outer stroke is removed) // For inner stroke we draw a center stroke (with double width) and clip to the original path (that way the extra outer stroke is removed)
StrokeKind::InnerStroke => { StrokeKind::InnerStroke => {
canvas.clip_path(&skia_path, skia::ClipOp::Intersect, true); canvas.clip_path(&skia_path, skia::ClipOp::Intersect, true);
@ -288,10 +289,11 @@ pub fn draw_image_stroke_in_container(
Kind::Path(p) => { Kind::Path(p) => {
let mut path = p.to_skia_path(); let mut path = p.to_skia_path();
path.transform(path_transform.unwrap()); path.transform(path_transform.unwrap());
if stroke.kind == StrokeKind::InnerStroke { let stroke_kind = stroke.render_kind(p.is_open());
if stroke_kind == StrokeKind::InnerStroke {
canvas.clip_path(&path, skia::ClipOp::Intersect, true); canvas.clip_path(&path, skia::ClipOp::Intersect, true);
} }
let paint = stroke.to_stroked_paint(&outer_rect); let paint = stroke.to_stroked_paint(stroke_kind, &outer_rect);
canvas.draw_path(&path, &paint); canvas.draw_path(&path, &paint);
} }
} }
@ -317,7 +319,8 @@ pub fn draw_image_stroke_in_container(
canvas.draw_image_rect(image, None, dest_rect, &image_paint); canvas.draw_image_rect(image, None, dest_rect, &image_paint);
// Clear outer stroke for paths if necessary. When adding an outer stroke we need to empty the stroke added too in the inner area. // Clear outer stroke for paths if necessary. When adding an outer stroke we need to empty the stroke added too in the inner area.
if let (Kind::Path(p), StrokeKind::OuterStroke) = (kind, &stroke.kind) { if let Kind::Path(p) = kind {
if stroke.render_kind(p.is_open()) == StrokeKind::OuterStroke {
let mut path = p.to_skia_path(); let mut path = p.to_skia_path();
path.transform(path_transform.unwrap()); path.transform(path_transform.unwrap());
let mut clear_paint = skia::Paint::default(); let mut clear_paint = skia::Paint::default();
@ -325,6 +328,7 @@ pub fn draw_image_stroke_in_container(
clear_paint.set_anti_alias(true); clear_paint.set_anti_alias(true);
canvas.draw_path(&path, &clear_paint); canvas.draw_path(&path, &clear_paint);
} }
}
// Restore canvas state // Restore canvas state
canvas.restore(); canvas.restore();

View file

@ -32,7 +32,7 @@ pub enum StrokeCap {
// Square, // Square,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum StrokeKind { pub enum StrokeKind {
InnerStroke, InnerStroke,
OuterStroke, OuterStroke,
@ -46,10 +46,19 @@ pub struct Stroke {
pub style: StrokeStyle, pub style: StrokeStyle,
pub cap_end: StrokeCap, pub cap_end: StrokeCap,
pub cap_start: StrokeCap, pub cap_start: StrokeCap,
pub kind: StrokeKind, kind: StrokeKind,
} }
impl Stroke { impl Stroke {
// Strokes for open shapes should be rendered as if they were centered.
pub fn render_kind(&self, is_open: bool) -> StrokeKind {
if is_open {
StrokeKind::CenterStroke
} else {
self.kind
}
}
pub fn new_center_stroke(width: f32, style: i32) -> Self { pub fn new_center_stroke(width: f32, style: i32) -> Self {
let transparent = skia::Color::from_argb(0, 0, 0, 0); let transparent = skia::Color::from_argb(0, 0, 0, 0);
Stroke { Stroke {
@ -124,7 +133,12 @@ impl Stroke {
let path_effect = match self.style { let path_effect = match self.style {
StrokeStyle::Dotted => { StrokeStyle::Dotted => {
let mut circle_path = skia::Path::new(); let mut circle_path = skia::Path::new();
circle_path.add_circle((0.0, 0.0), self.width / 2.0, None); let width = match self.kind {
StrokeKind::InnerStroke => self.width,
StrokeKind::CenterStroke => self.width / 2.0,
StrokeKind::OuterStroke => self.width,
};
circle_path.add_circle((0.0, 0.0), width, None);
let advance = self.width + 5.0; let advance = self.width + 5.0;
skia::PathEffect::path_1d( skia::PathEffect::path_1d(
&circle_path, &circle_path,
@ -153,9 +167,9 @@ impl Stroke {
paint paint
} }
pub fn to_stroked_paint(&self, rect: &math::Rect) -> skia::Paint { pub fn to_stroked_paint(&self, kind: StrokeKind, rect: &math::Rect) -> skia::Paint {
let mut paint = self.to_paint(rect); let mut paint = self.to_paint(rect);
match self.kind { match kind {
StrokeKind::InnerStroke => { StrokeKind::InnerStroke => {
paint.set_stroke_width(2. * self.width); paint.set_stroke_width(2. * self.width);
paint paint