This commit is contained in:
Alejandro Alonso
2026-02-10 13:38:03 +01:00
parent de49a1e980
commit 1ee07225b5
4 changed files with 66 additions and 75 deletions

View File

@@ -82,7 +82,7 @@ export default defineConfig({
snapshotPathTemplate: "{testDir}/{testFilePath}-snapshots/{arg}.png",
timeout: 2 * 60 * 1000,
expect: {
timeout: process.env.CI ? 20000 : 10000,
timeout: process.env.CI ? 20000 : 100000,
toHaveScreenshot: {
maxDiffPixelRatio: 0.001,
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 KiB

After

Width:  |  Height:  |  Size: 318 KiB

View File

@@ -1,7 +1,8 @@
use crate::math::{Matrix, Point, Rect};
use crate::shapes::{
Corners, Fill, ImageFill, Path, Shape, Stroke, StrokeCap, StrokeKind, SvgAttrs, Type,
Corners, Fill, ImageFill, Path, Shape, Stroke, StrokeCap, StrokeKind, StrokeStyle, SvgAttrs,
Type,
};
use skia_safe::{self as skia, ImageFilter, RRect};
@@ -29,80 +30,18 @@ fn draw_stroke_on_rect(
// - A smaller rect if it's an inner stroke
let stroke_rect = stroke.aligned_rect(rect, scale);
let mut paint = stroke.to_paint(selrect, svg_attrs, antialias);
// Apply both blur and shadow filters if present, composing them if necessary.
let filter = compose_filters(blur, shadow);
paint.set_image_filter(filter);
// For inner/outer strokes, we need clipping to prevent stroke pattern
// (like dotted circles) from appearing in wrong areas
match stroke.kind {
StrokeKind::Inner => {
// Inner: clip to original rect to hide parts outside boundary
canvas.save();
match corners {
Some(radii) => {
let rrect = RRect::new_rect_radii(*rect, radii);
canvas.clip_rrect(rrect, skia::ClipOp::Intersect, antialias);
}
None => {
canvas.clip_rect(*rect, skia::ClipOp::Intersect, antialias);
}
}
// Draw the stroke
match corners {
Some(radii) => {
let radii = stroke.outer_corners(radii);
let rrect = RRect::new_rect_radii(stroke_rect, &radii);
canvas.draw_rrect(rrect, &paint);
}
None => {
canvas.draw_rect(stroke_rect, &paint);
}
}
canvas.restore();
match corners {
Some(radii) => {
let radii = stroke.outer_corners(radii);
let rrect = RRect::new_rect_radii(stroke_rect, &radii);
canvas.draw_rrect(rrect, &paint);
}
StrokeKind::Outer => {
// Outer: clip to exclude original rect to hide parts inside boundary
canvas.save();
match corners {
Some(radii) => {
let rrect = RRect::new_rect_radii(*rect, radii);
canvas.clip_rrect(rrect, skia::ClipOp::Difference, antialias);
}
None => {
canvas.clip_rect(*rect, skia::ClipOp::Difference, antialias);
}
}
// Draw the stroke
match corners {
Some(radii) => {
let radii = stroke.outer_corners(radii);
let rrect = RRect::new_rect_radii(stroke_rect, &radii);
canvas.draw_rrect(rrect, &paint);
}
None => {
canvas.draw_rect(stroke_rect, &paint);
}
}
canvas.restore();
}
StrokeKind::Center => {
// Center strokes don't need clipping
match corners {
Some(radii) => {
let radii = stroke.outer_corners(radii);
let rrect = RRect::new_rect_radii(stroke_rect, &radii);
canvas.draw_rrect(rrect, &paint);
}
None => {
canvas.draw_rect(stroke_rect, &paint);
}
}
None => {
canvas.draw_rect(stroke_rect, &paint);
}
}
}
@@ -126,12 +65,34 @@ fn draw_stroke_on_circle(
// - A smaller oval if it's an inner stroke
let stroke_rect = stroke.aligned_rect(rect, scale);
let mut paint = stroke.to_paint(selrect, svg_attrs, antialias);
// Apply both blur and shadow filters if present, composing them if necessary.
let filter = compose_filters(blur, shadow);
paint.set_image_filter(filter);
// For inner/outer strokes, we need clipping to prevent stroke pattern
// Helper to draw the stroke without any clipping logic.
let draw_simple = |canvas: &skia::Canvas| {
canvas.draw_oval(stroke_rect, &paint);
};
// Igual que en el caso de los rectángulos: con blur activo, el clipping
// introduce un corte visible en el blur. Para evitarlo, cuando hay blur
// volvemos al comportamiento sin clipping.
if blur.is_some() {
draw_simple(canvas);
return;
}
// Para la mayoría de estilos sin blur, basta con dibujar sin clipping.
// Solo mantenemos clipping para el caso problemático de dotted inner/outer
// (evitar ver “medios puntos” cruzando el borde).
if !matches!(stroke.style, StrokeStyle::Dotted)
|| matches!(stroke.kind, StrokeKind::Center)
{
draw_simple(canvas);
return;
}
// For inner/outer dotted strokes, we need clipping to prevent stroke pattern
// (like dotted circles) from appearing in wrong areas
match stroke.kind {
StrokeKind::Inner => {

View File

@@ -218,13 +218,43 @@ impl Stroke {
StrokeKind::Center => self.width / 2.0,
StrokeKind::Outer => self.width,
};
circle_path.add_circle((0.0, 0.0), width, None);
// Experimental: usar exactamente media circunferencia:
// - Inner: arco superior (mirando “hacia dentro”)
// - Outer: arco inferior (mirando “hacia fuera”)
// - Center: círculo completo como antes
match self.kind {
StrokeKind::Inner => {
let rect = skia::Rect::from_xywh(
-width,
-width,
width * 2.0,
width * 2.0,
);
// De (width, 0) a (-width, 0) pasando por y > 0
circle_path.add_arc(rect, 0.0, 180.0);
}
StrokeKind::Outer => {
let rect = skia::Rect::from_xywh(
-width,
-width,
width * 2.0,
width * 2.0,
);
// De (width, 0) a (-width, 0) pasando por y < 0
circle_path.add_arc(rect, 0.0, -180.0);
}
StrokeKind::Center => {
circle_path.add_circle((0.0, 0.0), width, None);
}
}
let advance = self.width + 5.0;
skia::PathEffect::path_1d(
&circle_path,
advance,
0.0,
skia::path_1d_path_effect::Style::Translate,
skia::path_1d_path_effect::Style::Rotate,
)
}
StrokeStyle::Dashed => {