2 libimago - a multi-format image file input/output library.
3 Copyright (C) 2017 John Tsiombikas <nuclear@member.fsf.org>
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.
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.
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/>.
19 /* -- LBM (PNM/ILBM) module -- */
24 #if defined(__WATCOMC__) || defined(WIN32)
34 #define PACKED __attribute__((packed))
37 #define MKID(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
39 #define IS_IFF_CONTAINER(id) ((id) == IFF_FORM || (id) == IFF_CAT || (id) == IFF_LIST)
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')
58 #if defined(__WATCOMC__) || defined(_MSC_VER)
61 struct bitmap_header {
62 uint16_t width, height;
69 uint8_t aspect_num, aspect_denom;
70 int16_t pgwidth, pgheight;
72 #if defined(__WATCOMC__) || defined(_MSC_VER)
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);
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);
109 #ifdef IMAGO_LITTLE_ENDIAN
110 static uint16_t swap16(uint16_t x);
111 static uint32_t swap32(uint32_t x);
115 int img_register_lbm(void)
117 static struct ftype_module mod = {".lbm:.ilbm:.iff", check_file, read_file, write_file };
118 return img_register_module(&mod);
121 static int check_file(struct img_io *io)
125 long pos = io->seek(0, SEEK_CUR, io->uptr);
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);
134 hdr.size -= sizeof type; /* so we will seek fwd correctly */
136 io->seek(hdr.size, SEEK_CUR, io->uptr);
139 io->seek(pos, SEEK_SET, io->uptr);
143 static int read_file(struct img_pixmap *img, struct img_io *io)
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 */
153 if(type == IFF_ILBM) {
154 if(read_ilbm_pbm(io, type, hdr.size, img) == -1) {
159 if(type == IFF_PBM) {
160 if(read_ilbm_pbm(io, type, hdr.size, img) == -1) {
166 io->seek(hdr.size, SEEK_CUR, io->uptr);
171 static int write_file(struct img_pixmap *img, struct img_io *io)
173 return -1; /* TODO */
176 static int read_header(struct img_io *io, struct chdr *hdr)
178 if(io->read(hdr, sizeof *hdr, io->uptr) < sizeof *hdr) {
181 #ifdef IMAGO_LITTLE_ENDIAN
182 hdr->id = swap32(hdr->id);
183 hdr->size = swap32(hdr->size);
188 static int read_ilbm_pbm(struct img_io *io, uint32_t type, uint32_t size, struct img_pixmap *img)
192 struct bitmap_header bmhd;
194 /*struct colrange *crnode;*/
195 unsigned char pal[3 * 256];
196 long start = io->seek(0, SEEK_CUR, io->uptr);
198 memset(img, 0, sizeof *img);
200 while(read_header(io, &hdr) != -1 && io->seek(0, SEEK_CUR, io->uptr) - start < size) {
203 assert(hdr.size == 20);
204 if(read_bmhd(io, &bmhd) == -1) {
207 img->width = bmhd.width;
208 img->height = bmhd.height;
209 if(bmhd.nplanes > 8) {
211 fprintf(stderr, "libimago: %d planes found, only paletized LBM files supported\n", bmhd.nplanes);
214 if(img_set_pixels(img, img->width, img->height, IMG_FMT_RGB24, 0)) {
220 assert(hdr.size / 3 <= 256);
222 if(io->read(pal, hdr.size, io->uptr) < hdr.size) {
228 assert(hdr.size == sizeof crng);
230 if(read_crng(io, &crng) == -1) {
233 if(crng.low != crng.high && crng.rate > 0) {
234 /* XXX color cycling not currently supported
235 if(!(crnode = malloc(sizeof *crnode))) {
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;
251 fprintf(stderr, "libimago: malformed LBM image: encountered BODY chunk before BMHD\n");
254 if(type == IFF_ILBM) {
255 if(read_body_ilbm(io, &bmhd, img) == -1) {
259 assert(type == IFF_PBM);
260 if(read_body_pbm(io, &bmhd, img) == -1) {
265 convert_rgb(img, pal);
267 res = 0; /* sucessfully read image */
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);
283 static void convert_rgb(struct img_pixmap *img, unsigned char *pal)
285 int i, npixels = img->width * img->height;
286 unsigned char *sptr, *dptr = img->pixels;
288 dptr = (unsigned char*)img->pixels + npixels * 3;
289 sptr = (unsigned char*)img->pixels + npixels;
291 for(i=0; i<npixels; i++) {
293 *--dptr = pal[c * 3 + 2];
294 *--dptr = pal[c * 3 + 1];
295 *--dptr = pal[c * 3];
300 static int read_bmhd(struct img_io *io, struct bitmap_header *bmhd)
302 if(io->read(bmhd, sizeof *bmhd, io->uptr) < 1) {
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);
317 static int read_crng(struct img_io *io, struct crng *crng)
319 if(io->read(crng, sizeof *crng, io->uptr) < 1) {
322 #ifdef IMAGO_LITTLE_ENDIAN
323 crng->rate = swap16(crng->rate);
324 crng->flags = swap16(crng->flags);
329 /* scanline: [bp0 row][bp1 row]...[bpN-1 row][opt mask row]
330 * each uncompressed row is width / 8 bytes
332 static int read_body_ilbm(struct img_io *io, struct bitmap_header *bmhd, struct img_pixmap *img)
335 int rowsz = img->width / 8;
336 unsigned char *src, *dest = img->pixels;
337 unsigned char *rowbuf = alloca(rowsz);
339 assert(bmhd->width == img->width);
340 assert(bmhd->height == img->height);
343 for(i=0; i<img->height; i++) {
345 memset(dest, 0, img->width); /* clear the whole scanline to OR bits into place */
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) {
354 if(io->read(rowbuf, rowsz, io->uptr) < rowsz) {
359 /* distribute all bits across the linear output scanline */
363 for(k=0; k<img->width; k++) {
364 dest[k] |= ((*src >> (7 - bitidx)) & 1) << j;
373 if(bmhd->masking & MASK_PLANE) {
374 /* skip the mask (1bpp) */
375 io->seek(rowsz, SEEK_CUR, io->uptr);
383 static int read_body_pbm(struct img_io *io, struct bitmap_header *bmhd, struct img_pixmap *img)
386 int npixels = img->width * img->height;
387 unsigned char *dptr = img->pixels;
389 assert(bmhd->width == img->width);
390 assert(bmhd->height == img->height);
393 if(bmhd->compression) {
394 for(i=0; i<img->height; i++) {
395 if(read_compressed_scanline(io, dptr, img->width) == -1) {
403 if(io->read(img->pixels, npixels, io->uptr) < npixels) {
411 static int read_compressed_scanline(struct img_io *io, unsigned char *scanline, int width)
417 if(io->read(&ctl, 1, io->uptr) < 1) return -1;
419 if(ctl == -128) continue;
423 if(io->read(scanline, count, io->uptr) < count) return -1;
429 if(io->read(&pixel, 1, io->uptr) < 1) return -1;
431 for(i=0; i<count; i++) {
442 #ifdef IMAGO_LITTLE_ENDIAN
443 static uint16_t swap16(uint16_t x)
445 return (x << 8) | (x >> 8);
448 static uint32_t swap32(uint32_t x)
450 return (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24);