amiga_imgv

view src/image_ilbm.c @ 8:a9edbbdb8213

forgot to add the ILBM image loader
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 29 Oct 2017 15:08:29 +0200
parents
children
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <assert.h>
6 #include "image.h"
7 #include "logger.h"
8 #include "endian.h"
10 #ifdef __GNUC__
11 #define PACKED __attribute__((packed))
12 #else
13 #define PACKED
14 #endif
16 #define MKID(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
18 #define IS_IFF_CONTAINER(id) ((id) == IFF_FORM || (id) == IFF_CAT || (id) == IFF_LIST)
20 enum {
21 IFF_FORM = MKID('F', 'O', 'R', 'M'),
22 IFF_CAT = MKID('C', 'A', 'T', ' '),
23 IFF_LIST = MKID('L', 'I', 'S', 'T'),
24 IFF_ILBM = MKID('I', 'L', 'B', 'M'),
25 IFF_PBM = MKID('P', 'B', 'M', ' '),
26 IFF_BMHD = MKID('B', 'M', 'H', 'D'),
27 IFF_CMAP = MKID('C', 'M', 'A', 'P'),
28 IFF_BODY = MKID('B', 'O', 'D', 'Y'),
29 IFF_CRNG = MKID('C', 'R', 'N', 'G')
30 };
33 struct chdr {
34 uint32_t id;
35 uint32_t size;
36 };
38 #ifdef __WATCOMC__
39 #pragma push(pack, 1)
40 #endif
41 struct bitmap_header {
42 uint16_t width, height;
43 int16_t xoffs, yoffs;
44 uint8_t nplanes;
45 uint8_t masking;
46 uint8_t compression;
47 uint8_t padding;
48 uint16_t colorkey;
49 uint8_t aspect_num, aspect_denom;
50 int16_t pgwidth, pgheight;
51 } PACKED;
52 #ifdef __WATCOMC__
53 #pragma pop(pack)
54 #endif
56 enum {
57 MASK_NONE,
58 MASK_PLANE,
59 MASK_COLORKEY,
60 MASK_LASSO
61 };
63 struct crng {
64 uint16_t padding;
65 uint16_t rate;
66 uint16_t flags;
67 uint8_t low, high;
68 };
70 enum {
71 CRNG_ENABLE = 1,
72 CRNG_REVERSE = 2
73 };
75 static int read_header(FILE *fp, struct chdr *hdr);
76 static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct ham_image *img);
77 static int read_bmhd(FILE *fp, struct bitmap_header *bmhd);
78 static int read_crng(FILE *fp, struct crng *crng);
79 static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct ham_image *img);
80 static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct ham_image *img);
81 static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width);
83 int file_is_lbm(FILE *fp)
84 {
85 uint32_t type;
86 struct chdr hdr;
88 while(read_header(fp, &hdr) != -1) {
89 if(IS_IFF_CONTAINER(hdr.id)) {
90 if(fread(&type, 4, 1, fp) < 1) {
91 break;
92 }
93 type = ntohl(type);
95 if(type == IFF_ILBM || type == IFF_PBM ) {
96 rewind(fp);
97 return 1;
98 }
99 hdr.size -= sizeof type; /* so we will seek fwd correctly */
100 }
101 fseek(fp, hdr.size, SEEK_CUR);
102 }
103 fseek(fp, 0, SEEK_SET);
104 return 0;
105 }
107 struct ham_image *load_ilbm(const char *fname)
108 {
109 uint32_t type;
110 struct chdr hdr;
111 FILE *fp = 0;
112 struct ham_image *img = 0;
114 if(!(fp = fopen(fname, "rb"))) {
115 logmsg("failed to open %s: %s\n", fname, strerror(errno));
116 return 0;
117 }
118 if(!(img = calloc(1, sizeof *img))) {
119 logmsg("failed to allocate image structure\n");
120 goto err;
121 }
123 while(read_header(fp, &hdr) != -1) {
124 if(IS_IFF_CONTAINER(hdr.id)) {
125 if(fread(&type, 4, 1, fp) < 1) {
126 break;
127 }
128 type = ntohl(type);
129 hdr.size -= sizeof type; /* to compensate for having advanced 4 more bytes */
131 if(type == IFF_ILBM) {
132 if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) {
133 goto err;
134 }
135 return img;
136 }
137 if(type == IFF_PBM) {
138 if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) {
139 goto err;
140 }
141 return img;
142 }
143 }
144 fseek(fp, hdr.size, SEEK_CUR);
145 }
147 err:
148 fclose(fp);
149 free(img);
150 return 0;
151 }
153 static int read_header(FILE *fp, struct chdr *hdr)
154 {
155 if(fread(hdr, 1, sizeof *hdr, fp) < sizeof *hdr) {
156 return -1;
157 }
158 hdr->id = ntohl(hdr->id);
159 hdr->size = ntohl(hdr->size);
160 return 0;
161 }
163 static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct ham_image *img)
164 {
165 int i, palsz, res = -1;
166 struct chdr hdr;
167 struct bitmap_header bmhd;
168 struct crng crng;
169 struct colrange *crnode;
170 unsigned char pal[3 * 256];
171 unsigned char *pptr;
172 long start = ftell(fp);
174 while(read_header(fp, &hdr) != -1 && ftell(fp) - start < size) {
175 switch(hdr.id) {
176 case IFF_BMHD:
177 assert(hdr.size == 20);
178 if(read_bmhd(fp, &bmhd) == -1) {
179 return -1;
180 }
181 img->width = bmhd.width;
182 img->height = bmhd.height;
183 if(bmhd.nplanes > 8) {
184 logmsg("%d planes found, only paletized LBM files supported\n", (int)bmhd.nplanes);
185 return -1;
186 }
187 img->nbitplanes = bmhd.nplanes;
189 if(!(img->pixels = malloc(img->width * img->height / 8 * img->nbitplanes))) {
190 logmsg("failed to allocate %dx%d (%d bitplane) image\n", img->width, img->height, img->nbitplanes);
191 return -1;
192 }
193 break;
195 case IFF_CMAP:
196 assert(hdr.size / 3 <= 256);
198 palsz = hdr.size / 3;
200 if(fread(pal, 1, hdr.size, fp) < hdr.size) {
201 logmsg("failed to read colormap\n");
202 return -1;
203 }
204 pptr = pal;
205 for(i=0; i<palsz; i++) {
206 uint16_t r = ((uint16_t)*pptr++ << 4) & 0xf00;
207 uint16_t g = (uint16_t)*pptr++ & 0xf0;
208 uint16_t b = ((uint16_t)*pptr++ >> 4) & 0xf;
209 img->palette[i] = r | g | b;
210 }
211 break;
213 /*
214 case IFF_CRNG:
215 assert(hdr.size == sizeof crng);
217 if(read_crng(fp, &crng) == -1) {
218 logmsg("failed to read color cycling range chunk\n");
219 return -1;
220 }
221 if(crng.low != crng.high && crng.rate > 0) {
222 if(!(crnode = malloc(sizeof *crnode))) {
223 logmsg("failed to allocate color range node\n");
224 return -1;
225 }
226 crnode->low = crng.low;
227 crnode->high = crng.high;
228 crnode->cmode = (crng.flags & CRNG_REVERSE) ? CYCLE_REVERSE : CYCLE_NORMAL;
229 crnode->rate = crng.rate;
230 crnode->next = img->range;
231 img->range = crnode;
232 ++img->num_ranges;
233 }
234 break;
235 */
237 case IFF_BODY:
238 if(!img->pixels) {
239 logmsg("malformed ILBM image: encountered BODY chunk before BMHD\n");
240 return -1;
241 }
242 if(type == IFF_ILBM) {
243 if(read_body_ilbm(fp, &bmhd, img) == -1) {
244 logmsg("failed to read interleaved pixel data\n");
245 return -1;
246 }
247 } else {
248 assert(type == IFF_PBM);
249 if(read_body_pbm(fp, &bmhd, img) == -1) {
250 logmsg("failed to read linear pixel data\n");
251 return -1;
252 }
253 }
254 res = 0; /* sucessfully read image */
255 break;
257 default:
258 /* skip unknown chunks */
259 fseek(fp, hdr.size, SEEK_CUR);
260 if(ftell(fp) & 1) {
261 /* chunks must start at even offsets */
262 fseek(fp, 1, SEEK_CUR);
263 }
264 }
265 }
267 return res;
268 }
271 static int read_bmhd(FILE *fp, struct bitmap_header *bmhd)
272 {
273 if(fread(bmhd, sizeof *bmhd, 1, fp) < 1) {
274 return -1;
275 }
276 bmhd->width = ntohs(bmhd->width);
277 bmhd->height = ntohs(bmhd->height);
278 bmhd->xoffs = ntohs(bmhd->xoffs);
279 bmhd->yoffs = ntohs(bmhd->yoffs);
280 bmhd->colorkey = ntohs(bmhd->colorkey);
281 bmhd->pgwidth = ntohs(bmhd->pgwidth);
282 bmhd->pgheight = ntohs(bmhd->pgheight);
283 return 0;
284 }
286 static int read_crng(FILE *fp, struct crng *crng)
287 {
288 if(fread(crng, sizeof *crng, 1, fp) < 1) {
289 return -1;
290 }
291 crng->rate = ntohs(crng->rate);
292 crng->flags = ntohs(crng->flags);
293 return 0;
294 }
296 /* scanline: [bp0 row][bp1 row]...[bpN-1 row][opt mask row]
297 * each uncompressed row is width / 8 bytes
298 */
299 static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct ham_image *img)
300 {
301 int i, j;
302 int rowsz = img->width / 8;
303 unsigned char *dest = img->pixels;
305 assert(bmhd->width == img->width);
306 assert(bmhd->height == img->height);
307 assert(img->pixels);
309 for(i=0; i<img->height; i++) {
311 for(j=0; j<bmhd->nplanes; j++) {
312 /* read a row corresponding to bitplane j */
313 if(bmhd->compression) {
314 if(read_compressed_scanline(fp, dest, rowsz) == -1) {
315 return -1;
316 }
317 } else {
318 if(fread(dest, 1, rowsz, fp) < rowsz) {
319 return -1;
320 }
321 }
322 dest += rowsz;
323 }
325 if(bmhd->masking & MASK_PLANE) {
326 /* skip the mask (1bpp) */
327 fseek(fp, rowsz, SEEK_CUR);
328 }
329 }
330 return 0;
331 }
333 static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct ham_image *img)
334 {
335 /*
336 int i;
337 int npixels = img->width * img->height;
338 unsigned char *dptr = img->pixels;
340 assert(bmhd->width == img->width);
341 assert(bmhd->height == img->height);
342 assert(img->pixels);
344 if(bmhd->compression) {
345 for(i=0; i<img->height; i++) {
346 if(read_compressed_scanline(fp, dptr, img->width) == -1) {
347 return -1;
348 }
349 dptr += img->width;
350 }
352 } else {
353 if(fread(img->pixels, 1, npixels, fp) < npixels) {
354 return -1;
355 }
356 }
357 return 0;
358 */
359 return -1; /* TODO */
360 }
362 static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width)
363 {
364 int i, count, x = 0;
365 signed char ctl;
367 while(x < width) {
368 if(fread(&ctl, 1, 1, fp) < 1) return -1;
370 if(ctl == -128) continue;
372 if(ctl >= 0) {
373 count = ctl + 1;
374 if(fread(scanline, 1, count, fp) < count) return -1;
375 scanline += count;
377 } else {
378 unsigned char pixel;
379 count = 1 - ctl;
380 if(fread(&pixel, 1, 1, fp) < 1) return -1;
382 for(i=0; i<count; i++) {
383 *scanline++ = pixel;
384 }
385 }
387 x += count;
388 }
390 return 0;
391 }