mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-31 05:07:51 -06:00
Flatten transparent image pattern tiles in legacy .ai v7 export
Fixes #3666 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
committed by
bflatastic
co-authored by
Claude Opus 5.5
parent
2e572c7805
commit
94759cbe71
@@ -15,8 +15,13 @@
|
||||
//! blending mode written opaque is a box hiding the art under it (version 7 images have no alpha
|
||||
//! channel, §8.1). With `flatten` (the default) each becomes an opaque image of itself and the art
|
||||
//! under it, rendered from the document as it is, clipped to the area where it changes what shows;
|
||||
//! the art under it stays as it was. With `omit` they are left out. An image that can't be decoded
|
||||
//! is missing from the images that flattening and expanding raster effects draw, and the warnings
|
||||
//! the art under it stays as it was. An image in a group with opacity or a blending mode is
|
||||
//! flattened the same way, and a pattern fill that paints a transparent image (see-through
|
||||
//! pixels, or opacity or a blending mode on the image, the fill, the shape or a group it is in) is
|
||||
//! expanded into its tiles first so its images flatten too. With `omit` the images with
|
||||
//! see-through pixels, opacity or a blending mode of their own are left out, and pattern fills stay
|
||||
//! as the writer draws them (their tiles opaque over white). An image that can't be decoded is
|
||||
//! missing from the images that flattening and expanding raster effects draw, and the warnings
|
||||
//! then carry the raster exports' note once.
|
||||
//!
|
||||
//! The format can't hide objects, only layers (`hiddenObjects`): with `layer` (the default) each
|
||||
@@ -117,6 +122,9 @@ pub(super) fn encode(cmd: &str, f: &Format, doc: &Document, p: &Value) -> Result
|
||||
let (full, placed) = crate::cmd::place::document::full_documents(&one);
|
||||
let mut d = full.into_owned();
|
||||
expand_appearances(&mut d);
|
||||
if mode == SeeThrough::Flatten {
|
||||
expand_image_patterns(&mut d)?;
|
||||
}
|
||||
let images = see_through_images(&d, mode);
|
||||
if !images.is_empty() {
|
||||
note(&mut enc.warnings, if mode == SeeThrough::Flatten { FLATTENED } else { OMITTED });
|
||||
@@ -153,17 +161,30 @@ fn expand_appearances(d: &mut Document) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Expand every layer's pattern fills that paint a transparent image into their tiles, so those
|
||||
/// images flatten like any other transparent image. The writer would otherwise draw them opaque
|
||||
/// over white.
|
||||
fn expand_image_patterns(d: &mut Document) -> Result<()> {
|
||||
let keys = super::super::flatten::see_through_images(d);
|
||||
let layers: Vec<NodeId> = d.layers.iter().map(|l| l.id).collect();
|
||||
for id in layers {
|
||||
super::super::expand::expand_transparent_image_patterns(d, id, &keys)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The images of `d` a legacy `.ai` can't hold, in paint order with their places in it (as
|
||||
/// [`Document::walk`] counts): those with see-through pixels, opacity or a blending mode. Only
|
||||
/// visible ones flatten (a hidden one, which the hidden objects option may write, is written over
|
||||
/// white); all are left out with `omit`.
|
||||
/// [`Document::walk`] counts). These are the images with see-through pixels, and the images
|
||||
/// that have opacity or a blending mode of their own or, with `flatten`, of a group they are in.
|
||||
/// With `flatten` only visible images are returned, because a hidden one (which the hidden objects
|
||||
/// option may write) is written over white. With `omit` all of them are returned.
|
||||
fn see_through_images(d: &Document, mode: SeeThrough) -> Vec<(usize, NodeId)> {
|
||||
let keys = super::super::flatten::see_through_images(d);
|
||||
let mut out = vec![];
|
||||
let mut at = 0;
|
||||
d.walk(|n| {
|
||||
if let NodeKind::Image(im) = &n.kind
|
||||
&& (keys.contains(&im.key) || n.opacity < 1.0 || n.blend != BlendMode::Normal)
|
||||
&& (keys.contains(&im.key) || n.opacity < 1.0 || n.blend != BlendMode::Normal || (mode == SeeThrough::Flatten && transparent(d, n.id)))
|
||||
{
|
||||
out.push((at, n.id));
|
||||
}
|
||||
@@ -175,6 +196,12 @@ fn see_through_images(d: &Document, mode: SeeThrough) -> Vec<(usize, NodeId)> {
|
||||
out
|
||||
}
|
||||
|
||||
/// Whether node `id` or a group it is in has opacity or a blending mode. [`Document::ancestry`]
|
||||
/// lists `id` itself last, so its own opacity and blending mode count too.
|
||||
fn transparent(d: &Document, id: NodeId) -> bool {
|
||||
d.ancestry(id).unwrap_or_default().iter().filter_map(|a| d.node(*a)).any(|n| n.opacity < 1.0 || n.blend != BlendMode::Normal)
|
||||
}
|
||||
|
||||
/// Replace each of `images` (from [`see_through_images`]) with an opaque image of what shows
|
||||
/// where it is, clipped to where it changes that; one that changes nothing is left out. Each is
|
||||
/// rendered from `d` as it was, so images over one another flatten as they look.
|
||||
|
||||
Reference in New Issue
Block a user