Code

fix by Preben S for LP bug 389780, flatness
[inkscape.git] / src / trace / potrace / greymap.cpp
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. */
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   }
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;
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';
162 /* ---------------------------------------------------------------------- */
164 char const *gm_read_error = NULL;
166 /** Read a PNM stream: P1-P6 format (see pnm(5)), or a BMP stream, and
167    convert the output to a greymap. Return greymap in *gmp. Return 0
168    on success, -1 on error with errno set, -2 on bad file format (with
169    error message in gm_read_error), and 1 on premature end of file, -3
170    on empty file (including files with only whitespace and comments),
171    -4 if wrong magic number. If the return value is >=0, *gmp is
172    valid.
173    */
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;
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 */
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;
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;
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;
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;
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;
444 /* reset padding boundary */
445 static void bmp_pad_reset(void) {
446   bmp_count = 0;
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;
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;
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;
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 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;
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   }
548   
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   }
578   
579   /* zero it out */
580   gm_clear(gm, 0);
582   switch (bmpinfo.bits + 0x100*bmpinfo.comp) {
583     
584   default:
585     goto format_error;
586     break;
587     
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;
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;
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;
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];
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   }  
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;
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;