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