winlivebg_test1

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