/* * xvpbm.c - load routine for 'pm' format pictures * * LoadPBM(fname, pinfo) - loads a PBM, PGM, or PPM file * WritePBM(fp,pic,ptype,w,h,r,g,b,numcols,style,raw,cmt,comment) */ /* Copyright Notice * ================ * Copyright 1989, 1990, 1991, 1992, 1993 by John Bradley * * Permission to use, copy, and distribute XV in its entirety, for * non-commercial purposes, is hereby granted without fee, provided that * this license information and copyright notice appear in all copies. * * Note that distributing XV 'bundled' in with ANY product is considered * to be a 'commercial purpose'. * * Also note that any copies of XV that are distributed MUST be built * and/or configured to be in their 'unregistered copy' mode, so that it * is made obvious to the user that XV is shareware, and that they should * consider donating, or at least reading this License Info. * * The software may be modified for your own purposes, but modified * versions may NOT be distributed without prior consent of the author. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the author be held liable for any damages * arising from the use of this software. * * If you would like to do something with XV that this copyright * prohibits (such as distributing it with a commercial product, * using portions of the source in some other program, etc.), please * contact the author (preferably via email). Arrangements can * probably be worked out. * * XV is shareware for PERSONAL USE only. You may use XV for your own * amusement, and if you find it nifty, useful, generally cool, or of * some value to you, your non-deductable donation would be greatly * appreciated. $25 is the suggested donation, though, of course, * larger donations are quite welcome. Folks who donate $25 or more * can receive a Real Nice bound copy of the XV manual for no extra * charge. * * Commercial, government, and institutional users MUST register their * copies of XV, for the exceedingly REASONABLE price of just $25 per * workstation/X terminal. Site licenses are available for those who * wish to run XV on a large number of machines. Contact the author * for more details. * * The author may be contacted via: * US Mail: John Bradley * 1053 Floyd Terrace * Bryn Mawr, PA 19010 * * Phone: (215) 898-8813 * EMail: bradley@cis.upenn.edu */ #include "xv.h" /* comments on error handling: a truncated file is not considered a Major Error. The file is loaded, the rest of the pic is filled with 0's. a file with garbage characters in it is an unloadable file. All allocated stuff is tossed, and LoadPBM returns non-zero not being able to malloc is a Fatal Error. The program is aborted. */ #define TRUNCSTR "File appears to be truncated." static int garbage; static long numgot, filesize; #ifdef __STDC__ static int loadpbm(FILE *, PICINFO *, int); static int loadpgm(FILE *, PICINFO *, int, int); static int loadppm(FILE *, PICINFO *, int, int); static int getint (FILE *, PICINFO *); static int getbit (FILE *, PICINFO *); static int pbmError(char *, char *); #else static int loadpbm(), loadpgm(), loadppm(); static int getint(), getbit(); static int pbmError(); #endif static char *bname; /*******************************************/ int LoadPBM(fname, pinfo) char *fname; PICINFO *pinfo; /*******************************************/ { /* returns '1' on success */ FILE *fp; int c, c1; int maxv, rv; garbage = maxv = 0; bname = BaseName(fname); pinfo->pic = (byte *) NULL; pinfo->comment = (char *) NULL; /* open the file */ fp=fopen(fname,"r"); if (!fp) return (pbmError(bname, "can't open file")); /* compute file length */ fseek(fp, 0L, 2); filesize = ftell(fp); fseek(fp, 0L, 0); /* read the first two bytes of the file to determine which format this file is. "P1" = ascii bitmap, "P2" = ascii greymap, "P3" = ascii pixmap, "P4" = raw bitmap, "P5" = raw greymap, "P6" = raw pixmap */ c = getc(fp); c1 = getc(fp); if (c!='P' || c1<'1' || c1>'6') return(pbmError(bname, "unknown format")); /* read in header information */ pinfo->w = getint(fp, pinfo); pinfo->h = getint(fp, pinfo); /* if we're not reading a bitmap, read the 'max value' */ if ( !(c1=='1' || c1=='4')) { maxv = getint(fp, pinfo); if (maxv < 1) garbage=1; /* to avoid 'div by zero' probs */ } if (garbage) { fclose(fp); if (pinfo->comment) free(pinfo->comment); pinfo->comment = (char *) NULL; return (pbmError(bname, "Garbage characters in header.")); } if (c1=='1' || c1=='2' || c1=='3') pinfo->frmType = F_PBMASCII; else pinfo->frmType = F_PBMRAW; /* note: pic, type, r,g,b, frmInfo, shortFrm, and colorType fields of picinfo struct are filled in in the format-specific loaders */ /* call the appropriate subroutine to handle format-specific stuff */ if (c1=='1' || c1=='4') rv = loadpbm(fp, pinfo, c1=='4' ? 1 : 0); else if (c1=='2' || c1=='5') rv = loadpgm(fp, pinfo, c1=='5' ? 1 : 0, maxv); else if (c1=='3' || c1=='6') rv = loadppm(fp, pinfo, c1=='6' ? 1 : 0, maxv); fclose(fp); if (!rv) { if (pinfo->pic) free(pinfo->comment); if (pinfo->comment) free(pinfo->comment); pinfo->pic = (byte *) NULL; pinfo->comment = (char *) NULL; } return rv; } /*******************************************/ static int loadpbm(fp, pinfo, raw) FILE *fp; PICINFO *pinfo; int raw; { byte *pic8; byte *pix; int i,j,bit,w,h; w = pinfo->w; h = pinfo->h; pic8 = (byte *) calloc(w * h, 1); if (!pic8) return pbmError(bname, "couldn't malloc 'pic8'"); pinfo->pic = pic8; pinfo->type = PIC8; sprintf(pinfo->fullInfo, "PBM, %s format. (%ld bytes)", (raw) ? "raw" : "ascii", filesize); sprintf(pinfo->shrtInfo, "%dx%d PBM.", w, h); pinfo->colType = F_BWDITHER; /* B/W bitmaps have a two entry colormap */ pinfo->r[0] = pinfo->g[0] = pinfo->b[0] = 255; /* entry #0 = white */ pinfo->r[1] = pinfo->g[1] = pinfo->b[1] = 0; /* entry #1 = black */ if (!raw) { numgot = 0; for (i=0, pix=pic8; iw; h = pinfo->h; pic8 = (byte *) calloc(w*h,1); if (!pic8) return(pbmError(bname, "couldn't malloc 'pic8'")); pinfo->pic = pic8; pinfo->type = PIC8; sprintf(pinfo->fullInfo, "PGM, %s format. (%ld bytes)", (raw) ? "raw" : "ascii", filesize); sprintf(pinfo->shrtInfo, "%dx%d PGM.", pinfo->w, pinfo->h); pinfo->colType = F_GREYSCALE; /* if maxv>255, keep dropping bits until it's reasonable */ bitshift = 0; while (maxv>255) { maxv = maxv>>1; bitshift++; } /* fill in a greyscale colormap where maxv maps to 255 */ for (i=0; i<=maxv; i++) pinfo->r[i] = pinfo->g[i] = pinfo->b[i] = (i*255)/maxv; if (!raw) { numgot = 0; for (i=0, pix=pic8; i> bitshift); } } else numgot = fread(pic8, 1, w*h, fp); /* read raw data */ if (numgot != w*h) pbmError(bname, TRUNCSTR); /* warning only */ if (garbage) { return (pbmError(bname, "Garbage characters in image data.")); } return 1; } /*******************************************/ static int loadppm(fp, pinfo, raw, maxv) FILE *fp; PICINFO *pinfo; int raw, maxv; { byte *pix, *pic24, scale[256], *pic8; int i,j,bitshift, w, h; w = pinfo->w; h = pinfo->h; /* allocate 24-bit image */ pic24 = (byte *) calloc(w*h*3,1); if (!pic24) FatalError("couldn't malloc 'pic24'"); pinfo->pic = pic24; pinfo->type = PIC24; sprintf(pinfo->fullInfo, "PPM, %s format. (%ld bytes)", (raw) ? "raw" : "ascii", filesize); sprintf(pinfo->shrtInfo, "%dx%d PPM.", w, h); pinfo->colType = F_FULLCOLOR; /* if maxv>255, keep dropping bits until it's reasonable */ bitshift = 0; while (maxv>255) { maxv = maxv>>1; bitshift++; } if (!raw) { numgot = 0; for (i=0, pix=pic24; i> bitshift); } } else numgot = fread(pic24, 1, w*h*3, fp); /* read raw data */ if (numgot != w*h*3) pbmError(bname, TRUNCSTR); if (garbage) return(pbmError(bname, "Garbage characters in image data.")); /* have to scale all RGB values up (Conv24to8 expects RGB values to range from 0-255 */ if (maxv<255) { for (i=0; i<=maxv; i++) scale[i] = (i * 255) / maxv; for (i=0, pix=pic24; i 0) { /* add to pinfo->comment */ if (!pinfo->comment) { pinfo->comment = (char *) malloc(strlen(cmt)+1); if (!pinfo->comment) FatalError("malloc failure in xvpbm.c getint"); pinfo->comment[0] = '\0'; } else { tmpptr = (char *) realloc(pinfo->comment, strlen(pinfo->comment) + strlen(cmt) + 1); if (!tmpptr) FatalError("realloc failure in xvpbm.c getint"); pinfo->comment = tmpptr; } strcat(pinfo->comment, cmt); } } if (c==EOF) return 0; if (c>='0' && c<='9') break; /* we've found what we were looking for */ /* see if we are getting garbage (non-whitespace) */ if (c!=' ' && c!='\t' && c!='\r' && c!='\n' && c!=',') garbage=1; c = getc(fp); } /* we're at the start of a number, continue until we hit a non-number */ i = 0; while (1) { i = (i*10) + (c - '0'); c = getc(fp); if (c==EOF) return i; if (c<'0' || c>'9') break; } numgot++; return i; } /*******************************************/ static int getbit(fp, pinfo) FILE *fp; PICINFO *pinfo; { int c; /* skip forward to start of next number */ c = getc(fp); while (1) { /* eat comments */ if (c=='#') { /* if we're at a comment, read to end of line */ char cmt[256], *sp, *tmpptr; sp = cmt; while (1) { c=getc(fp); if (c == '\n' || c == EOF) break; if ((sp-cmt)<250) *sp++ = c; } *sp++ = '\n'; *sp = '\0'; if (strlen(cmt) > 0) { /* add to pinfo->comment */ if (!pinfo->comment) { pinfo->comment = (char *) malloc(strlen(cmt)+1); if (!pinfo->comment) FatalError("malloc failure in xvpbm.c getint"); pinfo->comment[0] = '\0'; } else { tmpptr = (char *) realloc(pinfo->comment, strlen(pinfo->comment) + strlen(cmt) + 1); if (!tmpptr) FatalError("realloc failure in xvpbm.c getint"); pinfo->comment = tmpptr; } strcat(pinfo->comment, cmt); } } if (c==EOF) return 0; if (c=='0' || c=='1') break; /* we've found what we were looking for */ /* see if we are getting garbage (non-whitespace) */ if (c!=' ' && c!='\t' && c!='\r' && c!='\n' && c!=',') garbage=1; c = getc(fp); } numgot++; return(c-'0'); } /*******************************************/ static int pbmError(fname, st) char *fname, *st; { SetISTR(ISTR_WARNING,"%s: %s", fname, st); return 0; } /*******************************************/ int WritePBM(fp,pic,ptype,w,h,rmap,gmap,bmap,numcols,colorstyle,raw,comment) FILE *fp; byte *pic; int ptype, w,h; byte *rmap, *gmap, *bmap; int numcols, colorstyle, raw; char *comment; { /* writes a PBM/PGM/PPM file to the already open stream if (raw), writes as RAW bytes, otherwise writes as ASCII 'colorstyle' single-handedly determines the type of file written if colorstyle==0, (Full Color) a PPM file is written if colorstyle==1, (Greyscale) a PGM file is written if colorstyle==2, (B/W stipple) a PBM file is written */ int magic; byte *pix; int i,j,len; /* calc the appropriate magic number for this file type */ magic = 0; if (colorstyle==0) magic = 3; else if (colorstyle==1) magic = 2; else if (colorstyle==2) magic = 1; if (raw && magic) magic+=3; /* write the header info */ fprintf(fp,"P%d\n",magic); fprintf(fp,"# CREATOR: XV %s\n", REVDATE); if (comment) { /* write comment lines */ char *sp; sp = comment; while (*sp) { fprintf(fp, "# "); while (*sp && *sp != '\n') fputc(*sp++, fp); if (*sp == '\n') sp++; fputc('\n', fp); } } fprintf(fp,"%d %d\n",w,h); if (colorstyle!=2) fprintf(fp,"255\n"); if (ferror(fp)) return -1; /* write the image data */ if (colorstyle==0) { /* 24bit RGB, 3 bytes per pixel */ for (i=0, pix=pic, len=0; i58) { fprintf(fp,"\n"); len=0; } } pix += (ptype==PIC24) ? 3 : 1; } } } else if (colorstyle==1) { /* 8-bit greyscale */ byte rgb[256]; if (ptype==PIC8) for (i=0; i66) { fprintf(fp,"\n"); len=0; } } pix += (ptype==PIC24) ? 3 : 1; } } else if (colorstyle==2) { /* 1-bit B/W stipple */ int bit,k,flipbw; char *str0, *str1; /* shouldn't happen */ if (ptype == PIC24) FatalError("PIC24 and B/W Stipple in WritePBM()\n"); /* if '0' is black, set flipbw */ flipbw = (MONO(rmap[0],gmap[0],bmap[0]) < MONO(rmap[1],gmap[1],bmap[1])); str0 = (flipbw) ? "1 " : "0 "; str1 = (flipbw) ? "0 " : "1 "; for (i=0, pix=pic, len=0; i68) { fprintf(fp,"\n"); len=0; } } } /* j */ if (raw && bit) { k = k << (8-bit); if (flipbw) k = ~k; fputc(k,fp); } } } if (ferror(fp)) return -1; return 0; }