Skip to main content

xcf_rs/data/
property.rs

1use byteorder::{BigEndian, ReadBytesExt};
2use std::io::Read;
3
4use crate::data::error::Error;
5use crate::data::xcf::XcfCompression;
6use crate::RgbaPixel;
7
8#[derive(Debug, PartialEq)]
9pub struct ResolutionProperty {
10    pub xres: f32,
11    pub yres: f32,
12}
13
14#[derive(Debug, PartialEq)]
15pub struct ParasiteProperty {
16    pub name: String,
17    pub flags: u32,
18    pub data: String,
19}
20
21#[derive(Debug, PartialEq)]
22pub enum PropertyPayload {
23    ColorMap { colors: usize },
24    End,
25    Compression(XcfCompression),
26    ResolutionProperty(ResolutionProperty),
27    Tatoo(u32),
28    Unit(u32),
29    Parasites(Vec<ParasiteProperty>),
30    // layer property
31    ActiveLayer(),
32    OpacityLayer(RgbaPixel),
33    FloatOpacityLayer(),
34    VisibleLayer(),
35    LinkedLayer(u32),
36    ColorTagLayer(u32),
37    LockContentLayer(u32),
38    LockAlphaLayer(u32),
39    LockPositionLayer(u32),
40    ApplyMaskLayer(u32),
41    EditMaskLayer(u32),
42    ShowMaskLayer(u32),
43    OffsetsLayer(u32, u32),
44    ModeLayer(u32),
45    BlendSpaceLayer(u32),
46    CompositeSpaceLayer(u32),
47    CompositeModeLayer(u32),
48    Unknown(Vec<u8>),
49}
50
51#[derive(Debug, PartialEq)]
52pub struct Property {
53    pub kind: PropertyIdentifier,
54    pub length: usize,
55    pub payload: PropertyPayload,
56}
57
58impl Property {
59    // TODO: GIMP usually calculates sizes based on data and goes from that instead of the reported
60    // property length... (for known properties)
61    fn guess_size(&self) -> usize {
62        match self.payload {
63            PropertyPayload::ColorMap { colors, .. } => {
64                /* apparently due to a GIMP bug sometimes self.length will be n + 4 */
65                3 * colors + 4
66            }
67            // this is the best we can do otherwise
68            _ => self.length,
69        }
70    }
71
72    fn parse<R: Read>(mut rdr: R) -> Result<Property, Error> {
73        let kind = PropertyIdentifier::new(rdr.read_u32::<BigEndian>()?);
74        let length = rdr.read_u32::<BigEndian>()? as usize;
75        let payload = PropertyPayload::parse(&mut rdr, kind, length)?;
76        Ok(Property {
77            kind,
78            length,
79            payload,
80        })
81    }
82
83    pub fn parse_list<R: Read>(mut rdr: R) -> Result<Vec<Property>, Error> {
84        let mut props = Vec::new();
85        loop {
86            let p = Property::parse(&mut rdr)?;
87            if let PropertyIdentifier::PropEnd = p.kind {
88                break;
89            }
90            // only push non end
91            props.push(p);
92        }
93        Ok(props)
94    }
95}
96
97macro_rules! prop_ident_gen {
98    (
99        #[derive(Debug, Clone, Copy, PartialEq)]
100        #[repr(u32)]
101        pub enum PropertyIdentifier {
102            //Unknown(u32),
103            $(
104                $prop:ident = $val:expr
105            ),+,
106        }
107    ) => {
108        #[derive(Debug, Clone, Copy, PartialEq)]
109        #[repr(u32)]
110        pub enum PropertyIdentifier {
111            $(
112                $prop = $val
113            ),+,
114            // we have to put this at the end, since otherwise it will try to have value zero,
115            // we really don't care what it is as long as it doesn't conflict with anything else
116            // (however in the macro we have to put it first since it's a parsing issue)
117            //Unknown(u32),
118        }
119
120        impl PropertyIdentifier {
121            pub fn new(prop: u32) -> PropertyIdentifier {
122                match prop {
123                    $(
124                        $val => PropertyIdentifier::$prop
125                    ),+,
126                    _ => todo!()
127                    //_ => PropertyIdentifier::Unknown(prop),
128                }
129            }
130        }
131    }
132}
133
134prop_ident_gen! {
135    #[derive(Debug, Clone, Copy, PartialEq)]
136    #[repr(u32)]
137    pub enum PropertyIdentifier {
138        PropEnd = 0,
139        PropColormap = 1,
140        PropActiveLayer = 2,
141        PropActiveChannel = 3,
142        PropSelection = 4,
143        PropFloatingSelection = 5,
144        PropOpacity = 6,
145        PropMode = 7,
146        PropVisible = 8,
147        PropLinked = 9,
148        PropLockAlpha = 10,
149        PropApplyMask = 11,
150        PropEditMask = 12,
151        PropShowMask = 13,
152        PropOffsets = 15,
153        PropCompression = 17,
154        TypeIdentification = 18,
155        PropResolution = 19,
156        PropTattoo = 20,
157        PropParasites = 21,
158        PropUnit = 22,
159        PropPaths = 23,
160        PropUserUnit = 24,
161        PropVectors = 25,
162        PropTextLayerFlags = 26,
163        PropOldSamplePoints = 27,
164        PropLockContent = 28,
165        PropLockPosition = 32,
166        PropFloatOpacity = 33,
167        PropColorTag = 34,
168        PropCompositeMode = 35,
169        PropCompositeSpace = 36,
170        PropBlendSpace = 37,
171        PropFloatColor = 38,
172        PropSamplePoints = 39,
173        PropItemSet = 40,
174        PropItemSetItem = 41,
175        PropLockVisibility = 42,
176        PropSelectedPath = 43,
177        PropFilterRegion = 44,
178        PropFilterArgument = 45,
179        PropFilterClip = 46,
180    }
181}