1 /* Copyright (C) 2007-2008 Claus-Justus Heine
2  *
3  * This file is part of Geomview.
4  *
5  * Geomview is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published
7  * by the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * Geomview is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with Geomview; see the file COPYING.  If not, write
17  * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
18  * USA, or visit http://www.gnu.org.
19  */
20 
21 #if HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24 
25 #include <limits.h>
26 #include <ctype.h>
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 #include <unistd.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <signal.h>
34 
35 /*#undef HAVE_LIBZ*/
36 
37 #ifndef HAVE_LIBZ
38 # define HAVE_LIBZ 0
39 #endif
40 
41 #if HAVE_LIBZ
42 # include <zlib.h>
43 #endif
44 
45 #include "appearance.h"
46 #include "handleP.h"
47 
48 #ifndef max
49 # define max(a, b) ((a) >= (b) ? (a) : (b))
50 #endif
51 #ifndef min
52 # define min(a, b) ((a) <= (b) ? (a) : (b))
53 #endif
54 
55 HandleOps ImageOps = {
56   "img",
57   (int ((*)(Pool *p, Handle **hp, Ref **rp)))ImgStreamIn,
58   (int ((*)(Pool *p, Handle *h, Ref *r)))ImgStreamOut,
59   (void ((*)(Ref *rp)))ImgDelete,
60   NULL,
61   NULL,
62 };
63 
64 struct imgheader {
65   int xsize, ysize, channels, maxval;
66   enum _format {
67     IMGF_BYTE,
68     IMGF_BIT,
69     IMGF_ASCII,
70     IMGF_SGIRLE,  /* SGI RLE image */
71     IMGF_SGIRAW   /* for (z) for (y=bottom to top) { data } */
72   } format;
73   int *rleoff;   /* For IMGF_SGIRLE: offsets[z][y], then lengths[z][y] */
74   char *rledata; /* For IMGF_SGIRLE: all data
75 		  * (don't fseek so we can read from pipe)
76 		  */
77 };
78 
79 static bool readimage(Image *img, unsigned *chmask, char *filter,
80 		      char *imgfname, char *imgdata, int datalen);
81 static bool parseheader(Image *img, IOBFILE *imgf, unsigned *chmask,
82 			const char *type, struct imgheader *header);
83 static bool readdata(Image *img, IOBFILE *imgf, unsigned chmask,
84 		     struct imgheader *header);
85 static int run_filter(const char *filter, int fdin, bool wronly, int *cpidp);
86 static int data_pipe(const char *data, int datalen, int *cpidp);
87 #if HAVE_LIBZ
88 static int zlib_data_pipe(const char *data, int datalen, int *cpidp);
89 static int gzip_data_pipe(const char *data, int datalen, int *cpidp);
90 static int gv_compress2(Bytef *dest, uLongf *destLen,
91 			const Bytef *source, uLong sourceLen,
92 			int level);
93 #endif
94 
ImgDefault(Image * img)95 Image *ImgDefault(Image *img)
96 {
97   memset((char *)img, 0, sizeof(*img));
98   RefInit((Ref *)img, IMGMAGIC);
99   img->maxval = 255;
100   return img;
101 }
102 
_ImgSet(Image * img,int attr1,va_list * alist)103 Image *_ImgSet(Image *img, int attr1, va_list *alist)
104 {
105   int attr, val;
106   bool newimg = false;
107   char *chanfile, *filter;
108   unsigned chmask;
109   void *chandata;
110   int chansize = 0;
111 #undef NEXT
112 #define NEXT(type) va_arg(*alist, type)
113 
114   if (img == NULL) {
115     /*
116      * New Image created here.
117      */
118     img = OOGLNewE(Image, "ImgCreate Image");
119     ImgDefault(img);
120     newimg = true;
121   }
122 
123   for (attr = attr1; attr != IMG_END; attr = NEXT(int)) {
124     switch (attr) { /* parse argument list */
125     case IMG_WIDTH:
126       val = NEXT(int);
127       if (val != img->width && img->data != NULL) {
128 	OOGLFree(img->data);
129 	img->data = NULL;
130       }
131       img->width = val;
132       break;
133     case IMG_HEIGHT:
134       val = NEXT(int);
135       if (val != img->height && img->data != NULL) {
136 	OOGLFree(img->data);
137 	img->data = NULL;
138       }
139       img->height = val;
140       break;
141     case IMG_CHANNELS:
142       val = NEXT(int);
143       if (val != img->channels && img->data != NULL) {
144 	OOGLFree(img->data);
145 	img->data = NULL;
146       }
147       img->channels = val;
148       break;
149     case IMG_MAXVAL:
150       val = NEXT(int);
151       if (val != img->maxval && img->data != NULL) {
152 	OOGLFree(img->data);
153 	img->data = NULL;
154       }
155       img->maxval = val;
156       if (img->maxval != 255) {
157 	OOGLError(1, "ImgSet: "
158 		  "maxval is tied to 255, nothing else is implemented");
159 	goto nope;
160       }
161       break;
162     case IMG_DATA:
163       if (img->data) {
164 	OOGLFree(img->data);
165       }
166       img->data = NEXT(char *);
167       break;
168     case IMG_DATA_CHAN_FILE:
169     case IMG_DATA_CHAN_DATA:
170       chmask   = NEXT(unsigned);
171       filter   = NEXT(char *); /* can be NULL */
172       if (attr == IMG_DATA_CHAN_FILE) {
173 	chanfile = NEXT(char *);
174 	chandata = NULL;
175       } else {
176 	chandata = NEXT(void *);
177 	chansize = NEXT(int);
178 	chanfile = NULL;
179       }
180       if (chmask == IMGF_ALPHA) {
181 	if (img->channels == 0) {
182 	  OOGLError(1,
183 		    "ImgSet(): data: "
184 		    "don't know which channel is the alpha mask");
185 	  goto nope;
186 	}
187 	switch (img->channels) {
188 	case 1: case 2: chmask = 0x2; break;
189 	case 3: case 4: chmask = 0x8; break;
190 	}
191       } else if (chmask == IMGF_AUTO) {
192 	chmask = 0;
193       }
194       /* try to suck it in, readimage() will examine filter
195        * and the suffix of imgfname and the magic bytes at
196        * the beginning of the image data. An explicitly
197        * specified filter will take precedence.
198        */
199       if (!readimage(img, &chmask, filter, chanfile, chandata, chansize)) {
200 	goto nope;
201       }
202       break;
203     default:
204       OOGLError(1, "ImgSet: unknown attribute %d", attr);
205       break;
206     }
207   }
208   return img;
209 
210  nope:
211   if (newimg) {
212     ImgDelete(img);
213   }
214   return NULL;
215 }
216 
ImgCreate(int a1,...)217 Image *ImgCreate(int a1, ...)
218 {
219   va_list alist;
220   Image *img;
221 
222   va_start(alist, a1);
223   img = _ImgSet(NULL, a1, &alist);
224   va_end(alist);
225 
226   return img;
227 }
228 
ImgDelete(Image * img)229 void ImgDelete(Image *img)
230 {
231   if (img == NULL) {
232     return;
233   }
234 
235   if (img->magic != IMGMAGIC) {
236     OOGLWarn("Internal warning: ImgDelete on non-Imag %x (%x != %x)",
237 	     img, img->magic, IMGMAGIC);
238     return;
239   }
240   if (RefDecr((Ref *)img) > 0) {
241     return;
242   }
243   if (img->data) {
244     OOGLFree(img->data);
245   }
246   OOGLFree(img);
247 }
248 
ImgSet(Image * img,int a1,...)249 Image *ImgSet(Image *img, int a1, ...)
250 {
251   va_list alist;
252 
253   va_start(alist, a1);
254   img = _ImgSet(img, a1, &alist);
255   va_end(alist);
256 
257   return img;
258 }
259 
260 /* see the comments in src/lib/gprim/geom/geomstream.c */
ImgStreamIn(Pool * p,Handle ** hp,Image ** imgp)261 int ImgStreamIn(Pool *p, Handle **hp, Image **imgp)
262 {
263   Handle *h = NULL;
264   Handle *hname = NULL;
265   Image *img = NULL;
266   char *w, *raww = NULL;
267   int c, i, val;
268   int moredata;
269   int brack = 0;
270   IOBFILE *inf;
271   char *fname, *imgfname = NULL, *filter, *imgdata = NULL;
272   char filterpath[PATH_MAX];
273   long datasize = 0;
274   unsigned long chmask;
275   bool have_chdata;
276   static const struct imgkw {
277     const char *word;
278     int aval;
279     int nargs;
280     int min;
281     int max;
282   } imgkw[] = {
283     { "width", IMG_WIDTH, 1, 1, 0 },
284     { "height", IMG_HEIGHT, 1, 1, 0 },
285     { "channels", IMG_CHANNELS, 1, 1, 4 },
286     { "maxval", IMG_MAXVAL, 1, 255, 255 },
287     { "data", IMG_DATA, -1, 1, 0 } /* data chmask filter DATA */
288   };
289   const struct imgkw *kw;
290   static const struct chflkw {
291     const char *word;
292     int value;
293   } chflkw[] = {
294     { "LUMINANCE", IMGF_LUMINANCE },
295     { "LUMINANCE_ALPHA", IMGF_LUMINANCE_ALPHA },
296     { "RGB", IMGF_RGB },
297     { "RGBA", IMGF_RGBA },
298     { "ALPHA", IMGF_ALPHA },
299     { "AUTO", IMGF_AUTO }
300   };
301   const struct chflkw *chfl;
302 #undef N_ITEMS
303 #define N_ITEMS(array) (sizeof(array)/sizeof(*(array)))
304 
305   if (p == NULL || (inf = PoolInputFile(p)) == NULL) {
306     return 0;
307   }
308   fname = PoolName(p);
309 
310   have_chdata = false;
311 
312   do {
313     switch(c = iobfnextc(inf, 0)) {
314     case '{': brack++; iobfgetc(inf); break;
315     case '}': if (brack--) iobfgetc(inf); break;
316     case '<':
317     case ':':
318     case '@':
319       iobfgetc(inf);
320       w = iobfdelimtok("{}()", inf, 0);
321       if (c == '<' && (h = HandleByName(w, &ImageOps)) == NULL && w[0] != '/') {
322 	w = findfile(PoolName(p), raww = w);
323 	if (w == NULL) {
324 	  OOGLSyntax(inf,
325 		     "Reading image from \"%s\": "
326 		     "can't find file \"%s\"",
327 		     PoolName(p), raww);
328 	  return false;
329 	}
330       } else if (h) {
331 	/* HandleByName() increases the ref. count s.t. the
332 	 * caller of HandleByName() owns the returned handle.
333 	 */
334 	HandleDelete(h);
335       }
336       h = HandleReferringTo(c, w, &ImageOps, NULL);
337       if (h) {
338 	img = (Image *)HandleObject(h);
339 	/* Increment the ref. count. This way we can call
340 	 * HandleDelete() and ImgDelete() independently.
341 	 */
342 	REFGET(Image, img);
343       }
344       break;
345 
346     default:
347       w = iobfdelimtok("{}()", inf, 0);
348       if (w == NULL) {
349 	break;
350       }
351       if (strcmp(w, "define") == 0) {
352 	hname = HandleCreateGlobal(iobftoken(inf, 0), &ImageOps);
353 	break;
354       }
355       for (i = N_ITEMS(imgkw), kw = imgkw; --i >= 0; kw++) {
356 	if (!strcmp(kw->word, w)) {
357 	  break;
358 	}
359       }
360       if (i < 0) {
361 	OOGLSyntax(inf, "%s: unknown image keyword %s", fname, w);
362 	ImgDelete(img);
363 	return false;
364       }
365       if (img == NULL) {
366 	img = ImgCreate(IMG_END);
367       }
368 
369       if (kw->nargs == 1) {
370 	int val, n;
371 
372 	if (have_chdata) {
373 	  OOGLSyntax(inf,
374 		     "%s: %s must be specified before defining the image data",
375 		     fname, w);
376 	  ImgDelete(img);
377 	  return false;
378 	}
379 
380 	n = iobfgetni(inf, kw->nargs, &val, 0);
381 	if (n != kw->nargs) {
382 	  OOGLSyntax(inf, "%s: %s expected %d numeric values",
383 		     fname, w, kw->nargs);
384 	  ImgDelete(img);
385 	  return false;
386 	}
387 	if (kw->min <= kw->max && (val < kw->min || val > kw->max)) {
388 	  OOGLSyntax(inf, "%s: %s value %d out of range (min %d, max %d",
389 		     fname, w, val, kw->min, kw->max);
390 	}
391 	ImgSet(img, kw->aval, val, IMG_END);
392       } else {
393 	/* data CHANNELMASK [FILTER] [{] < FILENAME [}]
394 	 *
395 	 * or
396 	 *
397 	 * data CHANNELMASK [FILTER] DATASIZE [{]\nDATA ... [}]
398 	 *
399 	 * FILTER may be a program or one of the know types, i.e. one
400 	 * of raw, png, tif[f], jpeg, gif, gzip, bzip2, compress.  If
401 	 * FILTER is a program, then it must convert FILENAME to pnm
402 	 * and the output must be presented at stdout. If no type is
403 	 * specified, then the data must bin SGIRAW or SGIRLE or PNM
404 	 * format. These formats are autodetected. If no SGIRAW,
405 	 * SGIRLE or PNM format could be detected, then "raw" is
406 	 * assumed.
407 	 *
408 	 * If the FILENAME has a known suffix (Z, z, gz, bz2,
409 	 * raw, tiff, tif, png, gif) then the input filter is
410 	 * deduced from the suffix.
411 	 *
412 	 * DATASIZE _must_ be specified for embedded images and gives
413 	 * the size of the embedded data.
414 	 */
415 	w = iobfdelimtok("{}()", inf, 0);
416 	if (w == NULL) {
417 	  OOGLSyntax(inf, "%s: data: expected the channel mask", fname);
418 	  ImgDelete(img);
419 	  return false;
420 	}
421 	for (i = N_ITEMS(chflkw), chfl = chflkw; --i >= 0; chfl++) {
422 	  if (!strcmp(chfl->word, w)) {
423 	    chmask = chfl->value;
424 	    break;
425 	  }
426 	}
427 	if (i < 0) {
428 	  /* should be a numerical value */
429 	  chmask = strtol(w, NULL, 0);
430 	  if (datasize == LONG_MIN || datasize == LONG_MAX) {
431 	    OOGLSyntax(inf, "%s: data: bogus channel mask definition: \"%s\"",
432 		       fname, w);
433 	    ImgDelete(img);
434 	    return false;
435 	  }
436 	  chmask &= 0xf;
437 	}
438 
439 	imgfname = NULL;
440 	filter   = NULL;
441 	moredata = 0;
442 	datasize = -1;
443 	do {
444 	  switch(c = iobfnextc(inf, 1)) {
445 	  case '{':
446 	    /* allow brackets around data and file-name, i.e.
447 	     * data MASK [FILTER] { < FILENAME }
448 	     * or
449 	     * data MASK [FILTER] DATASIZE {\n
450 	     * data bytes ...
451 	     * }
452 	     */
453 	    brack++;
454 	    iobfgetc(inf);
455 	    moredata = 1;
456 	    break;
457 	  case '<': /* read from a file, find and open it */
458 	    iobfgetc(inf); /* consume '<' */
459 	    w = iobfdelimtok("{}()", inf, 0);
460 	    imgfname = findfile(fname, w);
461 	    if (imgfname == NULL) {
462 	      OOGLSyntax(inf,
463 			 "Warning: reading \"%s\": can't find file \"%s\"",
464 			 fname, raww);
465 	      ImgDelete(img);
466 	      return false;
467 	    }
468 	    moredata = 0;
469 	    break;
470 	  case '\n': /* read embedded data from _this_ file */
471 	    if (datasize < 0) {
472 	      /* this means that the "filter" token is actually the
473 	       * size of the data section
474 	       */
475 	      if (filter == NULL) {
476 		OOGLSyntax(inf,
477 			   "%s: the size of the embedded image data is missing",
478 			   fname);
479 		ImgDelete(img);
480 		return false;
481 	      }
482 	      datasize = strtol(filter, NULL, 0);
483 	      if (datasize == LONG_MIN || datasize == LONG_MAX) {
484 		OOGLSyntax(inf, "%s: bogus size definition: \"%s\"",
485 			   fname, filter);
486 		ImgDelete(img);
487 		return false;
488 	      }
489 	      filter = NULL;
490 	    }
491 	    iobfgetc(inf); /* consume '\n', image data follows */
492 
493 	    /* read all the stuff in, we do it here to maintain the
494 	     * file position
495 	     */
496 	    imgdata = OOGLNewNE(char, datasize, "embedded image data");
497 	    if (iobfread(imgdata, datasize, 1, inf) <= 0) {
498 	      OOGLError(0, "%s: can't read embedded image data", fname);
499 	      OOGLFree(imgdata);
500 	      ImgDelete(img);
501 	      return false;
502 	    }
503 	    moredata = 0;
504 	    break;
505 	  default:
506 	    /* assume that it is a filter definition _or_ the data-size. */
507 	    if (filter) {
508 	      /* must be the data-size */
509 	      if (iobfgetni(inf, 1, &val, 0) != 1) {
510 		OOGLSyntax(inf, "%s: expected the data size", fname);
511 		ImgDelete(img);
512 		return false;
513 	      }
514 	      datasize = val;
515 	    } else {
516 	      /* maybe a filter of the data size, decide later */
517 	      filter = iobfdelimtok("{}()", inf, 0);
518 	      if (filter == NULL) {
519 		OOGLSyntax(inf, "%s: expected an input filter or "
520 			   "the size of the data-section",
521 			   fname);
522 		ImgDelete(img);
523 		return false;
524 	      }
525 	      /* we leave it to readimage() to determine
526 	       * whether this is a valid filter program, or
527 	       * prefix, file type spec.
528 	       *
529 	       * We have to save the filter because thiobfdelimtok()
530 	       * would otherwise overwrite it with the next token.
531 	       */
532 	      strncpy(filterpath, filter, PATH_MAX);
533 	      filterpath[PATH_MAX-1] = '\0';
534 	      filter = filterpath;
535 	      moredata = 1;
536 	    }
537 	    break;
538 	  }
539 	} while (moredata);
540 
541 	if ((imgfname
542 	     ? ImgSet(img, IMG_DATA_CHAN_FILE,
543 		      chmask, filter, imgfname, IMG_END)
544 	     : ImgSet(img, IMG_DATA_CHAN_DATA,
545 		      chmask, filter, imgdata, (int)datasize, IMG_END))
546 	    == NULL) {
547 #if 1
548 	  OOGLSyntax(inf, "%s: unable to read data for channelmask 0x%x, "
549 		     "continuing anyway", fname, (int)chmask);
550 #else
551 	  ImgDelete(img);
552 	  if (imgdata) { /* free buffer for embedded data */
553 	    OOGLFree(imgdata);
554 	  }
555 	  return false;
556 #endif
557 	}
558 
559 	if (imgdata) { /* free buffer for embedded data */
560 	  OOGLFree(imgdata);
561 	  imgdata = NULL;
562 	} else {
563 	  imgfname = NULL;
564 	}
565 
566 	have_chdata = true;
567       }
568       break;
569     }
570   } while (brack);
571 
572   if (hname != NULL) {
573     if (img) {
574       HandleSetObject(hname, (Ref *)img);
575     }
576     if (h) {
577       /* HandleReferringTo() has passed the ownership to us, so delete
578        * h because we do not need it anymore.
579        */
580       HandleDelete(h);
581     }
582     h = hname;
583   }
584 
585   /* Pass the ownership of h and img to the caller if requested */
586 
587   if (hp != NULL) {
588     /* pass on ownership of the handle h to the caller of this function */
589     if (*hp != NULL) {
590       if (*hp != h) {
591 	HandlePDelete(hp);
592       } else {
593 	HandleDelete(*hp);
594       }
595     }
596     *hp = h;
597   } else if (h) {
598     /* Otherwise delete h because we are its owner. Note that
599      * HandleReferringTo() has passed the ownership of h to us;
600      * explicitly defined handles (hdefine and define constructs)
601      * will not be deleted by this call.
602      */
603     HandleDelete(h);
604   }
605 
606   /* same logic as for hp */
607   if (imgp != NULL) {
608     if (*imgp != NULL) {
609       ImgDelete(*imgp);
610     }
611     *imgp = img;
612   } else if(img) {
613     ImgDelete(img);
614   }
615 
616   return (h != NULL || img != NULL);
617 }
618 
ign_fwrite(const void * ptr,size_t size,size_t nmemb,FILE * stream)619 static void ign_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
620 {
621   size_t result;
622 
623   result = fwrite(ptr, size, nmemb, stream);
624   (void)result;
625 }
626 
627 /* We do not record the file-name, so we can only output raw image
628  * data. What format to choose? Highly compressed would be best; we
629  * choose "gzip --best" or "bzip --best" if available; the
630  * alpha-channel is output as pgm and the image data as RGB pnm
631  * (channels == 4) or also as pgm (channels == 2).
632  */
ImgStreamOut(Pool * p,Handle * h,Image * img)633 int ImgStreamOut(Pool *p, Handle *h, Image *img)
634 {
635   FILE *f = PoolOutputFile(p);
636   char *obuf;
637   int olen;
638 
639   if (f == NULL ||
640       (img == NULL &&
641        (h == NULL || (img = (Image *)HandleObject(h)) == NULL))) {
642     return false;
643   }
644 
645   fprintf(f, "image {\n");
646   PoolIncLevel(p, 1);
647   if (PoolStreamOutHandle(p, h, img != NULL)) {
648     PoolFPrint(p, f, "width %d\n", img->width);
649     PoolFPrint(p, f, "height %d\n", img->height);
650     PoolFPrint(p, f, "channels %d\n", img->channels);
651     PoolFPrint(p, f, "maxval %d\n", img->maxval);
652     /* For convenience we dump the stuff in pnm/pgm format with separate
653      * alpha channel.
654      */
655     switch (img->channels) {
656     case 2: /* two greymaps, luminance and alpha */
657 #if 1
658       olen = ImgWritePAM(img, 0x3, HAVE_LIBZ, &obuf);
659       PoolFPrint(p, f, "data LUMINANCE_ALPHA %s%d {\n",
660 		 HAVE_LIBZ ? "gzip " : "", olen);
661       ign_fwrite(obuf, olen, 1, f);
662       fprintf(f, "\n");
663       PoolFPrint(p, f, "}\n");
664       OOGLFree(obuf);
665       break;
666 #else
667       olen = ImgWritePGM(img, 1, HAVE_LIBZ, obuf);
668       PoolFPrint(p, f, "data ALPHA %s%d {\n", HAVE_LIBZ ? "gzip " : "", olen);
669       ign_fwrite(obuf, olen, 1, f);
670       fprintf(f, "\n");
671       PoolFPrint(p, f, "}\n");
672       OOGLFree(obuf);
673       /* fall through to luminance only */
674 #endif
675     case 1: /* greymap, luminance */
676       olen = ImgWritePGM(img, 0, HAVE_LIBZ, &obuf);
677       PoolFPrint(p, f,
678 		 "data LUMINANCE %s%d {\n", HAVE_LIBZ ? "gzip " : "", olen);
679       ign_fwrite(obuf, olen, 1, f);
680       fprintf(f, "\n");
681       PoolFPrint(p, f, "}\n");
682       OOGLFree(obuf);
683       break;
684     case 4: /* RGBA */
685 #if 1
686       olen = ImgWritePAM(img, 0xf, HAVE_LIBZ, &obuf);
687       PoolFPrint(p, f, "data RGBA %s%d {\n", HAVE_LIBZ ? "gzip " : "", olen);
688       ign_fwrite(obuf, olen, 1, f);
689       fprintf(f, "\n");
690       PoolFPrint(p, f, "}\n");
691       OOGLFree(obuf);
692       break;
693 #else
694       olen = ImgWritePGM(img, 3, HAVE_LIBZ, &obuf);
695       PoolFPrint(p, f, "data ALPHA %s%d {\n", HAVE_LIBZ ? "gzip " : "", olen);
696       ign_fwrite(obuf, olen, 1, f);
697       fprintf(f, "\n");
698       PoolFPrint(p, f, "}\n");
699       OOGLFree(obuf);
700       /* fall through to RGB */
701 #endif
702     case 3: /* RGB only */
703       olen = ImgWritePNM(img, 0x07, HAVE_LIBZ, &obuf);
704       PoolFPrint(p, f, "data RGB %s%d {\n", HAVE_LIBZ ? "gzip " : "", olen);
705       ign_fwrite(obuf, olen, 1, f);
706       fprintf(f, "\n");
707       PoolFPrint(p, f, "}\n");
708       OOGLFree(obuf);
709       break;
710     }
711   }
712   PoolIncLevel(p, -1);
713   PoolFPrint(p, f, "}\n");
714   return !ferror(f);
715 }
716 
ImgFSave(Image * img,FILE * outf,char * fname)717 Image *ImgFSave(Image *img, FILE *outf, char *fname)
718 {
719   Pool *p;
720   int ok;
721 
722   p = PoolStreamTemp(fname, NULL, outf, 1, NULL);
723   PoolSetOType(p, PO_DATA);
724   PoolIncLevel(p, 1);
725   ok = ImgStreamOut(p, NULL, img);
726   PoolDelete(p);
727   return ok ? img : NULL;
728 }
729 
ImgFLoad(IOBFILE * inf,char * fname)730 Image *ImgFLoad(IOBFILE *inf, char *fname)
731 {
732   Pool *p;
733   Image *img = NULL;
734 
735   p = PoolStreamTemp(fname, inf, NULL, 0, &ImageOps);
736   ImgStreamIn(p, NULL, &img);
737   PoolDelete(p);
738   return img;
739 }
740 
741 /* Compress the contents of *buffer, free *buffer and return the
742  * compressed buffer in a newly allocated chunk pointed to by
743  * *buffer. Return the amount of compressed data.
744  */
maybe_compress_buffer(char ** buffer,int n_bytes)745 static inline int maybe_compress_buffer(char **buffer, int n_bytes)
746 {
747 #if HAVE_LIBZ
748 # if !HAVE_COMPRESSBOUND
749 #  define compressBound(slen) ((slen) + ((slen) >> 12) + ((slen) >> 14) + 11)
750 # endif
751   char *bufptr;
752   unsigned long c_n_bytes;
753 
754   /* we do it inefficient and quick and dirty: we simply call the
755    * compress() function and compress the entire buffer
756    */
757   bufptr = *buffer;
758   c_n_bytes = compressBound(n_bytes);
759   *buffer = OOGLNewNE(char, c_n_bytes, "compressed buffer");
760   if (gv_compress2((Bytef *)*buffer, &c_n_bytes,
761 		   (Bytef *)bufptr, n_bytes, 9) != Z_OK) {
762     OOGLFree(*buffer);
763     *buffer = bufptr;
764   } else {
765     OOGLFree(bufptr);
766     n_bytes = c_n_bytes;
767   }
768 #endif
769   return n_bytes;
770 }
771 
772 #define PNM_HEADER_LEN (2 + 1 + 10 + 1 + 10 + 1 + 5 + 1)
773 /* P5 1000000000 1000000000 65535\n */
774 
775 /* Pack one channel of the image data into an PGM image and write that
776  * data to *buffer. *buffer is allocated in this function, its length
777  * is returned. If channel >= img->channel, then the result will be an
778  * all-black PGM image. Optionally compress the image.
779  */
ImgWritePGM(Image * img,int channel,bool compressed,char ** buffer)780 int ImgWritePGM(Image *img, int channel, bool compressed, char **buffer)
781 {
782   int row, col, stride, rowlen, depth;
783   unsigned long n_bytes, h_len;
784   char *imgptr, *bufptr;
785 
786   depth   = (img->maxval > 255) + 1;
787   rowlen  = depth * img->width;
788   n_bytes = rowlen * img->height + PNM_HEADER_LEN;
789   bufptr  = *buffer = OOGLNewNE(char, n_bytes, "PGM buffer");
790 
791   bufptr += h_len =
792     sprintf(*buffer, "P5 %d %d %d\n", img->width, img->height, img->maxval);
793   n_bytes -= PNM_HEADER_LEN - h_len;
794 
795   if (channel >= img->channels) {
796     memset(*buffer, 0, n_bytes);
797   } else {
798     stride = img->channels * depth;
799     for (row = img->height-1; row >= 0; row--) {
800       imgptr = img->data + channel + rowlen * img->channels * row;
801       for (col = 0; col < img->width; col++) {
802 	*bufptr++ = *imgptr;
803 	if (depth == 2) {
804 	  *bufptr++ = *(imgptr+1);
805 	}
806 	imgptr += stride;
807       }
808     }
809   }
810 
811   return compressed ? maybe_compress_buffer(buffer, n_bytes) : (int)n_bytes;
812 }
813 
814 /* Pack up to 3 channels of the image data into an PNM image and write
815  * that data to *buffer. *buffer is allocated in this function, its
816  * length is returned. Missing channels are filled with 0's.
817  *
818  * Optionally compress the image.
819  */
ImgWritePNM(Image * img,unsigned chmask,bool compressed,char ** buffer)820 int ImgWritePNM(Image *img, unsigned chmask, bool compressed, char **buffer)
821 {
822   int row, col, stride, depth, rowlen;
823   unsigned long n_bytes, h_len;
824   int channels[3], i, j;
825   char *imgptr, *bufptr;
826 
827   depth   = (img->maxval > 255) + 1;
828   rowlen  = 3 * depth * img->width;
829   n_bytes = rowlen * img->height + PNM_HEADER_LEN;
830 
831   bufptr = *buffer = OOGLNewNE(char, n_bytes, "PNM buffer");
832 
833   channels[0] = channels[1] = channels[2] = -1;
834   for (i = j = 0; i < img->channels && j < 3 && chmask; i++, chmask >>= 1) {
835     if (chmask & 1) {
836       channels[j++] = i;
837     }
838   }
839 
840   bufptr += h_len =
841     sprintf(*buffer, "P6 %d %d %d\n", img->width, img->height, img->maxval);
842   n_bytes -= PNM_HEADER_LEN - h_len;
843 
844   imgptr = img->data;
845   stride = img->channels * depth;
846   for (row = img->height-1; row >= 0; row--) {
847     imgptr = img->data + stride * img->width * row;
848     for (col = 0; col < img->width; col++) {
849       for (j = 0; j < 3; j++) {
850 	if (channels[j] >= 0) {
851 	  for (i = 0; i < depth; i++) {
852 	    *bufptr++ = *(imgptr + channels[j] + i);
853 	  }
854 	} else {
855 	  for (i = 0; i < depth; i++) {
856 	    *bufptr++ = '\0';
857 	  }
858 	}
859       }
860       imgptr += stride;
861     }
862   }
863 
864   return compressed ? maybe_compress_buffer(buffer, n_bytes) : (int)n_bytes;
865 }
866 
867 /* Pack any number of channels into a PAM image. Optionally compress
868  * the image. Excess bits set in chmask are ignored. The destination
869  * image will have min(img->channels, #chmask bits) channels (i.e. at
870  * most 4).
871  *
872  * The destination image is written into memory, allocated in
873  * *buffer. The amount of data in *buffer is returned (size after
874  * compression).
875  */
876 
877 /* worst case header length for our case */
878 #define PAM_HEADER_LEN				\
879   sizeof("P7\n"					\
880 	 "WIDTH 1000000000\n"			\
881 	 "HEIGHT 1000000000\n"			\
882 	 "DEPTH 4\n"				\
883 	 "MAXVAL 65535\n"			\
884 	 "ENDHDR\n")
885 
ImgWritePAM(Image * img,unsigned chmask,bool compressed,char ** buffer)886 int ImgWritePAM(Image *img, unsigned chmask, bool compressed, char **buffer)
887 {
888   int row, col, stride, depth, rowlen;
889   unsigned long n_bytes, h_len;
890   int channels[4], n_chan, i, j;
891   char *imgptr, *bufptr;
892 
893   for (i = n_chan = 0; i < img->channels && chmask; i++, chmask >>= 1) {
894     if (chmask & 1) {
895       channels[n_chan++] = i;
896     }
897   }
898 
899   depth   = (img->maxval > 255) + 1; /* #bytes for a single channel */
900   rowlen  = n_chan * depth * img->width;
901   n_bytes = rowlen * img->height + PAM_HEADER_LEN;
902 
903   bufptr = *buffer = OOGLNewNE(char, n_bytes, "PAM buffer");
904 
905   bufptr += h_len =
906     sprintf(*buffer,
907 	    "P7\nWIDTH %d\nHEIGHT %d\nDEPTH %d\nMAXVAL %d\nENDHDR\n",
908 	    img->width, img->height, n_chan, img->maxval);
909   n_bytes -= PAM_HEADER_LEN - h_len;
910 
911   imgptr = img->data;
912   stride = img->channels * depth;
913   for (row = img->height-1; row >= 0; row--) {
914     imgptr = img->data + stride * img->width * row;
915     for (col = 0; col < img->width; col++) {
916       for (j = 0; j < n_chan; j++) {
917 	for (i = 0; i < depth; i++) {
918 	  *bufptr++ = *(imgptr + channels[j] + i);
919 	}
920       }
921       imgptr += stride;
922     }
923   }
924 
925   return compressed ? maybe_compress_buffer(buffer, n_bytes) : (int)n_bytes;
926 }
927 
928 /* Dump an image to disk through the given filter. The filter must
929  * read from stdin. The output of the filter is discarded, it is
930  * assumed that "filter" writes the data to disk or to whatever
931  * destination itself.
932  *
933  * This is mainly to support textures with the RenderMan MG backend.
934  *
935  * The image filter must understand PAM image data if chmask has 2 or
936  * 4 bits set, otherwise it must understand PNM data.
937  *
938  * This routine is ugly: we simply convert the image data into a PGM,
939  * PNM or PAM image in memory using ImgWritePGM/PNM/PAM() and then
940  * pipe the resulting data with data_pipe() to the stdin of the
941  * filter.
942  *
943  * E.g.:
944  *
945  * ImgWriteFilter(img, 0xf, "pamtotiff -lzw -truecolor > img.tiff");
946  *
947  */
ImgWriteFilter(Image * img,unsigned chmask,const char * filter)948 bool ImgWriteFilter(Image *img, unsigned chmask, const char *filter)
949 {
950   int n_chan, buflen = 0;
951   unsigned mask;
952   char *buf = NULL;
953   int data_fd = -1;
954   int data_pid = -1, filter_pid = -1;
955   int result = false;
956   void (*old_sigchld)(int);
957 
958   for (n_chan = 0, mask = chmask; mask; n_chan += mask & 1, mask >>= 1);
959   n_chan = min(img->channels, n_chan);
960 
961   switch (n_chan) {
962   case 1:
963     buflen = ImgWritePGM(img, 0, false, &buf);
964     break;
965   case 3:
966     buflen = ImgWritePNM(img, chmask, false, &buf);
967     break;
968   case 2:
969   case 4:
970     buflen = ImgWritePAM(img, chmask, false, &buf);
971     break;
972   }
973   if ((data_fd = data_pipe(buf, buflen, &data_pid)) <= 0) {
974     OOGLError(1, "ImgWriteFilter(): unable to generate data pipe");
975     goto out;
976   }
977   if (run_filter(filter, data_fd, true, &filter_pid) < 0) {
978     OOGLError(1, "ImgWriteFilter(): unable to run image filter");
979     goto out;
980   }
981   result = true;
982  out:
983   if (buf) {
984     OOGLFree(buf);
985   }
986   if (data_fd) {
987     close(data_fd);
988   }
989   /* This could cause problems when an emodule exits during the short
990    * period of SIGDFLT being in action ...
991    */
992   signal(SIGCHLD, old_sigchld = signal(SIGCHLD, SIG_DFL));
993   if (old_sigchld != SIG_DFL &&
994       old_sigchld != SIG_IGN &&
995       old_sigchld != SIG_ERR) {
996     /* ... send ourselves a SIGCHLD in this case. */
997     kill(getpid(), SIGCHLD);
998   } else {
999     /* possibly wait for the filter processes. */
1000     while (filter_pid != -1 || data_pid != -1) {
1001       int cpid, status;
1002 
1003       cpid = wait(&status);
1004       if (cpid == filter_pid) {
1005 	filter_pid = -1;
1006       } else if (cpid == data_pid) {
1007 	data_pid = -1;
1008       }
1009       if (cpid == -1) {
1010 	break;
1011       }
1012     }
1013   }
1014   return result;
1015 }
1016 
1017 /* reading of images */
1018 
1019 struct filter
1020 {
1021   struct filter *next;
1022   const char *program;
1023   int (*builtin)(const char *data, int datalen, int *cpidp);
1024   const char *suffixes[10];
1025 };
1026 
1027 static struct filter decompressors[] = {
1028   { decompressors+1, "gzip -dc", gzip_data_pipe, { "z", "gz", "gzip", NULL } },
1029   { decompressors+2, "bzip2 -dc", NULL, { "bz2", "bzip2", NULL } },
1030   { NULL, NULL, zlib_data_pipe, { "zlib", NULL } },
1031 };
1032 
1033 static struct filter converters[] = {
1034   { converters+1, "tifftopnm", NULL, { "tiff", "tif", NULL } },
1035   { converters+2, "pngtopnm", NULL, { "png", NULL } },
1036   { converters+3, "giftoppm", NULL, { "gif", NULL } },
1037   { NULL, "jpegtopnm", NULL, { "jpeg", "jpg", NULL } },
1038 };
1039 
readimage(Image * img,unsigned * chmask,char * filtertype,char * imgfname,char * imgdata,int datalen)1040 static bool readimage(Image *img, unsigned *chmask, char *filtertype,
1041 		      char *imgfname, char *imgdata, int datalen)
1042 {
1043   struct filter *filters[2] = { decompressors, converters }, *filter;
1044   int filterpids[2] = { -1, -1 }, datapid = -1;
1045   int filterfds[2] = { -1, -1 };
1046   IOBFILE *imgf = NULL;
1047   int imgfd = -1;
1048   char *suffix, *explicit_filter = NULL;
1049   struct imgheader header;
1050   int i, j;
1051   bool result = true;
1052   void (*old_sigchld)(int);
1053 
1054   if (imgfname) {
1055     if ((imgfd = open(imgfname, O_RDONLY)) == -1) {
1056       OOGLError(0, "can't open file \"%s\"", imgfname);
1057       return false;
1058     }
1059   } else {
1060     /* What to do with imgdata? If we do not need a filter, then we
1061      * could just use the data-block we have already read in. If we
1062      * need a filter, then we fork a child that pipes imgdata to the
1063      * filter (in order to prevent deadlocks).
1064      */
1065   }
1066 
1067   if (filtertype == NULL) {
1068     filtertype = imgfname ? strdup(imgfname) : NULL;
1069   } else {
1070     filtertype = strdup(filtertype);
1071     explicit_filter = filtertype;
1072   }
1073 
1074   for (j = 0; j < 2; j++) {
1075     if (filtertype == NULL || (suffix = strrchr(filtertype, '.')) == NULL) {
1076       suffix = filtertype;
1077     } else {
1078       suffix++; /* advance past '.' */
1079     }
1080     if (suffix) {
1081       for (filter = filters[j]; filter != NULL; filter = filter->next) {
1082 	for (i = 0; filter->suffixes[i] != NULL; i++) {
1083 	  if (strcasecmp(suffix, filter->suffixes[i]) == 0) {
1084 	    if (imgfd == -1 && filter->builtin != NULL) {
1085 	      /* tail-filter: maybe use builtin */
1086 	      imgfd = filter->builtin(imgdata, datalen, &datapid);
1087 	      if (imgfd < 0) {
1088 		result = false;
1089 		goto out;
1090 	      }
1091 	    } else {
1092 	      if (imgfd == -1) {
1093 		imgfd = data_pipe(imgdata, datalen, &datapid);
1094 		if (imgfd < 0) {
1095 		  result = false;
1096 		  goto out;
1097 		}
1098 	      }
1099 	      filterfds[j] =
1100 		run_filter(filter->program, imgfd, false, &filterpids[j]);
1101 	      if (filterfds[j] < 0) {
1102 		result = false;
1103 		goto out;
1104 	      }
1105 	    }
1106 	    if (suffix != filtertype) {
1107 	      suffix--;
1108 	    }
1109 	    *suffix = '\0'; /* mark this part as handled */
1110 	    break;
1111 	  }
1112 	}
1113 	if (filterfds[j] >= 0) {
1114 	  close(imgfd);
1115 	  imgfd = filterfds[j];
1116 	  break;
1117 	}
1118       }
1119     }
1120   }
1121   /* try to run any explicitly specified filter, but omit the dummy
1122    * filter "raw", which just means to interprete the stuff literally.
1123    */
1124   if (filterfds[1] < 0 && explicit_filter &&
1125       *explicit_filter != '\0' && strcasecmp(suffix, "raw") != 0) {
1126     /* explicitly specified filter */
1127     filterfds[1] = run_filter(explicit_filter, imgfd, false, &filterpids[1]);
1128     if (filterfds[1] < 0) {
1129       result = false;
1130       goto out;
1131     } else {
1132       close(imgfd);
1133       imgfd = filterfds[1];
1134     }
1135   }
1136 
1137   /* at this point we assume that reading from imgfd will give us raw,
1138    * SGI or PNM data. We generate an IOBFILE, simply because we want
1139    * to be able to use those iobfgetni() etc. functions, and because
1140    * parseheader() might want to set a file-marker.
1141    */
1142   if (imgfd == -1) {
1143     if ((imgf = iobfileopen(fmemopen(imgdata, datalen, "r"))) == NULL) {
1144       result = false;
1145       goto out;
1146     }
1147   } else {
1148     if ((imgf = iobfileopen(fdopen(imgfd, "r"))) == NULL) {
1149       result = false;
1150       goto out;
1151     }
1152   }
1153 
1154   if (filtertype == NULL || (suffix = strrchr(filtertype, '.')) == NULL) {
1155     suffix = filtertype;
1156   } else {
1157     suffix++; /* advance past '.' */
1158   }
1159   if (!parseheader(img, imgf, chmask, suffix, &header)
1160       ||
1161       !readdata(img, imgf, *chmask, &header)) {
1162     result = false;
1163     goto out;
1164   }
1165 
1166  out:
1167   free(filtertype); /* undo strdup() */
1168 
1169   /* This could cause problems when an emodule exits during the short
1170    * period of SIGDFLT being in action ...
1171    */
1172   signal(SIGCHLD, old_sigchld = signal(SIGCHLD, SIG_DFL));
1173   if (old_sigchld != SIG_DFL &&
1174       old_sigchld != SIG_IGN &&
1175       old_sigchld != SIG_ERR) {
1176     /* ... send us a SIGCHLD in this case. */
1177     kill(getpid(), SIGCHLD);
1178   } else {
1179     /* possibly wait for the filter processes. */
1180     while (filterpids[0] != -1 || filterpids[1] != -1 || datapid != -1) {
1181       int cpid, status;
1182 
1183       cpid = wait(&status);
1184       for (j = 0; j < 2; j++) {
1185 	if (cpid == filterpids[j]) {
1186 	  filterpids[j] = -1;
1187 	}
1188       }
1189       if (cpid == datapid) {
1190 	datapid = -1;
1191       }
1192       if (cpid == -1) {
1193 	break;
1194       }
1195     }
1196   }
1197 
1198   if (imgf) {
1199     iobfclose(imgf);
1200   }
1201 
1202   return result;
1203 }
1204 
1205 static bool
parseheader(Image * img,IOBFILE * imgf,unsigned * chmask,const char * type,struct imgheader * header)1206 parseheader(Image *img, IOBFILE *imgf, unsigned *chmask,
1207 	    const char *type, struct imgheader *header)
1208 {
1209   char scratch[1024];
1210   char *w;
1211   int chmask_channels;
1212   unsigned mask;
1213   int i, c;
1214   char *msg = NULL;
1215   IOBFILE *f = NULL;
1216 
1217   header->rleoff = NULL;
1218   header->rledata = NULL;
1219 
1220   chmask_channels = 0;
1221   for (i = 0, mask = *chmask; i < 4 && mask; i++, mask >>= 1) {
1222     if (mask & 1) {
1223       chmask_channels++;
1224     }
1225   }
1226 
1227   if (type && strcasecmp(type, "raw") == 0) {
1228     if (*chmask == IMGF_AUTO) {
1229       OOGLError(1, "parseheader(): "
1230 		"chmask == AUTO cannot work with raw image data");
1231       return false;
1232     }
1233     header->format   = IMGF_BYTE;
1234     header->xsize    = img->width;
1235     header->ysize    = img->height;
1236     header->maxval   = img->maxval ? img->maxval : 255;
1237     header->channels = chmask_channels;
1238   } else {
1239     if (iobfsetmark(imgf) < 0) {
1240       OOGLError(1, "parseheader(): cannot set file mark");
1241       return false;
1242     }
1243     c = iobfgetc(imgf);
1244     if (c == 0x01 && (c = iobfgetc(imgf)) == 0xDA) {
1245       /* SGI image file */
1246       short shorts[3];
1247       int storage = iobfgetc(imgf);
1248       int bpp = iobfgetc(imgf);
1249       if (bpp != 1) {
1250 	msg = "%s: must have 8-bit image values";
1251 	goto nope;
1252       }
1253       iobfgetc(imgf); iobfgetc(imgf);		/* Skip "dimension" */
1254       iobfgetns(imgf, 3, shorts, 1);	/* Read big-endian 16-bit values */
1255       header->xsize    = shorts[0];
1256       header->ysize    = shorts[1];
1257       header->channels = shorts[2];
1258       header->maxval   = 255;
1259       if (iobfread(scratch, 4+4+492, 1, imgf) <= 0) {
1260 	/* Skip min, max, pad data */
1261 	msg = "can't consume pad data";
1262 	goto nope;
1263       }
1264       header->format = (storage == 0x01) ? IMGF_SGIRLE : IMGF_SGIRAW;
1265       if (header->format == IMGF_SGIRLE) {
1266 	/* Inhale offset&length table */
1267 	int n = header->ysize * header->channels;
1268 	int max = 0;
1269 
1270 	header->rleoff = OOGLNewNE(int, n*2, "IMGF_SGIRLE offsets");
1271 	msg = "%s: can't read RLE offsets";
1272 	if (iobfgetni(imgf, n*2, header->rleoff, 1) != n*2) {
1273 	  goto nope;
1274 	}
1275 	if (iobftell(imgf) < 0) {
1276 	  for (i = 0; i < n; i++) {
1277 	    if (max < header->rleoff[i]) {
1278 	      max = header->rleoff[i] + header->rleoff[i+n];
1279 	    }
1280 	  }
1281 	  header->rledata = OOGLNewNE(char, max+1, "IMGF_SGIRLE data");
1282 	  if (iobfread(header->rledata, max, 1, f) <= 0) {
1283 	    goto nope;
1284 	  }
1285 	}
1286       }
1287     } else if (c == 'P' && (c = iobfgetc(imgf)) >= '1' && c <= '7') {
1288       if (c <= '6') {
1289 	msg = "%s: Bad header on PNM image";
1290 	header->channels = (c == '3' || c == '6') ? 3 : 1;
1291 	if (iobfgetni(imgf, 1, &header->xsize, 0) != 1) {
1292 	  goto nope;
1293 	}
1294 	if (iobfgetni(imgf, 1, &header->ysize, 0) != 1) {
1295 	  goto nope;
1296 	}
1297 	if (c != '1' && c != '4') {
1298 	  if (iobfgetni(imgf, 1, &header->maxval, 0) <= 0) {
1299 	    goto nope;
1300 	  }
1301 	} else {
1302 	  header->maxval = 255;
1303 	}
1304 	switch(c) {
1305 	case '1': case '2': case '3': header->format = IMGF_ASCII; break;
1306 	case '4':                     header->format = IMGF_BIT;   break;
1307 	case '5': case '6':           header->format = IMGF_BYTE;  break;
1308 	}
1309 	while((c = iobfgetc(imgf)) != '\n' && c != EOF);
1310       } else {
1311 	int need = 4;
1312 	/* newer PAM format, possibly including an alpha channel */
1313 	msg = "%s: Bad header on PAM image";
1314 
1315 	while ((w = iobftoken(imgf, 0)) != NULL) {
1316 	  if (strncmp(w, "ENDHDR", 6) == 0) {
1317 	    break;
1318 	  } else if (strncmp(w, "HEIGHT", 6) == 0) {
1319 	    if (iobfgetni(imgf, 1, &header->ysize, 0) != 1) {
1320 	      goto nope;
1321 	    }
1322 	    --need;
1323 	  } else if (strncmp(w, "WIDTH", 5) == 0) {
1324 	    if (iobfgetni(imgf, 1, &header->xsize, 0) != 1) {
1325 	      goto nope;
1326 	    }
1327 	    --need;
1328 	  } else if (strncmp(w, "DEPTH", 5) == 0) {
1329 	    if (iobfgetni(imgf, 1, &header->channels, 0) != 1) {
1330 	      goto nope;
1331 	    }
1332 	    --need;
1333 	  } else if (strncmp(w, "MAXVAL", 5) == 0) {
1334 	    if (iobfgetni(imgf, 1, &header->maxval, 0) != 1) {
1335 	      goto nope;
1336 	    }
1337 	    if (header->maxval > 255) {
1338 	      msg =
1339 		"%s: PAM herader: sorry, 16 bits per channel is unsupported";
1340 	      goto nope;
1341 	    }
1342 	    --need;
1343 	  } else {
1344 	    /* just skip everything else, i.e. the tuple type. We
1345 	     * interprete 1 channel as luminance, 2 channels as
1346 	     * luminance & alpha, 3 channels as RGB and 4 channels as
1347 	     * RGBA, no matter what the header says.
1348 	     */
1349 	  }
1350 	}
1351 	if (need != 0) {
1352 	  goto nope; /* did not find all required fields */
1353 	}
1354 	header->format = IMGF_BYTE; /* will work */
1355       }
1356     } else {
1357       /* assume raw pixel data */
1358       iobfseekmark(imgf);
1359       iobfclearmark(imgf);
1360       if (*chmask == 0xf) {
1361 	OOGLError(1, "parseheader(): "
1362 		  "chmask == AUTO cannot work with raw image data");
1363 	return false;
1364       }
1365       header->format   = IMGF_BYTE;
1366       header->xsize    = img->width;
1367       header->ysize    = img->height;
1368       header->maxval   = img->maxval ? img->maxval : 255;
1369       header->channels = chmask_channels;
1370     }
1371   }
1372 
1373   /* set image dimensions from the data if the user did not specify
1374    * them explicitly.
1375    */
1376   if (img->width <= 0) {
1377     img->width = header->xsize;
1378   }
1379   if (img->height <= 0) {
1380     img->height = header->ysize;
1381   }
1382 
1383   /* Check for consistency. The number of channels can be different,
1384    * but the x- and y-size must match the width and height of the
1385    * image.
1386    */
1387   if (img->width != header->xsize || img->height != header->ysize) {
1388     msg = "%s: specified image dimensions do not match the image data";
1389     goto nope;
1390   }
1391 
1392   if (*chmask == 0) { /* determine channels from image header */
1393     *chmask = (1 << header->channels) - 1;
1394   } else if (chmask_channels > header->channels && header->channels != 1) {
1395     /* We allow to fill several destination channels with a single
1396      * grey-level image, but otherwise the user has to provide enough
1397      * channels such that the number destination and source channels
1398      * match.
1399      */
1400     OOGLError(1, "parseheader(): "
1401 	      "source image has only %d channels, needed are %d channels",
1402 	      header->channels, chmask_channels);
1403     msg = NULL;
1404     goto nope;
1405   }
1406 
1407   return true;
1408 
1409  nope:
1410   header->xsize = header->ysize = header->channels = 0;
1411   if (msg) {
1412     OOGLError(0, msg, "parseheader()");
1413   }
1414   return false;
1415 }
1416 
1417 static bool
readdata(Image * img,IOBFILE * imgf,unsigned chmask,struct imgheader * header)1418 readdata(Image *img, IOBFILE *imgf, unsigned chmask, struct imgheader *header)
1419 {
1420   int val, bit, i, j, k, depth;
1421   int chan_map[4] = { -1, -1, -1, -1 };
1422   int n_chan, maxval, mask_channels;
1423   unsigned mask;
1424   int rowsize, stride, yup, rlebase;
1425 
1426   if (header->xsize <= 0 || header->ysize <= 0) {
1427     return false;
1428   }
1429 
1430   /* readdata() only works with maxval == 255; and nothing else is
1431    * used elsewhere in Geomview, so what.
1432    */
1433   if (img->maxval != 255) {
1434     OOGLError(0, "readdata(): maxval != 255 is not supported yet");
1435     return false;
1436   }
1437   maxval = 255;
1438 
1439   /* chan_map[j] == i: stuff the j-th source channel into the i-th
1440    * destination channel
1441    */
1442   for (i = mask_channels = 0, mask = chmask;
1443        i < 4 && mask != 0; i++, mask >>= 1) {
1444     if (mask & 1) {
1445       chan_map[mask_channels++] = i;
1446     }
1447   }
1448   n_chan = max(img->channels, i);
1449 
1450   depth   = ((maxval > 255) + 1);
1451   stride  = n_chan * depth;
1452   rowsize = img->width * stride;
1453 
1454   if (img->data == NULL || img->channels < n_chan) {
1455     char *old_data = img->data;
1456     char *new_data;
1457     int old_stride;
1458     char *old_buf = old_data;
1459 
1460     img->data =
1461       new_data = OOGLNewNE(char, img->height * rowsize, "New image data");
1462 
1463     old_stride = old_data ? img->channels : 0;
1464     for (i = 0; i < img->height * img->width; i++) {
1465       for (k = 0; k < old_stride; k++) {
1466 	new_data[k] = old_data[k];
1467       }
1468       /* Also initialize any new fields. This would only be needed for
1469        * channels not set in chmask, but the conditional would
1470        * probably eat up more time than the assignment. Reverting the
1471        * loop-order would also do no good because this is a pixmap:
1472        * iterating over the channels in the outer loop would be very
1473        * unfriendly for the cache.
1474        */
1475       for (; k < stride-1; k++) {
1476 	new_data[k] = '\0';
1477       }
1478       new_data[stride-1] = (char)0xff; /* alpha channel defaults to 0xff */
1479       old_data += old_stride;
1480       new_data += stride;
1481     }
1482     OOGLFree(old_buf);
1483     img->channels = n_chan;
1484   }
1485 
1486   switch (header->format) {
1487   case IMGF_SGIRAW:
1488     for (k = 0; k < min(mask_channels, header->channels); k++) {
1489       int dst_chan = chan_map[k];
1490       for (i = 0; i < header->ysize; i++) {
1491 	char *pix = img->data + dst_chan + rowsize * i;
1492 	j = header->xsize;
1493 	do {
1494 	  *pix = iobfgetc(imgf);
1495 	  pix += stride;
1496 	} while(--j > 0);
1497       }
1498       if (iobfeof(imgf)) {
1499 	goto nope;
1500       }
1501     }
1502     break;
1503   case IMGF_SGIRLE:
1504     yup = header->rleoff[0] < header->rleoff[1];
1505     rlebase = 512 + header->channels*header->ysize*4;
1506     for (k = 0; k < min(mask_channels, header->channels); k++) {
1507       int dst_chan = chan_map[k];
1508       for (i = 0; i < header->ysize; i++) {
1509 	char *rle = NULL;
1510 	int row = (yup ? i : header->ysize-i-1);
1511 	char *pix = img->data + dst_chan + rowsize * row;
1512 	int foff = header->rleoff[k*header->ysize + row];
1513 	/*int len = header->rleoff[(k+header->channels)*header->ysize + row];*/
1514 	int count;
1515 	j = header->xsize;
1516 	if (header->rledata) {
1517 	  rle = header->rledata + foff - rlebase;
1518 	} else {
1519 	  iobfseek(imgf, foff, SEEK_SET);
1520 	}
1521 	while ((count = rle ? *rle++ : iobfgetc(imgf)) > 0) {
1522 	  if (count & 0x80) {
1523 	    count &= 0x7F;
1524 	    do {
1525 	      *pix = rle ? *rle++ : iobfgetc(imgf);
1526 	      pix += stride;
1527 	    } while(--count > 0);
1528 	  } else {
1529 	    int val = rle ? *rle++ : iobfgetc(imgf);
1530 	    do {
1531 	      *pix = val;
1532 	      pix += stride;
1533 	    } while(--count > 0);
1534 	  }
1535 	}
1536 	if (iobfeof(imgf)) {
1537 	  goto nope;
1538 	}
1539       }
1540     }
1541   case IMGF_BYTE:
1542   case IMGF_BIT:
1543   case IMGF_ASCII:
1544     for (i = 0; i < header->ysize; i++) {
1545       char *row = img->data + rowsize * (header->ysize - i - 1);
1546       if (img->channels == header->channels &&
1547 	  header->format == IMGF_BYTE &&
1548 	  (chmask ^ ((1 << n_chan) - 1)) == 0 && header->maxval == 255){
1549 	/* all channels specified, so just copy the row in one run */
1550 	j = iobfread(row, header->channels, header->xsize, imgf);
1551       } else {
1552 	char *pix = row;
1553 	j = header->xsize;
1554 	switch(header->format) {
1555 	case IMGF_BYTE:
1556 	  do {
1557 	    for (k = 0; k < min(mask_channels, header->channels); k++) {
1558 	      pix[chan_map[k]] = iobfgetc(imgf) * 255 / header->maxval;
1559 	    }
1560 	    for (; k < header->channels; k++) {
1561 	      iobfgetc(imgf); /* consume any remaining channels */
1562 	    }
1563 	    pix += stride;
1564 	  } while(--j);
1565 	  break;
1566 	case IMGF_BIT:
1567 	  bit = 0;
1568 	  do {
1569 	    if (--bit < 0) {
1570 	      bit = 7;
1571 	      k = iobfgetc(imgf);
1572 	    }
1573 	    pix[chan_map[0]] = (k >> bit) & 1;
1574 	    pix += stride;
1575 	  } while(--j > 0);
1576 	  break;
1577 	case IMGF_ASCII:
1578 	  do {
1579 	    for (k = 0; k < min(mask_channels, header->channels); k++) {
1580 	      iobfgetni(imgf, 1, &val, 0);
1581 	      pix[chan_map[k]] = val * 255 / header->maxval;
1582 	    }
1583 	    for (; k < header->channels; k++) {
1584 	      iobfgetni(imgf, 1, &val, 0); /* consume any remaining channels */
1585 	    }
1586 	    pix += stride;
1587 	  } while(--j > 0);
1588 	  break;
1589 	default:
1590 	  break;
1591 	}
1592       }
1593       if (iobfeof(imgf))
1594 	break;
1595     }
1596   }
1597 
1598   if (header->channels == 1 && mask_channels > 1) {
1599     /* clone the data from chan_map[0] into all other channels; this
1600      * allows to generate an RGB(A) image from a single grey-map
1601      */
1602     char *pix = img->data;
1603 
1604     for (i = 0; i < img->height * rowsize; i++) {
1605       for (j = 1; j < mask_channels; j++) {
1606 	for (k = 0; k < depth; k++) {
1607 	  pix[chan_map[j]+k] = pix[chan_map[0]+k];
1608 	}
1609       }
1610       pix += stride;
1611     }
1612   }
1613 
1614  nope:
1615   if (header->rleoff) {
1616     OOGLFree(header->rleoff);
1617   }
1618   if (header->rledata) {
1619     OOGLFree(header->rledata);
1620   }
1621   header->rleoff = NULL;
1622   header->rledata = NULL;
1623 
1624   if (i < header->ysize) {
1625     OOGLError(0, "readdata(): Error reading image row %d of %d",
1626 	      i, header->ysize);
1627     return false;
1628   }
1629   return true;
1630 }
1631 
1632 /* Take a file-descriptor and connect it to the stdin of a filter
1633  * program, return a file descriptor which is connected to stdout of
1634  * the filter.
1635  *
1636  * If wronly == true, then also close the read-part of the pipe and
1637  * return 0 on success.
1638  */
run_filter(const char * filter,int fdin,bool wronly,int * cpidp)1639 static int run_filter(const char *filter, int fdin, bool wronly, int *cpidp)
1640 {
1641   int pfd[2];
1642   int cpid;
1643 
1644   if (!wronly) {
1645     if (pipe(pfd) == -1) {
1646       OOGLError(1, "%s: pipe() failed", filter);
1647       return -1;
1648     }
1649   }
1650 
1651   if ((cpid = fork()) == -1) {
1652     OOGLError(1, "%s: fork() failed", filter);
1653     return -1;
1654   }
1655 
1656   if (cpid == 0) {
1657     /* child (filter process), close pdf[0] (the reader), connect fdin
1658      * to stdin, and pdf[1] to stdout.
1659      */
1660     close(STDIN_FILENO);
1661     if (dup2(fdin, STDIN_FILENO) != STDIN_FILENO) {
1662       OOGLError(1, "%s: cannot reassign STDIN_FILENO");
1663       _exit(EXIT_FAILURE);
1664     }
1665     close(fdin);
1666 
1667     if (wronly) {
1668       /* close stdout and duplicate it on stderr, otherwise a process
1669        * listening on our stdout might get confused.
1670        */
1671       close(STDOUT_FILENO);
1672       if (dup2(STDERR_FILENO, STDOUT_FILENO) != STDOUT_FILENO) {
1673 	OOGLError(1, "%s: cannot reassign STDOUT_FILENO");
1674 	_exit(EXIT_FAILURE);
1675       }
1676     } else {
1677       /* if !wronly close the read-end of the pipe and dup the write
1678        * end on STDOUT.
1679        */
1680       close(pfd[0]);
1681       close(STDOUT_FILENO);
1682       if (dup2(pfd[1], STDOUT_FILENO) != STDOUT_FILENO) {
1683 	OOGLError(1, "%s: cannot reassign STDOUT_FILENO");
1684 	_exit(EXIT_FAILURE);
1685       }
1686       close(pfd[1]);
1687     }
1688 
1689     /* now run the filter process, run it through sh 'cause `filter'
1690      * can be a command with arguments (and possibly file descriptor
1691      * redirections).
1692      */
1693     execl("/bin/sh", "sh", "-c", filter, NULL);
1694 
1695     OOGLError(1, "execl(%s) failed.", filter);
1696     _exit(EXIT_FAILURE);
1697   } else {
1698     /* parent */
1699     if (cpidp) {
1700       *cpidp = cpid;
1701     }
1702     if (!wronly) {
1703       close(pfd[1]); /* close the write end */
1704     }
1705   }
1706 
1707   return wronly ? 0 : pfd[0]; /* return the read end */
1708 }
1709 
1710 /* Fork a child which pipes data through a pipe to the parent.
1711  *
1712  * The return value is a pipe descriptor connected to the child's
1713  * output.
1714  */
data_pipe(const char * data,int datalen,int * cpidp)1715 static int data_pipe(const char *data, int datalen, int *cpidp)
1716 {
1717   int pfd[2];
1718   int cpid;
1719 
1720   if (pipe(pfd) == -1) {
1721     OOGLError(1, "data_pipe(): pipe() failed");
1722     return -1;
1723   }
1724 
1725   if ((cpid = fork()) == -1) {
1726     OOGLError(1, "data_pipe(): fork() failed");
1727     return -1;
1728   }
1729 
1730   if (cpid == 0) { /* child */
1731     /* NOTE: we use _exit() and not exit() to avoid calling atexit()
1732      * functions inherited from the parent.
1733      */
1734     close(pfd[0]); /* close the reader */
1735     if (write(pfd[1], data, datalen) != datalen) {
1736       OOGLError(1, "data_pipe(): write() failed");
1737       _exit(EXIT_FAILURE);
1738     }
1739     if (close(pfd[1]) < 0) {
1740       OOGLError(1, "data_pipe(): close() failed");
1741       _exit(EXIT_FAILURE);
1742     }
1743     _exit(EXIT_SUCCESS);
1744   } else {         /* parent */
1745     if (cpidp) {
1746       *cpidp = cpid;
1747     }
1748     close(pfd[1]); /* close the write end */
1749   }
1750 
1751   return pfd[0]; /* return the read end */
1752 }
1753 
1754 #if HAVE_LIBZ
1755 /* Same as data_pipe(), but also decompress the data, assuming it is
1756  * in zlib format.
1757  *
1758  * NOTE: we use _exit() and not exit() to avoid calling atexit()
1759  * functions inherited from the parent.
1760  */
1761 static inline
__zlib_data_pipe(const char * data,int datalen,int * cpidp,bool gzip)1762 int __zlib_data_pipe(const char *data, int datalen, int *cpidp, bool gzip)
1763 {
1764 
1765   int pfd[2];
1766   int cpid;
1767 
1768   if (pipe(pfd) == -1) {
1769     OOGLError(1, "data_pipe(): pipe() failed");
1770     return -1;
1771   }
1772 
1773   if ((cpid = fork()) == -1) {
1774     OOGLError(1, "data_pipe(): fork() failed");
1775     return -1;
1776   }
1777 
1778   if (cpid == 0) { /* child */
1779     Bytef outBuffer[32*1024]; /* 32k, so what */
1780     z_stream stream;
1781     int err, chunklen;
1782 
1783     close(pfd[0]); /* close the reader */
1784 
1785     /* Initialize the zlib interface */
1786     memset(&stream, 0, sizeof(stream)); /* safety */
1787     stream.next_in   = (Bytef*)data;
1788     stream.avail_in  = (uInt)datalen;
1789     stream.next_out  = outBuffer;
1790     stream.avail_out = (uInt)sizeof(outBuffer);
1791 
1792     err = inflateInit2(&stream, MAX_WBITS + (gzip ? 16 : 0));
1793     if (err != Z_OK) {
1794       OOGLError(1, "zlib_data_pipe(): infalteInite2() failed");
1795       _exit(EXIT_FAILURE);
1796     }
1797 
1798     do {
1799       err = inflate(&stream, false /* no flush */);
1800       if (err != Z_OK && err != Z_STREAM_END) {
1801 	OOGLError(1, "zlib_data_pipe(): inflate() returned %d", err);
1802 	_exit(EXIT_FAILURE);
1803       }
1804       chunklen = sizeof(outBuffer) - stream.avail_out;
1805       if (write(pfd[1], outBuffer, chunklen) != chunklen) {
1806 	OOGLError(1, "zlib_data_pipe(): write() failed");
1807 	_exit(EXIT_FAILURE);
1808       }
1809       /* reset output buffer state to idle */
1810       stream.next_out  = outBuffer;
1811       stream.avail_out = (uInt)sizeof(outBuffer);
1812     } while (err != Z_STREAM_END);
1813 
1814     inflateEnd(&stream); /* cleanup (should be a no-op here) */
1815 
1816     if (close(pfd[1]) < 0) {
1817       OOGLError(1, "zlib_data_pipe(): close() failed");
1818       _exit(EXIT_FAILURE);
1819     }
1820     _exit(EXIT_SUCCESS);
1821   } else {         /* parent */
1822     if (cpidp) {
1823       *cpidp = cpid;
1824     }
1825     close(pfd[1]); /* close the write end */
1826   }
1827 
1828   return pfd[0]; /* return the read end */
1829 }
1830 
zlib_data_pipe(const char * data,int datalen,int * cpidp)1831 static int zlib_data_pipe(const char *data, int datalen, int *cpidp)
1832 {
1833   return __zlib_data_pipe(data, datalen, cpidp, false);
1834 }
1835 
gzip_data_pipe(const char * data,int datalen,int * cpidp)1836 static int gzip_data_pipe(const char *data, int datalen, int *cpidp)
1837 {
1838   return __zlib_data_pipe(data, datalen, cpidp, true);
1839 }
1840 
1841 /* Munge a little bit with zlib's interna. The magic bits are the
1842  * "+16" in the "..., MAX_WBITS+16, ..." argument: it instructs zlib
1843  * to emit a gzip header, instead of the simpler zlib header. Mmmh.
1844  */
gv_compress2(Bytef * dest,uLongf * destLen,const Bytef * source,uLong sourceLen,int level)1845 static int gv_compress2(Bytef *dest, uLongf *destLen,
1846 			const Bytef *source, uLong sourceLen,
1847 			int level)
1848 {
1849     z_stream stream;
1850     int err;
1851 
1852     stream.next_in = (Bytef*)source;
1853     stream.avail_in = (uInt)sourceLen;
1854 
1855     stream.next_out = dest;
1856     stream.avail_out = (uInt)*destLen;
1857     if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
1858 
1859     stream.zalloc = (alloc_func)0;
1860     stream.zfree = (free_func)0;
1861     stream.opaque = (voidpf)0;
1862 
1863     err = deflateInit2(&stream, level, Z_DEFLATED, MAX_WBITS+16, MAX_MEM_LEVEL,
1864 		       Z_DEFAULT_STRATEGY);
1865     if (err != Z_OK) return err;
1866 
1867     err = deflate(&stream, Z_FINISH);
1868     if (err != Z_STREAM_END) {
1869         deflateEnd(&stream);
1870         return err == Z_OK ? Z_BUF_ERROR : err;
1871     }
1872     *destLen = stream.total_out;
1873 
1874     err = deflateEnd(&stream);
1875     return err;
1876 }
1877 #endif
1878 
1879 /*
1880  * Local Variables: ***
1881  * mode: c ***
1882  * c-basic-offset: 2 ***
1883  * End: ***
1884  */
1885 
1886