amiga_imgv
changeset 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 | 4c36d0f44aa6 |
children | 01bd8bbc46d4 |
files | src/image_ilbm.c |
diffstat | 1 files changed, 391 insertions(+), 0 deletions(-) [+] |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/image_ilbm.c Sun Oct 29 15:08:29 2017 +0200 1.3 @@ -0,0 +1,391 @@ 1.4 +#include <stdio.h> 1.5 +#include <stdlib.h> 1.6 +#include <string.h> 1.7 +#include <errno.h> 1.8 +#include <assert.h> 1.9 +#include "image.h" 1.10 +#include "logger.h" 1.11 +#include "endian.h" 1.12 + 1.13 +#ifdef __GNUC__ 1.14 +#define PACKED __attribute__((packed)) 1.15 +#else 1.16 +#define PACKED 1.17 +#endif 1.18 + 1.19 +#define MKID(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) 1.20 + 1.21 +#define IS_IFF_CONTAINER(id) ((id) == IFF_FORM || (id) == IFF_CAT || (id) == IFF_LIST) 1.22 + 1.23 +enum { 1.24 + IFF_FORM = MKID('F', 'O', 'R', 'M'), 1.25 + IFF_CAT = MKID('C', 'A', 'T', ' '), 1.26 + IFF_LIST = MKID('L', 'I', 'S', 'T'), 1.27 + IFF_ILBM = MKID('I', 'L', 'B', 'M'), 1.28 + IFF_PBM = MKID('P', 'B', 'M', ' '), 1.29 + IFF_BMHD = MKID('B', 'M', 'H', 'D'), 1.30 + IFF_CMAP = MKID('C', 'M', 'A', 'P'), 1.31 + IFF_BODY = MKID('B', 'O', 'D', 'Y'), 1.32 + IFF_CRNG = MKID('C', 'R', 'N', 'G') 1.33 +}; 1.34 + 1.35 + 1.36 +struct chdr { 1.37 + uint32_t id; 1.38 + uint32_t size; 1.39 +}; 1.40 + 1.41 +#ifdef __WATCOMC__ 1.42 +#pragma push(pack, 1) 1.43 +#endif 1.44 +struct bitmap_header { 1.45 + uint16_t width, height; 1.46 + int16_t xoffs, yoffs; 1.47 + uint8_t nplanes; 1.48 + uint8_t masking; 1.49 + uint8_t compression; 1.50 + uint8_t padding; 1.51 + uint16_t colorkey; 1.52 + uint8_t aspect_num, aspect_denom; 1.53 + int16_t pgwidth, pgheight; 1.54 +} PACKED; 1.55 +#ifdef __WATCOMC__ 1.56 +#pragma pop(pack) 1.57 +#endif 1.58 + 1.59 +enum { 1.60 + MASK_NONE, 1.61 + MASK_PLANE, 1.62 + MASK_COLORKEY, 1.63 + MASK_LASSO 1.64 +}; 1.65 + 1.66 +struct crng { 1.67 + uint16_t padding; 1.68 + uint16_t rate; 1.69 + uint16_t flags; 1.70 + uint8_t low, high; 1.71 +}; 1.72 + 1.73 +enum { 1.74 + CRNG_ENABLE = 1, 1.75 + CRNG_REVERSE = 2 1.76 +}; 1.77 + 1.78 +static int read_header(FILE *fp, struct chdr *hdr); 1.79 +static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct ham_image *img); 1.80 +static int read_bmhd(FILE *fp, struct bitmap_header *bmhd); 1.81 +static int read_crng(FILE *fp, struct crng *crng); 1.82 +static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct ham_image *img); 1.83 +static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct ham_image *img); 1.84 +static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width); 1.85 + 1.86 +int file_is_lbm(FILE *fp) 1.87 +{ 1.88 + uint32_t type; 1.89 + struct chdr hdr; 1.90 + 1.91 + while(read_header(fp, &hdr) != -1) { 1.92 + if(IS_IFF_CONTAINER(hdr.id)) { 1.93 + if(fread(&type, 4, 1, fp) < 1) { 1.94 + break; 1.95 + } 1.96 + type = ntohl(type); 1.97 + 1.98 + if(type == IFF_ILBM || type == IFF_PBM ) { 1.99 + rewind(fp); 1.100 + return 1; 1.101 + } 1.102 + hdr.size -= sizeof type; /* so we will seek fwd correctly */ 1.103 + } 1.104 + fseek(fp, hdr.size, SEEK_CUR); 1.105 + } 1.106 + fseek(fp, 0, SEEK_SET); 1.107 + return 0; 1.108 +} 1.109 + 1.110 +struct ham_image *load_ilbm(const char *fname) 1.111 +{ 1.112 + uint32_t type; 1.113 + struct chdr hdr; 1.114 + FILE *fp = 0; 1.115 + struct ham_image *img = 0; 1.116 + 1.117 + if(!(fp = fopen(fname, "rb"))) { 1.118 + logmsg("failed to open %s: %s\n", fname, strerror(errno)); 1.119 + return 0; 1.120 + } 1.121 + if(!(img = calloc(1, sizeof *img))) { 1.122 + logmsg("failed to allocate image structure\n"); 1.123 + goto err; 1.124 + } 1.125 + 1.126 + while(read_header(fp, &hdr) != -1) { 1.127 + if(IS_IFF_CONTAINER(hdr.id)) { 1.128 + if(fread(&type, 4, 1, fp) < 1) { 1.129 + break; 1.130 + } 1.131 + type = ntohl(type); 1.132 + hdr.size -= sizeof type; /* to compensate for having advanced 4 more bytes */ 1.133 + 1.134 + if(type == IFF_ILBM) { 1.135 + if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) { 1.136 + goto err; 1.137 + } 1.138 + return img; 1.139 + } 1.140 + if(type == IFF_PBM) { 1.141 + if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) { 1.142 + goto err; 1.143 + } 1.144 + return img; 1.145 + } 1.146 + } 1.147 + fseek(fp, hdr.size, SEEK_CUR); 1.148 + } 1.149 + 1.150 +err: 1.151 + fclose(fp); 1.152 + free(img); 1.153 + return 0; 1.154 +} 1.155 + 1.156 +static int read_header(FILE *fp, struct chdr *hdr) 1.157 +{ 1.158 + if(fread(hdr, 1, sizeof *hdr, fp) < sizeof *hdr) { 1.159 + return -1; 1.160 + } 1.161 + hdr->id = ntohl(hdr->id); 1.162 + hdr->size = ntohl(hdr->size); 1.163 + return 0; 1.164 +} 1.165 + 1.166 +static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct ham_image *img) 1.167 +{ 1.168 + int i, palsz, res = -1; 1.169 + struct chdr hdr; 1.170 + struct bitmap_header bmhd; 1.171 + struct crng crng; 1.172 + struct colrange *crnode; 1.173 + unsigned char pal[3 * 256]; 1.174 + unsigned char *pptr; 1.175 + long start = ftell(fp); 1.176 + 1.177 + while(read_header(fp, &hdr) != -1 && ftell(fp) - start < size) { 1.178 + switch(hdr.id) { 1.179 + case IFF_BMHD: 1.180 + assert(hdr.size == 20); 1.181 + if(read_bmhd(fp, &bmhd) == -1) { 1.182 + return -1; 1.183 + } 1.184 + img->width = bmhd.width; 1.185 + img->height = bmhd.height; 1.186 + if(bmhd.nplanes > 8) { 1.187 + logmsg("%d planes found, only paletized LBM files supported\n", (int)bmhd.nplanes); 1.188 + return -1; 1.189 + } 1.190 + img->nbitplanes = bmhd.nplanes; 1.191 + 1.192 + if(!(img->pixels = malloc(img->width * img->height / 8 * img->nbitplanes))) { 1.193 + logmsg("failed to allocate %dx%d (%d bitplane) image\n", img->width, img->height, img->nbitplanes); 1.194 + return -1; 1.195 + } 1.196 + break; 1.197 + 1.198 + case IFF_CMAP: 1.199 + assert(hdr.size / 3 <= 256); 1.200 + 1.201 + palsz = hdr.size / 3; 1.202 + 1.203 + if(fread(pal, 1, hdr.size, fp) < hdr.size) { 1.204 + logmsg("failed to read colormap\n"); 1.205 + return -1; 1.206 + } 1.207 + pptr = pal; 1.208 + for(i=0; i<palsz; i++) { 1.209 + uint16_t r = ((uint16_t)*pptr++ << 4) & 0xf00; 1.210 + uint16_t g = (uint16_t)*pptr++ & 0xf0; 1.211 + uint16_t b = ((uint16_t)*pptr++ >> 4) & 0xf; 1.212 + img->palette[i] = r | g | b; 1.213 + } 1.214 + break; 1.215 + 1.216 + /* 1.217 + case IFF_CRNG: 1.218 + assert(hdr.size == sizeof crng); 1.219 + 1.220 + if(read_crng(fp, &crng) == -1) { 1.221 + logmsg("failed to read color cycling range chunk\n"); 1.222 + return -1; 1.223 + } 1.224 + if(crng.low != crng.high && crng.rate > 0) { 1.225 + if(!(crnode = malloc(sizeof *crnode))) { 1.226 + logmsg("failed to allocate color range node\n"); 1.227 + return -1; 1.228 + } 1.229 + crnode->low = crng.low; 1.230 + crnode->high = crng.high; 1.231 + crnode->cmode = (crng.flags & CRNG_REVERSE) ? CYCLE_REVERSE : CYCLE_NORMAL; 1.232 + crnode->rate = crng.rate; 1.233 + crnode->next = img->range; 1.234 + img->range = crnode; 1.235 + ++img->num_ranges; 1.236 + } 1.237 + break; 1.238 + */ 1.239 + 1.240 + case IFF_BODY: 1.241 + if(!img->pixels) { 1.242 + logmsg("malformed ILBM image: encountered BODY chunk before BMHD\n"); 1.243 + return -1; 1.244 + } 1.245 + if(type == IFF_ILBM) { 1.246 + if(read_body_ilbm(fp, &bmhd, img) == -1) { 1.247 + logmsg("failed to read interleaved pixel data\n"); 1.248 + return -1; 1.249 + } 1.250 + } else { 1.251 + assert(type == IFF_PBM); 1.252 + if(read_body_pbm(fp, &bmhd, img) == -1) { 1.253 + logmsg("failed to read linear pixel data\n"); 1.254 + return -1; 1.255 + } 1.256 + } 1.257 + res = 0; /* sucessfully read image */ 1.258 + break; 1.259 + 1.260 + default: 1.261 + /* skip unknown chunks */ 1.262 + fseek(fp, hdr.size, SEEK_CUR); 1.263 + if(ftell(fp) & 1) { 1.264 + /* chunks must start at even offsets */ 1.265 + fseek(fp, 1, SEEK_CUR); 1.266 + } 1.267 + } 1.268 + } 1.269 + 1.270 + return res; 1.271 +} 1.272 + 1.273 + 1.274 +static int read_bmhd(FILE *fp, struct bitmap_header *bmhd) 1.275 +{ 1.276 + if(fread(bmhd, sizeof *bmhd, 1, fp) < 1) { 1.277 + return -1; 1.278 + } 1.279 + bmhd->width = ntohs(bmhd->width); 1.280 + bmhd->height = ntohs(bmhd->height); 1.281 + bmhd->xoffs = ntohs(bmhd->xoffs); 1.282 + bmhd->yoffs = ntohs(bmhd->yoffs); 1.283 + bmhd->colorkey = ntohs(bmhd->colorkey); 1.284 + bmhd->pgwidth = ntohs(bmhd->pgwidth); 1.285 + bmhd->pgheight = ntohs(bmhd->pgheight); 1.286 + return 0; 1.287 +} 1.288 + 1.289 +static int read_crng(FILE *fp, struct crng *crng) 1.290 +{ 1.291 + if(fread(crng, sizeof *crng, 1, fp) < 1) { 1.292 + return -1; 1.293 + } 1.294 + crng->rate = ntohs(crng->rate); 1.295 + crng->flags = ntohs(crng->flags); 1.296 + return 0; 1.297 +} 1.298 + 1.299 +/* scanline: [bp0 row][bp1 row]...[bpN-1 row][opt mask row] 1.300 + * each uncompressed row is width / 8 bytes 1.301 + */ 1.302 +static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct ham_image *img) 1.303 +{ 1.304 + int i, j; 1.305 + int rowsz = img->width / 8; 1.306 + unsigned char *dest = img->pixels; 1.307 + 1.308 + assert(bmhd->width == img->width); 1.309 + assert(bmhd->height == img->height); 1.310 + assert(img->pixels); 1.311 + 1.312 + for(i=0; i<img->height; i++) { 1.313 + 1.314 + for(j=0; j<bmhd->nplanes; j++) { 1.315 + /* read a row corresponding to bitplane j */ 1.316 + if(bmhd->compression) { 1.317 + if(read_compressed_scanline(fp, dest, rowsz) == -1) { 1.318 + return -1; 1.319 + } 1.320 + } else { 1.321 + if(fread(dest, 1, rowsz, fp) < rowsz) { 1.322 + return -1; 1.323 + } 1.324 + } 1.325 + dest += rowsz; 1.326 + } 1.327 + 1.328 + if(bmhd->masking & MASK_PLANE) { 1.329 + /* skip the mask (1bpp) */ 1.330 + fseek(fp, rowsz, SEEK_CUR); 1.331 + } 1.332 + } 1.333 + return 0; 1.334 +} 1.335 + 1.336 +static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct ham_image *img) 1.337 +{ 1.338 + /* 1.339 + int i; 1.340 + int npixels = img->width * img->height; 1.341 + unsigned char *dptr = img->pixels; 1.342 + 1.343 + assert(bmhd->width == img->width); 1.344 + assert(bmhd->height == img->height); 1.345 + assert(img->pixels); 1.346 + 1.347 + if(bmhd->compression) { 1.348 + for(i=0; i<img->height; i++) { 1.349 + if(read_compressed_scanline(fp, dptr, img->width) == -1) { 1.350 + return -1; 1.351 + } 1.352 + dptr += img->width; 1.353 + } 1.354 + 1.355 + } else { 1.356 + if(fread(img->pixels, 1, npixels, fp) < npixels) { 1.357 + return -1; 1.358 + } 1.359 + } 1.360 + return 0; 1.361 + */ 1.362 + return -1; /* TODO */ 1.363 +} 1.364 + 1.365 +static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width) 1.366 +{ 1.367 + int i, count, x = 0; 1.368 + signed char ctl; 1.369 + 1.370 + while(x < width) { 1.371 + if(fread(&ctl, 1, 1, fp) < 1) return -1; 1.372 + 1.373 + if(ctl == -128) continue; 1.374 + 1.375 + if(ctl >= 0) { 1.376 + count = ctl + 1; 1.377 + if(fread(scanline, 1, count, fp) < count) return -1; 1.378 + scanline += count; 1.379 + 1.380 + } else { 1.381 + unsigned char pixel; 1.382 + count = 1 - ctl; 1.383 + if(fread(&pixel, 1, 1, fp) < 1) return -1; 1.384 + 1.385 + for(i=0; i<count; i++) { 1.386 + *scanline++ = pixel; 1.387 + } 1.388 + } 1.389 + 1.390 + x += count; 1.391 + } 1.392 + 1.393 + return 0; 1.394 +}