1 /* ======================================================= *
2  * Copyright 1998-2005 Stephen C. Grubb                    *
3  * http://ploticus.sourceforge.net                         *
4  * Covered by GPL; see the file ./Copyright for details.   *
5  * ======================================================= */
6 
7 /*
8  ploticus interface to Thomas Boutell's GD library (www.boutell.com)
9 
10  Notes:
11 
12  * For development, "make devgrgd" can be used
13 
14  * This module supports these #defines:
15 	GD13  (create GIF only using GD 1.3; no import)
16 	GD16  (create PNG only using GD 1.6; also can import PNG)
17 	GD18   (use GD 1.8+ to create PNG, JPEG, or WBMP; also can import these formats)
18 	GDFREETYPE (use FreeType font rendering; may be used only when GD18 is in effect)
19 
20  * GD renders text such that the TOP of the character box is at x, y
21 
22 */
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28 #ifdef WIN32
29 #include <fcntl.h>  /* for _O_BINARY */
30 #endif
31 
32 #include "pixpt.h"  /* circle ptlists */
33 
34 extern double PLG_xsca_inv(), PLG_ysca_inv();
35 extern int TDH_err(), GL_member(), GL_goodnum();
36 extern int PLGG_color(), PLG_xsca(), PLG_ysca(), PLGG_linestyle(), PLGG_fill(), PLG_xrgb_to_rgb(), PLG_colorname_to_rgb();
37 extern int PLG_bb(), PL_clickmap_getdemomode(), PL_clickmap_show(), PL_clickmap_inprogress(), PL_clickmap_out();
38 extern int atoi(), chmod();
39 
40 #define Exsca( h )      PLG_xsca( h )
41 #define Eysca( h )      PLG_ysca( h )
42 #define Exsca_inv( h )  PLG_xsca_inv( h )
43 #define Eysca_inv( h )  PLG_ysca_inv( h )
44 #define Eerr(a,b,c)  TDH_err(a,b,c)
45 
46 #define VERT 1.570796  /* 90 degrees, expressed in radians */
47 
48 static char g_fmt[20] = "";
49 
50 /* ================================= */
51 /* SETIMFMT - set the image format that we will be creating..
52    allowable values for fmt are "gif", "png", "jpeg", etc.  */
53 int
PLGG_setimfmt(fmt)54 PLGG_setimfmt( fmt )
55 char *fmt;
56 {
57 strcpy( g_fmt, fmt );
58 return( 0 );
59 }
60 /* ================================= */
61 /* GETIMFMT - allow other modules to find out the image format.
62    Format name is copied into fmt. */
63 int
PLGG_getimfmt(fmt)64 PLGG_getimfmt( fmt )
65 char *fmt;
66 {
67 strcpy( fmt, g_fmt );
68 return( 0 );
69 }
70 
71 
72 /* =============================================================================== */
73 #ifndef NOGD
74 
75 #include "gd.h"
76 #include "gdfontg.h"
77 #include "gdfontl.h"
78 #include "gdfontmb.h"
79 #include "gdfonts.h"
80 #include "gdfontt.h"
81 
82 /* #define MAX_D_ROWS 1000 */
83 #define NBRUSH 8
84 #define CHARHW 1.75 /* 2.0 */
85 #define MINTEXTSIZE 4 /* anything less than this is rendered as a line for thumbnails */
86 #define stricmp(a,b) strcasecmp(a,b)
87 
88 static gdImagePtr Gm; /* image */
89 static gdImagePtr Gm2 = NULL; /* secondary image */
90 static int Gm2height = 0, Gm2width = 0;
91 static gdFontPtr Gfont;  /* current font */
92 static int Gxmax, Gymax; /* drawing area image size */
93 static double Goldx = 0.0, Goldy = 0.0; /* last passed x,y */
94 static double Gcharwidth;
95 static int Gvertchar = 0;
96 static int Gtextsize;
97 static char Gcurcolorname[40];
98 static int Gcurcolor;
99 
100 /* static gdPoint Gptlist[MAX_D_ROWS]; */
101 static gdPoint *Gptlist = NULL;
102 static int Gmax_pts;
103 static int Gnpts = 0;
104 
105 static int Gcurlinestyle = 0;
106 static double Gcurlinewidth = 1.0;
107 static double Gcurdashscale = 1.0;
108 static gdImagePtr Gbrush[NBRUSH]; /* brush images */
109 static int Gdash[10][6]= { {1}, {1,1}, {3,1}, {5,1}, {2,1,1,1}, {4,1,1,1}, {6,1,1,1},
110                           {2,1,1,1,1,1}, {4,1,1,1,1,1}, {6,1,1,1,1,1} };  /* constants */
111 static int Gndash[10] = { 1, 2, 2, 2, 4, 4, 4, 6, 6, 6 }; /* constants */
112 static int Gdashpat[1000];
113 static int Ginitialized = 0;
114 static int Gtransparent_color = -1;
115 static int Gblack = 0;
116 static char GFTfont[80] = "";
117 #ifdef GDFREETYPE
118   static int GFTbox[8];
119   static double GFTsize;
120 #endif
121 
122 static int Gpixelsinch;
123 static int Greqwidth = 0, Greqheight = 0;
124 
125 
126 
127 /* ================================= */
128 int
PLGG_initstatic()129 PLGG_initstatic()
130 {
131 strcpy( g_fmt, "" );
132 Gm2 = NULL;
133 Gm2height = 0; Gm2width = 0;
134 Goldx = 0.0, Goldy = 0.0;
135 Gvertchar = 0;
136 Gnpts = 0;
137 Gcurlinestyle = 0;
138 Gcurlinewidth = 1.0;
139 Gcurdashscale = 1.0;
140 Ginitialized = 0;
141 Gtransparent_color = -1;
142 Gblack = 0;
143 strcpy( GFTfont, "" );
144 strcpy( Gcurcolorname, "" );
145 Greqwidth = 0, Greqheight = 0;
146 
147 return( 0 );
148 }
149 
150 
151 /* ================================= */
152 /* GETIMG - allow API access to the image and its size */
153 gdImagePtr
PLGG_getimg(width,height)154 PLGG_getimg( width, height )
155 int *width, *height; /* pixels */
156 {
157 if( !Ginitialized ) return( NULL );
158 *width = Gxmax;
159 *height = Gymax;
160 return( Gm );
161 }
162 
163 /* =================================== */
164 /* SETIMPIXSIZE - set an exact pixel height and width for result (cropped) image */
165 int
PLGG_setimpixsize(width,height)166 PLGG_setimpixsize( width, height )
167 int width, height;
168 {
169 Greqwidth = width;
170 Greqheight = height;
171 return( 0 );
172 }
173 
174 
175 /* ================================ */
176 int
PLGG_setup(name,pixelsinch,ux,uy,upleftx,uplefty,maxdrivervect)177 PLGG_setup( name, pixelsinch, ux, uy, upleftx, uplefty, maxdrivervect )
178 char *name;
179 int pixelsinch;
180 double ux, uy; /* size of image in inches x y */
181 int upleftx, uplefty; /* position of window - not used by this driver */
182 int maxdrivervect;
183 {
184 int i;
185 
186 if( Ginitialized ) { /* could be if a late setsize was issued.. */
187 	gdImageDestroy( Gm );
188 	for( i = 0; i < NBRUSH; i++ ) gdImageDestroy( Gbrush[i] );
189 	}
190 Ginitialized = 1;
191 
192 Gpixelsinch = pixelsinch;
193 Gxmax = (int)(ux * pixelsinch );
194 Gymax = (int)(uy * pixelsinch );
195 
196 /* Allocate pixels.. */
197 Gm = gdImageCreate( Gxmax, Gymax );
198 if( Gm == NULL ) return( Eerr( 12003, "Cannot create working image", "" ) );
199 for( i = 0; i < NBRUSH; i++ ) {
200 	Gbrush[i] = gdImageCreate( i+1, i+1 );
201 	if( Gbrush[i] == NULL ) return( Eerr( 12004, "Cannot create brush image", "" ) );
202 	}
203 
204 
205 PLGG_color( "white" );
206 PLGG_color( "black" );
207 gdImageSetBrush( Gm, Gbrush[0] );
208 
209 
210 Gmax_pts = maxdrivervect;
211 if( Gptlist != NULL ) free( Gptlist );
212 Gptlist = (gdPoint *) malloc( Gmax_pts * sizeof( gdPoint ) );
213 
214 return( 0 );
215 }
216 
217 /* ================================ */
218 int
PLGG_moveto(x,y)219 PLGG_moveto( x, y )
220 double x, y;
221 {
222 Goldx = x;
223 Goldy = y;
224 return( 0 );
225 }
226 /* ================================ */
227 int
PLGG_lineto(x,y)228 PLGG_lineto( x, y )
229 double x, y;
230 {
231 int a, b, c, d;
232 a = Exsca( Goldx ); b = Eysca( Goldy ); c = Exsca( x ); d = Eysca( y );
233 /* gdImageLine( Gm, a, b, c, d, gdStyled ); */
234 gdImageLine( Gm, a, b, c, d, gdStyledBrushed );
235 Goldx = x;
236 Goldy = y;
237 return( 0 );
238 }
239 
240 /* ================================ */
241 int
PLGG_rawline(a,b,c,d)242 PLGG_rawline( a, b, c, d )
243 int a, b, c, d;
244 {
245 gdImageLine( Gm, a, b, c, d, gdStyledBrushed );
246 return( 0 );
247 }
248 
249 /* ================================ */
250 int
PLGG_linetype(s,x,y)251 PLGG_linetype( s, x, y )
252 char *s;
253 double x, y;
254 {
255 int style;
256 style = atoi( s );
257 return( PLGG_linestyle( style, x, y ) );
258 }
259 
260 /* ================================ */
261 int
PLGG_linestyle(style,linewidth,dashscale)262 PLGG_linestyle( style, linewidth, dashscale )
263 int style;
264 double linewidth, dashscale;
265 {
266 int i, j, k, np, state, ds;
267 
268 style  = style % 9;
269 
270 ds = (int)(dashscale*2.0);
271 if( ds < 1 ) { style = 0; ds = 1; }
272 np = 0;
273 
274 /* build array p to indicate dash pattern, and set the dash style.. */
275 state = 1;
276 for( i = 0; i < Gndash[style]; i++ ) {
277 	for( j = 0; j < Gdash[style][i]; j++ ) {
278 		for( k = 0; k < ds; k++ ) {
279 			if( np >= 1000 ) {
280 				Eerr( 12005, "img dashscale out of range", "" );
281 				return( 0 );
282 				}
283 			Gdashpat[np++] = state;
284 			}
285 		}
286 	if( state == 1 ) state = 0;
287 	else state = 1;
288 	}
289 gdImageSetStyle( Gm, Gdashpat, np );
290 Gcurlinestyle = style;
291 Gcurdashscale = dashscale;
292 
293 /* set the line width by setting pixels in the brush image.. */
294 i = (int) linewidth;
295 if( i > (NBRUSH-1) ) i = NBRUSH - 1;
296 gdImageSetBrush( Gm, Gbrush[i] );
297 Gcurlinewidth = linewidth;
298 
299 
300 return( 0 );
301 }
302 /* ================================ */
303 int
PLGG_pathto(px,py)304 PLGG_pathto( px, py )
305 double px, py;
306 {
307 
308 if( (Gnpts+2) > Gmax_pts ) PLGG_fill(); /* fill what we have so far, then start again.  scg 5/4/04 */
309 
310 if( Gnpts == 0 ) {
311 	Gptlist[ Gnpts ].x = Exsca( Goldx );
312 	Gptlist[ Gnpts ].y = Eysca( Goldy );
313 	Gnpts++;
314 	}
315 Gptlist[ Gnpts ].x = Exsca( px );
316 Gptlist[ Gnpts ].y = Eysca( py );
317 Gnpts++;
318 return( 0 );
319 }
320 /* ================================ */
321 int
PLGG_fill()322 PLGG_fill()
323 {
324 if( Gnpts < 3 ) {
325 	Eerr( 12007, "warning, not enough points", "" );
326 	return( 0 );
327 	}
328 Gptlist[ Gnpts ].x = Gptlist[0].x;
329 Gptlist[ Gnpts ].y = Gptlist[0].y;
330 Gnpts++;
331 gdImageFilledPolygon( Gm, Gptlist, Gnpts, Gcurcolor );
332 Gnpts = 0;
333 return( 0 );
334 }
335 /* ================================ */
336 /* note: caller must restore previous color after this routine returns. */
337 int
PLGG_rect(x1,y1,x2,y2,color)338 PLGG_rect( x1, y1, x2, y2, color )
339 double x1, y1, x2, y2;
340 char *color;
341 {
342 int a, b, c, d;
343 a = Exsca( x1 );
344 b = Eysca( y1 );
345 c = Exsca( x2 );
346 d = Eysca( y2 );
347 PLGG_color( color );
348 gdImageFilledRectangle( Gm, a, b, c, d, Gcurcolor );
349 return( 0 );
350 }
351 
352 /* ================================ */
353 /* set a freetype font */
354 int
PLGG_font(s)355 PLGG_font( s )
356 char *s;
357 {
358 #ifdef GDFREETYPE
359 char *fontpath;
360   if( s[0] == '/' ) return( 0 ); /* ignore postscript fonts */
361   if( strcmp( s, "ascii" )==0 ) strcpy( GFTfont, "" );
362   else 	{
363 	fontpath = getenv( "GDFONTPATH" );
364 	if( fontpath == NULL ) Eerr( 12358, "warning: environment var GDFONTPATH not found. See ploticus fonts docs.", "" );
365 	if( strcmp( &s[ strlen(s) - 4 ], ".ttf" )==0 ) s[ strlen( s)-4 ] = '\0'; /* strip off .ttf ending - scg 1/26/05 */
366 	strcpy( GFTfont, s );
367 	}
368 #endif
369 return( 0 );
370 }
371 
372 /* ================================ */
373 int
PLGG_textsize(p)374 PLGG_textsize( p )
375 int p;
376 {
377 
378 #ifdef GDFREETYPE
379 if( GFTfont[0] ) {
380 	GFTsize = (double)p;
381 	Gtextsize = p;
382 	Gcharwidth = 0.0; /* no top/bottom adjustment needed with FT */
383 	return( 0 );
384 	}
385 #endif
386 
387 /* this logic is replicated in Etextsize() */
388 if( p <= 6 ) { Gfont = gdFontTiny; Gcharwidth = 0.05; }
389 else if( p >= 7 && p <= 9 ) { Gfont = gdFontSmall; Gcharwidth = 0.06; } /* was 0.0615384 */
390 else if( p >= 10 && p <= 12 ) { Gfont = gdFontMediumBold; Gcharwidth = 0.070; } /* was 0.0727272 */
391 else if( p >= 13 && p <= 15 ) { Gfont = gdFontLarge; Gcharwidth = 0.08; }
392 else if( p >= 15 ) { Gfont = gdFontGiant; Gcharwidth = 0.09; } /* was 0.0930232 */
393 Gtextsize = p;
394 
395 return( 0 );
396 }
397 /* ================================ */
398 int
PLGG_chardir(d)399 PLGG_chardir( d )
400 int d;
401 {
402 if( d == 90 ) Gvertchar = 1;
403 else Gvertchar = 0;
404 return( 0 );
405 }
406 /* ================================ */
407 int
PLGG_text(s)408 PLGG_text( s )
409 char *s;
410 {
411 int a, b, c, d;
412 double x, y;
413 #ifdef GDFREETYPE
414   char *err;
415 #endif
416 
417 a = Exsca( Goldx ); b = Eysca( Goldy );
418 if( Gvertchar ) {
419 	if( Gtextsize < MINTEXTSIZE ) {
420 		x = Goldx - (Gtextsize/90.0);
421 		a = Exsca( x ); b = Eysca( Goldy );
422 		c = Exsca( x ); d = Eysca( Goldy + (((double)Gtextsize/100.0)*strlen(s)));
423 		gdImageLine( Gm, a, b, c, d, gdStyledBrushed );
424 		}
425 	else	{
426 		x = Goldx - (Gcharwidth*CHARHW);  /* adjust for top loc */
427 		a = Exsca( x ); b = Eysca( Goldy );
428 #ifdef GDFREETYPE
429 		if( GFTfont[0] ) {
430 			err = gdImageStringFT( Gm, GFTbox, Gcurcolor, GFTfont, GFTsize, VERT, a, b, s );
431 			if( err ) { fprintf( stderr, "%s (%s)\n", err, GFTfont ); return( 1 ); }
432 			}
433 #endif
434 		if( GFTfont[0] == '\0' ) gdImageStringUp( Gm, Gfont, a, b, (unsigned char *)s, Gcurcolor );
435 		}
436 	}
437 else 	{
438 	if( Gtextsize < MINTEXTSIZE ) {
439 		a = Exsca( Goldx ); b = Eysca( Goldy );
440 		c = Exsca( Goldx + (((double)Gtextsize/100.0) * strlen(s)) ); d = Eysca( Goldy );
441 		gdImageLine( Gm, a, b, c, d, gdStyledBrushed );
442 		}
443 	else	{
444 		y = Goldy +  (Gcharwidth*CHARHW);  /* adjust for top loc */
445 		a = Exsca( Goldx ); b = Eysca( y );
446 #ifdef GDFREETYPE
447 		if( GFTfont[0] ) {
448 			err = gdImageStringFT( Gm, GFTbox, Gcurcolor, GFTfont, GFTsize, 0.0, a, b, s );
449 			if( err ) { fprintf( stderr, "%s (%s)\n", err, GFTfont ); return( 1 ); }
450 			}
451 #endif
452 		if( GFTfont[0] == '\0' ) gdImageString( Gm, Gfont, a, b, (unsigned char *)s, Gcurcolor );
453 		}
454 	}
455 Goldx = x;
456 Goldy = y;
457 
458 return( 0 );
459 }
460 /* ================================ */
461 int
PLGG_centext(s)462 PLGG_centext( s )
463 char *s;
464 {
465 double halflen, x, y;
466 int a, b, c, d;
467 #ifdef GDFREETYPE
468   char *err;
469 #endif
470 
471 halflen = (Gcharwidth * (double)(strlen( s ))) / 2.0;
472 if( Gvertchar ) {
473 	if( Gtextsize < MINTEXTSIZE ) {
474 		halflen = (double)(strlen(s))/2.0 * ((double)Gtextsize/100.0);
475 		x = Goldx - (Gtextsize/90.0);  /* adjust for top loc */
476 		a = Exsca( x ); b = Eysca( Goldy - halflen );
477 		c = Exsca( x ); d = Eysca( Goldy + halflen );
478 		gdImageLine( Gm, a, b, c, d, gdStyledBrushed );
479 		}
480 	else	{
481 #ifdef GDFREETYPE
482 		if( GFTfont[0] ) {
483 			a = Exsca( Goldx ); b = Eysca( Goldy );
484 			err = gdImageStringFT( NULL, GFTbox, Gcurcolor, GFTfont, GFTsize, 0.0, a, b, s );
485 			if( err ) { fprintf( stderr, "%s (%s) (width calc)\n", err, GFTfont ); return( 1 ); }
486 			b += (GFTbox[4] - GFTbox[0])/2;
487 			err = gdImageStringFT( Gm, GFTbox, Gcurcolor, GFTfont, GFTsize, VERT, a, b, s );
488 			if( err ) { fprintf( stderr, "%s (%s)\n", err, GFTfont ); return( 1 ); }
489 			}
490 #endif
491 		if( GFTfont[0] == '\0' ) {
492 			x = Goldx - (Gcharwidth*CHARHW);  /* adjust for top loc */
493 			y = Goldy - halflen;
494 			a = Exsca( x ); b = Eysca( y );
495 			gdImageStringUp( Gm, Gfont, a, b, (unsigned char *)s, Gcurcolor );
496 			}
497 		}
498 	}
499 else	{
500 	if( Gtextsize < MINTEXTSIZE ) {
501 		halflen = (double)(strlen(s))/2.0 * ((double)Gtextsize/100.0);
502 		a = Exsca( Goldx - halflen ); b = Eysca( Goldy );
503 		c = Exsca( Goldx + halflen ); d = Eysca( Goldy );
504 		gdImageLine( Gm, a, b, c, d, gdStyledBrushed );
505 		}
506 	else	{
507 #ifdef GDFREETYPE
508 		if( GFTfont[0] ) {
509 			a = Exsca( Goldx ); b = Eysca( Goldy );
510 			err = gdImageStringFT( NULL, GFTbox, Gcurcolor, GFTfont, GFTsize, 0.0, a, b, s );
511 			if( err ) { fprintf( stderr, "%s (%s) (width calc)\n", err, GFTfont ); return( 1 ); }
512 			a -= (GFTbox[4] - GFTbox[0])/2;
513 			err = gdImageStringFT( Gm, GFTbox, Gcurcolor, GFTfont, GFTsize, 0.0, a, b, s );
514 			if( err ) { fprintf( stderr, "%s (%s)\n", err, GFTfont ); return( 1 ); }
515 			}
516 #endif
517 		if( GFTfont[0] == '\0' ) {    /* ascii font */
518 			x = (Goldx - halflen) + (Gcharwidth/4.0); /* not sure why the extra is needed..*/
519 			y = Goldy + (Gcharwidth*CHARHW);  /* adjust for top loc */
520 			a = Exsca( x ); b = Eysca( y );
521 			gdImageString( Gm, Gfont, a, b, (unsigned char *)s, Gcurcolor );
522 			}
523 		}
524 	}
525 Goldx = x;
526 Goldy = y;
527 return( 0 );
528 }
529 /* ================================ */
530 int
PLGG_rightjust(s)531 PLGG_rightjust( s )
532 char *s;
533 {
534 double len, x, y;
535 int a, b, c, d;
536 #ifdef GDFREETYPE
537   char *err;
538 #endif
539 
540 len = Gcharwidth * strlen( s );
541 if( Gvertchar ) {
542 	if( Gtextsize < MINTEXTSIZE ) {
543 		x = Goldx - (Gtextsize/90.0);
544 		a = Exsca(x); b = Eysca( Goldy - (((double)Gtextsize/100.0)*strlen(s)));
545 		c = Exsca(x); d = Eysca( Goldy );
546 		}
547 	else	{
548 #ifdef GDFREETYPE
549 		if( GFTfont[0] ) {
550 			a = Exsca( Goldx ); b = Eysca( Goldy );
551 			err = gdImageStringFT( NULL, GFTbox, Gcurcolor, GFTfont, GFTsize, 0.0, a, b, s );
552 			if( err ) { fprintf( stderr, "%s (%s) (width calc)\n", err, GFTfont ); return( 1 ); }
553 			b += (GFTbox[4] - GFTbox[0]);
554 			/* err = gdImageStringFT( Gm, GFTbox, Gcurcolor, GFTfont, GFTsize, 0.0, a, b, s ); */
555 			err = gdImageStringFT( Gm, GFTbox, Gcurcolor, GFTfont, GFTsize, VERT, a, b, s );  /* fixed 9/17/02 - Artur Zaprzala */
556 			if( err ) { fprintf( stderr, "%s (%s)\n", err, GFTfont ); return( 1 ); }
557 			}
558 #endif
559 		if( GFTfont[0] == '\0' ) {
560 			x = Goldx - (Gcharwidth*CHARHW);  /* adjust for top loc */
561 			y = Goldy - len;
562 			a = Exsca( x ); b = Eysca( y );
563 			gdImageStringUp( Gm, Gfont, a, b, (unsigned char *)s, Gcurcolor );
564 			}
565 		}
566 	}
567 else	{
568 	if( Gtextsize < MINTEXTSIZE ) {
569 		a = Exsca( Goldx - ((strlen(s))*(Gtextsize/100.0))); b = Eysca( Goldy );
570 		c = Exsca( Goldx ); d = Eysca( Goldy );
571 		gdImageLine( Gm, a, b, c, d, gdStyledBrushed );
572 		}
573 	else	{
574 #ifdef GDFREETYPE
575 		if( GFTfont[0] ) {
576 			a = Exsca( Goldx ); b = Eysca( Goldy );
577 			err = gdImageStringFT( NULL, GFTbox, Gcurcolor, GFTfont, GFTsize, 0.0, a, b, s );
578 			if( err ) { fprintf( stderr, "%s (%s) (width calc)\n", err, GFTfont ); return( 1 ); }
579 			a -= (GFTbox[4] - GFTbox[0]);
580 			err = gdImageStringFT( Gm, GFTbox, Gcurcolor, GFTfont, GFTsize, 0.0, a, b, s );
581 			if( err ) { fprintf( stderr, "%s (%s)\n", err, GFTfont ); return( 1 ); }
582 			}
583 #endif
584 		if( GFTfont[0] == '\0' ) {
585 			x = Goldx - len;
586 			y = Goldy + (Gcharwidth*CHARHW);  /* adjust for top loc */
587 			a = Exsca( x ); b = Eysca( y );
588 			gdImageString( Gm, Gfont, a, b, (unsigned char *)s, Gcurcolor );
589 			}
590 		}
591 	}
592 Goldx = x;
593 Goldy = y;
594 return( 0 );
595 }
596 
597 /* ================================ */
598 /* find the width of the given txt using given freetype font and size */
599 /* added 8/5/05 - sugg by Erik Zachte */
600 int
PLGG_freetype_twidth(txt,font,size,twidth)601 PLGG_freetype_twidth( txt, font, size, twidth )
602 char *txt, *font;
603 double size;
604 double *twidth;
605 {
606 
607 *twidth = 0.0;
608 
609 #ifdef GDFREETYPE
610 if( font[0] ) {
611 	char *err;
612 	err = gdImageStringFT( NULL, GFTbox, 0, font, size, 0.0, 0, 0, txt );
613 	if( err ) { fprintf( stderr, "%s (%s) (width calc)\n", err, font ); return( 0 ); }
614 	*twidth = (GFTbox[2] - GFTbox[0]) / 100.0;
615 	}
616 #endif
617 return(0);
618 }
619 
620 
621 /* ================================ */
622 int
PLGG_color(color)623 PLGG_color( color )
624 char *color;
625 {
626 int i, n;
627 double r, g, b;
628 int ir, ig, ib, len;
629 int bc; /* brush color */
630 double atof();
631 
632 
633 /* request to load the color we currently have.. ignore - scg 6/18/04 */
634 /* this is necessary even with pcode lazy color change, because of rectangles */
635 if( strcmp( color, Gcurcolorname ) ==0 ) return( 0 );
636 else strcpy( Gcurcolorname, color );
637 
638 
639 /* parse graphcore color spec.. */
640 for( i = 0, len = strlen( color ); i < len; i++ ) {
641         if( GL_member( color[i], "(),/:|-" ) ) color[i] = ' ';
642         }
643 
644 if( strncmp( color, "rgb", 3 )==0 ) {
645         n = sscanf( color, "%*s %lf %lf %lf", &r, &g, &b );
646         if( n != 3 ) { Eerr( 12008, "Invalid color", color ); return(1); }
647         }
648 else if( strncmp( color, "gray", 4 )==0 || strncmp( color, "grey", 4 )==0 ) {
649         n = sscanf( color, "%*s %lf", &r );
650         if( n != 1 ) { Eerr( 12008, "Invalid color", color ); return(1); }
651         g = b = r;
652         }
653 else if( strcmp( color, "transparent" )==0 ) {  /* added scg 12/29/99 */
654 	if( Gtransparent_color < 0 ) {
655 		/* allocate transparent color.. */
656 		Gtransparent_color = gdImageColorAllocate( Gm, 254, 254, 254 ); /* white fallbk */
657 		/* gdImageColorTransparent( Gm, Gtransparent_color ); */
658 		/* also keep brushes in sync.. */
659 		for( i = 0; i < NBRUSH; i++ ) gdImageColorAllocate( Gbrush[i], 254, 254, 254 );
660 		}
661 	if( Gtransparent_color >= 0 ) Gcurcolor = Gtransparent_color;
662 	/* Don't set brushes, etc, because we will never need to draw
663 	   transparent lines.  Brushes remain set to previous color. */
664 	return( 0 );
665 	}
666 else if( strncmp( color, "xrgb", 4 )==0 ) {
667         if (PLG_xrgb_to_rgb( &color[5], &r, &g, &b)) return(1);
668         }
669 else if( color[0] == 'x' ) {  /* added scg 5/31/07 */
670         if (PLG_xrgb_to_rgb( &color[1], &r, &g, &b)) return(1);
671         }
672 else if( GL_goodnum( color, &i ) ) {
673         r = atof( color );
674         g = b = r;
675         }
676 else PLG_colorname_to_rgb( color, &r, &g, &b );
677 
678 ir = (int)(r * 255);
679 ig = (int)(g * 255);
680 ib = (int)(b * 255);
681 
682 
683 Gcurcolor = gdImageColorExact( Gm, ir, ig, ib );
684 if( Gcurcolor < 0 ) {
685 	Gcurcolor = gdImageColorAllocate( Gm, ir, ig, ib );
686 	if( Gcurcolor < 0 ) {
687 		Gcurcolor = gdImageColorClosest( Gm, ir, ig, ib );
688 		if( Gcurcolor < 0 ) return( 1 ); /* Eerr( 12009, "Error on img color allocation", color ); exit(1); */
689 		}
690 	}
691 
692 if( ir + ig + ib == 0 ) Gblack = Gcurcolor; /* for wbmp */
693 
694 
695 /* also set all brushes to new color.. */
696 for( i = 0; i < NBRUSH; i++ ) {
697 	/* first find color index or allocate new one.. */
698 	bc = gdImageColorExact( Gbrush[i], ir, ig, ib );
699 	if( bc < 0 ) {
700 		bc = gdImageColorAllocate( Gbrush[i], ir, ig, ib );
701 		if( bc < 0 ) {
702 			bc = gdImageColorClosest( Gbrush[i], ir, ig, ib );
703 			if( bc < 0 ) {
704 				Eerr( 12010, "Error on img brush color alloc", color );
705 				bc = 0;
706 				}
707 			}
708 		}
709 	/* next set color of each brush.. */
710 	gdImageFilledRectangle( Gbrush[i], 0, 0, i+1, i+1, bc ); /* bc should == Gcurcolor */
711 	}
712 
713 /* This message occurs all the time with JPEG.. perhaps not needed..
714  * if( bc != Gcurcolor ) {
715  *	Eerr( 12011, "warning, img brush color does not match current color", "" );
716  * 	}
717  */
718 
719 /* now, need to update brush image to new color using current linewidth */
720 i = (int) Gcurlinewidth;
721 if( i > (NBRUSH-1) ) i = NBRUSH - 1;
722 gdImageSetBrush( Gm, Gbrush[i] );
723 
724 return( 0 );
725 }
726 
727 /* ================================ */
728 /* PIXPT - pixel data point - clean data points rendered by setting GD pixels directly */
729 /* added 5/29/06 scg */
730 /* note: color already set by symboldet() */
731 int
PLGG_pixpt(x,y,symcode)732 PLGG_pixpt( x, y, symcode )
733 double x, y;
734 char *symcode;
735 {
736 int a, b;
737 int i, j, irow, icol, radius, iw, omode, scanend;
738 double atof();
739 
740 /* Note - this code is essentially replicated in x11.c */
741 
742 if( symcode[0] == 'o' ) { omode = 1; symcode[0] = 'p'; }
743 else omode = 0;
744 
745 a = Exsca( x ); b = Eysca( y ); /* convert to pixel coordinates */
746 
747 if( strncmp( symcode, "pixsquare", 9 )==0 ) {
748 	radius = (int) (atof( &symcode[9] ) * Gpixelsinch);
749 	if( radius < 1 ) radius = 3;
750 	if( omode ) { /* do top and bottom lines */
751 		irow = b-radius; for( icol = a-radius; icol < a+radius; icol++ ) gdImageSetPixel( Gm, icol, irow, gdStyledBrushed );
752 		irow = b+radius; for( icol = a-radius; icol <= a+radius; icol++ ) gdImageSetPixel( Gm, icol, irow, gdStyledBrushed );
753 		}
754 	for( irow = b-radius; irow < b+radius; irow++ ) {
755 		if( omode ) { gdImageSetPixel( Gm, a-radius, irow, gdStyledBrushed ); gdImageSetPixel( Gm, a+radius, irow, gdStyledBrushed ); }
756 		else { for( icol = a-radius; icol < a+radius; icol++ ) gdImageSetPixel( Gm, icol, irow, gdStyledBrushed ); }
757 		}
758 	}
759 else if( strncmp( symcode, "pixcircle", 9 )==0 ) {
760 	radius = (int) (atof( &symcode[9] ) * Gpixelsinch);
761 	if( radius <= 2 ) goto DO_DIAMOND;
762 	else if( radius > 9 ) radius = 9;
763 	for( i = circliststart[radius]; ; i+= 2 ) {
764 		scanend = circpt[i+1];
765 		if( circpt[i] == 0 && scanend == 0 ) break;
766 		for( j = 0; j <= scanend; j++ ) {
767 			if( omode && !( j == scanend )) continue;
768 			gdImageSetPixel( Gm, a-j, b+circpt[i], gdStyledBrushed );
769 			if( j > 0 ) gdImageSetPixel( Gm, a+j, b+circpt[i], gdStyledBrushed );
770 			gdImageSetPixel( Gm, a-j, b-circpt[i], gdStyledBrushed );
771 			if( j > 0 ) gdImageSetPixel( Gm, a+j, b-circpt[i], gdStyledBrushed );
772 			if( omode ) {
773 				gdImageSetPixel( Gm, a+circpt[i], b-j, gdStyledBrushed );
774 				if( j > 0 ) gdImageSetPixel( Gm, a+circpt[i], b+j, gdStyledBrushed );
775 				gdImageSetPixel( Gm, a-circpt[i], b-j, gdStyledBrushed );
776 				if( j > 0 ) gdImageSetPixel( Gm, a-circpt[i], b+j, gdStyledBrushed );
777 				}
778 			}
779 		}
780 	}
781 else if( strncmp( symcode, "pixdiamond", 10 )==0 ) {
782 	radius = (int) (atof( &symcode[10] ) * Gpixelsinch);
783 	DO_DIAMOND:
784 	if( radius < 1 ) radius = 3;
785 	radius++;  /* improves consistency w/ other shapes */
786 	for( irow = b-radius, iw = 0; irow <= (b+radius); irow++, iw++ ) {
787 	    scanend = a+abs(iw);
788 	    for( icol = a-abs(iw), j = 0; icol <= scanend; icol++, j++ ) {
789 		if( omode && !( j == 0 || icol == scanend )) ;
790 		else gdImageSetPixel( Gm, icol, irow, gdStyledBrushed );
791 		}
792 	    if( irow == b ) iw = (-radius);
793 	    }
794 	}
795 else if( strncmp( symcode, "pixtriangle", 11 )==0 ) {
796 	radius = (int) (atof( &symcode[11] ) * Gpixelsinch);
797 	if( radius < 1 ) radius = 3;
798 	for( irow = b-radius, iw = 0; irow <= b+radius; irow++, iw++ ) {
799 	    scanend = a+abs(iw/2);
800 	    for( icol = a-abs(iw/2), j = 0; icol <= scanend; icol++, j++ ) {
801 		if( omode && irow == b+radius ) gdImageSetPixel( Gm, icol, irow-1, gdStyledBrushed );
802 		else if( omode && !( j == 0 || icol == scanend ));
803 		else gdImageSetPixel( Gm, icol, irow-1, gdStyledBrushed );
804 		}
805 	    }
806 	}
807 else if( strncmp( symcode, "pixdowntriangle", 15 )==0 ) {
808 	radius = (int) (atof( &symcode[15] ) * Gpixelsinch);
809 	if( radius < 1 ) radius = 3;
810 	for( irow = b+radius, iw = 0; irow >= (b-radius); irow--, iw++ ) {
811 	    scanend = a+abs(iw/2);
812 	    for( icol = a-abs(iw/2), j = 0; icol <= scanend; icol++, j++ ) {
813 		if( omode && irow == b-radius ) gdImageSetPixel( Gm, icol, irow-1, gdStyledBrushed );
814 		else if( omode && !(j == 0 || icol == scanend ));
815 		else gdImageSetPixel( Gm, icol, irow, gdStyledBrushed );
816 		}
817 	    }
818 	}
819 return( 0 );
820 }
821 
822 /* ============================== */
823 int
PLGG_imload(imgname,width,height)824 PLGG_imload( imgname, width, height )
825 char *imgname;
826 int width, height;
827 {
828 FILE *fp;
829 
830 if( Gm2 != NULL ) {
831 	gdImageDestroy( Gm2 );
832 	Gm2 = NULL;
833 	}
834 
835 fp = fopen( imgname, "rb" );
836 if( fp == NULL ) return( -1 );
837 
838 #ifdef GD13
839   Gm2 = gdImageCreateFromGif( fp );
840 #endif
841 #ifdef GD16
842   Gm2 = gdImageCreateFromPng( fp );
843 #endif
844 #ifdef GD18
845   if( strcmp( g_fmt, "png" )==0 ) Gm2 = gdImageCreateFromPng( fp );
846   else if( strcmp( g_fmt, "jpeg" )==0 ) Gm2 = gdImageCreateFromJpeg( fp );
847   else if( strcmp( g_fmt, "wbmp" )==0 ) Gm2 = gdImageCreateFromWBMP( fp );
848 #endif
849 
850 if( width != 0 ) Gm2width = width;
851 if( height != 0 ) Gm2height = height;
852 
853 fclose( fp );
854 return( 0 );
855 }
856 
857 /* ================================ */
858 /* place secondary GIF image within main image at absolute x, y
859  *	align may be one of: topleft   topcenter  center  bottomleft
860  */
861 int
PLGG_implace(x,y,align,width,height)862 PLGG_implace( x, y, align, width, height )
863 double x, y;
864 char *align;
865 int width, height;  /* render the image using this width and height in pixels... if 0 0 then use natural size */
866 {
867 int gx, gy;
868 double PLG_xsca_inv(), PLG_ysca_inv();
869 
870 fprintf( stderr, "in gd imcopy..\n" );
871 if( Gm2 == NULL ) return( -1 );
872 
873 if( width < 1 ) width = Gm2width;  /* as it may have been set in imload */
874 if( width < 1 ) width = Gm2->sx;   /* fallback to image's natural size */
875 
876 if( height < 1 ) height = Gm2height;  /* as it may have been set in imload */
877 if( height < 1 ) height = Gm2->sy;    /* fallback to image's natural size */
878 
879 if( strncmp( align, "center", 6 )==0 ) { gx = Exsca( x ) - (width/2); gy = Eysca( y ) - (height/2); }
880 else if( strcmp( align, "topcenter" )==0 ) { gx = Exsca( x ) - (width/2); gy = Eysca( y ); }
881 else if( strcmp( align, "bottomleft" )==0 ) { gx = Exsca( x ); gy = Eysca( y ) - height; }
882 else { gx = Exsca( x ); gy = Eysca( y ); } /* default to top left */
883 
884 if( gx < 0 ) gx = 0;
885 if( gy < 0 ) gy = 0;
886 
887 gdImageCopyResized( Gm, Gm2, gx, gy, 0, 0, width, height, Gm2->sx, Gm2->sy );
888 
889 /* add to app bounding box */
890 PLG_bb( Exsca_inv( gx ), Eysca_inv( gy ) );
891 PLG_bb( Exsca_inv( gx + width ), Eysca_inv( gy + height ) );
892 
893 return( 0 );
894 }
895 
896 
897 /* ================================ */
898 /* EOF - crop image to bounding box size, and create output file */
899 /* scg 11/23/01 added click map support */
900 
901 int
PLGG_eof(filename,x1,y1,x2,y2)902 PLGG_eof( filename, x1, y1, x2, y2 )
903 char *filename;
904 double x1, y1, x2, y2; /* rectangle of the image that we will be copying into to do the final cropping */
905 {
906 int i, width, height, ux, uy;
907 gdImagePtr outim;
908 FILE *outfp;
909 int t;
910 
911 
912 if( x1 < 0.0 ) x1 = 0.0;
913 if( y1 < 0.0 ) y1 = 0.0;
914 /* if( x2 < 0.0 ) x2 = 0.0; */
915 /* if( y2 < 0.0 ) y2 = 0.0; */
916 
917 /* final area may not be larger than originally defined 'pagesize' - added scg 5/8/06 */
918 if( x2 > (Gxmax/(double)Gpixelsinch) || x2 < 0.0 ) x2 = Gxmax/(double)Gpixelsinch ;
919 if( y2 > (Gymax/(double)Gpixelsinch) || y2 < 0.0 ) y2 = Gymax/(double)Gpixelsinch ;
920 
921 #ifdef PLOTICUS
922 if( PL_clickmap_getdemomode() ) PL_clickmap_show( 'g' ); /* 11/23/01 */
923 #endif
924 
925 if( Greqwidth > 0 && Greqheight > 0 ) {
926 	width = Greqwidth;
927 	height = Greqheight;
928 	}
929 else	{
930 	width = Exsca( x2 ) - Exsca( x1 );
931 	height = Eysca( y1 ) - Eysca( y2 );
932 	}
933 if( height < 10 || width < 10 ) return( Eerr( 12012, "Result image is too small - not created", "" ) );
934 
935 ux = Exsca( x1 );
936 uy = Eysca( y2 );
937 
938 
939 /* copy to smaller img sized by bounding box.. */
940 outim = gdImageCreate( width, height );
941 if( outim == NULL ) return( Eerr( 12013, "Error on creation of image output", "" ) );
942 gdImageCopy( outim, Gm, 0, 0, ux, uy, width, height );
943 /* fprintf( stderr, "new im w:%d h:%d   ux:%d uy:%d\n", width, height, ux, uy ); */
944 
945 /* if a transparent color was used, set it now in outim.. */
946 if( Gtransparent_color >= 0 ) {
947 	t = gdImageColorExact( outim, 254, 254, 254 );
948 	gdImageColorTransparent( outim, t );
949 	}
950 
951 /* Open a file for writing. "wb" means "write binary", important under MSDOS, harmless under Unix. */
952 if( strcmp( filename, "stdout" )==0 ) {
953 	fflush( stdout );
954 #ifdef WIN32
955 	_setmode( _fileno( stdout ), _O_BINARY ); /* use binary mode stdout */
956 #endif
957 	outfp = stdout;
958 	}
959 else outfp = fopen( filename, "wb");
960 if( outfp == NULL ) return( Eerr( 12014, "Cannot open for write", filename ) );
961 
962 /* Output the image to the disk file. */
963 #ifdef GD13
964 gdImageGif( outim, outfp );
965 #endif
966 #ifdef GD16
967 gdImagePng( outim, outfp );
968 #endif
969 #ifdef GD18
970 if( strcmp( g_fmt, "png" )==0 ) gdImagePng( outim, outfp );
971 else if( strcmp( g_fmt, "jpeg" )==0 ) gdImageJpeg( outim, outfp, 75 );
972 else if( strcmp( g_fmt, "wbmp" )==0 ) gdImageWBMP( outim, Gblack, outfp );
973 #endif
974 
975 if( strcmp( filename, "stdout" )!=0 ) {
976 	fclose( outfp );
977 #ifndef WIN32
978 	chmod( filename, 00644 );
979 #endif
980 	}
981 else	{
982 	fflush( stdout );
983 #ifdef WIN32
984 	_setmode( _fileno( stdout ), _O_TEXT ); /* if using stdout, restore stdout to text mode */
985 #endif
986 	}
987 
988 /* free memory (other ims freed in EGSetup() for subsequent pages) */
989 gdImageDestroy( Gm );
990 for( i = 0; i < NBRUSH; i++ ) gdImageDestroy( Gbrush[i] );
991 gdImageDestroy( outim );
992 Ginitialized = 0;
993 
994 #ifdef PLOTICUS
995 /* write map file */
996 if( PL_clickmap_inprogress() ) PL_clickmap_out( ux, uy );
997 #endif
998 
999 return( 0 );
1000 }
1001 
1002 
1003 
1004 
1005 #endif /* NOGD */
1006 
1007 /* ======================================================= *
1008  * Copyright 1998-2005 Stephen C. Grubb                    *
1009  * http://ploticus.sourceforge.net                         *
1010  * Covered by GPL; see the file ./Copyright for details.   *
1011  * ======================================================= */
1012