Repository navigation
Expand file tree
/
Copy pathtree.rs
More file actions
88 lines (84 loc) · 3.21 KB
/
Copy pathtree.rs
File metadata and controls
88 lines (84 loc) · 3.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//! Nested layer tree reconstruction from section dividers.
//!
//! In the file, layers are stored bottom-to-top. A group appears as a
//! *bounding section divider* (`lsct` type 3, usually named
//! `</Layer group>`) below its children, followed by the children, followed
//! by the group's own record (`lsct` type 1 = open or 2 = closed folder).
use crate::file::PsdFile;
use crate::tagged::SectionType;
/// A node of the layer tree. Children are in file order (bottom-to-top).
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LayerNode {
/// A non-group layer.
Layer {
/// Index into [`PsdFile::layers`].
index: usize,
},
/// A group (folder).
Group {
/// Index of the group's own record (the folder, top of the group).
index: usize,
/// Index of the bounding section divider, if one was found.
divider: Option<usize>,
/// Children, bottom-to-top.
children: Vec<LayerNode>,
},
}
impl LayerNode {
/// Record index of this node.
pub fn index(&self) -> usize {
match self {
LayerNode::Layer { index } | LayerNode::Group { index, .. } => *index,
}
}
/// Children (empty for layers).
pub fn children(&self) -> &[LayerNode] {
match self {
LayerNode::Layer { .. } => &[],
LayerNode::Group { children, .. } => children,
}
}
/// `true` for groups.
pub fn is_group(&self) -> bool {
matches!(self, LayerNode::Group { .. })
}
/// Total number of nodes in this subtree (including self).
pub fn count(&self) -> usize {
1 + self.children().iter().map(LayerNode::count).sum::<usize>()
}
}
impl PsdFile {
/// Builds the nested layer tree. Root nodes and children are ordered
/// bottom-to-top (file order).
///
/// Malformed nesting is tolerated: a folder without a matching divider
/// becomes an empty group; unclosed dividers at the end are dropped and
/// their children are hoisted to the enclosing level.
pub fn layer_tree(&self) -> Vec<LayerNode> {
// Stack of (divider index, children).
let mut stack: Vec<(Option<usize>, Vec<LayerNode>)> = vec![(None, Vec::new())];
for (i, rec) in self.layers().iter().enumerate() {
match rec.section_type() {
SectionType::BoundingDivider => stack.push((Some(i), Vec::new())),
SectionType::OpenFolder | SectionType::ClosedFolder => {
let (divider, children) = if stack.len() > 1 { stack.pop().unwrap_or_default() } else { (None, Vec::new()) };
if let Some(top) = stack.last_mut() {
top.1.push(LayerNode::Group { index: i, divider, children });
}
}
_ => {
if let Some(top) = stack.last_mut() {
top.1.push(LayerNode::Layer { index: i });
}
}
}
}
while stack.len() > 1 {
let (_, children) = stack.pop().unwrap_or_default();
if let Some(top) = stack.last_mut() {
top.1.extend(children);
}
}
stack.pop().map(|(_, c)| c).unwrap_or_default()
}
}