File indexing completed on 2024-12-22 04:04:12
0001 /* Copyright (C) 2001-2019 Peter Selinger. 0002 This file is part of Potrace. It is free software and it is covered 0003 by the GNU General Public License. See the file COPYING for details. */ 0004 0005 0006 /* Routines for manipulating greymaps, including reading pgm files. We 0007 only deal with greymaps of depth 8 bits. */ 0008 0009 #ifdef HAVE_CONFIG_H 0010 #include <config.h> 0011 #endif 0012 0013 #include <stdlib.h> 0014 #include <string.h> 0015 #include <math.h> 0016 #include <errno.h> 0017 #include <stddef.h> 0018 #ifdef HAVE_INTTYPES_H 0019 #include <inttypes.h> 0020 #endif 0021 0022 #include "greymap.h" 0023 #include "bitops.h" 0024 0025 #define INTBITS (8*sizeof(int)) 0026 0027 #define mod(a,n) ((a)>=(n) ? (a)%(n) : (a)>=0 ? (a) : (n)-1-(-1-(a))%(n)) 0028 0029 static int gm_readbody_pnm(FILE *f, greymap_t **gmp, int magic); 0030 static int gm_readbody_bmp(FILE *f, greymap_t **gmp); 0031 0032 #define TRY(x) if (x) goto try_error 0033 #define TRY_EOF(x) if (x) goto eof 0034 #define TRY_STD(x) if (x) goto std_error 0035 0036 /* ---------------------------------------------------------------------- */ 0037 /* basic greymap routines */ 0038 0039 /* calculate the size, in bytes, required for the data area of a 0040 greymap of the given dy and h. Assume h >= 0. Return -1 if the size 0041 does not fit into the ptrdiff_t type. */ 0042 static inline ptrdiff_t getsize(int dy, int h) { 0043 ptrdiff_t size; 0044 0045 if (dy < 0) { 0046 dy = -dy; 0047 } 0048 0049 size = (ptrdiff_t)dy * (ptrdiff_t)h * (ptrdiff_t)GM_SAMPLESIZE; 0050 0051 /* check for overflow error */ 0052 if (size < 0 || (h != 0 && dy != 0 && size / h / dy != GM_SAMPLESIZE)) { 0053 return -1; 0054 } 0055 0056 return size; 0057 } 0058 0059 /* return the size, in bytes, of the data area of the greymap. Return 0060 -1 if the size does not fit into the ptrdiff_t type; however, this 0061 cannot happen if the bitmap is well-formed, i.e., if created with 0062 gm_new or gm_dup. */ 0063 static inline ptrdiff_t gm_size(const greymap_t *gm) { 0064 return getsize(gm->dy, gm->h); 0065 } 0066 0067 0068 0069 /* return new greymap initialized to 0. NULL with errno on error. 0070 Assumes w, h >= 0. */ 0071 greymap_t *gm_new(int w, int h) { 0072 greymap_t *gm; 0073 int dy = w; 0074 ptrdiff_t size; 0075 0076 size = getsize(dy, h); 0077 if (size < 0) { 0078 errno = ENOMEM; 0079 return NULL; 0080 } 0081 if (size == 0) { 0082 size = GM_SAMPLESIZE; /* make sure calloc() doesn't return NULL */ 0083 } 0084 0085 gm = (greymap_t *) malloc(sizeof(greymap_t)); 0086 if (!gm) { 0087 return NULL; 0088 } 0089 gm->w = w; 0090 gm->h = h; 0091 gm->dy = dy; 0092 gm->base = (gm_sample_t *) calloc(1, size); 0093 if (!gm->base) { 0094 free(gm); 0095 return NULL; 0096 } 0097 gm->map = gm->base; 0098 return gm; 0099 } 0100 0101 /* free the given greymap */ 0102 void gm_free(greymap_t *gm) { 0103 if (gm) { 0104 free(gm->base); 0105 } 0106 free(gm); 0107 } 0108 0109 /* duplicate the given greymap. Return NULL on error with errno set. */ 0110 greymap_t *gm_dup(greymap_t *gm) { 0111 greymap_t *gm1 = gm_new(gm->w, gm->h); 0112 int y; 0113 0114 if (!gm1) { 0115 return NULL; 0116 } 0117 for (y=0; y<gm->h; y++) { 0118 memcpy(gm_scanline(gm1, y), gm_scanline(gm, y), (size_t)gm1->dy * GM_SAMPLESIZE); 0119 } 0120 return gm1; 0121 } 0122 0123 /* clear the given greymap to color b. */ 0124 void gm_clear(greymap_t *gm, int b) { 0125 ptrdiff_t size = gm_size(gm); 0126 int x, y; 0127 0128 if (b==0) { 0129 memset(gm->base, 0, size); 0130 } else { 0131 for (y=0; y<gm->h; y++) { 0132 for (x=0; x<gm->w; x++) { 0133 GM_UPUT(gm, x, y, b); 0134 } 0135 } 0136 } 0137 } 0138 0139 /* turn the given greymap upside down. This does not move the pixel 0140 data or change the base address. */ 0141 static inline void gm_flip(greymap_t *gm) { 0142 int dy = gm->dy; 0143 0144 if (gm->h == 0 || gm->h == 1) { 0145 return; 0146 } 0147 0148 gm->map = gm_scanline(gm, gm->h - 1); 0149 gm->dy = -dy; 0150 } 0151 0152 /* resize the greymap to the given new height. The pixel data remains 0153 bottom-aligned (truncated at the top) when dy >= 0 and top-aligned 0154 (truncated at the bottom) when dy < 0. Return 0 on success, or 1 on 0155 error with errno set. If the new height is <= the old one, no error 0156 should occur. If the new height is larger, the additional pixel 0157 data is *not* initialized. */ 0158 static inline int gm_resize(greymap_t *gm, int h) { 0159 int dy = gm->dy; 0160 ptrdiff_t newsize; 0161 gm_sample_t *newbase; 0162 0163 if (dy < 0) { 0164 gm_flip(gm); 0165 } 0166 0167 newsize = getsize(dy, h); 0168 if (newsize < 0) { 0169 errno = ENOMEM; 0170 goto error; 0171 } 0172 if (newsize == 0) { 0173 newsize = GM_SAMPLESIZE; /* make sure realloc() doesn't return NULL */ 0174 } 0175 0176 newbase = (gm_sample_t *)realloc(gm->base, newsize); 0177 if (newbase == NULL) { 0178 goto error; 0179 } 0180 gm->base = newbase; 0181 gm->map = newbase; 0182 gm->h = h; 0183 0184 if (dy < 0) { 0185 gm_flip(gm); 0186 } 0187 return 0; 0188 0189 error: 0190 if (dy < 0) { 0191 gm_flip(gm); 0192 } 0193 return 1; 0194 } 0195 0196 0197 /* ---------------------------------------------------------------------- */ 0198 /* routines for reading pnm streams */ 0199 0200 /* read next character after whitespace and comments. Return EOF on 0201 end of file or error. */ 0202 static int fgetc_ws(FILE *f) { 0203 int c; 0204 0205 while (1) { 0206 c = fgetc(f); 0207 if (c=='#') { 0208 while (1) { 0209 c = fgetc(f); 0210 if (c=='\n' || c==EOF) { 0211 break; 0212 } 0213 } 0214 } 0215 /* space, tab, line feed, carriage return, form-feed */ 0216 if (c!=' ' && c!='\t' && c!='\r' && c!='\n' && c!=12) { 0217 return c; 0218 } 0219 } 0220 } 0221 0222 /* skip whitespace and comments, then read a non-negative decimal 0223 number from a stream. Return -1 on EOF. Tolerate other errors (skip 0224 bad characters). Do not the read any characters following the 0225 number (put next character back into the stream) */ 0226 0227 static int readnum(FILE *f) { 0228 int c; 0229 uint64_t acc; 0230 0231 /* skip whitespace and comments */ 0232 while (1) { 0233 c = fgetc_ws(f); 0234 if (c==EOF) { 0235 return -1; 0236 } 0237 if (c>='0' && c<='9') { 0238 break; 0239 } 0240 } 0241 0242 /* first digit is already in c */ 0243 acc = c-'0'; 0244 while (1) { 0245 c = fgetc(f); 0246 if (c==EOF) { 0247 break; 0248 } 0249 if (c<'0' || c>'9') { 0250 ungetc(c, f); 0251 break; 0252 } 0253 acc *= 10; 0254 acc += c-'0'; 0255 if (acc > 0x7fffffff) { 0256 return -1; 0257 } 0258 } 0259 return acc; 0260 } 0261 0262 /* similar to readnum, but read only a single 0 or 1, and do not read 0263 any characters after it. */ 0264 0265 static int readbit(FILE *f) { 0266 int c; 0267 0268 /* skip whitespace and comments */ 0269 while (1) { 0270 c = fgetc_ws(f); 0271 if (c==EOF) { 0272 return -1; 0273 } 0274 if (c>='0' && c<='1') { 0275 break; 0276 } 0277 } 0278 0279 return c-'0'; 0280 } 0281 0282 /* ---------------------------------------------------------------------- */ 0283 0284 /* read a PNM stream: P1-P6 format (see pnm(5)), or a BMP stream, and 0285 convert the output to a greymap. Return greymap in *gmp. Return 0 0286 on success, -1 on error with errno set, -2 on bad file format (with 0287 error message in gm_read_error), and 1 on premature end of file, -3 0288 on empty file (including files with only whitespace and comments), 0289 -4 if wrong magic number. If the return value is >=0, *gmp is 0290 valid. */ 0291 0292 const char *gm_read_error = NULL; 0293 0294 int gm_read(FILE *f, greymap_t **gmp) { 0295 int magic[2]; 0296 0297 /* read magic number. We ignore whitespace and comments before the 0298 magic, for the benefit of concatenated files in P1-P3 format. 0299 Multiple P1-P3 images in a single file are not formally allowed 0300 by the PNM standard, but there is no harm in being lenient. */ 0301 0302 magic[0] = fgetc_ws(f); 0303 if (magic[0] == EOF) { 0304 /* files which contain only comments and whitespace count as "empty" */ 0305 return -3; 0306 } 0307 magic[1] = fgetc(f); 0308 if (magic[0] == 'P' && magic[1] >= '1' && magic[1] <= '6') { 0309 return gm_readbody_pnm(f, gmp, magic[1]); 0310 } 0311 if (magic[0] == 'B' && magic[1] == 'M') { 0312 return gm_readbody_bmp(f, gmp); 0313 } 0314 return -4; 0315 } 0316 0317 /* ---------------------------------------------------------------------- */ 0318 /* read PNM format */ 0319 0320 /* read PNM stream after magic number. Return values as for gm_read */ 0321 static int gm_readbody_pnm(FILE *f, greymap_t **gmp, int magic) { 0322 greymap_t *gm; 0323 int x, y, i, j, b, b1, sum; 0324 int bpr; /* bytes per row (as opposed to 4*gm->c) */ 0325 int w, h, max; 0326 int realheight; /* in case of incomplete file, keeps track of how 0327 many scan lines actually contain data */ 0328 0329 gm = NULL; 0330 0331 w = readnum(f); 0332 if (w<0) { 0333 goto format_error; 0334 } 0335 0336 h = readnum(f); 0337 if (h<0) { 0338 goto format_error; 0339 } 0340 0341 /* allocate greymap */ 0342 gm = gm_new(w, h); 0343 if (!gm) { 0344 goto std_error; 0345 } 0346 0347 realheight = 0; 0348 0349 switch (magic) { 0350 default: 0351 /* not reached */ 0352 goto format_error; 0353 0354 case '1': 0355 /* read P1 format: PBM ascii */ 0356 0357 for (y=0; y<h; y++) { 0358 realheight = y+1; 0359 for (x=0; x<w; x++) { 0360 b = readbit(f); 0361 if (b<0) { 0362 goto eof; 0363 } 0364 GM_UPUT(gm, x, y, b ? 0 : 255); 0365 } 0366 } 0367 break; 0368 0369 case '2': 0370 /* read P2 format: PGM ascii */ 0371 0372 max = readnum(f); 0373 if (max<1) { 0374 goto format_error; 0375 } 0376 0377 for (y=0; y<h; y++) { 0378 realheight = y+1; 0379 for (x=0; x<w; x++) { 0380 b = readnum(f); 0381 if (b<0) { 0382 goto eof; 0383 } 0384 GM_UPUT(gm, x, y, b*255/max); 0385 } 0386 } 0387 break; 0388 0389 case '3': 0390 /* read P3 format: PPM ascii */ 0391 0392 max = readnum(f); 0393 if (max<1) { 0394 goto format_error; 0395 } 0396 0397 for (y=0; y<h; y++) { 0398 realheight = y+1; 0399 for (x=0; x<w; x++) { 0400 sum = 0; 0401 for (i=0; i<3; i++) { 0402 b = readnum(f); 0403 if (b<0) { 0404 goto eof; 0405 } 0406 sum += b; 0407 } 0408 GM_UPUT(gm, x, y, sum*(255/3)/max); 0409 } 0410 } 0411 break; 0412 0413 case '4': 0414 /* read P4 format: PBM raw */ 0415 0416 b = fgetc(f); /* read single white-space character after height */ 0417 if (b==EOF) { 0418 goto format_error; 0419 } 0420 0421 bpr = (w+7)/8; 0422 0423 for (y=0; y<h; y++) { 0424 realheight = y+1; 0425 for (i=0; i<bpr; i++) { 0426 b = fgetc(f); 0427 if (b==EOF) { 0428 goto eof; 0429 } 0430 for (j=0; j<8; j++) { 0431 GM_PUT(gm, i*8+j, y, b & (0x80 >> j) ? 0 : 255); 0432 } 0433 } 0434 } 0435 break; 0436 0437 case '5': 0438 /* read P5 format: PGM raw */ 0439 0440 max = readnum(f); 0441 if (max<1) { 0442 goto format_error; 0443 } 0444 0445 b = fgetc(f); /* read single white-space character after max */ 0446 if (b==EOF) { 0447 goto format_error; 0448 } 0449 0450 for (y=0; y<h; y++) { 0451 realheight = y+1; 0452 for (x=0; x<w; x++) { 0453 b = fgetc(f); 0454 if (b==EOF) 0455 goto eof; 0456 if (max>=256) { 0457 b <<= 8; 0458 b1 = fgetc(f); 0459 if (b1==EOF) 0460 goto eof; 0461 b |= b1; 0462 } 0463 GM_UPUT(gm, x, y, b*255/max); 0464 } 0465 } 0466 break; 0467 0468 case '6': 0469 /* read P6 format: PPM raw */ 0470 0471 max = readnum(f); 0472 if (max<1) { 0473 goto format_error; 0474 } 0475 0476 b = fgetc(f); /* read single white-space character after max */ 0477 if (b==EOF) { 0478 goto format_error; 0479 } 0480 0481 for (y=0; y<h; y++) { 0482 realheight = y+1; 0483 for (x=0; x<w; x++) { 0484 sum = 0; 0485 for (i=0; i<3; i++) { 0486 b = fgetc(f); 0487 if (b==EOF) { 0488 goto eof; 0489 } 0490 if (max>=256) { 0491 b <<= 8; 0492 b1 = fgetc(f); 0493 if (b1==EOF) 0494 goto eof; 0495 b |= b1; 0496 } 0497 sum += b; 0498 } 0499 GM_UPUT(gm, x, y, sum*(255/3)/max); 0500 } 0501 } 0502 break; 0503 } 0504 0505 gm_flip(gm); 0506 *gmp = gm; 0507 return 0; 0508 0509 eof: 0510 TRY_STD(gm_resize(gm, realheight)); 0511 gm_flip(gm); 0512 *gmp = gm; 0513 return 1; 0514 0515 format_error: 0516 gm_free(gm); 0517 if (magic == '1' || magic == '4') { 0518 gm_read_error = "invalid pbm file"; 0519 } else if (magic == '2' || magic == '5') { 0520 gm_read_error = "invalid pgm file"; 0521 } else { 0522 gm_read_error = "invalid ppm file"; 0523 } 0524 return -2; 0525 0526 std_error: 0527 gm_free(gm); 0528 return -1; 0529 } 0530 0531 /* ---------------------------------------------------------------------- */ 0532 /* read BMP format */ 0533 0534 struct bmp_info_s { 0535 unsigned int FileSize; 0536 unsigned int reserved; 0537 unsigned int DataOffset; 0538 unsigned int InfoSize; 0539 unsigned int w; /* width */ 0540 unsigned int h; /* height */ 0541 unsigned int Planes; 0542 unsigned int bits; /* bits per sample */ 0543 unsigned int comp; /* compression mode */ 0544 unsigned int ImageSize; 0545 unsigned int XpixelsPerM; 0546 unsigned int YpixelsPerM; 0547 unsigned int ncolors; /* number of colors in palette */ 0548 unsigned int ColorsImportant; 0549 unsigned int RedMask; 0550 unsigned int GreenMask; 0551 unsigned int BlueMask; 0552 unsigned int AlphaMask; 0553 unsigned int ctbits; /* sample size for color table */ 0554 int topdown; /* top-down mode? */ 0555 }; 0556 typedef struct bmp_info_s bmp_info_t; 0557 0558 /* auxiliary */ 0559 0560 static int bmp_count = 0; /* counter for byte padding */ 0561 static int bmp_pos = 0; /* counter from start of BMP data */ 0562 0563 /* read n-byte little-endian integer. Return 1 on EOF or error, else 0564 0. Assume n<=4. */ 0565 static int bmp_readint(FILE *f, int n, unsigned int *p) { 0566 int i; 0567 unsigned int sum = 0; 0568 int b; 0569 0570 for (i=0; i<n; i++) { 0571 b = fgetc(f); 0572 if (b==EOF) { 0573 return 1; 0574 } 0575 sum += (unsigned)b << (8*i); 0576 } 0577 bmp_count += n; 0578 bmp_pos += n; 0579 *p = sum; 0580 return 0; 0581 } 0582 0583 /* reset padding boundary */ 0584 static void bmp_pad_reset(void) { 0585 bmp_count = 0; 0586 } 0587 0588 /* read padding bytes to 4-byte boundary. Return 1 on EOF or error, 0589 else 0. */ 0590 static int bmp_pad(FILE *f) { 0591 int c, i, b; 0592 0593 c = (-bmp_count) & 3; 0594 for (i=0; i<c; i++) { 0595 b = fgetc(f); 0596 if (b==EOF) { 0597 return 1; 0598 } 0599 } 0600 bmp_pos += c; 0601 bmp_count = 0; 0602 return 0; 0603 } 0604 0605 /* forward to the new file position. Return 1 on EOF or error, else 0 */ 0606 static int bmp_forward(FILE *f, int pos) { 0607 int b; 0608 0609 while (bmp_pos < pos) { 0610 b = fgetc(f); 0611 if (b==EOF) { 0612 return 1; 0613 } 0614 bmp_pos++; 0615 bmp_count++; 0616 } 0617 return 0; 0618 } 0619 0620 /* safe colortable access */ 0621 #define COLTABLE(c) ((c) < bmpinfo.ncolors ? coltable[(c)] : 0) 0622 0623 /* read BMP stream after magic number. Return values as for gm_read. 0624 We choose to be as permissive as possible, since there are many 0625 programs out there which produce BMP. For instance, ppmtobmp can 0626 produce codings with anywhere from 1-8 or 24 bits per sample, 0627 although most specifications only allow 1,4,8,24,32. We can also 0628 read both the old and new OS/2 BMP formats in addition to the 0629 Windows BMP format. */ 0630 static int gm_readbody_bmp(FILE *f, greymap_t **gmp) { 0631 bmp_info_t bmpinfo; 0632 int *coltable; 0633 unsigned int b, c; 0634 unsigned int i, j; 0635 greymap_t *gm; 0636 unsigned int x, y; 0637 int col[2]; 0638 unsigned int bitbuf; 0639 unsigned int n; 0640 unsigned int redshift, greenshift, blueshift; 0641 int realheight; /* in case of incomplete file, keeps track of how 0642 many scan lines actually contain data */ 0643 0644 gm_read_error = NULL; 0645 gm = NULL; 0646 coltable = NULL; 0647 0648 bmp_pos = 2; /* set file position */ 0649 0650 /* file header (minus magic number) */ 0651 TRY(bmp_readint(f, 4, &bmpinfo.FileSize)); 0652 TRY(bmp_readint(f, 4, &bmpinfo.reserved)); 0653 TRY(bmp_readint(f, 4, &bmpinfo.DataOffset)); 0654 0655 /* info header */ 0656 TRY(bmp_readint(f, 4, &bmpinfo.InfoSize)); 0657 if (bmpinfo.InfoSize == 40 || bmpinfo.InfoSize == 64 0658 || bmpinfo.InfoSize == 108 || bmpinfo.InfoSize == 124) { 0659 /* Windows or new OS/2 format */ 0660 bmpinfo.ctbits = 32; /* sample size in color table */ 0661 TRY(bmp_readint(f, 4, &bmpinfo.w)); 0662 TRY(bmp_readint(f, 4, &bmpinfo.h)); 0663 TRY(bmp_readint(f, 2, &bmpinfo.Planes)); 0664 TRY(bmp_readint(f, 2, &bmpinfo.bits)); 0665 TRY(bmp_readint(f, 4, &bmpinfo.comp)); 0666 TRY(bmp_readint(f, 4, &bmpinfo.ImageSize)); 0667 TRY(bmp_readint(f, 4, &bmpinfo.XpixelsPerM)); 0668 TRY(bmp_readint(f, 4, &bmpinfo.YpixelsPerM)); 0669 TRY(bmp_readint(f, 4, &bmpinfo.ncolors)); 0670 TRY(bmp_readint(f, 4, &bmpinfo.ColorsImportant)); 0671 if (bmpinfo.InfoSize >= 108) { /* V4 and V5 bitmaps */ 0672 TRY(bmp_readint(f, 4, &bmpinfo.RedMask)); 0673 TRY(bmp_readint(f, 4, &bmpinfo.GreenMask)); 0674 TRY(bmp_readint(f, 4, &bmpinfo.BlueMask)); 0675 TRY(bmp_readint(f, 4, &bmpinfo.AlphaMask)); 0676 } 0677 if (bmpinfo.w > 0x7fffffff) { 0678 goto format_error; 0679 } 0680 if (bmpinfo.h > 0x7fffffff) { 0681 bmpinfo.h = (-bmpinfo.h) & 0xffffffff; 0682 bmpinfo.topdown = 1; 0683 } else { 0684 bmpinfo.topdown = 0; 0685 } 0686 if (bmpinfo.h > 0x7fffffff) { 0687 goto format_error; 0688 } 0689 } else if (bmpinfo.InfoSize == 12) { 0690 /* old OS/2 format */ 0691 bmpinfo.ctbits = 24; /* sample size in color table */ 0692 TRY(bmp_readint(f, 2, &bmpinfo.w)); 0693 TRY(bmp_readint(f, 2, &bmpinfo.h)); 0694 TRY(bmp_readint(f, 2, &bmpinfo.Planes)); 0695 TRY(bmp_readint(f, 2, &bmpinfo.bits)); 0696 bmpinfo.comp = 0; 0697 bmpinfo.ncolors = 0; 0698 bmpinfo.topdown = 0; 0699 } else { 0700 goto format_error; 0701 } 0702 0703 if (bmpinfo.comp == 3 && bmpinfo.InfoSize < 108) { 0704 /* bitfield feature is only understood with V4 and V5 format */ 0705 goto format_error; 0706 } 0707 0708 if (bmpinfo.comp > 3 || bmpinfo.bits > 32) { 0709 goto format_error; 0710 } 0711 0712 /* forward to color table (e.g., if bmpinfo.InfoSize == 64) */ 0713 TRY(bmp_forward(f, 14+bmpinfo.InfoSize)); 0714 0715 if (bmpinfo.Planes != 1) { 0716 gm_read_error = "cannot handle bmp planes"; 0717 goto format_error; /* can't handle planes */ 0718 } 0719 0720 if (bmpinfo.ncolors == 0 && bmpinfo.bits <= 8) { 0721 bmpinfo.ncolors = 1 << bmpinfo.bits; 0722 } 0723 0724 /* color table, present only if bmpinfo.bits <= 8. */ 0725 if (bmpinfo.bits <= 8) { 0726 coltable = (int *) calloc(bmpinfo.ncolors, sizeof(int)); 0727 if (!coltable) { 0728 goto std_error; 0729 } 0730 /* NOTE: since we are reading a greymap, we can immediately convert 0731 the color table entries to grey values. */ 0732 for (i=0; i<bmpinfo.ncolors; i++) { 0733 TRY(bmp_readint(f, bmpinfo.ctbits/8, &c)); 0734 c = ((c>>16) & 0xff) + ((c>>8) & 0xff) + (c & 0xff); 0735 coltable[i] = c/3; 0736 } 0737 } 0738 0739 /* forward to data */ 0740 if (bmpinfo.InfoSize != 12) { /* not old OS/2 format */ 0741 TRY(bmp_forward(f, bmpinfo.DataOffset)); 0742 } 0743 0744 /* allocate greymap */ 0745 gm = gm_new(bmpinfo.w, bmpinfo.h); 0746 if (!gm) { 0747 goto std_error; 0748 } 0749 0750 realheight = 0; 0751 0752 switch (bmpinfo.bits + 0x100*bmpinfo.comp) { 0753 0754 default: 0755 goto format_error; 0756 break; 0757 0758 case 0x001: /* monochrome palette */ 0759 0760 /* raster data */ 0761 for (y=0; y<bmpinfo.h; y++) { 0762 realheight = y+1; 0763 bmp_pad_reset(); 0764 for (i=0; 8*i<bmpinfo.w; i++) { 0765 TRY_EOF(bmp_readint(f, 1, &b)); 0766 for (j=0; j<8; j++) { 0767 GM_PUT(gm, i*8+j, y, b & (0x80 >> j) ? COLTABLE(1) : COLTABLE(0)); 0768 } 0769 } 0770 TRY(bmp_pad(f)); 0771 } 0772 break; 0773 0774 case 0x002: /* 2-bit to 8-bit palettes */ 0775 case 0x003: 0776 case 0x004: 0777 case 0x005: 0778 case 0x006: 0779 case 0x007: 0780 case 0x008: 0781 for (y=0; y<bmpinfo.h; y++) { 0782 realheight = y+1; 0783 bmp_pad_reset(); 0784 bitbuf = 0; /* bit buffer: bits in buffer are high-aligned */ 0785 n = 0; /* number of bits currently in bitbuffer */ 0786 for (x=0; x<bmpinfo.w; x++) { 0787 if (n < bmpinfo.bits) { 0788 TRY_EOF(bmp_readint(f, 1, &b)); 0789 bitbuf |= b << (INTBITS - 8 - n); 0790 n += 8; 0791 } 0792 b = bitbuf >> (INTBITS - bmpinfo.bits); 0793 bitbuf <<= bmpinfo.bits; 0794 n -= bmpinfo.bits; 0795 GM_UPUT(gm, x, y, COLTABLE(b)); 0796 } 0797 TRY(bmp_pad(f)); 0798 } 0799 break; 0800 0801 case 0x010: /* 16-bit encoding */ 0802 /* can't do this format because it is not well-documented and I 0803 don't have any samples */ 0804 gm_read_error = "cannot handle bmp 16-bit coding"; 0805 goto format_error; 0806 break; 0807 0808 case 0x018: /* 24-bit encoding */ 0809 case 0x020: /* 32-bit encoding */ 0810 for (y=0; y<bmpinfo.h; y++) { 0811 realheight = y+1; 0812 bmp_pad_reset(); 0813 for (x=0; x<bmpinfo.w; x++) { 0814 TRY_EOF(bmp_readint(f, bmpinfo.bits/8, &c)); 0815 c = ((c>>16) & 0xff) + ((c>>8) & 0xff) + (c & 0xff); 0816 GM_UPUT(gm, x, y, c/3); 0817 } 0818 TRY(bmp_pad(f)); 0819 } 0820 break; 0821 0822 case 0x320: /* 32-bit encoding with bitfields */ 0823 redshift = lobit(bmpinfo.RedMask); 0824 greenshift = lobit(bmpinfo.GreenMask); 0825 blueshift = lobit(bmpinfo.BlueMask); 0826 0827 for (y=0; y<bmpinfo.h; y++) { 0828 realheight = y+1; 0829 bmp_pad_reset(); 0830 for (x=0; x<bmpinfo.w; x++) { 0831 TRY_EOF(bmp_readint(f, bmpinfo.bits/8, &c)); 0832 c = ((c & bmpinfo.RedMask) >> redshift) + ((c & bmpinfo.GreenMask) >> greenshift) + ((c & bmpinfo.BlueMask) >> blueshift); 0833 GM_UPUT(gm, x, y, c/3); 0834 } 0835 TRY(bmp_pad(f)); 0836 } 0837 break; 0838 0839 case 0x204: /* 4-bit runlength compressed encoding (RLE4) */ 0840 x = 0; 0841 y = 0; 0842 while (1) { 0843 TRY_EOF(bmp_readint(f, 1, &b)); /* opcode */ 0844 TRY_EOF(bmp_readint(f, 1, &c)); /* argument */ 0845 if (b>0) { 0846 /* repeat count */ 0847 col[0] = COLTABLE((c>>4) & 0xf); 0848 col[1] = COLTABLE(c & 0xf); 0849 for (i=0; i<b && x<bmpinfo.w; i++) { 0850 if (x>=bmpinfo.w) { 0851 x=0; 0852 y++; 0853 } 0854 if (x>=bmpinfo.w || y>=bmpinfo.h) { 0855 break; 0856 } 0857 realheight = y+1; 0858 GM_PUT(gm, x, y, col[i&1]); 0859 x++; 0860 } 0861 } else if (c == 0) { 0862 /* end of line */ 0863 y++; 0864 x = 0; 0865 } else if (c == 1) { 0866 /* end of greymap */ 0867 break; 0868 } else if (c == 2) { 0869 /* "delta": skip pixels in x and y directions */ 0870 TRY_EOF(bmp_readint(f, 1, &b)); /* x offset */ 0871 TRY_EOF(bmp_readint(f, 1, &c)); /* y offset */ 0872 x += b; 0873 y += c; 0874 } else { 0875 /* verbatim segment */ 0876 for (i=0; i<c; i++) { 0877 if ((i&1)==0) { 0878 TRY_EOF(bmp_readint(f, 1, &b)); 0879 } 0880 if (x>=bmpinfo.w) { 0881 x=0; 0882 y++; 0883 } 0884 if (x>=bmpinfo.w || y>=bmpinfo.h) { 0885 break; 0886 } 0887 realheight = y+1; 0888 GM_PUT(gm, x, y, COLTABLE((b>>(4-4*(i&1))) & 0xf)); 0889 x++; 0890 } 0891 if ((c+1) & 2) { 0892 /* pad to 16-bit boundary */ 0893 TRY_EOF(bmp_readint(f, 1, &b)); 0894 } 0895 } 0896 } 0897 break; 0898 0899 case 0x108: /* 8-bit runlength compressed encoding (RLE8) */ 0900 x = 0; 0901 y = 0; 0902 while (1) { 0903 TRY_EOF(bmp_readint(f, 1, &b)); /* opcode */ 0904 TRY_EOF(bmp_readint(f, 1, &c)); /* argument */ 0905 if (b>0) { 0906 /* repeat count */ 0907 for (i=0; i<b; i++) { 0908 if (x>=bmpinfo.w) { 0909 x=0; 0910 y++; 0911 } 0912 if (x>=bmpinfo.w || y>=bmpinfo.h) { 0913 break; 0914 } 0915 realheight = y+1; 0916 GM_PUT(gm, x, y, COLTABLE(c)); 0917 x++; 0918 } 0919 } else if (c == 0) { 0920 /* end of line */ 0921 y++; 0922 x = 0; 0923 } else if (c == 1) { 0924 /* end of greymap */ 0925 break; 0926 } else if (c == 2) { 0927 /* "delta": skip pixels in x and y directions */ 0928 TRY_EOF(bmp_readint(f, 1, &b)); /* x offset */ 0929 TRY_EOF(bmp_readint(f, 1, &c)); /* y offset */ 0930 x += b; 0931 y += c; 0932 } else { 0933 /* verbatim segment */ 0934 for (i=0; i<c; i++) { 0935 TRY_EOF(bmp_readint(f, 1, &b)); 0936 if (x>=bmpinfo.w) { 0937 x=0; 0938 y++; 0939 } 0940 if (x>=bmpinfo.w || y>=bmpinfo.h) { 0941 break; 0942 } 0943 realheight = y+1; 0944 GM_PUT(gm, x, y, COLTABLE(b)); 0945 x++; 0946 } 0947 if (c & 1) { 0948 /* pad input to 16-bit boundary */ 0949 TRY_EOF(bmp_readint(f, 1, &b)); 0950 } 0951 } 0952 } 0953 break; 0954 0955 } /* switch */ 0956 0957 /* skip any potential junk after the data section, but don't 0958 complain in case EOF is encountered */ 0959 bmp_forward(f, bmpinfo.FileSize); 0960 0961 free(coltable); 0962 if (bmpinfo.topdown) { 0963 gm_flip(gm); 0964 } 0965 *gmp = gm; 0966 return 0; 0967 0968 eof: 0969 TRY_STD(gm_resize(gm, realheight)); 0970 free(coltable); 0971 if (bmpinfo.topdown) { 0972 gm_flip(gm); 0973 } 0974 *gmp = gm; 0975 return 1; 0976 0977 format_error: 0978 try_error: 0979 free(coltable); 0980 gm_free(gm); 0981 if (!gm_read_error) { 0982 gm_read_error = "invalid bmp file"; 0983 } 0984 return -2; 0985 0986 std_error: 0987 free(coltable); 0988 gm_free(gm); 0989 return -1; 0990 } 0991 0992 /* ---------------------------------------------------------------------- */ 0993 0994 /* write a pgm stream, either P2 or (if raw != 0) P5 format. Include 0995 one-line comment if non-NULL. Mode determines how out-of-range 0996 color values are converted. Gamma is the desired gamma correction, 0997 if any (set to 2.2 if the image is to look optimal on a CRT monitor, 0998 2.8 for LCD). Set to 1.0 for no gamma correction */ 0999 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 */ 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 }