diff --git a/frontend/playwright.config.js b/frontend/playwright.config.js index e673f1da8f..2118f03140 100644 --- a/frontend/playwright.config.js +++ b/frontend/playwright.config.js @@ -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, }, diff --git a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-a-file-with-blurs-applied-to-any-kind-of-shape-1.png b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-a-file-with-blurs-applied-to-any-kind-of-shape-1.png index de3fd45317..09b8026ae5 100644 Binary files a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-a-file-with-blurs-applied-to-any-kind-of-shape-1.png and b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-a-file-with-blurs-applied-to-any-kind-of-shape-1.png differ diff --git a/render-wasm/src/render/strokes.rs b/render-wasm/src/render/strokes.rs index 2e889abe26..c6273d1c33 100644 --- a/render-wasm/src/render/strokes.rs +++ b/render-wasm/src/render/strokes.rs @@ -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 => { diff --git a/render-wasm/src/shapes/strokes.rs b/render-wasm/src/shapes/strokes.rs index 3e4d719188..baa40c88d2 100644 --- a/render-wasm/src/shapes/strokes.rs +++ b/render-wasm/src/shapes/strokes.rs @@ -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 => {