1 /* Copyright (C) 2001-2005 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. */
5 /* $Id$ */
7 /* Routines for manipulating greymaps, including reading pgm files. We
8 only deal with greymaps of depth 8 bits. */
10 #include <stdlib.h>
11 #include <errno.h>
12 #include <string.h>
13 #include <math.h>
15 #include "greymap.h"
17 #define INTBITS (8*sizeof(int))
19 #define mod(a,n) ((a)>=(n) ? (a)%(n) : (a)>=0 ? (a) : (n)-1-(-1-(a))%(n))
21 static int gm_readbody_pnm(FILE *f, greymap_t **gmp, int magic);
22 static int gm_readbody_bmp(FILE *f, greymap_t **gmp);
24 /* ---------------------------------------------------------------------- */
25 /* basic greymap routines */
27 /* return new un-initialized greymap. NULL with errno on error */
29 greymap_t *gm_new(int w, int h) {
30 greymap_t *gm;
31 int errno_save;
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 }
49 /* free the given greymap */
50 void gm_free(greymap_t *gm) {
51 if (gm) {
52 free(gm->map);
53 }
54 free(gm);
55 }
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 }
67 /* clear the given greymap to color b. */
68 void gm_clear(greymap_t *gm, int b) {
69 int i;
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 }
80 /* ---------------------------------------------------------------------- */
81 /* routines for reading pnm streams */
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;
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 }
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) */
110 static int readnum(FILE *f) {
111 int c;
112 int acc;
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 }
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 }
142 /* similar to readnum, but read only a single 0 or 1, and do not read
143 any characters after it. */
145 static int readbit(FILE *f) {
146 int c;
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 }
159 return c-'0';
160 }
162 /* ---------------------------------------------------------------------- */
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. */
172 char *gm_read_error = NULL;
174 int gm_read(FILE *f, greymap_t **gmp) {
175 int magic[2];
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. */
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 }
197 /* ---------------------------------------------------------------------- */
198 /* read PNM format */
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;
207 gm = NULL;
209 w = readnum(f);
210 if (w<0) {
211 goto format_error;
212 }
214 h = readnum(f);
215 if (h<0) {
216 goto format_error;
217 }
219 /* allocate greymap */
220 gm = gm_new(w, h);
221 if (!gm) {
222 return -1;
223 }
225 /* zero it out */
226 gm_clear(gm, 0);
228 switch (magic) {
229 default:
230 /* not reached */
231 goto format_error;
233 case '1':
234 /* read P1 format: PBM ascii */
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;
247 case '2':
248 /* read P2 format: PGM ascii */
250 max = readnum(f);
251 if (max<1) {
252 goto format_error;
253 }
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;
266 case '3':
267 /* read P3 format: PPM ascii */
269 max = readnum(f);
270 if (max<1) {
271 goto format_error;
272 }
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;
289 case '4':
290 /* read P4 format: PBM raw */
292 b = fgetc(f); /* read single white-space character after height */
293 if (b==EOF) {
294 goto format_error;
295 }
297 bpr = (w+7)/8;
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;
312 case '5':
313 /* read P5 format: PGM raw */
315 max = readnum(f);
316 if (max<1) {
317 goto format_error;
318 }
320 b = fgetc(f); /* read single white-space character after max */
321 if (b==EOF) {
322 goto format_error;
323 }
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;
342 case '6':
343 /* read P6 format: PPM raw */
345 max = readnum(f);
346 if (max<1) {
347 goto format_error;
348 }
350 b = fgetc(f); /* read single white-space character after max */
351 if (b==EOF) {
352 goto format_error;
353 }
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 }
378 *gmp = gm;
379 return 0;
381 eof:
382 *gmp = gm;
383 return 1;
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 }
397 /* ---------------------------------------------------------------------- */
398 /* read BMP format */
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;
419 /* auxiliary */
421 static int bmp_count = 0; /* counter for byte padding */
422 static int bmp_pos = 0; /* counter from start of BMP data */
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;
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 }
444 /* reset padding boundary */
445 static void bmp_pad_reset() {
446 bmp_count = 0;
447 }
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;
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 }
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;
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 }
481 #define TRY(x) if (x) goto try_error
482 #define TRY_EOF(x) if (x) goto eof
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 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;
502 gm_read_error = NULL;
503 gm = NULL;
504 coltable = NULL;
506 bmp_pos = 2; /* set file position */
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));
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 }
541 /* forward to color table (i.e., if bmpinfo.InfoSize == 64) */
542 TRY(bmp_forward(f, 14+bmpinfo.InfoSize));
544 if (bmpinfo.Planes != 1) {
545 gm_read_error = "cannot handle bmp planes";
546 goto format_error; /* can't handle planes */
547 }
549 if (bmpinfo.ncolors == 0) {
550 bmpinfo.ncolors = 1 << bmpinfo.bits;
551 }
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 }
568 /* forward to data */
569 if (bmpinfo.InfoSize != 12) { /* not old OS/2 format */
570 TRY(bmp_forward(f, bmpinfo.DataOffset));
571 }
573 /* allocate greymap */
574 gm = gm_new(bmpinfo.w, bmpinfo.h);
575 if (!gm) {
576 goto std_error;
577 }
579 /* zero it out */
580 gm_clear(gm, 0);
582 switch (bmpinfo.bits + 0x100*bmpinfo.comp) {
584 default:
585 goto format_error;
586 break;
588 case 0x001: /* monochrome palette */
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;
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;
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;
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;
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;
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;
761 } /* switch */
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);
767 free(coltable);
768 *gmp = gm;
769 return 0;
771 eof:
772 free(coltable);
773 *gmp = gm;
774 return 1;
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;
785 std_error:
786 free(coltable);
787 free(gm);
788 return -1;
789 }
791 /* ---------------------------------------------------------------------- */
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 */
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];
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 }
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];
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 }
861 /* ---------------------------------------------------------------------- */
862 /* output - for primitive debugging purposes only! */
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;
871 sw = gm->w < 79 ? gm->w : 79;
872 sh = gm->w < 79 ? gm->h : gm->h*sw*44/(79*gm->w);
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 }