limage - xcf/layer_decode.go
1 package xcf
2
3 import (
4 "errors"
5 "image"
6
7 "vimagination.zapto.org/limage"
8 )
9
10 type layer struct {
11 limage.Layer
12 alpha bool
13 group bool
14 itemPath []rune
15 }
16
17 func (d *decoder) ReadLayer() layer {
18 var (
19 l layer
20 parasites parasites
21 )
22 l.LayerBounds.Max.X = int(d.ReadUint32())
23 l.LayerBounds.Max.Y = int(d.ReadUint32())
24 typ := d.ReadUint32()
25 if typ>>1 != d.baseType {
26 d.SetError(ErrInvalidLayerType)
27 return l
28 }
29 l.alpha = typ&1 == 1
30 l.Name = d.ReadString()
31
32 // read properties
33 PropertyLoop:
34 for {
35 typ := d.ReadUint32()
36 plength := d.ReadUint32()
37 switch typ {
38 // general properties
39 case propEnd:
40 if plength != 0 {
41 d.SetError(ErrInvalidProperties)
42 }
43 break PropertyLoop
44 case propLinked:
45 d.SkipBoolProperty()
46 case propLockContent:
47 d.SkipBoolProperty()
48 case propOpacity:
49 o := d.ReadUint32()
50 if o > 255 {
51 d.SetError(ErrInvalidOpacity)
52 }
53 l.Transparency = 255 - uint8(o)
54 case propParasites:
55 parasites = d.ReadParasites(plength)
56 case propTattoo:
57 d.SkipUint32()
58 case propVisible:
59 l.Invisible = !d.ReadBoolProperty()
60
61 //layer properties
62 case propActiveLayer:
63 // active layer
64 case propApplyMask:
65 d.SkipBoolProperty()
66 case propEditMask:
67 d.SkipBoolProperty()
68 case propFloatingSelection:
69 d.SkipUint32()
70 case propGroupItem:
71 l.group = true
72 case propItemPath:
73 if plength&3 != 0 {
74 d.SetError(ErrInvalidItemPathLength)
75 }
76 l.itemPath = make([]rune, plength>>2)
77 for i := uint32(0); i < plength>>2; i++ {
78 l.itemPath[i] = rune(d.ReadUint32())
79 }
80 case propGroupItemFlags:
81 d.SkipUint32()
82 case propLockAlpha:
83 d.SkipBoolProperty()
84 case propMode:
85 if d.baseType != 0 {
86 switch d.ReadUint32() {
87 case 2: // Behind
88 l.Mode = limage.CompositeBehind
89 default:
90 l.Mode = limage.CompositeNormal
91 }
92 } else {
93 switch d.ReadUint32() {
94 case 0, 28:
95 l.Mode = limage.CompositeNormal
96 case 1:
97 l.Mode = limage.CompositeDissolve
98 case 2, 29:
99 l.Mode = limage.CompositeBehind
100 case 3, 30:
101 l.Mode = limage.CompositeMultiply
102 case 4, 31:
103 l.Mode = limage.CompositeScreen
104 case 5, 23:
105 l.Mode = limage.CompositeOverlay
106 case 6, 32:
107 l.Mode = limage.CompositeDifference
108 case 7, 33:
109 l.Mode = limage.CompositeAddition
110 case 8, 34:
111 l.Mode = limage.CompositeSubtract
112 case 9, 35, 54:
113 l.Mode = limage.CompositeDarkenOnly
114 case 10, 36, 55:
115 l.Mode = limage.CompositeLightenOnly
116 case 11, 24, 37:
117 l.Mode = limage.CompositeHue
118 case 12, 38:
119 l.Mode = limage.CompositeSaturation
120 case 13, 26, 39:
121 l.Mode = limage.CompositeColor
122 case 14, 40:
123 l.Mode = limage.CompositeValue
124 case 15, 41:
125 l.Mode = limage.CompositeDivide
126 case 16, 42:
127 l.Mode = limage.CompositeDodge
128 case 17, 43:
129 l.Mode = limage.CompositeBurn
130 case 18, 44:
131 l.Mode = limage.CompositeHardLight
132 case 19, 45:
133 l.Mode = limage.CompositeSoftLight
134 case 20, 46:
135 l.Mode = limage.CompositeGrainExtract
136 case 21, 47:
137 l.Mode = limage.CompositeGrainMerge
138 case 22, 57:
139 l.Mode = limage.CompositeColorErase
140 case 25:
141 l.Mode = limage.CompositeChroma
142 case 27:
143 l.Mode = limage.CompositeLightness
144 case 48:
145 l.Mode = limage.CompositeVividLight
146 case 49:
147 l.Mode = limage.CompositePinLight
148 case 50:
149 l.Mode = limage.CompositeLinearLight
150 case 51:
151 l.Mode = limage.CompositeHardMix
152 case 52:
153 l.Mode = limage.CompositeExclusion
154 case 53:
155 l.Mode = limage.CompositeLinearBurn
156 case 56:
157 l.Mode = limage.CompositeLuminance
158 case 58:
159 l.Mode = limage.CompositeErase
160 case 59:
161 l.Mode = limage.CompositeMerge
162 case 60:
163 l.Mode = limage.CompositeSplit
164 case 61:
165 l.Mode = limage.CompositePassThrough
166 default:
167 l.Mode = 0
168 }
169 }
170 case propOffsets:
171 offsetX := int(d.ReadInt32())
172 offsetY := int(d.ReadInt32())
173 l.LayerBounds = l.LayerBounds.Add(image.Pt(offsetX, offsetY))
174 case propShowMask:
175 d.SkipBoolProperty()
176 case propTextLayerFlags:
177 d.SkipUint32()
178 case propFloatOpacity:
179 l.Transparency = 255 - uint8(d.ReadFloat32()*255)
180 default:
181 d.Skip(plength)
182 }
183 }
184
185 var hptr, mptr uint64
186 if d.mode < 2 {
187 hptr = uint64(d.ReadUint32())
188 mptr = uint64(d.ReadUint32())
189 } else {
190 hptr = d.ReadUint64()
191 mptr = d.ReadUint64()
192 }
193
194 d.Goto(hptr)
195 // read hierarchy
196
197 if !l.group { // skip reading image if its a group
198 l.Image = d.ReadImage(uint32(l.LayerBounds.Dx()), uint32(l.LayerBounds.Dy()), typ)
199 if l.Image == nil {
200 return l
201 }
202 }
203 if t := parasites.Get(textParasiteName); t != nil {
204 textData, err := parseTextData(t)
205 if err != nil {
206 d.SetError(ErrInvalidLayerType)
207 return l
208 }
209 l.Image = limage.Text{
210 Image: l.Image,
211 TextData: textData,
212 }
213 }
214 if mptr != 0 { // read layer mask
215 d.Goto(mptr)
216 var m limage.MaskedImage
217 m.Image = l.Image
218 m.Mask = d.ReadChannel()
219 if m.Mask == nil {
220 return l
221 }
222 if !l.LayerBounds.Eq(m.Mask.Bounds()) {
223 d.SetError(ErrInconsistantData)
224 return l
225 }
226 l.Image = m
227 }
228 return l
229 }
230
231 // Errors
232 var (
233 ErrInvalidLayerType = errors.New("invalid layer type")
234 ErrInvalidItemPathLength = errors.New("invalid item path length")
235 ErrInconsistantData = errors.New("inconsistant data read")
236 )
237