lbm2bin

view src/image.c @ 4:0b96363742d3

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