1 /*
2  * xvpbm.c - load routine for 'pm' format pictures
3  *
4  * LoadPBM(fname, pinfo)  -  loads a PBM, PGM, or PPM file
5  * WritePBM(fp,pic,ptype,w,h,r,g,b,numcols,style,raw,cmt,comment)
6  */
7 
8 #include "copyright.h"
9 
10 #include "xv.h"
11 
12 
13 
14 /*
15  * comments on error handling:
16  * a truncated file is not considered a Major Error.  The file is loaded, the
17  * rest of the pic is filled with 0's.
18  *
19  * a file with garbage characters in it is an unloadable file.  All allocated
20  * stuff is tossed, and LoadPBM returns non-zero
21  *
22  * not being able to malloc is a Fatal Error.  The program is aborted.
23  */
24 
25 
26 typedef unsigned short  ush;
27 typedef unsigned char   uch;
28 
29 #define alpha_composite(composite, fg, alpha, bg) {               \
30     ush temp = ((ush)(fg)*(ush)(alpha) +                          \
31                 (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);  \
32     (composite) = (uch)((temp + (temp >> 8)) >> 8);               \
33 }
34 
35 #define TRUNCSTR "File appears to be truncated."
36 
37 static int garbage;
38 static long numgot, filesize;
39 
40 static int loadpbm  PARM((FILE *, PICINFO *, int));
41 static int loadpgm  PARM((FILE *, PICINFO *, int, int));
42 static int loadppm  PARM((FILE *, PICINFO *, int, int));
43 static int loadpam  PARM((FILE *, PICINFO *, int, int));
44 static int getint   PARM((FILE *, PICINFO *));
45 static int getbit   PARM((FILE *, PICINFO *));
46 static int getshort PARM((FILE *));
47 static int pbmError PARM((const char *, const char *));
48 
49 static const char *bname;
50 
51 
52 #ifdef HAVE_MGCSFX
53 /*
54  * When file read or file write is fail, probably it's caused by
55  * reading from pipe which has no data yet, or writing to pipe
56  * which is not ready yet.
57  * Then we can use system call select() on descriptor of pipe and wait.
58  * If you want, change 'undef' to 'define' in the following line.
59  * This feature is performance-killer.
60  */
61 #undef FIX_PIPE_ERROR
62 
63 #ifdef __osf__
64 #  ifdef __alpha
65 #    define FIX_PIPE_ERROR
66 #  endif
67 #endif
68 
69 #endif /* HAVE_MGCSFX */
70 
71 
72 #ifdef FIX_PIPE_ERROR
73 
74 int pipefdr;
75 
76 struct timeval timeout;
77 int    width;
78 fd_set fds;
79 
ready_read()80 static void ready_read()
81 {
82   if(pipefdr < 0) return; /* if file descriptor is not pipe, OK */
83   WaitCursor();
84 
85 reselect:
86   /* setting of timeout */
87   timeout.tv_sec = 1;  /* 1 sec */
88   timeout.tv_usec = 0; /* 0 usec */
89 
90   FD_ZERO(&fds);     /* clear bits */
91   FD_SET(pipefdr, &fds); /* set bit of fd in fds */
92 
93   /* number of file descriptor to want check (0 $B!A(B width-1) */
94   width = pipefdr + 1;
95 
96   /* select returns number of file descriptors */
97   if (select(width, &fds, NULL, NULL, &timeout) < 0){
98     if(DEBUG){
99       fprintf(stderr, "No file descriptors can't selected, waiting...\n");
100     }
101     goto reselect;
102   }
103 
104   if (FD_ISSET(pipefdr, &fds)){
105     /* Now, descriptor of pipe is ready to read */
106     return;
107   }else{
108     if(DEBUG){
109       fprintf(stderr, "Can't read from pipe yet, waiting...\n");
110     }
111     goto reselect;
112   }
113 
114 }
115 #endif /* FIX_PIPE_ERROR */
116 
117 /*******************************************/
118 #ifdef HAVE_MGCSFX
LoadPBM(fname,pinfo,fd)119 int LoadPBM(fname, pinfo, fd)
120      char    *fname;
121      PICINFO *pinfo;
122      int      fd;
123 #else
124 int LoadPBM(fname, pinfo)
125      char    *fname;
126      PICINFO *pinfo;
127 #endif /* HAVE_MGCSFX */
128 /*******************************************/
129 {
130   /* returns '1' on success */
131 
132   FILE  *fp;
133   int    c, c1;
134   int    maxv, rv;
135 
136 #ifdef FIX_PIPE_ERROR
137   pipefdr = fd;
138 #endif
139 
140   garbage = maxv = rv = 0;
141   bname = BaseName(fname);
142 
143   pinfo->pic     = (byte *) NULL;
144   pinfo->comment = (char *) NULL;
145 
146 
147 #ifdef HAVE_MGCSFX
148   if(fd < 0){
149     /* open the file */
150     fp = xv_fopen(fname,"r");
151     if (!fp) return (pbmError(bname, "can't open file"));
152 
153     /* compute file length */
154     fseek(fp, 0L, 2);
155     filesize = ftell(fp);
156     fseek(fp, 0L, 0);
157   }else{
158     fp = fdopen(fd, "r");
159     if (!fp) return (pbmError(bname, "can't open file"));
160     filesize = 0; /* dummy */
161   }
162 #else
163   /* open the file */
164   fp = xv_fopen(fname,"r");
165   if (!fp) return (pbmError(bname, "can't open file"));
166 
167   /* compute file length */
168   fseek(fp, 0L, 2);
169   filesize = ftell(fp);
170   fseek(fp, 0L, 0);
171 #endif /* HAVE_MGCSFX */
172 
173 
174   /* read the first two bytes of the file to determine which format
175      this file is.  "P1" = ascii bitmap, "P2" = ascii greymap,
176      "P3" = ascii pixmap, "P4" = raw bitmap, "P5" = raw greymap,
177      "P6" = raw pixmap */
178 
179   c = getc(fp);  c1 = getc(fp);
180   if (c!='P' || c1<'1' || (c1>'6' && c1!='8'))	/* GRR alpha */
181     return(pbmError(bname, "unknown format"));
182 
183   /* read in header information */
184   pinfo->w = getint(fp, pinfo);  pinfo->h = getint(fp, pinfo);
185   pinfo->normw = pinfo->w;   pinfo->normh = pinfo->h;
186 
187   /* if we're not reading a bitmap, read the 'max value' */
188   if ( !(c1=='1' || c1=='4')) {
189     maxv = getint(fp, pinfo);
190     if (maxv < 1) garbage=1;    /* to avoid 'div by zero' probs */
191   }
192 
193 
194   if (garbage) {
195     fclose(fp);
196     if (pinfo->comment) free(pinfo->comment);
197     pinfo->comment = (char *) NULL;
198     return (pbmError(bname, "Garbage characters in header."));
199   }
200 
201 
202   if (c1=='1' || c1=='2' || c1=='3') pinfo->frmType = F_PBMASCII;
203                                 else pinfo->frmType = F_PBMRAW;
204 
205   /* note:  pic, type, r,g,b, frmInfo, shortFrm, and colorType fields of
206      picinfo struct are filled in in the format-specific loaders */
207 
208   /* call the appropriate subroutine to handle format-specific stuff */
209   if      (c1=='1' || c1=='4') rv = loadpbm(fp, pinfo, c1=='4' ? 1 : 0);
210   else if (c1=='2' || c1=='5') rv = loadpgm(fp, pinfo, c1=='5' ? 1 : 0, maxv);
211   else if (c1=='3' || c1=='6') rv = loadppm(fp, pinfo, c1=='6' ? 1 : 0, maxv);
212   else if            (c1=='8') rv = loadpam(fp, pinfo,           1    , maxv);
213 
214   fclose(fp);
215 
216   if (!rv) {
217     if (pinfo->pic) free(pinfo->pic);
218     if (pinfo->comment) free(pinfo->comment);
219     pinfo->pic     = (byte *) NULL;
220     pinfo->comment = (char *) NULL;
221   }
222 
223   return rv;
224 }
225 
226 
227 
228 /*******************************************/
loadpbm(fp,pinfo,raw)229 static int loadpbm(fp, pinfo, raw)
230      FILE    *fp;
231      PICINFO *pinfo;
232      int      raw;
233 {
234   byte *pic8;
235   byte *pix;
236   int   i,j,bit,w,h,npixels;
237 
238   w = pinfo->w;
239   h = pinfo->h;
240 
241   npixels = w * h;
242   if (w <= 0 || h <= 0 || npixels/w != h)
243     return pbmError(bname, "image dimensions too large");
244 
245   pic8 = (byte *) calloc((size_t) npixels, (size_t) 1);
246   if (!pic8) FatalError("couldn't malloc 'pic8' for PBM");
247 
248   pinfo->pic  = pic8;
249   pinfo->type = PIC8;
250   sprintf(pinfo->fullInfo, "PBM, %s format.  (%ld bytes)",
251 	  (raw) ? "raw" : "ascii", filesize);
252   sprintf(pinfo->shrtInfo, "%dx%d PBM.", w, h);
253   pinfo->colType = F_BWDITHER;
254 
255 
256   /* B/W bitmaps have a two entry colormap */
257   pinfo->r[0] = pinfo->g[0] = pinfo->b[0] = 255;   /* entry #0 = white */
258   pinfo->r[1] = pinfo->g[1] = pinfo->b[1] = 0;     /* entry #1 = black */
259 
260 
261   if (!raw) {
262     numgot = 0;
263     for (i=0, pix=pic8; i<h; i++) {
264       if ((i&0x3f)==0) WaitCursor();
265       for (j=0; j<w; j++, pix++) *pix = getbit(fp, pinfo);
266     }
267 
268     if (numgot != npixels) pbmError(bname, TRUNCSTR);
269     if (garbage) {
270       return(pbmError(bname, "Garbage characters in image data."));
271     }
272   }
273 
274 
275   else {   /* read raw bits */
276     int trunc = 0, k = 0;
277 
278     for (i=0, pix=pic8; i<h; i++) {
279       if ((i&15)==0) WaitCursor();
280       for (j=0,bit=0; j<w; j++, pix++, bit++) {
281 
282 	bit &= 7;
283 	if (!bit) {
284 	  k = getc(fp);
285 	  if (k==EOF) { trunc=1; k=0; }
286 	}
287 
288 	*pix = (k&0x80) ? 1 : 0;
289 	k = k << 1;
290       }
291     }
292 
293     if (trunc) pbmError(bname, TRUNCSTR);
294   }
295 
296   return 1;
297 }
298 
299 
300 /*******************************************/
loadpgm(fp,pinfo,raw,maxv)301 static int loadpgm(fp, pinfo, raw, maxv)
302      FILE    *fp;
303      PICINFO *pinfo;
304      int      raw, maxv;
305 {
306   byte *pix, *pic8;
307   int   i,j,bitshift,w,h,npixels, holdmaxv;
308 
309 
310   w = pinfo->w;
311   h = pinfo->h;
312 
313   npixels = w * h;
314   if (w <= 0 || h <= 0 || npixels/w != h)
315     return pbmError(bname, "image dimensions too large");
316 
317   pic8 = (byte *) calloc((size_t) npixels, (size_t) 1);
318   if (!pic8) FatalError("couldn't malloc 'pic8' for PGM");
319 
320 
321   pinfo->pic  = pic8;
322   pinfo->type = PIC8;
323   sprintf(pinfo->fullInfo, "PGM, %s format.  (%ld bytes)",
324 	  (raw) ? "raw" : "ascii", filesize);
325   sprintf(pinfo->shrtInfo, "%dx%d PGM.", pinfo->w, pinfo->h);
326   pinfo->colType = F_GREYSCALE;
327 
328 
329   /* if maxv>255, keep dropping bits until it's reasonable */
330   holdmaxv = maxv;
331   bitshift = 0;
332   while (maxv>255) { maxv = maxv>>1;  bitshift++; }
333 
334   /* fill in a greyscale colormap where maxv maps to 255 */
335   for (i=0; i<=maxv; i++)
336     pinfo->r[i] = pinfo->g[i] = pinfo->b[i] = (i*255)/maxv;
337 
338 
339   numgot = 0;
340 
341   if (!raw) {
342     for (i=0, pix=pic8; i<h; i++) {
343       if ((i&0x3f)==0) WaitCursor();
344       for (j=0; j<w; j++, pix++)
345 	*pix = (byte) (getint(fp, pinfo) >> bitshift);
346     }
347   }
348   else { /* raw */
349     if (holdmaxv>255) {
350       for (i=0, pix=pic8; i<h; i++) {
351 	if ((i&0x3f)==0) WaitCursor();
352 	for (j=0; j<w; j++, pix++)
353 	  *pix = (byte) (getshort(fp) >> bitshift);
354       }
355     }
356     else {
357 #ifdef FIX_PIPE_ERROR
358   reread:
359       numgot += fread(pic8 + numgot, (size_t) 1, (size_t) w*h - numgot, fp); /* read raw data */
360       if(errno == EINTR){
361         if(DEBUG){
362 	  fprintf(stderr,
363 	  "Can't read all data from pipe, call select and waiting...\n");
364 	}
365 	ready_read();
366 	goto reread;
367       }
368 #else
369       numgot = fread(pic8, (size_t)1, (size_t)npixels, fp);  /* read raw data */
370 #endif
371     }
372   }
373 
374   if (numgot != npixels) pbmError(bname, TRUNCSTR);   /* warning only */
375 
376   if (garbage) {
377     return (pbmError(bname, "Garbage characters in image data."));
378   }
379 
380   return 1;
381 }
382 
383 
384 /*******************************************/
loadppm(fp,pinfo,raw,maxv)385 static int loadppm(fp, pinfo, raw, maxv)
386      FILE    *fp;
387      PICINFO *pinfo;
388      int      raw, maxv;
389 {
390   byte *pix, *pic24, scale[256];
391   int   i,j,bitshift, w, h, npixels, bufsize, holdmaxv;
392 
393   w = pinfo->w;
394   h = pinfo->h;
395 
396   npixels = w * h;
397   bufsize = 3*npixels;
398   if (w <= 0 || h <= 0 || npixels/w != h || bufsize/3 != npixels)
399     return pbmError(bname, "image dimensions too large");
400 
401   /* allocate 24-bit image */
402   pic24 = (byte *) calloc((size_t) bufsize, (size_t) 1);
403   if (!pic24) FatalError("couldn't malloc 'pic24' for PPM");
404 
405   pinfo->pic  = pic24;
406   pinfo->type = PIC24;
407   sprintf(pinfo->fullInfo, "PPM, %s format.  (%ld bytes)",
408 	  (raw) ? "raw" : "ascii", filesize);
409   sprintf(pinfo->shrtInfo, "%dx%d PPM.", w, h);
410   pinfo->colType = F_FULLCOLOR;
411 
412 
413   /* if maxv>255, keep dropping bits until it's reasonable */
414   holdmaxv = maxv;
415   bitshift = 0;
416   while (maxv>255) { maxv = maxv>>1;  bitshift++; }
417 
418 
419   numgot = 0;
420 
421   if (!raw) {
422     for (i=0, pix=pic24; i<h; i++) {
423       if ((i&0x3f)==0) WaitCursor();
424       for (j=0; j<w*3; j++, pix++)
425 	*pix = (byte) (getint(fp, pinfo) >> bitshift);
426     }
427   }
428   else { /* raw */
429     if (holdmaxv>255) {
430       for (i=0, pix=pic24; i<h; i++) {
431 	if ((i&0x3f)==0) WaitCursor();
432 	for (j=0; j<w*3; j++,pix++)
433 	  *pix = (byte) (getshort(fp) >> bitshift);
434       }
435     }
436     else {
437 #ifdef FIX_PIPE_ERROR
438   reread:
439       numgot += fread(pic24 + numgot, (size_t) 1, (size_t) w*h*3 - numgot, fp);  /* read data */
440       if(errno == EINTR){
441         if(DEBUG){
442 	  fprintf(stderr,
443 	  "Can't read all data from pipe, call select and waiting...\n");
444 	}
445 	ready_read();
446 	goto reread;
447       }
448 #else
449       numgot = fread(pic24, (size_t) 1, (size_t) bufsize, fp);  /* read data */
450 #endif
451     }
452   }
453 
454   if (numgot != bufsize) pbmError(bname, TRUNCSTR);
455 
456   if (garbage)
457     return(pbmError(bname, "Garbage characters in image data."));
458 
459 
460   /* have to scale up all RGB values (Conv24to8 expects RGB values to
461      range from 0-255) */
462 
463   if (maxv<255) {
464     for (i=0; i<=maxv; i++) scale[i] = (i * 255) / maxv;
465 
466     for (i=0, pix=pic24; i<h; i++) {
467       if ((i&0x3f)==0) WaitCursor();
468       for (j=0; j<w*3; j++, pix++) *pix = scale[*pix];
469     }
470   }
471 
472   return 1;
473 }
474 
475 
476 /*******************************************/
loadpam(fp,pinfo,raw,maxv)477 static int loadpam(fp, pinfo, raw, maxv)	/* unofficial RGBA extension */
478      FILE    *fp;
479      PICINFO *pinfo;
480      int      raw, maxv;
481 {
482   byte *p, *pix, *pic24, *linebuf, scale[256], bgR, bgG, bgB, r, g, b, a;
483   int   i, j, bitshift, w, h, npixels, bufsize, linebufsize, holdmaxv;
484 
485   w = pinfo->w;
486   h = pinfo->h;
487 
488   npixels = w * h;
489   bufsize = 3*npixels;
490   linebufsize = 4*w;
491   if (w <= 0 || h <= 0 || npixels/w != h || bufsize/3 != npixels ||
492       linebufsize/4 != w)
493     return pbmError(bname, "image dimensions too large");
494 
495   /* allocate 24-bit image */
496   pic24 = (byte *) calloc((size_t) bufsize, (size_t) 1);
497   if (!pic24) FatalError("couldn't malloc 'pic24' for PAM");
498 
499   /* allocate line buffer for pre-composited RGBA data */
500   linebuf = (byte *) malloc((size_t) linebufsize);
501   if (!linebuf) {
502     free(pic24);
503     FatalError("couldn't malloc 'linebuf' for PAM");
504   }
505 
506   pinfo->pic  = pic24;
507   pinfo->type = PIC24;
508   sprintf(pinfo->fullInfo, "PAM, %s format.  (%ld bytes)",
509 	  (raw) ? "raw" : "ascii", filesize);
510   sprintf(pinfo->shrtInfo, "%dx%d PAM.", w, h);
511   pinfo->colType = F_FULLCOLOR;
512 
513 
514   /* if maxv>255, keep dropping bits until it's reasonable */
515   holdmaxv = maxv;
516   bitshift = 0;
517   while (maxv>255) { maxv = maxv>>1;  bitshift++; }
518 
519 
520   numgot = 0;
521 
522   if (!raw) {					/* GRR:  not alpha-ready */
523     return pbmError(bname, "can't handle non-raw PAM image");
524 /*
525     for (i=0, pix=pic24; i<h; i++) {
526       if ((i&0x3f)==0) WaitCursor();
527       for (j=0; j<w*3; j++, pix++)
528 	*pix = (byte) (getint(fp, pinfo) >> bitshift);
529     }
530  */
531   }
532   else { /* raw */
533     if (holdmaxv>255) {				/* GRR:  not alpha-ready */
534       return pbmError(bname, "can't handle PAM image with maxval > 255");
535 /*
536       for (i=0, pix=pic24; i<h; i++) {
537 	if ((i&0x3f)==0) WaitCursor();
538 	for (j=0; j<w*3; j++,pix++)
539 	  *pix = (byte) (getshort(fp) >> bitshift);
540       }
541  */
542     }
543     else {
544       if (have_imagebg) {			/* GRR:  alpha-ready */
545         bgR = (imagebgR >> 8);
546         bgG = (imagebgG >> 8);
547         bgB = (imagebgB >> 8);
548       } else {
549         bgR = bgG = bgB = 0;
550       }
551       for (i=0, pix=pic24; i<h; i++) {
552         numgot += fread(linebuf, (size_t) 1, (size_t) linebufsize, fp);  /* read data */
553 	if ((i&0x3f)==0) WaitCursor();
554 	for (j=0, p=linebuf; j<w; j++) {
555           r = *p++;
556           g = *p++;
557           b = *p++;
558           a = *p++;
559           alpha_composite(*pix++, r, a, bgR)
560           alpha_composite(*pix++, g, a, bgG)
561           alpha_composite(*pix++, b, a, bgB)
562 	}
563       }
564     }
565   }
566 
567   free(linebuf);
568 
569   /* in principle this could overflow, but not critical */
570   if (numgot != w*h*4) pbmError(bname, TRUNCSTR);
571 
572   if (garbage)
573     return(pbmError(bname, "Garbage characters in image data."));
574 
575 
576   /* have to scale up all RGB values (Conv24to8 expects RGB values to
577      range from 0-255) */
578 
579   if (maxv<255) {
580     for (i=0; i<=maxv; i++) scale[i] = (i * 255) / maxv;
581 
582     for (i=0, pix=pic24; i<h; i++) {
583       if ((i&0x3f)==0) WaitCursor();
584       for (j=0; j<w*3; j++, pix++) *pix = scale[*pix];
585     }
586   }
587 
588   return 1;
589 }
590 
591 
592 
593 /*******************************************/
getint(fp,pinfo)594 static int getint(fp, pinfo)
595      FILE *fp;
596      PICINFO *pinfo;
597 {
598   int c, i, firstchar;
599 
600   /* note:  if it sees a '#' character, all characters from there to end of
601      line are appended to the comment string */
602 
603   /* skip forward to start of next number */
604   c = getc(fp);
605   while (1) {
606     /* eat comments */
607     if (c=='#') {   /* if we're at a comment, read to end of line */
608       char cmt[256], *sp, *tmpptr;
609 
610       sp = cmt;  firstchar = 1;
611       while (1) {
612 	c=getc(fp);
613 	if (firstchar && c == ' ') firstchar = 0;  /* lop off 1 sp after # */
614 	else {
615 	  if (c == '\n' || c == EOF) break;
616 	  if ((sp-cmt)<250) *sp++ = c;
617 	}
618       }
619       *sp++ = '\n';
620       *sp   = '\0';
621 
622       if (strlen(cmt) > (size_t) 0) {    /* add to pinfo->comment */
623 	if (!pinfo->comment) {
624 	  pinfo->comment = (char *) malloc(strlen(cmt)+1);
625 	  if (!pinfo->comment) FatalError("malloc failure in xvpbm.c getint");
626 	  pinfo->comment[0] = '\0';
627 	}
628 	else {
629 	  tmpptr = (char *) realloc(pinfo->comment,
630 		      strlen(pinfo->comment) + strlen(cmt) + 1);
631 	  if (!tmpptr) FatalError("realloc failure in xvpbm.c getint");
632 	  pinfo->comment = tmpptr;
633 	}
634 	strcat(pinfo->comment, cmt);
635       }
636     }
637 
638     if (c==EOF) return 0;
639     if (c>='0' && c<='9') break;   /* we've found what we were looking for */
640 
641     /* see if we are getting garbage (non-whitespace) */
642     if (c!=' ' && c!='\t' && c!='\r' && c!='\n' && c!=',') garbage=1;
643 
644     c = getc(fp);
645   }
646 
647 
648   /* we're at the start of a number, continue until we hit a non-number */
649   i = 0;
650   while (1) {
651     i = (i*10) + (c - '0');
652     c = getc(fp);
653     if (c==EOF) return i;
654     if (c<'0' || c>'9') break;
655   }
656 
657   numgot++;
658   return i;
659 }
660 
661 
662 
663 /*******************************************/
getshort(fp)664 static int getshort(fp)
665      FILE    *fp;
666 {
667   /* used in RAW mode to read 16-bit values */
668 
669   int c1, c2;
670 
671   c1 = getc(fp);
672   if (c1 == EOF) return 0;
673   c2 = getc(fp);
674   if (c2 == EOF) return 0;
675 
676   numgot++;
677 
678   /* Sometime after 1995, NetPBM's ppm(5) man page was changed to say, "Each
679    * sample is represented in pure binary by either 1 or 2 bytes.  If the
680    * Maxval is less than  256, it is 1 byte.  Otherwise, it is 2 bytes.  The
681    * most significant byte is first."  This change is incompatible with
682    * images created for viewing with all previous versions of XV, however,
683    * so both approaches are left available as a compile-time option.  (Could
684    * make it runtime-selectable, too, but unclear whether anybody cares.) */
685 #ifdef ASSUME_RAW_PPM_LSB_FIRST  /* legacy approach */
686   return (c2 << 8) | c1;
687 #else /* MSB first */
688   return (c1 << 8) | c2;
689 #endif
690 }
691 
692 
693 
694 /*******************************************/
getbit(fp,pinfo)695 static int getbit(fp, pinfo)
696      FILE *fp;
697      PICINFO *pinfo;
698 {
699   int c;
700 
701   /* skip forward to start of next number */
702   c = getc(fp);
703   while (1) {
704     /* eat comments */
705     if (c=='#') {   /* if we're at a comment, read to end of line */
706       char cmt[256], *sp, *tmpptr;
707 
708       sp = cmt;
709       while (1) {
710 	c=getc(fp);
711 	if (c == '\n' || c == EOF) break;
712 
713 	if ((sp-cmt)<250) *sp++ = c;
714       }
715       *sp++ = '\n';
716       *sp = '\0';
717 
718       if (strlen(cmt) > (size_t) 0) {    /* add to pinfo->comment */
719 	if (!pinfo->comment) {
720 	  pinfo->comment = (char *) malloc(strlen(cmt)+1);
721 	  if (!pinfo->comment) FatalError("malloc failure in xvpbm.c getint");
722 	  pinfo->comment[0] = '\0';
723 	}
724 	else {
725 	  tmpptr = (char *) realloc(pinfo->comment,
726 		      strlen(pinfo->comment) + strlen(cmt) + 1);
727 	  if (!tmpptr) FatalError("realloc failure in xvpbm.c getint");
728 	  pinfo->comment = tmpptr;
729 	}
730 	strcat(pinfo->comment, cmt);
731       }
732     }
733     if (c==EOF) return 0;
734     if (c=='0' || c=='1') break;   /* we've found what we were looking for */
735 
736     /* see if we are getting garbage (non-whitespace) */
737     if (c!=' ' && c!='\t' && c!='\r' && c!='\n' && c!=',') garbage=1;
738 
739     c = getc(fp);
740   }
741 
742 
743   numgot++;
744   return(c-'0');
745 }
746 
747 
748 /*******************************************/
pbmError(fname,st)749 static int pbmError(fname, st)
750      const char *fname, *st;
751 {
752   SetISTR(ISTR_WARNING,"%s:  %s", fname, st);
753   return 0;
754 }
755 
756 
757 
758 
759 
760 /*******************************************/
WritePBM(fp,pic,ptype,w,h,rmap,gmap,bmap,numcols,colorstyle,raw,comment)761 int WritePBM(fp,pic,ptype,w,h,rmap,gmap,bmap,numcols,colorstyle,raw,comment)
762      FILE *fp;
763      byte *pic;
764      int   ptype, w,h;
765      byte *rmap, *gmap, *bmap;
766      int   numcols, colorstyle, raw;
767      char *comment;
768 {
769   /* writes a PBM/PGM/PPM file to the already open stream
770      if (raw), writes as RAW bytes, otherwise writes as ASCII
771      'colorstyle' single-handedly determines the type of file written
772      if colorstyle==0, (Full Color) a PPM file is written
773      if colorstyle==1, (Greyscale)  a PGM file is written
774      if colorstyle==2, (B/W stipple) a PBM file is written */
775 
776   int   magic;
777   byte *pix;
778   int   i,j,len;
779 
780   /* calc the appropriate magic number for this file type */
781   magic = 0;
782   if      (colorstyle==0) magic = 3;
783   else if (colorstyle==1) magic = 2;
784   else if (colorstyle==2) magic = 1;
785 
786   if (raw && magic) magic+=3;
787 
788 
789   /* write the header info */
790   fprintf(fp,"P%d\n",magic);
791   fprintf(fp,"# CREATOR: XV %s\n", REVDATE);
792 
793   if (comment) {      /* write comment lines */
794     char *sp;
795 
796     sp = comment;
797     while (*sp) {
798       fprintf(fp, "# ");
799       while (*sp && *sp != '\n') fputc(*sp++, fp);
800       if (*sp == '\n') sp++;
801       fputc('\n', fp);
802     }
803   }
804 
805 
806   fprintf(fp,"%d %d\n",w,h);
807   if (colorstyle!=2) fprintf(fp,"255\n");
808 
809   if (ferror(fp)) return -1;
810 
811   /* write the image data */
812 
813   if (colorstyle==0) {                  /* 24bit RGB, 3 bytes per pixel */
814     for (i=0, pix=pic, len=0; i<h; i++) {
815       if ((i&63)==0) WaitCursor();
816       for (j=0; j<w; j++) {
817 	if (raw) {
818 	  if (ptype==PIC8) {
819 	    putc(rmap[*pix],fp);  putc(gmap[*pix],fp);  putc(bmap[*pix],fp);
820 	  }
821 	  else {  /* PIC24 */
822 	    putc(pix[0],fp);  putc(pix[1],fp);  putc(pix[2],fp);
823 	  }
824 	}
825 	else {
826 	  if (ptype==PIC8)
827 	    fprintf(fp,"%3d %3d %3d ",rmap[*pix], gmap[*pix], bmap[*pix]);
828 	  else
829 	    fprintf(fp,"%3d %3d %3d ",pix[0], pix[1], pix[2]);
830 
831 	  len+=12;
832 	  if (len>58) { fprintf(fp,"\n");  len=0; }
833 	}
834 
835 	pix += (ptype==PIC24) ? 3 : 1;
836       }
837     }
838   }
839 
840 
841   else if (colorstyle==1) {             /* 8-bit greyscale */
842     byte rgb[256];
843     if (ptype==PIC8)
844       for (i=0; i<numcols; i++) rgb[i] = MONO(rmap[i],gmap[i],bmap[i]);
845 
846     for (i=0, pix=pic, len=0; i<w*h; i++) {
847       if ((i&0x7fff)==0) WaitCursor();
848 
849       if (raw) putc((ptype==PIC8) ? rgb[*pix] : MONO(pix[0],pix[1],pix[2]),fp);
850 
851       else {
852 	if (ptype==PIC8) fprintf(fp,"%3d ",rgb[*pix]);
853 	            else fprintf(fp,"%3d ",MONO(pix[0],pix[1],pix[2]));
854 	len += 4;
855 	if (len>66) { fprintf(fp,"\n");  len=0; }
856       }
857 
858       pix += (ptype==PIC24) ? 3 : 1;
859     }
860   }
861 
862   else if (colorstyle==2) {             /* 1-bit B/W stipple */
863     int bit,k,flipbw;
864     const char *str0, *str1;
865 
866     /* shouldn't happen */
867     if (ptype == PIC24) FatalError("PIC24 and B/W Stipple in WritePBM()\n");
868 
869     /* if '0' is black, set flipbw */
870     flipbw = (MONO(rmap[0],gmap[0],bmap[0]) < MONO(rmap[1],gmap[1],bmap[1]));
871 
872     str0 = (flipbw) ? "1 " : "0 ";
873     str1 = (flipbw) ? "0 " : "1 ";
874 
875     for (i=0, pix=pic, len=0; i<h; i++) {
876       if ((i&15)==0) WaitCursor();
877       for (j=0, bit=0, k=0; j<w; j++, pix++) {
878 	if (raw) {
879 	  k = (k << 1) | *pix;
880 	  bit++;
881 	  if (bit==8) {
882 	    if (flipbw) k = ~k;
883 	    fputc(k,fp);
884 	    bit = k = 0;
885 	  }
886 	}
887 	else {
888 	  if (*pix) fprintf(fp,str1);
889 	       else fprintf(fp,str0);
890 	  len+=2;
891 	  if (len>68) { fprintf(fp,"\n"); len=0; }
892 	}
893       } /* j */
894       if (raw && bit) {
895 	k = k << (8-bit);
896 	if (flipbw) k = ~k;
897 	fputc(k,fp);
898       }
899     }
900   }
901 
902   if (ferror(fp)) return -1;
903 
904   return 0;
905 }
906