1use std::collections::{HashMap, HashSet};
10
11use rusqlite::Connection;
12
13use crate::database::{Row, select, select_all, table_exists};
14use crate::error::{Result, bail};
15
16pub const OPACITY_FULL: i64 = 256;
18const MAX_DEPTH: usize = 256;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
22#[serde(rename_all = "snake_case")]
23#[non_exhaustive]
24pub enum Unit {
25 Pixel,
27 Centimeter,
29 Millimeter,
31 Inch,
33 Point,
35 Unknown(i64),
37}
38
39impl Unit {
40 pub fn from_code(code: i64) -> Self {
42 match code {
43 0 => Unit::Pixel,
44 1 => Unit::Centimeter,
45 2 => Unit::Millimeter,
46 3 => Unit::Inch,
47 5 => Unit::Point,
48 other => Unit::Unknown(other),
49 }
50 }
51
52 pub fn to_pixels(&self, value: f64, dpi: f64) -> f64 {
54 match self {
55 Unit::Pixel | Unit::Unknown(_) => value,
56 Unit::Centimeter => value * dpi / 2.54,
57 Unit::Millimeter => value * dpi / 25.4,
58 Unit::Inch => value * dpi,
59 Unit::Point => value * dpi / 72.0,
60 }
61 }
62}
63
64#[derive(Debug, Clone, serde::Serialize)]
66pub struct Canvas {
67 pub main_id: i64,
69 pub unit: Unit,
71 pub width: f64,
73 pub height: f64,
75 pub resolution: f64,
77 pub width_px: u32,
79 pub height_px: u32,
81 pub root_folder: i64,
83 pub channel_bytes: i64,
85 pub double_page: bool,
87 #[serde(skip)]
89 pub row: Row,
90}
91
92impl Canvas {
93 pub fn from_row(row: Row) -> Self {
95 let unit = Unit::from_code(row.int("CanvasUnit"));
96 let width = row.real("CanvasWidth");
97 let height = row.real("CanvasHeight");
98 let resolution = row.real("CanvasResolution");
99 Canvas {
100 main_id: row.int("MainId"),
101 width_px: unit.to_pixels(width, resolution).round_ties_even().max(0.0) as u32,
102 height_px: unit.to_pixels(height, resolution).round_ties_even().max(0.0) as u32,
103 unit,
104 width,
105 height,
106 resolution,
107 root_folder: row.int("CanvasRootFolder"),
108 channel_bytes: row.int("CanvasChannelBytes"),
109 double_page: row.truthy("CanvasDoublePage"),
110 row,
111 }
112 }
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize)]
118#[serde(rename_all = "snake_case")]
119#[non_exhaustive]
120pub enum LayerKind {
121 Root,
123 Paper,
125 StoryInfo,
127 Folder,
129 FrameFolder,
131 Raster,
133 Vector,
135 Text,
137 Gradient,
139 Correction,
141 ImageMaterial,
143 ThreeD,
145 Other,
147}
148
149impl LayerKind {
150 pub fn is_folder(self) -> bool {
152 matches!(self, LayerKind::Root | LayerKind::Folder | LayerKind::FrameFolder)
153 }
154
155 pub fn name(self) -> &'static str {
157 match self {
158 LayerKind::Root => "root",
159 LayerKind::Paper => "paper",
160 LayerKind::StoryInfo => "story_info",
161 LayerKind::Folder => "folder",
162 LayerKind::FrameFolder => "frame_folder",
163 LayerKind::Raster => "raster",
164 LayerKind::Vector => "vector",
165 LayerKind::Text => "text",
166 LayerKind::Gradient => "gradient",
167 LayerKind::Correction => "correction",
168 LayerKind::ImageMaterial => "image_material",
169 LayerKind::ThreeD => "3d",
170 LayerKind::Other => "other",
171 }
172 }
173}
174
175pub const LAYER_TYPE_RASTER: i64 = 1;
177pub const LAYER_TYPE_MASKED: i64 = 2;
179pub const LAYER_TYPE_ROOT: i64 = 256;
181pub const LAYER_TYPE_STORY_INFO: i64 = 800;
183pub const LAYER_TYPE_PAPER: i64 = 1584;
185pub const LAYER_TYPE_CORRECTION: i64 = 4096;
187
188#[derive(Debug, Clone, serde::Serialize)]
190pub struct Layer {
191 pub main_id: i64,
193 pub name: String,
195 pub kind: LayerKind,
197 pub layer_type: i64,
199 pub visible: bool,
201 pub mask_enabled: bool,
203 pub opacity: i64,
205 pub blend: i64,
207 pub clipping: bool,
209 pub locked: bool,
211 pub folder_flags: i64,
213 pub offset: (i64, i64),
215 pub render_offset: (i64, i64),
217 pub mask_offset: (i64, i64),
219 pub mask_render_offset: (i64, i64),
221 pub render_mipmap: i64,
223 pub mask_mipmap: i64,
225 pub first_child: i64,
227 pub next: i64,
229 pub uuid: Option<String>,
231 #[serde(skip)]
233 pub row: Row,
234}
235
236impl Layer {
237 pub fn from_row(row: Row, vector_layers: &HashSet<i64>) -> Self {
239 let main_id = row.int("MainId");
240 let layer_type = row.int("LayerType");
241 let folder_flags = row.int("LayerFolder");
242 let kind = classify(&row, main_id, layer_type, folder_flags, vector_layers);
243 let visibility = row.int("LayerVisibility");
244 let pair = |x: &str, y: &str| (row.int(x), row.int(y));
245 Layer {
246 main_id,
247 name: row.text("LayerName").unwrap_or_default(),
248 kind,
249 layer_type,
250 visible: visibility & 1 != 0,
251 mask_enabled: visibility & 2 != 0,
252 opacity: row.opt_int("LayerOpacity").unwrap_or(OPACITY_FULL),
253 blend: row.int("LayerComposite"),
254 clipping: row.int("LayerClip") != 0,
255 locked: row.int("LayerLock") != 0,
256 folder_flags,
257 offset: pair("LayerOffsetX", "LayerOffsetY"),
258 render_offset: pair("LayerRenderOffscrOffsetX", "LayerRenderOffscrOffsetY"),
259 mask_offset: pair("LayerMaskOffsetX", "LayerMaskOffsetY"),
260 mask_render_offset: pair("LayerMaskOffscrOffsetX", "LayerMaskOffscrOffsetY"),
261 render_mipmap: row.int("LayerRenderMipmap"),
262 mask_mipmap: row.int("LayerLayerMaskMipmap"),
263 first_child: row.int("LayerFirstChildIndex"),
264 next: row.int("LayerNextIndex"),
265 uuid: row.text("LayerUuid"),
266 row,
267 }
268 }
269
270 pub fn pixel_origin(&self) -> (i64, i64) {
272 (self.offset.0 + self.render_offset.0, self.offset.1 + self.render_offset.1)
273 }
274
275 pub fn mask_origin(&self) -> (i64, i64) {
280 (
281 self.offset.0 + self.mask_offset.0 + self.mask_render_offset.0,
282 self.offset.1 + self.mask_offset.1 + self.mask_render_offset.1,
283 )
284 }
285
286 pub fn has_mask(&self) -> bool {
288 self.mask_mipmap != 0
289 }
290
291 pub fn opacity_fraction(&self) -> f64 {
293 self.opacity.clamp(0, OPACITY_FULL) as f64 / OPACITY_FULL as f64
294 }
295
296 pub fn is_open(&self) -> bool {
298 self.folder_flags & 16 != 0
299 }
300
301 pub fn color(&self, prefix: &str) -> Option<[u8; 3]> {
304 let channel =
305 |name: &str| self.row.opt_int(&format!("{prefix}{name}")).map(|v| ((v as u64 >> 24) & 0xff) as u8);
306 Some([channel("Red")?, channel("Green")?, channel("Blue")?])
307 }
308}
309
310fn classify(row: &Row, main_id: i64, layer_type: i64, folder: i64, vector_layers: &HashSet<i64>) -> LayerKind {
311 if layer_type == LAYER_TYPE_ROOT {
312 return LayerKind::Root;
313 }
314 if layer_type == LAYER_TYPE_PAPER {
315 return LayerKind::Paper;
316 }
317 if layer_type == LAYER_TYPE_STORY_INFO {
318 return LayerKind::StoryInfo;
319 }
320 if folder & 1 != 0 {
321 return if row.truthy("ComicFrameLineMipmap") { LayerKind::FrameFolder } else { LayerKind::Folder };
322 }
323 if row.is_set("TextLayerString") {
324 return LayerKind::Text;
325 }
326 if row.truthy("GradationFillInfo") {
327 return LayerKind::Gradient;
328 }
329 if row.truthy("FilterLayerInfo") || layer_type & LAYER_TYPE_CORRECTION != 0 {
330 return LayerKind::Correction;
331 }
332 if row.truthy("FirstLayerObject") {
333 return LayerKind::ThreeD;
334 }
335 if row.truthy("ResizableImageInfo") {
336 return LayerKind::ImageMaterial;
337 }
338 if vector_layers.contains(&main_id) {
339 return LayerKind::Vector;
340 }
341 if layer_type & LAYER_TYPE_RASTER != 0 {
342 return LayerKind::Raster;
343 }
344 if layer_type == 0 && row.is_set("VectorNormalStrokeIndex") {
348 return LayerKind::Vector;
349 }
350 LayerKind::Other
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356 use rusqlite::types::Value;
357
358 #[test]
359 fn empty_vector_layers_are_vector() {
360 let row = |columns: &[(&str, Value)]| {
361 Row::new(
362 columns.iter().map(|(c, _)| c.to_string()).collect(),
363 columns.iter().map(|(_, v)| v.clone()).collect(),
364 )
365 };
366 let none = HashSet::new();
367 let empty = row(&[("VectorNormalStrokeIndex", Value::Integer(0))]);
368 assert_eq!(classify(&empty, 5, 0, 0, &none), LayerKind::Vector);
369 let raster = row(&[("VectorNormalStrokeIndex", Value::Null)]);
370 assert_eq!(classify(&raster, 5, 1, 0, &none), LayerKind::Raster);
371 assert_eq!(classify(&raster, 5, 0, 0, &none), LayerKind::Other);
372 }
373}
374
375#[derive(Debug, Clone)]
377pub struct LayerNode {
378 pub layer: Layer,
380 pub children: Vec<LayerNode>,
382}
383
384impl LayerNode {
385 pub fn walk<'a>(&'a self, depth: usize, visit: &mut impl FnMut(&'a LayerNode, usize)) {
387 visit(self, depth);
388 for child in &self.children {
389 child.walk(depth + 1, visit);
390 }
391 }
392
393 pub fn find(&self, main_id: i64) -> Option<&LayerNode> {
395 if self.layer.main_id == main_id {
396 return Some(self);
397 }
398 self.children.iter().find_map(|child| child.find(main_id))
399 }
400}
401
402#[derive(Debug, Clone)]
404pub struct LayerTree {
405 pub layers: Vec<Layer>,
407 pub root: LayerNode,
409 pub unreachable: Vec<i64>,
411}
412
413impl LayerTree {
414 pub fn read(connection: &Connection, root_id: i64) -> Result<Self> {
416 let vector_layers: HashSet<i64> = if table_exists(connection, "VectorObjectList")? {
417 select(connection, "SELECT LayerId FROM VectorObjectList", [])?
418 .iter()
419 .map(|row| row.int("LayerId"))
420 .collect()
421 } else {
422 HashSet::new()
423 };
424 let layers: Vec<Layer> =
425 select_all(connection, "Layer")?.into_iter().map(|row| Layer::from_row(row, &vector_layers)).collect();
426 let by_id: HashMap<i64, &Layer> = layers.iter().map(|layer| (layer.main_id, layer)).collect();
427 if by_id.len() != layers.len() {
428 bail!("Layer MainIds are not unique");
429 }
430 let mut seen = HashSet::new();
431 let root = build(&by_id, root_id, &mut seen, 0)?;
432 let unreachable = layers.iter().map(|layer| layer.main_id).filter(|id| !seen.contains(id)).collect();
433 Ok(LayerTree { layers: layers.clone(), root, unreachable })
434 }
435
436 pub fn get(&self, main_id: i64) -> Option<&Layer> {
438 self.layers.iter().find(|layer| layer.main_id == main_id)
439 }
440
441 pub fn flatten(&self) -> Vec<(&Layer, usize)> {
443 let mut result = Vec::new();
444 self.root.walk(0, &mut |node, depth| result.push((&node.layer, depth)));
445 result
446 }
447}
448
449fn build(by_id: &HashMap<i64, &Layer>, id: i64, seen: &mut HashSet<i64>, depth: usize) -> Result<LayerNode> {
450 if depth > MAX_DEPTH {
451 bail!("Layer tree deeper than {MAX_DEPTH}");
452 }
453 let layer = *by_id.get(&id).ok_or_else(|| crate::Error::format(format!("Layer {id} does not exist")))?;
454 if !seen.insert(id) {
455 bail!("Layer {id} is linked twice (the tree has a cycle)");
456 }
457 let mut children = Vec::new();
458 let mut child = layer.first_child;
459 while child != 0 {
460 let node = build(by_id, child, seen, depth + 1)?;
461 child = node.layer.next;
462 children.push(node);
463 }
464 Ok(LayerNode { layer: layer.clone(), children })
465}
466
467#[derive(Debug, Clone, serde::Serialize)]
469pub struct Project {
470 pub internal_version: Option<String>,
472 pub name: Option<String>,
474 pub canvas: i64,
476 #[serde(skip)]
478 pub row: Row,
479}
480
481impl Project {
482 pub fn from_row(row: Row) -> Self {
484 Project {
485 internal_version: row.text("ProjectInternalVersion"),
486 name: row.text("ProjectName"),
487 canvas: row.int("ProjectCanvas"),
488 row,
489 }
490 }
491}