From 1c692e519147ec7db96c9412b70e1790bcf40a20 Mon Sep 17 00:00:00 2001 From: Juan Manuel Verges Solanas Date: Fri, 9 Oct 2026 12:28:26 +0000 Subject: [PATCH] Part of #758: .ai import draws the page once when weighing the layers against it Each comparison of the layers' art with the page made a new renderer and drew both again, so the page was drawn and every image decoded once per text object weighed, plus once more for the final check. The page is now drawn once, and the layers keep one renderer (with its decoded images) across their draws. The page's images keep a renderer of their own, as their keys can match the layers' without their pixels doing so. Co-Authored-By: Claude Opus 5.5 --- crates/eps/src/import/native.rs | 67 +++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/crates/eps/src/import/native.rs b/crates/eps/src/import/native.rs index 6b742528..6c4e2bf5 100644 --- a/crates/eps/src/import/native.rs +++ b/crates/eps/src/import/native.rs @@ -556,25 +556,41 @@ fn lightness(p: &[u8; 4]) -> u8 { ((on_white(p[0]) * 299 + on_white(p[1]) * 587 + on_white(p[2]) * 114) / 1000).min(255) as u8 } -/// How much two documents draw differently (0 to 1), `a` on `ra` and `b` on `rb` (rectangles of -/// the same size), each seen on white. Pixels are compared by lightness: the page of an RGB -/// document is written in the colours' CMYK equivalents, and what the comparison looks for is art -/// that is missing or out of place. -fn difference(a: &Document, ra: Rect, b: &Document, rb: Rect) -> f64 { - // The page doesn't print what isn't printed. - let printed = as_printed(b); - let b = printed.as_ref().unwrap_or(b); - let scale = (400.0 / ra.width().max(ra.height()).max(1.0)).min(4.0); - let mut r = vectorcraft_render::Renderer::new(); - // Drawn on nothing and then seen on white: blending modes have no page to blend with, as in - // the app that wrote the file. - let (x, y) = (r.render_region(a, ra, scale, false), r.render_region(b, rb, scale, false)); - if x.width != y.width || x.height != y.height { - return 1.0; +/// The page drawn once, to weigh the layers' art against it: [`Self::difference`] draws the layers +/// with a renderer of their own, which keeps their decoded images between draws (the page's images +/// can share their keys and not their pixels, so the two don't share one). +struct PageView { + page: vectorcraft_render::Rendered, + layers: vectorcraft_render::Renderer, + scale: f64, +} + +impl PageView { + fn new(frame: &Frame<'_>) -> Self { + let ra = frame.page_rect; + let scale = (400.0 / ra.width().max(ra.height()).max(1.0)).min(4.0); + // Drawn on nothing and then seen on white: blending modes have no page to blend with, as in + // the app that wrote the file. + let page = vectorcraft_render::Renderer::new().render_region(frame.page, ra, scale, false); + Self { page, layers: vectorcraft_render::Renderer::new(), scale } + } + + /// How much `b` on `rb` (a rectangle the page's size) draws differently from the page (0 to 1), + /// each seen on white. Pixels are compared by lightness: the page of an RGB document is written in + /// the colours' CMYK equivalents, and what the comparison looks for is art that is missing or out + /// of place. + fn difference(&mut self, b: &Document, rb: Rect) -> f64 { + // The page doesn't print what isn't printed. + let printed = as_printed(b); + let b = printed.as_ref().unwrap_or(b); + let (x, y) = (&self.page, self.layers.render_region(b, rb, self.scale, false)); + if x.width != y.width || x.height != y.height { + return 1.0; + } + let differing = + x.pixels.as_chunks::<4>().0.iter().zip(y.pixels.as_chunks::<4>().0).filter(|(p, q)| lightness(p).abs_diff(lightness(q)) > 48).count(); + differing as f64 / (f64::from(x.width) * f64::from(x.height)).max(1.0) } - let differing = - x.pixels.as_chunks::<4>().0.iter().zip(y.pixels.as_chunks::<4>().0).filter(|(p, q)| lightness(p).abs_diff(lightness(q)) > 48).count(); - differing as f64 / (f64::from(x.width) * f64::from(x.height)).max(1.0) } /// More of the page than this differing between the layers' art and the page's own, and the @@ -671,11 +687,12 @@ fn build(data: &[u8], visible: Option<&Document>, page: Page) -> Result<(Documen } match frame { Some(frame) => { - let unseen = prune_unseen(&frame, &mut doc); + let mut view = PageView::new(&frame); + let unseen = prune_unseen(&frame, &mut view, &mut doc); if unseen > 0 { notes.push(format!("{unseen} {UNSEEN_TEXT}")); } - notes.extend(compare(&frame, &doc)?); + notes.extend(compare(&frame, &mut view, &doc)?); } None => unmark(&mut doc.layers, true, &mut vec![]), } @@ -729,14 +746,14 @@ fn take_out(nodes: &mut Vec>, id: NodeId, hide: bool) -> bool { /// has no type to match them, and that the layers look no worse without, are not drawn by the app /// that wrote the file (which paints every shown text object; it keeps the type of what was turned /// to outlines): they are taken out. How many. -fn prune_unseen(frame: &Frame<'_>, doc: &mut Document) -> usize { +fn prune_unseen(frame: &Frame<'_>, view: &mut PageView, doc: &mut Document) -> usize { let mut found = vec![]; unmark(&mut doc.layers, true, &mut found); let rect = frame.rect; if found.is_empty() || found.len() > MAX_WEIGHED { return 0; } - let mut with = difference(frame.page, frame.page_rect, doc, rect); + let mut with = view.difference(doc, rect); let mut removed = 0; for (id, bounds) in found { if bounds.is_none_or(|b| b.intersect(rect).is_zero_area()) { @@ -746,7 +763,7 @@ fn prune_unseen(frame: &Frame<'_>, doc: &mut Document) -> usize { if !take_out(&mut without.layers, id, true) { continue; } - let d = difference(frame.page, frame.page_rect, &without, rect); + let d = view.difference(&without, rect); if d - with <= UNSEEN_MARGIN { take_out(&mut doc.layers, id, false); with = d; @@ -758,8 +775,8 @@ fn prune_unseen(frame: &Frame<'_>, doc: &mut Document) -> usize { /// Do the layers' art and the page's own look alike? An error says they don't; `Ok` has the note /// to give when they differ a little. -fn compare(frame: &Frame<'_>, layered: &Document) -> Result, String> { - let diff = difference(frame.page, frame.page_rect, layered, frame.rect); +fn compare(frame: &Frame<'_>, view: &mut PageView, layered: &Document) -> Result, String> { + let diff = view.difference(layered, frame.rect); if diff > MAX_DIFFERENCE { return Err(format!("the art on them differs from the page's in {:.0}% of it", diff * 100.0)); }