Files
vectorcraft/crates/render/src/tests_layeropts.rs
T

238 lines
11 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Layer Options on screen: Preview off draws a layer in outline, Dim Images fades its images;
//! exports (which leave templates out) draw both as they are.
use vectorcraft_color::{Color, Paint};
use vectorcraft_doc::{Appearance, ImageBlob, ImageObject, Node, NodeId, NodeKind};
use vectorcraft_geom::shapes;
use super::*;
/// A red square (10..40) and a 30×30 red image at (50, 10), on one layer set up by `layer`.
fn doc(layer: impl FnOnce(&mut NodeKind)) -> Document {
let mut d = Document::new(100.0, 100.0);
let img = image::RgbaImage::from_pixel(30, 30, image::Rgba([255, 0, 0, 255]));
let mut png = vec![];
img.write_to(&mut std::io::Cursor::new(&mut png), image::ImageFormat::Png).unwrap();
d.images.insert("red".into(), ImageBlob::new("image/png", png));
let l = d.layers[0].id;
let red = Appearance::basic(Paint::solid(Color::rgb(1.0, 0.0, 0.0)), Paint::None, 0.0);
let sq = Node::path(d.alloc_id(), shapes::rectangle(Rect::new(10.0, 10.0, 40.0, 40.0)), red);
d.insert(Some(l), 0, sq).unwrap();
let mut im = Node::new(
NodeId(0),
NodeKind::Image(Box::new(ImageObject {
key: "red".into(),
width: 30,
height: 30,
xf: Affine::translate((50.0, 10.0)),
link: None,
placement: Default::default(),
pixels: None,
})),
);
im.id = d.alloc_id();
d.insert(Some(l), 9, im).unwrap();
layer(&mut d.node_mut(l).unwrap().kind);
d
}
fn render(d: &Document, export: bool) -> Rendered {
let opts = RenderOptions { background: Some([255, 255, 255, 255]), skip_templates: export, ..Default::default() };
Renderer::new().render(d, 100, 100, Affine::IDENTITY, &opts)
}
#[test]
fn preview_off_draws_the_layer_in_outline_on_screen_only() {
let d = doc(|k| {
if let NodeKind::Layer { preview, .. } = k {
*preview = false;
}
});
assert_eq!(render(&d, false).pixel(25, 25), [255, 255, 255, 255], "no fill in outline");
assert_eq!(render(&d, true).pixel(25, 25), [255, 0, 0, 255], "exports draw it as it is");
}
#[test]
fn dim_images_fades_only_the_images() {
let plain = render(&doc(|_| {}), false);
assert_eq!(plain.pixel(65, 25), [255, 0, 0, 255]);
let d = doc(|k| {
if let NodeKind::Layer { dim_images, .. } = k {
*dim_images = Some(50);
}
});
let img = render(&d, false);
let [r, g, b, _] = img.pixel(65, 25);
assert!(r == 255 && (120..=135).contains(&g) && g == b, "half way to white: {:?}", img.pixel(65, 25));
assert_eq!(img.pixel(25, 25), [255, 0, 0, 255], "vector art stays");
assert_eq!(render(&d, true).pixel(65, 25), [255, 0, 0, 255], "exports are not dimmed");
}
/// #1683: a template layer dims its images by its Dim Images percentage and nothing else: with
/// the option off they show at full strength, and the layer's paths are never faded.
#[test]
fn a_template_layer_dims_its_images_by_dim_images() {
let template = |dim: Option<u8>| {
let d = doc(|k| {
if let NodeKind::Layer { template, dim_images, .. } = k {
(*template, *dim_images) = (true, dim);
}
});
let img = render(&d, false);
(img.pixel(65, 25), img.pixel(25, 25))
};
let red = [255, 0, 0, 255];
assert_eq!(template(None), (red, red), "Dim Images off: as it is");
let (image, path) = template(Some(50));
assert!(image[0] == 255 && (120..=135).contains(&image[1]) && image[1] == image[2], "half way to white: {image:?}");
assert_eq!(path, red, "vector art stays");
let (image, _) = template(Some(20));
assert!((198..=210).contains(&image[1]) && image[1] == image[2], "a fifth of its strength: {image:?}");
assert_eq!(template(Some(100)), (red, red));
}
/// In Outline view a template layer still draws in Preview, dimmed, so its picture can be traced;
/// the layer above it draws in outline (#1486).
#[test]
fn a_template_layer_stays_in_preview_in_outline_view() {
let mut d = doc(|k| {
if let NodeKind::Layer { template, preview, dim_images, .. } = k {
// As Layer Options makes a template: its images dimmed to 50 %.
(*template, *dim_images) = (true, Some(50));
// Preview off doesn't put a template in outline either.
*preview = false;
}
});
let top = Node::layer(d.alloc_id(), "Drawing", vectorcraft_doc::LayerColor::Preset(0));
let top_id = d.insert(None, 9, top).unwrap();
let red = Appearance::basic(Paint::solid(Color::rgb(1.0, 0.0, 0.0)), Paint::None, 0.0);
let sq = Node::path(d.alloc_id(), shapes::rectangle(Rect::new(10.0, 60.0, 40.0, 90.0)), red);
d.insert(Some(top_id), 0, sq).unwrap();
let draw = |outline: bool| {
let opts = RenderOptions { background: Some([255, 255, 255, 255]), outline, ..Default::default() };
Renderer::new().render(&d, 100, 100, Affine::IDENTITY, &opts)
};
let (preview, outline) = (draw(false), draw(true));
let [r, g, b, _] = outline.pixel(65, 25);
assert!(r == 255 && (120..=135).contains(&g) && g == b, "the template's image, dimmed: {:?}", outline.pixel(65, 25));
assert_eq!(outline.pixel(65, 25), preview.pixel(65, 25), "as in Preview");
assert_eq!(outline.pixel(25, 75), [255, 255, 255, 255], "the drawing layer has no fill in outline");
assert_eq!(preview.pixel(25, 75), [255, 0, 0, 255]);
}
/// View › Hide Template (#2000) leaves template layers out of the screen view and nothing else:
/// a guide path on another layer still draws, unlike in an export.
#[test]
fn hide_template_leaves_only_the_template_layers_out() {
let mut d = doc(|k| {
if let NodeKind::Layer { template, .. } = k {
*template = true;
}
});
let top = d.add_layer(Some("Guides"));
let mut g = Node::path(d.alloc_id(), shapes::rectangle(Rect::new(70.0, 70.0, 90.0, 90.0)), Appearance::default());
if let NodeKind::Path { guide, .. } = &mut g.kind {
*guide = true;
}
d.insert(Some(top), 0, g).unwrap();
let render = |hide: bool| {
let opts = RenderOptions { background: Some([255, 255, 255, 255]), hide_templates: hide, ..Default::default() };
Renderer::new().render(&d, 100, 100, Affine::IDENTITY, &opts)
};
let guide_drawn = |r: &Rendered| (60..100).flat_map(|y| (60..100).map(move |x| (x, y))).any(|(x, y)| r.pixel(x, y) != [255, 255, 255, 255]);
let shown = render(false);
assert_ne!(shown.pixel(25, 25), [255, 255, 255, 255], "the template layer's square shows (dimmed)");
assert!(guide_drawn(&shown));
let hidden = render(true);
assert_eq!(hidden.pixel(25, 25), [255, 255, 255, 255], "Hide Template: the template layer is left out");
assert_eq!(hidden.pixel(65, 25), [255, 255, 255, 255], "its image too");
assert!(guide_drawn(&hidden), "the guide on the other layer still draws");
}
/// A guide path draws on screen only: exports leave it out, as they leave templates out (and as
/// the SVG, PSD and PDF exports and printing already do).
#[test]
fn guides_draw_on_screen_only() {
let mut d = Document::new(100.0, 100.0);
let l = d.layers[0].id;
let mut g = Node::path(d.alloc_id(), shapes::rectangle(Rect::new(70.0, 70.0, 90.0, 90.0)), Appearance::default());
if let NodeKind::Path { guide, .. } = &mut g.kind {
*guide = true;
}
d.insert(Some(l), 0, g).unwrap();
let drawn = |export: bool| {
let r = render(&d, export);
(60..100).flat_map(|y| (60..100).map(move |x| (x, y))).filter(|&(x, y)| r.pixel(x, y) != [255, 255, 255, 255]).count()
};
assert!(drawn(false) > 0, "on screen the guide shows");
assert_eq!(drawn(true), 0, "exports leave it out");
}
/// #3219: View > Hide Guides hides paths made into guides without using the export/template
/// filtering switch; Show Guides (the default) draws them again.
#[test]
fn hide_guides_hides_guide_paths_on_screen() {
let mut d = Document::new(100.0, 100.0);
let l = d.layers[0].id;
let mut g = Node::path(d.alloc_id(), shapes::rectangle(Rect::new(70.0, 70.0, 90.0, 90.0)), Appearance::default());
if let NodeKind::Path { guide, .. } = &mut g.kind {
*guide = true;
}
d.insert(Some(l), 0, g).unwrap();
let pixels = |hide_guides: bool| {
let opts = RenderOptions { background: Some([255; 4]), hide_guides, ..Default::default() };
let r = Renderer::new().render(&d, 100, 100, Affine::IDENTITY, &opts);
(60..100).flat_map(|y| (60..100).map(move |x| (x, y))).filter(|&(x, y)| r.pixel(x, y) != [255; 4]).count()
};
assert!(pixels(false) > 0, "Show Guides draws the guide path");
assert_eq!(pixels(true), 0, "Hide Guides leaves the guide path out");
}
/// #3219: hiding guides omits guide paths, including those inside cached group effects, without
/// hiding ordinary artwork. The same renderer must show them again when the view is toggled back.
#[test]
fn hidden_guide_paths_leave_the_artwork_and_refresh_cached_effects() {
for effect in [false, true] {
let mut d = Document::new(120.0, 120.0);
let layer = d.layers[0].id;
let mut guide =
Node::path(d.alloc_id(), shapes::rectangle(Rect::new(20.5, 20.5, 60.5, 60.5)), Appearance::basic(Paint::None, Paint::None, 0.0));
if let NodeKind::Path { guide, .. } = &mut guide.kind {
*guide = true;
}
let id = guide.id;
let art = Node::path(
d.alloc_id(),
shapes::rectangle(Rect::new(80.0, 80.0, 100.0, 100.0)),
Appearance::basic(Paint::solid(Color::rgb(0.0, 0.0, 1.0)), Paint::None, 0.0),
);
let mut group = Node::group(d.alloc_id(), vec![Arc::new(guide), Arc::new(art)]);
if effect {
group.appearance.effects.push(vectorcraft_doc::Effect {
id: "blur.gaussian".into(),
params: serde_json::json!({"radius":2}),
visible: true,
});
}
d.insert(Some(layer), 0, group).unwrap();
let mut without_guide = d.clone();
without_guide.remove(id).unwrap();
let mut renderer = Renderer::new();
for outline in [false, true] {
let opts = RenderOptions { outline, background: Some([255; 4]), ..Default::default() };
let shown = renderer.render(&d, 120, 120, Affine::IDENTITY, &opts);
let hidden = RenderOptions { hide_guides: true, ..opts.clone() };
let expected = Renderer::new().render(&without_guide, 120, 120, Affine::IDENTITY, &hidden);
assert_ne!(shown.pixels, expected.pixels, "the guide is initially visible, effect={effect}, outline={outline}");
assert!(
renderer.render(&d, 120, 120, Affine::IDENTITY, &hidden).pixels == expected.pixels,
"only guides hide, effect={effect}, outline={outline}"
);
assert!(
renderer.render(&d, 120, 120, Affine::IDENTITY, &opts).pixels == shown.pixels,
"guides show again, effect={effect}, outline={outline}"
);
}
}
}