1 
2  /* ==================================================================
3     FILE: "/home/joze/pub/zimg/zimg/zimg.c"
4     LAST MODIFIED: "Sa, 27 Aug 2005 14:36:50 CEST (joze)"
5     (C) 1999 - 2003 by Johannes Zellner <johannes@zellner.org>
6     $Id: zimg.c,v 1.75 2005/08/27 12:46:20 joze Exp $
7     ---
8     Copyright (c) 1999 - 2003, Johannes Zellner <johannes@zellner.org>
9     All rights reserved.
10 
11     Redistribution and use in source and binary forms, with or without
12     modification, are permitted provided that the following conditions
13     are met:
14 
15       * Redistributions of source code must retain the above copyright
16         notice, this list of conditions and the following disclaimer.
17       * Redistributions in binary form must reproduce the above copyright
18         notice, this list of conditions and the following disclaimer in the
19         documentation and/or other materials provided with the distribution.
20       * Neither the name of Johannes Zellner nor the names of contributors
21         to this software may be used to endorse or promote products derived
22         from this software without specific prior written permission.
23 
24     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25     ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27     A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR
28     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29     EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33     NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35     ================================================================== */
36 
37 #define MAIN
38 #include "zimg_priv.h"
39 #include "zimg.h"
40 #include "dynaload.h"
41 #include <gd.h>
42 #include <gdfontt.h>  /* tiny */
43 #include <gdfonts.h>  /* small */
44 #include <gdfontmb.h> /* medium bold */
45 #include <gdfontl.h>  /* large */
46 #include <gdfontg.h>  /* giant */
47 
48 #if !defined(unix) && !defined(__unix) && !defined(__unix__)
49 #   include <fcntl.h> /* for setmode() */
50 #endif
51 
52 typedef struct grow_str {
53     char* str;
54     int len;
55 } grow_str;
56 
57 /* not more than 0xff lines */
58 #define ZIMG_MULTILINE_MAXLINES (0xff)
59 
60 typedef struct zimg_multiline_t {
61     int x;
62     int y;
63     int width;  /* max number of characters */
64     int height; /* number of lines */
65     char* lines[ZIMG_MULTILINE_MAXLINES];
66     grow_str str;
67 } zimg_multiline_t;
68 
69 typedef struct zimg_escape_info_t {
70     float max;
71     float min;
72     double integral;
73     char* filename;
74 } zimg_escape_info_t;
75 
76 #if 0
77 static unsigned int
78 getIndex(const zimg_data_t* spec, unsigned int x, unsigned int y)
79 {
80     return spec->x * (spec->ytop + y) + spec->xleft + x;
81 }
82 
83 static const unsigned char
84 getValueUnscaledUnbinned(float* value, const float* data,
85        	const unsigned char* flags, const zimg_data_t* spec, unsigned int x, unsigned int y)
86 {
87     unsigned int idx = getIndex(spec, x, y);
88     if (flags && flags[idx]) {
89 	/* invalid (masked) */
90 	return 1;
91     } else {
92 	*value = *(data + idx);
93 	return 0;
94     }
95 }
96 #endif
97 
98 static void
getValueCroppedBinned(float * value,unsigned char * flags,const float * data,const zimg_data_t * spec,unsigned int x,unsigned int y,float nda)99 getValueCroppedBinned(float* value /* out */, unsigned char* flags /* out */,
100        	const float* data /* in */, const zimg_data_t* spec /* in */,
101        	unsigned int x /* in */, unsigned int y /* in */, float nda /* in */)
102 {
103     unsigned int valid = 0;
104 
105     unsigned int yb, yend;
106     unsigned int idx, idxend;
107     float dval;
108 
109     *value = 0;
110 
111     for (yb = spec->ytop + y * spec->yibin, yend = yb + spec->yibin; yb < yend; yb++) {
112 	for (idx = spec->xleft + x * spec->xibin + yb * spec->x, idxend = idx + spec->xibin; idx < idxend; idx++) {
113 	    dval = *(data + idx);
114 	    if (!flags || nda != dval) {
115 		*value += *(data + idx);
116 		valid++;
117 	    }
118 	}
119     }
120 
121     if (flags) {
122 	*flags = (valid == 0);
123     }
124 
125     if (valid) {
126 	*value /= valid;
127     }
128 }
129 
130 #if 0
131 static float
132 bicubicspline(float z[5], float x, float y)
133 {
134     float z0_ = 0.5 * z[0];
135     float z1  = z[1];
136     float z2_ = 0.5 * z[2];
137     float z3_ = 0.5 * z[3];
138     float z4_ = 0.5 * z[4];
139 
140     float a = -z1 + z0_ + z2_;
141     float b = -z0_ + z2_;
142     float c = z1;
143     float d = -z1 + z3_ + z4_;
144     float e = -z3_ + z4_;
145 
146     return a * x * x + b * x + c + d * y * y + e * y;
147 }
148 #else
149 static float
bicubicspline(float z[5],float x,float y)150 bicubicspline(float z[5], float x, float y)
151 {
152     float c = z[1];
153 
154     float b = 0.5 * (z[2] - z[0]);
155     float a = z[0] + b - c;
156 
157     float e = 0.5 * (z[4] - z[3]);
158     float d = z[3] + e - c;
159 
160 #if 0
161     fprintf(stderr, "1=%g 0=%g 2=%g 3=%g 4=%g\n",
162 	    z[1] - c,
163 	    z[0] - a * (-1) * (-1) - b * (-1) - c,
164 	    z[2] - a * ( 1) * ( 1) - b * ( 1) - c,
165 	    z[3] - d * (-1) * (-1) - e * (-1) - c,
166 	    z[4] - d * ( 1) * ( 1) - e * ( 1) - c);
167 #endif
168 
169     return a * x * x + b * x + c + d * y * y + e * y;
170 }
171 #endif
172 
173 static unsigned char
getData(float * value,const float * data,const unsigned char * flags,const zimg_data_t * spec,unsigned int x,unsigned int y)174 getData(float* value /* out */, const float* data /* in */,
175 	const unsigned char* flags /* in */, const zimg_data_t* spec /* in */,
176 	unsigned int x /* in */, unsigned int y /* in */)
177 {
178     unsigned int idx;
179 
180     if (x >= spec->x || y >= spec->y) {
181 	return 1;
182     }
183 
184     idx = y * spec->x + x;
185 
186     if (flags && *(flags + idx))
187 	return 1;
188 
189     *value = *(data + idx);
190     return 0;
191 }
192 
193 static float
triangle1quadrant(float z1,float z2,float z3,float x,float y)194 triangle1quadrant(float z1, float z2, float z3, float x, float y)
195 {
196     float a = -z1 + z2;
197     float b = -z3 + z1;
198     float c = z1;
199     return a * x + b * y + c;
200 }
201 
202 static float
triangle2quadrant(float z1,float z2,float z3,float x,float y)203 triangle2quadrant(float z1, float z2, float z3, float x, float y)
204 {
205     float a = -z3 + z1;
206     float b =  z1 - z2;
207     float c = z1;
208     return a * x + b * y + c;
209 }
210 
211 static float
triangle3quadrant(float z1,float z2,float z3,float x,float y)212 triangle3quadrant(float z1, float z2, float z3, float x, float y)
213 {
214     float a = z1 - z2;
215     float b = z3 - z1;
216     float c = z1;
217     return a * x + b * y + c;
218 }
219 
220 static float
triangle4quadrant(float z1,float z2,float z3,float x,float y)221 triangle4quadrant(float z1, float z2, float z3, float x, float y)
222 {
223     float a =  z3 - z1;
224     float b = -z1 + z2;
225     float c = z1;
226     return a * x + b * y + c;
227 }
228 
229 static unsigned char
getValueScaled(float * value,const float * data,const unsigned char * flags,const zimg_data_t * spec,unsigned int x,unsigned int y)230 getValueScaled(float* value /* out */, const float* data /* in */,
231        	const unsigned char* flags /* in */, const zimg_data_t* spec /* in */,
232        	unsigned int x /* in, image property */, unsigned int y /* in, image property */)
233 {
234     float data_dx = ((float)x / (float)spec->xdscale) - 0.5;
235     float data_dy = ((float)y / (float)spec->ydscale) - 0.5;
236 
237     unsigned char left   = 0;
238     unsigned char center = 0;
239     unsigned char right  = 0;
240     unsigned char top    = 0;
241     unsigned char bot    = 0;
242 
243     float z[5];
244 
245     unsigned int data_xcen = (int)rint(data_dx);
246     unsigned int data_ycen = (int)rint(data_dy);
247     unsigned int data_xright;
248     unsigned int data_xleft;
249     unsigned int data_ytop;
250     unsigned int data_ybot;
251 
252     if (data_xcen < 0)
253 	data_xcen = 0;
254     else if (data_xcen >= spec->x)
255 	data_xcen = spec->x - 1;
256 
257     if (data_ycen < 0)
258 	data_ycen = 0;
259     else if (data_ycen >= spec->y)
260 	data_ycen = spec->y - 1;
261 
262     data_xright = data_xcen + 1;
263     data_xleft = data_xcen - 1;
264 
265     data_ytop = data_ycen - 1;
266     data_ybot = data_ycen + 1;
267 
268     /* if (data_dx > spec->x - 1 || data_dx < 1 || data_dy > spec->y - 1 || data_dy < 1) { */
269     if (data_xcen >= spec->x - 1 || data_xcen < 1 || data_ycen >= spec->y - 1 || data_ycen < 1) {
270 	/* border */
271 	if (data_xcen >= spec->x)
272 	    data_xcen = spec->x - 1;
273 	if (data_ycen >= spec->y)
274 	    data_ycen = spec->y - 1;
275 	return getData(value, data, flags, spec, data_xcen, data_ycen);
276     }
277 
278 #if 0
279     fprintf(stderr, "x = % 2d, y=% 2d, data_dx, data_dy = %g, %g --> ", x, y, data_dx, data_dy);
280 #endif
281 
282     data_dx -= (float)data_xcen;
283     data_dy -= (float)data_ycen;
284 #if 0
285     fprintf(stderr, "%g, %g\n", data_dx, data_dy);
286 #endif
287 
288     center = !getData(z + 1, data, flags, spec, data_xcen, data_ycen);
289 
290     if (!center) {
291 	/* fprintf(stderr, "(getValueScaled) !center\n"); */
292 	return 1;
293     }
294 
295     if (data_xcen > 0) {
296 	left = !getData(z + 0, data, flags, spec, data_xleft , data_ycen);
297     }
298     if (data_xright < spec->x) {
299 	right = !getData(z + 2, data, flags, spec, data_xright, data_ycen);
300     }
301     if (data_ycen > 0) {
302 	top = !getData(z + 3, data, flags, spec, data_xcen, data_ytop);
303     }
304     if (data_ybot < spec->y) {
305 	bot = !getData(z + 4, data, flags, spec, data_xcen, data_ybot);
306     }
307 
308     if (left && right && top && bot) {
309 	/* all five points are valid */
310 	*value = bicubicspline(z, data_dx, data_dy);
311 	return 0;
312     }
313 
314     if (data_dx > 0) {
315 	if (right) {
316 	    /* interpolation */
317 	    if (data_dy > 0) {
318 		if (bot) {
319 		    *value = triangle4quadrant(z[1], z[4], z[2], data_dx, data_dy);
320 		    return 0;
321 		}
322 	    } else {
323 		if (top) {
324 		    *value = triangle1quadrant(z[1], z[2], z[3], data_dx, data_dy);
325 		    return 0;
326 		}
327 	    }
328 	}
329     } else {
330 	/* data_dx < 0 */
331 	if (left) {
332 	    if (data_dy > 0) {
333 		if (bot) {
334 		    *value = triangle3quadrant(z[1], z[0], z[4], data_dx, data_dy);
335 		    return 0;
336 		}
337 	    } else {
338 		if (top) {
339 		    *value = triangle2quadrant(z[1], z[3], z[0], data_dx, data_dy);
340 		    return 0;
341 		}
342 	    }
343 	}
344     }
345 
346 #if 0
347     fprintf(stderr, "cen = %d %d  data_dx, data_dy = %f %f   left=%d right=%d top=%d bot=%d\n",
348 	    data_xcen, data_ycen, data_dx, data_dy, left, right, top, bot);
349 #endif
350 
351     return 1;
352 }
353 
354 #if 0
355 static const unsigned char
356 getValueScaledUnbinned(float* value, const float* data,
357        	const unsigned char* flags, const zimg_data_t* spec, unsigned int x, unsigned int y)
358 {
359     unsigned int idx = getIndex(spec, x, y);
360     if (flags && flags[idx]) {
361 	/* invalid (masked) */
362 	return 1;
363     } else {
364 	*value = *(data + idx);
365 	return 0;
366     }
367 }
368 
369 static const unsigned char
370 getValueScaledBinned(float* value, const float* data,
371        	const unsigned char* flags, const zimg_data_t* spec, unsigned int x, unsigned int y)
372 {
373     unsigned int idx = getIndex(spec, x, y);
374     if (flags && flags[idx]) {
375 	/* invalid (masked) */
376 	return 1;
377     } else {
378 	*value = *(data + idx);
379 	return 0;
380     }
381 }
382 #endif
383 
384 #if 0
385 
386 		    dval = 0.0;
387 
388 		    for (ib = 0; ib < z.data.yibin; ib++)
389 			for (jb = 0; jb < z.data.xibin; jb++)
390 			    dval += data[((i + ib) * z.data.x) + j + jb];
391 
392 		    dval *= rez_bin;
393 
394 		    /*
395 		    * rely here on a correct
396 		    * scaling, so I can remove
397 		    * this check to speed up.
398 		    *
399 		    if (value >= entries) {
400 		    value = entries - 1;
401 		    } else if (value < 0) {
402 		    value = 0;
403 		    }
404 		    */
405 
406 		    xend = ximg + z.data.xdscale;
407 		    yend = yimg + z.data.ydscale;
408 
409 		    for (y = yimg; y < yend; y++) {
410 			for (x = ximg; x < xend; x++) {
411 			    gdImageSetPixel(im, x, y, value);
412 			}
413 		    }
414 #endif
415 
416 static void
GrowStrInit(grow_str * s)417 GrowStrInit(grow_str* s)
418 {
419     s->str = (char*)0;
420     s->len = 0;
421 }
422 
423 static void
GrowStrFree(grow_str * s)424 GrowStrFree(grow_str* s)
425 {
426     if (s->str)
427 	free(s->str);
428     GrowStrInit(s);
429 }
430 
431 static void
GrowStrAppendN(grow_str * s,char * rhs,int n)432 GrowStrAppendN(grow_str* s, char* rhs, int n)
433 {
434     if (n > 0) {
435 	int new_len = s->len + n + 1 /* terminating 0 */;
436 	s->str = (char*)realloc((void*)s->str, new_len);
437 	assert(s->str);
438 	if (s->len) {
439 	    /* append */
440 	    strncat(s->str, rhs, n);
441 	} else {
442 	    /* first string */
443 	    strncpy(s->str, rhs, n);
444 	}
445 	s->len = new_len - 1; /* string length w/o terminating 0 */
446 	s->str[s->len] = '\0'; /* terminate */
447     }
448 }
449 
450 static void
GrowStrAppend(grow_str * s,char * rhs)451 GrowStrAppend(grow_str* s, char* rhs)
452 {
453     GrowStrAppendN(s, rhs, strlen(rhs));
454 }
455 
456 static void
MultilineInit(zimg_multiline_t * spec)457 MultilineInit(zimg_multiline_t* spec)
458 {
459     spec->x = 0;
460     spec->y = 0;
461     spec->width = 0;
462     spec->height = 0;
463     GrowStrInit(&spec->str);
464 }
465 
466 static void
MultilineFree(zimg_multiline_t * spec)467 MultilineFree(zimg_multiline_t* spec)
468 {
469     GrowStrFree(&spec->str);
470     MultilineInit(spec);
471 }
472 
473 static void
MultilinePutTextVertical(gdImagePtr im,gdFontPtr font,const zimg_multiline_t * spec,int color)474 MultilinePutTextVertical(gdImagePtr im, gdFontPtr font,
475        	const zimg_multiline_t* spec, int color)
476 {
477     int i;
478     int x = spec->x;
479     int y = spec->y;
480     for (i = 0; i < spec->height; i++, x += font->h) {
481 	gdImageStringUp(im, font, x, y, spec->lines[i], color);
482     }
483 }
484 
485 static void
MultilinePutText(gdImagePtr im,gdFontPtr font,const zimg_multiline_t * spec,int color)486 MultilinePutText(gdImagePtr im, gdFontPtr font,
487        	const zimg_multiline_t* spec, int color)
488 {
489     int i;
490     int x = spec->x;
491     int y = spec->y;
492     for (i = 0; i < spec->height; i++, y += font->h) {
493 	gdImageString(im, font, x, y, spec->lines[i], color);
494     }
495 }
496 
497 static int
parse_lines(char * text,char ** args,int maxargs)498 parse_lines(char* text, char** args, int maxargs)
499 {
500     char* ptr;
501     int i = 0;
502 
503     if (!text || !(*text))
504 	return 0;
505 
506     args[i++] = text; /* first line */
507     for (ptr = text; ptr && *ptr && i < maxargs; /* empty */) {
508 	switch (*ptr) {
509 	    case '\\':
510 		if ('n' == *(ptr+1)) {
511 		    *ptr = '\0'; /* terminate */
512 		    ptr += 2; /* skip backslash and n */
513 		}
514 		break;
515 	    case '\n':
516 	    case '\r':
517 		*ptr = '\0'; /* terminate */
518 		ptr++; /* skip */
519 		break;
520 	    default:
521 		ptr++;
522 		continue;
523 	}
524 	if (*ptr)
525 	    args[i++] = ptr;
526     }
527     return i;
528 }
529 
530 /* NOTE: dest must have been initialized by GrowStrInit */
531 static void
parseescapes(grow_str * dest,char * source,const zimg_escape_info_t * escape_info)532 parseescapes(grow_str* dest, char* source, const zimg_escape_info_t* escape_info)
533 {
534     char* ptr;
535     char tmp[BUFSIZ];
536 
537     /* replace escape sequences */
538     while (source && *source && (ptr = strchr(source, '%'))) {
539 
540 	GrowStrAppendN(dest, source, ptr - source);
541 	ptr++;
542 	switch (*ptr) {
543 	    case 'c':
544 		/* not really %c, but a RFC822-conformant date */
545 		GrowStrAppend(dest, ztime());
546 		break;
547 	    case 'f':
548 		GrowStrAppend(dest, escape_info->filename);
549 		break;
550 	    case 'm':
551 		/* min */
552 		{
553 		    sprintf(tmp, "%f", escape_info->min);
554 		    GrowStrAppend(dest, tmp);
555 		}
556 		break;
557 	    case 'M':
558 		/* max */
559 		{
560 		    sprintf(tmp, "%f", escape_info->max);
561 		    GrowStrAppend(dest, tmp);
562 		}
563 		break;
564 	    case 'i':
565 		/* max */
566 		{
567 		    sprintf(tmp, "%f", escape_info->integral);
568 		    GrowStrAppend(dest, tmp);
569 		}
570 		break;
571 #ifdef HAVE_POPEN
572 	    case '{':
573 		/* insert output of shell command */
574 		{
575 		    char* close = strchr(ptr + 1, '}');
576 		    if (close) {
577 			char* cmd;
578 			int len = close - ptr - 1;
579 			FILE* fp;
580 			cmd = (char*)malloc(len + 1);
581 			assert(cmd);
582 			strncpy(cmd, ptr + 1, len);
583 			cmd[len] = '\0'; /* terminate */
584 			ptr = close; /* advance ptr */
585 			fp = popen(cmd, "r");
586 			if (fp) {
587 			    char buf[BUFSIZ];
588 			    int items;
589 			    while ((items = fread(buf, 1, sizeof (buf), fp))) {
590 				GrowStrAppendN(dest, buf, items);
591 			    }
592 			}
593 			pclose(fp);
594 			free(cmd);
595 		    }
596 		}
597 		break;
598 #endif
599 	    default:
600 		/* unknown escape sequence */
601 		GrowStrAppendN(dest, ptr, 1);
602 		break;
603 	}
604 	source = ptr + 1;
605     }
606 
607     /* append rest of the string which contains no escape sequences */
608     if (source && *source)
609 	GrowStrAppend(dest, source);
610 }
611 
612 static void
MultilineGet(zimg_multiline_t * dest,char * source,const zimg_escape_info_t * escape_info)613 MultilineGet(zimg_multiline_t* dest, char* source, const zimg_escape_info_t* escape_info)
614 {
615     int i;
616 
617     parseescapes(&dest->str, source, escape_info);
618 
619 #if 0
620     fprintf(stderr, "(MultilineGet) dest->str = %s\n", dest->str.str);
621 #endif
622 
623     dest->height = parse_lines(dest->str.str, dest->lines, ZIMG_MULTILINE_MAXLINES);
624 
625     dest->width = 0;
626     for (i = 0; i < dest->height; i++) {
627 	int len = strlen(dest->lines[i]);
628 	if (len > dest->width)
629 	    dest->width = len;
630     }
631 }
632 
633 /* allocates grow_str, which must be freed by the caller */
634 static grow_str*
prepare_for_FT(char * source,const zimg_escape_info_t * escape_info)635 prepare_for_FT(char* source, const zimg_escape_info_t* escape_info)
636 {
637     char* ptr;
638 
639     grow_str* s = malloc(sizeof (grow_str));
640     GrowStrInit(s);
641     assert(s);
642     parseescapes(s, source, escape_info);
643 
644     for (ptr = s->str; ptr && *ptr; ptr++) {
645 
646 	if ('\\' == *ptr && 'n' == *(ptr + 1)) {
647 	    *ptr = '\r';
648 	    ptr++; /* skip over */
649 	    *ptr = '\n';
650 	}
651     }
652 
653     return s;
654 }
655 
getFont(zimg_t * z)656 static gdFontPtr getFont(zimg_t* z)
657 {
658     gdFontPtr font = (gdFontPtr)0;
659 #ifdef HAVE_GD_FREETYPE
660     if (z->fontspec)
661 	return font;
662 #endif
663 
664     /* get font */
665     if (z->legend || z->label || z->colorbox.show) {
666 
667 	if (FONT_UNDEFINED != (int)z->fontsize) {
668 
669 	    switch ((int)z->fontsize) {
670 		case FONT_GIANT:
671 		    font = gdFontGiant;
672 		    break;
673 		case FONT_LARGE:
674 		    font = gdFontLarge;
675 		    break;
676 		case FONT_SMALL:
677 		    font = gdFontSmall;
678 		    break;
679 		case FONT_TINY:
680 		    font = gdFontTiny;
681 		    break;
682 		default:
683 		    /* ignore */
684 		    break;
685 	    }
686 	}
687 
688 	if (!font) {
689 	    /* TODO:
690 	     * selectable font */
691 	    font = gdFontTiny;
692 	    if (z->ximg > 300)
693 		font = gdFontSmall;
694 	    if (z->ximg > 600)
695 		font = gdFontLarge;
696 	    if (z->ximg > 900)
697 		font = gdFontGiant;
698 	}
699     }
700 
701     return font;
702 }
703 
704 int
main(int argc,char * argv[])705 main(int argc, char *argv[])
706 {
707     int i = 0, file;
708     int items = 0;
709 
710     float* data;
711     float data_min;
712     float data_max;
713     float fact;
714     double integral;
715 #ifdef ENABLE_PNG_AS_SOURCE
716     int dumped = 0;
717 #endif
718 
719     register int x, y, xpad = 0, ypad = 0;
720 
721     zimg_t z;
722 
723     gdImagePtr im = (gdImagePtr) 0;
724     int gdcolor[0x100];
725 
726     gdFontPtr font = (gdFontPtr)0;
727     int textcolor = (int)0;
728     int nda_color;
729 
730 #if defined HAVE_DLSYM || defined HAVE_SHL_LOAD
731     /* float (*zimg_expression)(unsigned int, unsigned int, float, const zimg_expression_info_t*) = (zimg_expression_t)0; */
732     zimg_expression_t zimg_expression = (zimg_expression_t)0;
733     zimg_expression_info_t info;
734     char* objectfile = (char*)0;
735 #endif
736 
737     int ximg = 0;
738     int yimg = 0;
739 
740     /* rc file arguments */
741     int Argc = 0;
742 #define RCFILE_MAX_ARGV 0xfff
743     char* Argv[RCFILE_MAX_ARGV];
744 
745     int    ifiles; /* number of input files */
746     char **iname;  /* names of input files */
747 
748     int pixel;
749     int color_index;
750     int entries = ZIMG_MAP_COLORS;
751     int entries_ = entries - 1;
752     char* ptr;
753 
754     char* rcfiles[4];
755     int font_width = 0;
756     int font_height = 0;
757 
758 #define DATA(xx, yy) (*(data + (yy) * z.data.x + (xx)))
759 
760 #define COLOR_TO_PIXEL(v)                             \
761     do {                                              \
762 	color_index = (int)(((v) - data_min) * fact); \
763 	if (color_index < 0)                          \
764 	    color_index = 0;                          \
765 	if (color_index >= entries)                   \
766 	    color_index = entries_;                   \
767 	pixel = gdcolor[color_index];                 \
768     } while (0)
769 
770 
771     /* Argv[0] should contain the command name */
772     Argc = 1; /* the command name is at least present */
773     Argv[0] = argv[0]; /* command name */
774 
775     /* [-- check for rc files --] */
776 
777     /* system wide zimgrc, e.g. /etc/zimgrc or /usr/local/etc/zimgrc */
778     rcfiles[0] = strdup(SYSCONFDIR "/" RCFILE);
779 
780     /* ~/.zimgrc */
781     if ((ptr = getenv((const char *) "HOME"))) {
782 	char* home = (char*)malloc(strlen(ptr) + strlen(RCFILE) + 3);
783 	strcpy(home, ptr);
784 	strcat(home, "/." RCFILE);
785 	rcfiles[1] = strdup(home);
786 	free(home);
787     } else {
788 	rcfiles[1] = "";
789     }
790 
791     /* .zimgrc in the local directory */
792     rcfiles[2] = strdup("." RCFILE);
793 
794     /* zimgrc in the local directory */
795     rcfiles[3] = strdup(RCFILE);
796 
797     for (i = 0; i < 4; i++) {
798 
799 	char rcbuf[0xffff];
800 	char ibuf[BUFSIZ];
801 	FILE* fp;
802 
803 	if (!rcfiles[i] || !(*(rcfiles[i]))) {
804 	    continue;
805 	}
806 
807 	if (!(fp = fopen(rcfiles[i], "r"))) {
808 	    free(rcfiles[i]);
809 	    rcfiles[i] = (char*)0;
810 	    continue;
811     }
812 
813     /* copy options from the rc file to Argv */
814 
815 	rcbuf[0] = '\0';
816 
817 	while (fgets(ibuf, sizeof(ibuf), fp)) {
818 	    char* ptr;
819 	    if ((ptr = strchr(ibuf, '#'))) /* discard comments */
820 		*ptr = '\0';
821 	    strcat(rcbuf, " ");
822 	    strcat(rcbuf, ibuf);
823 	}
824 	/* must do a strdup(rcbuf), because the Argv will
825 	 * be saved and point to rcbuf */
826 	Argc += parse(Argv + Argc, RCFILE_MAX_ARGV - Argc, strdup(rcbuf));
827 	fclose(fp);
828     }
829 
830     if (1 == Argc) {
831        	/* no rcfile found, Argv[0] contains only the program name */
832 	Argc = 0;
833     }
834 
835     /* set z default values */
836     init_z(&z);
837     /* parse arguments of .zimgrc or ~/.zimgrc */
838     readcmdln(Argc, Argv, &z, (int*)0, (char***)0);
839     /* read in commandline arguments */
840     readcmdln(argc, argv, &z, &ifiles, &iname);
841 
842     if (TRUE == verbose) {
843 	if (Argc > 0) {
844 	    fprintf(stderr, "arguments from rcfiles: ");
845 	    for (i = 0; i < 4; i++) {
846 		if (rcfiles[i] && *rcfiles[i]) {
847 		    fprintf(stderr, " [%s]", rcfiles[i]);
848 		}
849 	    }
850 	    fprintf(stderr, "\n");
851 	    for (i = 1 /* omit command name */; i < Argc; i++) {
852 		fprintf(stderr, "  %s\n", Argv[i]);
853 	    }
854 	}
855 	fprintf(stderr, "command line arguments:\n");
856 	for (i = 1 /* omit command name */; i < argc; i++) {
857 	    fprintf(stderr, "  %s\n", argv[i]);
858 	}
859     }
860 
861 #if defined HAVE_DLSYM || defined HAVE_SHL_LOAD
862     {
863 	if (z.expr || z.expr_source) {
864 	    objectfile = dynaload_compile(z.expr, z.expr_source, z.expr_object);
865 	} else if (z.expr_object) {
866 	    objectfile = z.expr_object;
867 	}
868 	if (objectfile) {
869 	    zimg_expression = dynaload_expression_load(objectfile);
870 	}
871     }
872 #endif
873 
874     if (!z.column) {
875         z.column = 1; /* read column 1 by default */
876     }
877 
878     if (z.dump_colormap && !ifiles) {
879 	goto dump_colormap;
880     }
881 
882 #ifdef HAVE_GD_FREETYPE
883     if (z.fontspec && (strchr(z.fontspec, ':') || (!strstr(z.fontspec, ".ttf") && !strstr(z.fontspec, ".TTF")))) {
884 	if (!gdFTUseFontConfig(1)) {
885 	    fprintf(stderr, "zimg ERROR: fontconfig is not available -- use ttf file names for the font spec.\n");
886 	    return 1;
887 	}
888     }
889 
890     if (z.fontspec) {
891 
892 	gdFTStringExtra info;
893 	char* err;
894 	int brect[8];
895 	info.flags = gdFTEX_RETURNFONTPATHNAME;
896 	info.fontpath = (char*)0;
897 	err = gdImageStringFTEx((gdImagePtr)0, brect, 0, z.fontspec, z.fontsize, 0.0, 0, 0, "zimg", &info);
898 	if (err) {
899 	    fprintf(stderr, "%s:%d ERROR: %s\n", __FILE__, __LINE__, err);
900 	    return 1;
901 	}
902 
903 	if (verbose && info.fontpath) {
904 	    fprintf(stderr, "font path: \"%s\"\n", info.fontpath);
905 	}
906 
907 	if (info.fontpath)
908 	    free(info.fontpath);
909     }
910 #endif
911 
912     for (file = 0; file < ifiles || 0 == ifiles;
913 	file++, ifiles = (0 == ifiles ? -1 : ifiles)) {
914 
915 	unsigned char *flags = (unsigned char*)0;
916 	unsigned char cropping = 0;
917 	int colorbox_width = 0;
918 	int colorbox_boxwidth = 0;
919 	int colorbox_ticwidth = 0;
920 	int colorbox_height = 0;
921 	char** colorbox_strings = (char**)0;
922 	zimg_multiline_t cbox_label;
923 	int colorbox_label_height = 0;
924 	zimg_multiline_t legend;
925 	zimg_escape_info_t escape_info;
926 #ifdef HAVE_POPEN
927 	int (*ofp_close)(FILE*) = fclose;
928 #endif
929 
930 	if (0 != file) {
931 	    /* free allocated space */
932 	    free_z(&z);
933 	    /* set z default values */
934 	    init_z(&z);
935 	    /* parse arguments of .zimgrc or ~/.zimgrc */
936 	    readcmdln(Argc, Argv, &z, (int*)0, (char***)0);
937 	    /* read in commandline arguments */
938 	    readcmdln(argc, argv, &z, (int*)0, (char***)0);
939 	}
940 
941 	if (TRUE == verbose && ifiles) {
942 	    fprintf (stderr, "zimg: %s\n", iname[file]);
943 	}
944 
945 #ifdef HAVE_REGEX_H
946 	if (ifiles) {
947 	    for (i = 0; i < z.noptions; i++) {
948 		if (!regexec(&(z.options[i].preg), iname[file],
949 			    0, (regmatch_t*)0, 0)) {
950 		    parse_switches(&z, z.options[i].switches);
951 		}
952 	    }
953 	}
954 #endif
955 
956 	/* open input file */
957 	z.ifp = varopen(ifiles ? iname[file] : (char*)0,
958 	    z.inputformat == ASCII_FORMAT ? "r" : "rb"
959 #ifdef HAVE_POPEN
960 	    , z.filter
961 #endif
962 	    );
963 
964 	/* if no dimensions are given via
965 	 * commandline, read them from file.  */
966 	im = (gdImagePtr)0;
967 	if (z.data.len == 0 && z.inputformat != MATRIX_FORMAT)
968 	    im = fgets_dimension(&z);
969 
970 #ifdef ENABLE_PNG_AS_SOURCE
971 	if (im) {
972 	    /* 'data' source is a png image */
973 	    varclose(z.ifp);
974 	    z.data.x = im->sx;
975 	    z.data.y = im->sy;
976 	    z.data.len = z.data.x * z.data.y;
977 	    data = (float*)0;
978 	    if (z.dump_colormap) {
979 		dumped = 1;
980 		printf("# zimg colormap\n");
981 		if (iname[file]) {
982 		    printf("# %s\n", iname[file]);
983 		}
984 		for (i = 0; i < im->colorsTotal; i++) {
985 		    zimg_dump_color((gdImagePtr)0,
986 			im->red[i], im->green[i], im->blue[i]);
987 		}
988 		gdImageDestroy(im);
989 		/* if dump_colormap was selected, no further
990 		 * processing is done */
991 		continue;
992 	    } else if (z.color) {
993 		/* create a copy with a new colormap */
994 		gdImagePtr newim = gdImageCreate(z.data.x, z.data.y);
995 		entries = getGdColorMap(newim, gdcolor, z.color,
996 		    z.rgbformulae, z.colormapfile, &z.xor_color);
997 		for (y = 0; y < z.data.y; y++) {
998 		    for (x = 0; x < z.data.x; x++) {
999 			gdImageSetPixel(newim, x, y, gdImageGetPixel(im, x, y));
1000 		    }
1001 		}
1002 		gdImageDestroy(im);
1003 		im = newim;
1004 	    }
1005 	    font = getFont(&z);
1006 	    goto image_created;
1007 	} else if (z.dump_colormap) {
1008 	    continue;
1009 	}
1010 #endif
1011 
1012 	if ((0 == z.data.x || 0 == z.data.y) && MATRIX_FORMAT != z.inputformat) {
1013 	    fprintf(stderr, "WARNING: '%s' has zero size\n", (ifiles ? iname[file] : "<stdin>"));
1014 	    continue;
1015 	}
1016 
1017 	/* allocate memory and read data */
1018 	if (z.data.len) {
1019 	    data = (float*)malloc(sizeof (float) * z.data.len);
1020 	} else {
1021 	    assert(MATRIX_FORMAT == z.inputformat);
1022 	    data = (float*)0;
1023 	}
1024 
1025 	items = readfile(&z, &data); /* read input file */
1026 
1027 	varclose(z.ifp);
1028 
1029 
1030 	if (items < z.data.len) {
1031 	    char msg[0xff];
1032 	    sprintf(msg, "bad defined dimensions. read %d items (should be %d)",
1033 		items, z.data.len);
1034 	    Fatal(msg);
1035 	}
1036 
1037 	/* eventually guess nda value */
1038 	if (z.nda_color.set) {
1039 
1040 	    if (!z.nda_specified) {
1041 
1042 		/* guess NDA value from the border:
1043 		 * count the number of occurences of each border value. The NDA
1044 		 * value will be set to the value with the highest count, i.e.
1045 		 * the value which occurs most often at the border */
1046 
1047 		int end;
1048 		int other[2];
1049 
1050 		typedef struct counter_t {
1051 		    struct counter_t* next;
1052 		    double value;
1053 		    int count;
1054 		} counter_t;
1055 
1056 #define INSERT_COUNTER(val) \
1057     do { \
1058 	counter_t* new = (counter_t*)malloc(sizeof (counter_t)); \
1059 	assert(new); \
1060 	new->next = (counter_t*)0; \
1061 	new->value = val; \
1062 	new->count = 1; \
1063 	last->next = new; \
1064 	last = new; \
1065     } while (0)
1066 
1067 		counter_t start;
1068 		counter_t* ptr;
1069 		counter_t* last;
1070 		counter_t* max_counter = (counter_t*)0;
1071 
1072 		start.count = 0;
1073 		start.value = *data;
1074 		start.next = (counter_t*)0;
1075 		last = &start;
1076 
1077 		/* left and right column, omit top and bottom pixels,
1078 		 * as they're counted when evaluating the top and
1079 		 * bottom row (see below) */
1080 		other[0] = 0;
1081 		other[1] = z.data.x - 1;
1082 		for (i = 0; i < 2; i++) {
1083 		    for (y = 1, end = z.data.y - 1; y < end; y++) {
1084 
1085 			double val = DATA(other[i], y);
1086 			unsigned char found = 0;
1087 			for (ptr = &start; ptr; ptr = ptr->next) {
1088 			    if (ptr->value == val) {
1089 				ptr->count++;
1090 				found = 1;
1091 				break;
1092 			    }
1093 			}
1094 			if (!found) {
1095 			    INSERT_COUNTER(val);
1096 			}
1097 		    }
1098 		}
1099 
1100 		/* top and bottom row */
1101 		other[0] = 0;
1102 		other[1] = z.data.y - 1;
1103 		for (i = 0; i < 2; i++) {
1104 		    for (x = 0, end = z.data.x; x < end; x++) {
1105 
1106 			double val = DATA(x, other[i]);
1107 			unsigned char found = 0;
1108 			for (ptr = &start; ptr; ptr = ptr->next) {
1109 			    if (ptr->value == val) {
1110 				ptr->count++;
1111 				found = 1;
1112 				break;
1113 			    }
1114 			}
1115 			if (!found) {
1116 			    INSERT_COUNTER(val);
1117 			}
1118 		    }
1119 		}
1120 
1121 		max_counter = &start;
1122 		for (ptr = &start; ptr; ptr = ptr->next) {
1123 		    if (ptr->count > max_counter->count) {
1124 			max_counter = ptr;
1125 		    }
1126 		}
1127 
1128 		if (z.nda_thres < 0 ||
1129 			max_counter->count > (z.nda_thres * (double)((2 * z.data.x) + (2 * (z.data.y - 1))))) {
1130 		    z.nda = max_counter->value;
1131 		} else {
1132 		    z.nda_color.set = 0; /* disable nda */
1133 		}
1134 
1135 		/* free counters */
1136 		for (ptr = start.next; ptr; /* empty */) {
1137 		    counter_t* next = ptr->next;
1138 		    free(ptr);
1139 		    ptr = next;
1140 	    }
1141 	}
1142 	} /* if (z.nda_color.set) */
1143 
1144 	if (z.data.xright == 0)
1145 	    z.data.xright = z.data.x;
1146 	if (z.data.ybottom == 0)
1147 	    z.data.ybottom = z.data.y;
1148 
1149 	if (z.data.xleft < 0)
1150 	    z.data.xleft = 0;
1151 	if (z.data.xright > z.data.x)
1152 	    z.data.xright = z.data.x;
1153 	if (z.data.ytop < 0)
1154 	    z.data.ytop = 0;
1155 	if (z.data.ybottom > z.data.y)
1156 	    z.data.ybottom = z.data.y;
1157 
1158 	if (z.data.xleft != 0 || z.data.xright != z.data.x || z.data.ytop != 0
1159 	       	|| z.data.ybottom != z.data.y || z.data.autocrop) {
1160 
1161 	    cropping = 1;
1162 
1163 	    if (z.data.autocrop) {
1164 
1165 		float topleft     = DATA(0, 0);
1166 		float topright    = DATA(z.data.x - 1, 0);
1167 		float bottomleft  = DATA(0, z.data.y - 1);
1168 		float bottomright = DATA(z.data.x - 1, z.data.y - 1);
1169 
1170 		float* ptr;
1171 		float* end = data + z.data.len;
1172 
1173 		/* cropping from top */
1174 		z.data.ytop = 0;
1175 		if (topleft == topright) {
1176 		    float val = topleft;
1177 		    unsigned char found = 0;
1178 		    for (ptr = data, y = 0; y < z.data.y && !found; y++) {
1179 			for (x = 0; x < z.data.x; x++, ptr++) {
1180 			    if (*ptr != val) {
1181 				found = 1;
1182 				break;
1183 			    }
1184 			}
1185 		    }
1186 		    z.data.ytop = y;
1187 		}
1188 
1189 		/* cropping from bottom */
1190 		if (bottomleft == bottomright) {
1191 		    float val = bottomleft;
1192 		    unsigned char found = 0;
1193 		    for (ptr = end - 1, y = z.data.y - 1; y >= 0 && !found; y--) {
1194 			for (x = 0; x < z.data.x; x++, ptr--) {
1195 			    if (*ptr != val) {
1196 				found = 1;
1197 				break;
1198 			    }
1199 			}
1200 		    }
1201 		    z.data.ybottom = y;
1202 		}
1203 
1204 		/* cropping from left */
1205 		if (bottomleft == topleft) {
1206 		    float val = bottomleft;
1207 		    unsigned char found = 0;
1208 		    for (x = 0; x < z.data.x && !found; x++) {
1209 			for (ptr = data + x; ptr < end; ptr += z.data.x) {
1210 			    if (*ptr != val) {
1211 				found = 1;
1212 				break;
1213 			    }
1214 			}
1215 		    }
1216 		    z.data.xleft = x;
1217 		}
1218 
1219 		/* cropping from right */
1220 		if (bottomright == topright) {
1221 		    float val = bottomright;
1222 		    unsigned char found = 0;
1223 		    for (x = z.data.x - 1; x >= 0 && !found; x--) {
1224 			for (ptr = data + x; ptr < end; ptr += z.data.x) {
1225 			    if (*ptr != val) {
1226 				found = 1;
1227 				break;
1228 			    }
1229 			}
1230 		    }
1231 		    z.data.xright = x;
1232 		}
1233 
1234 	    } else {
1235 		if (z.data.xleft >= z.data.xright) {
1236 		    unsigned int tmp = z.data.xleft;
1237 		    z.data.xleft = z.data.xright;
1238 		    z.data.xright = tmp;
1239 		} else if (z.data.ytop >= z.data.ybottom) {
1240 		    unsigned int tmp = z.data.ytop;
1241 		    z.data.ytop = z.data.ybottom;
1242 		    z.data.ybottom = tmp;
1243 		}
1244 	    }
1245 	}
1246 
1247 	z.ximg = (int)floor((double)((z.data.xright - z.data.xleft)) / (double)z.data.xibin);
1248 	z.yimg = (int)floor((double)((z.data.ybottom - z.data.ytop)) / (double)z.data.yibin);
1249 
1250 	/* raw target image dimensions z.ximg, z.yimg are calculated now */
1251 
1252 	/* first pass: integer size operations 'bin' / 'crop' */
1253 	if (z.data.xibin > 1 || z.data.yibin > 1 || cropping) {
1254 
1255 	    unsigned int imagelen = z.ximg * z.yimg;
1256 
1257 	    float* newdata = (float*)malloc(sizeof (float) * imagelen);
1258 	    float* newdata_ptr = newdata;
1259 	    unsigned char* flags_ptr = (unsigned char*)0;
1260 
1261 	    if (!newdata) {
1262 		perror("zimg->newdata");
1263 		exit(2);
1264 	    }
1265 
1266 	    if (z.nda_color.set) {
1267 		flags = (unsigned char*)malloc(sizeof (unsigned char) * imagelen);
1268 		flags_ptr = flags;
1269 		if (!flags) {
1270 		    perror("zimg->flags");
1271 		    exit(2);
1272 		}
1273 	    }
1274 
1275 	    for (y = 0; y < z.yimg; y++) {
1276 		for (x = 0; x < z.ximg; x++) {
1277 		    getValueCroppedBinned(newdata_ptr++, flags_ptr, data, &z.data, x, y, z.nda);
1278 		    if (flags_ptr) {
1279 			flags_ptr++;
1280 		    }
1281 		}
1282 	    }
1283 
1284 	    free(data);
1285 	    data = newdata;
1286 
1287 	    z.data.x = z.ximg;
1288 	    z.data.y = z.yimg;
1289 	    z.data.len = z.data.x * z.data.y;
1290 
1291 	} else if (z.nda_color.set) {
1292 	    /* build nda mask for unbinned & uncropped values */
1293 
1294 	    float* ptr;
1295 	    float* end;
1296 	    unsigned char* flagptr;
1297 	    flags = (unsigned char*)malloc(sizeof (unsigned char) * z.data.len);
1298 
1299 	    if (!flags) {
1300 		perror("zimg->flags");
1301 		exit(2);
1302 	    }
1303 
1304 	    /* create mask */
1305 	    for (ptr = data, end = ptr + z.data.len, flagptr = flags; ptr < end; ptr++, flagptr++) {
1306 		*flagptr = ((*ptr == z.nda) ? 1 : 0);
1307 	    }
1308 	}
1309 
1310 	if (z.data.xdscale != 1) {
1311 	    double dtmp = rint((double)z.ximg * z.data.xdscale);
1312 	    z.data.xdscale = dtmp / z.ximg;
1313 	    z.ximg = dtmp;
1314 	}
1315 
1316 	if (z.data.ydscale != 1) {
1317 	    double dtmp = rint((double)z.yimg * z.data.ydscale);
1318 	    z.data.ydscale = dtmp / z.yimg;
1319 	    z.yimg = dtmp;
1320 	}
1321 
1322 	if (z.align[0]) {
1323 	    int tmp = (int)ceil((double)z.ximg / (double)z.align[0]) * (double)z.align[0];
1324 	    xpad = (tmp - z.ximg);
1325 #if 0
1326 	    z.ximg = tmp;
1327 #endif
1328 	} else {
1329 	    xpad = 0;
1330 	}
1331 
1332 	if (z.align[1]) {
1333 	    int tmp = (int)ceil((double)z.yimg / (double)z.align[1]) * (double)z.align[1];
1334 	    ypad = (tmp - z.yimg);
1335 #if 0
1336 	    z.yimg = tmp;
1337 #endif
1338 	} else {
1339 	    ypad = 0;
1340 	}
1341 
1342 	font = getFont(&z);
1343 
1344 #ifdef HAVE_GD_FREETYPE
1345 	if (font) {
1346 #endif
1347 	    font_height = font->h;
1348 	    font_width = font->w;
1349 #ifdef HAVE_GD_FREETYPE
1350 	} else if (z.fontspec) {
1351 	    font_height = (int)z.fontsize;
1352 	    font_width = (int)z.fontsize; /* TODO: get freetype font width */
1353 	}
1354 #endif
1355 
1356 	ximg = z.ximg + xpad;
1357 	yimg = z.yimg + ypad;
1358 
1359 	for (i = 0; i < z.transform_valid; i++) {
1360 	    switch (z.transform[i].type) {
1361 		case differentiate_TT:
1362 		    if (debug) {
1363 			fprintf(stderr, "differentiating ...");
1364 			fflush(stderr);
1365 		    }
1366 		    differentiate(data, z.data.x, z.data.y);
1367 		    break;
1368 		case curvature_TT:
1369 		    if (debug) {
1370 			fprintf(stderr, "curvature ...");
1371 			fflush(stderr);
1372 		    }
1373 		    curvature(data, z.data.x, z.data.y);
1374 		    break;
1375 		case smooth_TT:
1376 		    if (debug) {
1377 			fprintf(stderr, "smoothing ...");
1378 			fflush(stderr);
1379 		    }
1380 		    smooth(data, z.data.x, z.data.y, z.transform[i].p1 /* sigma */);
1381 		    break;
1382 		case logarithmic_TT:
1383 		    {
1384 			float* ptr;
1385 			float* end;
1386 
1387 			if (debug) {
1388 			    fprintf(stderr, "taking the logarithm ...");
1389 			    fflush(stderr);
1390 			}
1391 
1392 			integral = minmax(data, flags, z.data.len, &data_min, &data_max); /* get min/max */
1393 
1394 			/* scale to 0 -- z.logarithmic */
1395 			fact = (float)z.transform[i].p1 / (data_max - data_min);
1396 
1397 			for (ptr = data, end = data + z.data.len; ptr < end; ptr++) {
1398 			    *ptr = log1p((double)((*ptr - data_min) * fact));
1399 			}
1400 
1401 			data_max = log1p((double) z.transform[i].p1);
1402 			data_min = 0.0; /* == log1p (0.0) */
1403 			if (TRUE == verbose) {
1404 			    fprintf(stderr, "zimg: data_min=%f\n", data_min);
1405 			    fprintf(stderr, "zimg: data_max=%f\n", data_max);
1406 			}
1407 		    }
1408 		    break;
1409 		case fabs_TT:
1410 		    {
1411 			float* ptr;
1412 			float* end;
1413 
1414 			if (debug) {
1415 			    fprintf(stderr, "taking the absolute value (fabs) ...");
1416 			    fflush(stderr);
1417 			}
1418 
1419 			for (ptr = data, end = data + z.data.len; ptr < end; ptr++) {
1420 			    *ptr = fabs(*ptr);
1421 			}
1422 		    }
1423 		    break;
1424 		case absolute_TT:
1425 		case absolute_min_TT:
1426 		case absolute_max_TT:
1427 		    {
1428 			if (debug) {
1429 			    fprintf(stderr, "absolute scaling ...");
1430 			    fflush(stderr);
1431 			}
1432 			integral = minmax(data, flags, z.data.len, &data_min, &data_max); /* get min/max */
1433 			/* Overwrite 'data_min' and 'data_max'
1434 			 * data values over  'data_max' are set to 'data_max' and
1435 			 * data values below 'data_min' are set to 'data_min'.*/
1436 			if (absolute_max_TT != z.transform[i].type)
1437 			    data_min = (float)z.transform[i].p1; /* abs min */
1438 			if (absolute_min_TT != z.transform[i].type)
1439 			    data_max = (float)z.transform[i].p2; /* abs max */
1440 			torange(data, (int)(z.data.len), data_min, data_max);
1441 		    }
1442 		    break;
1443 		case relative_TT:
1444 		case relative_min_TT:
1445 		case relative_max_TT:
1446 		    {
1447 			if (debug) {
1448 			    fprintf(stderr, "relative scaling ...");
1449 			    fflush(stderr);
1450 			}
1451 			integral = minmax(data, flags, z.data.len, &data_min, &data_max); /* get min/max */
1452 			/* Overwrite 'data_min' and 'data_max'
1453 			 * data values over  'data_max' are set to 'data_max' and
1454 			 * data values below 'data_min' are set to 'data_min'.*/
1455 			/* TODO: check this */
1456 			if (relative_max_TT != z.transform[i].type)
1457 			    data_min = (data_min * (1. - z.transform[i].p1)) + (data_max * z.transform[i].p1);
1458 			if (relative_min_TT != z.transform[i].type)
1459 			    data_max = (data_min * (1. - z.transform[i].p2)) + (data_max * z.transform[i].p2);
1460 			torange(data, (int)(z.data.len), data_min, data_max);
1461 		    }
1462 		    break;
1463 		default:
1464 		    fprintf(stderr, "%s:%d ERROR\n", __FILE__, __LINE__);
1465 		    exit(1);
1466 		    break;
1467 	    }
1468 	    if (debug) {
1469 		fprintf(stderr, "done.\n");
1470 	    }
1471 	}
1472 
1473 #if defined (HAVE_DLSYM) || (defined HAVE_SHL_LOAD)
1474 	if (zimg_expression) {
1475 	    float* data_ptr = data;
1476 	    float rez_cen_x = 1.0 / ((float)(z.data.x - 1) * 0.5);
1477 	    float rez_cen_y = 1.0 / ((float)(z.data.y - 1) * 0.5);
1478 	    info.width = z.data.x;
1479 	    info.height = z.data.y;
1480 	    for (y = 0; y < z.data.y; y++) {
1481 		for (x = 0; x < z.data.x; x++, data_ptr++) {
1482 		    *data_ptr = zimg_expression(x, y,
1483 			    (((float)x) * rez_cen_x) - 1.0,
1484 			    (((float)y) * rez_cen_y) - 1.0,
1485 			    *data_ptr, &info);
1486 		}
1487 	    }
1488 	}
1489 #endif
1490 	/* min and max from the image: */
1491 	integral = minmax(data, flags, z.data.len, &data_min, &data_max);
1492 	/* min and max color range forced by command line */
1493 	if (z.crange.setMin)
1494 	    data_min = z.crange.theMin;
1495 	if (z.crange.setMax)
1496 	    data_max = z.crange.theMax;
1497 
1498 	if (z.statistics)
1499 	    statistics(data, flags, z.data.x, z.data.y, data_min, data_max, z.statistics);
1500 
1501 	/* XXX data processing is finished here XXX */
1502 	escape_info.filename = ifiles ? iname[file] : "<stdin>";
1503 	escape_info.min = data_min;
1504 	escape_info.max = data_max;
1505 	escape_info.integral = integral;
1506 
1507 	if (z.colorbox.show) {
1508 
1509 	    char tmp[0xff];
1510 	    char* default_fmt_pos = "%-g";
1511 	    char* default_fmt = "%- g";
1512 	    char* fmt;
1513 	    int len;
1514 	    double delta;
1515 	    int maxlen = 0;
1516 	    int colorbox_label_width = 0;
1517 
1518 	    colorbox_boxwidth = (z.ximg + xpad) * 0.1;
1519 	    colorbox_height = yimg - font_height;
1520 	    if (colorbox_boxwidth < 10)
1521 		colorbox_boxwidth = 10;
1522 	    colorbox_ticwidth = colorbox_boxwidth * 0.2;
1523 	    if (colorbox_ticwidth < 5)
1524 		colorbox_ticwidth = 5;
1525 
1526 	    if (z.colorbox.label) {
1527 
1528 		/* reserve space for label above colorbox */
1529 		if (font) {
1530 		    MultilineInit(&cbox_label);
1531 		    MultilineGet(&cbox_label, z.colorbox.label, &escape_info);
1532 		    colorbox_label_height = cbox_label.height * font_height;
1533 		    colorbox_label_width = (cbox_label.width + 1) * font_width;
1534 #ifdef HAVE_GD_FREETYPE
1535 		} else if (z.fontspec) {
1536 
1537 		    grow_str* tmp = prepare_for_FT(z.colorbox.label, &escape_info);
1538 		    int brect[8];
1539 		    char* err = gdImageStringFT((gdImagePtr)0, brect, 0, z.fontspec, z.fontsize, 0., 0, 0, tmp->str);
1540 		    if (err) {
1541 			fprintf(stderr, "%s:%d ERROR: %s\n", __FILE__, __LINE__, err);
1542 			return 1;
1543 		    } else {
1544 			colorbox_label_height = brect[3] - brect[7];
1545 			colorbox_label_width = brect[2] - brect[6] + 1 * font_width /* one additional characters */;
1546 		    }
1547 		    GrowStrFree(tmp);
1548 		} else {
1549 		    fprintf(stderr, "%s:%d ERROR: no font for colorbox label (internal error?)\n", __FILE__, __LINE__);
1550 		    return 1;
1551 #endif
1552 		}
1553 
1554 		colorbox_height -= colorbox_label_height;
1555 	    }
1556 
1557 	    if (z.colorbox.levels < 0) {
1558 
1559 		/* calculate a reasonable number of levels automatically */
1560 		z.colorbox.levels = colorbox_height / (2 * font_height);
1561 		if (z.colorbox.levels < 2)
1562 		    z.colorbox.levels = 2;
1563 	    }
1564 
1565 	    delta = (data_max - data_min) / (float)(z.colorbox.levels - 1);
1566 
1567 	    colorbox_strings = (char**)malloc(sizeof (char*) * z.colorbox.levels);
1568 	    assert(colorbox_strings);
1569 
1570 	    if (z.colorbox.fmt)
1571 		fmt = z.colorbox.fmt;
1572 	    else if (data_min > 0)
1573 		fmt = default_fmt_pos;
1574 	    else
1575 		fmt = default_fmt;
1576 
1577 	    for (i = 0; i < z.colorbox.levels; i++) {
1578 		sprintf(tmp, fmt, data_min + ((float)i) * delta);
1579 		colorbox_strings[i] = strdup(tmp);
1580 		assert(colorbox_strings[i]);
1581 		if (font) {
1582 		    if ((len = (strlen(tmp) + 2) * font_width) > maxlen)
1583 			maxlen = len; /* length in pixels */
1584 #ifdef HAVE_GD_FREETYPE
1585 		} else if (z.fontspec) {
1586 		    int brect[8];
1587 		    char* err = gdImageStringFT((gdImagePtr)0, brect, 0, z.fontspec, z.fontsize, 0., 0, 0, tmp);
1588 		    if (err) {
1589 			fprintf(stderr, "%s:%d ERROR: %s\n", __FILE__, __LINE__, err);
1590 			return 1;
1591 		    } else {
1592 			len = brect[2] - brect[6] + 2 * font_width;
1593 			if (len > maxlen)
1594 			    maxlen = len; /* length in pixels */
1595 		    }
1596 		} else {
1597 		    fprintf(stderr, "%s:%d ERROR: no font for colorbox level labels\n", __FILE__, __LINE__);
1598 		    return 1;
1599 #endif
1600 		}
1601 	    }
1602 
1603 	    colorbox_width = colorbox_boxwidth + colorbox_ticwidth + maxlen;
1604 
1605 	    if (z.colorbox.label) {
1606 		if (colorbox_label_width > colorbox_width)
1607 		    colorbox_width = colorbox_label_width;
1608 	    }
1609 
1610 	    ximg += colorbox_width;
1611 	}
1612 
1613 	/* maximal width of legend */
1614 	if (z.legend) {
1615 
1616 	    float ratio_legend_right;
1617 	    float ratio_legend_bottom;
1618 	    int legend_width = 0;
1619 	    int legend_height = 0;
1620 
1621 	    if (font) {
1622 
1623 		MultilineInit(&legend);
1624 		MultilineGet(&legend, z.legend, &escape_info);
1625 
1626 		legend_width = font_width * (0.4 + legend.width);
1627 		legend_height = (legend.height) * font_height;
1628 
1629 #ifdef HAVE_GD_FREETYPE
1630 	    } else if (z.fontspec) {
1631 
1632 		int brect[8];
1633 		grow_str* tmp = prepare_for_FT(z.legend, &escape_info);
1634 		char* err = gdImageStringFT((gdImagePtr)0, brect, 0, z.fontspec, z.fontsize, 0., 0, 0, tmp->str);
1635 		if (err) {
1636 		    fprintf(stderr, "%s:%d ERROR: %s\n", __FILE__, __LINE__, err);
1637 		    return 1;
1638 		} else {
1639 		    legend_width = brect[2] - brect[6] + 0.4 * font_width;
1640 		    legend_height = brect[3] - brect[7];
1641 		}
1642 		GrowStrFree(tmp);
1643 	    } else {
1644 		fprintf(stderr, "%s:%d ERROR: no font for legend available (internal error?)\n", __FILE__, __LINE__);
1645 		return 1;
1646 #endif
1647 	    }
1648 
1649 	    ratio_legend_right  = (float)(ximg + legend_width) / (float)(yimg < legend_height ? legend_height : yimg);
1650 	    ratio_legend_bottom = (float)(yimg + legend_height) / (float)(ximg < legend_width ? legend_width : ximg);
1651 
1652 	    if (ratio_legend_right < ratio_legend_bottom) {
1653 		/* place the legend at the right */
1654 		if (yimg < legend_height)
1655 		    yimg = legend_height;
1656 		legend.x = ximg + 0.2 * font_width;
1657 		legend.y = 0;
1658 		ximg += legend_width;
1659 	    } else {
1660 		/* place the legend below the image */
1661 		if (ximg < legend_width)
1662 		    ximg = legend_width;
1663 		legend.x = 0.2 * font_width;
1664 		legend.y = yimg;
1665 		yimg += legend_height;
1666 	    }
1667 	}
1668 
1669 	/* allocate gd colors. */
1670 	im = gdImageCreate(ximg, yimg);
1671 
1672 	entries = getGdColorMap(im, gdcolor, z.color,
1673 	    z.rgbformulae, z.colormapfile, &z.xor_color);
1674 	entries_ = entries - 1;
1675 
1676 	fact = (float) (entries) / (data_max - data_min);
1677 
1678 	if (debug == TRUE)
1679 	    fprintf (stderr, "\n fact = %f == %d / (%f - %f)\n", fact, entries, data_max, data_min);
1680 
1681 
1682 	/* eventually allocate nda color */
1683 	if (z.nda_color.set) {
1684 	    nda_color = zimg_gdImageLineColorAllocate(im,
1685 		    z.nda_color.red, z.nda_color.green, z.nda_color.blue);
1686 	} else {
1687 	    /* nda_color is also used, if align is non-zero
1688 	     * and z.bordercolor.set is NOT set.
1689 	     */
1690 	    nda_color = zimg_gdImageLineColorAllocate(im, 0xff, 0xff, 0xff); /* white */
1691 	}
1692 
1693 	/* white color for colorbox / legend background */
1694 	if (z.legend || z.colorbox.show) {
1695 	    int white = zimg_gdImageLineColorAllocate(im, 0xff, 0xff, 0xff);
1696 	    if (z.legend)
1697 		gdImageFilledRectangle(im, 0, 0, ximg - 1, yimg - 1, white);
1698 	    else /* z.colorbox.show */
1699 		gdImageFilledRectangle(im, ximg - colorbox_width, 0, ximg - 1, yimg - 1, white);
1700 	}
1701 
1702 	/* drawing the colorbox */
1703 	if (z.colorbox.show) {
1704 
1705 	    /* draw the colored box itself */
1706 	    float value;
1707 	    int ivalue;
1708 	    int pixel;
1709 	    int sep = colorbox_boxwidth * 0.2;
1710 	    unsigned int x_beg = z.ximg + sep;
1711 	    unsigned int x_end = x_beg + colorbox_boxwidth - sep;
1712 	    unsigned int x_text;
1713 	    float delta = (float)(entries) / (float)(colorbox_height);
1714 	    int black = zimg_gdImageLineColorAllocate(im, 0, 0, 0);
1715 	    int font_h2 = (int)ceil((float)font_height * 0.5);
1716 	    int ystart = font_h2 + colorbox_height;
1717 	    int brect[8];
1718 
1719 	    if (z.colorbox.label)
1720 		ystart += colorbox_label_height;
1721 
1722 	    for (i = 0, y = ystart, value = 0;
1723 		    i < colorbox_height; i++, y--, value += delta) {
1724 		ivalue = (int)floor(value);
1725 		pixel = gdcolor[ivalue];
1726 		gdImageLine(im, x_beg, y, x_end, y, pixel);
1727 		/* fprintf(stderr, "(main) y = %d\n", y); */
1728 	    }
1729 
1730 	    /* placing tics and strings */
1731 	    delta = (float)(colorbox_height - 1) / (float)(z.colorbox.levels - 1);
1732 	    sep = 2;
1733 	    x_beg = x_end + sep;
1734 	    x_end = x_beg + colorbox_ticwidth - sep;
1735 	    x_text = x_end + font_width * 1.5; /* we've allocated (maxlen + 2), see above */
1736 
1737 	    for (i = 0; i < z.colorbox.levels; i++) {
1738 
1739 		y = (int)rint((float)ystart - (float)i * delta);
1740 		gdImageLine(im, x_beg, y, x_end, y, black); /* tic */
1741 
1742 		if (font) {
1743 
1744 		    gdImageString(im, font, x_text, y - font_h2, colorbox_strings[i], black);
1745 
1746 		} else if (z.fontspec) {
1747 
1748 		    char* err = gdImageStringFT(im, brect, black, z.fontspec, z.fontsize,
1749 			    0. /* angle */, x_text, y - font_h2 + font_height, colorbox_strings[i]);
1750 
1751 		    if (err)
1752 			fprintf(stderr, "%s:%d WARNING: %s\n", __FILE__, __LINE__, err);
1753 
1754 		} else {
1755 		    fprintf(stderr, "%s:%d WARNING: no font for colorbox labels available (internal error?)\n", __FILE__, __LINE__);
1756 		}
1757 		/* fprintf(stderr, "(main) y = %d\n", y); */
1758 	    }
1759 
1760 	    /* freeing the colorbox strings */
1761 	    for (i = 0; i < z.colorbox.levels; i++) {
1762 		free(colorbox_strings[i]);
1763 	    }
1764 	    free(colorbox_strings);
1765 
1766 	    /* place label? */
1767 	    if (z.colorbox.label) {
1768 
1769 		if (font) {
1770 
1771 		    cbox_label.x = z.ximg + 0.5 * font_width;
1772 		    cbox_label.y = 0;
1773 		    MultilinePutText(im, font, &cbox_label, black);
1774 		    MultilineFree(&cbox_label);
1775 
1776 		} else if (z.fontspec) {
1777 
1778 		    grow_str* tmp = prepare_for_FT(z.colorbox.label, &escape_info);
1779 		    char* err = gdImageStringFT(im, brect, black, z.fontspec, z.fontsize,
1780 			    0. /* angle */, z.ximg + 0.2 * font_width, font_height, tmp->str);
1781 
1782 		    if (err)
1783 			fprintf(stderr, "%s:%d WARNING: %s\n", __FILE__, __LINE__, err);
1784 
1785 		    GrowStrFree(tmp);
1786 
1787 		} else {
1788 		    fprintf(stderr, "%s:%d WARNING: no font for colorbox labels available (internal error?)\n", __FILE__, __LINE__);
1789 		}
1790 	    }
1791 	}
1792 
1793 	if (z.align[0] || z.align[1]) {
1794 
1795 	    int bordercolor = 0;
1796 
1797 	    if (!z.bordercolor.set) {
1798 
1799 		/* determine the border color */
1800 
1801 		int idx;
1802 		int max_frequency = 0;
1803 		int x_ = z.data.x - 1;
1804 		unsigned int frequency[0x100];
1805 		int value;
1806 
1807 		for (idx = 0; idx < 0x100; idx++)
1808 		    frequency[idx] = 0;
1809 
1810 #define UPDATE_FREQUENCY                                  \
1811 		do { \
1812 		    if (flags && !flags[idx]) { \
1813 			value = (int)((data[idx] - data_min) * fact); \
1814 			if (value >= 0 && value < 0x100)          \
1815 			    frequency[value] ++; \
1816 		    } \
1817 		} while (0)
1818 
1819 		/* top row (w/o left and rightmost pixel) */
1820 		for (idx = 1; idx < x_; idx++) {
1821 		    UPDATE_FREQUENCY;
1822 		}
1823 
1824 		/* left column */
1825 		for (idx = 0; idx < z.data.len; idx += z.data.x) {
1826 		    UPDATE_FREQUENCY;
1827 		}
1828 
1829 		/* right column */
1830 		for (idx = x_; idx < z.data.len; idx += z.data.x) {
1831 		    UPDATE_FREQUENCY;
1832 		}
1833 
1834 		/* bottom row (w/o left and rightmost pixel) */
1835 		for (idx = 1; idx < x_; idx++) {
1836 		    UPDATE_FREQUENCY;
1837 		}
1838 
1839 		for (idx = 0; idx < 0x100; idx++) {
1840 		    if (frequency[idx] > max_frequency) {
1841 			bordercolor = idx;
1842 			max_frequency = frequency[idx];
1843 		    }
1844 		}
1845 
1846 		if (0 == max_frequency) {
1847 		    /* all border pixels were nda */
1848 		    bordercolor = nda_color;
1849 		}
1850 
1851 	    } else {
1852 		bordercolor = zimg_gdImageLineColorAllocate(im,
1853 			z.bordercolor.red, z.bordercolor.green,
1854 		       	z.bordercolor.blue);
1855 	    }
1856 
1857 	    if (debug) {
1858 		fprintf(stderr, "using bordercolor = %d\n", bordercolor);
1859 	    }
1860 #if 0
1861 	    /* fill with the border color */
1862 	    for (yimg = 0; yimg < yend; yimg++) {
1863 		for (ximg = 0; ximg < xend; ximg++) {
1864 		    gdImageSetPixel(im, ximg, yimg, bordercolor);
1865 		}
1866 	    }
1867 #else
1868 	    gdImageFilledRectangle(im, 0, 0, z.ximg + xpad - 1, yimg - 1, bordercolor);
1869 #endif
1870 	}
1871 
1872 
1873 	/* write the gd image region (NOT the border).  */
1874 
1875 	if (!z.contour.background) {
1876 
1877 	    unsigned int ximg, yimg;
1878 
1879 	    if (z.data.xdscale == 1 && z.data.ydscale == 1) {
1880 
1881 		float* data_ptr = data;
1882 		unsigned char* flags_ptr = flags;
1883 
1884 		for (y = 0, yimg = ypad / 2; y < z.yimg; y++, yimg++) {
1885 		    for (x = 0, ximg = xpad / 2; x < z.ximg; x++, ximg++) {
1886 
1887 			if (flags_ptr && *flags_ptr++) {
1888 			    /* not valid (nda / masked) */
1889 			    pixel = nda_color;
1890 			} else {
1891 			    COLOR_TO_PIXEL(*data_ptr);
1892 			}
1893 
1894 			gdImageSetPixel(im, ximg, yimg, pixel);
1895 
1896 			data_ptr++;
1897 		    }
1898 		}
1899 	    } else {
1900 
1901 		/* scaled image (option -S) */
1902 
1903 		float dval;
1904 		for (y = 0, yimg = ypad / 2; y < z.yimg; y++, yimg++) {
1905 		    for (x = 0, ximg = xpad / 2; x < z.ximg; x++, ximg++) {
1906 
1907 			if (getValueScaled(&dval, data, flags, &z.data, x, y)) {
1908 			    /* not valid (nda / masked) */
1909 			    pixel = nda_color;
1910 			} else {
1911 			    COLOR_TO_PIXEL(dval);
1912 			}
1913 
1914 			gdImageSetPixel(im, ximg, yimg, pixel);
1915 		    }
1916 		}
1917 	    }
1918 
1919 #if 0
1920 	    int y_max = z.data.ybottom - z.data.yibin + 1;
1921 	    int x_max = z.data.xright - z.data.xibin + 1;
1922 	    int idx;
1923 	    int entries_ = entries - 1;
1924 
1925 	    for (yimg = ypad, i = z.data.ytop; i < y_max;
1926 		i += z.data.yibin, yimg += z.data.ydscale) {
1927 
1928 		for (ximg = xpad, j = z.data.xleft; j < x_max;
1929 		    j += z.data.xibin, ximg += z.data.xdscale) {
1930 
1931 		    dval = 0.0;
1932 
1933 		    for (ib = 0; ib < z.data.yibin; ib++)
1934 			for (jb = 0; jb < z.data.xibin; jb++)
1935 			    dval += data[((i + ib) * z.data.x) + j + jb];
1936 
1937 		    dval *= rez_bin;
1938 		    if (-1 != nda_color && dval == z.nda) {
1939 			value = nda_color;
1940 		    } else {
1941 			idx = (int)((dval - data_min) * fact);
1942 			if (idx < 0)
1943 			    idx = 0;
1944 			if (idx >= entries_)
1945 			    idx = entries_;
1946 			value = gdcolor[idx];
1947 		    }
1948 
1949 		    /*
1950 		    * rely here on a correct
1951 		    * scaling, so I can remove
1952 		    * this check to speed up.
1953 		    *
1954 		    if (value >= entries) {
1955 		    value = entries - 1;
1956 		    } else if (value < 0) {
1957 		    value = 0;
1958 		    }
1959 		    */
1960 
1961 		    xend = ximg + z.data.xdscale;
1962 		    yend = yimg + z.data.ydscale;
1963 
1964 		    for (y = yimg; y < yend; y++) {
1965 			for (x = ximg; x < xend; x++) {
1966 			    gdImageSetPixel(im, x, y, value);
1967 			}
1968 		    }
1969 		}
1970 	    }
1971 #endif
1972 
1973 	} else {
1974 	    /* fill background */
1975 	    int contourcolor = zimg_gdImageLineColorAllocate(im,
1976 		    z.contour.color.red, z.contour.color.green,
1977 		    z.contour.color.blue);
1978 	    for (y = 0; y < z.yimg; y++) {
1979 		for (x = 0; x < z.ximg; x++) {
1980 		    gdImageSetPixel(im, x, y, contourcolor);
1981 		}
1982 	    }
1983 	}
1984 
1985 	if (z.contour.levels) {
1986 	    /* TODO: check, if this works with alignment/padding */
1987 	    contours(&z, data, im, entries, data_min, data_max);
1988 	}
1989 
1990 	/* data is now written to the image, the plain image is created */
1991 
1992 #ifdef ENABLE_PNG_AS_SOURCE
1993 image_created:
1994 #endif
1995 
1996 	if (z.label || z.line) {
1997 	    /* int xor = z.color & INVERT_MAP ? 0x00 : 0xff; */
1998 	    if (z.textcolor.set) {
1999 		textcolor = zimg_gdImageLineColorAllocate(im, z.textcolor.red,
2000 			z.textcolor.green, z.textcolor.blue);
2001 	    } else {
2002 		/* try to get a text color with high contrast */
2003 		int c1 = gdImageGetPixel(im, 0     , 0);
2004 		int c2 = gdImageGetPixel(im, 0     , z.ximg);
2005 		int c3 = gdImageGetPixel(im, z.yimg, 0);
2006 		int c4 = gdImageGetPixel(im, z.yimg, z.ximg);
2007 		int color
2008 		    = im->red[c1] + im->green[c1] + im->blue[c1]
2009 		    + im->red[c2] + im->green[c2] + im->blue[c2]
2010 		    + im->red[c3] + im->green[c3] + im->blue[c3]
2011 		    + im->red[c4] + im->green[c4] + im->blue[c4];
2012 		if (color < 2 * 256)
2013 		    /* dark background --> use white as textcolor */
2014 		    textcolor = zimg_gdImageLineColorAllocate(im, 0xff, 0xff, 0xff);
2015 		else
2016 		    /* light background --> use black as textcolor */
2017 		    textcolor = zimg_gdImageLineColorAllocate(im, 0, 0, 0);
2018 	    }
2019 	}
2020 
2021 	if (z.legend) {
2022 
2023 	    int black = zimg_gdImageLineColorAllocate(im, 0, 0, 0);
2024 
2025 	    if (font) {
2026 
2027 		MultilinePutText(im, font, &legend, black);
2028 		MultilineFree(&legend);
2029 
2030 	    } else if (z.fontspec) {
2031 
2032 		int brect[8];
2033 		grow_str* tmp = prepare_for_FT(z.legend, &escape_info);
2034 		char* err = gdImageStringFT(im, brect, black, z.fontspec, z.fontsize,
2035 			0. /* angle */, legend.x, legend.y + font_height, tmp->str);
2036 
2037 		if (err)
2038 		    fprintf(stderr, "%s:%d WARNING: %s\n", __FILE__, __LINE__, err);
2039 
2040 		GrowStrFree(tmp);
2041 
2042 	    } else {
2043 		fprintf(stderr, "%s:%d WARNING: no font for legend available (internal error?)\n", __FILE__, __LINE__);
2044 	    }
2045 	}
2046 
2047 	if (z.line) {
2048 
2049 	    zimg_polyline_t* line;
2050 
2051 	    for (line = z.line; line; line = line->next) {
2052 
2053 		zimg_vertex_t* vertex = line->vertex;
2054 		unsigned char first = 1;
2055 		int x1 = 0, y1 = 0, x2, y2;
2056 
2057 		for (vertex = line->vertex; vertex; vertex = vertex->next) {
2058 
2059 		    if (!first && (line->flag & ZIMG_LINE_RELATIVE)) {
2060 			x2 = x1 + vertex->x;
2061 			y2 = y1 + vertex->y;
2062 		    } else {
2063 			if (vertex->x < 0)
2064 			    x2 = z.ximg - vertex->x;
2065 			else
2066 			    x2 = vertex->x;
2067 
2068 			if (vertex->y < 0)
2069 			    y2 = z.yimg - vertex->y;
2070 			else
2071 			    y2 = vertex->y;
2072 		    }
2073 
2074 		    if (!first)
2075 			gdImageLine(im, x1, y1, x2, y2, textcolor);
2076 		    else
2077 			first = 0;
2078 
2079 		    x1 = x2;
2080 		    y1 = y2;
2081 		}
2082 	    }
2083 	}
2084 
2085 	if (z.label) {
2086 
2087 	    if (font) {
2088 		zimg_label_t* current;
2089 		zimg_multiline_t label;
2090 
2091 		MultilineInit(&label);
2092 
2093 		for (current = z.label; current; current = current->next) {
2094 
2095 		    int width;
2096 		    int height;
2097 
2098 		    MultilineGet(&label, current->text, &escape_info);
2099 		    label.x = current->x;
2100 		    label.y = current->y;
2101 
2102 		    /* horizontal text */
2103 		    width = label.width * font_width;
2104 		    height = label.height * font_height;
2105 
2106 		    if (current->flag & ZIMG_VERTICAL) {
2107 			/* swap width and height */
2108 			int tmp = width;
2109 			width = height;
2110 			height = tmp;
2111 		    }
2112 
2113 		    /* if label.x or label.y are positive, do NOT make any
2114 		     * check, if the text will be entirely on the image */
2115 
2116 		    if (label.x < 0) {
2117 			if (width >= z.ximg)
2118 			    label.x = 0; /* width of label is larger than width of image */
2119 			else
2120 			    label.x = z.ximg - width + label.x;
2121 		    }
2122 
2123 		    if (current->flag & ZIMG_VERTICAL) {
2124 			if (label.y < 0) {
2125 			    if (height >= z.yimg)
2126 				label.y = z.yimg - 1; /* height of label is larger than height of image */
2127 			    else
2128 				label.y += z.yimg;
2129 			}
2130 			MultilinePutTextVertical(im, font, &label, textcolor);
2131 		    } else {
2132 			if (label.y < 0) {
2133 			    if (height >= z.yimg)
2134 				label.y = 0; /* height of label is larger than height of image */
2135 			    else
2136 				label.y += z.yimg - height;
2137 			}
2138 			MultilinePutText(im, font, &label, textcolor);
2139 		    }
2140 
2141 		    MultilineFree(&label);
2142 		}
2143 
2144 	    } else if (z.fontspec) {
2145 
2146 		zimg_label_t* current;
2147 
2148 		for (current = z.label; current; current = current->next) {
2149 
2150 		    int width, height;
2151 
2152 		    int x = current->x;
2153 		    int y = current->y;
2154 
2155 		    int brect[8];
2156 		    grow_str* tmp = prepare_for_FT(current->text, &escape_info);
2157 		    char* err = gdImageStringFT((gdImagePtr)0, brect, 0, z.fontspec, z.fontsize,
2158 			    (current->flag & ZIMG_VERTICAL ? 90.0 : 0.0), 0, 0, tmp->str);
2159 
2160 		    if (err) {
2161 			fprintf(stderr, "%s:%d ERROR: %s\n", __FILE__, __LINE__, err);
2162 			continue;
2163 		    }
2164 
2165 		    width = brect[2] - brect[6];
2166 		    height = brect[3] - brect[7];
2167 
2168 		    /* if label.x or label.y are positive, do NOT make any
2169 		     * check, if the text will be entirely on the image */
2170 
2171 		    if (x < 0) {
2172 
2173 			if (width >= z.ximg)
2174 			    x = 0; /* width of label is larger than width of image */
2175 			else
2176 			    x += z.ximg - width;
2177 		    }
2178 
2179 		    if (y < 0) {
2180 			if (height >= z.yimg)
2181 			    y = 0; /* height of label is larger than height of image */
2182 			else
2183 			    y += z.yimg - height;
2184 		    }
2185 
2186 		    err = gdImageStringFT(im, brect, textcolor, z.fontspec, z.fontsize,
2187 			    (current->flag & ZIMG_VERTICAL ? 90.0 : 0.0), x, y + font_height, tmp->str);
2188 
2189 		    if (err)
2190 			fprintf(stderr, "%s:%d ERROR: %s\n", __FILE__, __LINE__, err);
2191 
2192 		    GrowStrFree(tmp);
2193 		}
2194 	    } else {
2195 		fprintf(stderr, "%s:%d ERROR: no font for labels available (internal error?)\n", __FILE__, __LINE__);
2196 		return 1;
2197 	    }
2198 	}
2199 
2200 	if (z.interlace)
2201 	    gdImageInterlace(im, 1);
2202 
2203 	if (ifiles > 1 && strcmp(z.oname,"-")) {
2204 	    /* TODO: this is not tested */
2205 	    /* multiple files are given --> z.oname is a directory */
2206 	    int len = (*z.oname ? strlen(z.oname) : 0) + strlen(iname[file]) + 7;
2207 	    char* oname = (char*) malloc(len);
2208 	    assert(oname);
2209 	    if (*z.oname) {
2210 		strcpy(oname, z.oname);
2211 		strcat(oname, "/");
2212 		strcat(oname, iname[file]);
2213 	    } else {
2214 		strcpy(oname, iname[file]);
2215 	    }
2216 	    /* append ".gif", ".png", ".jpg", ".ppm" or ".pgm" to the filename */
2217 	    switch (z.imformat){
2218 		case IMFORMAT_PPM: strcat(oname, ".ppm"); break;
2219 		case IMFORMAT_PGM: strcat(oname, ".pgm"); break;
2220 		case IMFORMAT_JPG: strcat(oname, ".jpg"); break;
2221 #ifdef GD_HAS_GIF
2222 		case IMFORMAT_GIF: strcat(oname, ".gif"); break;
2223 #endif
2224 #ifdef GD_HAS_PNG
2225 		case IMFORMAT_PNG: strcat(oname, ".png"); break;
2226 #endif
2227 	    }
2228 
2229 	    if (!(z.ofp = fopen(oname, "wb")))
2230 		perrorexit(oname);
2231 	    free(oname);
2232 	} else if (*z.oname && strcmp(z.oname, "-")) {
2233 	    /* one input file or stdin --> only one output file */
2234 	    if (!(z.ofp = fopen(z.oname, "wb")))
2235 		perrorexit(z.oname);
2236 	} else {
2237 	    if (!ttyname(fileno(stdout))) {
2238 		z.ofp = stdout;
2239 #if !defined(unix) && !defined(__unix) && !defined(__unix__)
2240 		setmode(fileno(stdout), O_BINARY);
2241 #endif
2242 #ifdef HAVE_POPEN
2243 	    } else {
2244 		/* output is a terminal. The user probably forgot to
2245 		 * redirect the output to a file or a program --> launch
2246 		 * a viewer, e.g. xv - */
2247 		char* viewer = getenv("ZIMG_VIEWER");
2248 		/* NOTE: "wb" fails on linux (popen() returns (FILE*)0) */
2249 		z.ofp = popen((viewer ? viewer : X_VIEWER), "w");
2250 		if (!z.ofp) {
2251 		    /* fallback to stdout */
2252 		    z.ofp = stdout;
2253 #if !defined(unix) && !defined(__unix) && !defined(__unix__)
2254 		    setmode(fileno(stdout), O_BINARY);
2255 #endif
2256 		} else {
2257 		    ofp_close = pclose;
2258 		}
2259 	    }
2260 #endif
2261 	}
2262 
2263 	/* Write GD */
2264 	switch (z.imformat) {
2265 	    case IMFORMAT_PPM: zimgGdImagePpm(im, z.ofp); break;
2266 	    case IMFORMAT_PGM: zimgGdImagePgm(im, z.ofp); break;
2267 # ifdef GD_JPEG_VERSION
2268 	    case IMFORMAT_JPG: gdImageJpeg(im, z.ofp, z.jpeg_quality);
2269 			     break;
2270 # endif
2271 #ifdef GD_HAS_GIF
2272 	    case IMFORMAT_GIF:
2273 		gdImageGif(im, z.ofp);
2274 		break;
2275 #endif
2276 #ifdef GD_HAS_PNG
2277 	    case IMFORMAT_PNG:
2278 		gdImagePng(im, z.ofp);
2279 		break;
2280 #endif
2281 	}
2282 	gdImageDestroy(im);
2283 
2284 	if (z.ofp != stdout) {
2285 #ifdef HAVE_POPEN
2286 	    ofp_close(z.ofp);
2287 #else
2288 	    fclose(z.ofp);
2289 #endif
2290 	}
2291 
2292 	if (data)
2293 	    free(data);
2294 	data = (float*)0;
2295 	if (flags)
2296 	    free(flags);
2297 	flags = (unsigned char*)0;
2298     } /* loop over input files */
2299 
2300 #if defined(HAVE_DLSYM) || defined(HAVE_SHL_LOAD)
2301     if (zimg_expression) {
2302 	dynaload_expression_unload();
2303     }
2304     if (objectfile) {
2305 	if (!z.expr_object)
2306 	    unlink(objectfile);
2307 	free(objectfile);
2308     }
2309 #endif
2310 
2311 dump_colormap:
2312     if (z.dump_colormap
2313 #ifdef ENABLE_PNG_AS_SOURCE
2314 	&& !dumped
2315 #endif
2316 	) {
2317 	/* getGdColorMap() will print the colormap instead
2318 	 * of allocating the colors if it is called with
2319 	 * (gdImagePtr)0 */
2320 	entries = getGdColorMap((gdImagePtr)0, gdcolor, z.color,
2321 	    z.rgbformulae, z.colormapfile, &z.xor_color);
2322     }
2323 
2324     /* free allocated space */
2325     free_z(&z);
2326 
2327     exit (0);
2328 }
2329