lbm2bin

annotate src/image.c @ 2:a8988e958120

added readme and license
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 15 Jul 2017 13:31:10 +0300
parents
children
rev   line source
nuclear@0 1 /*
nuclear@0 2 colcycle - color cycling image viewer
nuclear@0 3 Copyright (C) 2016-2017 John Tsiombikas <nuclear@member.fsf.org>
nuclear@0 4
nuclear@0 5 This program is free software: you can redistribute it and/or modify
nuclear@0 6 it under the terms of the GNU General Public License as published by
nuclear@0 7 the Free Software Foundation, either version 3 of the License, or
nuclear@0 8 (at your option) any later version.
nuclear@0 9
nuclear@0 10 This program is distributed in the hope that it will be useful,
nuclear@0 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
nuclear@0 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
nuclear@0 13 GNU General Public License for more details.
nuclear@0 14
nuclear@0 15 You should have received a copy of the GNU General Public License
nuclear@0 16 along with this program. If not, see <http://www.gnu.org/licenses/>.
nuclear@0 17 */
nuclear@0 18 #include <stdio.h>
nuclear@0 19 #include <stdlib.h>
nuclear@0 20 #include <string.h>
nuclear@0 21 #include <errno.h>
nuclear@0 22 #include <ctype.h>
nuclear@0 23 #include <math.h>
nuclear@0 24 #if defined(__WATCOMC__) || defined(_MSC_VER) || defined(WIN32)
nuclear@0 25 #include <malloc.h>
nuclear@0 26 #else
nuclear@0 27 #include <alloca.h>
nuclear@0 28 #endif
nuclear@0 29 #include "image.h"
nuclear@0 30 #include "imagelbm.h"
nuclear@0 31
nuclear@0 32 #ifndef M_PI
nuclear@0 33 #define M_PI 3.141593
nuclear@0 34 #endif
nuclear@0 35
nuclear@0 36 static int flatten_crange_list(struct image *img);
nuclear@0 37
nuclear@0 38 int gen_test_image(struct image *img)
nuclear@0 39 {
nuclear@0 40 int i, j;
nuclear@0 41 unsigned char *pptr;
nuclear@0 42
nuclear@0 43 img->width = 640;
nuclear@0 44 img->height = 480;
nuclear@0 45 img->bpp = 8;
nuclear@0 46
nuclear@0 47 if(!(img->range = malloc(sizeof *img->range))) {
nuclear@0 48 return -1;
nuclear@0 49 }
nuclear@0 50 img->num_ranges = 1;
nuclear@0 51 img->range[0].low = 0;
nuclear@0 52 img->range[0].high = 255;
nuclear@0 53 img->range[0].cmode = CYCLE_NORMAL;
nuclear@0 54 img->range[0].rate = 5000;
nuclear@0 55
nuclear@0 56 if(!(img->pixels = malloc(img->width * img->height))) {
nuclear@0 57 free(img->range);
nuclear@0 58 return -1;
nuclear@0 59 }
nuclear@0 60
nuclear@0 61 for(i=0; i<256; i++) {
nuclear@0 62 float theta = M_PI * 2.0 * (float)i / 256.0;
nuclear@0 63 float r = cos(theta) * 0.5 + 0.5;
nuclear@0 64 float g = sin(theta) * 0.5 + 0.5;
nuclear@0 65 float b = -cos(theta) * 0.5 + 0.5;
nuclear@0 66 img->palette[i].r = (int)(r * 255.0);
nuclear@0 67 img->palette[i].g = (int)(g * 255.0);
nuclear@0 68 img->palette[i].b = (int)(b * 255.0);
nuclear@0 69 }
nuclear@0 70
nuclear@0 71 pptr = img->pixels;
nuclear@0 72 for(i=0; i<img->height; i++) {
nuclear@0 73 int c = (i << 8) / img->height;
nuclear@0 74 for(j=0; j<img->width; j++) {
nuclear@0 75 int chess = ((i >> 6) & 1) == ((j >> 6) & 1);
nuclear@0 76 *pptr++ = (chess ? c : c + 128) & 0xff;
nuclear@0 77 }
nuclear@0 78 }
nuclear@0 79 return 0;
nuclear@0 80 }
nuclear@0 81
nuclear@0 82 #define MAX_TOKEN_SIZE 256
nuclear@0 83 static int image_block(FILE *fp, struct image *img);
nuclear@0 84 static int nextc = -1;
nuclear@0 85 static char token[MAX_TOKEN_SIZE];
nuclear@0 86
nuclear@0 87 int load_image(struct image *img, const char *fname)
nuclear@0 88 {
nuclear@0 89 FILE *fp;
nuclear@0 90 int c;
nuclear@0 91
nuclear@0 92 if(!(fp = fopen(fname, "rb"))) {
nuclear@0 93 fprintf(stderr, "failed to open file: %s: %s\n", fname, strerror(errno));
nuclear@0 94 return -1;
nuclear@0 95 }
nuclear@0 96
nuclear@0 97 if(file_is_lbm(fp)) {
nuclear@0 98 if(load_image_lbm(img, fp) == -1) {
nuclear@0 99 fclose(fp);
nuclear@0 100 return -1;
nuclear@0 101 }
nuclear@0 102 fclose(fp);
nuclear@0 103 flatten_crange_list(img);
nuclear@0 104 return 0;
nuclear@0 105 }
nuclear@0 106
nuclear@0 107 /* find the start of the root block */
nuclear@0 108 while((c = fgetc(fp)) != -1 && c != '{');
nuclear@0 109 if(feof(fp)) {
nuclear@0 110 fprintf(stderr, "invalid image format, no image block found: %s\n", fname);
nuclear@0 111 return -1;
nuclear@0 112 }
nuclear@0 113
nuclear@0 114 nextc = c; /* prime the pump with the extra char we read above */
nuclear@0 115 if(image_block(fp, img) == -1) {
nuclear@0 116 fprintf(stderr, "failed to read image block from: %s\n", fname);
nuclear@0 117 fclose(fp);
nuclear@0 118 return -1;
nuclear@0 119 }
nuclear@0 120 fclose(fp);
nuclear@0 121
nuclear@0 122 flatten_crange_list(img);
nuclear@0 123 return 0;
nuclear@0 124 }
nuclear@0 125
nuclear@0 126 void destroy_image(struct image *img)
nuclear@0 127 {
nuclear@0 128 if(img) {
nuclear@0 129 free(img->pixels);
nuclear@0 130 free(img->range);
nuclear@0 131 memset(img, 0, sizeof *img);
nuclear@0 132 }
nuclear@0 133 }
nuclear@0 134
nuclear@0 135 static int flatten_crange_list(struct image *img)
nuclear@0 136 {
nuclear@0 137 struct colrange *list = img->range;
nuclear@0 138 struct colrange *rptr;
nuclear@0 139
nuclear@0 140 if(img->num_ranges <= 0) {
nuclear@0 141 return 0;
nuclear@0 142 }
nuclear@0 143
nuclear@0 144 if(!(img->range = malloc(img->num_ranges * sizeof *img->range))) {
nuclear@0 145 perror("flatten_crange_list: failed to allocate range array\n");
nuclear@0 146 return -1;
nuclear@0 147 }
nuclear@0 148
nuclear@0 149 rptr = img->range;
nuclear@0 150 while(list) {
nuclear@0 151 struct colrange *rng = list;
nuclear@0 152 list = list->next;
nuclear@0 153 *rptr++ = *rng;
nuclear@0 154 free(rng);
nuclear@0 155 }
nuclear@0 156 return 0;
nuclear@0 157 }
nuclear@0 158
nuclear@0 159 /* ---- parser ---- */
nuclear@0 160
nuclear@0 161 enum {
nuclear@0 162 TOKEN_NUM,
nuclear@0 163 TOKEN_NAME,
nuclear@0 164 TOKEN_STR
nuclear@0 165 };
nuclear@0 166
nuclear@0 167 static int next_char(FILE *fp)
nuclear@0 168 {
nuclear@0 169 while((nextc = fgetc(fp)) != -1 && isspace(nextc));
nuclear@0 170 return nextc;
nuclear@0 171 }
nuclear@0 172
nuclear@0 173 static int next_token(FILE *fp)
nuclear@0 174 {
nuclear@0 175 char *ptr;
nuclear@0 176
nuclear@0 177 if(nextc == -1) {
nuclear@0 178 return -1;
nuclear@0 179 }
nuclear@0 180
nuclear@0 181 switch(nextc) {
nuclear@0 182 case '{':
nuclear@0 183 case '}':
nuclear@0 184 case ',':
nuclear@0 185 case '[':
nuclear@0 186 case ']':
nuclear@0 187 case ':':
nuclear@0 188 token[0] = nextc;
nuclear@0 189 token[1] = 0;
nuclear@0 190 nextc = next_char(fp);
nuclear@0 191 return token[0];
nuclear@0 192
nuclear@0 193 case '\'':
nuclear@0 194 ptr = token;
nuclear@0 195 nextc = next_char(fp);
nuclear@0 196 while(nextc != -1 && nextc != '\'') {
nuclear@0 197 *ptr++ = nextc;
nuclear@0 198 nextc = fgetc(fp);
nuclear@0 199 }
nuclear@0 200 nextc = next_char(fp);
nuclear@0 201 return TOKEN_STR;
nuclear@0 202
nuclear@0 203 default:
nuclear@0 204 break;
nuclear@0 205 }
nuclear@0 206
nuclear@0 207 if(isalpha(nextc)) {
nuclear@0 208 ptr = token;
nuclear@0 209 while(nextc != -1 && isalpha(nextc)) {
nuclear@0 210 *ptr++ = nextc;
nuclear@0 211 nextc = next_char(fp);
nuclear@0 212 }
nuclear@0 213 *ptr = 0;
nuclear@0 214 return TOKEN_NAME;
nuclear@0 215 }
nuclear@0 216 if(isdigit(nextc)) {
nuclear@0 217 ptr = token;
nuclear@0 218 while(nextc != -1 && isdigit(nextc)) {
nuclear@0 219 *ptr++ = nextc;
nuclear@0 220 nextc = next_char(fp);
nuclear@0 221 }
nuclear@0 222 *ptr = 0;
nuclear@0 223 return TOKEN_NUM;
nuclear@0 224 }
nuclear@0 225
nuclear@0 226 token[0] = nextc;
nuclear@0 227 token[1] = 0;
nuclear@0 228 fprintf(stderr, "next_token: unexpected character: %c\n", nextc);
nuclear@0 229 return -1;
nuclear@0 230 }
nuclear@0 231
nuclear@0 232 static int expect(FILE *fp, int tok)
nuclear@0 233 {
nuclear@0 234 if(next_token(fp) != tok) {
nuclear@0 235 return 0;
nuclear@0 236 }
nuclear@0 237 return 1;
nuclear@0 238 }
nuclear@0 239
nuclear@0 240 static const char *toktypestr(int tok)
nuclear@0 241 {
nuclear@0 242 static char buf[] = "' '";
nuclear@0 243 switch(tok) {
nuclear@0 244 case TOKEN_NUM:
nuclear@0 245 return "number";
nuclear@0 246 case TOKEN_NAME:
nuclear@0 247 return "name";
nuclear@0 248 case TOKEN_STR:
nuclear@0 249 return "string";
nuclear@0 250 default:
nuclear@0 251 break;
nuclear@0 252 }
nuclear@0 253 buf[1] = tok;
nuclear@0 254 return buf;
nuclear@0 255 }
nuclear@0 256
nuclear@0 257 #define EXPECT(fp, x) \
nuclear@0 258 do { \
nuclear@0 259 if(!expect(fp, x)) { \
nuclear@0 260 fprintf(stderr, "%s: expected: %s, found: %s\n", __FUNCTION__, toktypestr(x), token); \
nuclear@0 261 return -1; \
nuclear@0 262 } \
nuclear@0 263 } while(0)
nuclear@0 264
nuclear@0 265 static int palette(FILE *fp, struct image *img)
nuclear@0 266 {
nuclear@0 267 int tok, cidx = 0;
nuclear@0 268 struct color col, *cptr;
nuclear@0 269
nuclear@0 270 EXPECT(fp, '[');
nuclear@0 271
nuclear@0 272 while((tok = next_token(fp)) == '[') {
nuclear@0 273 cptr = cidx < 256 ? &img->palette[cidx] : &col;
nuclear@0 274 ++cidx;
nuclear@0 275
nuclear@0 276 EXPECT(fp, TOKEN_NUM);
nuclear@0 277 cptr->r = atoi(token);
nuclear@0 278 EXPECT(fp, ',');
nuclear@0 279 EXPECT(fp, TOKEN_NUM);
nuclear@0 280 cptr->g = atoi(token);
nuclear@0 281 EXPECT(fp, ',');
nuclear@0 282 EXPECT(fp, TOKEN_NUM);
nuclear@0 283 cptr->b = atoi(token);
nuclear@0 284 EXPECT(fp, ']');
nuclear@0 285
nuclear@0 286 if(nextc == ',') {
nuclear@0 287 next_token(fp); /* skip comma */
nuclear@0 288 }
nuclear@0 289 }
nuclear@0 290
nuclear@0 291 if(tok != ']') {
nuclear@0 292 fprintf(stderr, "palette must be closed by a ']' token\n");
nuclear@0 293 return -1;
nuclear@0 294 }
nuclear@0 295 return 0;
nuclear@0 296 }
nuclear@0 297
nuclear@0 298 static int crange(FILE *fp, struct colrange *rng)
nuclear@0 299 {
nuclear@0 300 int val;
nuclear@0 301 char name[MAX_TOKEN_SIZE];
nuclear@0 302
nuclear@0 303 EXPECT(fp, '{');
nuclear@0 304
nuclear@0 305 while(nextc != -1 && nextc != '}') {
nuclear@0 306 EXPECT(fp, TOKEN_NAME);
nuclear@0 307 strcpy(name, token);
nuclear@0 308 EXPECT(fp, ':');
nuclear@0 309 EXPECT(fp, TOKEN_NUM);
nuclear@0 310 val = atoi(token);
nuclear@0 311
nuclear@0 312 if(strcmp(name, "reverse") == 0) {
nuclear@0 313 rng->cmode = val;
nuclear@0 314 } else if(strcmp(name, "rate") == 0) {
nuclear@0 315 rng->rate = val;
nuclear@0 316 } else if(strcmp(name, "low") == 0) {
nuclear@0 317 rng->low = val;
nuclear@0 318 } else if(strcmp(name, "high") == 0) {
nuclear@0 319 rng->high = val;
nuclear@0 320 } else {
nuclear@0 321 fprintf(stderr, "invalid attribute %s in cycles range\n", name);
nuclear@0 322 return -1;
nuclear@0 323 }
nuclear@0 324
nuclear@0 325 if(nextc == ',') {
nuclear@0 326 next_token(fp);
nuclear@0 327 }
nuclear@0 328 }
nuclear@0 329
nuclear@0 330 EXPECT(fp, '}');
nuclear@0 331 return 0;
nuclear@0 332 }
nuclear@0 333
nuclear@0 334 static int cycles(FILE *fp, struct image *img)
nuclear@0 335 {
nuclear@0 336 struct colrange *list = 0, *rng;
nuclear@0 337
nuclear@0 338 EXPECT(fp, '[');
nuclear@0 339
nuclear@0 340 img->num_ranges = 0;
nuclear@0 341 while(nextc == '{') {
nuclear@0 342 if(!(rng = malloc(sizeof *rng))) {
nuclear@0 343 perror("failed to allocate color range");
nuclear@0 344 goto err;
nuclear@0 345 }
nuclear@0 346 if(crange(fp, rng) == -1) {
nuclear@0 347 free(rng);
nuclear@0 348 goto err;
nuclear@0 349 }
nuclear@0 350 if(rng->low != rng->high && rng->rate > 0) {
nuclear@0 351 rng->next = list;
nuclear@0 352 list = rng;
nuclear@0 353 ++img->num_ranges;
nuclear@0 354 } else {
nuclear@0 355 free(rng);
nuclear@0 356 }
nuclear@0 357
nuclear@0 358 if(nextc == ',') {
nuclear@0 359 next_token(fp); /* eat the comma */
nuclear@0 360 }
nuclear@0 361 }
nuclear@0 362
nuclear@0 363 img->range = list;
nuclear@0 364
nuclear@0 365 if(!expect(fp, ']')) {
nuclear@0 366 fprintf(stderr, "cycles: missing closing bracket\n");
nuclear@0 367 goto err;
nuclear@0 368 }
nuclear@0 369 return 0;
nuclear@0 370
nuclear@0 371 err:
nuclear@0 372 while(list) {
nuclear@0 373 rng = list;
nuclear@0 374 list = list->next;
nuclear@0 375 free(rng);
nuclear@0 376 }
nuclear@0 377 img->num_ranges = 0;
nuclear@0 378 return -1;
nuclear@0 379 }
nuclear@0 380
nuclear@0 381 static int pixels(FILE *fp, struct image *img)
nuclear@0 382 {
nuclear@0 383 int tok, num_pixels;
nuclear@0 384 unsigned char *pptr;
nuclear@0 385
nuclear@0 386 if(img->width <= 0 || img->height <= 0) {
nuclear@0 387 fprintf(stderr, "pixel block found before defining the image dimensions!\n");
nuclear@0 388 return -1;
nuclear@0 389 }
nuclear@0 390 num_pixels = img->width * img->height;
nuclear@0 391 if(!(img->pixels = malloc(num_pixels))) {
nuclear@0 392 perror("failed to allocate pixels");
nuclear@0 393 return -1;
nuclear@0 394 }
nuclear@0 395 pptr = img->pixels;
nuclear@0 396
nuclear@0 397 EXPECT(fp, '[');
nuclear@0 398
nuclear@0 399 while((tok = next_token(fp)) == TOKEN_NUM) {
nuclear@0 400 if(pptr - img->pixels >= num_pixels) {
nuclear@0 401 pptr = 0;
nuclear@0 402 fprintf(stderr, "more pixel data provided than the specified image dimensions\n");
nuclear@0 403 }
nuclear@0 404 if(!pptr) continue;
nuclear@0 405 *pptr++ = atoi(token);
nuclear@0 406
nuclear@0 407 if(nextc == ',') {
nuclear@0 408 next_token(fp); /* eat comma */
nuclear@0 409 }
nuclear@0 410 }
nuclear@0 411
nuclear@0 412 if(tok != ']') {
nuclear@0 413 printf("pixels: missing closing bracket\n");
nuclear@0 414 return -1;
nuclear@0 415 }
nuclear@0 416 return 0;
nuclear@0 417 }
nuclear@0 418
nuclear@0 419 static int attribute(FILE *fp, struct image *img)
nuclear@0 420 {
nuclear@0 421 char *attr_name;
nuclear@0 422
nuclear@0 423 if(!expect(fp, TOKEN_NAME)) {
nuclear@0 424 return -1;
nuclear@0 425 }
nuclear@0 426 attr_name = alloca(strlen(token) + 1);
nuclear@0 427 strcpy(attr_name, token);
nuclear@0 428
nuclear@0 429 if(!expect(fp, ':')) {
nuclear@0 430 return -1;
nuclear@0 431 }
nuclear@0 432
nuclear@0 433 if(strcmp(attr_name, "filename") == 0) {
nuclear@0 434 if(!expect(fp, TOKEN_STR)) {
nuclear@0 435 fprintf(stderr, "attribute: filename should be a string\n");
nuclear@0 436 return -1;
nuclear@0 437 }
nuclear@0 438
nuclear@0 439 } else if(strcmp(attr_name, "width") == 0 || strcmp(attr_name, "height") == 0) {
nuclear@0 440 int *dst = attr_name[0] == 'w' ? &img->width : &img->height;
nuclear@0 441 if(!expect(fp, TOKEN_NUM)) {
nuclear@0 442 fprintf(stderr, "attribute: %s should be a number\n", attr_name);
nuclear@0 443 return -1;
nuclear@0 444 }
nuclear@0 445 *dst = atoi(token);
nuclear@0 446
nuclear@0 447 } else if(strcmp(attr_name, "colors") == 0) {
nuclear@0 448 if(palette(fp, img) == -1) {
nuclear@0 449 fprintf(stderr, "attribute: colors should be a palette\n");
nuclear@0 450 return -1;
nuclear@0 451 }
nuclear@0 452
nuclear@0 453 } else if(strcmp(attr_name, "cycles") == 0) {
nuclear@0 454 if(cycles(fp, img) == -1) {
nuclear@0 455 fprintf(stderr, "attribute: cycles should be a list of palranges\n");
nuclear@0 456 return -1;
nuclear@0 457 }
nuclear@0 458
nuclear@0 459 } else if(strcmp(attr_name, "pixels") == 0) {
nuclear@0 460 if(pixels(fp, img) == -1) {
nuclear@0 461 fprintf(stderr, "attribute: pixels should be a list of indices\n");
nuclear@0 462 return -1;
nuclear@0 463 }
nuclear@0 464
nuclear@0 465 } else {
nuclear@0 466 fprintf(stderr, "unknown attribute: %s\n", attr_name);
nuclear@0 467 return -1;
nuclear@0 468 }
nuclear@0 469 return 0;
nuclear@0 470 }
nuclear@0 471
nuclear@0 472 static int image_block(FILE *fp, struct image *img)
nuclear@0 473 {
nuclear@0 474 EXPECT(fp, '{');
nuclear@0 475
nuclear@0 476 img->width = img->height = -1;
nuclear@0 477 img->bpp = 8;
nuclear@0 478
nuclear@0 479 while(attribute(fp, img) != -1) {
nuclear@0 480 if(nextc == ',') {
nuclear@0 481 next_token(fp); /* eat comma */
nuclear@0 482 }
nuclear@0 483 }
nuclear@0 484 return 0;
nuclear@0 485 }