mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Merge pull request #3697 from Taha-Mahmoodi/fix/2788-dashed-opacity
Fix #2788: a dashed stroke with object opacity folds the opacity into its colour instead of compositing a canvas-wide layer
This commit is contained in:
@@ -2229,14 +2229,16 @@ fn single_plain_fill(n: &Node) -> bool {
|
||||
matches!((painted.next(), painted.next()), (Some(AppearanceItem::Fill(f)), None) if f.blend == vectorcraft_color::BlendMode::Normal && f.opacity >= 1.0 && !matches!(f.paint, vectorcraft_color::Paint::Pattern { .. }))
|
||||
}
|
||||
|
||||
/// Number of visible painted fill/stroke items (opacity folding is exact only for one).
|
||||
/// Number of visible painted fill/stroke items (opacity folding is exact only for one). A dashed
|
||||
/// stroke is one paint: its dashes are outlined into one path and filled once, so overlapping
|
||||
/// dashes don't darken where they overlap.
|
||||
fn painted_items(n: &Node) -> usize {
|
||||
n.appearance
|
||||
.items
|
||||
.iter()
|
||||
.filter(|i| match i {
|
||||
AppearanceItem::Fill(f) => f.visible && !f.paint.is_none() && matches!(f.paint, vectorcraft_color::Paint::Solid { .. }),
|
||||
AppearanceItem::Stroke(s) => s.visible && !s.paint.is_none() && s.width > 0.0 && matches!(s.paint, vectorcraft_color::Paint::Solid { .. }) && s.dash.is_none(),
|
||||
AppearanceItem::Stroke(s) => s.visible && !s.paint.is_none() && s.width > 0.0 && matches!(s.paint, vectorcraft_color::Paint::Solid { .. }),
|
||||
})
|
||||
.count()
|
||||
.max(if n.appearance.items.iter().any(|i| matches!(i, AppearanceItem::Fill(f) if f.visible && !matches!(f.paint, vectorcraft_color::Paint::Solid { .. } | vectorcraft_color::Paint::None)) || matches!(i, AppearanceItem::Stroke(s) if s.visible && !matches!(s.paint, vectorcraft_color::Paint::Solid { .. } | vectorcraft_color::Paint::None))) { 2 } else { 0 })
|
||||
|
||||
@@ -956,3 +956,34 @@ fn zero_length_subpaths_draw_dots_with_their_cap() {
|
||||
let butt = dots(vectorcraft_doc::LineCap::Butt);
|
||||
assert_eq!(butt.pixels.iter().filter(|&&p| p != 255).count(), 0, "a butt cap draws no dot");
|
||||
}
|
||||
|
||||
/// #2788: a dashed solid stroke is one paint, so its object opacity folds into its colour instead
|
||||
/// of compositing a viewport-wide layer; the result matches the layered one, overlapping dashes
|
||||
/// included.
|
||||
#[test]
|
||||
fn dashed_stroke_opacity_folds_into_its_colour() {
|
||||
let dashed = |d: &mut Document, object: f32, stroke: f32| {
|
||||
let mut n = rect_node(d, Rect::new(20.0, 20.0, 80.0, 60.0), Paint::None, Paint::solid(Color::rgb(0.2, 0.4, 0.9)), 6.0);
|
||||
let st = n.appearance.stroke_mut().unwrap();
|
||||
st.dash = Some(vectorcraft_doc::Dash { pattern: vec![4.0, 1.0], ..Default::default() });
|
||||
st.cap = vectorcraft_doc::LineCap::Round;
|
||||
st.opacity = stroke;
|
||||
n.opacity = object;
|
||||
n
|
||||
};
|
||||
let doc = |object: f32, stroke: f32| {
|
||||
let mut d = Document::new(100.0, 100.0);
|
||||
let n = dashed(&mut d, object, stroke);
|
||||
let l = d.layers[0].id;
|
||||
d.insert(Some(l), 0, n).unwrap();
|
||||
d
|
||||
};
|
||||
let mut d = Document::new(100.0, 100.0);
|
||||
assert_eq!(painted_items(&dashed(&mut d, 0.6, 1.0)), 1, "a dashed stroke takes the folded fast path");
|
||||
let folded = render(&doc(0.6, 1.0));
|
||||
let layered = render(&doc(1.0, 0.6));
|
||||
assert_ne!(folded.pixel(20, 40), [255, 255, 255, 255], "the stroke is drawn");
|
||||
for (a, b) in folded.pixels.chunks(4).zip(layered.pixels.chunks(4)) {
|
||||
assert!(a.iter().zip(b).all(|(x, y)| (*x as i32 - *y as i32).abs() <= 1), "folded {a:?} vs layered {b:?}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user