amiga_imgv

annotate 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
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 }