mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 05:37:23 -06:00
Envelopes: Anti-Alias and Preserve Shape Using change how distorted images draw
Envelope Options stored Anti-Alias and Preserve Shape Using, but nothing that draws an image read them: a distorted image was always smoothed and always cut into clip groups. - With Anti-Alias off, each piece of a distorted image is marked pixelated (saved as `pixelated`). The renderer draws it with nearest-neighbor sampling and without mips, SVG export writes `image-rendering: pixelated`, and PDF export writes it without /Interpolate. - With Preserve Shape Using Transparency, each piece is the image with a white opacity mask in the piece's shape instead of a clip group. The renderer paints an image whose mask is one plain white path straight into that path, as it does for an image in a clip group, when the path lies inside the image's frame. Docs: docs/mcp.md, docs/target-app-parity.md.
This commit is contained in:
@@ -157,8 +157,10 @@ fn affine_between(s: [Point; 3], d: [Point; 3]) -> Option<Affine> {
|
||||
|
||||
/// Image `n` (`im`) pushed through the warp as a raster mesh warp: its frame cut into
|
||||
/// `w.cells`² cells of two triangles, each a clip group showing the image under the affine map
|
||||
/// that takes the triangle where the warp takes its corners. Each piece reaches a little past its
|
||||
/// edges (at most half a point) so neighbours overlap and no seams show.
|
||||
/// that takes the triangle where the warp takes its corners (with Preserve Shape Using
|
||||
/// Transparency, the image masked by the triangle; with Anti-Alias off, drawn without smoothing).
|
||||
/// Each piece reaches a little past its edges (at most half a point) so neighbours overlap and no
|
||||
/// seams show.
|
||||
fn warp_image(n: &Node, im: &ImageObject, w: &Warper) -> Node {
|
||||
let k = w.cells.max(1);
|
||||
// Two triangles a cell, three objects each; an image an inner envelope already cut into
|
||||
@@ -189,11 +191,20 @@ fn warp_image(n: &Node, im: &ImageObject, w: &Warper) -> Node {
|
||||
if len > 1e-12 { *p + v * (grow / len) } else { *p }
|
||||
})
|
||||
.collect();
|
||||
let mut clip = Node::path(n.id, PathData::single(SubPath::polyline(&tri, true)), Appearance::default());
|
||||
let shape = PathData::single(SubPath::polyline(&tri, true));
|
||||
let mut image = Node::new(n.id, NodeKind::Image(Box::new(ImageObject { xf, ..im.clone() })));
|
||||
image.extras_mut().pixelated = !w.options.anti_alias;
|
||||
if w.options.preserve_shape == PreserveShape::Transparency {
|
||||
// Preserve Shape Using Transparency: the triangle is the piece's alpha (a
|
||||
// white opacity mask) instead of its clipping path.
|
||||
let white = Node::path(n.id, shape, Appearance::basic(Paint::solid(Color::WHITE), Paint::None, 0.0));
|
||||
pieces.push(Arc::new(image.with_mask(Some(Box::new(crate::OpacityMask::new(white, true))))));
|
||||
continue;
|
||||
}
|
||||
let mut clip = Node::path(n.id, shape, Appearance::default());
|
||||
if let NodeKind::Path { clipping, .. } = &mut clip.kind {
|
||||
*clipping = true;
|
||||
}
|
||||
let image = Node::new(n.id, NodeKind::Image(Box::new(ImageObject { xf, ..im.clone() })));
|
||||
pieces.push(Arc::new(Node::new(n.id, NodeKind::Group { children: vec![Arc::new(clip), Arc::new(image)], clip: true })));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ pub struct NodeExtras {
|
||||
/// The group holds the glyph outlines of type. Geometry effects that reshape it move and scale
|
||||
/// the placed gradients in it with the outlines.
|
||||
pub type_outlines: bool,
|
||||
/// An image drawn without smoothing: each pixel of the picture takes the color of the image
|
||||
/// pixel nearest to it (an envelope's distorted image with Envelope Options › Anti-Alias off).
|
||||
pub pixelated: bool,
|
||||
/// Editable Shaper composition; the original art is retained in its source child.
|
||||
pub shaper: Option<Box<crate::shaper::ShaperSpec>>,
|
||||
/// The [`crate::GraphicStyle::id`] last applied to this object. It stays linked while it keeps
|
||||
@@ -56,6 +59,7 @@ pub(super) static NO_EXTRAS: NodeExtras = NodeExtras {
|
||||
graph: None,
|
||||
series_index: None,
|
||||
type_outlines: false,
|
||||
pixelated: false,
|
||||
shaper: None,
|
||||
graphic_style: None,
|
||||
attrs: None,
|
||||
|
||||
@@ -30,6 +30,7 @@ impl Serialize for Node {
|
||||
e.graph.is_none(),
|
||||
e.series_index.is_none(),
|
||||
is_default(&e.type_outlines),
|
||||
is_default(&e.pixelated),
|
||||
e.shaper.is_none(),
|
||||
false,
|
||||
e.graphic_style.is_none(),
|
||||
@@ -69,6 +70,7 @@ impl Serialize for Node {
|
||||
field!("graph", &e.graph);
|
||||
field!("series_index", &e.series_index);
|
||||
field!("type_outlines", &e.type_outlines);
|
||||
field!("pixelated", &e.pixelated);
|
||||
field!("shaper", &e.shaper);
|
||||
field!("kind", &self.kind);
|
||||
field!("graphic_style", &e.graphic_style);
|
||||
@@ -158,6 +160,8 @@ pub(super) struct NodeWire {
|
||||
#[serde(default)]
|
||||
type_outlines: bool,
|
||||
#[serde(default)]
|
||||
pixelated: bool,
|
||||
#[serde(default)]
|
||||
shaper: Option<Box<crate::shaper::ShaperSpec>>,
|
||||
kind: NodeKind,
|
||||
#[serde(default)]
|
||||
@@ -189,6 +193,7 @@ impl From<NodeWire> for Node {
|
||||
graph: w.graph,
|
||||
series_index: w.series_index,
|
||||
type_outlines: w.type_outlines,
|
||||
pixelated: w.pixelated,
|
||||
shaper: w.shaper,
|
||||
graphic_style: w.graphic_style,
|
||||
attrs: w.attrs,
|
||||
|
||||
Reference in New Issue
Block a user