winlivebg_test1

annotate src/imagelbm.c @ 2:a9025f31ae2d

sortof works, testing with colcycle hack
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 28 Oct 2019 16:07:25 +0200
parents
children
rev   line source
nuclear@2 1 /*
nuclear@2 2 colcycle - color cycling image viewer
nuclear@2 3 Copyright (C) 2016-2017 John Tsiombikas <nuclear@member.fsf.org>
nuclear@2 4
nuclear@2 5 This program is free software: you can redistribute it and/or modify
nuclear@2 6 it under the terms of the GNU General Public License as published by
nuclear@2 7 the Free Software Foundation, either version 3 of the License, or
nuclear@2 8 (at your option) any later version.
nuclear@2 9
nuclear@2 10 This program is distributed in the hope that it will be useful,
nuclear@2 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
nuclear@2 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
nuclear@2 13 GNU General Public License for more details.
nuclear@2 14
nuclear@2 15 You should have received a copy of the GNU General Public License
nuclear@2 16 along with this program. If not, see <http://www.gnu.org/licenses/>.
nuclear@2 17 */
nuclear@2 18 #include <stdio.h>
nuclear@2 19 #include <stdlib.h>
nuclear@2 20 #include <assert.h>
nuclear@2 21 #include <inttypes.h>
nuclear@2 22 #include <string.h>
nuclear@2 23 #if defined(__WATCOMC__) || defined(WIN32)
nuclear@2 24 #include <malloc.h>
nuclear@2 25 #else
nuclear@2 26 #include <alloca.h>
nuclear@2 27 #endif
nuclear@2 28 #include "imagelbm.h"
nuclear@2 29
nuclear@2 30 #ifdef __GNUC__
nuclear@2 31 #define PACKED __attribute__((packed))
nuclear@2 32 #endif
nuclear@2 33
nuclear@2 34 #ifdef __BYTE_ORDER__
nuclear@2 35 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
nuclear@2 36 #define LENDIAN
nuclear@2 37 #else
nuclear@2 38 #define BENDIAN
nuclear@2 39 #endif
nuclear@2 40 #endif
nuclear@2 41
nuclear@2 42 #define MKID(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
nuclear@2 43
nuclear@2 44 #define IS_IFF_CONTAINER(id) ((id) == IFF_FORM || (id) == IFF_CAT || (id) == IFF_LIST)
nuclear@2 45
nuclear@2 46 enum {
nuclear@2 47 IFF_FORM = MKID('F', 'O', 'R', 'M'),
nuclear@2 48 IFF_CAT = MKID('C', 'A', 'T', ' '),
nuclear@2 49 IFF_LIST = MKID('L', 'I', 'S', 'T'),
nuclear@2 50 IFF_ILBM = MKID('I', 'L', 'B', 'M'),
nuclear@2 51 IFF_PBM = MKID('P', 'B', 'M', ' '),
nuclear@2 52 IFF_BMHD = MKID('B', 'M', 'H', 'D'),
nuclear@2 53 IFF_CMAP = MKID('C', 'M', 'A', 'P'),
nuclear@2 54 IFF_BODY = MKID('B', 'O', 'D', 'Y'),
nuclear@2 55 IFF_CRNG = MKID('C', 'R', 'N', 'G')
nuclear@2 56 };
nuclear@2 57
nuclear@2 58
nuclear@2 59 struct chdr {
nuclear@2 60 uint32_t id;
nuclear@2 61 uint32_t size;
nuclear@2 62 };
nuclear@2 63
nuclear@2 64 #ifdef __WATCOMC__
nuclear@2 65 #pragma push(pack, 1)
nuclear@2 66 #endif
nuclear@2 67 struct bitmap_header {
nuclear@2 68 uint16_t width, height;
nuclear@2 69 int16_t xoffs, yoffs;
nuclear@2 70 uint8_t nplanes;
nuclear@2 71 uint8_t masking;
nuclear@2 72 uint8_t compression;
nuclear@2 73 uint8_t padding;
nuclear@2 74 uint16_t colorkey;
nuclear@2 75 uint8_t aspect_num, aspect_denom;
nuclear@2 76 int16_t pgwidth, pgheight;
nuclear@2 77 } PACKED;
nuclear@2 78 #ifdef __WATCOMC__
nuclear@2 79 #pragma pop(pack)
nuclear@2 80 #endif
nuclear@2 81
nuclear@2 82 enum {
nuclear@2 83 MASK_NONE,
nuclear@2 84 MASK_PLANE,
nuclear@2 85 MASK_COLORKEY,
nuclear@2 86 MASK_LASSO
nuclear@2 87 };
nuclear@2 88
nuclear@2 89 struct crng {
nuclear@2 90 uint16_t padding;
nuclear@2 91 uint16_t rate;
nuclear@2 92 uint16_t flags;
nuclear@2 93 uint8_t low, high;
nuclear@2 94 };
nuclear@2 95
nuclear@2 96 enum {
nuclear@2 97 CRNG_ENABLE = 1,
nuclear@2 98 CRNG_REVERSE = 2
nuclear@2 99 };
nuclear@2 100
nuclear@2 101 static int read_header(FILE *fp, struct chdr *hdr);
nuclear@2 102 static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct image *img);
nuclear@2 103 static int read_bmhd(FILE *fp, struct bitmap_header *bmhd);
nuclear@2 104 static int read_crng(FILE *fp, struct crng *crng);
nuclear@2 105 static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct image *img);
nuclear@2 106 static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct image *img);
nuclear@2 107 static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width);
nuclear@2 108 static int read16(FILE *fp, uint16_t *res);
nuclear@2 109 static int read32(FILE *fp, uint32_t *res);
nuclear@2 110 static uint16_t swap16(uint16_t x);
nuclear@2 111 static uint32_t swap32(uint32_t x);
nuclear@2 112
nuclear@2 113 int file_is_lbm(FILE *fp)
nuclear@2 114 {
nuclear@2 115 uint32_t type;
nuclear@2 116 struct chdr hdr;
nuclear@2 117
nuclear@2 118 while(read_header(fp, &hdr) != -1) {
nuclear@2 119 if(IS_IFF_CONTAINER(hdr.id)) {
nuclear@2 120 if(read32(fp, &type) == -1) {
nuclear@2 121 break;
nuclear@2 122 }
nuclear@2 123
nuclear@2 124 if(type == IFF_ILBM || type == IFF_PBM ) {
nuclear@2 125 rewind(fp);
nuclear@2 126 return 1;
nuclear@2 127 }
nuclear@2 128 hdr.size -= sizeof type; /* so we will seek fwd correctly */
nuclear@2 129 }
nuclear@2 130 fseek(fp, hdr.size, SEEK_CUR);
nuclear@2 131 }
nuclear@2 132 fseek(fp, 0, SEEK_SET);
nuclear@2 133 return 0;
nuclear@2 134 }
nuclear@2 135
nuclear@2 136 void print_chunkid(uint32_t id)
nuclear@2 137 {
nuclear@2 138 char str[5] = {0};
nuclear@2 139 #ifdef LENDIAN
nuclear@2 140 id = swap32(id);
nuclear@2 141 #endif
nuclear@2 142 memcpy(str, &id, 4);
nuclear@2 143 puts(str);
nuclear@2 144 }
nuclear@2 145
nuclear@2 146 int load_image_lbm(struct image *img, FILE *fp)
nuclear@2 147 {
nuclear@2 148 uint32_t type;
nuclear@2 149 struct chdr hdr;
nuclear@2 150
nuclear@2 151 while(read_header(fp, &hdr) != -1) {
nuclear@2 152 if(IS_IFF_CONTAINER(hdr.id)) {
nuclear@2 153 if(read32(fp, &type) == -1) {
nuclear@2 154 break;
nuclear@2 155 }
nuclear@2 156 hdr.size -= sizeof type; /* to compensate for having advanced 4 more bytes */
nuclear@2 157
nuclear@2 158 if(type == IFF_ILBM) {
nuclear@2 159 if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) {
nuclear@2 160 return -1;
nuclear@2 161 }
nuclear@2 162 return 0;
nuclear@2 163 }
nuclear@2 164 if(type == IFF_PBM) {
nuclear@2 165 if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) {
nuclear@2 166 return -1;
nuclear@2 167 }
nuclear@2 168 return 0;
nuclear@2 169 }
nuclear@2 170 }
nuclear@2 171 fseek(fp, hdr.size, SEEK_CUR);
nuclear@2 172 }
nuclear@2 173 return 0;
nuclear@2 174 }
nuclear@2 175
nuclear@2 176 static int read_header(FILE *fp, struct chdr *hdr)
nuclear@2 177 {
nuclear@2 178 if(fread(hdr, 1, sizeof *hdr, fp) < sizeof *hdr) {
nuclear@2 179 return -1;
nuclear@2 180 }
nuclear@2 181 #ifdef LENDIAN
nuclear@2 182 hdr->id = swap32(hdr->id);
nuclear@2 183 hdr->size = swap32(hdr->size);
nuclear@2 184 #endif
nuclear@2 185 return 0;
nuclear@2 186 }
nuclear@2 187
nuclear@2 188 static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct image *img)
nuclear@2 189 {
nuclear@2 190 int i, res = -1;
nuclear@2 191 struct chdr hdr;
nuclear@2 192 struct bitmap_header bmhd;
nuclear@2 193 struct crng crng;
nuclear@2 194 struct colrange *crnode;
nuclear@2 195 unsigned char pal[3 * 256];
nuclear@2 196 unsigned char *pptr;
nuclear@2 197 long start = ftell(fp);
nuclear@2 198
nuclear@2 199 memset(img, 0, sizeof *img);
nuclear@2 200
nuclear@2 201 while(read_header(fp, &hdr) != -1 && ftell(fp) - start < size) {
nuclear@2 202 switch(hdr.id) {
nuclear@2 203 case IFF_BMHD:
nuclear@2 204 assert(hdr.size == 20);
nuclear@2 205 if(read_bmhd(fp, &bmhd) == -1) {
nuclear@2 206 return -1;
nuclear@2 207 }
nuclear@2 208 img->width = bmhd.width;
nuclear@2 209 img->height = bmhd.height;
nuclear@2 210 img->bpp = bmhd.nplanes;
nuclear@2 211 if(bmhd.nplanes > 8) {
nuclear@2 212 fprintf(stderr, "%d planes found, only paletized LBM files supported\n", bmhd.nplanes);
nuclear@2 213 return -1;
nuclear@2 214 }
nuclear@2 215 if(!(img->pixels = malloc(img->width * img->height))) {
nuclear@2 216 fprintf(stderr, "failed to allocate %dx%d image\n", img->width, img->height);
nuclear@2 217 return -1;
nuclear@2 218 }
nuclear@2 219 break;
nuclear@2 220
nuclear@2 221 case IFF_CMAP:
nuclear@2 222 assert(hdr.size / 3 <= 256);
nuclear@2 223
nuclear@2 224 if(fread(pal, 1, hdr.size, fp) < hdr.size) {
nuclear@2 225 fprintf(stderr, "failed to read colormap\n");
nuclear@2 226 return -1;
nuclear@2 227 }
nuclear@2 228 pptr = pal;
nuclear@2 229 for(i=0; i<256; i++) {
nuclear@2 230 img->palette[i].r = *pptr++;
nuclear@2 231 img->palette[i].g = *pptr++;
nuclear@2 232 img->palette[i].b = *pptr++;
nuclear@2 233 }
nuclear@2 234 break;
nuclear@2 235
nuclear@2 236 case IFF_CRNG:
nuclear@2 237 assert(hdr.size == sizeof crng);
nuclear@2 238
nuclear@2 239 if(read_crng(fp, &crng) == -1) {
nuclear@2 240 fprintf(stderr, "failed to read color cycling range chunk\n");
nuclear@2 241 return -1;
nuclear@2 242 }
nuclear@2 243 if(crng.low != crng.high && crng.rate > 0) {
nuclear@2 244 if(!(crnode = malloc(sizeof *crnode))) {
nuclear@2 245 fprintf(stderr, "failed to allocate color range node\n");
nuclear@2 246 return -1;
nuclear@2 247 }
nuclear@2 248 crnode->low = crng.low;
nuclear@2 249 crnode->high = crng.high;
nuclear@2 250 crnode->cmode = (crng.flags & CRNG_REVERSE) ? CYCLE_REVERSE : CYCLE_NORMAL;
nuclear@2 251 crnode->rate = crng.rate;
nuclear@2 252 crnode->next = img->range;
nuclear@2 253 img->range = crnode;
nuclear@2 254 ++img->num_ranges;
nuclear@2 255 }
nuclear@2 256 break;
nuclear@2 257
nuclear@2 258 case IFF_BODY:
nuclear@2 259 if(!img->pixels) {
nuclear@2 260 fprintf(stderr, "malformed ILBM image: encountered BODY chunk before BMHD\n");
nuclear@2 261 return -1;
nuclear@2 262 }
nuclear@2 263 if(type == IFF_ILBM) {
nuclear@2 264 if(read_body_ilbm(fp, &bmhd, img) == -1) {
nuclear@2 265 fprintf(stderr, "failed to read interleaved pixel data\n");
nuclear@2 266 return -1;
nuclear@2 267 }
nuclear@2 268 } else {
nuclear@2 269 assert(type == IFF_PBM);
nuclear@2 270 if(read_body_pbm(fp, &bmhd, img) == -1) {
nuclear@2 271 fprintf(stderr, "failed to read linear pixel data\n");
nuclear@2 272 return -1;
nuclear@2 273 }
nuclear@2 274 }
nuclear@2 275 res = 0; /* sucessfully read image */
nuclear@2 276 break;
nuclear@2 277
nuclear@2 278 default:
nuclear@2 279 /* skip unknown chunks */
nuclear@2 280 fseek(fp, hdr.size, SEEK_CUR);
nuclear@2 281 if(ftell(fp) & 1) {
nuclear@2 282 /* chunks must start at even offsets */
nuclear@2 283 fseek(fp, 1, SEEK_CUR);
nuclear@2 284 }
nuclear@2 285 }
nuclear@2 286 }
nuclear@2 287
nuclear@2 288 return res;
nuclear@2 289 }
nuclear@2 290
nuclear@2 291
nuclear@2 292 static int read_bmhd(FILE *fp, struct bitmap_header *bmhd)
nuclear@2 293 {
nuclear@2 294 if(fread(bmhd, sizeof *bmhd, 1, fp) < 1) {
nuclear@2 295 return -1;
nuclear@2 296 }
nuclear@2 297 #ifdef LENDIAN
nuclear@2 298 bmhd->width = swap16(bmhd->width);
nuclear@2 299 bmhd->height = swap16(bmhd->height);
nuclear@2 300 bmhd->xoffs = swap16(bmhd->xoffs);
nuclear@2 301 bmhd->yoffs = swap16(bmhd->yoffs);
nuclear@2 302 bmhd->colorkey = swap16(bmhd->colorkey);
nuclear@2 303 bmhd->pgwidth = swap16(bmhd->pgwidth);
nuclear@2 304 bmhd->pgheight = swap16(bmhd->pgheight);
nuclear@2 305 #endif
nuclear@2 306 return 0;
nuclear@2 307 }
nuclear@2 308
nuclear@2 309 static int read_crng(FILE *fp, struct crng *crng)
nuclear@2 310 {
nuclear@2 311 if(fread(crng, sizeof *crng, 1, fp) < 1) {
nuclear@2 312 return -1;
nuclear@2 313 }
nuclear@2 314 #ifdef LENDIAN
nuclear@2 315 crng->rate = swap16(crng->rate);
nuclear@2 316 crng->flags = swap16(crng->flags);
nuclear@2 317 #endif
nuclear@2 318 return 0;
nuclear@2 319 }
nuclear@2 320
nuclear@2 321 /* scanline: [bp0 row][bp1 row]...[bpN-1 row][opt mask row]
nuclear@2 322 * each uncompressed row is width / 8 bytes
nuclear@2 323 */
nuclear@2 324 static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct image *img)
nuclear@2 325 {
nuclear@2 326 int i, j, k, bitidx;
nuclear@2 327 int rowsz = img->width / 8;
nuclear@2 328 unsigned char *src, *dest = img->pixels;
nuclear@2 329 unsigned char *rowbuf = alloca(rowsz);
nuclear@2 330
nuclear@2 331 assert(bmhd->width == img->width);
nuclear@2 332 assert(bmhd->height == img->height);
nuclear@2 333 assert(img->pixels);
nuclear@2 334
nuclear@2 335 for(i=0; i<img->height; i++) {
nuclear@2 336
nuclear@2 337 memset(dest, 0, img->width); /* clear the whole scanline to OR bits into place */
nuclear@2 338
nuclear@2 339 for(j=0; j<bmhd->nplanes; j++) {
nuclear@2 340 // read a row corresponding to bitplane j
nuclear@2 341 if(bmhd->compression) {
nuclear@2 342 if(read_compressed_scanline(fp, rowbuf, rowsz) == -1) {
nuclear@2 343 return -1;
nuclear@2 344 }
nuclear@2 345 } else {
nuclear@2 346 if(fread(rowbuf, 1, rowsz, fp) < rowsz) {
nuclear@2 347 return -1;
nuclear@2 348 }
nuclear@2 349 }
nuclear@2 350
nuclear@2 351 // distribute all bits across the linear output scanline
nuclear@2 352 src = rowbuf;
nuclear@2 353 bitidx = 0;
nuclear@2 354
nuclear@2 355 for(k=0; k<img->width; k++) {
nuclear@2 356 dest[k] |= ((*src >> (7 - bitidx)) & 1) << j;
nuclear@2 357
nuclear@2 358 if(++bitidx >= 8) {
nuclear@2 359 bitidx = 0;
nuclear@2 360 ++src;
nuclear@2 361 }
nuclear@2 362 }
nuclear@2 363 }
nuclear@2 364
nuclear@2 365 if(bmhd->masking & MASK_PLANE) {
nuclear@2 366 /* skip the mask (1bpp) */
nuclear@2 367 fseek(fp, rowsz, SEEK_CUR);
nuclear@2 368 }
nuclear@2 369
nuclear@2 370 dest += img->width;
nuclear@2 371 }
nuclear@2 372 return 0;
nuclear@2 373 }
nuclear@2 374
nuclear@2 375 static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct image *img)
nuclear@2 376 {
nuclear@2 377 int i;
nuclear@2 378 int npixels = img->width * img->height;
nuclear@2 379 unsigned char *dptr = img->pixels;
nuclear@2 380
nuclear@2 381 assert(bmhd->width == img->width);
nuclear@2 382 assert(bmhd->height == img->height);
nuclear@2 383 assert(img->pixels);
nuclear@2 384
nuclear@2 385 if(bmhd->compression) {
nuclear@2 386 for(i=0; i<img->height; i++) {
nuclear@2 387 if(read_compressed_scanline(fp, dptr, img->width) == -1) {
nuclear@2 388 return -1;
nuclear@2 389 }
nuclear@2 390 dptr += img->width;
nuclear@2 391 }
nuclear@2 392
nuclear@2 393 } else {
nuclear@2 394 /* uncompressed */
nuclear@2 395 if(fread(img->pixels, 1, npixels, fp) < npixels) {
nuclear@2 396 return -1;
nuclear@2 397 }
nuclear@2 398 }
nuclear@2 399 return 0;
nuclear@2 400 }
nuclear@2 401
nuclear@2 402 static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width)
nuclear@2 403 {
nuclear@2 404 int i, count, x = 0;
nuclear@2 405 signed char ctl;
nuclear@2 406
nuclear@2 407 while(x < width) {
nuclear@2 408 if(fread(&ctl, 1, 1, fp) < 1) return -1;
nuclear@2 409
nuclear@2 410 if(ctl == -128) continue;
nuclear@2 411
nuclear@2 412 if(ctl >= 0) {
nuclear@2 413 count = ctl + 1;
nuclear@2 414 if(fread(scanline, 1, count, fp) < count) return -1;
nuclear@2 415 scanline += count;
nuclear@2 416
nuclear@2 417 } else {
nuclear@2 418 unsigned char pixel;
nuclear@2 419 count = 1 - ctl;
nuclear@2 420 if(fread(&pixel, 1, 1, fp) < 1) return -1;
nuclear@2 421
nuclear@2 422 for(i=0; i<count; i++) {
nuclear@2 423 *scanline++ = pixel;
nuclear@2 424 }
nuclear@2 425 }
nuclear@2 426
nuclear@2 427 x += count;
nuclear@2 428 }
nuclear@2 429
nuclear@2 430 return 0;
nuclear@2 431 }
nuclear@2 432
nuclear@2 433 static int read16(FILE *fp, uint16_t *res)
nuclear@2 434 {
nuclear@2 435 if(fread(res, sizeof *res, 1, fp) < 1) {
nuclear@2 436 return -1;
nuclear@2 437 }
nuclear@2 438 #ifdef LENDIAN
nuclear@2 439 *res = swap16(*res);
nuclear@2 440 #endif
nuclear@2 441 return 0;
nuclear@2 442 }
nuclear@2 443
nuclear@2 444 static int read32(FILE *fp, uint32_t *res)
nuclear@2 445 {
nuclear@2 446 if(fread(res, sizeof *res, 1, fp) < 1) {
nuclear@2 447 return -1;
nuclear@2 448 }
nuclear@2 449 #ifdef LENDIAN
nuclear@2 450 *res = swap32(*res);
nuclear@2 451 #endif
nuclear@2 452 return 0;
nuclear@2 453 }
nuclear@2 454
nuclear@2 455 static uint16_t swap16(uint16_t x)
nuclear@2 456 {
nuclear@2 457 return (x << 8) | (x >> 8);
nuclear@2 458 }
nuclear@2 459
nuclear@2 460 static uint32_t swap32(uint32_t x)
nuclear@2 461 {
nuclear@2 462 return (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24);
nuclear@2 463 }