1use std::collections::HashMap;
22
23use flate2::{write::ZlibEncoder, Compression as FlateCompression};
24use std::io::Write as _;
25
26use crate::psd::{BlendMode, ChannelId, Compression, Layer, LayerColor, PixelData};
27
28pub const MOCK_HANDLERS: bool = false;
30pub const RAW_IMAGE_DATA: bool = false;
32
33pub fn to_blend_mode(key: &str) -> Option<BlendMode> {
40 Some(match key {
41 "pass" => BlendMode::PassThrough,
42 "norm" => BlendMode::Normal,
43 "diss" => BlendMode::Dissolve,
44 "dark" => BlendMode::Darken,
45 "mul " => BlendMode::Multiply,
46 "idiv" => BlendMode::ColorBurn,
47 "lbrn" => BlendMode::LinearBurn,
48 "dkCl" => BlendMode::DarkerColor,
49 "lite" => BlendMode::Lighten,
50 "scrn" => BlendMode::Screen,
51 "div " => BlendMode::ColorDodge,
52 "lddg" => BlendMode::LinearDodge,
53 "lgCl" => BlendMode::LighterColor,
54 "over" => BlendMode::Overlay,
55 "sLit" => BlendMode::SoftLight,
56 "hLit" => BlendMode::HardLight,
57 "vLit" => BlendMode::VividLight,
58 "lLit" => BlendMode::LinearLight,
59 "pLit" => BlendMode::PinLight,
60 "hMix" => BlendMode::HardMix,
61 "diff" => BlendMode::Difference,
62 "smud" => BlendMode::Exclusion,
63 "fsub" => BlendMode::Subtract,
64 "fdiv" => BlendMode::Divide,
65 "hue " => BlendMode::Hue,
66 "sat " => BlendMode::Saturation,
67 "colr" => BlendMode::Color,
68 "lum " => BlendMode::Luminosity,
69 _ => return None,
70 })
71}
72
73pub fn from_blend_mode(mode: BlendMode) -> &'static str {
77 match mode {
78 BlendMode::PassThrough => "pass",
79 BlendMode::Normal => "norm",
80 BlendMode::Dissolve => "diss",
81 BlendMode::Darken => "dark",
82 BlendMode::Multiply => "mul ",
83 BlendMode::ColorBurn => "idiv",
84 BlendMode::LinearBurn => "lbrn",
85 BlendMode::DarkerColor => "dkCl",
86 BlendMode::Lighten => "lite",
87 BlendMode::Screen => "scrn",
88 BlendMode::ColorDodge => "div ",
89 BlendMode::LinearDodge => "lddg",
90 BlendMode::LighterColor => "lgCl",
91 BlendMode::Overlay => "over",
92 BlendMode::SoftLight => "sLit",
93 BlendMode::HardLight => "hLit",
94 BlendMode::VividLight => "vLit",
95 BlendMode::LinearLight => "lLit",
96 BlendMode::PinLight => "pLit",
97 BlendMode::HardMix => "hMix",
98 BlendMode::Difference => "diff",
99 BlendMode::Exclusion => "smud",
100 BlendMode::Subtract => "fsub",
101 BlendMode::Divide => "fdiv",
102 BlendMode::Hue => "hue ",
103 BlendMode::Saturation => "sat ",
104 BlendMode::Color => "colr",
105 BlendMode::Luminosity => "lum ",
106 }
107}
108
109pub const LAYER_COLORS: [LayerColor; 8] = [
111 LayerColor::None,
112 LayerColor::Red,
113 LayerColor::Orange,
114 LayerColor::Yellow,
115 LayerColor::Green,
116 LayerColor::Blue,
117 LayerColor::Violet,
118 LayerColor::Gray,
119];
120
121pub const LARGE_ADDITIONAL_INFO_KEYS: [&str; 14] = [
123 "LMsk", "Lr16", "Lr32", "Layr", "Mt16", "Mt32", "Mtrn", "Alph", "FMsk", "lnk2", "FEid",
125 "FXid", "PxSD", "cinf",
127];
128
129pub type Dict = HashMap<String, String>;
135
136pub fn rev_map(map: &Dict) -> Dict {
138 let mut result = Dict::new();
139 for (key, value) in map {
140 result.insert(value.clone(), key.clone());
141 }
142 result
143}
144
145pub struct EnumCodec {
149 prefix: String,
150 def: String,
151 map: Dict,
152 rev: Dict,
153}
154
155impl EnumCodec {
156 pub fn new(prefix: &str, def: &str, map: Dict) -> Self {
158 let rev = rev_map(&map);
159 EnumCodec {
160 prefix: prefix.to_string(),
161 def: def.to_string(),
162 map,
163 rev,
164 }
165 }
166
167 pub fn decode(&self, val: &str) -> Result<String, String> {
170 let value = val.split('.').nth(1).unwrap_or("");
172 if !value.is_empty() && !self.rev.contains_key(value) {
173 return Err(format!("Unrecognized value for enum: '{val}'"));
174 }
175 Ok(self
176 .rev
177 .get(value)
178 .cloned()
179 .unwrap_or_else(|| self.def.clone()))
180 }
181
182 pub fn encode(&self, val: Option<&str>) -> Result<String, String> {
186 if let Some(v) = val {
187 if !self.map.contains_key(v) {
188 return Err(format!("Invalid value for enum: '{v}'"));
189 }
190 }
191 let mapped = val
192 .and_then(|v| self.map.get(v))
193 .or_else(|| self.map.get(&self.def))
194 .cloned()
195 .unwrap_or_default();
196 Ok(format!("{}.{}", self.prefix, mapped))
197 }
198}
199
200#[derive(Debug, Clone, Copy, PartialEq, Eq)]
206pub enum ColorSpace {
207 Rgb = 0,
208 Hsb = 1,
209 Cmyk = 2,
210 Lab = 7,
211 Grayscale = 8,
212}
213
214#[derive(Debug, Clone, Copy, PartialEq, Eq)]
216pub enum LayerMaskFlags {
217 PositionRelativeToLayer = 1,
218 LayerMaskDisabled = 2,
219 InvertLayerMaskWhenBlending = 4,
221 LayerMaskFromRenderingOtherData = 8,
222 MaskHasParametersAppliedToIt = 16,
223}
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq)]
227pub enum MaskParams {
228 UserMaskDensity = 1,
229 UserMaskFeather = 2,
230 VectorMaskDensity = 4,
231 VectorMaskFeather = 8,
232}
233
234#[derive(Debug, Clone)]
240pub struct ChannelData {
241 pub channel_id: ChannelId,
242 pub compression: Compression,
243 pub buffer: Option<Vec<u8>>,
244 pub length: usize,
245}
246
247#[derive(Debug, Clone, Copy, Default)]
249pub struct Bounds {
250 pub top: i32,
251 pub left: i32,
252 pub right: i32,
253 pub bottom: i32,
254}
255
256pub struct LayerChannelData {
258 pub layer: Layer,
259 pub channels: Vec<ChannelData>,
260 pub top: i32,
261 pub left: i32,
262 pub right: i32,
263 pub bottom: i32,
264 pub mask: Option<Bounds>,
265 pub real_mask: Option<Bounds>,
266}
267
268pub fn offset_for_channel(channel_id: ChannelId, cmyk: bool) -> i32 {
275 let id = channel_id as i32;
276 match channel_id {
277 ChannelId::Color0 => 0,
278 ChannelId::Color1 => 1,
279 ChannelId::Color2 => 2,
280 ChannelId::Color3 => {
281 if cmyk {
282 3
283 } else {
284 id + 1
285 }
286 }
287 ChannelId::Transparency => {
288 if cmyk {
289 4
290 } else {
291 3
292 }
293 }
294 _ => id + 1,
295 }
296}
297
298pub fn clamp(value: f64, min: f64, max: f64) -> f64 {
300 if value < min {
301 min
302 } else if value > max {
303 max
304 } else {
305 value
306 }
307}
308
309pub fn has_alpha(data: &PixelData) -> bool {
311 let size = (data.width as usize) * (data.height as usize) * 4;
312 let mut i = 3usize;
313 while i < size {
314 if data.data[i] != 255 {
315 return true;
316 }
317 i += 4;
318 }
319 false
320}
321
322pub fn reset_image_data(data: &mut PixelData) {
326 let buf = &mut data.data;
327 let alpha = 0xffu8;
328 let size = buf.len();
329 let mut p = 0usize;
330 while p < size {
331 buf[p] = 0;
332 buf[p + 1] = 0;
333 buf[p + 2] = 0;
334 buf[p + 3] = alpha;
335 p += 4;
336 }
337}
338
339pub fn decode_bitmap(input: &[u8], output: &mut [u8], width: usize, height: usize) {
342 let mut p = 0usize;
343 let mut o = 0usize;
344 for _y in 0..height {
345 let mut x = 0usize;
346 while x < width {
347 let mut b = input[o];
348 o += 1;
349 let mut i = 0;
350 while i < 8 && x < width {
351 let v: u8 = if b & 0x80 != 0 { 0 } else { 255 };
352 b <<= 1;
353 output[p] = v;
354 output[p + 1] = v;
355 output[p + 2] = v;
356 output[p + 3] = 255;
357 i += 1;
358 x += 1;
359 p += 4;
360 }
361 }
362 }
363}
364
365pub fn write_data_raw(data: &PixelData, offset: usize, width: usize, height: usize) -> Option<Vec<u8>> {
372 if width == 0 || height == 0 {
373 return None;
374 }
375 let mut array = vec![0u8; width * height];
376 for (i, slot) in array.iter_mut().enumerate() {
377 *slot = data.data[i * 4 + offset];
378 }
379 Some(array)
380}
381
382pub fn write_data_rle(
386 buffer: &mut [u8],
387 data_pixels: &PixelData,
388 offsets: &[usize],
389 large: bool,
390) -> Option<Vec<u8>> {
391 let width = data_pixels.width as i64;
392 let height = data_pixels.height as i64;
393 if width == 0 || height == 0 {
394 return None;
395 }
396 let data = &data_pixels.data;
397 let stride = 4 * width;
398
399 let mut ol: i64 = 0;
400 let mut o: i64 = (offsets.len() as i64) * (if large { 4 } else { 2 }) * height;
401
402 let get = |idx: i64| -> i64 { data[idx as usize] as i64 };
403
404 macro_rules! set {
411 ($buf:expr, $idx:expr, $val:expr) => {{
412 let idx = $idx as usize;
413 if idx < $buf.len() {
414 $buf[idx] = $val;
415 }
416 }};
417 }
418
419 for &offset in offsets {
420 let offset = offset as i64;
421 for y in 0..height {
422 let stride_start = y * stride;
423 let stride_end = stride_start + stride;
424 let last_index = stride_end + offset - 4;
425 let last_index2 = last_index - 4;
426 let start_offset = o;
427
428 let mut p = stride_start + offset;
429 while p < stride_end {
430 if p < last_index2 {
431 let mut value1 = get(p);
432 p += 4;
433 let mut value2 = get(p);
434 p += 4;
435 let mut value3 = get(p);
436
437 if value1 == value2 && value1 == value3 {
438 let mut count: i64 = 3;
439 while count < 128 && p < last_index && get(p + 4) == value1 {
440 count += 1;
441 p += 4;
442 }
443 set!(buffer, o, (1 - count) as u8);
444 o += 1;
445 set!(buffer, o, value1 as u8);
446 o += 1;
447 } else {
448 let count_index = o;
449 let mut write_last = true;
450 let mut count: i64 = 1;
451 set!(buffer, o, 0);
452 o += 1;
453 set!(buffer, o, value1 as u8);
454 o += 1;
455
456 while p < last_index && count < 128 {
457 p += 4;
458 value1 = value2;
459 value2 = value3;
460 value3 = get(p);
461
462 if value1 == value2 && value1 == value3 {
463 p -= 12;
464 write_last = false;
465 break;
466 } else {
467 count += 1;
468 set!(buffer, o, value1 as u8);
469 o += 1;
470 }
471 }
472
473 if write_last {
474 if count < 127 {
475 set!(buffer, o, value2 as u8);
476 o += 1;
477 set!(buffer, o, value3 as u8);
478 o += 1;
479 count += 2;
480 } else if count < 128 {
481 set!(buffer, o, value2 as u8);
482 o += 1;
483 count += 1;
484 p -= 4;
485 } else {
486 p -= 8;
487 }
488 }
489
490 set!(buffer, count_index, (count - 1) as u8);
491 }
492 } else if p == last_index {
493 set!(buffer, o, 0);
494 o += 1;
495 set!(buffer, o, get(p) as u8);
496 o += 1;
497 } else {
498 set!(buffer, o, 1);
500 o += 1;
501 set!(buffer, o, get(p) as u8);
502 o += 1;
503 p += 4;
504 set!(buffer, o, get(p) as u8);
505 o += 1;
506 }
507
508 p += 4;
509 }
510
511 let length = o - start_offset;
512
513 if large {
514 set!(buffer, ol, ((length >> 24) & 0xff) as u8);
515 ol += 1;
516 set!(buffer, ol, ((length >> 16) & 0xff) as u8);
517 ol += 1;
518 }
519
520 set!(buffer, ol, ((length >> 8) & 0xff) as u8);
521 ol += 1;
522 set!(buffer, ol, (length & 0xff) as u8);
523 ol += 1;
524 }
525 }
526
527 let end = (o as usize).min(buffer.len());
531 Some(buffer[..end].to_vec())
532}
533
534pub fn write_data_zip_without_prediction(data_pixels: &PixelData, offsets: &[usize]) -> Option<Vec<u8>> {
537 let size = (data_pixels.width as usize) * (data_pixels.height as usize);
538 let data = &data_pixels.data;
539 let mut channel = vec![0u8; size];
540 let mut buffers: Vec<Vec<u8>> = Vec::new();
541 let mut total_length = 0usize;
542
543 for &offset in offsets {
544 let mut o = offset;
545 for slot in channel.iter_mut().take(size) {
546 *slot = data[o];
547 o += 4;
548 }
549
550 let buffer = deflate_sync(&channel);
551 total_length += buffer.len();
552 buffers.push(buffer);
553 }
554
555 if !buffers.is_empty() {
556 let mut buffer = Vec::with_capacity(total_length);
557 for b in &buffers {
558 buffer.extend_from_slice(b);
559 }
560 Some(buffer)
561 } else {
562 None
564 }
565}
566
567fn deflate_sync(input: &[u8]) -> Vec<u8> {
569 let mut encoder = ZlibEncoder::new(Vec::new(), FlateCompression::default());
570 encoder.write_all(input).expect("zlib write");
571 encoder.finish().expect("zlib finish")
572}
573
574pub fn image_data_to_canvas(pixel_data: &PixelData) -> PixelData {
583 pixel_data.clone()
584}
585
586pub fn create_canvas_from_data(_data: &[u8]) -> PixelData {
590 create_canvas(100, 100)
591}
592
593pub fn create_canvas(width: u32, height: u32) -> PixelData {
597 PixelData {
598 width,
599 height,
600 data: vec![0u8; (width as usize) * (height as usize) * 4],
601 }
602}
603
604pub fn create_image_data(width: u32, height: u32) -> PixelData {
607 create_canvas(width, height)
608}
609
610pub fn initialize_canvas() {
614 }
616
617#[cfg(test)]
622mod tests {
623 use super::*;
624
625 #[test]
626 fn blend_mode_round_trip_all_entries() {
627 let all = [
628 BlendMode::PassThrough,
629 BlendMode::Normal,
630 BlendMode::Dissolve,
631 BlendMode::Darken,
632 BlendMode::Multiply,
633 BlendMode::ColorBurn,
634 BlendMode::LinearBurn,
635 BlendMode::DarkerColor,
636 BlendMode::Lighten,
637 BlendMode::Screen,
638 BlendMode::ColorDodge,
639 BlendMode::LinearDodge,
640 BlendMode::LighterColor,
641 BlendMode::Overlay,
642 BlendMode::SoftLight,
643 BlendMode::HardLight,
644 BlendMode::VividLight,
645 BlendMode::LinearLight,
646 BlendMode::PinLight,
647 BlendMode::HardMix,
648 BlendMode::Difference,
649 BlendMode::Exclusion,
650 BlendMode::Subtract,
651 BlendMode::Divide,
652 BlendMode::Hue,
653 BlendMode::Saturation,
654 BlendMode::Color,
655 BlendMode::Luminosity,
656 ];
657 for mode in all {
658 let key = from_blend_mode(mode);
659 assert_eq!(key.len(), 4, "key must be 4 chars: {key:?}");
660 assert_eq!(to_blend_mode(key), Some(mode), "round trip failed for {key:?}");
661 }
662 }
663
664 #[test]
665 fn blend_mode_spacey_keys() {
666 assert_eq!(to_blend_mode("mul "), Some(BlendMode::Multiply));
667 assert_eq!(to_blend_mode("div "), Some(BlendMode::ColorDodge));
668 assert_eq!(from_blend_mode(BlendMode::Luminosity), "lum ");
669 assert_eq!(to_blend_mode("nope"), None);
670 }
671
672 #[test]
673 fn clamp_edges() {
674 assert_eq!(clamp(-1.0, 0.0, 10.0), 0.0);
675 assert_eq!(clamp(11.0, 0.0, 10.0), 10.0);
676 assert_eq!(clamp(5.0, 0.0, 10.0), 5.0);
677 assert_eq!(clamp(0.0, 0.0, 10.0), 0.0);
678 assert_eq!(clamp(10.0, 0.0, 10.0), 10.0);
679 }
680
681 #[test]
682 fn offset_for_channel_rgb_and_cmyk() {
683 assert_eq!(offset_for_channel(ChannelId::Color0, false), 0);
684 assert_eq!(offset_for_channel(ChannelId::Color1, false), 1);
685 assert_eq!(offset_for_channel(ChannelId::Color2, false), 2);
686 assert_eq!(offset_for_channel(ChannelId::Color3, false), 4);
688 assert_eq!(offset_for_channel(ChannelId::Color3, true), 3);
689 assert_eq!(offset_for_channel(ChannelId::Transparency, false), 3);
691 assert_eq!(offset_for_channel(ChannelId::Transparency, true), 4);
692 assert_eq!(offset_for_channel(ChannelId::UserMask, false), -1);
694 assert_eq!(offset_for_channel(ChannelId::RealUserMask, true), -2);
695 }
696
697 #[test]
698 fn has_alpha_detects_non_opaque() {
699 let opaque = PixelData {
700 width: 2,
701 height: 1,
702 data: vec![1, 2, 3, 255, 4, 5, 6, 255],
703 };
704 assert!(!has_alpha(&opaque));
705
706 let translucent = PixelData {
707 width: 2,
708 height: 1,
709 data: vec![1, 2, 3, 255, 4, 5, 6, 128],
710 };
711 assert!(has_alpha(&translucent));
712 }
713
714 #[test]
715 fn reset_image_data_sets_black_opaque() {
716 let mut pd = PixelData {
717 width: 2,
718 height: 1,
719 data: vec![9, 9, 9, 9, 9, 9, 9, 9],
720 };
721 reset_image_data(&mut pd);
722 assert_eq!(pd.data, vec![0, 0, 0, 255, 0, 0, 0, 255]);
723 }
724
725 #[test]
726 fn decode_bitmap_packs_bits() {
727 let input = [0b1010_0000u8];
729 let mut output = vec![0u8; 8 * 4];
730 decode_bitmap(&input, &mut output, 8, 1);
731 assert_eq!(output[0], 0);
733 assert_eq!(output[4], 255);
734 assert_eq!(output[8], 0);
735 assert_eq!(output[12], 255);
736 assert_eq!(output[3], 255);
738 }
739
740 #[test]
741 fn write_data_raw_extracts_channel() {
742 let pd = PixelData {
744 width: 2,
745 height: 1,
746 data: vec![10, 20, 30, 40, 50, 60, 70, 80],
747 };
748 assert_eq!(write_data_raw(&pd, 0, 2, 1), Some(vec![10, 50])); assert_eq!(write_data_raw(&pd, 3, 2, 1), Some(vec![40, 80])); assert_eq!(write_data_raw(&pd, 0, 0, 1), None);
751 }
752
753 #[test]
754 fn zip_without_prediction_round_trips() {
755 use flate2::read::ZlibDecoder;
756 use std::io::Read;
757
758 let pd = PixelData {
759 width: 4,
760 height: 1,
761 data: vec![
762 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0,
763 ],
764 };
765 let out = write_data_zip_without_prediction(&pd, &[0]).unwrap();
766 let mut decoder = ZlibDecoder::new(&out[..]);
767 let mut decoded = Vec::new();
768 decoder.read_to_end(&mut decoded).unwrap();
769 assert_eq!(decoded, vec![1, 2, 3, 4]);
770 }
771
772 #[test]
773 fn rev_map_swaps() {
774 let mut m = Dict::new();
775 m.insert("a".into(), "1".into());
776 m.insert("b".into(), "2".into());
777 let r = rev_map(&m);
778 assert_eq!(r.get("1"), Some(&"a".to_string()));
779 assert_eq!(r.get("2"), Some(&"b".to_string()));
780 }
781
782 #[test]
783 fn enum_codec_encode_decode() {
784 let mut map = Dict::new();
785 map.insert("alpha".into(), "Alph".into());
786 map.insert("beta".into(), "Beta".into());
787 let codec = EnumCodec::new("Enum", "alpha", map);
788
789 assert_eq!(codec.encode(Some("beta")).unwrap(), "Enum.Beta");
790 assert_eq!(codec.encode(None).unwrap(), "Enum.Alph"); assert!(codec.encode(Some("gamma")).is_err());
792
793 assert_eq!(codec.decode("Enum.Beta").unwrap(), "beta");
794 assert_eq!(codec.decode("Enum.Alph").unwrap(), "alpha");
795 assert_eq!(codec.decode("Enum").unwrap(), "alpha");
797 assert!(codec.decode("Enum.Zzzz").is_err());
798 }
799}