lbm2bin
diff src/image.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/image.c Sat Jul 15 13:26:32 2017 +0300 1.3 @@ -0,0 +1,485 @@ 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 <string.h> 1.24 +#include <errno.h> 1.25 +#include <ctype.h> 1.26 +#include <math.h> 1.27 +#if defined(__WATCOMC__) || defined(_MSC_VER) || defined(WIN32) 1.28 +#include <malloc.h> 1.29 +#else 1.30 +#include <alloca.h> 1.31 +#endif 1.32 +#include "image.h" 1.33 +#include "imagelbm.h" 1.34 + 1.35 +#ifndef M_PI 1.36 +#define M_PI 3.141593 1.37 +#endif 1.38 + 1.39 +static int flatten_crange_list(struct image *img); 1.40 + 1.41 +int gen_test_image(struct image *img) 1.42 +{ 1.43 + int i, j; 1.44 + unsigned char *pptr; 1.45 + 1.46 + img->width = 640; 1.47 + img->height = 480; 1.48 + img->bpp = 8; 1.49 + 1.50 + if(!(img->range = malloc(sizeof *img->range))) { 1.51 + return -1; 1.52 + } 1.53 + img->num_ranges = 1; 1.54 + img->range[0].low = 0; 1.55 + img->range[0].high = 255; 1.56 + img->range[0].cmode = CYCLE_NORMAL; 1.57 + img->range[0].rate = 5000; 1.58 + 1.59 + if(!(img->pixels = malloc(img->width * img->height))) { 1.60 + free(img->range); 1.61 + return -1; 1.62 + } 1.63 + 1.64 + for(i=0; i<256; i++) { 1.65 + float theta = M_PI * 2.0 * (float)i / 256.0; 1.66 + float r = cos(theta) * 0.5 + 0.5; 1.67 + float g = sin(theta) * 0.5 + 0.5; 1.68 + float b = -cos(theta) * 0.5 + 0.5; 1.69 + img->palette[i].r = (int)(r * 255.0); 1.70 + img->palette[i].g = (int)(g * 255.0); 1.71 + img->palette[i].b = (int)(b * 255.0); 1.72 + } 1.73 + 1.74 + pptr = img->pixels; 1.75 + for(i=0; i<img->height; i++) { 1.76 + int c = (i << 8) / img->height; 1.77 + for(j=0; j<img->width; j++) { 1.78 + int chess = ((i >> 6) & 1) == ((j >> 6) & 1); 1.79 + *pptr++ = (chess ? c : c + 128) & 0xff; 1.80 + } 1.81 + } 1.82 + return 0; 1.83 +} 1.84 + 1.85 +#define MAX_TOKEN_SIZE 256 1.86 +static int image_block(FILE *fp, struct image *img); 1.87 +static int nextc = -1; 1.88 +static char token[MAX_TOKEN_SIZE]; 1.89 + 1.90 +int load_image(struct image *img, const char *fname) 1.91 +{ 1.92 + FILE *fp; 1.93 + int c; 1.94 + 1.95 + if(!(fp = fopen(fname, "rb"))) { 1.96 + fprintf(stderr, "failed to open file: %s: %s\n", fname, strerror(errno)); 1.97 + return -1; 1.98 + } 1.99 + 1.100 + if(file_is_lbm(fp)) { 1.101 + if(load_image_lbm(img, fp) == -1) { 1.102 + fclose(fp); 1.103 + return -1; 1.104 + } 1.105 + fclose(fp); 1.106 + flatten_crange_list(img); 1.107 + return 0; 1.108 + } 1.109 + 1.110 + /* find the start of the root block */ 1.111 + while((c = fgetc(fp)) != -1 && c != '{'); 1.112 + if(feof(fp)) { 1.113 + fprintf(stderr, "invalid image format, no image block found: %s\n", fname); 1.114 + return -1; 1.115 + } 1.116 + 1.117 + nextc = c; /* prime the pump with the extra char we read above */ 1.118 + if(image_block(fp, img) == -1) { 1.119 + fprintf(stderr, "failed to read image block from: %s\n", fname); 1.120 + fclose(fp); 1.121 + return -1; 1.122 + } 1.123 + fclose(fp); 1.124 + 1.125 + flatten_crange_list(img); 1.126 + return 0; 1.127 +} 1.128 + 1.129 +void destroy_image(struct image *img) 1.130 +{ 1.131 + if(img) { 1.132 + free(img->pixels); 1.133 + free(img->range); 1.134 + memset(img, 0, sizeof *img); 1.135 + } 1.136 +} 1.137 + 1.138 +static int flatten_crange_list(struct image *img) 1.139 +{ 1.140 + struct colrange *list = img->range; 1.141 + struct colrange *rptr; 1.142 + 1.143 + if(img->num_ranges <= 0) { 1.144 + return 0; 1.145 + } 1.146 + 1.147 + if(!(img->range = malloc(img->num_ranges * sizeof *img->range))) { 1.148 + perror("flatten_crange_list: failed to allocate range array\n"); 1.149 + return -1; 1.150 + } 1.151 + 1.152 + rptr = img->range; 1.153 + while(list) { 1.154 + struct colrange *rng = list; 1.155 + list = list->next; 1.156 + *rptr++ = *rng; 1.157 + free(rng); 1.158 + } 1.159 + return 0; 1.160 +} 1.161 + 1.162 +/* ---- parser ---- */ 1.163 + 1.164 +enum { 1.165 + TOKEN_NUM, 1.166 + TOKEN_NAME, 1.167 + TOKEN_STR 1.168 +}; 1.169 + 1.170 +static int next_char(FILE *fp) 1.171 +{ 1.172 + while((nextc = fgetc(fp)) != -1 && isspace(nextc)); 1.173 + return nextc; 1.174 +} 1.175 + 1.176 +static int next_token(FILE *fp) 1.177 +{ 1.178 + char *ptr; 1.179 + 1.180 + if(nextc == -1) { 1.181 + return -1; 1.182 + } 1.183 + 1.184 + switch(nextc) { 1.185 + case '{': 1.186 + case '}': 1.187 + case ',': 1.188 + case '[': 1.189 + case ']': 1.190 + case ':': 1.191 + token[0] = nextc; 1.192 + token[1] = 0; 1.193 + nextc = next_char(fp); 1.194 + return token[0]; 1.195 + 1.196 + case '\'': 1.197 + ptr = token; 1.198 + nextc = next_char(fp); 1.199 + while(nextc != -1 && nextc != '\'') { 1.200 + *ptr++ = nextc; 1.201 + nextc = fgetc(fp); 1.202 + } 1.203 + nextc = next_char(fp); 1.204 + return TOKEN_STR; 1.205 + 1.206 + default: 1.207 + break; 1.208 + } 1.209 + 1.210 + if(isalpha(nextc)) { 1.211 + ptr = token; 1.212 + while(nextc != -1 && isalpha(nextc)) { 1.213 + *ptr++ = nextc; 1.214 + nextc = next_char(fp); 1.215 + } 1.216 + *ptr = 0; 1.217 + return TOKEN_NAME; 1.218 + } 1.219 + if(isdigit(nextc)) { 1.220 + ptr = token; 1.221 + while(nextc != -1 && isdigit(nextc)) { 1.222 + *ptr++ = nextc; 1.223 + nextc = next_char(fp); 1.224 + } 1.225 + *ptr = 0; 1.226 + return TOKEN_NUM; 1.227 + } 1.228 + 1.229 + token[0] = nextc; 1.230 + token[1] = 0; 1.231 + fprintf(stderr, "next_token: unexpected character: %c\n", nextc); 1.232 + return -1; 1.233 +} 1.234 + 1.235 +static int expect(FILE *fp, int tok) 1.236 +{ 1.237 + if(next_token(fp) != tok) { 1.238 + return 0; 1.239 + } 1.240 + return 1; 1.241 +} 1.242 + 1.243 +static const char *toktypestr(int tok) 1.244 +{ 1.245 + static char buf[] = "' '"; 1.246 + switch(tok) { 1.247 + case TOKEN_NUM: 1.248 + return "number"; 1.249 + case TOKEN_NAME: 1.250 + return "name"; 1.251 + case TOKEN_STR: 1.252 + return "string"; 1.253 + default: 1.254 + break; 1.255 + } 1.256 + buf[1] = tok; 1.257 + return buf; 1.258 +} 1.259 + 1.260 +#define EXPECT(fp, x) \ 1.261 + do { \ 1.262 + if(!expect(fp, x)) { \ 1.263 + fprintf(stderr, "%s: expected: %s, found: %s\n", __FUNCTION__, toktypestr(x), token); \ 1.264 + return -1; \ 1.265 + } \ 1.266 + } while(0) 1.267 + 1.268 +static int palette(FILE *fp, struct image *img) 1.269 +{ 1.270 + int tok, cidx = 0; 1.271 + struct color col, *cptr; 1.272 + 1.273 + EXPECT(fp, '['); 1.274 + 1.275 + while((tok = next_token(fp)) == '[') { 1.276 + cptr = cidx < 256 ? &img->palette[cidx] : &col; 1.277 + ++cidx; 1.278 + 1.279 + EXPECT(fp, TOKEN_NUM); 1.280 + cptr->r = atoi(token); 1.281 + EXPECT(fp, ','); 1.282 + EXPECT(fp, TOKEN_NUM); 1.283 + cptr->g = atoi(token); 1.284 + EXPECT(fp, ','); 1.285 + EXPECT(fp, TOKEN_NUM); 1.286 + cptr->b = atoi(token); 1.287 + EXPECT(fp, ']'); 1.288 + 1.289 + if(nextc == ',') { 1.290 + next_token(fp); /* skip comma */ 1.291 + } 1.292 + } 1.293 + 1.294 + if(tok != ']') { 1.295 + fprintf(stderr, "palette must be closed by a ']' token\n"); 1.296 + return -1; 1.297 + } 1.298 + return 0; 1.299 +} 1.300 + 1.301 +static int crange(FILE *fp, struct colrange *rng) 1.302 +{ 1.303 + int val; 1.304 + char name[MAX_TOKEN_SIZE]; 1.305 + 1.306 + EXPECT(fp, '{'); 1.307 + 1.308 + while(nextc != -1 && nextc != '}') { 1.309 + EXPECT(fp, TOKEN_NAME); 1.310 + strcpy(name, token); 1.311 + EXPECT(fp, ':'); 1.312 + EXPECT(fp, TOKEN_NUM); 1.313 + val = atoi(token); 1.314 + 1.315 + if(strcmp(name, "reverse") == 0) { 1.316 + rng->cmode = val; 1.317 + } else if(strcmp(name, "rate") == 0) { 1.318 + rng->rate = val; 1.319 + } else if(strcmp(name, "low") == 0) { 1.320 + rng->low = val; 1.321 + } else if(strcmp(name, "high") == 0) { 1.322 + rng->high = val; 1.323 + } else { 1.324 + fprintf(stderr, "invalid attribute %s in cycles range\n", name); 1.325 + return -1; 1.326 + } 1.327 + 1.328 + if(nextc == ',') { 1.329 + next_token(fp); 1.330 + } 1.331 + } 1.332 + 1.333 + EXPECT(fp, '}'); 1.334 + return 0; 1.335 +} 1.336 + 1.337 +static int cycles(FILE *fp, struct image *img) 1.338 +{ 1.339 + struct colrange *list = 0, *rng; 1.340 + 1.341 + EXPECT(fp, '['); 1.342 + 1.343 + img->num_ranges = 0; 1.344 + while(nextc == '{') { 1.345 + if(!(rng = malloc(sizeof *rng))) { 1.346 + perror("failed to allocate color range"); 1.347 + goto err; 1.348 + } 1.349 + if(crange(fp, rng) == -1) { 1.350 + free(rng); 1.351 + goto err; 1.352 + } 1.353 + if(rng->low != rng->high && rng->rate > 0) { 1.354 + rng->next = list; 1.355 + list = rng; 1.356 + ++img->num_ranges; 1.357 + } else { 1.358 + free(rng); 1.359 + } 1.360 + 1.361 + if(nextc == ',') { 1.362 + next_token(fp); /* eat the comma */ 1.363 + } 1.364 + } 1.365 + 1.366 + img->range = list; 1.367 + 1.368 + if(!expect(fp, ']')) { 1.369 + fprintf(stderr, "cycles: missing closing bracket\n"); 1.370 + goto err; 1.371 + } 1.372 + return 0; 1.373 + 1.374 +err: 1.375 + while(list) { 1.376 + rng = list; 1.377 + list = list->next; 1.378 + free(rng); 1.379 + } 1.380 + img->num_ranges = 0; 1.381 + return -1; 1.382 +} 1.383 + 1.384 +static int pixels(FILE *fp, struct image *img) 1.385 +{ 1.386 + int tok, num_pixels; 1.387 + unsigned char *pptr; 1.388 + 1.389 + if(img->width <= 0 || img->height <= 0) { 1.390 + fprintf(stderr, "pixel block found before defining the image dimensions!\n"); 1.391 + return -1; 1.392 + } 1.393 + num_pixels = img->width * img->height; 1.394 + if(!(img->pixels = malloc(num_pixels))) { 1.395 + perror("failed to allocate pixels"); 1.396 + return -1; 1.397 + } 1.398 + pptr = img->pixels; 1.399 + 1.400 + EXPECT(fp, '['); 1.401 + 1.402 + while((tok = next_token(fp)) == TOKEN_NUM) { 1.403 + if(pptr - img->pixels >= num_pixels) { 1.404 + pptr = 0; 1.405 + fprintf(stderr, "more pixel data provided than the specified image dimensions\n"); 1.406 + } 1.407 + if(!pptr) continue; 1.408 + *pptr++ = atoi(token); 1.409 + 1.410 + if(nextc == ',') { 1.411 + next_token(fp); /* eat comma */ 1.412 + } 1.413 + } 1.414 + 1.415 + if(tok != ']') { 1.416 + printf("pixels: missing closing bracket\n"); 1.417 + return -1; 1.418 + } 1.419 + return 0; 1.420 +} 1.421 + 1.422 +static int attribute(FILE *fp, struct image *img) 1.423 +{ 1.424 + char *attr_name; 1.425 + 1.426 + if(!expect(fp, TOKEN_NAME)) { 1.427 + return -1; 1.428 + } 1.429 + attr_name = alloca(strlen(token) + 1); 1.430 + strcpy(attr_name, token); 1.431 + 1.432 + if(!expect(fp, ':')) { 1.433 + return -1; 1.434 + } 1.435 + 1.436 + if(strcmp(attr_name, "filename") == 0) { 1.437 + if(!expect(fp, TOKEN_STR)) { 1.438 + fprintf(stderr, "attribute: filename should be a string\n"); 1.439 + return -1; 1.440 + } 1.441 + 1.442 + } else if(strcmp(attr_name, "width") == 0 || strcmp(attr_name, "height") == 0) { 1.443 + int *dst = attr_name[0] == 'w' ? &img->width : &img->height; 1.444 + if(!expect(fp, TOKEN_NUM)) { 1.445 + fprintf(stderr, "attribute: %s should be a number\n", attr_name); 1.446 + return -1; 1.447 + } 1.448 + *dst = atoi(token); 1.449 + 1.450 + } else if(strcmp(attr_name, "colors") == 0) { 1.451 + if(palette(fp, img) == -1) { 1.452 + fprintf(stderr, "attribute: colors should be a palette\n"); 1.453 + return -1; 1.454 + } 1.455 + 1.456 + } else if(strcmp(attr_name, "cycles") == 0) { 1.457 + if(cycles(fp, img) == -1) { 1.458 + fprintf(stderr, "attribute: cycles should be a list of palranges\n"); 1.459 + return -1; 1.460 + } 1.461 + 1.462 + } else if(strcmp(attr_name, "pixels") == 0) { 1.463 + if(pixels(fp, img) == -1) { 1.464 + fprintf(stderr, "attribute: pixels should be a list of indices\n"); 1.465 + return -1; 1.466 + } 1.467 + 1.468 + } else { 1.469 + fprintf(stderr, "unknown attribute: %s\n", attr_name); 1.470 + return -1; 1.471 + } 1.472 + return 0; 1.473 +} 1.474 + 1.475 +static int image_block(FILE *fp, struct image *img) 1.476 +{ 1.477 + EXPECT(fp, '{'); 1.478 + 1.479 + img->width = img->height = -1; 1.480 + img->bpp = 8; 1.481 + 1.482 + while(attribute(fp, img) != -1) { 1.483 + if(nextc == ',') { 1.484 + next_token(fp); /* eat comma */ 1.485 + } 1.486 + } 1.487 + return 0; 1.488 +}