1 /* Copyright (C) 2001-2017 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
6 /* Routines for manipulating greymaps, including reading pgm files. We
7 * only deal with greymaps of depth 8 bits. */
8
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <errno.h>
14 #include <math.h>
15 #include <stddef.h>
16 #include <stdlib.h>
17 #include <string.h>
18
19 #include "bitops.h"
20 #include "greymap.h"
21
22 #define INTBITS ( 8 * sizeof( int ) )
23
24 #define mod( a, n ) \
25 ( ( a ) >= ( n ) ? ( a ) % ( n ) : ( a ) >= 0 ? ( a ) : (n) - 1 - ( -1 - ( a ) ) % ( n ) )
26
27 static int gm_readbody_pnm( FILE* f, greymap_t** gmp, int magic );
28 static int gm_readbody_bmp( FILE* f, greymap_t** gmp );
29
30 #define TRY( x ) \
31 if( x ) \
32 goto try_error
33 #define TRY_EOF( x ) \
34 if( x ) \
35 goto eof
36 #define TRY_STD( x ) \
37 if( x ) \
38 goto std_error
39
40 /* ---------------------------------------------------------------------- */
41 /* basic greymap routines */
42
43 /* calculate the size, in bytes, required for the data area of a
44 * greymap of the given dy and h. Assume h >= 0. Return -1 if the size
45 * does not fit into the ptrdiff_t type. */
getsize(int dy,int h)46 static inline ptrdiff_t getsize( int dy, int h )
47 {
48 ptrdiff_t size;
49
50 if( dy < 0 )
51 {
52 dy = -dy;
53 }
54
55 size = (ptrdiff_t) dy * (ptrdiff_t) h * (ptrdiff_t) sizeof( gm_sample_t );
56
57 /* check for overflow error */
58 if( size < 0 || ( h != 0 && dy != 0 && size / h / dy != sizeof( gm_sample_t ) ) )
59 {
60 return -1;
61 }
62
63 return size;
64 }
65
66
67 /* return the size, in bytes, of the data area of the greymap. Return
68 * -1 if the size does not fit into the ptrdiff_t type; however, this
69 * cannot happen if the bitmap is well-formed, i.e., if created with
70 * gm_new or gm_dup. */
gm_size(const greymap_t * gm)71 static inline ptrdiff_t gm_size( const greymap_t* gm )
72 {
73 return getsize( gm->dy, gm->h );
74 }
75
76
77 /* return new greymap initialized to 0. NULL with errno on error.
78 * Assumes w, h >= 0. */
gm_new(int w,int h)79 greymap_t* gm_new( int w, int h )
80 {
81 greymap_t* gm;
82 int dy = w;
83 ptrdiff_t size;
84
85 size = getsize( dy, h );
86
87 if( size < 0 )
88 {
89 errno = ENOMEM;
90 return NULL;
91 }
92
93 if( size == 0 )
94 {
95 size = 1; /* make surecmalloc() doesn't return NULL */
96 }
97
98 gm = (greymap_t*) malloc( sizeof( greymap_t ) );
99
100 if( !gm )
101 {
102 return NULL;
103 }
104
105 gm->w = w;
106 gm->h = h;
107 gm->dy = dy;
108 gm->base = (gm_sample_t*) calloc( 1, size );
109
110 if( !gm->base )
111 {
112 free( gm );
113 return NULL;
114 }
115
116 gm->map = gm->base;
117 return gm;
118 }
119
120
121 /* free the given greymap */
gm_free(greymap_t * gm)122 void gm_free( greymap_t* gm )
123 {
124 if( gm )
125 {
126 free( gm->base );
127 }
128
129 free( gm );
130 }
131
132
133 /* duplicate the given greymap. Return NULL on error with errno set. */
gm_dup(greymap_t * gm)134 greymap_t* gm_dup( greymap_t* gm )
135 {
136 greymap_t* gm1 = gm_new( gm->w, gm->h );
137 int y;
138
139 if( !gm1 )
140 {
141 return NULL;
142 }
143
144 for( y = 0; y < gm->h; y++ )
145 {
146 memcpy( gm_scanline( gm1, y ), gm_scanline( gm, y ),
147 (size_t) gm1->dy * sizeof( gm_sample_t ) );
148 }
149
150 return gm1;
151 }
152
153
154 /* clear the given greymap to color b. */
gm_clear(greymap_t * gm,int b)155 void gm_clear( greymap_t* gm, int b )
156 {
157 ptrdiff_t size = gm_size( gm );
158 int x, y;
159
160 if( b == 0 )
161 {
162 if( size > 0 )
163 memset( gm->base, 0, size );
164 }
165 else
166 {
167 for( y = 0; y < gm->h; y++ )
168 {
169 for( x = 0; x < gm->w; x++ )
170 {
171 GM_UPUT( gm, x, y, b );
172 }
173 }
174 }
175 }
176
177
178 /* turn the given greymap upside down. This does not move the pixel
179 * data or change the base address. */
gm_flip(greymap_t * gm)180 static inline void gm_flip( greymap_t* gm )
181 {
182 int dy = gm->dy;
183
184 if( gm->h == 0 || gm->h == 1 )
185 {
186 return;
187 }
188
189 gm->map = gm_scanline( gm, gm->h - 1 );
190 gm->dy = -dy;
191 }
192
193
194 /* resize the greymap to the given new height. The pixel data remains
195 * bottom-aligned (truncated at the top) when dy >= 0 and top-aligned
196 * (truncated at the bottom) when dy < 0. Return 0 on success, or 1 on
197 * error with errno set. If the new height is <= the old one, no error
198 * should occur. If the new height is larger, the additional pixel
199 * data is *not* initialized. */
gm_resize(greymap_t * gm,int h)200 static inline int gm_resize( greymap_t* gm, int h )
201 {
202 int dy = gm->dy;
203 ptrdiff_t newsize;
204 gm_sample_t* newbase;
205
206 if( dy < 0 )
207 {
208 gm_flip( gm );
209 }
210
211 newsize = getsize( dy, h );
212
213 if( newsize < 0 )
214 {
215 errno = ENOMEM;
216 goto error;
217 }
218
219 if( newsize == 0 )
220 {
221 newsize = 1; /* make sure realloc() doesn't return NULL */
222 }
223
224 newbase = (gm_sample_t*) realloc( gm->base, newsize );
225
226 if( newbase == NULL )
227 {
228 goto error;
229 }
230
231 gm->base = newbase;
232 gm->map = newbase;
233 gm->h = h;
234
235 if( dy < 0 )
236 {
237 gm_flip( gm );
238 }
239
240 return 0;
241
242 error:
243
244 if( dy < 0 )
245 {
246 gm_flip( gm );
247 }
248
249 return 1;
250 }
251
252
253 /* ---------------------------------------------------------------------- */
254 /* routines for reading pnm streams */
255
256 /* read next character after whitespace and comments. Return EOF on
257 * end of file or error. */
fgetc_ws(FILE * f)258 static int fgetc_ws( FILE* f )
259 {
260 int c;
261
262 while( 1 )
263 {
264 c = fgetc( f );
265
266 if( c == '#' )
267 {
268 while( 1 )
269 {
270 c = fgetc( f );
271
272 if( c == '\n' || c == EOF )
273 {
274 break;
275 }
276 }
277 }
278
279 /* space, tab, line feed, carriage return, form-feed */
280 if( c != ' ' && c != '\t' && c != '\r' && c != '\n' && c != 12 )
281 {
282 return c;
283 }
284 }
285 }
286
287
288 /* skip whitespace and comments, then read a non-negative decimal
289 * number from a stream. Return -1 on EOF. Tolerate other errors (skip
290 * bad characters). Do not the read any characters following the
291 * number (put next character back into the stream) */
292
readnum(FILE * f)293 static int readnum( FILE* f )
294 {
295 int c;
296 int acc;
297
298 /* skip whitespace and comments */
299 while( 1 )
300 {
301 c = fgetc_ws( f );
302
303 if( c == EOF )
304 {
305 return -1;
306 }
307
308 if( c >= '0' && c <= '9' )
309 {
310 break;
311 }
312 }
313
314 /* first digit is already in c */
315 acc = c - '0';
316
317 while( 1 )
318 {
319 c = fgetc( f );
320
321 if( c == EOF )
322 {
323 break;
324 }
325
326 if( c < '0' || c > '9' )
327 {
328 ungetc( c, f );
329 break;
330 }
331
332 acc *= 10;
333 acc += c - '0';
334 }
335
336 return acc;
337 }
338
339
340 /* similar to readnum, but read only a single 0 or 1, and do not read
341 * any characters after it. */
342
readbit(FILE * f)343 static int readbit( FILE* f )
344 {
345 int c;
346
347 /* skip whitespace and comments */
348 while( 1 )
349 {
350 c = fgetc_ws( f );
351
352 if( c == EOF )
353 {
354 return -1;
355 }
356
357 if( c >= '0' && c <= '1' )
358 {
359 break;
360 }
361 }
362
363 return c - '0';
364 }
365
366
367 /* ---------------------------------------------------------------------- */
368
369 /* read a PNM stream: P1-P6 format (see pnm(5)), or a BMP stream, and
370 * convert the output to a greymap. Return greymap in *gmp. Return 0
371 * on success, -1 on error with errno set, -2 on bad file format (with
372 * error message in gm_read_error), and 1 on premature end of file, -3
373 * on empty file (including files with only whitespace and comments),
374 * -4 if wrong magic number. If the return value is >=0, *gmp is
375 * valid. */
376
377 const char* gm_read_error = NULL;
378
gm_read(FILE * f,greymap_t ** gmp)379 int gm_read( FILE* f, greymap_t** gmp )
380 {
381 int magic[2];
382
383 /* read magic number. We ignore whitespace and comments before the
384 * magic, for the benefit of concatenated files in P1-P3 format.
385 * Multiple P1-P3 images in a single file are not formally allowed
386 * by the PNM standard, but there is no harm in being lenient. */
387
388 magic[0] = fgetc_ws( f );
389
390 if( magic[0] == EOF )
391 {
392 /* files which contain only comments and whitespace count as "empty" */
393 return -3;
394 }
395
396 magic[1] = fgetc( f );
397
398 if( magic[0] == 'P' && magic[1] >= '1' && magic[1] <= '6' )
399 {
400 return gm_readbody_pnm( f, gmp, magic[1] );
401 }
402
403 if( magic[0] == 'B' && magic[1] == 'M' )
404 {
405 return gm_readbody_bmp( f, gmp );
406 }
407
408 return -4;
409 }
410
411
412 /* ---------------------------------------------------------------------- */
413 /* read PNM format */
414
415 /* read PNM stream after magic number. Return values as for gm_read */
gm_readbody_pnm(FILE * f,greymap_t ** gmp,int magic)416 static int gm_readbody_pnm( FILE* f, greymap_t** gmp, int magic )
417 {
418 greymap_t* gm;
419 int x, y, i, j, b, b1, sum;
420 int bpr; /* bytes per row (as opposed to 4*gm->c) */
421 int w, h, max;
422 int realheight; /* in case of incomplete file, keeps track of how
423 * many scan lines actually contain data */
424
425 gm = NULL;
426
427 w = readnum( f );
428
429 if( w < 0 )
430 {
431 goto format_error;
432 }
433
434 h = readnum( f );
435
436 if( h < 0 )
437 {
438 goto format_error;
439 }
440
441 /* allocate greymap */
442 gm = gm_new( w, h );
443
444 if( !gm )
445 {
446 goto std_error;
447 }
448
449 realheight = 0;
450
451 switch( magic )
452 {
453 default:
454 /* not reached */
455 goto format_error;
456
457 case '1':
458 /* read P1 format: PBM ascii */
459
460 for( y = 0; y < h; y++ )
461 {
462 realheight = y + 1;
463
464 for( x = 0; x < w; x++ )
465 {
466 b = readbit( f );
467
468 if( b < 0 )
469 {
470 goto eof;
471 }
472
473 GM_UPUT( gm, x, y, b ? 0 : 255 );
474 }
475 }
476
477 break;
478
479 case '2':
480 /* read P2 format: PGM ascii */
481
482 max = readnum( f );
483
484 if( max < 1 )
485 {
486 goto format_error;
487 }
488
489 for( y = 0; y < h; y++ )
490 {
491 realheight = y + 1;
492
493 for( x = 0; x < w; x++ )
494 {
495 b = readnum( f );
496
497 if( b < 0 )
498 {
499 goto eof;
500 }
501
502 GM_UPUT( gm, x, y, b * 255 / max );
503 }
504 }
505
506 break;
507
508 case '3':
509 /* read P3 format: PPM ascii */
510
511 max = readnum( f );
512
513 if( max < 1 )
514 {
515 goto format_error;
516 }
517
518 for( y = 0; y < h; y++ )
519 {
520 realheight = y + 1;
521
522 for( x = 0; x < w; x++ )
523 {
524 sum = 0;
525
526 for( i = 0; i < 3; i++ )
527 {
528 b = readnum( f );
529
530 if( b < 0 )
531 {
532 goto eof;
533 }
534
535 sum += b;
536 }
537
538 GM_UPUT( gm, x, y, sum * ( 255 / 3 ) / max );
539 }
540 }
541
542 break;
543
544 case '4':
545 /* read P4 format: PBM raw */
546
547 b = fgetc( f ); /* read single white-space character after height */
548
549 if( b == EOF )
550 {
551 goto format_error;
552 }
553
554 bpr = ( w + 7 ) / 8;
555
556 for( y = 0; y < h; y++ )
557 {
558 realheight = y + 1;
559
560 for( i = 0; i < bpr; i++ )
561 {
562 b = fgetc( f );
563
564 if( b == EOF )
565 {
566 goto eof;
567 }
568
569 for( j = 0; j < 8; j++ )
570 {
571 GM_PUT( gm, i * 8 + j, y, b & ( 0x80 >> j ) ? 0 : 255 );
572 }
573 }
574 }
575
576 break;
577
578 case '5':
579 /* read P5 format: PGM raw */
580
581 max = readnum( f );
582
583 if( max < 1 )
584 {
585 goto format_error;
586 }
587
588 b = fgetc( f ); /* read single white-space character after max */
589
590 if( b == EOF )
591 {
592 goto format_error;
593 }
594
595 for( y = 0; y < h; y++ )
596 {
597 realheight = y + 1;
598
599 for( x = 0; x < w; x++ )
600 {
601 b = fgetc( f );
602
603 if( b == EOF )
604 goto eof;
605
606 if( max >= 256 )
607 {
608 b <<= 8;
609 b1 = fgetc( f );
610
611 if( b1 == EOF )
612 goto eof;
613
614 b |= b1;
615 }
616
617 GM_UPUT( gm, x, y, b * 255 / max );
618 }
619 }
620
621 break;
622
623 case '6':
624 /* read P6 format: PPM raw */
625
626 max = readnum( f );
627
628 if( max < 1 )
629 {
630 goto format_error;
631 }
632
633 b = fgetc( f ); /* read single white-space character after max */
634
635 if( b == EOF )
636 {
637 goto format_error;
638 }
639
640 for( y = 0; y < h; y++ )
641 {
642 realheight = y + 1;
643
644 for( x = 0; x < w; x++ )
645 {
646 sum = 0;
647
648 for( i = 0; i < 3; i++ )
649 {
650 b = fgetc( f );
651
652 if( b == EOF )
653 {
654 goto eof;
655 }
656
657 if( max >= 256 )
658 {
659 b <<= 8;
660 b1 = fgetc( f );
661
662 if( b1 == EOF )
663 goto eof;
664
665 b |= b1;
666 }
667
668 sum += b;
669 }
670
671 GM_UPUT( gm, x, y, sum * ( 255 / 3 ) / max );
672 }
673 }
674
675 break;
676 }
677
678 gm_flip( gm );
679 *gmp = gm;
680 return 0;
681
682 eof:
683 TRY_STD( gm_resize( gm, realheight ) );
684 gm_flip( gm );
685 *gmp = gm;
686 return 1;
687
688 format_error:
689 gm_free( gm );
690
691 if( magic == '1' || magic == '4' )
692 {
693 gm_read_error = "invalid pbm file";
694 }
695 else if( magic == '2' || magic == '5' )
696 {
697 gm_read_error = "invalid pgm file";
698 }
699 else
700 {
701 gm_read_error = "invalid ppm file";
702 }
703
704 return -2;
705
706 std_error:
707 gm_free( gm );
708 return -1;
709 }
710
711
712 /* ---------------------------------------------------------------------- */
713 /* read BMP format */
714
715 struct bmp_info_s
716 {
717 unsigned int FileSize;
718 unsigned int reserved;
719 unsigned int DataOffset;
720 unsigned int InfoSize;
721 unsigned int w; /* width */
722 unsigned int h; /* height */
723 unsigned int Planes;
724 unsigned int bits; /* bits per sample */
725 unsigned int comp; /* compression mode */
726 unsigned int ImageSize;
727 unsigned int XpixelsPerM;
728 unsigned int YpixelsPerM;
729 unsigned int ncolors; /* number of colors in palette */
730 unsigned int ColorsImportant;
731 unsigned int RedMask;
732 unsigned int GreenMask;
733 unsigned int BlueMask;
734 unsigned int AlphaMask;
735 unsigned int ctbits; /* sample size for color table */
736 int topdown; /* top-down mode? */
737 };
738 typedef struct bmp_info_s bmp_info_t;
739
740 /* auxiliary */
741
742 static int bmp_count = 0; /* counter for byte padding */
743 static int bmp_pos = 0; /* counter from start of BMP data */
744
745 /* read n-byte little-endian integer. Return 1 on EOF or error, else
746 * 0. Assume n<=4. */
bmp_readint(FILE * f,int n,unsigned int * p)747 static int bmp_readint( FILE* f, int n, unsigned int* p )
748 {
749 int i;
750 unsigned int sum = 0;
751 int b;
752
753 for( i = 0; i < n; i++ )
754 {
755 b = fgetc( f );
756
757 if( b == EOF )
758 {
759 return 1;
760 }
761
762 sum += (unsigned) b << ( 8 * i );
763 }
764
765 bmp_count += n;
766 bmp_pos += n;
767 *p = sum;
768 return 0;
769 }
770
771
772 /* reset padding boundary */
bmp_pad_reset(void)773 static void bmp_pad_reset( void )
774 {
775 bmp_count = 0;
776 }
777
778
779 /* read padding bytes to 4-byte boundary. Return 1 on EOF or error,
780 * else 0. */
bmp_pad(FILE * f)781 static int bmp_pad( FILE* f )
782 {
783 int c, i, b;
784
785 c = ( -bmp_count ) & 3;
786
787 for( i = 0; i < c; i++ )
788 {
789 b = fgetc( f );
790
791 if( b == EOF )
792 {
793 return 1;
794 }
795 }
796
797 bmp_pos += c;
798 bmp_count = 0;
799 return 0;
800 }
801
802
803 /* forward to the new file position. Return 1 on EOF or error, else 0 */
bmp_forward(FILE * f,int pos)804 static int bmp_forward( FILE* f, int pos )
805 {
806 int b;
807
808 while( bmp_pos < pos )
809 {
810 b = fgetc( f );
811
812 if( b == EOF )
813 {
814 return 1;
815 }
816
817 bmp_pos++;
818 bmp_count++;
819 }
820
821 return 0;
822 }
823
824
825 /* safe colortable access */
826 #define COLTABLE( c ) ( ( c ) < bmpinfo.ncolors ? coltable[( c )] : 0 )
827
828 /* read BMP stream after magic number. Return values as for gm_read.
829 * We choose to be as permissive as possible, since there are many
830 * programs out there which produce BMP. For instance, ppmtobmp can
831 * produce codings with anywhere from 1-8 or 24 bits per sample,
832 * although most specifications only allow 1,4,8,24,32. We can also
833 * read both the old and new OS/2 BMP formats in addition to the
834 * Windows BMP format. */
gm_readbody_bmp(FILE * f,greymap_t ** gmp)835 static int gm_readbody_bmp( FILE* f, greymap_t** gmp )
836 {
837 bmp_info_t bmpinfo;
838 int* coltable;
839 unsigned int b, c;
840 unsigned int i, j;
841 greymap_t* gm;
842 unsigned int x, y;
843 int col[2];
844 unsigned int bitbuf;
845 unsigned int n;
846 unsigned int redshift, greenshift, blueshift;
847 int realheight; /* in case of incomplete file, keeps track of how
848 * many scan lines actually contain data */
849
850 gm_read_error = NULL;
851 gm = NULL;
852 coltable = NULL;
853
854 bmp_pos = 2; /* set file position */
855
856 /* file header (minus magic number) */
857 TRY( bmp_readint( f, 4, &bmpinfo.FileSize ) );
858 TRY( bmp_readint( f, 4, &bmpinfo.reserved ) );
859 TRY( bmp_readint( f, 4, &bmpinfo.DataOffset ) );
860
861 /* info header */
862 TRY( bmp_readint( f, 4, &bmpinfo.InfoSize ) );
863
864 if( bmpinfo.InfoSize == 40 || bmpinfo.InfoSize == 64 || bmpinfo.InfoSize == 108
865 || bmpinfo.InfoSize == 124 )
866 {
867 /* Windows or new OS/2 format */
868 bmpinfo.ctbits = 32; /* sample size in color table */
869 TRY( bmp_readint( f, 4, &bmpinfo.w ) );
870 TRY( bmp_readint( f, 4, &bmpinfo.h ) );
871 TRY( bmp_readint( f, 2, &bmpinfo.Planes ) );
872 TRY( bmp_readint( f, 2, &bmpinfo.bits ) );
873 TRY( bmp_readint( f, 4, &bmpinfo.comp ) );
874 TRY( bmp_readint( f, 4, &bmpinfo.ImageSize ) );
875 TRY( bmp_readint( f, 4, &bmpinfo.XpixelsPerM ) );
876 TRY( bmp_readint( f, 4, &bmpinfo.YpixelsPerM ) );
877 TRY( bmp_readint( f, 4, &bmpinfo.ncolors ) );
878 TRY( bmp_readint( f, 4, &bmpinfo.ColorsImportant ) );
879
880 if( bmpinfo.InfoSize >= 108 )
881 {
882 /* V4 and V5 bitmaps */
883 TRY( bmp_readint( f, 4, &bmpinfo.RedMask ) );
884 TRY( bmp_readint( f, 4, &bmpinfo.GreenMask ) );
885 TRY( bmp_readint( f, 4, &bmpinfo.BlueMask ) );
886 TRY( bmp_readint( f, 4, &bmpinfo.AlphaMask ) );
887 }
888
889 if( bmpinfo.w > 0x7fffffff )
890 {
891 goto format_error;
892 }
893
894 if( bmpinfo.h > 0x7fffffff )
895 {
896 bmpinfo.h = ( -bmpinfo.h ) & 0xffffffff;
897 bmpinfo.topdown = 1;
898 }
899 else
900 {
901 bmpinfo.topdown = 0;
902 }
903
904 if( bmpinfo.h > 0x7fffffff )
905 {
906 goto format_error;
907 }
908 }
909 else if( bmpinfo.InfoSize == 12 )
910 {
911 /* old OS/2 format */
912 bmpinfo.ctbits = 24; /* sample size in color table */
913 TRY( bmp_readint( f, 2, &bmpinfo.w ) );
914 TRY( bmp_readint( f, 2, &bmpinfo.h ) );
915 TRY( bmp_readint( f, 2, &bmpinfo.Planes ) );
916 TRY( bmp_readint( f, 2, &bmpinfo.bits ) );
917 bmpinfo.comp = 0;
918 bmpinfo.ncolors = 0;
919 bmpinfo.topdown = 0;
920 }
921 else
922 {
923 goto format_error;
924 }
925
926 if( bmpinfo.comp == 3 && bmpinfo.InfoSize < 108 )
927 {
928 /* bitfield feature is only understood with V4 and V5 format */
929 goto format_error;
930 }
931
932 if( bmpinfo.comp > 3 || bmpinfo.bits > 32 )
933 {
934 goto format_error;
935 }
936
937 /* forward to color table (e.g., if bmpinfo.InfoSize == 64) */
938 TRY( bmp_forward( f, 14 + bmpinfo.InfoSize ) );
939
940 if( bmpinfo.Planes != 1 )
941 {
942 gm_read_error = "cannot handle bmp planes";
943 goto format_error; /* can't handle planes */
944 }
945
946 if( bmpinfo.ncolors == 0 && bmpinfo.bits <= 8 )
947 {
948 bmpinfo.ncolors = 1 << bmpinfo.bits;
949 }
950
951 /* color table, present only if bmpinfo.bits <= 8. */
952 if( bmpinfo.bits <= 8 )
953 {
954 coltable = (int*) calloc( bmpinfo.ncolors, sizeof( int ) );
955
956 if( !coltable )
957 {
958 goto std_error;
959 }
960
961 /* NOTE: since we are reading a greymap, we can immediately convert
962 * the color table entries to grey values. */
963 for( i = 0; i < bmpinfo.ncolors; i++ )
964 {
965 TRY( bmp_readint( f, bmpinfo.ctbits / 8, &c ) );
966 c = ( ( c >> 16 ) & 0xff ) + ( ( c >> 8 ) & 0xff ) + ( c & 0xff );
967 coltable[i] = c / 3;
968 }
969 }
970
971 /* forward to data */
972 if( bmpinfo.InfoSize != 12 )
973 {
974 /* not old OS/2 format */
975 TRY( bmp_forward( f, bmpinfo.DataOffset ) );
976 }
977
978 /* allocate greymap */
979 gm = gm_new( bmpinfo.w, bmpinfo.h );
980
981 if( !gm )
982 {
983 goto std_error;
984 }
985
986 realheight = 0;
987
988 switch( bmpinfo.bits + 0x100 * bmpinfo.comp )
989 {
990 default:
991 goto format_error; break;
992
993 case 0x001: /* monochrome palette */
994
995 /* raster data */
996 for( y = 0; y < bmpinfo.h; y++ )
997 {
998 realheight = y + 1;
999 bmp_pad_reset();
1000
1001 for( i = 0; 8 * i < bmpinfo.w; i++ )
1002 {
1003 TRY_EOF( bmp_readint( f, 1, &b ) );
1004
1005 for( j = 0; j < 8; j++ )
1006 {
1007 GM_PUT( gm, i * 8 + j, y, b & ( 0x80 >> j ) ? COLTABLE( 1 ) : COLTABLE( 0 ) );
1008 }
1009 }
1010
1011 TRY( bmp_pad( f ) );
1012 }
1013
1014 break;
1015
1016 case 0x002: /* 2-bit to 8-bit palettes */
1017 case 0x003:
1018 case 0x004:
1019 case 0x005:
1020 case 0x006:
1021 case 0x007:
1022 case 0x008:
1023
1024 for( y = 0; y < bmpinfo.h; y++ )
1025 {
1026 realheight = y + 1;
1027 bmp_pad_reset();
1028 bitbuf = 0; /* bit buffer: bits in buffer are high-aligned */
1029 n = 0; /* number of bits currently in bitbuffer */
1030
1031 for( x = 0; x < bmpinfo.w; x++ )
1032 {
1033 if( n < bmpinfo.bits )
1034 {
1035 TRY_EOF( bmp_readint( f, 1, &b ) );
1036 bitbuf |= b << ( INTBITS - 8 - n );
1037 n += 8;
1038 }
1039
1040 b = bitbuf >> ( INTBITS - bmpinfo.bits );
1041 bitbuf <<= bmpinfo.bits;
1042 n -= bmpinfo.bits;
1043 GM_UPUT( gm, x, y, COLTABLE( b ) );
1044 }
1045
1046 TRY( bmp_pad( f ) );
1047 }
1048
1049 break;
1050
1051 case 0x010: /* 16-bit encoding */
1052 /* can't do this format because it is not well-documented and I
1053 * don't have any samples */
1054 gm_read_error = "cannot handle bmp 16-bit coding";
1055 goto format_error;
1056 break;
1057
1058 case 0x018: /* 24-bit encoding */
1059 case 0x020: /* 32-bit encoding */
1060
1061 for( y = 0; y < bmpinfo.h; y++ )
1062 {
1063 realheight = y + 1;
1064 bmp_pad_reset();
1065
1066 for( x = 0; x < bmpinfo.w; x++ )
1067 {
1068 TRY_EOF( bmp_readint( f, bmpinfo.bits / 8, &c ) );
1069 c = ( ( c >> 16 ) & 0xff ) + ( ( c >> 8 ) & 0xff ) + ( c & 0xff );
1070 GM_UPUT( gm, x, y, c / 3 );
1071 }
1072
1073 TRY( bmp_pad( f ) );
1074 }
1075
1076 break;
1077
1078 case 0x320: /* 32-bit encoding with bitfields */
1079 redshift = lobit( bmpinfo.RedMask );
1080 greenshift = lobit( bmpinfo.GreenMask );
1081 blueshift = lobit( bmpinfo.BlueMask );
1082
1083 for( y = 0; y < bmpinfo.h; y++ )
1084 {
1085 realheight = y + 1;
1086 bmp_pad_reset();
1087
1088 for( x = 0; x < bmpinfo.w; x++ )
1089 {
1090 TRY_EOF( bmp_readint( f, bmpinfo.bits / 8, &c ) );
1091 c = ( ( c & bmpinfo.RedMask ) >> redshift )
1092 + ( ( c & bmpinfo.GreenMask ) >> greenshift )
1093 + ( ( c & bmpinfo.BlueMask ) >> blueshift );
1094 GM_UPUT( gm, x, y, c / 3 );
1095 }
1096
1097 TRY( bmp_pad( f ) );
1098 }
1099
1100 break;
1101
1102 case 0x204: /* 4-bit runlength compressed encoding (RLE4) */
1103 x = 0;
1104 y = 0;
1105
1106 while( 1 )
1107 {
1108 TRY_EOF( bmp_readint( f, 1, &b ) ); /* opcode */
1109 TRY_EOF( bmp_readint( f, 1, &c ) ); /* argument */
1110
1111 if( b > 0 )
1112 {
1113 /* repeat count */
1114 col[0] = COLTABLE( ( c >> 4 ) & 0xf );
1115 col[1] = COLTABLE( c & 0xf );
1116
1117 for( i = 0; i < b && x < bmpinfo.w; i++ )
1118 {
1119 if( x >= bmpinfo.w )
1120 {
1121 x = 0;
1122 y++;
1123 }
1124
1125 if( x >= bmpinfo.w || y >= bmpinfo.h )
1126 {
1127 break;
1128 }
1129
1130 realheight = y + 1;
1131 GM_PUT( gm, x, y, col[i & 1] );
1132 x++;
1133 }
1134 }
1135 else if( c == 0 )
1136 {
1137 /* end of line */
1138 y++;
1139 x = 0;
1140 }
1141 else if( c == 1 )
1142 {
1143 /* end of greymap */
1144 break;
1145 }
1146 else if( c == 2 )
1147 {
1148 /* "delta": skip pixels in x and y directions */
1149 TRY_EOF( bmp_readint( f, 1, &b ) ); /* x offset */
1150 TRY_EOF( bmp_readint( f, 1, &c ) ); /* y offset */
1151 x += b;
1152 y += c;
1153 }
1154 else
1155 {
1156 /* verbatim segment */
1157 for( i = 0; i < c; i++ )
1158 {
1159 if( ( i & 1 ) == 0 )
1160 {
1161 TRY_EOF( bmp_readint( f, 1, &b ) );
1162 }
1163
1164 if( x >= bmpinfo.w )
1165 {
1166 x = 0;
1167 y++;
1168 }
1169
1170 if( x >= bmpinfo.w || y >= bmpinfo.h )
1171 {
1172 break;
1173 }
1174
1175 realheight = y + 1;
1176 GM_PUT( gm, x, y, COLTABLE( ( b >> ( 4 - 4 * ( i & 1 ) ) ) & 0xf ) );
1177 x++;
1178 }
1179
1180 if( ( c + 1 ) & 2 )
1181 {
1182 /* pad to 16-bit boundary */
1183 TRY_EOF( bmp_readint( f, 1, &b ) );
1184 }
1185 }
1186 }
1187
1188 break;
1189
1190 case 0x108: /* 8-bit runlength compressed encoding (RLE8) */
1191 x = 0;
1192 y = 0;
1193
1194 while( 1 )
1195 {
1196 TRY_EOF( bmp_readint( f, 1, &b ) ); /* opcode */
1197 TRY_EOF( bmp_readint( f, 1, &c ) ); /* argument */
1198
1199 if( b > 0 )
1200 {
1201 /* repeat count */
1202 for( i = 0; i < b; i++ )
1203 {
1204 if( x >= bmpinfo.w )
1205 {
1206 x = 0;
1207 y++;
1208 }
1209
1210 if( x >= bmpinfo.w || y >= bmpinfo.h )
1211 {
1212 break;
1213 }
1214
1215 realheight = y + 1;
1216 GM_PUT( gm, x, y, COLTABLE( c ) );
1217 x++;
1218 }
1219 }
1220 else if( c == 0 )
1221 {
1222 /* end of line */
1223 y++;
1224 x = 0;
1225 }
1226 else if( c == 1 )
1227 {
1228 /* end of greymap */
1229 break;
1230 }
1231 else if( c == 2 )
1232 {
1233 /* "delta": skip pixels in x and y directions */
1234 TRY_EOF( bmp_readint( f, 1, &b ) ); /* x offset */
1235 TRY_EOF( bmp_readint( f, 1, &c ) ); /* y offset */
1236 x += b;
1237 y += c;
1238 }
1239 else
1240 {
1241 /* verbatim segment */
1242 for( i = 0; i < c; i++ )
1243 {
1244 TRY_EOF( bmp_readint( f, 1, &b ) );
1245
1246 if( x >= bmpinfo.w )
1247 {
1248 x = 0;
1249 y++;
1250 }
1251
1252 if( x >= bmpinfo.w || y >= bmpinfo.h )
1253 {
1254 break;
1255 }
1256
1257 realheight = y + 1;
1258 GM_PUT( gm, x, y, COLTABLE( b ) );
1259 x++;
1260 }
1261
1262 if( c & 1 )
1263 {
1264 /* pad input to 16-bit boundary */
1265 TRY_EOF( bmp_readint( f, 1, &b ) );
1266 }
1267 }
1268 }
1269
1270 break;
1271 } /* switch */
1272
1273 /* skip any potential junk after the data section, but don't
1274 * complain in case EOF is encountered */
1275 bmp_forward( f, bmpinfo.FileSize );
1276
1277 free( coltable );
1278
1279 if( bmpinfo.topdown )
1280 {
1281 gm_flip( gm );
1282 }
1283
1284 *gmp = gm;
1285 return 0;
1286
1287 eof:
1288 TRY_STD( gm_resize( gm, realheight ) );
1289 free( coltable );
1290
1291 if( bmpinfo.topdown )
1292 {
1293 gm_flip( gm );
1294 }
1295
1296 *gmp = gm;
1297 return 1;
1298
1299 format_error:
1300 try_error:
1301 free( coltable );
1302 gm_free( gm );
1303
1304 if( !gm_read_error )
1305 {
1306 gm_read_error = "invalid bmp file";
1307 }
1308
1309 return -2;
1310
1311 std_error:
1312 free( coltable );
1313 gm_free( gm );
1314 return -1;
1315 }
1316
1317
1318 /* ---------------------------------------------------------------------- */
1319
1320 /* write a pgm stream, either P2 or (if raw != 0) P5 format. Include
1321 * one-line comment if non-NULL. Mode determines how out-of-range
1322 * color values are converted. Gamma is the desired gamma correction,
1323 * if any (set to 2.2 if the image is to look optimal on a CRT monitor,
1324 * 2.8 for LCD). Set to 1.0 for no gamma correction */
1325
gm_writepgm(FILE * f,greymap_t * gm,const char * comment,int raw,int mode,double gamma)1326 int gm_writepgm( FILE* f, greymap_t* gm, const char* comment, int raw, int mode, double gamma )
1327 {
1328 int x, y, v;
1329 int gammatable[256];
1330
1331 /* prepare gamma correction lookup table */
1332 if( gamma != 1.0 )
1333 {
1334 gammatable[0] = 0;
1335
1336 for( v = 1; v < 256; v++ )
1337 {
1338 gammatable[v] = (int) ( 255 * exp( log( v / 255.0 ) / gamma ) + 0.5 );
1339 }
1340 }
1341 else
1342 {
1343 for( v = 0; v < 256; v++ )
1344 {
1345 gammatable[v] = v;
1346 }
1347 }
1348
1349 fprintf( f, raw ? "P5\n" : "P2\n" );
1350
1351 if( comment && *comment )
1352 {
1353 fprintf( f, "# %s\n", comment );
1354 }
1355
1356 fprintf( f, "%d %d 255\n", gm->w, gm->h );
1357
1358 for( y = gm->h - 1; y >= 0; y-- )
1359 {
1360 for( x = 0; x < gm->w; x++ )
1361 {
1362 v = GM_UGET( gm, x, y );
1363
1364 if( mode == GM_MODE_NONZERO )
1365 {
1366 if( v > 255 )
1367 {
1368 v = 510 - v;
1369 }
1370
1371 if( v < 0 )
1372 {
1373 v = 0;
1374 }
1375 }
1376 else if( mode == GM_MODE_ODD )
1377 {
1378 v = mod( v, 510 );
1379
1380 if( v > 255 )
1381 {
1382 v = 510 - v;
1383 }
1384 }
1385 else if( mode == GM_MODE_POSITIVE )
1386 {
1387 if( v < 0 )
1388 {
1389 v = 0;
1390 }
1391 else if( v > 255 )
1392 {
1393 v = 255;
1394 }
1395 }
1396 else if( mode == GM_MODE_NEGATIVE )
1397 {
1398 v = 510 - v;
1399
1400 if( v < 0 )
1401 {
1402 v = 0;
1403 }
1404 else if( v > 255 )
1405 {
1406 v = 255;
1407 }
1408 }
1409
1410 v = gammatable[v];
1411
1412 if( raw )
1413 {
1414 fputc( v, f );
1415 }
1416 else
1417 {
1418 fprintf( f, x == gm->w - 1 ? "%d\n" : "%d ", v );
1419 }
1420 }
1421 }
1422
1423 return 0;
1424 }
1425
1426
1427 /* ---------------------------------------------------------------------- */
1428 /* output - for primitive debugging purposes only! */
1429
1430 /* print greymap to screen */
gm_print(FILE * f,greymap_t * gm)1431 int gm_print( FILE* f, greymap_t* gm )
1432 {
1433 int x, y;
1434 int xx, yy;
1435 int d, t;
1436 int sw, sh;
1437
1438 sw = gm->w < 79 ? gm->w : 79;
1439 sh = gm->w < 79 ? gm->h : gm->h * sw * 44 / ( 79 * gm->w );
1440
1441 for( yy = sh - 1; yy >= 0; yy-- )
1442 {
1443 for( xx = 0; xx < sw; xx++ )
1444 {
1445 d = 0;
1446 t = 0;
1447
1448 for( x = xx * gm->w / sw; x < ( xx + 1 ) * gm->w / sw; x++ )
1449 {
1450 for( y = yy * gm->h / sh; y < ( yy + 1 ) * gm->h / sh; y++ )
1451 {
1452 d += GM_GET( gm, x, y );
1453 t += 256;
1454 }
1455 }
1456
1457 fputc( "*#=- "[5 * d / t], f ); /* what a cute trick :) */
1458 }
1459
1460 fputc( '\n', f );
1461 }
1462
1463 return 0;
1464 }
1465