PDF import: a mesh-painted stroke's outline keeps dash and tolerance in reason

This commit is contained in:
bflatastic
2026-10-30 19:45:52 +07:00
parent 75e8f69734
commit b127918be5
+27 -3
View File
@@ -245,6 +245,8 @@ pub(super) fn stroke_layer(paint: Paint, opacity: f32, p: &StrokeProps, scale: f
/// The most entries of a dash array kept (an even count keeps it a pattern of dashes and gaps).
const MAX_DASH_ENTRIES: usize = 64;
/// Most dash periods [`stroke_outline`] cuts a stroke into.
const MAX_OUTLINE_DASHES: f64 = 1e5;
/// The area stroke `p` paints along `path` (in its own space, before `transform`), in the document:
/// where a shading painting the stroke shows. Width, caps, joins and dashes as [`stroke_layer`]
@@ -258,11 +260,14 @@ pub(super) fn stroke_outline(path: &BezPath, transform: Affine, p: &StrokeProps)
kurbo::Stroke::new(line_width(p, scale) / scale).with_join(p.line_join).with_caps(p.line_cap).with_miter_limit(p.miter_limit as f64);
let dashes: Vec<f64> = p.dash_array.iter().take(MAX_DASH_ENTRIES).map(|v| *v as f64).collect();
let dash = Dash { pattern: dashes.clone(), offset: p.dash_offset as f64, align_corners: false };
if dash.is_dashed() {
// A dash pattern far finer than the path would cut it into countless pieces: it strokes solid.
let period: f64 = dashes.iter().sum();
if dash.is_dashed() && period > 0.0 && kurbo::Shape::perimeter(path, 1e-3) / period < MAX_OUTLINE_DASHES {
style = style.with_dashes(dash.offset, dashes);
}
// To a hundredth of a point in the document.
let mut out = kurbo::stroke(path.iter(), &style, &kurbo::StrokeOpts::default(), 0.01 / scale);
// To a hundredth of a point in the document, within reason at extreme scales.
let tolerance = (0.01 / scale).clamp(1e-4, 1.0);
let mut out = kurbo::stroke(path.iter(), &style, &kurbo::StrokeOpts::default(), tolerance);
out.apply_affine(transform);
(!out.elements().is_empty()).then_some(out)
}
@@ -301,3 +306,22 @@ pub(super) fn resize_alpha(a: &LumaData, w: u32, h: u32) -> Vec<u8> {
None => vec![255; (w * h) as usize],
}
}
#[cfg(test)]
mod stroke_outline_tests {
use super::*;
/// A dash pattern far finer than the path strokes it solid instead of cutting it into
/// millions of pieces; one in reason dashes it.
#[test]
fn a_dash_far_finer_than_the_path_strokes_solid() {
let mut path = BezPath::new();
path.move_to((0.0, 0.0));
path.line_to((1000.0, 0.0));
let props = |dash: f32| StrokeProps { line_width: 2.0, dash_array: [dash; 4].into(), ..StrokeProps::default() };
let solid = stroke_outline(&path, Affine::IDENTITY, &props(1e-6)).unwrap();
assert!(solid.elements().len() < 20, "{} elements", solid.elements().len());
let dashed = stroke_outline(&path, Affine::IDENTITY, &props(10.0)).unwrap();
assert!(dashed.elements().len() > 100, "dashed: {} elements", dashed.elements().len());
}
}