mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Merge pull request #2029 from midasmatterhorn/fix/issue-2003-ai-layer-opacity
eps: a layer's own transparency in the editing data is read (#2003)
This commit is contained in:
@@ -373,6 +373,8 @@ struct Reader<'a> {
|
||||
hidden_ids: HashSet<NodeId>,
|
||||
/// A group just ended, its state (`Xy`, `Xd`, `XW`) not yet read.
|
||||
group_ended: bool,
|
||||
/// A layer's own state (`Xy` after its art, before `LB`), until something says the art moves on.
|
||||
layer_state: Option<(f32, BlendMode, bool, Knockout)>,
|
||||
/// Painted copies of clipping paths that have a fill (drawn under what they clip).
|
||||
clip_fills: HashSet<NodeId>,
|
||||
/// Groups closed as an object's drawn look (`9 () XW`).
|
||||
@@ -524,6 +526,7 @@ impl<'a> Reader<'a> {
|
||||
clip_fills: HashSet::new(),
|
||||
looks: HashSet::new(),
|
||||
group_ended: false,
|
||||
layer_state: None,
|
||||
styles: HashMap::new(),
|
||||
mesh: None,
|
||||
cmyk_cache: HashMap::new(),
|
||||
@@ -915,6 +918,10 @@ impl<'a> Reader<'a> {
|
||||
layer.name = Some(a.name.clone());
|
||||
layer.visible = a.visible;
|
||||
layer.locked = a.locked;
|
||||
// The layer's own opacity, blend and knockout, written after its art (#2003).
|
||||
if let Some((opacity, blend, isolate, knockout)) = self.layer_state.take() {
|
||||
(layer.opacity, layer.blend, layer.isolate, layer.knockout) = (opacity, blend, isolate, knockout);
|
||||
}
|
||||
match self.frames.last_mut() {
|
||||
Some(parent) => {
|
||||
parent.children.push(layer);
|
||||
@@ -969,9 +976,11 @@ impl<'a> Reader<'a> {
|
||||
";" => return self.end_obj(),
|
||||
_ => {}
|
||||
}
|
||||
// What follows an object (its name, transparency and style) ends with anything else.
|
||||
if !matches!(w, "Xy" | "Xd" | "XW" | "BB") {
|
||||
// What follows an object (its name, transparency and style) ends with anything else. The
|
||||
// state block of a layer whose art ended (`Xy`, `Xd`, `XW`, then `LB`) is read at the `LB`.
|
||||
if !matches!(w, "Xy" | "Xd" | "XW" | "BB" | "LB") {
|
||||
self.after_object = false;
|
||||
self.layer_state = None;
|
||||
}
|
||||
let group_ended = std::mem::take(&mut self.group_ended);
|
||||
let vals = self.operands();
|
||||
@@ -1479,13 +1488,20 @@ impl<'a> Reader<'a> {
|
||||
};
|
||||
gs.knockout_shape = rest.get(2).is_some_and(|v| *v != 0.0);
|
||||
let gs = self.gs.clone();
|
||||
if let Some(n) = self.last_object()
|
||||
&& matches!(n.kind, NodeKind::Group { .. } | NodeKind::Compound { .. })
|
||||
{
|
||||
n.opacity = gs.opacity;
|
||||
n.blend = gs.blend;
|
||||
n.isolate = gs.isolate;
|
||||
n.knockout = gs.knockout;
|
||||
if self.last_object().is_some_and(|n| matches!(n.kind, NodeKind::Group { .. } | NodeKind::Compound { .. })) {
|
||||
if let Some(n) = self.last_object() {
|
||||
n.opacity = gs.opacity;
|
||||
n.blend = gs.blend;
|
||||
n.isolate = gs.isolate;
|
||||
n.knockout = gs.knockout;
|
||||
}
|
||||
self.layer_state = None;
|
||||
} else {
|
||||
// After a layer's art and before its `LB`: the layer's own state (#2003). Kept until
|
||||
// an operator that isn't part of the state block (or the `LB` that takes it) says the
|
||||
// art moves on, so a path's state before its paint isn't read as its layer's.
|
||||
self.layer_state =
|
||||
matches!(self.frames.last(), Some(Frame { kind: Kind::Layer(_), .. })).then_some((gs.opacity, gs.blend, gs.isolate, gs.knockout));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -303,6 +303,24 @@ fn a_group_whose_state_has_no_transparency_takes_the_current_one() {
|
||||
assert_eq!((a[1].opacity, a[1].children().unwrap()[0].opacity), (1.0, 0.5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_layer_keeps_its_own_transparency() {
|
||||
// Illustrator writes a layer's own opacity, blend and knockout after its art, before `LB`
|
||||
// (`Xy`, `Xd`, then `n () XW`); they belong to the layer, not to its last object (#2003).
|
||||
let art_ = format!("{RED}{}f\n0 0.35 0 2 0 Xy\n0 0 Xd\n7 () XW\n", rect_path(0.0, 0.0, 10.0, 10.0));
|
||||
let doc = read_ok(&layer(&art_)).doc;
|
||||
let l = &doc.layers[0];
|
||||
assert_eq!((l.opacity, l.blend), (0.35, BlendMode::Normal), "the layer's own state");
|
||||
assert_eq!((l.isolate, l.knockout), (false, Knockout::Neutral));
|
||||
// The art inside keeps its own state.
|
||||
assert_eq!(art(&doc)[0].opacity, 1.0);
|
||||
// A layer whose last object is a group keeps that group's state apart from the layer's.
|
||||
let art_ = format!("0 Ae\nu\n0 0.5 0 0 0 Xy\n{}f\nU\n0 0 Xd\n6 () XW\n", rect_path(0.0, 0.0, 10.0, 10.0));
|
||||
let doc = read_ok(&layer(&art_)).doc;
|
||||
assert_eq!(doc.layers[0].opacity, 1.0, "no layer state written: the layer stays opaque");
|
||||
assert_eq!(art(&doc)[0].opacity, 0.5, "the group's own state");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transparency_of_objects_and_groups() {
|
||||
let art_ = format!(
|
||||
|
||||
Reference in New Issue
Block a user