gvsig-raster / libjni-potrace / trunk / libjni-potrace / src / main / native / jpotrace / greymap.c @ 1780
History | View | Annotate | Download (19.4 KB)
1 |
/* Copyright (C) 2001-2007 Peter Selinger.
|
---|---|
2 |
This file is part of Potrace. It is free software and it is covered
|
3 |
by the GNU General Public License. See the file COPYING for details. */
|
4 |
|
5 |
/* $Id: greymap.c 147 2007-04-09 00:44:09Z selinger $ */
|
6 |
|
7 |
/* Routines for manipulating greymaps, including reading pgm files. We
|
8 |
only deal with greymaps of depth 8 bits. */
|
9 |
|
10 |
#include <stdlib.h> |
11 |
#include <errno.h> |
12 |
#include <string.h> |
13 |
#include <math.h> |
14 |
|
15 |
#include "greymap.h" |
16 |
|
17 |
#define INTBITS (8*sizeof(int)) |
18 |
|
19 |
#define mod(a,n) ((a)>=(n) ? (a)%(n) : (a)>=0 ? (a) : (n)-1-(-1-(a))%(n)) |
20 |
|
21 |
static int gm_readbody_pnm(FILE *f, greymap_t **gmp, int magic); |
22 |
static int gm_readbody_bmp(FILE *f, greymap_t **gmp); |
23 |
|
24 |
/* ---------------------------------------------------------------------- */
|
25 |
/* basic greymap routines */
|
26 |
|
27 |
/* return new un-initialized greymap. NULL with errno on error */
|
28 |
|
29 |
greymap_t *gm_new(int w, int h) { |
30 |
greymap_t *gm; |
31 |
int errno_save;
|
32 |
|
33 |
gm = (greymap_t *) malloc(sizeof(greymap_t));
|
34 |
if (!gm) {
|
35 |
return NULL; |
36 |
} |
37 |
gm->w = w; |
38 |
gm->h = h; |
39 |
gm->map = (signed short int *) malloc(w*h*sizeof(signed short int)); |
40 |
if (!gm->map) {
|
41 |
errno_save = errno; |
42 |
free(gm); |
43 |
errno = errno_save; |
44 |
return NULL; |
45 |
} |
46 |
return gm;
|
47 |
} |
48 |
|
49 |
/* free the given greymap */
|
50 |
void gm_free(greymap_t *gm) {
|
51 |
if (gm) {
|
52 |
free(gm->map); |
53 |
} |
54 |
free(gm); |
55 |
} |
56 |
|
57 |
/* duplicate the given greymap. Return NULL on error with errno set. */
|
58 |
greymap_t *gm_dup(greymap_t *gm) { |
59 |
greymap_t *gm1 = gm_new(gm->w, gm->h); |
60 |
if (!gm1) {
|
61 |
return NULL; |
62 |
} |
63 |
memcpy(gm1->map, gm->map, gm->w*gm->h*2);
|
64 |
return gm1;
|
65 |
} |
66 |
|
67 |
/* clear the given greymap to color b. */
|
68 |
void gm_clear(greymap_t *gm, int b) { |
69 |
int i;
|
70 |
|
71 |
if (b==0) { |
72 |
memset(gm->map, 0, gm->w*gm->h*2); |
73 |
} else {
|
74 |
for (i=0; i<gm->w*gm->h; i++) { |
75 |
gm->map[i] = b; |
76 |
} |
77 |
} |
78 |
} |
79 |
|
80 |
/* ---------------------------------------------------------------------- */
|
81 |
/* routines for reading pnm streams */
|
82 |
|
83 |
/* read next character after whitespace and comments. Return EOF on
|
84 |
end of file or error. */
|
85 |
static int fgetc_ws(FILE *f) { |
86 |
int c;
|
87 |
|
88 |
while (1) { |
89 |
c = fgetc(f); |
90 |
if (c=='#') { |
91 |
while (1) { |
92 |
c = fgetc(f); |
93 |
if (c=='\n' || c==EOF) { |
94 |
break;
|
95 |
} |
96 |
} |
97 |
} |
98 |
/* space, tab, line feed, carriage return, form-feed */
|
99 |
if (c!=' ' && c!='\t' && c!='\r' && c!='\n' && c!=12) { |
100 |
return c;
|
101 |
} |
102 |
} |
103 |
} |
104 |
|
105 |
/* skip whitespace and comments, then read a non-negative decimal
|
106 |
number from a stream. Return -1 on EOF. Tolerate other errors (skip
|
107 |
bad characters). Do not the read any characters following the
|
108 |
number (put next character back into the stream) */
|
109 |
|
110 |
static int readnum(FILE *f) { |
111 |
int c;
|
112 |
int acc;
|
113 |
|
114 |
/* skip whitespace and comments */
|
115 |
while (1) { |
116 |
c = fgetc_ws(f); |
117 |
if (c==EOF) { |
118 |
return -1; |
119 |
} |
120 |
if (c>='0' && c<='9') { |
121 |
break;
|
122 |
} |
123 |
} |
124 |
|
125 |
/* first digit is already in c */
|
126 |
acc = c-'0';
|
127 |
while (1) { |
128 |
c = fgetc(f); |
129 |
if (c==EOF) { |
130 |
break;
|
131 |
} |
132 |
if (c<'0' || c>'9') { |
133 |
ungetc(c, f); |
134 |
break;
|
135 |
} |
136 |
acc *= 10;
|
137 |
acc += c-'0';
|
138 |
} |
139 |
return acc;
|
140 |
} |
141 |
|
142 |
/* similar to readnum, but read only a single 0 or 1, and do not read
|
143 |
any characters after it. */
|
144 |
|
145 |
static int readbit(FILE *f) { |
146 |
int c;
|
147 |
|
148 |
/* skip whitespace and comments */
|
149 |
while (1) { |
150 |
c = fgetc_ws(f); |
151 |
if (c==EOF) { |
152 |
return -1; |
153 |
} |
154 |
if (c>='0' && c<='1') { |
155 |
break;
|
156 |
} |
157 |
} |
158 |
|
159 |
return c-'0'; |
160 |
} |
161 |
|
162 |
/* ---------------------------------------------------------------------- */
|
163 |
|
164 |
/* read a PNM stream: P1-P6 format (see pnm(5)), or a BMP stream, and
|
165 |
convert the output to a greymap. Return greymap in *gmp. Return 0
|
166 |
on success, -1 on error with errno set, -2 on bad file format (with
|
167 |
error message in gm_read_error), and 1 on premature end of file, -3
|
168 |
on empty file (including files with only whitespace and comments),
|
169 |
-4 if wrong magic number. If the return value is >=0, *gmp is
|
170 |
valid. */
|
171 |
|
172 |
char *gm_read_error = NULL; |
173 |
|
174 |
int gm_read(FILE *f, greymap_t **gmp) {
|
175 |
int magic[2]; |
176 |
|
177 |
/* read magic number. We ignore whitespace and comments before the
|
178 |
magic, for the benefit of concatenated files in P1-P3 format.
|
179 |
Multiple P1-P3 images in a single file are not formally allowed
|
180 |
by the PNM standard, but there is no harm in being lenient. */
|
181 |
|
182 |
magic[0] = fgetc_ws(f);
|
183 |
if (magic[0] == EOF) { |
184 |
/* files which contain only comments and whitespace count as "empty" */
|
185 |
return -3; |
186 |
} |
187 |
magic[1] = fgetc(f);
|
188 |
if (magic[0] == 'P' && magic[1] >= '1' && magic[1] <= '6') { |
189 |
return gm_readbody_pnm(f, gmp, magic[1]); |
190 |
} |
191 |
if (magic[0] == 'B' && magic[1] == 'M') { |
192 |
return gm_readbody_bmp(f, gmp);
|
193 |
} |
194 |
return -4; |
195 |
} |
196 |
|
197 |
/* ---------------------------------------------------------------------- */
|
198 |
/* read PNM format */
|
199 |
|
200 |
/* read PNM stream after magic number. Return values as for gm_read */
|
201 |
static int gm_readbody_pnm(FILE *f, greymap_t **gmp, int magic) { |
202 |
greymap_t *gm; |
203 |
int x, y, i, j, b, b1, sum;
|
204 |
int bpr; /* bytes per row (as opposed to 4*gm->c) */ |
205 |
int w, h, max;
|
206 |
|
207 |
gm = NULL;
|
208 |
|
209 |
w = readnum(f); |
210 |
if (w<0) { |
211 |
goto format_error;
|
212 |
} |
213 |
|
214 |
h = readnum(f); |
215 |
if (h<0) { |
216 |
goto format_error;
|
217 |
} |
218 |
|
219 |
/* allocate greymap */
|
220 |
gm = gm_new(w, h); |
221 |
if (!gm) {
|
222 |
return -1; |
223 |
} |
224 |
|
225 |
/* zero it out */
|
226 |
gm_clear(gm, 0);
|
227 |
|
228 |
switch (magic) {
|
229 |
default:
|
230 |
/* not reached */
|
231 |
goto format_error;
|
232 |
|
233 |
case '1': |
234 |
/* read P1 format: PBM ascii */
|
235 |
|
236 |
for (y=h-1; y>=0; y--) { |
237 |
for (x=0; x<w; x++) { |
238 |
b = readbit(f); |
239 |
if (b<0) { |
240 |
goto eof;
|
241 |
} |
242 |
GM_UPUT(gm, x, y, b ? 0 : 255); |
243 |
} |
244 |
} |
245 |
break;
|
246 |
|
247 |
case '2': |
248 |
/* read P2 format: PGM ascii */
|
249 |
|
250 |
max = readnum(f); |
251 |
if (max<1) { |
252 |
goto format_error;
|
253 |
} |
254 |
|
255 |
for (y=h-1; y>=0; y--) { |
256 |
for (x=0; x<w; x++) { |
257 |
b = readnum(f); |
258 |
if (b<0) { |
259 |
goto eof;
|
260 |
} |
261 |
GM_UPUT(gm, x, y, b*255/max);
|
262 |
} |
263 |
} |
264 |
break;
|
265 |
|
266 |
case '3': |
267 |
/* read P3 format: PPM ascii */
|
268 |
|
269 |
max = readnum(f); |
270 |
if (max<1) { |
271 |
goto format_error;
|
272 |
} |
273 |
|
274 |
for (y=h-1; y>=0; y--) { |
275 |
for (x=0; x<w; x++) { |
276 |
sum = 0;
|
277 |
for (i=0; i<3; i++) { |
278 |
b = readnum(f); |
279 |
if (b<0) { |
280 |
goto eof;
|
281 |
} |
282 |
sum += b; |
283 |
} |
284 |
GM_UPUT(gm, x, y, sum*(255/3)/max); |
285 |
} |
286 |
} |
287 |
break;
|
288 |
|
289 |
case '4': |
290 |
/* read P4 format: PBM raw */
|
291 |
|
292 |
b = fgetc(f); /* read single white-space character after height */
|
293 |
if (b==EOF) { |
294 |
goto format_error;
|
295 |
} |
296 |
|
297 |
bpr = (w+7)/8; |
298 |
|
299 |
for (y=h-1; y>=0; y--) { |
300 |
for (i=0; i<bpr; i++) { |
301 |
b = fgetc(f); |
302 |
if (b==EOF) { |
303 |
goto eof;
|
304 |
} |
305 |
for (j=0; j<8; j++) { |
306 |
GM_PUT(gm, i*8+j, y, b & (0x80 >> j) ? 0 : 255); |
307 |
} |
308 |
} |
309 |
} |
310 |
break;
|
311 |
|
312 |
case '5': |
313 |
/* read P5 format: PGM raw */
|
314 |
|
315 |
max = readnum(f); |
316 |
if (max<1) { |
317 |
goto format_error;
|
318 |
} |
319 |
|
320 |
b = fgetc(f); /* read single white-space character after max */
|
321 |
if (b==EOF) { |
322 |
goto format_error;
|
323 |
} |
324 |
|
325 |
for (y=h-1; y>=0; y--) { |
326 |
for (x=0; x<w; x++) { |
327 |
b = fgetc(f); |
328 |
if (b==EOF) |
329 |
goto eof;
|
330 |
if (max>=256) { |
331 |
b <<= 8;
|
332 |
b1 = fgetc(f); |
333 |
if (b1==EOF) |
334 |
goto eof;
|
335 |
b |= b1; |
336 |
} |
337 |
GM_UPUT(gm, x, y, b*255/max);
|
338 |
} |
339 |
} |
340 |
break;
|
341 |
|
342 |
case '6': |
343 |
/* read P6 format: PPM raw */
|
344 |
|
345 |
max = readnum(f); |
346 |
if (max<1) { |
347 |
goto format_error;
|
348 |
} |
349 |
|
350 |
b = fgetc(f); /* read single white-space character after max */
|
351 |
if (b==EOF) { |
352 |
goto format_error;
|
353 |
} |
354 |
|
355 |
for (y=h-1; y>=0; y--) { |
356 |
for (x=0; x<w; x++) { |
357 |
sum = 0;
|
358 |
for (i=0; i<3; i++) { |
359 |
b = fgetc(f); |
360 |
if (b==EOF) { |
361 |
goto eof;
|
362 |
} |
363 |
if (max>=256) { |
364 |
b <<= 8;
|
365 |
b1 = fgetc(f); |
366 |
if (b1==EOF) |
367 |
goto eof;
|
368 |
b |= b1; |
369 |
} |
370 |
sum += b; |
371 |
} |
372 |
GM_UPUT(gm, x, y, sum*(255/3)/max); |
373 |
} |
374 |
} |
375 |
break;
|
376 |
} |
377 |
|
378 |
*gmp = gm; |
379 |
return 0; |
380 |
|
381 |
eof:
|
382 |
*gmp = gm; |
383 |
return 1; |
384 |
|
385 |
format_error:
|
386 |
gm_free(gm); |
387 |
if (magic == '1' || magic == '4') { |
388 |
gm_read_error = "invalid pbm file";
|
389 |
} else if (magic == '2' || magic == '5') { |
390 |
gm_read_error = "invalid pgm file";
|
391 |
} else {
|
392 |
gm_read_error = "invalid ppm file";
|
393 |
} |
394 |
return -2; |
395 |
} |
396 |
|
397 |
/* ---------------------------------------------------------------------- */
|
398 |
/* read BMP format */
|
399 |
|
400 |
struct bmp_info_s {
|
401 |
unsigned int FileSize; |
402 |
unsigned int reserved; |
403 |
unsigned int DataOffset; |
404 |
unsigned int InfoSize; |
405 |
unsigned int w; /* width */ |
406 |
unsigned int h; /* height */ |
407 |
unsigned int Planes; |
408 |
unsigned int bits; /* bits per sample */ |
409 |
unsigned int comp; /* compression mode */ |
410 |
unsigned int ImageSize; |
411 |
unsigned int XpixelsPerM; |
412 |
unsigned int YpixelsPerM; |
413 |
unsigned int ncolors; /* number of colors in palette */ |
414 |
unsigned int ColorsImportant; |
415 |
unsigned int ctbits; /* sample size for color table */ |
416 |
}; |
417 |
typedef struct bmp_info_s bmp_info_t; |
418 |
|
419 |
/* auxiliary */
|
420 |
|
421 |
static int bmp_count = 0; /* counter for byte padding */ |
422 |
static int bmp_pos = 0; /* counter from start of BMP data */ |
423 |
|
424 |
/* read n-byte little-endian integer. Return 1 on EOF or error, else
|
425 |
0. Assume n<=4. */
|
426 |
static int bmp_readint(FILE *f, int n, unsigned int *p) { |
427 |
int i;
|
428 |
unsigned int sum = 0; |
429 |
int b;
|
430 |
|
431 |
for (i=0; i<n; i++) { |
432 |
b = fgetc(f); |
433 |
if (b==EOF) { |
434 |
return 1; |
435 |
} |
436 |
sum += b << (8*i);
|
437 |
} |
438 |
bmp_count += n; |
439 |
bmp_pos += n; |
440 |
*p = sum; |
441 |
return 0; |
442 |
} |
443 |
|
444 |
/* reset padding boundary */
|
445 |
static void bmp_pad_reset(void) { |
446 |
bmp_count = 0;
|
447 |
} |
448 |
|
449 |
/* read padding bytes to 4-byte boundary. Return 1 on EOF or error,
|
450 |
else 0. */
|
451 |
static int bmp_pad(FILE *f) { |
452 |
int c, i, b;
|
453 |
|
454 |
c = (-bmp_count) & 3;
|
455 |
for (i=0; i<c; i++) { |
456 |
b = fgetc(f); |
457 |
if (b==EOF) { |
458 |
return 1; |
459 |
} |
460 |
} |
461 |
bmp_pos += c; |
462 |
bmp_count = 0;
|
463 |
return 0; |
464 |
} |
465 |
|
466 |
/* forward to the new file position. Return 1 on EOF or error, else 0 */
|
467 |
static int bmp_forward(FILE *f, int pos) { |
468 |
int b;
|
469 |
|
470 |
while (bmp_pos < pos) {
|
471 |
b = fgetc(f); |
472 |
if (b==EOF) { |
473 |
return 1; |
474 |
} |
475 |
bmp_pos++; |
476 |
bmp_count++; |
477 |
} |
478 |
return 0; |
479 |
} |
480 |
|
481 |
#define TRY(x) if (x) goto try_error |
482 |
#define TRY_EOF(x) if (x) goto eof |
483 |
|
484 |
/* read BMP stream after magic number. Return values as for gm_read.
|
485 |
We choose to be as permissive as possible, since there are many
|
486 |
programs out there which produce BMP. For instance, ppmtobmp can
|
487 |
produce codings with anywhere from 1-8 or 24 bits per sample,
|
488 |
although most specifications only allow 1,4,8,24,32. We can also
|
489 |
read both the old and new OS/2 BMP formats in addition to the
|
490 |
Windows BMP format. */
|
491 |
static int gm_readbody_bmp(FILE *f, greymap_t **gmp) { |
492 |
bmp_info_t bmpinfo; |
493 |
int *coltable;
|
494 |
unsigned int b, c; |
495 |
unsigned int i, j; |
496 |
greymap_t *gm; |
497 |
unsigned int x, y; |
498 |
int col[2]; |
499 |
unsigned int bitbuf; |
500 |
unsigned int n; |
501 |
|
502 |
gm_read_error = NULL;
|
503 |
gm = NULL;
|
504 |
coltable = NULL;
|
505 |
|
506 |
bmp_pos = 2; /* set file position */ |
507 |
|
508 |
/* file header (minus magic number) */
|
509 |
TRY(bmp_readint(f, 4, &bmpinfo.FileSize));
|
510 |
TRY(bmp_readint(f, 4, &bmpinfo.reserved));
|
511 |
TRY(bmp_readint(f, 4, &bmpinfo.DataOffset));
|
512 |
|
513 |
/* info header */
|
514 |
TRY(bmp_readint(f, 4, &bmpinfo.InfoSize));
|
515 |
if (bmpinfo.InfoSize == 40 || bmpinfo.InfoSize == 64) { |
516 |
/* Windows or new OS/2 format */
|
517 |
bmpinfo.ctbits = 32; /* sample size in color table */ |
518 |
TRY(bmp_readint(f, 4, &bmpinfo.w));
|
519 |
TRY(bmp_readint(f, 4, &bmpinfo.h));
|
520 |
TRY(bmp_readint(f, 2, &bmpinfo.Planes));
|
521 |
TRY(bmp_readint(f, 2, &bmpinfo.bits));
|
522 |
TRY(bmp_readint(f, 4, &bmpinfo.comp));
|
523 |
TRY(bmp_readint(f, 4, &bmpinfo.ImageSize));
|
524 |
TRY(bmp_readint(f, 4, &bmpinfo.XpixelsPerM));
|
525 |
TRY(bmp_readint(f, 4, &bmpinfo.YpixelsPerM));
|
526 |
TRY(bmp_readint(f, 4, &bmpinfo.ncolors));
|
527 |
TRY(bmp_readint(f, 4, &bmpinfo.ColorsImportant));
|
528 |
} else if (bmpinfo.InfoSize == 12) { |
529 |
/* old OS/2 format */
|
530 |
bmpinfo.ctbits = 24; /* sample size in color table */ |
531 |
TRY(bmp_readint(f, 2, &bmpinfo.w));
|
532 |
TRY(bmp_readint(f, 2, &bmpinfo.h));
|
533 |
TRY(bmp_readint(f, 2, &bmpinfo.Planes));
|
534 |
TRY(bmp_readint(f, 2, &bmpinfo.bits));
|
535 |
bmpinfo.comp = 0;
|
536 |
bmpinfo.ncolors = 0;
|
537 |
} else {
|
538 |
goto format_error;
|
539 |
} |
540 |
|
541 |
/* forward to color table (i.e., if bmpinfo.InfoSize == 64) */
|
542 |
TRY(bmp_forward(f, 14+bmpinfo.InfoSize));
|
543 |
|
544 |
if (bmpinfo.Planes != 1) { |
545 |
gm_read_error = "cannot handle bmp planes";
|
546 |
goto format_error; /* can't handle planes */ |
547 |
} |
548 |
|
549 |
if (bmpinfo.ncolors == 0) { |
550 |
bmpinfo.ncolors = 1 << bmpinfo.bits;
|
551 |
} |
552 |
|
553 |
/* color table, present only if bmpinfo.bits <= 8. */
|
554 |
if (bmpinfo.bits <= 8) { |
555 |
coltable = (int *) malloc(bmpinfo.ncolors * sizeof(int)); |
556 |
if (!coltable) {
|
557 |
goto std_error;
|
558 |
} |
559 |
/* NOTE: since we are reading a greymap, we can immediately convert
|
560 |
the color table entries to grey values. */
|
561 |
for (i=0; i<bmpinfo.ncolors; i++) { |
562 |
TRY(bmp_readint(f, bmpinfo.ctbits/8, &c));
|
563 |
c = ((c>>16) & 0xff) + ((c>>8) & 0xff) + (c & 0xff); |
564 |
coltable[i] = c/3;
|
565 |
} |
566 |
} |
567 |
|
568 |
/* forward to data */
|
569 |
if (bmpinfo.InfoSize != 12) { /* not old OS/2 format */ |
570 |
TRY(bmp_forward(f, bmpinfo.DataOffset)); |
571 |
} |
572 |
|
573 |
/* allocate greymap */
|
574 |
gm = gm_new(bmpinfo.w, bmpinfo.h); |
575 |
if (!gm) {
|
576 |
goto std_error;
|
577 |
} |
578 |
|
579 |
/* zero it out */
|
580 |
gm_clear(gm, 0);
|
581 |
|
582 |
switch (bmpinfo.bits + 0x100*bmpinfo.comp) { |
583 |
|
584 |
default:
|
585 |
goto format_error;
|
586 |
break;
|
587 |
|
588 |
case 0x001: /* monochrome palette */ |
589 |
|
590 |
/* raster data */
|
591 |
for (y=0; y<bmpinfo.h; y++) { |
592 |
bmp_pad_reset(); |
593 |
for (i=0; 8*i<bmpinfo.w; i++) { |
594 |
TRY_EOF(bmp_readint(f, 1, &b));
|
595 |
for (j=0; j<8; j++) { |
596 |
GM_PUT(gm, i*8+j, y, b & (0x80 >> j) ? coltable[1] : coltable[0]); |
597 |
} |
598 |
} |
599 |
TRY(bmp_pad(f)); |
600 |
} |
601 |
break;
|
602 |
|
603 |
case 0x002: /* 2-bit to 8-bit palettes */ |
604 |
case 0x003: |
605 |
case 0x004: |
606 |
case 0x005: |
607 |
case 0x006: |
608 |
case 0x007: |
609 |
case 0x008: |
610 |
for (y=0; y<bmpinfo.h; y++) { |
611 |
bmp_pad_reset(); |
612 |
bitbuf = 0; /* bit buffer: bits in buffer are high-aligned */ |
613 |
n = 0; /* number of bits currently in bitbuffer */ |
614 |
for (x=0; x<bmpinfo.w; x++) { |
615 |
if (n < bmpinfo.bits) {
|
616 |
TRY_EOF(bmp_readint(f, 1, &b));
|
617 |
bitbuf |= b << (INTBITS - 8 - n);
|
618 |
n += 8;
|
619 |
} |
620 |
b = bitbuf >> (INTBITS - bmpinfo.bits); |
621 |
bitbuf <<= bmpinfo.bits; |
622 |
n -= bmpinfo.bits; |
623 |
GM_UPUT(gm, x, y, coltable[b]); |
624 |
} |
625 |
TRY(bmp_pad(f)); |
626 |
} |
627 |
break;
|
628 |
|
629 |
case 0x010: /* 16-bit encoding */ |
630 |
/* can't do this format because it is not well-documented and I
|
631 |
don't have any samples */
|
632 |
gm_read_error = "cannot handle bmp 16-bit coding";
|
633 |
goto format_error;
|
634 |
break;
|
635 |
|
636 |
case 0x018: /* 24-bit encoding */ |
637 |
case 0x020: /* 32-bit encoding */ |
638 |
for (y=0; y<bmpinfo.h; y++) { |
639 |
bmp_pad_reset(); |
640 |
for (x=0; x<bmpinfo.w; x++) { |
641 |
TRY_EOF(bmp_readint(f, bmpinfo.bits/8, &c));
|
642 |
c = ((c>>16) & 0xff) + ((c>>8) & 0xff) + (c & 0xff); |
643 |
GM_UPUT(gm, x, y, c/3);
|
644 |
} |
645 |
TRY(bmp_pad(f)); |
646 |
} |
647 |
break;
|
648 |
|
649 |
case 0x204: /* 4-bit runlength compressed encoding (RLE4) */ |
650 |
x = 0;
|
651 |
y = 0;
|
652 |
while (1) { |
653 |
TRY_EOF(bmp_readint(f, 1, &b)); /* opcode */ |
654 |
TRY_EOF(bmp_readint(f, 1, &c)); /* argument */ |
655 |
if (b>0) { |
656 |
/* repeat count */
|
657 |
col[0] = coltable[(c>>4) & 0xf]; |
658 |
col[1] = coltable[c & 0xf]; |
659 |
for (i=0; i<b && x<bmpinfo.w; i++) { |
660 |
if (x>=bmpinfo.w) {
|
661 |
x=0;
|
662 |
y++; |
663 |
} |
664 |
if (y>=bmpinfo.h) {
|
665 |
break;
|
666 |
} |
667 |
GM_UPUT(gm, x, y, col[i&1]);
|
668 |
x++; |
669 |
} |
670 |
} else if (c == 0) { |
671 |
/* end of line */
|
672 |
y++; |
673 |
x = 0;
|
674 |
} else if (c == 1) { |
675 |
/* end of greymap */
|
676 |
break;
|
677 |
} else if (c == 2) { |
678 |
/* "delta": skip pixels in x and y directions */
|
679 |
TRY_EOF(bmp_readint(f, 1, &b)); /* x offset */ |
680 |
TRY_EOF(bmp_readint(f, 1, &c)); /* y offset */ |
681 |
x += b; |
682 |
y += c; |
683 |
} else {
|
684 |
/* verbatim segment */
|
685 |
for (i=0; i<c; i++) { |
686 |
if ((i&1)==0) { |
687 |
TRY_EOF(bmp_readint(f, 1, &b));
|
688 |
} |
689 |
if (x>=bmpinfo.w) {
|
690 |
x=0;
|
691 |
y++; |
692 |
} |
693 |
if (y>=bmpinfo.h) {
|
694 |
break;
|
695 |
} |
696 |
GM_PUT(gm, x, y, coltable[(b>>(4-4*(i&1))) & 0xf]); |
697 |
x++; |
698 |
} |
699 |
if ((c+1) & 2) { |
700 |
/* pad to 16-bit boundary */
|
701 |
TRY_EOF(bmp_readint(f, 1, &b));
|
702 |
} |
703 |
} |
704 |
} |
705 |
break;
|
706 |
|
707 |
case 0x108: /* 8-bit runlength compressed encoding (RLE8) */ |
708 |
x = 0;
|
709 |
y = 0;
|
710 |
while (1) { |
711 |
TRY_EOF(bmp_readint(f, 1, &b)); /* opcode */ |
712 |
TRY_EOF(bmp_readint(f, 1, &c)); /* argument */ |
713 |
if (b>0) { |
714 |
/* repeat count */
|
715 |
for (i=0; i<b; i++) { |
716 |
if (x>=bmpinfo.w) {
|
717 |
x=0;
|
718 |
y++; |
719 |
} |
720 |
if (y>=bmpinfo.h) {
|
721 |
break;
|
722 |
} |
723 |
GM_UPUT(gm, x, y, coltable[c]); |
724 |
x++; |
725 |
} |
726 |
} else if (c == 0) { |
727 |
/* end of line */
|
728 |
y++; |
729 |
x = 0;
|
730 |
} else if (c == 1) { |
731 |
/* end of greymap */
|
732 |
break;
|
733 |
} else if (c == 2) { |
734 |
/* "delta": skip pixels in x and y directions */
|
735 |
TRY_EOF(bmp_readint(f, 1, &b)); /* x offset */ |
736 |
TRY_EOF(bmp_readint(f, 1, &c)); /* y offset */ |
737 |
x += b; |
738 |
y += c; |
739 |
} else {
|
740 |
/* verbatim segment */
|
741 |
for (i=0; i<c; i++) { |
742 |
TRY_EOF(bmp_readint(f, 1, &b));
|
743 |
if (x>=bmpinfo.w) {
|
744 |
x=0;
|
745 |
y++; |
746 |
} |
747 |
if (y>=bmpinfo.h) {
|
748 |
break;
|
749 |
} |
750 |
GM_PUT(gm, x, y, coltable[b]); |
751 |
x++; |
752 |
} |
753 |
if (c & 1) { |
754 |
/* pad input to 16-bit boundary */
|
755 |
TRY_EOF(bmp_readint(f, 1, &b));
|
756 |
} |
757 |
} |
758 |
} |
759 |
break;
|
760 |
|
761 |
} /* switch */
|
762 |
|
763 |
/* skip any potential junk after the data section, but don't
|
764 |
complain in case EOF is encountered */
|
765 |
bmp_forward(f, bmpinfo.FileSize); |
766 |
|
767 |
free(coltable); |
768 |
*gmp = gm; |
769 |
return 0; |
770 |
|
771 |
eof:
|
772 |
free(coltable); |
773 |
*gmp = gm; |
774 |
return 1; |
775 |
|
776 |
format_error:
|
777 |
try_error:
|
778 |
free(coltable); |
779 |
free(gm); |
780 |
if (!gm_read_error) {
|
781 |
gm_read_error = "invalid bmp file";
|
782 |
} |
783 |
return -2; |
784 |
|
785 |
std_error:
|
786 |
free(coltable); |
787 |
free(gm); |
788 |
return -1; |
789 |
} |
790 |
|
791 |
/* ---------------------------------------------------------------------- */
|
792 |
|
793 |
/* write a pgm stream, either P2 or (if raw != 0) P5 format. Include
|
794 |
one-line comment if non-NULL. Mode determines how out-of-range
|
795 |
color values are converted. Gamma is the desired gamma correction,
|
796 |
if any (set to 2.2 if the image is to look optimal on a CRT monitor,
|
797 |
2.8 for LCD). Set to 1.0 for no gamma correction */
|
798 |
|
799 |
int gm_writepgm(FILE *f, greymap_t *gm, char *comment, int raw, int mode, double gamma) { |
800 |
int x, y, v;
|
801 |
int gammatable[256]; |
802 |
|
803 |
/* prepare gamma correction lookup table */
|
804 |
if (gamma != 1.0) { |
805 |
gammatable[0] = 0; |
806 |
for (v=1; v<256; v++) { |
807 |
gammatable[v] = (int)(255 * exp(log(v/255.0)/gamma) + 0.5); |
808 |
} |
809 |
} else {
|
810 |
for (v=0; v<256; v++) { |
811 |
gammatable[v] = v; |
812 |
} |
813 |
} |
814 |
|
815 |
fprintf(f, raw ? "P5\n" : "P2\n"); |
816 |
if (comment && *comment) {
|
817 |
fprintf(f, "# %s\n", comment);
|
818 |
} |
819 |
fprintf(f, "%d %d 255\n", gm->w, gm->h);
|
820 |
for (y=gm->h-1; y>=0; y--) { |
821 |
for (x=0; x<gm->w; x++) { |
822 |
v = GM_UGET(gm, x, y); |
823 |
if (mode == GM_MODE_NONZERO) {
|
824 |
if (v > 255) { |
825 |
v = 510 - v;
|
826 |
} |
827 |
if (v < 0) { |
828 |
v = 0;
|
829 |
} |
830 |
} else if (mode == GM_MODE_ODD) { |
831 |
v = mod(v, 510);
|
832 |
if (v > 255) { |
833 |
v = 510 - v;
|
834 |
} |
835 |
} else if (mode == GM_MODE_POSITIVE) { |
836 |
if (v < 0) { |
837 |
v = 0;
|
838 |
} else if (v > 255) { |
839 |
v = 255;
|
840 |
} |
841 |
} else if (mode == GM_MODE_NEGATIVE) { |
842 |
v = 510 - v;
|
843 |
if (v < 0) { |
844 |
v = 0;
|
845 |
} else if (v > 255) { |
846 |
v = 255;
|
847 |
} |
848 |
} |
849 |
v = gammatable[v]; |
850 |
|
851 |
if (raw) {
|
852 |
fputc(v, f); |
853 |
} else {
|
854 |
fprintf(f, x == gm->w-1 ? "%d\n" : "%d ", v); |
855 |
} |
856 |
} |
857 |
} |
858 |
return 0; |
859 |
} |
860 |
|
861 |
/* ---------------------------------------------------------------------- */
|
862 |
/* output - for primitive debugging purposes only! */
|
863 |
|
864 |
/* print greymap to screen */
|
865 |
int gm_print(FILE *f, greymap_t *gm) {
|
866 |
int x, y;
|
867 |
int xx, yy;
|
868 |
int d, t;
|
869 |
int sw, sh;
|
870 |
|
871 |
sw = gm->w < 79 ? gm->w : 79; |
872 |
sh = gm->w < 79 ? gm->h : gm->h*sw*44/(79*gm->w); |
873 |
|
874 |
for (yy=sh-1; yy>=0; yy--) { |
875 |
for (xx=0; xx<sw; xx++) { |
876 |
d=0;
|
877 |
t=0;
|
878 |
for (x=xx*gm->w/sw; x<(xx+1)*gm->w/sw; x++) { |
879 |
for (y=yy*gm->h/sh; y<(yy+1)*gm->h/sh; y++) { |
880 |
d += GM_GET(gm, x, y); |
881 |
t += 256;
|
882 |
} |
883 |
} |
884 |
fputc("*#=- "[5*d/t], f); /* what a cute trick :) */ |
885 |
} |
886 |
fputc('\n', f);
|
887 |
} |
888 |
return 0; |
889 |
} |