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