/* pbm.c: * * portable bit map (pbm) format images * * jim frost 09.27.89 * * patched by W. David Higgins (wdh@mkt.csd.harris.com) to support * raw-format PBM files. * * patched by Ian MacPhedran (macphed@dvinci.usask.ca) to support * PGM and PPM files (03-July-1990) */ #include "image.h" #include "pbm.h" /* SUPPRESS 558 */ static int IntTable[256]; static unsigned int Initialized= 0; #define NOTINT -1 #define COMMENT -2 #define SPACE -3 #define NEWLINE -4 #define BADREAD 0 /* read error */ #define NOTPBM 1 /* not a pbm file */ #define PBMNORMAL 2 /* pbm normal type file */ #define PBMCOMPACT 3 /* pbm compacty type file */ #define PBMRAWBITS 4 /* pbm raw bits type file */ #define PGMNORMAL 5 /* pgm normal type file */ #define PGMRAWBITS 6 /* pgm raw bytes type file */ #define PPMNORMAL 7 /* ppm normal type file */ #define PPMRAWBITS 8 /* ppm raw bytes type file */ static void initializeTable() { unsigned int a; for (a= 0; a < 256; a++) IntTable[a]= NOTINT; IntTable['#']= COMMENT; IntTable['\n']= NEWLINE; IntTable['\r']= IntTable['\t']= IntTable[' ']= SPACE; IntTable['0']= 0; IntTable['1']= 1; IntTable['2']= 2; IntTable['3']= 3; IntTable['4']= 4; IntTable['5']= 5; IntTable['6']= 6; IntTable['7']= 7; IntTable['8']= 8; IntTable['9']= 9; Initialized= 1; } static int pbmReadChar(zf) ZFILE *zf; { int c; if ((c= zgetc(zf)) == EOF) { zclose(zf); return(-1); } if (IntTable[c] == COMMENT) do { if ((c= zgetc(zf)) == EOF) return(-1); } while (IntTable[c] != NEWLINE); return(c); } static int pbmReadInt(zf) ZFILE *zf; { int c, value; for (;;) { c= pbmReadChar(zf); if (c < 0) return(-1); if (IntTable[c] >= 0) break; }; value= IntTable[c]; for (;;) { c= pbmReadChar(zf); if (c < 0) return(-1); if (IntTable[c] < 0) return(value); value= (value * 10) + IntTable[c]; } } static int isPBM(zf, name, width, height, maxval, verbose) ZFILE *zf; char *name; int *width, *height, *maxval; unsigned int verbose; { byte buf[4]; if (! Initialized) initializeTable(); if (zread(zf, buf, 2) != 2) return(NOTPBM); if (memToVal((byte *)buf, 2) == memToVal((byte *)"P1", 2)) { if (((*width= pbmReadInt(zf)) < 0) || ((*height= pbmReadInt(zf)) < 0)) return(NOTPBM); *maxval = 1; if (verbose) printf("%s is a %dx%d PBM image\n", name, *width, *height); return(PBMNORMAL); } if (memToVal((byte *)buf, 2) == memToVal((byte *)"P4", 2)) { if (((*width= pbmReadInt(zf)) < 0) || ((*height= pbmReadInt(zf)) < 0)) return(NOTPBM); *maxval = 1; if (verbose) printf("%s is a %dx%d RawBits PBM image\n", name, *width, *height); return(PBMRAWBITS); } if (memToVal(buf, 2) == 0x2a17) { if (zread(zf, buf, 4) != 4) return(NOTPBM); *width= memToVal((byte *)buf, 2); *height= memToVal((byte *)(buf + 2), 2); *maxval = 1; if (verbose) printf("%s is a %dx%d Compact PBM image\n", name, *width, *height); return(PBMCOMPACT); } if (memToVal(buf, 2) == memToVal((byte *)"P2", 2)) { if (((*width= pbmReadInt(zf)) < 0) || ((*height= pbmReadInt(zf)) < 0)) return(NOTPBM); *maxval = pbmReadInt(zf); if (verbose) printf("%s is a %dx%d PGM image with %d levels\n", name, *width, *height, (*maxval+1)); return(PGMNORMAL); } if (memToVal(buf, 2) == memToVal((byte *)"P5", 2)) { if (((*width= pbmReadInt(zf)) < 0) || ((*height= pbmReadInt(zf)) < 0)) return(NOTPBM); *maxval = pbmReadInt(zf); if (verbose) printf("%s is a %dx%d Raw PGM image with %d levels\n", name, *width, *height, (*maxval+1)); return(PGMRAWBITS); } if (memToVal(buf, 2) == memToVal((byte *)"P3", 2)) { if (((*width= pbmReadInt(zf)) < 0) || ((*height= pbmReadInt(zf)) < 0)) return(NOTPBM); *maxval = pbmReadInt(zf); if (verbose) printf("%s is a %dx%d PPM image with %d levels\n", name, *width, *height, (*maxval+1)); return(PPMNORMAL); } if (memToVal(buf, 2) == memToVal((byte *)"P6", 2)) { if (((*width= pbmReadInt(zf)) < 0) || ((*height= pbmReadInt(zf)) < 0)) return(NOTPBM); *maxval = pbmReadInt(zf); if (verbose) printf("%s is a %dx%d Raw PPM image with %d levels\n", name, *width, *height, (*maxval+1)); return(PPMRAWBITS); } return(NOTPBM); } int pbmIdent(fullname, name) char *fullname, *name; { ZFILE *zf; int width, height, maxval, ret; if (! (zf= zopen(fullname))) return(0); ret= isPBM(zf, name, &width, &height, &maxval, (unsigned int)1); zclose(zf); return(ret != NOTPBM); } Image *pbmLoad(fullname, name, verbose) char *fullname, *name; unsigned int verbose; { ZFILE *zf; Image *image; int pbm_type; int x, y; int width, height, maxval, depth; unsigned int linelen; byte srcmask, destmask; byte *destptr, *destline; int src, size; int red, grn, blu; if (! (zf= zopen(fullname))) return(NULL); pbm_type= isPBM(zf, name, &width, &height, &maxval, verbose); if (pbm_type == NOTPBM) { zclose(zf); return(NULL); } znocache(zf); switch (pbm_type) { case PBMNORMAL: image= newBitImage(width, height); linelen= (width / 8) + (width % 8 ? 1 : 0); destline= image->data; for (y= 0; y < height; y++) { destptr= destline; destmask= 0x80; for (x= 0; x < width; x++) { do { if ((src= pbmReadChar(zf)) < 0) { fprintf(stderr, "%s: Short image\n", fullname); zclose(zf); return(image); } if (IntTable[src] == NOTINT) { fprintf(stderr, "%s: Bad image data\n", fullname); zclose(zf); return(image); } } while (IntTable[src] < 0); switch (IntTable[src]) { case 1: *destptr |= destmask; case 0: if (! (destmask >>= 1)) { destmask= 0x80; destptr++; } break; default: fprintf(stderr, "%s: Bad image data\n", fullname); zclose(zf); return(image); } } destline += linelen; } break; case PBMRAWBITS: image= newBitImage(width, height); destline= image->data; linelen= (width + 7) / 8; srcmask= 0; /* force initial read */ for (y= 0; y < height; y++) { destptr= destline; destmask= 0x80; if (srcmask != 0x80) { srcmask= 0x80; src= zgetc(zf); if (src == EOF) { fprintf(stderr, "%s: Short image\n", fullname); zclose(zf); return(image); } } for (x= 0; x < width; x++) { if (src & srcmask) *destptr |= destmask; if (! (destmask >>= 1)) { destmask= 0x80; destptr++; } if (! (srcmask >>= 1)) { srcmask= 0x80; src = zgetc(zf); if (src == EOF) { fprintf(stderr, "%s: Short image\n", fullname); zclose(zf); return(image); } } } destline += linelen; } break; case PBMCOMPACT: image= newBitImage(width, height); destline= image->data; linelen= (width / 8) + (width % 8 ? 1 : 0); srcmask= 0x80; destmask= 0x80; src = zgetc(zf); if (src == EOF) { fprintf(stderr, "%s: Short image\n", fullname); zclose(zf); return(image); } for (y= 0; y < height; y++) { destptr= destline; destmask= 0x80; for (x= 0; x < width; x++) { if (src & srcmask) *destptr |= destmask; if (! (destmask >>= 1)) { destmask= 0x80; destptr++; } if (! (srcmask >>= 1)) { srcmask= 0x80; src = zgetc(zf); if (src == EOF) { fprintf(stderr, "%s: Short image\n", fullname); zclose(zf); return(image); } } } destline += linelen; } break; case PGMRAWBITS: depth= colorsToDepth(maxval); if (depth > 8) image = newTrueImage(width, height); else { image = newRGBImage(width, height, depth); for (y = 0; y <= maxval; y++) { /* As in sunraster.c, use simple ramp for grey scale */ *(image->rgb.red + y) = PM_SCALE(y, maxval, 0xffff); *(image->rgb.green + y) = PM_SCALE(y, maxval, 0xffff); *(image->rgb.blue + y) = PM_SCALE(y, maxval, 0xffff); } image->rgb.used = maxval+1; } size= height * width; switch (image->type) { case IRGB: /* read in the image in a chunk */ if (zread(zf, image->data, size) != size) { fprintf(stderr, "%s: Short image\n", fullname); zclose(zf); freeImage(image); return(NULL); } break; case ITRUE: destptr = image->data; for (y = 0; y < size; y++) { if ((src = zgetc(zf)) == EOF) { fprintf(stderr, "%s: Short image\n", fullname); zclose(zf); freeImage(image); return(NULL); } src = PM_SCALE(src, maxval, 0xff); *(destptr++) = src; /* red */ *(destptr++) = src; /* green */ *(destptr++) = src; /* blue */ } break; } break; case PGMNORMAL: depth= colorsToDepth(maxval); if (depth > 8) image= newTrueImage(width, height); else { image= newRGBImage(width, height, depth); for (y= 0; y <= maxval; y++) { /* As in sunraster.c, use simple ramp for grey scale */ *(image->rgb.red + y) = PM_SCALE(y, maxval, 0xffff); *(image->rgb.green + y) = PM_SCALE(y, maxval, 0xffff); *(image->rgb.blue + y) = PM_SCALE(y, maxval, 0xffff); } image->rgb.used = maxval+1; } destptr= image->data; size= height * width; for (y= 0; y < size; y++) { if ((src = pbmReadInt(zf)) < 0) { fprintf(stderr, "%s: Short image\n", fullname); zclose(zf); return(image); } else { if (TRUEP(image)) { src= PM_SCALE(src, maxval, 0xff); *(destptr++) = src; /* red */ *(destptr++) = src; /* green */ *(destptr++) = src; /* blue */ } else *(destptr++) = src; } } break; case PPMRAWBITS: /* this is nice because the bit format is exactly what we want except * for scaling. */ image= newTrueImage(width, height); size= height * width * 3; if (zread(zf, image->data, size) != size) { fprintf(stderr, "%s: Short image\n", fullname); zclose(zf); freeImage(image); return(NULL); } destptr= image->data; for (y= 0; y < size; y++) { *destptr= PM_SCALE(*destptr, maxval, 0xff); destptr++; } break; case PPMNORMAL: image= newTrueImage(width, height); size= height * width; destptr= image->data; for (y= 0; y < size; y++) { if (((red= pbmReadInt(zf)) == EOF) || ((grn= pbmReadInt(zf)) == EOF) || ((blu= pbmReadInt(zf)) == EOF)) { fprintf(stderr, "%s: Short image\n", fullname); zclose(zf); return(image); } *(destptr++)= PM_SCALE(red, maxval, 0xff); *(destptr++)= PM_SCALE(grn, maxval, 0xff); *(destptr++)= PM_SCALE(blu, maxval, 0xff); } break; } image->title= dupString(name); zclose(zf); return(image); } void pbmDump(image, options, file, verbose) Image *image; char *options; char *file; int verbose; { FILE *f; byte *srcptr; int srclinelen; int x, y; int normal = 0; /* raw by default */ int white, black; int mask; Pixel pixval; int pixlen; char *name, *value; while (getNextTypeOption(&options, &name, &value) > 0) { if (!strncmp("normal", name, strlen(name))) normal = 1; else if (!strncmp("raw", name, strlen(name))) normal = 0; else fprintf(stderr, "pbmDump: unknown option '%s'\n", name); } f = fopen(file, "w"); if (f == NULL) { perror(file); return; } srcptr = image->data; switch (image->type) { case IBITMAP: if (verbose) printf("Dumping %sPBM image to %s.\n", (normal ? "" : "RawBits "), file); if (image->rgb.red[1] > image->rgb.red[0]) { white = 1; black = 0; } else { white = 0; black = 1; } if (normal) { fprintf(f, "P1 %d %d\n", image->width, image->height); mask = 0x80; for (y = 0; y < image->height; y++) { for (x = 0; x < image->width; x++) { fprintf(f, "%d ", (*srcptr & mask ? black : white)); mask >>= 1; if (mask == 0) { mask = 0x80; srcptr++; } } } } else { srclinelen = (image->width / 8) + (image->width % 8 ? 1 : 0); fprintf(f, "P4 %d %d\n", image->width, image->height); for (y = 0; y < image->height; y++) { if (black == 1) { fwrite(srcptr, srclinelen, 1, f); srcptr += srclinelen; } else for (x = 0; x < image->width; x++) fputc(*(srcptr++) ^ 0xff, f); } } break; case IRGB: if (verbose) printf("Dumping %sPPM image to %s.\n", (normal ? "" : "RawBits "), file); /* technically we could dump this with 2-byte values but that's * a lot bigger and most of the readers don't support it anyway. */ fprintf(f, "P%c %d %d 255\n", (normal ? '3' : '6'), image->width, image->height); pixlen = image->pixlen; for (y = 0; y < image->height; y++) { for (x = 0; x < image->width; x++) { pixval = memToVal(srcptr, pixlen); srcptr += pixlen; fprintf(f, (normal ? "%d %d %d " : "%c%c%c"), image->rgb.red[pixval] >> 8, image->rgb.green[pixval] >> 8, image->rgb.blue[pixval] >> 8); } } break; case ITRUE: if (verbose) printf("Dumping %sPPM image to %s.\n", (normal ? "" : "RawBits "), file); fprintf(f, "P%c %d %d 257\n", (normal ? '3' : '6'), image->width, image->height); for (y = 0; y < image->height; y++) { for (x = 0; x < image->width; x++) { if (normal) { fprintf(f, "%d %d %d ", *srcptr, *(srcptr + 1), *(srcptr + 2)); srcptr += 3; } else { fputc(*(srcptr++), f); fputc(*(srcptr++), f); fputc(*(srcptr++), f); } } } break; } fclose(f); }