lbm2bin
diff src/imagelbm.c @ 0:bfd8aa2ca393
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 15 Jul 2017 13:26:32 +0300 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/imagelbm.c Sat Jul 15 13:26:32 2017 +0300 1.3 @@ -0,0 +1,463 @@ 1.4 +/* 1.5 +colcycle - color cycling image viewer 1.6 +Copyright (C) 2016-2017 John Tsiombikas <nuclear@member.fsf.org> 1.7 + 1.8 +This program is free software: you can redistribute it and/or modify 1.9 +it under the terms of the GNU General Public License as published by 1.10 +the Free Software Foundation, either version 3 of the License, or 1.11 +(at your option) any later version. 1.12 + 1.13 +This program is distributed in the hope that it will be useful, 1.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 1.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1.16 +GNU General Public License for more details. 1.17 + 1.18 +You should have received a copy of the GNU General Public License 1.19 +along with this program. If not, see <http://www.gnu.org/licenses/>. 1.20 +*/ 1.21 +#include <stdio.h> 1.22 +#include <stdlib.h> 1.23 +#include <assert.h> 1.24 +#include <inttypes.h> 1.25 +#include <string.h> 1.26 +#if defined(__WATCOMC__) || defined(_MSC_VER) 1.27 +#include <malloc.h> 1.28 +#else 1.29 +#include <alloca.h> 1.30 +#endif 1.31 +#include "imagelbm.h" 1.32 + 1.33 +#ifdef __GNUC__ 1.34 +#define PACKED __attribute__((packed)) 1.35 +#endif 1.36 + 1.37 +#ifdef __BYTE_ORDER__ 1.38 +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 1.39 +#define LENDIAN 1.40 +#else 1.41 +#define BENDIAN 1.42 +#endif 1.43 +#endif 1.44 + 1.45 +#define MKID(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) 1.46 + 1.47 +#define IS_IFF_CONTAINER(id) ((id) == IFF_FORM || (id) == IFF_CAT || (id) == IFF_LIST) 1.48 + 1.49 +enum { 1.50 + IFF_FORM = MKID('F', 'O', 'R', 'M'), 1.51 + IFF_CAT = MKID('C', 'A', 'T', ' '), 1.52 + IFF_LIST = MKID('L', 'I', 'S', 'T'), 1.53 + IFF_ILBM = MKID('I', 'L', 'B', 'M'), 1.54 + IFF_PBM = MKID('P', 'B', 'M', ' '), 1.55 + IFF_BMHD = MKID('B', 'M', 'H', 'D'), 1.56 + IFF_CMAP = MKID('C', 'M', 'A', 'P'), 1.57 + IFF_BODY = MKID('B', 'O', 'D', 'Y'), 1.58 + IFF_CRNG = MKID('C', 'R', 'N', 'G') 1.59 +}; 1.60 + 1.61 + 1.62 +struct chdr { 1.63 + uint32_t id; 1.64 + uint32_t size; 1.65 +}; 1.66 + 1.67 +#ifdef __WATCOMC__ 1.68 +#pragma push(pack, 1) 1.69 +#endif 1.70 +struct bitmap_header { 1.71 + uint16_t width, height; 1.72 + int16_t xoffs, yoffs; 1.73 + uint8_t nplanes; 1.74 + uint8_t masking; 1.75 + uint8_t compression; 1.76 + uint8_t padding; 1.77 + uint16_t colorkey; 1.78 + uint8_t aspect_num, aspect_denom; 1.79 + int16_t pgwidth, pgheight; 1.80 +} PACKED; 1.81 +#ifdef __WATCOMC__ 1.82 +#pragma pop(pack) 1.83 +#endif 1.84 + 1.85 +enum { 1.86 + MASK_NONE, 1.87 + MASK_PLANE, 1.88 + MASK_COLORKEY, 1.89 + MASK_LASSO 1.90 +}; 1.91 + 1.92 +struct crng { 1.93 + uint16_t padding; 1.94 + uint16_t rate; 1.95 + uint16_t flags; 1.96 + uint8_t low, high; 1.97 +}; 1.98 + 1.99 +enum { 1.100 + CRNG_ENABLE = 1, 1.101 + CRNG_REVERSE = 2 1.102 +}; 1.103 + 1.104 +static int read_header(FILE *fp, struct chdr *hdr); 1.105 +static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct image *img); 1.106 +static int read_bmhd(FILE *fp, struct bitmap_header *bmhd); 1.107 +static int read_crng(FILE *fp, struct crng *crng); 1.108 +static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct image *img); 1.109 +static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct image *img); 1.110 +static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width); 1.111 +static int read16(FILE *fp, uint16_t *res); 1.112 +static int read32(FILE *fp, uint32_t *res); 1.113 +static uint16_t swap16(uint16_t x); 1.114 +static uint32_t swap32(uint32_t x); 1.115 + 1.116 +int file_is_lbm(FILE *fp) 1.117 +{ 1.118 + uint32_t type; 1.119 + struct chdr hdr; 1.120 + 1.121 + while(read_header(fp, &hdr) != -1) { 1.122 + if(IS_IFF_CONTAINER(hdr.id)) { 1.123 + if(read32(fp, &type) == -1) { 1.124 + break; 1.125 + } 1.126 + 1.127 + if(type == IFF_ILBM || type == IFF_PBM ) { 1.128 + rewind(fp); 1.129 + return 1; 1.130 + } 1.131 + hdr.size -= sizeof type; /* so we will seek fwd correctly */ 1.132 + } 1.133 + fseek(fp, hdr.size, SEEK_CUR); 1.134 + } 1.135 + fseek(fp, 0, SEEK_SET); 1.136 + return 0; 1.137 +} 1.138 + 1.139 +void print_chunkid(uint32_t id) 1.140 +{ 1.141 + char str[5] = {0}; 1.142 +#ifdef LENDIAN 1.143 + id = swap32(id); 1.144 +#endif 1.145 + memcpy(str, &id, 4); 1.146 + puts(str); 1.147 +} 1.148 + 1.149 +int load_image_lbm(struct image *img, FILE *fp) 1.150 +{ 1.151 + uint32_t type; 1.152 + struct chdr hdr; 1.153 + 1.154 + while(read_header(fp, &hdr) != -1) { 1.155 + if(IS_IFF_CONTAINER(hdr.id)) { 1.156 + if(read32(fp, &type) == -1) { 1.157 + break; 1.158 + } 1.159 + hdr.size -= sizeof type; /* to compensate for having advanced 4 more bytes */ 1.160 + 1.161 + if(type == IFF_ILBM) { 1.162 + if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) { 1.163 + return -1; 1.164 + } 1.165 + return 0; 1.166 + } 1.167 + if(type == IFF_PBM) { 1.168 + if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) { 1.169 + return -1; 1.170 + } 1.171 + return 0; 1.172 + } 1.173 + } 1.174 + fseek(fp, hdr.size, SEEK_CUR); 1.175 + } 1.176 + return 0; 1.177 +} 1.178 + 1.179 +static int read_header(FILE *fp, struct chdr *hdr) 1.180 +{ 1.181 + if(fread(hdr, 1, sizeof *hdr, fp) < sizeof *hdr) { 1.182 + return -1; 1.183 + } 1.184 +#ifdef LENDIAN 1.185 + hdr->id = swap32(hdr->id); 1.186 + hdr->size = swap32(hdr->size); 1.187 +#endif 1.188 + return 0; 1.189 +} 1.190 + 1.191 +static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct image *img) 1.192 +{ 1.193 + int i, res = -1; 1.194 + struct chdr hdr; 1.195 + struct bitmap_header bmhd; 1.196 + struct crng crng; 1.197 + struct colrange *crnode; 1.198 + unsigned char pal[3 * 256]; 1.199 + unsigned char *pptr; 1.200 + long start = ftell(fp); 1.201 + 1.202 + memset(img, 0, sizeof *img); 1.203 + 1.204 + while(read_header(fp, &hdr) != -1 && ftell(fp) - start < size) { 1.205 + switch(hdr.id) { 1.206 + case IFF_BMHD: 1.207 + assert(hdr.size == 20); 1.208 + if(read_bmhd(fp, &bmhd) == -1) { 1.209 + return -1; 1.210 + } 1.211 + img->width = bmhd.width; 1.212 + img->height = bmhd.height; 1.213 + img->bpp = bmhd.nplanes; 1.214 + if(bmhd.nplanes > 8) { 1.215 + fprintf(stderr, "%d planes found, only paletized LBM files supported\n", bmhd.nplanes); 1.216 + return -1; 1.217 + } 1.218 + if(!(img->pixels = malloc(img->width * img->height))) { 1.219 + fprintf(stderr, "failed to allocate %dx%d image\n", img->width, img->height); 1.220 + return -1; 1.221 + } 1.222 + break; 1.223 + 1.224 + case IFF_CMAP: 1.225 + assert(hdr.size / 3 <= 256); 1.226 + 1.227 + if(fread(pal, 1, hdr.size, fp) < hdr.size) { 1.228 + fprintf(stderr, "failed to read colormap\n"); 1.229 + return -1; 1.230 + } 1.231 + pptr = pal; 1.232 + for(i=0; i<256; i++) { 1.233 + img->palette[i].r = *pptr++; 1.234 + img->palette[i].g = *pptr++; 1.235 + img->palette[i].b = *pptr++; 1.236 + } 1.237 + break; 1.238 + 1.239 + case IFF_CRNG: 1.240 + assert(hdr.size == sizeof crng); 1.241 + 1.242 + if(read_crng(fp, &crng) == -1) { 1.243 + fprintf(stderr, "failed to read color cycling range chunk\n"); 1.244 + return -1; 1.245 + } 1.246 + if(crng.low != crng.high && crng.rate > 0) { 1.247 + if(!(crnode = malloc(sizeof *crnode))) { 1.248 + fprintf(stderr, "failed to allocate color range node\n"); 1.249 + return -1; 1.250 + } 1.251 + crnode->low = crng.low; 1.252 + crnode->high = crng.high; 1.253 + crnode->cmode = (crng.flags & CRNG_REVERSE) ? CYCLE_REVERSE : CYCLE_NORMAL; 1.254 + crnode->rate = crng.rate; 1.255 + crnode->next = img->range; 1.256 + img->range = crnode; 1.257 + ++img->num_ranges; 1.258 + } 1.259 + break; 1.260 + 1.261 + case IFF_BODY: 1.262 + if(!img->pixels) { 1.263 + fprintf(stderr, "malformed ILBM image: encountered BODY chunk before BMHD\n"); 1.264 + return -1; 1.265 + } 1.266 + if(type == IFF_ILBM) { 1.267 + if(read_body_ilbm(fp, &bmhd, img) == -1) { 1.268 + fprintf(stderr, "failed to read interleaved pixel data\n"); 1.269 + return -1; 1.270 + } 1.271 + } else { 1.272 + assert(type == IFF_PBM); 1.273 + if(read_body_pbm(fp, &bmhd, img) == -1) { 1.274 + fprintf(stderr, "failed to read linear pixel data\n"); 1.275 + return -1; 1.276 + } 1.277 + } 1.278 + res = 0; /* sucessfully read image */ 1.279 + break; 1.280 + 1.281 + default: 1.282 + /* skip unknown chunks */ 1.283 + fseek(fp, hdr.size, SEEK_CUR); 1.284 + if(ftell(fp) & 1) { 1.285 + /* chunks must start at even offsets */ 1.286 + fseek(fp, 1, SEEK_CUR); 1.287 + } 1.288 + } 1.289 + } 1.290 + 1.291 + return res; 1.292 +} 1.293 + 1.294 + 1.295 +static int read_bmhd(FILE *fp, struct bitmap_header *bmhd) 1.296 +{ 1.297 + if(fread(bmhd, sizeof *bmhd, 1, fp) < 1) { 1.298 + return -1; 1.299 + } 1.300 +#ifdef LENDIAN 1.301 + bmhd->width = swap16(bmhd->width); 1.302 + bmhd->height = swap16(bmhd->height); 1.303 + bmhd->xoffs = swap16(bmhd->xoffs); 1.304 + bmhd->yoffs = swap16(bmhd->yoffs); 1.305 + bmhd->colorkey = swap16(bmhd->colorkey); 1.306 + bmhd->pgwidth = swap16(bmhd->pgwidth); 1.307 + bmhd->pgheight = swap16(bmhd->pgheight); 1.308 +#endif 1.309 + return 0; 1.310 +} 1.311 + 1.312 +static int read_crng(FILE *fp, struct crng *crng) 1.313 +{ 1.314 + if(fread(crng, sizeof *crng, 1, fp) < 1) { 1.315 + return -1; 1.316 + } 1.317 +#ifdef LENDIAN 1.318 + crng->rate = swap16(crng->rate); 1.319 + crng->flags = swap16(crng->flags); 1.320 +#endif 1.321 + return 0; 1.322 +} 1.323 + 1.324 +/* scanline: [bp0 row][bp1 row]...[bpN-1 row][opt mask row] 1.325 + * each uncompressed row is width / 8 bytes 1.326 + */ 1.327 +static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct image *img) 1.328 +{ 1.329 + int i, j, k, bitidx; 1.330 + int rowsz = img->width / 8; 1.331 + unsigned char *src, *dest = img->pixels; 1.332 + unsigned char *rowbuf = alloca(rowsz); 1.333 + 1.334 + assert(bmhd->width == img->width); 1.335 + assert(bmhd->height == img->height); 1.336 + assert(img->pixels); 1.337 + 1.338 + for(i=0; i<img->height; i++) { 1.339 + 1.340 + memset(dest, 0, img->width); /* clear the whole scanline to OR bits into place */ 1.341 + 1.342 + for(j=0; j<bmhd->nplanes; j++) { 1.343 + // read a row corresponding to bitplane j 1.344 + if(bmhd->compression) { 1.345 + if(read_compressed_scanline(fp, rowbuf, rowsz) == -1) { 1.346 + return -1; 1.347 + } 1.348 + } else { 1.349 + if(fread(rowbuf, 1, rowsz, fp) < rowsz) { 1.350 + return -1; 1.351 + } 1.352 + } 1.353 + 1.354 + // distribute all bits across the linear output scanline 1.355 + src = rowbuf; 1.356 + bitidx = 0; 1.357 + 1.358 + for(k=0; k<img->width; k++) { 1.359 + dest[k] |= ((*src >> (7 - bitidx)) & 1) << j; 1.360 + 1.361 + if(++bitidx >= 8) { 1.362 + bitidx = 0; 1.363 + ++src; 1.364 + } 1.365 + } 1.366 + } 1.367 + 1.368 + if(bmhd->masking & MASK_PLANE) { 1.369 + /* skip the mask (1bpp) */ 1.370 + fseek(fp, rowsz, SEEK_CUR); 1.371 + } 1.372 + 1.373 + dest += img->width; 1.374 + } 1.375 + return 0; 1.376 +} 1.377 + 1.378 +static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct image *img) 1.379 +{ 1.380 + int i; 1.381 + int npixels = img->width * img->height; 1.382 + unsigned char *dptr = img->pixels; 1.383 + 1.384 + assert(bmhd->width == img->width); 1.385 + assert(bmhd->height == img->height); 1.386 + assert(img->pixels); 1.387 + 1.388 + if(bmhd->compression) { 1.389 + for(i=0; i<img->height; i++) { 1.390 + if(read_compressed_scanline(fp, dptr, img->width) == -1) { 1.391 + return -1; 1.392 + } 1.393 + dptr += img->width; 1.394 + } 1.395 + 1.396 + } else { 1.397 + /* uncompressed */ 1.398 + if(fread(img->pixels, 1, npixels, fp) < npixels) { 1.399 + return -1; 1.400 + } 1.401 + } 1.402 + return 0; 1.403 +} 1.404 + 1.405 +static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width) 1.406 +{ 1.407 + int i, count, x = 0; 1.408 + signed char ctl; 1.409 + 1.410 + while(x < width) { 1.411 + if(fread(&ctl, 1, 1, fp) < 1) return -1; 1.412 + 1.413 + if(ctl == -128) continue; 1.414 + 1.415 + if(ctl >= 0) { 1.416 + count = ctl + 1; 1.417 + if(fread(scanline, 1, count, fp) < count) return -1; 1.418 + scanline += count; 1.419 + 1.420 + } else { 1.421 + unsigned char pixel; 1.422 + count = 1 - ctl; 1.423 + if(fread(&pixel, 1, 1, fp) < 1) return -1; 1.424 + 1.425 + for(i=0; i<count; i++) { 1.426 + *scanline++ = pixel; 1.427 + } 1.428 + } 1.429 + 1.430 + x += count; 1.431 + } 1.432 + 1.433 + return 0; 1.434 +} 1.435 + 1.436 +static int read16(FILE *fp, uint16_t *res) 1.437 +{ 1.438 + if(fread(res, sizeof *res, 1, fp) < 1) { 1.439 + return -1; 1.440 + } 1.441 +#ifdef LENDIAN 1.442 + *res = swap16(*res); 1.443 +#endif 1.444 + return 0; 1.445 +} 1.446 + 1.447 +static int read32(FILE *fp, uint32_t *res) 1.448 +{ 1.449 + if(fread(res, sizeof *res, 1, fp) < 1) { 1.450 + return -1; 1.451 + } 1.452 +#ifdef LENDIAN 1.453 + *res = swap32(*res); 1.454 +#endif 1.455 + return 0; 1.456 +} 1.457 + 1.458 +static uint16_t swap16(uint16_t x) 1.459 +{ 1.460 + return (x << 8) | (x >> 8); 1.461 +} 1.462 + 1.463 +static uint32_t swap32(uint32_t x) 1.464 +{ 1.465 + return (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24); 1.466 +}