limage - xcf/image_decode.go
1 package xcf
2
3 import (
4 "image"
5 "image/color"
6 "io"
7 "math"
8
9 "vimagination.zapto.org/limage"
10 "vimagination.zapto.org/limage/lcolor"
11 "vimagination.zapto.org/memio"
12 )
13
14 func (d *decoder) ReadImage(width, height, mode uint32) image.Image {
15 twidth := d.ReadUint32()
16 theight := d.ReadUint32()
17
18 if twidth != width || theight != height {
19 d.SetError(ErrInconsistantData)
20 return nil
21 }
22
23 bpp := d.ReadUint32()
24
25 switch mode {
26 case 2, 4:
27 if bpp != 1 {
28 d.SetError(ErrInconsistantData)
29 return nil
30 }
31 case 3, 5:
32 if bpp != 2 {
33 d.SetError(ErrInconsistantData)
34 return nil
35 }
36 case 0:
37 if bpp != 3 {
38 d.SetError(ErrInconsistantData)
39 return nil
40 }
41 case 1:
42 if bpp != 4 {
43 d.SetError(ErrInconsistantData)
44 return nil
45 }
46 }
47
48 var lptr uint64
49 if d.mode < 2 {
50 lptr = uint64(d.ReadUint32())
51 } else {
52 lptr = d.ReadUint64()
53 }
54
55 d.Goto(lptr)
56
57 w := d.ReadUint32()
58 h := d.ReadUint32()
59
60 if w != width || h != height {
61 d.SetError(ErrInconsistantData)
62 return nil
63 }
64
65 tiles := make([]uint64, int(math.Ceil(float64(w)/64)*math.Ceil(float64(h)/64)))
66
67 if d.mode < 2 {
68 for i := range tiles {
69 tiles[i] = uint64(d.ReadUint32())
70 }
71 } else {
72 for i := range tiles {
73 tiles[i] = d.ReadUint64()
74 }
75 }
76
77 if d.ReadUint32() != 0 {
78 d.SetError(ErrInconsistantData)
79 return nil
80 }
81
82 r := image.Rect(0, 0, int(width), int(height))
83
84 var pixBuffer [64 * 64 * 4]byte
85
86 if d.decompress || d.compression == 0 {
87 var (
88 im image.Image
89 imReader interface {
90 ReadColour(int, int, []byte)
91 }
92 )
93 switch mode {
94 case 0: // rgb
95 rgb := limage.NewRGB(r)
96 im = rgb
97 imReader = rgbImageReader{rgb}
98 case 1: // rgba
99 rgba := image.NewNRGBA(r)
100 im = rgba
101 imReader = rgbaImageReader{rgba}
102 case 2: // gray
103 g := image.NewGray(r)
104 im = g
105 imReader = grayImageReader{g}
106 case 3: // gray + alpha
107 ga := limage.NewGrayAlpha(r)
108 im = ga
109 imReader = grayAlphaImageReader{ga}
110 case 4: // indexed
111 in := image.NewPaletted(r, color.Palette(d.palette))
112 im = in
113 imReader = indexedImageReader{in}
114 case 5: // indexed + alpha
115 in := limage.NewPalettedAlpha(r, d.palette)
116 im = in
117 imReader = palettedAlphaReader{in}
118 }
119
120 var cr io.Reader
121 if d.compression == 0 { // no compression
122 cr = &d.reader
123 } else { // rle
124 cr = &rle{Reader: d.reader.StickyBigEndianReader}
125 }
126
127 pixel := make([]byte, bpp)
128 channels := make([][]byte, bpp)
129
130 for y := uint32(0); y < height; y += 64 {
131 for x := uint32(0); x < width; x += 64 {
132 d.Goto(tiles[0])
133 tiles = tiles[1:]
134 w := width - x
135 if w > 64 {
136 w = 64
137 }
138 h := height - y
139 if h > 64 {
140 h = 64
141 }
142 n := w * h
143 _, err := cr.Read(pixBuffer[:n*bpp])
144 d.SetError(err)
145 for i := uint32(0); i < bpp; i++ {
146 channels[i] = pixBuffer[n*i : n*(i+1)]
147 }
148 for j := uint32(0); j < h; j++ {
149 for i := uint32(0); i < w; i++ {
150 for k := uint32(0); k < bpp; k++ {
151 pixel[k] = channels[k][0]
152 channels[k] = channels[k][1:]
153 }
154 imReader.ReadColour(int(x+i), int(y+j), pixel)
155 }
156 }
157 }
158 }
159 return im
160 } else {
161 ci := compressedImage{
162 tiles: make([][][]byte, 0, len(tiles)),
163 width: int(width),
164 tile: -1,
165 }
166 buf := make(memio.Buffer, 0, 64*64*4)
167 for y := uint32(0); y < height; y += 64 {
168 for x := uint32(0); x < width; x += 64 {
169 d.Goto(tiles[0])
170 tiles = tiles[1:]
171 w := width - x
172 if w > 64 {
173 w = 64
174 }
175 h := height - y
176 if h > 64 {
177 h = 64
178 }
179 n := w * h
180 ts := make([][]byte, 0, bpp)
181 for i := uint32(0); i < bpp; i++ {
182 d.SetError(d.readRLE(int(n), &buf))
183 b := make([]byte, len(buf))
184 copy(b, buf)
185 buf = buf[:0]
186 ts = append(ts, b)
187 }
188 ci.tiles = append(ci.tiles, ts)
189 }
190
191 }
192 switch mode {
193 case 0: // rgb
194 return &CompressedRGB{ci, r}
195 case 1: // rgba
196 return &CompressedNRGBA{ci, r}
197 case 2: // gray
198 return &CompressedGray{ci, r}
199 case 3: // gray + alpha
200 return &CompressedGrayAlpha{ci, r}
201 case 4: // indexed
202 return &CompressedPaletted{ci, r, color.Palette(d.palette)}
203 case 5: // indexed + alpha
204 return &CompressedPalettedAlpha{ci, r, d.palette}
205 default:
206 return nil
207 }
208 }
209 }
210
211 /*
212 type colourReader interface {
213 ReadByte() byte
214 }
215 */
216
217 type rgbaImageReader struct {
218 *image.NRGBA
219 }
220
221 func (rgba rgbaImageReader) ReadColour(x, y int, pixel []byte) {
222 rgba.SetNRGBA(x, y, color.NRGBA{
223 R: pixel[0],
224 G: pixel[1],
225 B: pixel[2],
226 A: pixel[3],
227 })
228 }
229
230 type grayImageReader struct {
231 *image.Gray
232 }
233
234 func (g grayImageReader) ReadColour(x, y int, pixel []byte) {
235 g.SetGray(x, y, color.Gray{pixel[0]})
236 }
237
238 type indexedImageReader struct {
239 *image.Paletted
240 }
241
242 func (p indexedImageReader) ReadColour(x, y int, pixel []byte) {
243 p.SetColorIndex(x, y, pixel[0])
244 }
245
246 type grayAlphaImageReader struct {
247 *limage.GrayAlpha
248 }
249
250 func (ga grayAlphaImageReader) ReadColour(x, y int, pixels []byte) {
251 ga.SetGrayAlpha(x, y, lcolor.GrayAlpha{Y: pixels[0], A: pixels[1]})
252 }
253
254 type palettedAlphaReader struct {
255 *limage.PalettedAlpha
256 }
257
258 func (p palettedAlphaReader) ReadColour(x, y int, pixels []byte) {
259 p.SetIndexAlpha(x, y, lcolor.IndexedAlpha{
260 I: pixels[0],
261 A: pixels[1],
262 })
263 }
264
265 type rgbImageReader struct {
266 *limage.RGB
267 }
268
269 func (rg rgbImageReader) ReadColour(x, y int, pixels []byte) {
270 rg.SetRGB(x, y, lcolor.RGB{R: pixels[0], G: pixels[1], B: pixels[2]})
271 }
272