Rename GP_Context -> GP_Pixmap
[gfxprim.git] / libs / loaders / GP_BMP_RLE.h
blob950f3ee253f12775ba9b08ec71d87f79a9d0180e
1 /*****************************************************************************
2 * This file is part of gfxprim library. *
3 * *
4 * Gfxprim is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
8 * *
9 * Gfxprim is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with gfxprim; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
17 * Boston, MA 02110-1301 USA *
18 * *
19 * Copyright (C) 2009-2014 Cyril Hrubis <metan@ucw.cz> *
20 * *
21 *****************************************************************************/
25 BMP RLE decoder.
29 #include "core/GP_Fill.h"
31 enum RLE_state {
32 RLE_START,
33 /* end of RLE or bitmap was reached */
34 RLE_STOP,
35 /* in the middle of repeat sequence */
36 RLE_REPEAT,
37 /* in the middle of undecoded run */
38 RLE_UNDECODED,
41 struct RLE {
42 /* current state */
43 uint8_t state;
45 /* image size */
46 uint32_t w;
47 uint32_t h;
49 /* current position */
50 uint32_t x;
51 uint32_t y;
53 uint8_t rep;
54 uint8_t flag:1;
55 /* set to 1 if next pixel is in c */
56 uint8_t move:2;
58 /* current value */
59 int c;
61 GP_IO *io;
63 int buf_pos;
64 int buf_end;
65 uint8_t buf[512];
68 #define DECLARE_RLE(name, iw, ih, iio) struct RLE name = { \
69 .state = RLE_START, \
70 .w = iw, .h = ih, \
71 .x = 0, .y = 0, \
72 .rep = 0, \
73 .io = io, \
74 .buf_pos = 0, \
75 .buf_end = 0, \
78 #define GETC(rle) do { \
79 if (rle->buf_pos < rle->buf_end) { \
80 rle->c = rle->buf[rle->buf_pos++]; \
81 } else { \
82 rle->buf_end = GP_IORead(rle->io, rle->buf, sizeof(rle->buf));\
83 if (rle->buf_end <= 0) \
84 return EIO; \
85 rle->c = rle->buf[0]; \
86 rle->buf_pos = 1; \
87 } \
88 } while (0)
90 static void RLE8_move(struct RLE *rle)
92 if (!rle->move)
93 return;
95 if (++rle->x >= rle->w) {
96 /* wrap around the end of line */
97 rle->x = 0;
99 /* all pixels filled */
100 if (++rle->y >= rle->h) {
101 GP_DEBUG(4, "y >= h, stop");
102 rle->state = RLE_STOP;
106 //GP_DEBUG(4, "RLE Move to %u %u", rle->x, rle->y);
108 rle->move = 0;
111 static int RLE8_end_of_scanline(struct RLE *rle)
113 GP_DEBUG(4, "End of scanline at %u %u", rle->x, rle->y);
115 rle->x = 0;
116 rle->y++;
117 rle->move = 0;
119 if (rle->y >= rle->h) {
120 GP_DEBUG(4, "y >= h, stop");
121 rle->state = RLE_STOP;
124 return 0;
127 static int RLE8_end_of_bitmap(struct RLE *rle)
129 GP_DEBUG(4, "End of bitmap data");
131 rle->state = RLE_STOP;
133 return 0;
136 static int RLE8_repeat(uint8_t rep, struct RLE *rle)
138 GETC(rle);
140 GP_DEBUG(4, "RLE Repeat %i x 0x%02x", rep, rle->c);
142 rle->rep = rep;
143 rle->state = RLE_REPEAT;
145 return 0;
148 static int RLE8_offset(struct RLE *rle)
150 int x, y;
152 GETC(rle);
153 x = rle->c;
154 GETC(rle);
155 y = rle->c;
157 if (x == EOF || y == EOF)
158 return EIO;
160 GP_DEBUG(1, "RLE offset %i %i", x, y);
162 if (rle->x + (uint32_t)x >= rle->w || rle->y + (uint32_t)y >= rle->h) {
163 GP_DEBUG(1, "RLE offset out of image, stop");
164 rle->state = RLE_STOP;
167 rle->x += x;
168 rle->y += y;
170 return 0;
173 static int RLE8_next_undecoded(struct RLE *rle)
175 GETC(rle);
177 //GP_DEBUG(4, "RLE unencoded %u %u -> %02x", rle->x, rle->y, rle->c);
179 if (--rle->rep == 0) {
180 rle->state = RLE_START;
181 /* must be padded to odd number of bytes */
182 if (rle->flag)
183 GETC(rle);
186 RLE8_move(rle);
188 rle->move = 1;
190 return 0;
193 static int RLE8_next_repeat(struct RLE *rle)
195 //GP_DEBUG(4, "RLE repeat %u %u -> %02x", rle->x, rle->y, rle->c);
197 if (--rle->rep == 0)
198 rle->state = RLE_START;
200 RLE8_move(rle);
202 rle->move = 1;
204 return 0;
207 static int RLE8_esc(struct RLE *rle)
209 GETC(rle);
211 GP_DEBUG(4, "RLE ESC %02x", rle->c);
213 switch (rle->c) {
214 case 0x00:
215 return RLE8_end_of_scanline(rle);
216 case 0x01:
217 return RLE8_end_of_bitmap(rle);
218 case 0x02:
219 return RLE8_offset(rle);
220 /* Undecoded sequence */
221 default:
222 GP_DEBUG(4, "RLE Undecoded x %i", rle->c);
223 rle->state = RLE_UNDECODED;
224 rle->rep = rle->c;
225 rle->flag = rle->c % 2;
226 return 0;
230 static int RLE8_start(struct RLE *rle)
232 GETC(rle);
234 switch (rle->c) {
235 case 0x00:
236 return RLE8_esc(rle);
237 default:
238 return RLE8_repeat(rle->c, rle);
242 static int RLE8_next(struct RLE *rle)
244 int err;
246 for (;;) {
247 switch (rle->state) {
248 case RLE_START:
249 if ((err = RLE8_start(rle)))
250 return err;
251 break;
252 case RLE_REPEAT:
253 return RLE8_next_repeat(rle);
254 case RLE_UNDECODED:
255 return RLE8_next_undecoded(rle);
256 case RLE_STOP:
257 return 0;
258 default:
259 /* Shouldn't be reached */
260 GP_BUG("Invalid RLE state %u", rle->state);
261 return EINVAL;
266 static int read_RLE8(GP_IO *io, struct bitmap_info_header *header,
267 GP_Pixmap *pixmap, GP_ProgressCallback *callback)
269 uint32_t palette_size = get_palette_size(header);
270 DECLARE_RLE(rle, header->w, GP_ABS(header->h), io);
271 int err;
273 if (pixmap->pixel_type != GP_PIXEL_RGB888) {
274 GP_WARN("Corrupted BMP header! "
275 "RLE8 is 24bit (RGB888) palette but header says %s",
276 GP_PixelTypeName(pixmap->pixel_type));
277 return EINVAL;
280 GP_Pixel *palette = GP_TempAlloc(palette_size * sizeof(GP_Pixel));
282 if ((err = read_bitmap_palette(io, header, palette, palette_size)))
283 goto err;
285 if ((err = seek_pixels_offset(io, header)))
286 goto err;
288 int cnt = 0;
291 * Fill the image with first palette color.
293 * TODO: Untouched pixels should be treated as
294 * 1 bit transpanrency (in header3+)
296 GP_Fill(pixmap, palette[0]);
298 for (;;) {
299 if ((err = RLE8_next(&rle)))
300 goto err;
302 if (rle.state == RLE_STOP)
303 break;
305 GP_Pixel p;
307 uint8_t idx = rle.c;
309 if (idx >= palette_size) {
310 GP_DEBUG(1, "Index out of palette, ignoring");
311 p = 0;
312 } else {
313 p = palette[idx];
316 int32_t ry;
318 if (header->h < 0)
319 ry = rle.y;
320 else
321 ry = GP_ABS(header->h) - 1 - rle.y;
323 GP_PutPixel_Raw_24BPP(pixmap, rle.x, ry, p);
325 if (cnt++ > header->w) {
326 cnt = 0;
327 if (GP_ProgressCallbackReport(callback, rle.y,
328 pixmap->h, pixmap->w)) {
329 GP_DEBUG(1, "Operation aborted");
330 err = ECANCELED;
331 goto err;
336 GP_ProgressCallbackDone(callback);
337 err:
338 GP_TempFree(palette_size * sizeof(GP_Pixel), palette);
339 return err;