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