limage - compressed.go
1 package xcf
2
3 import (
4 "errors"
5 "image"
6 "image/color"
7 "io"
8
9 "vimagination.zapto.org/byteio"
10 "vimagination.zapto.org/limage/lcolor"
11 "vimagination.zapto.org/memio"
12 )
13
14 type compressedImage struct {
15 tiles [][][]byte
16 width int
17 tile int
18 decompressed [64 * 64 * 4]byte
19 }
20
21 func (c *compressedImage) decompressTile(x, y int) int {
22 if tile := (y/64)*((c.width+63)/64) + (x / 64); tile != c.tile {
23 var (
24 n int
25 data memio.Buffer
26 )
27
28 r := rle{Reader: &byteio.StickyBigEndianReader{Reader: &data}}
29
30 for n, data = range c.tiles[tile] {
31 r.Read(c.decompressed[64*64*n : 64*64*(n+1)])
32
33 if errors.Is(r.Reader.Err, io.EOF) {
34 r.Reader.Err = nil
35 }
36 }
37
38 c.tile = tile
39 }
40
41 if x < c.width & ^63 {
42 return 64*(y%64) + (x % 64)
43 }
44
45 return (c.width&63)*(y%64) + (x % 64)
46 }
47
48 // CompressedRGB is an image.Image for which the data remains in a compressed
49 // form until read.
50 type CompressedRGB struct {
51 compressedImage
52 Rect image.Rectangle
53 }
54
55 // ColorModel returns the RGB Color Model.
56 func (CompressedRGB) ColorModel() color.Model { return lcolor.RGBModel }
57
58 // Bounds returns a Rect containing the boundary data for the image.
59 func (c *CompressedRGB) Bounds() image.Rectangle { return c.Rect }
60
61 // At returns colour at the specified coords.
62 func (c *CompressedRGB) At(x, y int) color.Color { return c.RGBAt(x, y) }
63
64 // RGBAt returns RGB colour at the specified coords.
65 func (c *CompressedRGB) RGBAt(x, y int) lcolor.RGB {
66 if !(image.Point{x, y}).In(c.Rect) {
67 return lcolor.RGB{}
68 }
69
70 p := c.decompressTile(x, y)
71
72 return lcolor.RGB{
73 R: c.decompressed[p],
74 G: c.decompressed[p+64*64],
75 B: c.decompressed[p+64*64*2],
76 }
77 }
78
79 // CompressedNRGB is an image.Image for which the data remains in a compressed
80 // form until read.
81 type CompressedNRGBA struct {
82 compressedImage
83 Rect image.Rectangle
84 }
85
86 // ColorModel returns the NRGBA Color Model.
87 func (CompressedNRGBA) ColorModel() color.Model { return color.NRGBAModel }
88
89 // Bounds returns a Rect containing the boundary data for the image.
90 func (c *CompressedNRGBA) Bounds() image.Rectangle { return c.Rect }
91
92 // At returns colour at the specified coords.
93 func (c *CompressedNRGBA) At(x, y int) color.Color { return c.NRGBAAt(x, y) }
94
95 // NRGBAAt returns NRGBA colour at the specified coords.
96 func (c *CompressedNRGBA) NRGBAAt(x, y int) color.NRGBA {
97 if !(image.Point{x, y}).In(c.Rect) {
98 return color.NRGBA{}
99 }
100
101 p := c.decompressTile(x, y)
102
103 return color.NRGBA{
104 c.decompressed[p],
105 c.decompressed[p+64*64],
106 c.decompressed[p+64*64*2],
107 c.decompressed[p+64*64*3],
108 }
109 }
110
111 // CompressedGray is an image.Image for which the data remains in a compressed
112 // form until read.
113 type CompressedGray struct {
114 compressedImage
115 Rect image.Rectangle
116 }
117
118 // ColorModel returns the Gray Color Model.
119 func (CompressedGray) ColorModel() color.Model { return color.GrayModel }
120
121 // Bounds returns a Rect containing the boundary data for the image.
122 func (c *CompressedGray) Bounds() image.Rectangle { return c.Rect }
123
124 // At returns colour at the specified coords.
125 func (c *CompressedGray) At(x, y int) color.Color { return c.GrayAt(x, y) }
126
127 // GrayAt returns Gray colour at the specified coords.
128 func (c *CompressedGray) GrayAt(x, y int) color.Gray {
129 if !(image.Point{x, y}).In(c.Rect) {
130 return color.Gray{}
131 }
132
133 p := c.decompressTile(x, y)
134
135 return color.Gray{
136 c.decompressed[p],
137 }
138 }
139
140 // CompressedGrayAlpha is an image.Image for which the data remains in a
141 // compressed form until read.
142 type CompressedGrayAlpha struct {
143 compressedImage
144 Rect image.Rectangle
145 }
146
147 // ColorModel returns the Gray Alpha Color Model.
148 func (CompressedGrayAlpha) ColorModel() color.Model { return lcolor.GrayAlphaModel }
149
150 // Bounds returns a Rect containing the boundary data for the image.
151 func (c *CompressedGrayAlpha) Bounds() image.Rectangle { return c.Rect }
152
153 // At returns colour at the specified coords.
154 func (c *CompressedGrayAlpha) At(x, y int) color.Color { return c.GrayAlphaAt(x, y) }
155
156 // GrayAlphaAt returns Gray+Alpha colour at the specified coords.
157 func (c *CompressedGrayAlpha) GrayAlphaAt(x, y int) lcolor.GrayAlpha {
158 if !(image.Point{x, y}).In(c.Rect) {
159 return lcolor.GrayAlpha{}
160 }
161
162 p := c.decompressTile(x, y)
163
164 return lcolor.GrayAlpha{
165 Y: c.decompressed[p],
166 A: c.decompressed[p+64*64],
167 }
168 }
169
170 // CompressedPaletted is an image.Image for which the data remains in a
171 // compressed form until read.
172 type CompressedPaletted struct {
173 compressedImage
174 Rect image.Rectangle
175 Palette color.Palette
176 }
177
178 // ColorModel returns the Palette of the image.
179 func (c *CompressedPaletted) ColorModel() color.Model { return c.Palette }
180
181 // Bounds returns a Rect containing the boundary data for the image.
182 func (c *CompressedPaletted) Bounds() image.Rectangle { return c.Rect }
183
184 // At returns colour at the specified coords.
185 func (c *CompressedPaletted) At(x, y int) color.Color {
186 if c.Palette == nil {
187 return nil
188 }
189
190 if !(image.Point{x, y}).In(c.Rect) {
191 return color.Gray{}
192 }
193
194 p := c.decompressTile(x, y)
195 i := c.decompressed[p]
196 r, g, b, _ := c.Palette[i].RGBA()
197
198 return color.NRGBA{
199 R: uint8(r >> 8),
200 G: uint8(g >> 8),
201 B: uint8(b >> 8),
202 A: 255,
203 }
204 }
205
206 // CompressedPalettedAlpha is an image.Image for which the data remains in a
207 // compressed form until read.
208 type CompressedPalettedAlpha struct {
209 compressedImage
210 Rect image.Rectangle
211 Palette lcolor.AlphaPalette
212 }
213
214 // ColorModel returns the Palette of the image
215 func (c *CompressedPalettedAlpha) ColorModel() color.Model { return c.Palette }
216
217 // Bounds returns a Rect containing the boundary data for the image
218 func (c *CompressedPalettedAlpha) Bounds() image.Rectangle { return c.Rect }
219
220 // At returns colour at the specified coords.
221 func (c *CompressedPalettedAlpha) At(x, y int) color.Color {
222 if c.Palette == nil {
223 return nil
224 }
225
226 if !(image.Point{x, y}).In(c.Rect) {
227 return color.Gray{}
228 }
229
230 p := c.decompressTile(x, y)
231 r, g, b, _ := c.Palette[c.decompressed[p]].RGBA()
232
233 return color.NRGBA{
234 R: uint8(r >> 8),
235 G: uint8(g >> 8),
236 B: uint8(b >> 8),
237 A: c.decompressed[64*64+p],
238 }
239 }
240