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