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