textures, overlay images, libimago
[demo_prior] / libs / imago2 / src / file_lbm.c
1 /*
2 libimago - a multi-format image file input/output library.
3 Copyright (C) 2017 John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published
7 by the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /* -- LBM (PNM/ILBM) module -- */
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <assert.h>
24 #if defined(__WATCOMC__) || defined(WIN32)
25 #include <malloc.h>
26 #else
27 #include <alloca.h>
28 #endif
29 #include "imago2.h"
30 #include "ftype_module.h"
31 #include "byteord.h"
32
33 #ifdef __GNUC__
34 #define PACKED  __attribute__((packed))
35 #endif
36
37 #define MKID(a, b, c, d)        (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
38
39 #define IS_IFF_CONTAINER(id)    ((id) == IFF_FORM || (id) == IFF_CAT || (id) == IFF_LIST)
40
41 enum {
42         IFF_FORM = MKID('F', 'O', 'R', 'M'),
43         IFF_CAT = MKID('C', 'A', 'T', ' '),
44         IFF_LIST = MKID('L', 'I', 'S', 'T'),
45         IFF_ILBM = MKID('I', 'L', 'B', 'M'),
46         IFF_PBM = MKID('P', 'B', 'M', ' '),
47         IFF_BMHD = MKID('B', 'M', 'H', 'D'),
48         IFF_CMAP = MKID('C', 'M', 'A', 'P'),
49         IFF_BODY = MKID('B', 'O', 'D', 'Y'),
50         IFF_CRNG = MKID('C', 'R', 'N', 'G')
51 };
52
53 struct chdr {
54         uint32_t id;
55         uint32_t size;
56 };
57
58 #if defined(__WATCOMC__) || defined(_MSC_VER)
59 #pragma push(pack, 1)
60 #endif
61 struct bitmap_header {
62         uint16_t width, height;
63         int16_t xoffs, yoffs;
64         uint8_t nplanes;
65         uint8_t masking;
66         uint8_t compression;
67         uint8_t padding;
68         uint16_t colorkey;
69         uint8_t aspect_num, aspect_denom;
70         int16_t pgwidth, pgheight;
71 } PACKED;
72 #if defined(__WATCOMC__) || defined(_MSC_VER)
73 #pragma pop(pack)
74 #endif
75
76 enum {
77         MASK_NONE,
78         MASK_PLANE,
79         MASK_COLORKEY,
80         MASK_LASSO
81 };
82
83 struct crng {
84         uint16_t padding;
85         uint16_t rate;
86         uint16_t flags;
87         uint8_t low, high;
88 };
89
90 enum {
91         CRNG_ENABLE = 1,
92         CRNG_REVERSE = 2
93 };
94
95
96 static int check_file(struct img_io *io);
97 static int read_file(struct img_pixmap *img, struct img_io *io);
98 static int write_file(struct img_pixmap *img, struct img_io *io);
99
100 static int read_header(struct img_io *io, struct chdr *hdr);
101 static int read_ilbm_pbm(struct img_io *io, uint32_t type, uint32_t size, struct img_pixmap *img);
102 static void convert_rgb(struct img_pixmap *img, unsigned char *pal);
103 static int read_bmhd(struct img_io *io, struct bitmap_header *bmhd);
104 static int read_crng(struct img_io *io, struct crng *crng);
105 static int read_body_ilbm(struct img_io *io, struct bitmap_header *bmhd, struct img_pixmap *img);
106 static int read_body_pbm(struct img_io *io, struct bitmap_header *bmhd, struct img_pixmap *img);
107 static int read_compressed_scanline(struct img_io *io, unsigned char *scanline, int width);
108
109 #ifdef IMAGO_LITTLE_ENDIAN
110 static uint16_t swap16(uint16_t x);
111 static uint32_t swap32(uint32_t x);
112 #endif
113
114
115 int img_register_lbm(void)
116 {
117         static struct ftype_module mod = {".lbm:.ilbm:.iff", check_file, read_file, write_file };
118         return img_register_module(&mod);
119 }
120
121 static int check_file(struct img_io *io)
122 {
123         uint32_t type;
124         struct chdr hdr;
125         long pos = io->seek(0, SEEK_CUR, io->uptr);
126
127         while(read_header(io, &hdr) != -1) {
128                 if(IS_IFF_CONTAINER(hdr.id)) {
129                         type = img_read_uint32_be(io);
130                         if(type == IFF_ILBM || type == IFF_PBM ) {
131                                 io->seek(pos, SEEK_SET, io->uptr);
132                                 return 0;
133                         }
134                         hdr.size -= sizeof type;        /* so we will seek fwd correctly */
135                 }
136                 io->seek(hdr.size, SEEK_CUR, io->uptr);
137         }
138
139         io->seek(pos, SEEK_SET, io->uptr);
140         return -1;
141 }
142
143 static int read_file(struct img_pixmap *img, struct img_io *io)
144 {
145         uint32_t type;
146         struct chdr hdr;
147
148         while(read_header(io, &hdr) != -1) {
149                 if(IS_IFF_CONTAINER(hdr.id)) {
150                         type = img_read_uint32_be(io);
151                         hdr.size -= sizeof type;        /* to compensate for having advanced 4 more bytes */
152
153                         if(type == IFF_ILBM) {
154                                 if(read_ilbm_pbm(io, type, hdr.size, img) == -1) {
155                                         return -1;
156                                 }
157                                 return 0;
158                         }
159                         if(type == IFF_PBM) {
160                                 if(read_ilbm_pbm(io, type, hdr.size, img) == -1) {
161                                         return -1;
162                                 }
163                                 return 0;
164                         }
165                 }
166                 io->seek(hdr.size, SEEK_CUR, io->uptr);
167         }
168         return 0;
169 }
170
171 static int write_file(struct img_pixmap *img, struct img_io *io)
172 {
173         return -1;      /* TODO */
174 }
175
176 static int read_header(struct img_io *io, struct chdr *hdr)
177 {
178         if(io->read(hdr, sizeof *hdr, io->uptr) < sizeof *hdr) {
179                 return -1;
180         }
181 #ifdef IMAGO_LITTLE_ENDIAN
182         hdr->id = swap32(hdr->id);
183         hdr->size = swap32(hdr->size);
184 #endif
185         return 0;
186 }
187
188 static int read_ilbm_pbm(struct img_io *io, uint32_t type, uint32_t size, struct img_pixmap *img)
189 {
190         int res = -1;
191         struct chdr hdr;
192         struct bitmap_header bmhd;
193         struct crng crng;
194         /*struct colrange *crnode;*/
195         unsigned char pal[3 * 256];
196         long start = io->seek(0, SEEK_CUR, io->uptr);
197
198         memset(img, 0, sizeof *img);
199
200         while(read_header(io, &hdr) != -1 && io->seek(0, SEEK_CUR, io->uptr) - start < size) {
201                 switch(hdr.id) {
202                 case IFF_BMHD:
203                         assert(hdr.size == 20);
204                         if(read_bmhd(io, &bmhd) == -1) {
205                                 return -1;
206                         }
207                         img->width = bmhd.width;
208                         img->height = bmhd.height;
209                         if(bmhd.nplanes > 8) {
210                                 /* TODO */
211                                 fprintf(stderr, "libimago: %d planes found, only paletized LBM files supported\n", bmhd.nplanes);
212                                 return -1;
213                         }
214                         if(img_set_pixels(img, img->width, img->height, IMG_FMT_RGB24, 0)) {
215                                 return -1;
216                         }
217                         break;
218
219                 case IFF_CMAP:
220                         assert(hdr.size / 3 <= 256);
221
222                         if(io->read(pal, hdr.size, io->uptr) < hdr.size) {
223                                 return -1;
224                         }
225                         break;
226
227                 case IFF_CRNG:
228                         assert(hdr.size == sizeof crng);
229
230                         if(read_crng(io, &crng) == -1) {
231                                 return -1;
232                         }
233                         if(crng.low != crng.high && crng.rate > 0) {
234                                 /* XXX color cycling not currently supported
235                                 if(!(crnode = malloc(sizeof *crnode))) {
236                                         return -1;
237                                 }
238                                 crnode->low = crng.low;
239                                 crnode->high = crng.high;
240                                 crnode->cmode = (crng.flags & CRNG_REVERSE) ? CYCLE_REVERSE : CYCLE_NORMAL;
241                                 crnode->rate = crng.rate;
242                                 crnode->next = img->range;
243                                 img->range = crnode;
244                                 ++img->num_ranges;
245                                 */
246                         }
247                         break;
248
249                 case IFF_BODY:
250                         if(!img->pixels) {
251                                 fprintf(stderr, "libimago: malformed LBM image: encountered BODY chunk before BMHD\n");
252                                 return -1;
253                         }
254                         if(type == IFF_ILBM) {
255                                 if(read_body_ilbm(io, &bmhd, img) == -1) {
256                                         return -1;
257                                 }
258                         } else {
259                                 assert(type == IFF_PBM);
260                                 if(read_body_pbm(io, &bmhd, img) == -1) {
261                                         return -1;
262                                 }
263                         }
264
265                         convert_rgb(img, pal);
266
267                         res = 0;        /* sucessfully read image */
268                         break;
269
270                 default:
271                         /* skip unknown chunks */
272                         io->seek(hdr.size, SEEK_CUR, io->uptr);
273                         if(io->seek(0, SEEK_CUR, io->uptr) & 1) {
274                                 /* chunks must start at even offsets */
275                                 io->seek(1, SEEK_CUR, io->uptr);
276                         }
277                 }
278         }
279
280         return res;
281 }
282
283 static void convert_rgb(struct img_pixmap *img, unsigned char *pal)
284 {
285         int i, npixels = img->width * img->height;
286         unsigned char *sptr, *dptr = img->pixels;
287
288         dptr = (unsigned char*)img->pixels + npixels * 3;
289         sptr = (unsigned char*)img->pixels + npixels;
290
291         for(i=0; i<npixels; i++) {
292                 int c = *--sptr;
293                 *--dptr = pal[c * 3 + 2];
294                 *--dptr = pal[c * 3 + 1];
295                 *--dptr = pal[c * 3];
296         }
297 }
298
299
300 static int read_bmhd(struct img_io *io, struct bitmap_header *bmhd)
301 {
302         if(io->read(bmhd, sizeof *bmhd, io->uptr) < 1) {
303                 return -1;
304         }
305 #ifdef IMAGO_LITTLE_ENDIAN
306         bmhd->width = swap16(bmhd->width);
307         bmhd->height = swap16(bmhd->height);
308         bmhd->xoffs = swap16(bmhd->xoffs);
309         bmhd->yoffs = swap16(bmhd->yoffs);
310         bmhd->colorkey = swap16(bmhd->colorkey);
311         bmhd->pgwidth = swap16(bmhd->pgwidth);
312         bmhd->pgheight = swap16(bmhd->pgheight);
313 #endif
314         return 0;
315 }
316
317 static int read_crng(struct img_io *io, struct crng *crng)
318 {
319         if(io->read(crng, sizeof *crng, io->uptr) < 1) {
320                 return -1;
321         }
322 #ifdef IMAGO_LITTLE_ENDIAN
323         crng->rate = swap16(crng->rate);
324         crng->flags = swap16(crng->flags);
325 #endif
326         return 0;
327 }
328
329 /* scanline: [bp0 row][bp1 row]...[bpN-1 row][opt mask row]
330  * each uncompressed row is width / 8 bytes
331  */
332 static int read_body_ilbm(struct img_io *io, struct bitmap_header *bmhd, struct img_pixmap *img)
333 {
334         int i, j, k, bitidx;
335         int rowsz = img->width / 8;
336         unsigned char *src, *dest = img->pixels;
337         unsigned char *rowbuf = alloca(rowsz);
338
339         assert(bmhd->width == img->width);
340         assert(bmhd->height == img->height);
341         assert(img->pixels);
342
343         for(i=0; i<img->height; i++) {
344
345                 memset(dest, 0, img->width);    /* clear the whole scanline to OR bits into place */
346
347                 for(j=0; j<bmhd->nplanes; j++) {
348                         /* read a row corresponding to bitplane j */
349                         if(bmhd->compression) {
350                                 if(read_compressed_scanline(io, rowbuf, rowsz) == -1) {
351                                         return -1;
352                                 }
353                         } else {
354                                 if(io->read(rowbuf, rowsz, io->uptr) < rowsz) {
355                                         return -1;
356                                 }
357                         }
358
359                         /* distribute all bits across the linear output scanline */
360                         src = rowbuf;
361                         bitidx = 0;
362
363                         for(k=0; k<img->width; k++) {
364                                 dest[k] |= ((*src >> (7 - bitidx)) & 1) << j;
365
366                                 if(++bitidx >= 8) {
367                                         bitidx = 0;
368                                         ++src;
369                                 }
370                         }
371                 }
372
373                 if(bmhd->masking & MASK_PLANE) {
374                         /* skip the mask (1bpp) */
375                         io->seek(rowsz, SEEK_CUR, io->uptr);
376                 }
377
378                 dest += img->width;
379         }
380         return 0;
381 }
382
383 static int read_body_pbm(struct img_io *io, struct bitmap_header *bmhd, struct img_pixmap *img)
384 {
385         int i;
386         int npixels = img->width * img->height;
387         unsigned char *dptr = img->pixels;
388
389         assert(bmhd->width == img->width);
390         assert(bmhd->height == img->height);
391         assert(img->pixels);
392
393         if(bmhd->compression) {
394                 for(i=0; i<img->height; i++) {
395                         if(read_compressed_scanline(io, dptr, img->width) == -1) {
396                                 return -1;
397                         }
398                         dptr += img->width;
399                 }
400
401         } else {
402                 /* uncompressed */
403                 if(io->read(img->pixels, npixels, io->uptr) < npixels) {
404                         return -1;
405                 }
406         }
407
408         return 0;
409 }
410
411 static int read_compressed_scanline(struct img_io *io, unsigned char *scanline, int width)
412 {
413         int i, count, x = 0;
414         signed char ctl;
415
416         while(x < width) {
417                 if(io->read(&ctl, 1, io->uptr) < 1) return -1;
418
419                 if(ctl == -128) continue;
420
421                 if(ctl >= 0) {
422                         count = ctl + 1;
423                         if(io->read(scanline, count, io->uptr) < count) return -1;
424                         scanline += count;
425
426                 } else {
427                         unsigned char pixel;
428                         count = 1 - ctl;
429                         if(io->read(&pixel, 1, io->uptr) < 1) return -1;
430
431                         for(i=0; i<count; i++) {
432                                 *scanline++ = pixel;
433                         }
434                 }
435
436                 x += count;
437         }
438
439         return 0;
440 }
441
442 #ifdef IMAGO_LITTLE_ENDIAN
443 static uint16_t swap16(uint16_t x)
444 {
445         return (x << 8) | (x >> 8);
446 }
447
448 static uint32_t swap32(uint32_t x)
449 {
450         return (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24);
451 }
452 #endif