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)
36 #define PACKED __attribute__((packed))
39 #define MKID(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
41 #define IS_IFF_CONTAINER(id) ((id) == IFF_FORM || (id) == IFF_CAT || (id) == IFF_LIST)
44 IFF_FORM = MKID('F', 'O', 'R', 'M'),
45 IFF_CAT = MKID('C', 'A', 'T', ' '),
46 IFF_LIST = MKID('L', 'I', 'S', 'T'),
47 IFF_ILBM = MKID('I', 'L', 'B', 'M'),
48 IFF_PBM = MKID('P', 'B', 'M', ' '),
49 IFF_BMHD = MKID('B', 'M', 'H', 'D'),
50 IFF_CMAP = MKID('C', 'M', 'A', 'P'),
51 IFF_BODY = MKID('B', 'O', 'D', 'Y'),
52 IFF_CRNG = MKID('C', 'R', 'N', 'G')
60 #if defined(__WATCOMC__) || defined(_MSC_VER)
63 struct bitmap_header {
64 uint16_t width, height;
71 uint8_t aspect_num, aspect_denom;
72 int16_t pgwidth, pgheight;
74 #if defined(__WATCOMC__) || defined(_MSC_VER)
98 static int check_file(struct img_io *io);
99 static int read_file(struct img_pixmap *img, struct img_io *io);
100 static int write_file(struct img_pixmap *img, struct img_io *io);
102 static int read_header(struct img_io *io, struct chdr *hdr);
103 static int read_ilbm_pbm(struct img_io *io, uint32_t type, uint32_t size, struct img_pixmap *img);
104 static int read_bmhd(struct img_io *io, struct bitmap_header *bmhd);
105 static int read_crng(struct img_io *io, struct crng *crng);
106 static int read_body_ilbm(struct img_io *io, struct bitmap_header *bmhd, struct img_pixmap *img);
107 static int read_body_pbm(struct img_io *io, struct bitmap_header *bmhd, struct img_pixmap *img);
108 static int read_compressed_scanline(struct img_io *io, unsigned char *scanline, int width);
110 #ifdef IMAGO_LITTLE_ENDIAN
111 static uint16_t img_swap16(uint16_t x);
112 static uint32_t img_swap32(uint32_t x);
116 int img_register_lbm(void)
118 static struct ftype_module mod = {".lbm:.ilbm:.iff", check_file, read_file, write_file };
119 return img_register_module(&mod);
122 static int check_file(struct img_io *io)
126 long pos = io->seek(0, SEEK_CUR, io->uptr);
128 while(read_header(io, &hdr) != -1) {
129 if(IS_IFF_CONTAINER(hdr.id)) {
130 type = img_read_uint32_be(io);
131 if(type == IFF_ILBM || type == IFF_PBM ) {
132 io->seek(pos, SEEK_SET, io->uptr);
135 hdr.size -= sizeof type; /* so we will seek fwd correctly */
137 io->seek(hdr.size, SEEK_CUR, io->uptr);
140 io->seek(pos, SEEK_SET, io->uptr);
144 static int read_file(struct img_pixmap *img, struct img_io *io)
149 while(read_header(io, &hdr) != -1) {
150 if(IS_IFF_CONTAINER(hdr.id)) {
151 type = img_read_uint32_be(io);
152 hdr.size -= sizeof type; /* to compensate for having advanced 4 more bytes */
154 if(type == IFF_ILBM) {
155 if(read_ilbm_pbm(io, type, hdr.size, img) == -1) {
160 if(type == IFF_PBM) {
161 if(read_ilbm_pbm(io, type, hdr.size, img) == -1) {
167 io->seek(hdr.size, SEEK_CUR, io->uptr);
172 static int write_file(struct img_pixmap *img, struct img_io *io)
174 return -1; /* TODO */
177 static int read_header(struct img_io *io, struct chdr *hdr)
179 if(io->read(hdr, sizeof *hdr, io->uptr) < sizeof *hdr) {
182 #ifdef IMAGO_LITTLE_ENDIAN
183 hdr->id = img_swap32(hdr->id);
184 hdr->size = img_swap32(hdr->size);
189 static int read_ilbm_pbm(struct img_io *io, uint32_t type, uint32_t size, struct img_pixmap *img)
193 struct bitmap_header bmhd;
195 /*struct colrange *crnode;*/
196 struct img_colormap cmap;
197 long start = io->seek(0, SEEK_CUR, io->uptr);
199 memset(img, 0, sizeof *img);
201 while(read_header(io, &hdr) != -1 && io->seek(0, SEEK_CUR, io->uptr) - start < (int)size) {
204 assert(hdr.size == 20);
205 if(read_bmhd(io, &bmhd) == -1) {
208 img->width = bmhd.width;
209 img->height = bmhd.height;
210 if(bmhd.nplanes > 8) {
212 fprintf(stderr, "libimago: %d planes found, only paletized LBM files supported\n", bmhd.nplanes);
215 if(img_set_pixels(img, img->width, img->height, IMG_FMT_IDX8, 0)) {
221 cmap.ncolors = hdr.size / 3;
222 assert(cmap.ncolors <= 256);
223 if(io->read(cmap.color, hdr.size, io->uptr) < hdr.size) {
229 assert(hdr.size == sizeof crng);
231 if(read_crng(io, &crng) == -1) {
234 if(crng.low != crng.high && crng.rate > 0) {
235 /* XXX color cycling not currently supported
236 if(!(crnode = malloc(sizeof *crnode))) {
239 crnode->low = crng.low;
240 crnode->high = crng.high;
241 crnode->cmode = (crng.flags & CRNG_REVERSE) ? CYCLE_REVERSE : CYCLE_NORMAL;
242 crnode->rate = crng.rate;
243 crnode->next = img->range;
252 fprintf(stderr, "libimago: malformed LBM image: encountered BODY chunk before BMHD\n");
255 if(type == IFF_ILBM) {
256 if(read_body_ilbm(io, &bmhd, img) == -1) {
260 assert(type == IFF_PBM);
261 if(read_body_pbm(io, &bmhd, img) == -1) {
266 *img_colormap(img) = cmap;
268 res = 0; /* sucessfully read image */
272 /* skip unknown chunks */
273 io->seek(hdr.size, SEEK_CUR, io->uptr);
274 if(io->seek(0, SEEK_CUR, io->uptr) & 1) {
275 /* chunks must start at even offsets */
276 io->seek(1, SEEK_CUR, io->uptr);
284 static int read_bmhd(struct img_io *io, struct bitmap_header *bmhd)
286 if(io->read(bmhd, sizeof *bmhd, io->uptr) < 1) {
289 #ifdef IMAGO_LITTLE_ENDIAN
290 bmhd->width = img_swap16(bmhd->width);
291 bmhd->height = img_swap16(bmhd->height);
292 bmhd->xoffs = img_swap16(bmhd->xoffs);
293 bmhd->yoffs = img_swap16(bmhd->yoffs);
294 bmhd->colorkey = img_swap16(bmhd->colorkey);
295 bmhd->pgwidth = img_swap16(bmhd->pgwidth);
296 bmhd->pgheight = img_swap16(bmhd->pgheight);
301 static int read_crng(struct img_io *io, struct crng *crng)
303 if(io->read(crng, sizeof *crng, io->uptr) < 1) {
306 #ifdef IMAGO_LITTLE_ENDIAN
307 crng->rate = img_swap16(crng->rate);
308 crng->flags = img_swap16(crng->flags);
313 /* scanline: [bp0 row][bp1 row]...[bpN-1 row][opt mask row]
314 * each uncompressed row is width / 8 bytes
316 static int read_body_ilbm(struct img_io *io, struct bitmap_header *bmhd, struct img_pixmap *img)
319 int rowsz = img->width / 8;
320 unsigned char *src, *dest = img->pixels;
321 unsigned char *rowbuf = alloca(rowsz);
323 assert(bmhd->width == img->width);
324 assert(bmhd->height == img->height);
327 for(i=0; i<img->height; i++) {
329 memset(dest, 0, img->width); /* clear the whole scanline to OR bits into place */
331 for(j=0; j<bmhd->nplanes; j++) {
332 /* read a row corresponding to bitplane j */
333 if(bmhd->compression) {
334 if(read_compressed_scanline(io, rowbuf, rowsz) == -1) {
338 if(io->read(rowbuf, rowsz, io->uptr) < rowsz) {
343 /* distribute all bits across the linear output scanline */
347 for(k=0; k<img->width; k++) {
348 dest[k] |= ((*src >> (7 - bitidx)) & 1) << j;
357 if(bmhd->masking & MASK_PLANE) {
358 /* skip the mask (1bpp) */
359 io->seek(rowsz, SEEK_CUR, io->uptr);
367 static int read_body_pbm(struct img_io *io, struct bitmap_header *bmhd, struct img_pixmap *img)
370 int npixels = img->width * img->height;
371 unsigned char *dptr = img->pixels;
373 assert(bmhd->width == img->width);
374 assert(bmhd->height == img->height);
377 if(bmhd->compression) {
378 for(i=0; i<img->height; i++) {
379 if(read_compressed_scanline(io, dptr, img->width) == -1) {
387 if(io->read(img->pixels, npixels, io->uptr) < npixels) {
395 static int read_compressed_scanline(struct img_io *io, unsigned char *scanline, int width)
401 if(io->read(&ctl, 1, io->uptr) < 1) return -1;
403 if(ctl == -128) continue;
407 if(io->read(scanline, count, io->uptr) < count) return -1;
413 if(io->read(&pixel, 1, io->uptr) < 1) return -1;
415 for(i=0; i<count; i++) {
426 #ifdef IMAGO_LITTLE_ENDIAN
427 static uint16_t img_swap16(uint16_t x)
429 return (x << 8) | (x >> 8);
432 static uint32_t img_swap32(uint32_t x)
434 return (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24);