Merge main into feat/character-anti-aliasing-method

This commit is contained in:
dotCooCoo
2026-10-30 19:23:59 -07:00
189 changed files with 13534 additions and 577 deletions
+2
View File
@@ -121,6 +121,8 @@ impl Renderer {
}
} else if f.opts.mark_undecoded_images && !pending && (!outline || f.doc.setup.outline_images) {
self.draw_undecoded_mark(ctx, f, im, area);
} else if !pending && !outline {
self.image_left_out = self.image_left_out.map(|_| true);
}
if outline {
let mut p = rect.to_path(0.1);
+23 -4
View File
@@ -166,9 +166,9 @@ impl Renderer {
// mask is rendered over those only.
let bounds = self.bounds_of(f.symbols, a);
let Some((r, sub)) = group::area(ctx, f, bounds) else { return };
let mask = Some(self.mask_values(&sub, m, r.w, r.h));
let Some(mask) = self.mask_values(&sub, m, r.w, r.h) else { return };
let blends = a.blend != vectorcraft_color::BlendMode::Normal || (!a.isolate && self.blends_through(a));
self.group(ctx, f, Composite { mask, blends, bounds, ..Default::default() }, &mut |r, c, fr| r.draw_arc_body(c, fr, a));
self.group(ctx, f, Composite { mask: Some(mask), blends, bounds, ..Default::default() }, &mut |r, c, fr| r.draw_arc_body(c, fr, a));
return;
}
self.draw_arc_body(ctx, f, a);
@@ -176,7 +176,7 @@ impl Renderer {
/// The coverage of opacity mask `m` per output pixel (row-major): its art rendered offscreen,
/// as luminance ([`mask_value`]).
pub(super) fn mask_values(&mut self, f: &Frame, m: &vectorcraft_doc::OpacityMask, w: u16, h: u16) -> Vec<u8> {
pub(super) fn mask_values(&mut self, f: &Frame, m: &vectorcraft_doc::OpacityMask, w: u16, h: u16) -> Option<Vec<u8>> {
let mut mctx = f.offscreen_context(w, h);
// Mask art is a picture of its own: it takes no part in a knockout group around the object,
// and its luminance is that of its screen colours.
@@ -187,10 +187,22 @@ impl Renderer {
std::mem::take(&mut self.clip_paths),
self.picture.take(),
);
let image_left_out = self.image_left_out.replace(false);
let failures = crate::paint::decode_failures();
let pattern_images_left_out = crate::pattern::images_left_out();
self.draw_kept_node(&mut mctx, &Frame { mt: false, ink: Ink::Display, ..*f }, &m.art);
// Pattern tiles are drawn without undecoded-image marks, so an image a tile left out counts
// with the marks on too.
let incomplete = self.image_left_out.unwrap_or(false)
|| crate::pattern::images_left_out() > pattern_images_left_out
|| (!f.opts.mark_undecoded_images && crate::paint::decode_failures() > failures);
self.image_left_out = image_left_out.map(|outer| outer || incomplete);
let pm = self.finish(&mut mctx, self.raster);
(self.knockout, self.nested, self.backdrop, self.clip_paths, self.picture) = outer;
pm.data().iter().map(|p| mask_value(p.r, p.g, p.b, p.a, m.clip, m.invert)).collect()
if incomplete {
return None;
}
Some(pm.data().iter().map(|p| mask_value(p.r, p.g, p.b, p.a, m.clip, m.invert)).collect())
}
/// Everything [`Self::draw_arc`] does after culling.
@@ -593,6 +605,13 @@ impl Renderer {
if self.skipped(f, c) {
continue;
}
// Hard-edge coverage is binary, so an opaque fill replaces its knockout shape exactly.
// This also serves supersampled exports. Analytical AA keeps the erase/add passes:
// their intermediate rounding can differ from source-over at partially covered edges.
if f.opts.anti_alias == crate::AntiAlias::None && crate::node_checks::opaque_knockout_fill(c) {
self.draw_arc(ctx, f, c);
continue;
}
self.knockout_pass(ctx, f, c, Compose::DestOut);
match &backdrop {
Some(b) => self.knockout_over(ctx, f, c, b),