mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-02 14:16:56 -06:00
Flattening: an option leaves colorized images to writers that draw them
Flattening counted every colorized image as transparency, in the art and in pattern tiles. A new flattener option, keeps_colorize, stops counting an image colorized with a solid paint whose pixels are grayscale or 1-bit, for a format whose writer draws such images in their ink. Opacity, blending modes, opacity masks and effects still flatten them, and pattern tiles are still expanded for those. The option is off, so exports flatten as before; the EPS writer that draws colorized images turns it on. The EPS pattern tile tests now check the exported colors, and a tile group with opacity for the warning.
This commit is contained in:
@@ -74,7 +74,7 @@ pub(super) fn plan(src: &Document, roots: &[NodeId], o: &FlattenOptions, render:
|
||||
let mut sc = Scratch { brushes: vectorcraft_brush::library(&baked), doc: baked.clone() };
|
||||
let plain: Vec<Node> = art.iter().map(|n| sc.plain(n)).collect();
|
||||
let reaches: Vec<Option<Rect>> = plain.iter().map(reach).collect();
|
||||
let see_through: Vec<bool> = plain.iter().map(|n| n.shows_transparency() || o.draws_see_through_image(n)).collect();
|
||||
let see_through: Vec<bool> = plain.iter().map(|n| o.shows_transparency(&baked, n) || o.draws_see_through_image(n)).collect();
|
||||
let cmyk = src.color_mode == ColorMode::Cmyk;
|
||||
let mut out = Plan { flat: vec![], kept: vec![], transparent: roots.iter().zip(&see_through).filter(|(_, t)| **t).map(|(id, _)| *id).collect() };
|
||||
// Baking above stopped early when cancelled; each group is a place to stop (a plan changes nothing).
|
||||
@@ -298,28 +298,29 @@ pub(crate) fn expand_transparent_tile_images(doc: &Document, o: &FlattenOptions)
|
||||
return Ok(None);
|
||||
}
|
||||
let keys = o.no_transparency.clone().unwrap_or_default();
|
||||
if !crate::cmd::expand::has_transparent_image_patterns(doc, &keys) {
|
||||
let colorize = |d: &Document, n: &Node| o.counts_colorize(d, n);
|
||||
if !crate::cmd::expand::has_transparent_image_patterns(doc, &keys, &colorize) {
|
||||
return Ok(None);
|
||||
}
|
||||
let mut d = doc.clone();
|
||||
let layers: Vec<NodeId> = d.layers.iter().map(|l| l.id).collect();
|
||||
let mut changed = 0;
|
||||
for id in layers {
|
||||
changed += crate::cmd::expand::expand_transparent_image_patterns(&mut d, id, &keys)?;
|
||||
changed += crate::cmd::expand::expand_transparent_image_patterns(&mut d, id, &keys, &colorize)?;
|
||||
}
|
||||
Ok((changed > 0).then_some(d))
|
||||
}
|
||||
|
||||
/// `doc` with everything it draws flattened with `o`, as formats without transparency (EPS)
|
||||
/// write it: every visible object of the visible layers, locked ones too (a clipping layer's
|
||||
/// clipping path stays). Pattern tile images with opacity, blending or colorize are expanded;
|
||||
/// see-through tile pixels are expanded when `o.no_transparency` counts them. `None` when nothing
|
||||
/// changes.
|
||||
/// clipping path stays). Pattern tile images with opacity, blending or a colorize paint that
|
||||
/// `o` counts ([`FlattenOptions::counts_colorize`]) are expanded; see-through tile pixels are
|
||||
/// expanded when `o.no_transparency` counts them. `None` when nothing changes.
|
||||
pub(crate) fn flatten_document(doc: &Document, o: &FlattenOptions) -> Result<Option<Document>> {
|
||||
let expanded = expand_transparent_tile_images(doc, o)?;
|
||||
let doc = expanded.as_ref().unwrap_or(doc);
|
||||
let outlining = o.text_to_outlines || o.strokes_to_outlines || !o.preserve_overprints;
|
||||
if !outlining && !doc.layers.iter().any(|l| l.shows_transparency() || o.draws_see_through_image(l)) {
|
||||
if !outlining && !doc.layers.iter().any(|l| o.shows_transparency(doc, l) || o.draws_see_through_image(l)) {
|
||||
return Ok(expanded);
|
||||
}
|
||||
let mut roots = vec![];
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::analysis::draws_image;
|
||||
use super::apply::{apply, kept_work, plan};
|
||||
use super::presets::{FlattenerPreset, saved_preset};
|
||||
use super::report::target_roots;
|
||||
use super::{BTreeSet, Deserialize, EngineError, Node, Result, Serialize, Session, Value, bad, json, str_param};
|
||||
use super::{BTreeSet, Deserialize, Document, EngineError, Node, NodeKind, Paint, Result, Serialize, Session, Value, bad, json, str_param};
|
||||
|
||||
/// Object → Flatten Transparency settings: a preset's, adjusted by the command's params.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@@ -34,6 +34,10 @@ pub struct FlattenOptions {
|
||||
/// amounts). `None` for ordinary flattening. Not a preset option.
|
||||
#[serde(skip)]
|
||||
pub no_transparency: Option<BTreeSet<String>>,
|
||||
/// The file writes a colorized image in its colorize paint (EPS), so a colorize paint by
|
||||
/// itself doesn't count as transparency. Not a preset option.
|
||||
#[serde(skip)]
|
||||
pub keeps_colorize: bool,
|
||||
}
|
||||
|
||||
impl Default for FlattenOptions {
|
||||
@@ -81,6 +85,7 @@ impl FlattenOptions {
|
||||
preserve_alpha: false,
|
||||
preserve_overprints: true,
|
||||
no_transparency: None,
|
||||
keeps_colorize: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +143,21 @@ impl FlattenOptions {
|
||||
Ok(o)
|
||||
}
|
||||
|
||||
/// Whether `n`, art of `d`, shows transparency these options flatten
|
||||
/// ([`Node::shows_transparency`], colorized images as [`Self::counts_colorize`] says).
|
||||
pub(crate) fn shows_transparency(&self, d: &Document, n: &Node) -> bool {
|
||||
n.shows_transparency_with(&|c| self.counts_colorize(d, c))
|
||||
}
|
||||
|
||||
/// Whether colorized image `n`, art of `d`, counts as transparency by itself. With
|
||||
/// [`Self::keeps_colorize`], an image colorized with a solid paint whose pixels are grayscale
|
||||
/// or 1-bit doesn't: the EPS writer draws it in that paint.
|
||||
pub(crate) fn counts_colorize(&self, d: &Document, n: &Node) -> bool {
|
||||
let NodeKind::Image(im) = &n.kind else { return true };
|
||||
let solid = matches!(n.extras().colorize.as_deref(), Some(Paint::Solid { .. }));
|
||||
!self.keeps_colorize || !solid || d.image_blob(im).is_some_and(|b| b.gray_source().is_none())
|
||||
}
|
||||
|
||||
/// `n` shows an image whose see-through pixels count as transparency.
|
||||
pub(super) fn draws_see_through_image(&self, n: &Node) -> bool {
|
||||
self.no_transparency.as_ref().is_some_and(|keys| draws_image(n, keys))
|
||||
|
||||
Reference in New Issue
Block a user