1 
2 /*
3  * Include ./configure's header file
4  */
5 #ifdef HAVE_CONFIG_H
6 #include <config.h>
7 #endif
8 
9 
10 /* Rasterize current metafile buffer via gd library.  Loosly based
11    on the gxpng utility:
12 
13    gxpng Copyright 1999 Matthias Muennich
14 
15    Has been modified to behave more like the X interface, so the
16    output image will look more like the screen image. */
17 
18 #include "gd.h"
19 
20 int gxhpng (char *, int, int, int, int, char *, char *, int) ;
21 
22 void gdImageFilldPolygon(gdImagePtr im, gdPointPtr p, int n, int c);
23 void gdImageLne(gdImagePtr im, int x1, int y1, int x2, int y2, int color);
24 
25 /* default size of the graph */
26 
27 #define SX 800
28 #define SY 600
29 
30 static char pout[256];   /* Build error msgs here */
31 
32 /* rgb values for 16 defined colors */
33 /* these exactly match what's in gxX.c so that the graphical display
34    and the printim output look the same */
35 static int
36  rr[16]={0,255,250,  0, 30,  0,240,230,240,160,160,  0,230,  0,130,170},
37  gg[16]={0,255, 60,220, 60,200,  0,220,130,  0,230,160,175,210,  0,170},
38  bb[16]={0,255, 60,  0,255,200,130, 50, 40,200, 50,255, 45,140,220,170};
39 
gxhpng(char * fnout,int xin,int yin,int bwin,int gifflg,char * bgImage,char * fgImage,int tcolor)40 int gxhpng (char *fnout, int xin, int yin, int bwin, int gifflg,
41 	    char *bgImage, char *fgImage, int tcolor) {
42 FILE *ofile, *bgfile, *fgfile;
43 gdPoint *xybuf;
44 gdImagePtr im=NULL, imfg, imbg;
45 float xlo,xhi,ylo,yhi;
46 int xpos,ypos,xs,ys,xrs,yrs;
47 int cmd,i,j,cnt,flag,ii,siz,xp,yp,xp2,yp2,thck,h,w,backbw;
48 int ccol,lwide,fflag,xyc,red,grn,blu,xcur,ycur,xsav,ysav,retcod;
49 int cdef[100],cnum[100],rc[100],gc[100],bc[100];
50 short *poi,*pend;
51  int len;
52   if (bwin<-900) backbw = gxdbkq();
53   else backbw = bwin;
54 
55   for (i=0; i<100; i++) {
56     cdef[i]=0;
57     rc[i] = 125; gc[i] = 125; bc[i] = 125;
58   }
59   for (i=0; i<16; i++) {
60     rc[i] = rr[i]; gc[i] = gg[i]; bc[i] = bb[i];
61   }
62 
63   /*  Set up pointers into current meta buffer list */
64 
65   if (dbmode && pntf==0) {
66     lens2[pnt2-1] = hpnt-hbuff;
67     cnt = pnt2; flag = 1;
68   } else {
69     lens[pnt-1] = hpnt-hbuff;
70     cnt = pnt; flag = 0;
71   }
72 
73   /*  Allocate the gd image and set up the scaling for it */
74 
75   if (xin<0 || yin<0) {
76     if (xrsize > yrsize) {xs = SX; ys = SY;}
77     else {xs = SY; ys = SX;}
78   } else {xs = xin; ys = yin;}
79   xrs = (int)(xrsize*1000.0+0.5);
80   yrs = (int)(yrsize*1000.0+0.5);
81 
82   /* handle background PNG picture */
83   if(*bgImage) {
84     /* Make sure bgImage is a .png -- otherwise return error */
85     len = 0;
86     while (*(bgImage+len)) len++;
87     len = len-4;
88     if (len>0) {
89       if (*(bgImage+len+1)!='p' ||
90 	  *(bgImage+len+2)!='n' ||
91 	  *(bgImage+len+3)!='g' ) {
92 	if (*(bgImage+len+1)!='P' ||
93 	    *(bgImage+len+2)!='N' ||
94 	    *(bgImage+len+3)!='G' ) {
95 	  return(5);
96 	}
97       }
98     }
99 
100     if(bgfile=fopen(bgImage,"rb")) {
101       if((im=gdImageCreateFromPng(bgfile)) != NULL) {
102 	if (im->sx < xs || im->sy < ys) {
103 	  gdImageDestroy(im);
104 	  im=NULL;
105 	}
106       } else {
107 	fclose(bgfile);
108 	return(7);
109       }
110       fclose(bgfile);
111     } else {
112       return(3);
113     }
114   }
115 
116   if (!im) {
117     im = gdImageCreate(xs,ys);
118   }
119 
120   /*  Set up background and foreground colors */
121   if (backbw) {
122     cnum[0] = gdImageColorAllocate(im, 255, 255, 255);
123     cnum[1] = gdImageColorAllocate(im, 0, 0, 0);
124   } else {
125     cnum[0] = gdImageColorAllocate(im, 0, 0, 0);
126     cnum[1] = gdImageColorAllocate(im, 255, 255, 255);
127   }
128   cdef[0] = 1; cdef[1] = 1;
129   ccol = 1;
130 
131   /* Loop thru allocated meta buffers and handle the graphics commands found there */
132   fflag = 0;
133   for (ii=0; ii<cnt; ii++) {
134     if (flag) {
135       poi = bufs2[ii];
136       pend = poi + lens2[ii];
137     } else {
138       poi = bufs[ii];
139       pend = poi + lens[ii];
140     }
141 
142     while (poi<pend) {
143       /* Get message type */
144       cmd = *poi;
145 
146       /* Handle various message types */
147       /* -9 is end of file.  Should not happen. */
148       if (cmd==-9) {
149         gaprnt(0,"Logic Error 4 in gxhpng.  Notify Developer\n");
150         return(99);
151       }
152 
153       /*  -1 indicates start of file.  Should not ocurr. */
154       else if (cmd==-1) {
155         gaprnt(0,"Logic Error 8 in gxhpng.  Notify Developer\n");
156         return(99);
157       }
158 
159       /* -2 indicates new frame.  Also should not ocurr */
160       else if (cmd==-2) {
161         gaprnt(0,"Logic Error 12 in gxhpng.  Notify Developer\n");
162         return(99);
163       }
164 
165       /* -3 indicates new color.  One arg; color number.  */
166       /*  Allocate in gd if not done already */
167       else if (cmd==-3) {
168         ccol = *(poi+1);
169         if (ccol<0) ccol=0;
170         if (cdef[ccol]==0) {
171           cnum[ccol] = gdImageColorAllocate(im, rc[ccol], gc[ccol], bc[ccol]);
172           cdef[ccol] = 1;
173         }
174         poi += 2;
175       }
176 
177       /* -4 indicates new line thickness.  It has two arguments */
178       else if (cmd==-4) {
179         thck = *(poi+2);
180         poi += 3;
181       }
182 
183       /*  -5 defines a new color, in rgb.  It has four int args */
184       /*  If this changes the existing definition for this color,
185           then indicate this has not yet been allocated to gd */
186       else if (cmd==-5){
187         i = *(poi+1);
188         red = *(poi+2);
189         grn = *(poi+3);
190         blu = *(poi+4);
191         if (rc[i]!=red || gc[i]!=grn || bc[i]!=blu) {
192           rc[i] = red; gc[i] = grn; bc[i] = blu;
193           cdef[i] = 0;
194         }
195         poi += 5;
196       }
197 
198       /* -6 is for a filled rectangle.  It has four float args. */
199       else if (cmd==-6){
200         xlo = *(poi+1); ylo = *(poi+3);
201         xhi = *(poi+2); yhi = *(poi+4);
202         xp = (xlo*xs)/xrs;
203         yp = ys-(ylo*ys)/yrs;
204         xp2 = (xhi*xs)/xrs;
205         yp2 = ys-(yhi*ys)/yrs;
206         if (xp>xp2) {
207            i = xp;
208            xp = xp2;
209            xp2 = i;
210         }
211         if (yp>yp2) {
212            i = yp;
213            yp = yp2;
214            yp2 = i;
215         }
216         gdImageFilledRectangle(im, xp, yp, xp2, yp2, cnum[ccol]);
217         poi += 5;
218       }
219 
220       /* -7 indicates the start of a polygon fill.  It has one arg. */
221       else if (cmd==-7){
222         siz = *(poi+1);
223         xybuf = (gdPoint *)malloc(sizeof(gdPoint)*(siz+1));
224         if (xybuf==NULL) {
225           gaprnt(0,"Memory allocation error: gxhpng\n");
226           return(99);
227         }
228         fflag = 1;
229         xyc = 0;
230         poi += 2;
231       }
232 
233       /* -8 is to terminate polygon fill.  It has no args */
234       else if (cmd==-8) {
235         if (xybuf->x != (xybuf+xyc-1)->x ||
236             xybuf->y != (xybuf+xyc-1)->y) {
237           (xybuf+xyc)->x = xybuf->x;
238           (xybuf+xyc)->y = xybuf->y;
239           xyc++;
240         }
241         gdImageFilldPolygon(im, xybuf, xyc, cnum[ccol]);
242         free (xybuf);
243         fflag = 0;
244         poi += 1;
245       }
246 
247       /* -10 is a move to instruction.  It has two float args */
248       else if (cmd==-10){
249         xpos = *(poi+1); ypos = *(poi+2);
250         xsav = (xpos*xs)/xrs;
251         ysav = ys-(ypos*ys)/yrs;
252         if (fflag) {
253           (xybuf+xyc)->x = xsav;
254           (xybuf+xyc)->y = ysav;
255           xyc++;
256         }
257         poi += 3;
258       }
259 
260       /*  -11 is draw to.  It has two float args. */
261       else if (cmd==-11){
262         xpos = *(poi+1); ypos = *(poi+2);
263         xcur = (xpos*xs)/xrs;
264         ycur = ys-(ypos*ys)/yrs;
265         if (fflag) {    /* Assume first poly point is moveto */
266           if ((xybuf+xyc-1)->x != xcur || (xybuf+xyc-1)->y != ycur) {
267             (xybuf+xyc)->x = xcur;
268             (xybuf+xyc)->y = ycur;
269             xyc++;
270           }
271         } else {
272           gdImageLne(im, xsav, ysav, xcur, ycur, cnum[ccol]);
273           if (thck>5) {           /* if wide thcknss, draw extra lines */
274             w = xsav - xcur;
275             if (w<0) w = -1*w;
276             h = ysav - ycur;
277             if (h<0) h = -1*h;
278             if (w<h) {
279               gdImageLne(im, xsav-1, ysav, xcur-1, ycur, cnum[ccol]);
280               if (thck>11) gdImageLne(im, xsav+1, ysav, xcur+1, ycur, cnum[ccol]);
281             } else {
282               gdImageLne(im, xsav, ysav-1, xcur, ycur-1, cnum[ccol]);
283               if (thck>11) gdImageLne(im, xsav, ysav+1, xcur, ycur+1, cnum[ccol]);
284             }
285           }
286         }
287         xsav = xcur; ysav = ycur;
288         poi += 3;
289       }
290 
291       /* -12 indicates new fill pattern.  We ignore it here */
292       else if (cmd==-12) {
293         poi += 4;
294       }
295 
296       /* -20 is a draw widget.  We ignore it here. */
297       else if (cmd==-20) {
298         poi += 2;
299       }
300 
301       /* Any other command would be invalid */
302       else {
303         gaprnt(0,"Logic Error 20 in gxhpng.  Notify Developer\n");
304         return(99);
305       }
306     }
307   }
308 
309   /* handle foreground PNG picture */
310   if(*fgImage) {
311     /* Make sure fgImage is a .png -- otherwise return error */
312     len = 0;
313     while (*(fgImage+len)) len++;
314     len = len-4;
315     if (len>0) {
316       if (*(fgImage+len+1)!='p' ||
317 	  *(fgImage+len+2)!='n' ||
318 	  *(fgImage+len+3)!='g' ) {
319 	if (*(fgImage+len+1)!='P' ||
320 	    *(fgImage+len+2)!='N' ||
321 	    *(fgImage+len+3)!='G' ) {
322 	  return(6);
323 	}
324       }
325     }
326 
327     if(fgfile=fopen(fgImage,"rb")) {
328       if((imfg=gdImageCreateFromPng(fgfile)) !=NULL) {
329 	gdImageCopy(im,imfg,0,0,0,0,imfg->sx,imfg->sy);
330       }
331       else {
332 	fclose(fgfile);
333 	return(8);
334       }
335     } else {
336       return(4);
337     }
338     fclose(fgfile);
339     gdImageDestroy(imfg);
340   }
341 
342 
343   retcod = 0;
344   /* optionally convert a color to transparent */
345   if(tcolor != -1 ) {
346     if(cdef[tcolor]){
347       gdImageColorTransparent(im,cnum[tcolor]);
348       sprintf(pout,"Transparent color: #%d\n",tcolor);
349       gaprnt(2,pout);
350     }
351   }
352   if(*bgImage) {
353     if(bgfile=fopen(bgImage,"rb")) {
354       if((imbg=gdImageCreateFromPng(bgfile)) !=NULL) {
355 	gdImageCopy(imbg,im,0,0,0,0,im->sx,im->sy);
356       }
357     }
358     fclose(bgfile);
359     gdImageDestroy(im);
360     im=imbg;
361   }
362 
363   ofile = fopen(fnout, "wb");
364   if (ofile==NULL) {
365     sprintf(pout,"Open error on %s\n",fnout);
366     gaprnt(0,pout);
367     retcod = 1;
368   } else {
369     if (gifflg) gdImageGif(im, ofile);
370 #ifdef GXJPEG
371     else if (gifflg==3) gdImageJpeg(im, ofile,-1);
372 #endif
373     else gdImagePng(im, ofile);
374     fclose(ofile);
375   }
376   gdImageDestroy(im);
377   return (retcod);
378 }
379 
gdCompareInt(const void * a,const void * b)380 int gdCompareInt(const void *a, const void *b)
381 {
382 	return (*(const int *)a) - (*(const int *)b);
383 }
384 
385 
386 /* Version of gdImageFilledPolygon to invoke my local
387    version of gdImageLne.  Nothing else changed... B.Doty 5/31/01 */
388 
389 /* THANKS to Kirsten Schulz for the polygon fixes! */
390 
391 /* The intersection finding technique of this code could be improved  */
392 /* by remembering the previous intertersection, and by using the slope.*/
393 /* That could help to adjust intersections  to produce a nice */
394 /* interior_extrema. */
395 
gdImageFilldPolygon(gdImagePtr im,gdPointPtr p,int n,int c)396 void gdImageFilldPolygon(gdImagePtr im, gdPointPtr p, int n, int c)
397 {
398 	int i;
399 	int y;
400 	int miny, maxy;
401 	int x1, y1;
402 	int x2, y2;
403 	int ind1, ind2;
404 	int ints;
405 	if (!n) {
406 		return;
407 	}
408 	if (!im->polyAllocated) {
409 		im->polyInts = (int *) malloc(sizeof(int) * n);
410 		im->polyAllocated = n;
411 	}
412 	if (im->polyAllocated < n) {
413 		while (im->polyAllocated < n) {
414 			im->polyAllocated *= 2;
415 		}
416 		im->polyInts = (int *) realloc(im->polyInts,
417 			sizeof(int) * im->polyAllocated);
418 	}
419 	miny = p[0].y;
420 	maxy = p[0].y;
421 	for (i=1; (i < n); i++) {
422 		if (p[i].y < miny) {
423 			miny = p[i].y;
424 		}
425 		if (p[i].y > maxy) {
426 			maxy = p[i].y;
427 		}
428 	}
429 	/* Fix in 1.3: count a vertex only once */
430 	for (y=miny; (y <= maxy); y++) {
431 /*1.4		int interLast = 0; */
432 /*		int dirLast = 0; */
433 /*		int interFirst = 1; */
434 		ints = 0;
435 		for (i=0; (i < n); i++) {
436 			if (!i) {
437 				ind1 = n-1;
438 				ind2 = 0;
439 			} else {
440 				ind1 = i-1;
441 				ind2 = i;
442 			}
443 			y1 = p[ind1].y;
444 			y2 = p[ind2].y;
445 			if (y1 < y2) {
446 				x1 = p[ind1].x;
447 				x2 = p[ind2].x;
448 			} else if (y1 > y2) {
449 				y2 = p[ind1].y;
450 				y1 = p[ind2].y;
451 				x2 = p[ind1].x;
452 				x1 = p[ind2].x;
453 			} else {
454 				continue;
455 			}
456 			if ((y >= y1) && (y < y2)) {
457 				im->polyInts[ints++] = (y-y1) * (x2-x1) / (y2-y1) + x1;
458 			} else if ((y == maxy) && (y > y1) && (y <= y2)) {
459 				im->polyInts[ints++] = (y-y1) * (x2-x1) / (y2-y1) + x1;
460 			}
461 		}
462 		qsort(im->polyInts, ints, sizeof(int), gdCompareInt);
463 
464 		for (i=0; (i < (ints)); i+=2) {
465 			gdImageLne(im, im->polyInts[i], y,
466 				im->polyInts[i+1], y, c);
467 		}
468 	}
469 }
470 
471 /* My re-implemented version of gdImageLine.  The version
472    contained in the gd package is better, no doubt about it.
473    But in the gd package, the boundaries of polygons are
474    determined using a different algorithm than the
475    way lines are drawn.  Thus, if one plots a polygon and then
476    plots the edge of the polygon using lines, they don't
477    match ... there is bleeding over from the polygon into
478    areas outside the lines.  This is a problem for grads, where
479    the shaded contour routine uses the same algorithm as the
480    line contour routine, so that line contours can be overlaid
481    precisely at the boundaries between colors on a shaded plot.
482    But then when rendered thru the gd package, they don't line
483    up again.
484 
485    So, this version of gdImageLine uses the same algorithm as
486    gdImageFilledPolygon to determine edges.  Would have been
487    better to fix up gxImageFilledPolygon, but that looked to
488    be a lot more work... B.Doty 05/31/01 */
489 
gdImageLne(gdImagePtr im,int x1,int y1,int x2,int y2,int color)490 void gdImageLne(gdImagePtr im, int x1, int y1, int x2, int y2, int color) {
491 int x,y,xold,xx;
492 
493   /* Fast Path for horizontal or vertical lines */
494 
495   if (x1 == x2) {
496     if (y1<y2) {
497       for (y=y1; y<=y2; y++) {
498          gdImageSetPixel(im, x1, y, color);
499       }
500     } else {
501       for (y=y2; y<=y1; y++) {
502          gdImageSetPixel(im, x1, y, color);
503       }
504     }
505   }
506 
507   else if (y1 == y2) {
508     if (x1<x2) {
509       for (x=x1; x<=x2; x++) {
510          gdImageSetPixel(im, x, y1, color);
511       }
512     } else {
513       for (x=x2; x<=x1; x++) {
514          gdImageSetPixel(im, x, y1, color);
515       }
516     }
517     return;
518   }
519 
520   /* Angled line; this uses lots of integer divides, which
521      the *real* gdImageLine avoids.  */
522 
523   else {
524 
525     if (y1<y2) {
526       xold = x1;
527       for (y=y1; y<=y2; y++) {
528          x = (y-y1) * (x2-x1) / (y2-y1) + x1;
529          gdImageSetPixel(im, x, y, color);
530          if (x-xold>1) {
531            for (xx=xold+1; xx<x; xx++) {
532               gdImageSetPixel(im, xx, y, color);
533            }
534          }
535          if (xold-x>1) {
536            for (xx=x+1; xx<xold; xx++) {
537               gdImageSetPixel(im, xx, y, color);
538            }
539          }
540          xold = x;
541       }
542     } else {
543       xold = x2;
544       for (y=y2; y<=y1; y++) {
545          x = (y-y2) * (x1-x2) / (y1-y2) + x2;
546          gdImageSetPixel(im, x, y, color);
547          if (x-xold>1) {
548            for (xx=xold+1; xx<x; xx++) {
549               gdImageSetPixel(im, xx, y, color);
550            }
551          }
552          if (xold-x>1) {
553            for (xx=x+1; xx<xold; xx++) {
554               gdImageSetPixel(im, xx, y, color);
555            }
556          }
557          xold = x;
558       }
559     }
560   }
561 }
562