mirror of
https://github.com/penpot/penpot.git
synced 2026-02-12 14:42:56 +00:00
Merge pull request #8309 from penpot/superalex-fix-stroke-dot-dash-mix
🐛 Fix dot strokes
This commit is contained in:
@@ -165,6 +165,7 @@ test("Updates canvas background", async ({ page }) => {
|
|||||||
});
|
});
|
||||||
await canvasBackgroundInput.fill("FABADA");
|
await canvasBackgroundInput.fill("FABADA");
|
||||||
await workspace.page.keyboard.press("Enter");
|
await workspace.page.keyboard.press("Enter");
|
||||||
|
await workspace.waitForFirstRenderWithoutUI();
|
||||||
|
|
||||||
await expect(workspace.canvas).toHaveScreenshot();
|
await expect(workspace.canvas).toHaveScreenshot();
|
||||||
});
|
});
|
||||||
@@ -323,3 +324,19 @@ test("Renders a clipped frame with a large blur drop shadow", async ({
|
|||||||
|
|
||||||
await expect(workspace.canvas).toHaveScreenshot();
|
await expect(workspace.canvas).toHaveScreenshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Renders a file with solid, dotted, dashed and mixed stroke styles", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const workspace = new WasmWorkspacePage(page);
|
||||||
|
await workspace.setupEmptyFile();
|
||||||
|
await workspace.mockGetFile("render-wasm/get-file-stroke-styles.json");
|
||||||
|
|
||||||
|
await workspace.goToWorkspace({
|
||||||
|
id: "b888b894-3697-80d3-8006-51cc8a55c200",
|
||||||
|
pageId: "b888b894-3697-80d3-8006-51cc8a55c210",
|
||||||
|
});
|
||||||
|
await workspace.waitForFirstRenderWithoutUI();
|
||||||
|
|
||||||
|
await expect(workspace.canvas).toHaveScreenshot();
|
||||||
|
});
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 123 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 216 KiB After Width: | Height: | Size: 220 KiB |
@@ -26,7 +26,7 @@ fn draw_stroke_on_rect(
|
|||||||
// Draw the different kind of strokes for a rect is straightforward, we just need apply a stroke to:
|
// Draw the different kind of strokes for a rect is straightforward, we just need apply a stroke to:
|
||||||
// - The same rect if it's a center stroke
|
// - The same rect if it's a center stroke
|
||||||
// - A bigger rect if it's an outer stroke
|
// - A bigger rect if it's an outer stroke
|
||||||
// - A smaller rect if it's an outer stroke
|
// - A smaller rect if it's an inner stroke
|
||||||
let stroke_rect = stroke.aligned_rect(rect, scale);
|
let stroke_rect = stroke.aligned_rect(rect, scale);
|
||||||
let mut paint = stroke.to_paint(selrect, svg_attrs, antialias);
|
let mut paint = stroke.to_paint(selrect, svg_attrs, antialias);
|
||||||
|
|
||||||
@@ -34,7 +34,9 @@ fn draw_stroke_on_rect(
|
|||||||
let filter = compose_filters(blur, shadow);
|
let filter = compose_filters(blur, shadow);
|
||||||
paint.set_image_filter(filter);
|
paint.set_image_filter(filter);
|
||||||
|
|
||||||
match corners {
|
// By default just draw the rect. Only dotted inner/outer strokes need
|
||||||
|
// clipping to prevent the dotted pattern from appearing in wrong areas.
|
||||||
|
let draw_stroke = || match corners {
|
||||||
Some(radii) => {
|
Some(radii) => {
|
||||||
let radii = stroke.outer_corners(radii);
|
let radii = stroke.outer_corners(radii);
|
||||||
let rrect = RRect::new_rect_radii(stroke_rect, &radii);
|
let rrect = RRect::new_rect_radii(stroke_rect, &radii);
|
||||||
@@ -43,6 +45,24 @@ fn draw_stroke_on_rect(
|
|||||||
None => {
|
None => {
|
||||||
canvas.draw_rect(stroke_rect, &paint);
|
canvas.draw_rect(stroke_rect, &paint);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(clip_op) = stroke.clip_op() {
|
||||||
|
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&paint);
|
||||||
|
canvas.save_layer(&layer_rec);
|
||||||
|
match corners {
|
||||||
|
Some(radii) => {
|
||||||
|
let rrect = RRect::new_rect_radii(*rect, radii);
|
||||||
|
canvas.clip_rrect(rrect, clip_op, antialias);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
canvas.clip_rect(*rect, clip_op, antialias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
draw_stroke();
|
||||||
|
canvas.restore();
|
||||||
|
} else {
|
||||||
|
draw_stroke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +82,7 @@ fn draw_stroke_on_circle(
|
|||||||
// Draw the different kind of strokes for an oval is straightforward, we just need apply a stroke to:
|
// Draw the different kind of strokes for an oval is straightforward, we just need apply a stroke to:
|
||||||
// - The same oval if it's a center stroke
|
// - The same oval if it's a center stroke
|
||||||
// - A bigger oval if it's an outer stroke
|
// - A bigger oval if it's an outer stroke
|
||||||
// - A smaller oval if it's an outer stroke
|
// - A smaller oval if it's an inner stroke
|
||||||
let stroke_rect = stroke.aligned_rect(rect, scale);
|
let stroke_rect = stroke.aligned_rect(rect, scale);
|
||||||
let mut paint = stroke.to_paint(selrect, svg_attrs, antialias);
|
let mut paint = stroke.to_paint(selrect, svg_attrs, antialias);
|
||||||
|
|
||||||
@@ -70,7 +90,19 @@ fn draw_stroke_on_circle(
|
|||||||
let filter = compose_filters(blur, shadow);
|
let filter = compose_filters(blur, shadow);
|
||||||
paint.set_image_filter(filter);
|
paint.set_image_filter(filter);
|
||||||
|
|
||||||
canvas.draw_oval(stroke_rect, &paint);
|
// By default just draw the circle. Only dotted inner/outer strokes need
|
||||||
|
// clipping to prevent the dotted pattern from appearing in wrong areas.
|
||||||
|
if let Some(clip_op) = stroke.clip_op() {
|
||||||
|
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&paint);
|
||||||
|
canvas.save_layer(&layer_rec);
|
||||||
|
let mut clip_path = skia::Path::new();
|
||||||
|
clip_path.add_oval(rect, None);
|
||||||
|
canvas.clip_path(&clip_path, clip_op, antialias);
|
||||||
|
canvas.draw_oval(stroke_rect, &paint);
|
||||||
|
canvas.restore();
|
||||||
|
} else {
|
||||||
|
canvas.draw_oval(stroke_rect, &paint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_outer_stroke_path(
|
fn draw_outer_stroke_path(
|
||||||
|
|||||||
@@ -119,6 +119,19 @@ impl Stroke {
|
|||||||
self.width *= value;
|
self.width *= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the clip operation for dotted inner/outer strokes.
|
||||||
|
/// Returns `None` when no clipping is needed (center or non-dotted).
|
||||||
|
pub fn clip_op(&self) -> Option<skia::ClipOp> {
|
||||||
|
if self.style != StrokeStyle::Dotted || self.kind == StrokeKind::Center {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
match self.kind {
|
||||||
|
StrokeKind::Inner => Some(skia::ClipOp::Intersect),
|
||||||
|
StrokeKind::Outer => Some(skia::ClipOp::Difference),
|
||||||
|
StrokeKind::Center => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn delta(&self) -> f32 {
|
pub fn delta(&self) -> f32 {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
StrokeKind::Inner => 0.,
|
StrokeKind::Inner => 0.,
|
||||||
@@ -128,20 +141,28 @@ impl Stroke {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn outer_rect(&self, rect: &Rect) -> Rect {
|
pub fn outer_rect(&self, rect: &Rect) -> Rect {
|
||||||
match self.kind {
|
match (self.kind, self.style) {
|
||||||
StrokeKind::Inner => Rect::from_xywh(
|
(StrokeKind::Inner, StrokeStyle::Dotted) | (StrokeKind::Outer, StrokeStyle::Dotted) => {
|
||||||
rect.left + (self.width / 2.),
|
// Boundary so circles center on it and semicircles match after clipping
|
||||||
rect.top + (self.width / 2.),
|
*rect
|
||||||
rect.width() - self.width,
|
}
|
||||||
rect.height() - self.width,
|
_ => match self.kind {
|
||||||
),
|
StrokeKind::Inner => Rect::from_xywh(
|
||||||
StrokeKind::Center => Rect::from_xywh(rect.left, rect.top, rect.width(), rect.height()),
|
rect.left + (self.width / 2.),
|
||||||
StrokeKind::Outer => Rect::from_xywh(
|
rect.top + (self.width / 2.),
|
||||||
rect.left - (self.width / 2.),
|
rect.width() - self.width,
|
||||||
rect.top - (self.width / 2.),
|
rect.height() - self.width,
|
||||||
rect.width() + self.width,
|
),
|
||||||
rect.height() + self.width,
|
StrokeKind::Center => {
|
||||||
),
|
Rect::from_xywh(rect.left, rect.top, rect.width(), rect.height())
|
||||||
|
}
|
||||||
|
StrokeKind::Outer => Rect::from_xywh(
|
||||||
|
rect.left - (self.width / 2.),
|
||||||
|
rect.top - (self.width / 2.),
|
||||||
|
rect.width() + self.width,
|
||||||
|
rect.height() + self.width,
|
||||||
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,6 +176,11 @@ impl Stroke {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn outer_corners(&self, corners: &Corners) -> Corners {
|
pub fn outer_corners(&self, corners: &Corners) -> Corners {
|
||||||
|
if matches!(self.style, StrokeStyle::Dotted | StrokeStyle::Dashed) {
|
||||||
|
// Path at boundary so no corner offset
|
||||||
|
return *corners;
|
||||||
|
}
|
||||||
|
|
||||||
let offset = match self.kind {
|
let offset = match self.kind {
|
||||||
StrokeKind::Center => 0.0,
|
StrokeKind::Center => 0.0,
|
||||||
StrokeKind::Inner => -self.width / 2.0,
|
StrokeKind::Inner => -self.width / 2.0,
|
||||||
|
|||||||
Reference in New Issue
Block a user