mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 05:37:23 -06:00
Image Trace: pressing and holding the eye button shows the source image
The Image Trace panel had no way to compare a trace with its source image except changing View and changing it back. The panel now has an eye button to the right of View, enabled with an Image Trace object selected. While it is held down, the canvas draws the selected Image Trace objects with their source image, whatever their View, and draws their View again on release. The document, its View and the undo history are unchanged. The renderer takes the objects in RenderOptions::trace_source, which is also part of the canvas frame key and the raster-effect cache key.
This commit is contained in:
@@ -2130,6 +2130,7 @@ impl Renderer {
|
||||
if reads.images {
|
||||
let o = f.opts;
|
||||
(doc.images, self.dim_images.map(f32::to_bits), o.smooth_images, o.image_mips, o.trace_views, o.progressive_placed).hash(&mut h);
|
||||
o.trace_source.as_deref().hash(&mut h);
|
||||
}
|
||||
if reads.brushes {
|
||||
self.brushes.library_key(f.doc, self.stamp).hash(&mut h);
|
||||
|
||||
@@ -159,6 +159,10 @@ pub struct RenderOptions {
|
||||
/// Screen view: Image Trace objects draw as their View asks (outlines, the source image…);
|
||||
/// off, they draw their tracing result, as exports and printing do.
|
||||
pub trace_views: bool,
|
||||
/// Screen view with `trace_views` on: the Image Trace objects drawn with the Source Image view
|
||||
/// for this render only, whatever View they have (the Image Trace panel's eye button, while it
|
||||
/// is held down).
|
||||
pub trace_source: Option<Arc<Vec<NodeId>>>,
|
||||
/// Images are sampled smoothly when scaled or rotated; off, each pixel takes its nearest image
|
||||
/// pixel (Pixel Preview with File Handling › Display Bitmaps as Anti-aliased Images off).
|
||||
pub smooth_images: bool,
|
||||
@@ -266,6 +270,7 @@ impl Default for RenderOptions {
|
||||
anti_alias: AntiAlias::Art,
|
||||
progressive_placed: false,
|
||||
trace_views: false,
|
||||
trace_source: None,
|
||||
smooth_images: true,
|
||||
isolated: None,
|
||||
precise: false,
|
||||
@@ -1301,8 +1306,8 @@ impl Renderer {
|
||||
/// What `n` draws inside its transparency group (`knockout`: its children knock each other out).
|
||||
fn draw_content(&mut self, ctx: &mut RenderContext, f: &Frame, n: &Node, knockout: bool) {
|
||||
match &n.kind {
|
||||
NodeKind::Group { children, clip: false } if f.opts.trace_views && !f.opts.outline && n.trace_view() != TraceView::Result => {
|
||||
self.draw_trace_view(ctx, f, children, n.trace_view(), knockout)
|
||||
NodeKind::Group { children, clip: false } if f.opts.trace_views && !f.opts.outline && Self::trace_view(f, n) != TraceView::Result => {
|
||||
self.draw_trace_view(ctx, f, children, Self::trace_view(f, n), knockout)
|
||||
}
|
||||
NodeKind::Layer { children, clip: false, .. } | NodeKind::Group { children, clip: false } => {
|
||||
self.draw_children(ctx, f, children, knockout)
|
||||
@@ -1429,6 +1434,15 @@ impl Renderer {
|
||||
self.live_root = outer;
|
||||
}
|
||||
|
||||
/// The View `n` draws with on screen: Source Image when it is an Image Trace object listed in
|
||||
/// [`RenderOptions::trace_source`], else its own.
|
||||
fn trace_view(f: &Frame, n: &Node) -> TraceView {
|
||||
match &f.opts.trace_source {
|
||||
Some(ids) if n.extras().trace.is_some() && ids.contains(&n.id) => TraceView::Source,
|
||||
_ => n.trace_view(),
|
||||
}
|
||||
}
|
||||
|
||||
/// An Image Trace object's `children` (the hidden source image, then the traced shapes) as
|
||||
/// `view` shows them on screen.
|
||||
fn draw_trace_view(&mut self, ctx: &mut RenderContext, f: &Frame, children: &[Arc<Node>], view: TraceView, knockout: bool) {
|
||||
|
||||
@@ -579,6 +579,57 @@ fn outline_view_draws_paths_from_kept_geometry() {
|
||||
}
|
||||
}
|
||||
|
||||
/// An Image Trace object listed in `trace_source` draws its source image on screen whatever its
|
||||
/// View, also inside a group whose raster effect a renderer keeps; without the list, and in
|
||||
/// exports, it draws its result. A group that is not an Image Trace object draws its members.
|
||||
#[test]
|
||||
fn trace_source_draws_the_source_image_of_the_listed_objects() {
|
||||
use vectorcraft_doc::{ImageBlob, ImageObject};
|
||||
let img = image::RgbaImage::from_pixel(1, 1, image::Rgba([255, 0, 0, 255]));
|
||||
let mut png = vec![];
|
||||
img.write_to(&mut std::io::Cursor::new(&mut png), image::ImageFormat::Png).unwrap();
|
||||
let mut d = Document::new(100.0, 100.0);
|
||||
d.images.insert("red".into(), ImageBlob::new("image/png", png));
|
||||
// A group at `x`: the hidden red source image, then a blue traced square over it.
|
||||
let group = |d: &mut Document, x: f64, traced: bool| {
|
||||
let xf = Affine::translate((x, 10.0)) * Affine::scale(20.0);
|
||||
let im = ImageObject { key: "red".into(), width: 1, height: 1, xf, link: None, placement: Default::default(), pixels: None };
|
||||
let mut source = Node::new(d.alloc_id(), NodeKind::Image(Box::new(im)));
|
||||
source.visible = false;
|
||||
let shape = rect_node(d, Rect::new(x, 10.0, x + 20.0, 30.0), Paint::solid(Color::rgb(0.0, 0.0, 1.0)), Paint::None, 0.0);
|
||||
let mut g = Node::group(d.alloc_id(), vec![Arc::new(source), Arc::new(shape)]);
|
||||
if traced {
|
||||
g.extras_mut().trace = Some(Box::new(serde_json::json!({ "view": "tracingResult" })));
|
||||
}
|
||||
g
|
||||
};
|
||||
let plain = group(&mut d, 10.0, true);
|
||||
let blurred = group(&mut d, 40.0, true);
|
||||
let other = group(&mut d, 70.0, false);
|
||||
let ids = Arc::new(vec![plain.id, blurred.id, other.id]);
|
||||
let mut holder = Node::group(d.alloc_id(), vec![Arc::new(blurred)]);
|
||||
holder.appearance.effects.push(vectorcraft_doc::Effect { id: "blur.gaussian".into(), params: serde_json::json!({ "radius": 1 }), visible: true });
|
||||
let l = d.layers[0].id;
|
||||
for n in [plain, holder, other] {
|
||||
d.insert(Some(l), usize::MAX, n).unwrap();
|
||||
}
|
||||
let screen = RenderOptions { trace_views: true, background: Some([255; 4]), ..Default::default() };
|
||||
let held = RenderOptions { trace_source: Some(ids.clone()), ..screen.clone() };
|
||||
let export = RenderOptions { trace_source: Some(ids), background: Some([255; 4]), ..Default::default() };
|
||||
let red = |p: [u8; 4]| p[0] > 200 && p[2] < 60;
|
||||
let blue = |p: [u8; 4]| p[2] > 200 && p[0] < 60;
|
||||
let mut kept = Renderer::new();
|
||||
for (opts, source) in [(&screen, false), (&held, true), (&screen, false), (&export, false)] {
|
||||
for r in [kept.render(&d, 100, 100, Affine::IDENTITY, opts), Renderer::new().render(&d, 100, 100, Affine::IDENTITY, opts)] {
|
||||
for (x, what) in [(20, "the Image Trace object"), (50, "the one in the blurred group")] {
|
||||
let p = r.pixel(x, 20);
|
||||
assert!(if source { red(p) } else { blue(p) }, "{what} with trace_source {source}: {p:?}");
|
||||
}
|
||||
assert!(blue(r.pixel(80, 20)), "the plain group draws its square: {:?}", r.pixel(80, 20));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn artboards_white_on_pasteboard() {
|
||||
let d = Document::new(50.0, 50.0);
|
||||
|
||||
Reference in New Issue
Block a user