lbm2bin
changeset 0:bfd8aa2ca393
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 15 Jul 2017 13:26:32 +0300 |
parents | |
children | 2b02b0f288b5 |
files | Makefile src/image.c src/image.h src/imagelbm.c src/imagelbm.h src/main.c |
diffstat | 6 files changed, 1198 insertions(+), 0 deletions(-) [+] |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/Makefile Sat Jul 15 13:26:32 2017 +0300 1.3 @@ -0,0 +1,13 @@ 1.4 +src = $(wildcard src/*.c) 1.5 +obj = $(src:.c=.o) 1.6 + 1.7 +bin = ilbm2bin 1.8 + 1.9 +LDFLAGS = -lm 1.10 + 1.11 +$(bin): $(obj) 1.12 + $(CC) -o $@ $(obj) $(LDFLAGS) 1.13 + 1.14 +.PHONY: clean 1.15 +clean: 1.16 + rm -f $(obj) $(bin)
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/src/image.c Sat Jul 15 13:26:32 2017 +0300 2.3 @@ -0,0 +1,485 @@ 2.4 +/* 2.5 +colcycle - color cycling image viewer 2.6 +Copyright (C) 2016-2017 John Tsiombikas <nuclear@member.fsf.org> 2.7 + 2.8 +This program is free software: you can redistribute it and/or modify 2.9 +it under the terms of the GNU General Public License as published by 2.10 +the Free Software Foundation, either version 3 of the License, or 2.11 +(at your option) any later version. 2.12 + 2.13 +This program is distributed in the hope that it will be useful, 2.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 2.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 2.16 +GNU General Public License for more details. 2.17 + 2.18 +You should have received a copy of the GNU General Public License 2.19 +along with this program. If not, see <http://www.gnu.org/licenses/>. 2.20 +*/ 2.21 +#include <stdio.h> 2.22 +#include <stdlib.h> 2.23 +#include <string.h> 2.24 +#include <errno.h> 2.25 +#include <ctype.h> 2.26 +#include <math.h> 2.27 +#if defined(__WATCOMC__) || defined(_MSC_VER) || defined(WIN32) 2.28 +#include <malloc.h> 2.29 +#else 2.30 +#include <alloca.h> 2.31 +#endif 2.32 +#include "image.h" 2.33 +#include "imagelbm.h" 2.34 + 2.35 +#ifndef M_PI 2.36 +#define M_PI 3.141593 2.37 +#endif 2.38 + 2.39 +static int flatten_crange_list(struct image *img); 2.40 + 2.41 +int gen_test_image(struct image *img) 2.42 +{ 2.43 + int i, j; 2.44 + unsigned char *pptr; 2.45 + 2.46 + img->width = 640; 2.47 + img->height = 480; 2.48 + img->bpp = 8; 2.49 + 2.50 + if(!(img->range = malloc(sizeof *img->range))) { 2.51 + return -1; 2.52 + } 2.53 + img->num_ranges = 1; 2.54 + img->range[0].low = 0; 2.55 + img->range[0].high = 255; 2.56 + img->range[0].cmode = CYCLE_NORMAL; 2.57 + img->range[0].rate = 5000; 2.58 + 2.59 + if(!(img->pixels = malloc(img->width * img->height))) { 2.60 + free(img->range); 2.61 + return -1; 2.62 + } 2.63 + 2.64 + for(i=0; i<256; i++) { 2.65 + float theta = M_PI * 2.0 * (float)i / 256.0; 2.66 + float r = cos(theta) * 0.5 + 0.5; 2.67 + float g = sin(theta) * 0.5 + 0.5; 2.68 + float b = -cos(theta) * 0.5 + 0.5; 2.69 + img->palette[i].r = (int)(r * 255.0); 2.70 + img->palette[i].g = (int)(g * 255.0); 2.71 + img->palette[i].b = (int)(b * 255.0); 2.72 + } 2.73 + 2.74 + pptr = img->pixels; 2.75 + for(i=0; i<img->height; i++) { 2.76 + int c = (i << 8) / img->height; 2.77 + for(j=0; j<img->width; j++) { 2.78 + int chess = ((i >> 6) & 1) == ((j >> 6) & 1); 2.79 + *pptr++ = (chess ? c : c + 128) & 0xff; 2.80 + } 2.81 + } 2.82 + return 0; 2.83 +} 2.84 + 2.85 +#define MAX_TOKEN_SIZE 256 2.86 +static int image_block(FILE *fp, struct image *img); 2.87 +static int nextc = -1; 2.88 +static char token[MAX_TOKEN_SIZE]; 2.89 + 2.90 +int load_image(struct image *img, const char *fname) 2.91 +{ 2.92 + FILE *fp; 2.93 + int c; 2.94 + 2.95 + if(!(fp = fopen(fname, "rb"))) { 2.96 + fprintf(stderr, "failed to open file: %s: %s\n", fname, strerror(errno)); 2.97 + return -1; 2.98 + } 2.99 + 2.100 + if(file_is_lbm(fp)) { 2.101 + if(load_image_lbm(img, fp) == -1) { 2.102 + fclose(fp); 2.103 + return -1; 2.104 + } 2.105 + fclose(fp); 2.106 + flatten_crange_list(img); 2.107 + return 0; 2.108 + } 2.109 + 2.110 + /* find the start of the root block */ 2.111 + while((c = fgetc(fp)) != -1 && c != '{'); 2.112 + if(feof(fp)) { 2.113 + fprintf(stderr, "invalid image format, no image block found: %s\n", fname); 2.114 + return -1; 2.115 + } 2.116 + 2.117 + nextc = c; /* prime the pump with the extra char we read above */ 2.118 + if(image_block(fp, img) == -1) { 2.119 + fprintf(stderr, "failed to read image block from: %s\n", fname); 2.120 + fclose(fp); 2.121 + return -1; 2.122 + } 2.123 + fclose(fp); 2.124 + 2.125 + flatten_crange_list(img); 2.126 + return 0; 2.127 +} 2.128 + 2.129 +void destroy_image(struct image *img) 2.130 +{ 2.131 + if(img) { 2.132 + free(img->pixels); 2.133 + free(img->range); 2.134 + memset(img, 0, sizeof *img); 2.135 + } 2.136 +} 2.137 + 2.138 +static int flatten_crange_list(struct image *img) 2.139 +{ 2.140 + struct colrange *list = img->range; 2.141 + struct colrange *rptr; 2.142 + 2.143 + if(img->num_ranges <= 0) { 2.144 + return 0; 2.145 + } 2.146 + 2.147 + if(!(img->range = malloc(img->num_ranges * sizeof *img->range))) { 2.148 + perror("flatten_crange_list: failed to allocate range array\n"); 2.149 + return -1; 2.150 + } 2.151 + 2.152 + rptr = img->range; 2.153 + while(list) { 2.154 + struct colrange *rng = list; 2.155 + list = list->next; 2.156 + *rptr++ = *rng; 2.157 + free(rng); 2.158 + } 2.159 + return 0; 2.160 +} 2.161 + 2.162 +/* ---- parser ---- */ 2.163 + 2.164 +enum { 2.165 + TOKEN_NUM, 2.166 + TOKEN_NAME, 2.167 + TOKEN_STR 2.168 +}; 2.169 + 2.170 +static int next_char(FILE *fp) 2.171 +{ 2.172 + while((nextc = fgetc(fp)) != -1 && isspace(nextc)); 2.173 + return nextc; 2.174 +} 2.175 + 2.176 +static int next_token(FILE *fp) 2.177 +{ 2.178 + char *ptr; 2.179 + 2.180 + if(nextc == -1) { 2.181 + return -1; 2.182 + } 2.183 + 2.184 + switch(nextc) { 2.185 + case '{': 2.186 + case '}': 2.187 + case ',': 2.188 + case '[': 2.189 + case ']': 2.190 + case ':': 2.191 + token[0] = nextc; 2.192 + token[1] = 0; 2.193 + nextc = next_char(fp); 2.194 + return token[0]; 2.195 + 2.196 + case '\'': 2.197 + ptr = token; 2.198 + nextc = next_char(fp); 2.199 + while(nextc != -1 && nextc != '\'') { 2.200 + *ptr++ = nextc; 2.201 + nextc = fgetc(fp); 2.202 + } 2.203 + nextc = next_char(fp); 2.204 + return TOKEN_STR; 2.205 + 2.206 + default: 2.207 + break; 2.208 + } 2.209 + 2.210 + if(isalpha(nextc)) { 2.211 + ptr = token; 2.212 + while(nextc != -1 && isalpha(nextc)) { 2.213 + *ptr++ = nextc; 2.214 + nextc = next_char(fp); 2.215 + } 2.216 + *ptr = 0; 2.217 + return TOKEN_NAME; 2.218 + } 2.219 + if(isdigit(nextc)) { 2.220 + ptr = token; 2.221 + while(nextc != -1 && isdigit(nextc)) { 2.222 + *ptr++ = nextc; 2.223 + nextc = next_char(fp); 2.224 + } 2.225 + *ptr = 0; 2.226 + return TOKEN_NUM; 2.227 + } 2.228 + 2.229 + token[0] = nextc; 2.230 + token[1] = 0; 2.231 + fprintf(stderr, "next_token: unexpected character: %c\n", nextc); 2.232 + return -1; 2.233 +} 2.234 + 2.235 +static int expect(FILE *fp, int tok) 2.236 +{ 2.237 + if(next_token(fp) != tok) { 2.238 + return 0; 2.239 + } 2.240 + return 1; 2.241 +} 2.242 + 2.243 +static const char *toktypestr(int tok) 2.244 +{ 2.245 + static char buf[] = "' '"; 2.246 + switch(tok) { 2.247 + case TOKEN_NUM: 2.248 + return "number"; 2.249 + case TOKEN_NAME: 2.250 + return "name"; 2.251 + case TOKEN_STR: 2.252 + return "string"; 2.253 + default: 2.254 + break; 2.255 + } 2.256 + buf[1] = tok; 2.257 + return buf; 2.258 +} 2.259 + 2.260 +#define EXPECT(fp, x) \ 2.261 + do { \ 2.262 + if(!expect(fp, x)) { \ 2.263 + fprintf(stderr, "%s: expected: %s, found: %s\n", __FUNCTION__, toktypestr(x), token); \ 2.264 + return -1; \ 2.265 + } \ 2.266 + } while(0) 2.267 + 2.268 +static int palette(FILE *fp, struct image *img) 2.269 +{ 2.270 + int tok, cidx = 0; 2.271 + struct color col, *cptr; 2.272 + 2.273 + EXPECT(fp, '['); 2.274 + 2.275 + while((tok = next_token(fp)) == '[') { 2.276 + cptr = cidx < 256 ? &img->palette[cidx] : &col; 2.277 + ++cidx; 2.278 + 2.279 + EXPECT(fp, TOKEN_NUM); 2.280 + cptr->r = atoi(token); 2.281 + EXPECT(fp, ','); 2.282 + EXPECT(fp, TOKEN_NUM); 2.283 + cptr->g = atoi(token); 2.284 + EXPECT(fp, ','); 2.285 + EXPECT(fp, TOKEN_NUM); 2.286 + cptr->b = atoi(token); 2.287 + EXPECT(fp, ']'); 2.288 + 2.289 + if(nextc == ',') { 2.290 + next_token(fp); /* skip comma */ 2.291 + } 2.292 + } 2.293 + 2.294 + if(tok != ']') { 2.295 + fprintf(stderr, "palette must be closed by a ']' token\n"); 2.296 + return -1; 2.297 + } 2.298 + return 0; 2.299 +} 2.300 + 2.301 +static int crange(FILE *fp, struct colrange *rng) 2.302 +{ 2.303 + int val; 2.304 + char name[MAX_TOKEN_SIZE]; 2.305 + 2.306 + EXPECT(fp, '{'); 2.307 + 2.308 + while(nextc != -1 && nextc != '}') { 2.309 + EXPECT(fp, TOKEN_NAME); 2.310 + strcpy(name, token); 2.311 + EXPECT(fp, ':'); 2.312 + EXPECT(fp, TOKEN_NUM); 2.313 + val = atoi(token); 2.314 + 2.315 + if(strcmp(name, "reverse") == 0) { 2.316 + rng->cmode = val; 2.317 + } else if(strcmp(name, "rate") == 0) { 2.318 + rng->rate = val; 2.319 + } else if(strcmp(name, "low") == 0) { 2.320 + rng->low = val; 2.321 + } else if(strcmp(name, "high") == 0) { 2.322 + rng->high = val; 2.323 + } else { 2.324 + fprintf(stderr, "invalid attribute %s in cycles range\n", name); 2.325 + return -1; 2.326 + } 2.327 + 2.328 + if(nextc == ',') { 2.329 + next_token(fp); 2.330 + } 2.331 + } 2.332 + 2.333 + EXPECT(fp, '}'); 2.334 + return 0; 2.335 +} 2.336 + 2.337 +static int cycles(FILE *fp, struct image *img) 2.338 +{ 2.339 + struct colrange *list = 0, *rng; 2.340 + 2.341 + EXPECT(fp, '['); 2.342 + 2.343 + img->num_ranges = 0; 2.344 + while(nextc == '{') { 2.345 + if(!(rng = malloc(sizeof *rng))) { 2.346 + perror("failed to allocate color range"); 2.347 + goto err; 2.348 + } 2.349 + if(crange(fp, rng) == -1) { 2.350 + free(rng); 2.351 + goto err; 2.352 + } 2.353 + if(rng->low != rng->high && rng->rate > 0) { 2.354 + rng->next = list; 2.355 + list = rng; 2.356 + ++img->num_ranges; 2.357 + } else { 2.358 + free(rng); 2.359 + } 2.360 + 2.361 + if(nextc == ',') { 2.362 + next_token(fp); /* eat the comma */ 2.363 + } 2.364 + } 2.365 + 2.366 + img->range = list; 2.367 + 2.368 + if(!expect(fp, ']')) { 2.369 + fprintf(stderr, "cycles: missing closing bracket\n"); 2.370 + goto err; 2.371 + } 2.372 + return 0; 2.373 + 2.374 +err: 2.375 + while(list) { 2.376 + rng = list; 2.377 + list = list->next; 2.378 + free(rng); 2.379 + } 2.380 + img->num_ranges = 0; 2.381 + return -1; 2.382 +} 2.383 + 2.384 +static int pixels(FILE *fp, struct image *img) 2.385 +{ 2.386 + int tok, num_pixels; 2.387 + unsigned char *pptr; 2.388 + 2.389 + if(img->width <= 0 || img->height <= 0) { 2.390 + fprintf(stderr, "pixel block found before defining the image dimensions!\n"); 2.391 + return -1; 2.392 + } 2.393 + num_pixels = img->width * img->height; 2.394 + if(!(img->pixels = malloc(num_pixels))) { 2.395 + perror("failed to allocate pixels"); 2.396 + return -1; 2.397 + } 2.398 + pptr = img->pixels; 2.399 + 2.400 + EXPECT(fp, '['); 2.401 + 2.402 + while((tok = next_token(fp)) == TOKEN_NUM) { 2.403 + if(pptr - img->pixels >= num_pixels) { 2.404 + pptr = 0; 2.405 + fprintf(stderr, "more pixel data provided than the specified image dimensions\n"); 2.406 + } 2.407 + if(!pptr) continue; 2.408 + *pptr++ = atoi(token); 2.409 + 2.410 + if(nextc == ',') { 2.411 + next_token(fp); /* eat comma */ 2.412 + } 2.413 + } 2.414 + 2.415 + if(tok != ']') { 2.416 + printf("pixels: missing closing bracket\n"); 2.417 + return -1; 2.418 + } 2.419 + return 0; 2.420 +} 2.421 + 2.422 +static int attribute(FILE *fp, struct image *img) 2.423 +{ 2.424 + char *attr_name; 2.425 + 2.426 + if(!expect(fp, TOKEN_NAME)) { 2.427 + return -1; 2.428 + } 2.429 + attr_name = alloca(strlen(token) + 1); 2.430 + strcpy(attr_name, token); 2.431 + 2.432 + if(!expect(fp, ':')) { 2.433 + return -1; 2.434 + } 2.435 + 2.436 + if(strcmp(attr_name, "filename") == 0) { 2.437 + if(!expect(fp, TOKEN_STR)) { 2.438 + fprintf(stderr, "attribute: filename should be a string\n"); 2.439 + return -1; 2.440 + } 2.441 + 2.442 + } else if(strcmp(attr_name, "width") == 0 || strcmp(attr_name, "height") == 0) { 2.443 + int *dst = attr_name[0] == 'w' ? &img->width : &img->height; 2.444 + if(!expect(fp, TOKEN_NUM)) { 2.445 + fprintf(stderr, "attribute: %s should be a number\n", attr_name); 2.446 + return -1; 2.447 + } 2.448 + *dst = atoi(token); 2.449 + 2.450 + } else if(strcmp(attr_name, "colors") == 0) { 2.451 + if(palette(fp, img) == -1) { 2.452 + fprintf(stderr, "attribute: colors should be a palette\n"); 2.453 + return -1; 2.454 + } 2.455 + 2.456 + } else if(strcmp(attr_name, "cycles") == 0) { 2.457 + if(cycles(fp, img) == -1) { 2.458 + fprintf(stderr, "attribute: cycles should be a list of palranges\n"); 2.459 + return -1; 2.460 + } 2.461 + 2.462 + } else if(strcmp(attr_name, "pixels") == 0) { 2.463 + if(pixels(fp, img) == -1) { 2.464 + fprintf(stderr, "attribute: pixels should be a list of indices\n"); 2.465 + return -1; 2.466 + } 2.467 + 2.468 + } else { 2.469 + fprintf(stderr, "unknown attribute: %s\n", attr_name); 2.470 + return -1; 2.471 + } 2.472 + return 0; 2.473 +} 2.474 + 2.475 +static int image_block(FILE *fp, struct image *img) 2.476 +{ 2.477 + EXPECT(fp, '{'); 2.478 + 2.479 + img->width = img->height = -1; 2.480 + img->bpp = 8; 2.481 + 2.482 + while(attribute(fp, img) != -1) { 2.483 + if(nextc == ',') { 2.484 + next_token(fp); /* eat comma */ 2.485 + } 2.486 + } 2.487 + return 0; 2.488 +}
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/src/image.h Sat Jul 15 13:26:32 2017 +0300 3.3 @@ -0,0 +1,54 @@ 3.4 +/* 3.5 +colcycle - color cycling image viewer 3.6 +Copyright (C) 2016 John Tsiombikas <nuclear@member.fsf.org> 3.7 + 3.8 +This program is free software: you can redistribute it and/or modify 3.9 +it under the terms of the GNU General Public License as published by 3.10 +the Free Software Foundation, either version 3 of the License, or 3.11 +(at your option) any later version. 3.12 + 3.13 +This program is distributed in the hope that it will be useful, 3.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 3.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 3.16 +GNU General Public License for more details. 3.17 + 3.18 +You should have received a copy of the GNU General Public License 3.19 +along with this program. If not, see <http://www.gnu.org/licenses/>. 3.20 +*/ 3.21 +#ifndef IMAGE_H_ 3.22 +#define IMAGE_H_ 3.23 + 3.24 +enum cycle_mode { 3.25 + CYCLE_NORMAL = 0, 3.26 + CYCLE_UNUSED, /* ? */ 3.27 + CYCLE_REVERSE = 2, 3.28 + CYCLE_PINGPONG = 3, 3.29 + CYCLE_SINE_HALF = 4, /* sine -> [0, range/2] */ 3.30 + CYCLE_SINE = 5 /* sine -> [0, range] */ 3.31 +}; 3.32 + 3.33 +struct color { 3.34 + unsigned char r, g, b; 3.35 +}; 3.36 + 3.37 +struct colrange { 3.38 + int low, high; 3.39 + int cmode; 3.40 + unsigned int rate; 3.41 + struct colrange *next; 3.42 +}; 3.43 + 3.44 +struct image { 3.45 + int width, height; 3.46 + int bpp; 3.47 + struct color palette[256]; 3.48 + struct colrange *range; 3.49 + int num_ranges; 3.50 + unsigned char *pixels; 3.51 +}; 3.52 + 3.53 +int gen_test_image(struct image *img); 3.54 +int load_image(struct image *img, const char *fname); 3.55 +void destroy_image(struct image *img); 3.56 + 3.57 +#endif /* IMAGE_H_ */
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/src/imagelbm.c Sat Jul 15 13:26:32 2017 +0300 4.3 @@ -0,0 +1,463 @@ 4.4 +/* 4.5 +colcycle - color cycling image viewer 4.6 +Copyright (C) 2016-2017 John Tsiombikas <nuclear@member.fsf.org> 4.7 + 4.8 +This program is free software: you can redistribute it and/or modify 4.9 +it under the terms of the GNU General Public License as published by 4.10 +the Free Software Foundation, either version 3 of the License, or 4.11 +(at your option) any later version. 4.12 + 4.13 +This program is distributed in the hope that it will be useful, 4.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 4.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 4.16 +GNU General Public License for more details. 4.17 + 4.18 +You should have received a copy of the GNU General Public License 4.19 +along with this program. If not, see <http://www.gnu.org/licenses/>. 4.20 +*/ 4.21 +#include <stdio.h> 4.22 +#include <stdlib.h> 4.23 +#include <assert.h> 4.24 +#include <inttypes.h> 4.25 +#include <string.h> 4.26 +#if defined(__WATCOMC__) || defined(_MSC_VER) 4.27 +#include <malloc.h> 4.28 +#else 4.29 +#include <alloca.h> 4.30 +#endif 4.31 +#include "imagelbm.h" 4.32 + 4.33 +#ifdef __GNUC__ 4.34 +#define PACKED __attribute__((packed)) 4.35 +#endif 4.36 + 4.37 +#ifdef __BYTE_ORDER__ 4.38 +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 4.39 +#define LENDIAN 4.40 +#else 4.41 +#define BENDIAN 4.42 +#endif 4.43 +#endif 4.44 + 4.45 +#define MKID(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) 4.46 + 4.47 +#define IS_IFF_CONTAINER(id) ((id) == IFF_FORM || (id) == IFF_CAT || (id) == IFF_LIST) 4.48 + 4.49 +enum { 4.50 + IFF_FORM = MKID('F', 'O', 'R', 'M'), 4.51 + IFF_CAT = MKID('C', 'A', 'T', ' '), 4.52 + IFF_LIST = MKID('L', 'I', 'S', 'T'), 4.53 + IFF_ILBM = MKID('I', 'L', 'B', 'M'), 4.54 + IFF_PBM = MKID('P', 'B', 'M', ' '), 4.55 + IFF_BMHD = MKID('B', 'M', 'H', 'D'), 4.56 + IFF_CMAP = MKID('C', 'M', 'A', 'P'), 4.57 + IFF_BODY = MKID('B', 'O', 'D', 'Y'), 4.58 + IFF_CRNG = MKID('C', 'R', 'N', 'G') 4.59 +}; 4.60 + 4.61 + 4.62 +struct chdr { 4.63 + uint32_t id; 4.64 + uint32_t size; 4.65 +}; 4.66 + 4.67 +#ifdef __WATCOMC__ 4.68 +#pragma push(pack, 1) 4.69 +#endif 4.70 +struct bitmap_header { 4.71 + uint16_t width, height; 4.72 + int16_t xoffs, yoffs; 4.73 + uint8_t nplanes; 4.74 + uint8_t masking; 4.75 + uint8_t compression; 4.76 + uint8_t padding; 4.77 + uint16_t colorkey; 4.78 + uint8_t aspect_num, aspect_denom; 4.79 + int16_t pgwidth, pgheight; 4.80 +} PACKED; 4.81 +#ifdef __WATCOMC__ 4.82 +#pragma pop(pack) 4.83 +#endif 4.84 + 4.85 +enum { 4.86 + MASK_NONE, 4.87 + MASK_PLANE, 4.88 + MASK_COLORKEY, 4.89 + MASK_LASSO 4.90 +}; 4.91 + 4.92 +struct crng { 4.93 + uint16_t padding; 4.94 + uint16_t rate; 4.95 + uint16_t flags; 4.96 + uint8_t low, high; 4.97 +}; 4.98 + 4.99 +enum { 4.100 + CRNG_ENABLE = 1, 4.101 + CRNG_REVERSE = 2 4.102 +}; 4.103 + 4.104 +static int read_header(FILE *fp, struct chdr *hdr); 4.105 +static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct image *img); 4.106 +static int read_bmhd(FILE *fp, struct bitmap_header *bmhd); 4.107 +static int read_crng(FILE *fp, struct crng *crng); 4.108 +static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct image *img); 4.109 +static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct image *img); 4.110 +static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width); 4.111 +static int read16(FILE *fp, uint16_t *res); 4.112 +static int read32(FILE *fp, uint32_t *res); 4.113 +static uint16_t swap16(uint16_t x); 4.114 +static uint32_t swap32(uint32_t x); 4.115 + 4.116 +int file_is_lbm(FILE *fp) 4.117 +{ 4.118 + uint32_t type; 4.119 + struct chdr hdr; 4.120 + 4.121 + while(read_header(fp, &hdr) != -1) { 4.122 + if(IS_IFF_CONTAINER(hdr.id)) { 4.123 + if(read32(fp, &type) == -1) { 4.124 + break; 4.125 + } 4.126 + 4.127 + if(type == IFF_ILBM || type == IFF_PBM ) { 4.128 + rewind(fp); 4.129 + return 1; 4.130 + } 4.131 + hdr.size -= sizeof type; /* so we will seek fwd correctly */ 4.132 + } 4.133 + fseek(fp, hdr.size, SEEK_CUR); 4.134 + } 4.135 + fseek(fp, 0, SEEK_SET); 4.136 + return 0; 4.137 +} 4.138 + 4.139 +void print_chunkid(uint32_t id) 4.140 +{ 4.141 + char str[5] = {0}; 4.142 +#ifdef LENDIAN 4.143 + id = swap32(id); 4.144 +#endif 4.145 + memcpy(str, &id, 4); 4.146 + puts(str); 4.147 +} 4.148 + 4.149 +int load_image_lbm(struct image *img, FILE *fp) 4.150 +{ 4.151 + uint32_t type; 4.152 + struct chdr hdr; 4.153 + 4.154 + while(read_header(fp, &hdr) != -1) { 4.155 + if(IS_IFF_CONTAINER(hdr.id)) { 4.156 + if(read32(fp, &type) == -1) { 4.157 + break; 4.158 + } 4.159 + hdr.size -= sizeof type; /* to compensate for having advanced 4 more bytes */ 4.160 + 4.161 + if(type == IFF_ILBM) { 4.162 + if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) { 4.163 + return -1; 4.164 + } 4.165 + return 0; 4.166 + } 4.167 + if(type == IFF_PBM) { 4.168 + if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) { 4.169 + return -1; 4.170 + } 4.171 + return 0; 4.172 + } 4.173 + } 4.174 + fseek(fp, hdr.size, SEEK_CUR); 4.175 + } 4.176 + return 0; 4.177 +} 4.178 + 4.179 +static int read_header(FILE *fp, struct chdr *hdr) 4.180 +{ 4.181 + if(fread(hdr, 1, sizeof *hdr, fp) < sizeof *hdr) { 4.182 + return -1; 4.183 + } 4.184 +#ifdef LENDIAN 4.185 + hdr->id = swap32(hdr->id); 4.186 + hdr->size = swap32(hdr->size); 4.187 +#endif 4.188 + return 0; 4.189 +} 4.190 + 4.191 +static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct image *img) 4.192 +{ 4.193 + int i, res = -1; 4.194 + struct chdr hdr; 4.195 + struct bitmap_header bmhd; 4.196 + struct crng crng; 4.197 + struct colrange *crnode; 4.198 + unsigned char pal[3 * 256]; 4.199 + unsigned char *pptr; 4.200 + long start = ftell(fp); 4.201 + 4.202 + memset(img, 0, sizeof *img); 4.203 + 4.204 + while(read_header(fp, &hdr) != -1 && ftell(fp) - start < size) { 4.205 + switch(hdr.id) { 4.206 + case IFF_BMHD: 4.207 + assert(hdr.size == 20); 4.208 + if(read_bmhd(fp, &bmhd) == -1) { 4.209 + return -1; 4.210 + } 4.211 + img->width = bmhd.width; 4.212 + img->height = bmhd.height; 4.213 + img->bpp = bmhd.nplanes; 4.214 + if(bmhd.nplanes > 8) { 4.215 + fprintf(stderr, "%d planes found, only paletized LBM files supported\n", bmhd.nplanes); 4.216 + return -1; 4.217 + } 4.218 + if(!(img->pixels = malloc(img->width * img->height))) { 4.219 + fprintf(stderr, "failed to allocate %dx%d image\n", img->width, img->height); 4.220 + return -1; 4.221 + } 4.222 + break; 4.223 + 4.224 + case IFF_CMAP: 4.225 + assert(hdr.size / 3 <= 256); 4.226 + 4.227 + if(fread(pal, 1, hdr.size, fp) < hdr.size) { 4.228 + fprintf(stderr, "failed to read colormap\n"); 4.229 + return -1; 4.230 + } 4.231 + pptr = pal; 4.232 + for(i=0; i<256; i++) { 4.233 + img->palette[i].r = *pptr++; 4.234 + img->palette[i].g = *pptr++; 4.235 + img->palette[i].b = *pptr++; 4.236 + } 4.237 + break; 4.238 + 4.239 + case IFF_CRNG: 4.240 + assert(hdr.size == sizeof crng); 4.241 + 4.242 + if(read_crng(fp, &crng) == -1) { 4.243 + fprintf(stderr, "failed to read color cycling range chunk\n"); 4.244 + return -1; 4.245 + } 4.246 + if(crng.low != crng.high && crng.rate > 0) { 4.247 + if(!(crnode = malloc(sizeof *crnode))) { 4.248 + fprintf(stderr, "failed to allocate color range node\n"); 4.249 + return -1; 4.250 + } 4.251 + crnode->low = crng.low; 4.252 + crnode->high = crng.high; 4.253 + crnode->cmode = (crng.flags & CRNG_REVERSE) ? CYCLE_REVERSE : CYCLE_NORMAL; 4.254 + crnode->rate = crng.rate; 4.255 + crnode->next = img->range; 4.256 + img->range = crnode; 4.257 + ++img->num_ranges; 4.258 + } 4.259 + break; 4.260 + 4.261 + case IFF_BODY: 4.262 + if(!img->pixels) { 4.263 + fprintf(stderr, "malformed ILBM image: encountered BODY chunk before BMHD\n"); 4.264 + return -1; 4.265 + } 4.266 + if(type == IFF_ILBM) { 4.267 + if(read_body_ilbm(fp, &bmhd, img) == -1) { 4.268 + fprintf(stderr, "failed to read interleaved pixel data\n"); 4.269 + return -1; 4.270 + } 4.271 + } else { 4.272 + assert(type == IFF_PBM); 4.273 + if(read_body_pbm(fp, &bmhd, img) == -1) { 4.274 + fprintf(stderr, "failed to read linear pixel data\n"); 4.275 + return -1; 4.276 + } 4.277 + } 4.278 + res = 0; /* sucessfully read image */ 4.279 + break; 4.280 + 4.281 + default: 4.282 + /* skip unknown chunks */ 4.283 + fseek(fp, hdr.size, SEEK_CUR); 4.284 + if(ftell(fp) & 1) { 4.285 + /* chunks must start at even offsets */ 4.286 + fseek(fp, 1, SEEK_CUR); 4.287 + } 4.288 + } 4.289 + } 4.290 + 4.291 + return res; 4.292 +} 4.293 + 4.294 + 4.295 +static int read_bmhd(FILE *fp, struct bitmap_header *bmhd) 4.296 +{ 4.297 + if(fread(bmhd, sizeof *bmhd, 1, fp) < 1) { 4.298 + return -1; 4.299 + } 4.300 +#ifdef LENDIAN 4.301 + bmhd->width = swap16(bmhd->width); 4.302 + bmhd->height = swap16(bmhd->height); 4.303 + bmhd->xoffs = swap16(bmhd->xoffs); 4.304 + bmhd->yoffs = swap16(bmhd->yoffs); 4.305 + bmhd->colorkey = swap16(bmhd->colorkey); 4.306 + bmhd->pgwidth = swap16(bmhd->pgwidth); 4.307 + bmhd->pgheight = swap16(bmhd->pgheight); 4.308 +#endif 4.309 + return 0; 4.310 +} 4.311 + 4.312 +static int read_crng(FILE *fp, struct crng *crng) 4.313 +{ 4.314 + if(fread(crng, sizeof *crng, 1, fp) < 1) { 4.315 + return -1; 4.316 + } 4.317 +#ifdef LENDIAN 4.318 + crng->rate = swap16(crng->rate); 4.319 + crng->flags = swap16(crng->flags); 4.320 +#endif 4.321 + return 0; 4.322 +} 4.323 + 4.324 +/* scanline: [bp0 row][bp1 row]...[bpN-1 row][opt mask row] 4.325 + * each uncompressed row is width / 8 bytes 4.326 + */ 4.327 +static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct image *img) 4.328 +{ 4.329 + int i, j, k, bitidx; 4.330 + int rowsz = img->width / 8; 4.331 + unsigned char *src, *dest = img->pixels; 4.332 + unsigned char *rowbuf = alloca(rowsz); 4.333 + 4.334 + assert(bmhd->width == img->width); 4.335 + assert(bmhd->height == img->height); 4.336 + assert(img->pixels); 4.337 + 4.338 + for(i=0; i<img->height; i++) { 4.339 + 4.340 + memset(dest, 0, img->width); /* clear the whole scanline to OR bits into place */ 4.341 + 4.342 + for(j=0; j<bmhd->nplanes; j++) { 4.343 + // read a row corresponding to bitplane j 4.344 + if(bmhd->compression) { 4.345 + if(read_compressed_scanline(fp, rowbuf, rowsz) == -1) { 4.346 + return -1; 4.347 + } 4.348 + } else { 4.349 + if(fread(rowbuf, 1, rowsz, fp) < rowsz) { 4.350 + return -1; 4.351 + } 4.352 + } 4.353 + 4.354 + // distribute all bits across the linear output scanline 4.355 + src = rowbuf; 4.356 + bitidx = 0; 4.357 + 4.358 + for(k=0; k<img->width; k++) { 4.359 + dest[k] |= ((*src >> (7 - bitidx)) & 1) << j; 4.360 + 4.361 + if(++bitidx >= 8) { 4.362 + bitidx = 0; 4.363 + ++src; 4.364 + } 4.365 + } 4.366 + } 4.367 + 4.368 + if(bmhd->masking & MASK_PLANE) { 4.369 + /* skip the mask (1bpp) */ 4.370 + fseek(fp, rowsz, SEEK_CUR); 4.371 + } 4.372 + 4.373 + dest += img->width; 4.374 + } 4.375 + return 0; 4.376 +} 4.377 + 4.378 +static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct image *img) 4.379 +{ 4.380 + int i; 4.381 + int npixels = img->width * img->height; 4.382 + unsigned char *dptr = img->pixels; 4.383 + 4.384 + assert(bmhd->width == img->width); 4.385 + assert(bmhd->height == img->height); 4.386 + assert(img->pixels); 4.387 + 4.388 + if(bmhd->compression) { 4.389 + for(i=0; i<img->height; i++) { 4.390 + if(read_compressed_scanline(fp, dptr, img->width) == -1) { 4.391 + return -1; 4.392 + } 4.393 + dptr += img->width; 4.394 + } 4.395 + 4.396 + } else { 4.397 + /* uncompressed */ 4.398 + if(fread(img->pixels, 1, npixels, fp) < npixels) { 4.399 + return -1; 4.400 + } 4.401 + } 4.402 + return 0; 4.403 +} 4.404 + 4.405 +static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width) 4.406 +{ 4.407 + int i, count, x = 0; 4.408 + signed char ctl; 4.409 + 4.410 + while(x < width) { 4.411 + if(fread(&ctl, 1, 1, fp) < 1) return -1; 4.412 + 4.413 + if(ctl == -128) continue; 4.414 + 4.415 + if(ctl >= 0) { 4.416 + count = ctl + 1; 4.417 + if(fread(scanline, 1, count, fp) < count) return -1; 4.418 + scanline += count; 4.419 + 4.420 + } else { 4.421 + unsigned char pixel; 4.422 + count = 1 - ctl; 4.423 + if(fread(&pixel, 1, 1, fp) < 1) return -1; 4.424 + 4.425 + for(i=0; i<count; i++) { 4.426 + *scanline++ = pixel; 4.427 + } 4.428 + } 4.429 + 4.430 + x += count; 4.431 + } 4.432 + 4.433 + return 0; 4.434 +} 4.435 + 4.436 +static int read16(FILE *fp, uint16_t *res) 4.437 +{ 4.438 + if(fread(res, sizeof *res, 1, fp) < 1) { 4.439 + return -1; 4.440 + } 4.441 +#ifdef LENDIAN 4.442 + *res = swap16(*res); 4.443 +#endif 4.444 + return 0; 4.445 +} 4.446 + 4.447 +static int read32(FILE *fp, uint32_t *res) 4.448 +{ 4.449 + if(fread(res, sizeof *res, 1, fp) < 1) { 4.450 + return -1; 4.451 + } 4.452 +#ifdef LENDIAN 4.453 + *res = swap32(*res); 4.454 +#endif 4.455 + return 0; 4.456 +} 4.457 + 4.458 +static uint16_t swap16(uint16_t x) 4.459 +{ 4.460 + return (x << 8) | (x >> 8); 4.461 +} 4.462 + 4.463 +static uint32_t swap32(uint32_t x) 4.464 +{ 4.465 + return (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24); 4.466 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/src/imagelbm.h Sat Jul 15 13:26:32 2017 +0300 5.3 @@ -0,0 +1,27 @@ 5.4 +/* 5.5 +colcycle - color cycling image viewer 5.6 +Copyright (C) 2016-2017 John Tsiombikas <nuclear@member.fsf.org> 5.7 + 5.8 +This program is free software: you can redistribute it and/or modify 5.9 +it under the terms of the GNU General Public License as published by 5.10 +the Free Software Foundation, either version 3 of the License, or 5.11 +(at your option) any later version. 5.12 + 5.13 +This program is distributed in the hope that it will be useful, 5.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 5.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 5.16 +GNU General Public License for more details. 5.17 + 5.18 +You should have received a copy of the GNU General Public License 5.19 +along with this program. If not, see <http://www.gnu.org/licenses/>. 5.20 +*/ 5.21 +#ifndef IMAGE_LBM_H_ 5.22 +#define IMAGE_LBM_H_ 5.23 + 5.24 +#include <stdio.h> 5.25 +#include "image.h" 5.26 + 5.27 +int file_is_lbm(FILE *fp); 5.28 +int load_image_lbm(struct image *img, FILE *fp); 5.29 + 5.30 +#endif /* IMAGE_LBM_H_ */
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/src/main.c Sat Jul 15 13:26:32 2017 +0300 6.3 @@ -0,0 +1,156 @@ 6.4 +#include <stdio.h> 6.5 +#include <stdlib.h> 6.6 +#include <string.h> 6.7 +#include <errno.h> 6.8 +#include "image.h" 6.9 + 6.10 +static int proc_image(const char *fname); 6.11 +static const char *modestr(int cmode); 6.12 +static void output_planar(FILE *fp, struct image *img); 6.13 +static void output_chunky(FILE *fp, struct image *img); 6.14 + 6.15 +static int planar = 0; 6.16 + 6.17 +int main(int argc, char **argv) 6.18 +{ 6.19 + int i; 6.20 + 6.21 + for(i=1; i<argc; i++) { 6.22 + if(argv[i][0] == '-') { 6.23 + if(argv[i][2] == 0) { 6.24 + switch(argv[i][1]) { 6.25 + case 'p': 6.26 + planar = 1; 6.27 + break; 6.28 + 6.29 + case 'c': 6.30 + planar = 0; 6.31 + break; 6.32 + 6.33 + case 'h': 6.34 + printf("usage: %s [options] <file 1> <file 2> ... <file n>\n", argv[0]); 6.35 + printf("Options:\n"); 6.36 + printf(" -p: output image in planar format (default)\n"); 6.37 + printf(" -c: output image in chunky format\n"); 6.38 + printf(" -h: print usage and exit\n"); 6.39 + return 0; 6.40 + 6.41 + default: 6.42 + fprintf(stderr, "invalid option: %s\n", argv[i]); 6.43 + return 1; 6.44 + } 6.45 + } else { 6.46 + fprintf(stderr, "invalid option: %s\n", argv[i]); 6.47 + } 6.48 + } else { 6.49 + if(proc_image(argv[i]) == -1) { 6.50 + return 1; 6.51 + } 6.52 + } 6.53 + } 6.54 + return 0; 6.55 +} 6.56 + 6.57 +static int proc_image(const char *fname) 6.58 +{ 6.59 + int i; 6.60 + struct image img; 6.61 + char *outfname, *suffix; 6.62 + FILE *fp; 6.63 + 6.64 + if(load_image(&img, fname) == -1) { 6.65 + fprintf(stderr, "failed to load image: %s\n", fname); 6.66 + return -1; 6.67 + } 6.68 + 6.69 + if(!(outfname = malloc(strlen(fname) + 4))) { 6.70 + perror("failed to allocate output filename"); 6.71 + return -1; 6.72 + } 6.73 + strcpy(outfname, fname); 6.74 + if(!(suffix = strrchr(outfname, '.'))) { 6.75 + suffix = outfname + strlen(outfname); 6.76 + } 6.77 + strcpy(suffix, ".bin"); 6.78 + 6.79 + printf("processing %s -> %s\n", fname, outfname); 6.80 + 6.81 + if(!(fp = fopen(outfname, "wb"))) { 6.82 + fprintf(stderr, "failed to open output file: %s: %s\n", outfname, strerror(errno)); 6.83 + destroy_image(&img); 6.84 + free(outfname); 6.85 + return -1; 6.86 + } 6.87 + 6.88 + printf(" bits per pixel: %d\n", img.bpp); 6.89 + if(img.num_ranges) { 6.90 + printf(" color ranges:\n"); 6.91 + for(i=0; i<img.num_ranges; i++) { 6.92 + printf(" [%d]: %d - %d, rate: %u, mode: %s\n", i, img.range[i].low, img.range[i].high, 6.93 + img.range[i].rate, modestr(img.range[i].cmode)); 6.94 + } 6.95 + } 6.96 + 6.97 + if(planar) { 6.98 + output_planar(fp, &img); 6.99 + } else { 6.100 + output_chunky(fp, &img); 6.101 + } 6.102 + 6.103 + free(outfname); 6.104 + fclose(fp); 6.105 + destroy_image(&img); 6.106 + return 0; 6.107 +} 6.108 + 6.109 +static void output_planar(FILE *fp, struct image *img) 6.110 +{ 6.111 + int i, x, y; 6.112 + unsigned char acc = 0; 6.113 + int acc_bit = 7; 6.114 + unsigned char *pptr = img->pixels; 6.115 + 6.116 + for(i=0; i<img->bpp; i++) { 6.117 + for(y=0; y<img->height; y++) { 6.118 + for(x=0; x<img->width; x++) { 6.119 + acc |= (((*pptr++ >> i) & 1) << acc_bit); 6.120 + if(--acc_bit == 0) { 6.121 + fputc(acc, fp); 6.122 + acc_bit = 7; 6.123 + acc = 0; 6.124 + } 6.125 + } 6.126 + } 6.127 + } 6.128 +} 6.129 + 6.130 +static void output_chunky(FILE *fp, struct image *img) 6.131 +{ 6.132 + int i, j; 6.133 + unsigned char *pptr = img->pixels; 6.134 + 6.135 + for(i=0; i<img->height; i++) { 6.136 + for(j=0; j<img->width; j++) { 6.137 + fputc(*pptr++, fp); 6.138 + } 6.139 + } 6.140 +} 6.141 + 6.142 +static const char *modestr(int cmode) 6.143 +{ 6.144 + switch(cmode) { 6.145 + case CYCLE_NORMAL: 6.146 + return "forward"; 6.147 + case CYCLE_REVERSE: 6.148 + return "reverse"; 6.149 + case CYCLE_PINGPONG: 6.150 + return "ping-pong"; 6.151 + case CYCLE_SINE_HALF: 6.152 + return "half-sine"; 6.153 + case CYCLE_SINE: 6.154 + return "sine"; 6.155 + default: 6.156 + break; 6.157 + } 6.158 + return "unknown"; 6.159 +}