Initial release of Maemo 5 port of gnuplot
[gnuplot] / term / gd.trm
1 /* Hello, Emacs, this is -*-C-*-
2  * $Id: gd.trm,v 1.95.2.16 2009/03/03 02:43:52 sfeam Exp $
3  * based on gif.trm,v 1.26.2.1 2000/05/01 00:17:20 joze
4  */
5
6 /* GNUPLOT -- gd.trm */
7
8 /*[
9  * Copyright 1998, 2001, 2004
10  *
11  * Permission to use, copy, and distribute this software and its
12  * documentation for any purpose with or without fee is hereby granted,
13  * provided that the above copyright notice appear in all copies and
14  * that both that copyright notice and this permission notice appear
15  * in supporting documentation.
16  *
17  * Permission to modify the software is granted, but not the right to
18  * distribute the complete modified source code.  Modifications are to
19  * be distributed as patches to the released version.  Permission to
20  * distribute binaries produced by compiling modified sources is granted,
21  * provided you
22  *   1. distribute the corresponding source modifications from the
23  *    released version in the form of a patch file along with the binaries,
24  *   2. add special version identification to distinguish your version
25  *    in addition to the base release version number,
26  *   3. provide your name and address as the primary contact for the
27  *    support of your modified version, and
28  *   4. retain our contact information in regard to use of the base
29  *    software.
30  * Permission to distribute the released version of the source code along
31  * with corresponding source modifications in the form of a patch file is
32  * granted with same provisions 2 through 4 for binary distributions.
33  *
34  * This software is provided "as is" without express or implied warranty
35  * to the extent permitted by applicable law.
36 ]*/
37
38 /*
39  * This file is included by ../term.c.
40  *
41  * This terminal driver supports PNG and JPEG output using
42  *  GD library 1.8, 2.0
43  *
44  * To Use:
45  *
46  * set terminal png ?options ...?
47  *
48  * Where an option is:
49  *
50  * transparent - generate transparent PNGs.  The first color will
51  * be the transparent one.
52  *
53  * interlace - generate interlaced PNGs.
54  *
55  * image size (in pixels)
56  *
57  * font size (tiny,small,medium,large,giant)
58  *
59  * font name (TrueType or Adobe Type 1 font name is passed to libgd)
60  *
61  * xrrggbb - sets the next color.  x is the literal character 'x',
62  * rrggbb are the red green and blue components in hex.  For example
63  * x00ff00 is green.  The background color is set first, then the
64  * color borders, then the X & Y axis, then the plotting colors.
65  * (The wierd color spec is in order to get around limitations
66  * in gnuplot's scanner.)
67  *
68  * This driver is modeled after the PBM driver pbm.trm.
69  *
70  * AUTHORS
71  *  Sam Shen <sls@mh1.lbl.gov>
72  *  Alex Woo <woo@playfair.stanford.edu>
73  *  Ethan A Merritt <merritt@u.washington.edu>
74  *
75  * CONTRIBUTORS
76  *  Alfred Reibenschuh <alfred.reibenschuh@it-austria.com> or <fredo@blackbox.at>
77  *  Ben Laurie <ben@algroup.co.uk>
78  *
79  * This version outputs either color or monochrome PNGs.
80  * The default is 640x480 pixels.
81  *
82  ******************************************************************************
83  * PLEASE READ                                                                *
84  * This driver uses the gd library, available from http://www.boutell.com/gd/ *
85  * Earlier versions of the gd library produced GIF images, but starting with  *
86  * version 1.6 the gd library no longer supports creation of GIF images due   *
87  * to licensing issues. Hence this modified driver, which uses the old gd/gif *
88  * code to produce PNG images instead.                                        *
89  *                                                                            *
90  * requires:                                                                  *
91  *      libgd version 1.8 or greater     http://www.boutell.com/gd/           *
92  *      libfreetype version 2            http://www.freetype.org/             *
93  *                                                                            *
94  * This driver allows you to use TrueType fonts.  You can use this driver     *
95  * without having any TrueType fonts installed, but the default fonts are     *
96  * comparatively limited.                                                     *
97  ******************************************************************************
98  *
99  * Petr Mikulik, Jan 1999: terminal entries for PM3D functionality
100  * Ethan Merritt, May 2001: modified gd/gif driver to produce png instead;
101  *                          added support for line width and TrueType fonts
102  */
103
104 #include "driver.h"
105
106 #ifdef TERM_REGISTER
107 register_term(png)
108 #endif
109
110 #ifdef TERM_PROTO
111 TERM_PUBLIC void PNG_options __PROTO((void));
112 TERM_PUBLIC void PNG_init __PROTO((void));
113 TERM_PUBLIC void PNG_graphics __PROTO((void));
114 TERM_PUBLIC void PNG_text __PROTO((void));
115 TERM_PUBLIC void PNG_linetype __PROTO((int linetype));
116 TERM_PUBLIC void PNG_linewidth __PROTO((double linewidth));
117 TERM_PUBLIC void PNG_move __PROTO((unsigned int x, unsigned int y));
118 TERM_PUBLIC void PNG_vector __PROTO((unsigned int x, unsigned int y));
119 TERM_PUBLIC void PNG_put_text __PROTO((unsigned int x, unsigned int y, const char str[]));
120 TERM_PUBLIC int PNG_justify_text __PROTO((enum JUSTIFY mode));
121 TERM_PUBLIC void PNG_point __PROTO((unsigned int x, unsigned int y, int number));
122 TERM_PUBLIC int PNG_text_angle __PROTO((int ang));
123 TERM_PUBLIC void PNG_reset __PROTO((void));
124 TERM_PUBLIC int PNG_set_font __PROTO((const char *fontname));
125 TERM_PUBLIC void PNG_pointsize __PROTO((double ptsize));
126 TERM_PUBLIC void PNG_boxfill(int, unsigned int, unsigned int, unsigned int, unsigned int);
127 TERM_PUBLIC int PNG_make_palette (t_sm_palette *);
128 /* TERM_PUBLIC void PNG_previous_palette (void); */
129 TERM_PUBLIC void PNG_set_color (t_colorspec *);
130 TERM_PUBLIC void PNG_filled_polygon (int, gpiPoint *);
131 #ifdef WITH_IMAGE
132 TERM_PUBLIC void PNG_image __PROTO((unsigned, unsigned, coordval *, gpiPoint *, t_imagecolor));
133 #endif
134
135 /* To support "set term png enhanced" */
136 TERM_PUBLIC void ENHGD_put_text __PROTO((unsigned int x, unsigned int y, const char str[]));
137 TERM_PUBLIC void ENHGD_OPEN __PROTO((char * fontname, double fontsize,
138                         double base, TBOOLEAN widthflag, TBOOLEAN showflag,
139                         int overprint));
140 TERM_PUBLIC void ENHGD_FLUSH __PROTO((void));
141
142
143 #include "gd.h"
144
145 #if defined(WIN32) && !defined(NONDLL)
146   /* static font pointers are recommended when using bgd.dll */
147 # ifndef GD_NEED_LOCAL_FONT_POINTERS
148 #  define GD_NEED_LOCAL_FONT_POINTERS
149 # endif
150 #endif
151
152 #ifdef GD_NEED_LOCAL_FONT_POINTERS
153 # include "gdfonts.h"
154 # include "gdfontl.h"
155 # include "gdfontmb.h"
156 # include "gdfontt.h"
157 # include "gdfontg.h"
158 #endif
159
160 /* This is required for the shared library version of libgd on Windows.
161    Newer versions of libgd (>=2.0.24 ?) already define it. */
162 #ifndef BGD_EXPORT_DATA_PROT 
163 # define BGD_EXPORT_DATA_PROT extern
164 #endif
165
166 /* These intermediate functions are necessary on Windows since 
167    the shared version of libgd uses a different calling convention
168    and there is no proper macro defined.
169 */
170 #if defined(WIN32) && !defined(NONDLL)
171 static void gp_gdImagePolygon(gdImagePtr, gdPointPtr, int, int);
172 static void gp_gdImageFilledPolygon(gdImagePtr, gdPointPtr, int, int);
173 #else
174 # define gp_gdImagePolygon gdImagePolygon
175 # define gp_gdImageFilledPolygon gdImageFilledPolygon 
176 #endif
177
178 static void PNG_PointX __PROTO((unsigned int, unsigned int));
179 static void PNG_PointPlus __PROTO((unsigned int, unsigned int));
180 static void PNG_Triangle(unsigned int x, unsigned int y, int direction,
181         void (*draw_func)(gdImagePtr, gdPointPtr, int, int));
182 static void PNG_Diamond(unsigned int x, unsigned int y,
183         void (*draw_func)(gdImagePtr, gdPointPtr, int, int));
184
185 #ifndef GD_NEED_LOCAL_FONT_POINTERS
186 BGD_EXPORT_DATA_PROT gdFontPtr gdFontSmall;     /* 6x12 */
187 BGD_EXPORT_DATA_PROT gdFontPtr gdFontLarge;     /* 8x16 */
188 BGD_EXPORT_DATA_PROT gdFontPtr gdFontMediumBold;        /* 7x13 */
189 BGD_EXPORT_DATA_PROT gdFontPtr gdFontGiant;  /* 9x15 */
190 BGD_EXPORT_DATA_PROT gdFontPtr gdFontTiny;  /* 5x8 */
191 #else
192 static gdFontPtr gdFontSmall;   /* 6x12 */
193 static gdFontPtr gdFontLarge;   /* 8x16 */
194 static gdFontPtr gdFontMediumBold;      /* 7x13 */
195 static gdFontPtr gdFontGiant;  /* 9x15 */
196 static gdFontPtr gdFontTiny;  /* 5x8 */
197 #endif
198
199 #define GREG_XMAX 640
200 #define GREG_YMAX 480
201
202 /* This will be the default font */
203 # define gdfont gdFontMediumBold
204 # define PNG_VCHAR 13
205 # define PNG_HCHAR 7
206
207 #define PNG_TICSIZE (GREG_YMAX/100)
208
209 #define PNG_MAX_COLORS 256
210 #define GOT_NEXT_PROTO
211 #endif
212
213 #ifndef TERM_PROTO_ONLY
214 #ifdef TERM_BODY
215
216 static TBOOLEAN PNG_initialized = FALSE;        /* Set when terminal first initialized */
217
218 static struct {
219     gdImagePtr image;
220     gdFontPtr font;
221     unsigned int x, y;
222     int height;
223     int charh, charw;
224     int color;             /* Magic index returned by libgd */
225     int rgb;               /* Our guess at the corresponding rgb */
226     int n_colors;
227     int color_table[PNG_MAX_COLORS];
228     int rgb_table[PNG_MAX_COLORS];
229     int angle;
230     enum JUSTIFY justify;
231     int flags;
232     int linetype;
233     int linewidth;
234     TBOOLEAN capbutt;  /* use capbutt on lines with GD2, 20051205 MWS*/
235     int ttfsize;
236     char *ttffont;
237     gdFontPtr default_font;
238     char *    default_ttffont;
239     int       default_ttfsize;
240     TBOOLEAN  TrueColor;
241     /* Variables for animated gif support: */
242     TBOOLEAN  animate;          /* Only gif supports animation */
243     int       loop_count;       /* Number of times to repeat sequence */
244     int       frame_count;      /* Number of frames in animation */
245     int       frame_delay;      /* Time between frames in .01 seconds */
246     TBOOLEAN  frame_optimization;
247     gdImagePtr previous_image;  /* Needed to encode animation as a series of deltas */
248 } png_state;
249
250 #define PNG_USE_TRANSPARENT 1
251 #define PNG_USE_INTERLACE   2
252 #define PNG_USE_CROP        4
253
254 enum PNG_id {
255     PNG_TRANSPARENT, PNG_NOTRANSPARENT,
256     PNG_INTERLACE, PNG_NOINTERLACE,
257     PNG_CROP, PNG_NOCROP,
258     /* Font size */
259     PNG_TINY, PNG_SMALL, PNG_MEDIUM, PNG_LARGE, PNG_GIANT,
260     PNG_FONT,
261     PNG_SIZE,
262     PNG_ENHANCED, PNG_NOENHANCED,
263     PNG_TRUECOLOR, PNG_NOTRUECOLOR,
264     PNG_LINEWIDTH, PNG_BUTT, PNG_ROUNDED,
265     GIF_ANIMATE, GIF_DELAY, GIF_LOOP, GIF_NOOPT, GIF_OPT,
266     PNG_OTHER
267 };
268
269 /* Only needed for dubious backwards compatibility with 'set size'
270  * in pre-4.0 versions that didn't support 'set term size'
271  */
272 static TBOOLEAN PNG_explicit_size = FALSE;
273
274
275 #ifdef Y
276 #   undef Y
277 #endif
278 #define Y(y) (png_state.height - (y))
279
280 static int PNG_XMAX = GREG_XMAX;
281 static int PNG_YMAX = GREG_YMAX;
282 static const int PNG_POINT_SCALE = 3;
283 static int PNG_ps = 3;
284
285 static struct gen_table PNG_opts[] =
286 {
287     { "trans$parent", PNG_TRANSPARENT },
288     { "notran$sparent", PNG_NOTRANSPARENT },
289     { "inter$lace", PNG_INTERLACE },
290     { "nointer$lace", PNG_NOINTERLACE },
291     { "crop", PNG_CROP },
292     { "nocrop", PNG_NOCROP },
293     { "ti$ny", PNG_TINY },
294     { "s$mall", PNG_SMALL },
295     { "m$edium", PNG_MEDIUM },
296     { "l$arge", PNG_LARGE },
297     { "g$iant", PNG_GIANT },
298     { "fo$nt", PNG_FONT },
299     { "si$ze", PNG_SIZE },
300     { "enh$anced", PNG_ENHANCED },
301     { "noenh$anced", PNG_NOENHANCED },
302     { "true$color", PNG_TRUECOLOR },
303     { "notrue$color", PNG_NOTRUECOLOR },
304     { "linew$idth", PNG_LINEWIDTH },
305     { "anim$ate", GIF_ANIMATE }, /* gif animation options */
306     { "delay", GIF_DELAY },
307     { "loop", GIF_LOOP },
308     { "noopt$imize", GIF_NOOPT },
309     { "opt$imize", GIF_OPT }, /* end of gif animation options */
310     { "lw", PNG_LINEWIDTH },
311     { "butt", PNG_BUTT},
312     { "round$ed", PNG_ROUNDED},
313     { NULL, PNG_OTHER }
314 };
315
316 #undef MAXLINEWIDTH
317 #define MAXLINEWIDTH 12
318 static double PNG_linewidth_factor = 1.0;
319
320 /* EAM - gdImage structure to hold brushes for linewidth */
321 /* We only use brushes 2 through MAXLINEWIDTH            */
322 typedef struct {
323     gdImagePtr im;
324     unsigned int last_rgb;
325     int bgnd;
326     int fgnd;
327 }   PNG_BRUSH;
328
329 static PNG_BRUSH PNG_brush[MAXLINEWIDTH+1];
330
331 typedef struct {
332     gdImagePtr im;
333     unsigned int last_rgb;
334     int fillpar;
335 }   PNG_FILL_TILE;
336
337 static PNG_FILL_TILE PNG_fill_tile = { (gdImagePtr)0, 0, 0 };
338
339 /* To be used with libgd 2.0.34 to request Symbol encoding */
340 #ifdef gdFTEX_Adobe_Custom
341 static gdFTStringExtra PNG_FONT_INFO = {0,0,0,0,0,NULL,NULL};
342 #endif
343
344 #if defined(WIN32) && !defined(NONDLL)
345 static void 
346 gp_gdImagePolygon(gdImagePtr im, gdPointPtr p, int n, int c)
347 {
348     gdImagePolygon(im, p, n, c);
349 }
350
351 static void 
352 gp_gdImageFilledPolygon(gdImagePtr im, gdPointPtr p, int n, int c)
353 {
354     gdImageFilledPolygon(im, p, n, c);
355 }
356 #endif
357
358
359 /* Common code to crop the image around its bounding box, just before writing
360    down the file.
361 */
362 static void
363 image_do_crop ()
364 {
365     if (png_state.flags & PNG_USE_CROP) {
366         int x, y, x1, y1, x2, y2, flag;
367         int bg = png_state.color_table[0]; /* index of the background color */
368         gdImagePtr im_crop;
369         for (flag=0, x1=0; x1 < gdImageSX(png_state.image)-1; x1++) {
370             for (y=0; y < gdImageSY(png_state.image); y++)
371                 if (gdImageGetPixel(png_state.image, x1, y) != bg) { flag = 1; break; }
372             if (flag) break;
373         }
374         for (flag=0, x2=gdImageSX(png_state.image)-1; x2 >= x1; x2--) {
375             for (y=0; y < gdImageSY(png_state.image); y++)
376                 if (gdImageGetPixel(png_state.image, x2, y) != bg) { flag = 1; break; }
377             if (flag) break;
378         }
379         for (flag=0, y1=0; y1 < gdImageSY(png_state.image)-1; y1++) {
380             for (x=x1; x <= x2; x++)
381                 if (gdImageGetPixel(png_state.image, x, y1) != bg) { flag = 1; break; };
382             if (flag) break;
383         }
384         for (flag=0, y2=gdImageSY(png_state.image)-1; y2 >= y1; y2--) {
385             for (x=x1; x <= x2; x++)
386                 if (gdImageGetPixel(png_state.image, x, y2) != bg) { flag = 1; break; };
387             if (flag) break;
388         }
389         x = x2 - x1 + 1; /* width */
390         y = y2 - y1 + 1; /* height */
391 #if (GD2_VERS >= 2)
392         if (png_state.TrueColor)
393             im_crop = gdImageCreateTrueColor(x,y);
394         else
395             im_crop = gdImageCreate(x,y);
396         if (!im_crop) {
397             int_warn(NO_CARET,"libgd: failed to create cropped image structure");
398             return;
399         }
400         bg = gdImageColorAllocateAlpha(im_crop,255,255,255,127);
401 #else
402         im_crop = gdImageCreate(x,y);
403 #endif
404
405         gdImagePaletteCopy(im_crop, png_state.image);
406         if (png_state.flags & PNG_USE_TRANSPARENT) {
407             gdImageColorTransparent(im_crop, bg);
408             /* WARNING: This is a work-around for strangeness in libgd,  */
409             /* which doesn't copy transparent pixels in TrueColor images. */
410             if (png_state.TrueColor)
411                 gdImageColorTransparent(png_state.image, -1);
412         } else
413             gdImageColorTransparent(im_crop, -1);
414
415         gdImageCopy(im_crop, png_state.image, 0, 0, x1, y1, x, y);
416         gdImageDestroy(png_state.image);
417         png_state.image = im_crop;
418     }
419 }
420
421
422 static int PNG_FillSolid __PROTO((int fillpar));
423 static int PNG_FillPattern __PROTO((int fillpar));
424
425 static int
426 PNG_FillSolid(int fillpar)
427 {
428     int red   = (png_state.rgb >> 16) & 0xff;
429     int green = (png_state.rgb >> 8) & 0xff;
430     int blue  = png_state.rgb & 0xff;
431
432     double fact = (double)(100 - fillpar) * 0.01;
433
434     int color;
435
436     if (fact <= 0 || fact >= 1.0)
437         return png_state.color;
438
439     red   += (0xff - red) * fact;
440     green += (0xff - green) * fact;
441     blue  += (0xff - blue) * fact;
442
443     color = gdImageColorExact(png_state.image, red, green, blue);
444     if (color < 0) {
445         color = gdImageColorAllocate(png_state.image, red, green, blue);
446     }
447     if (color < 0) {
448         color = gdImageColorClosest(png_state.image, red, green, blue);
449     }
450
451     return color;
452 }
453
454 static int
455 PNG_FillPattern(int fillpar)
456 {
457     int rgb = png_state.rgb;
458     fillpar %= 8;
459
460     if (!PNG_fill_tile.im || rgb != PNG_fill_tile.last_rgb || PNG_fill_tile.fillpar != fillpar) {
461
462         int foreground, background;
463
464         if (PNG_fill_tile.im) {
465             gdImageDestroy(PNG_fill_tile.im);
466             PNG_fill_tile.im = (gdImagePtr)0;
467         }
468
469         /* save new values */
470         PNG_fill_tile.fillpar = fillpar;
471         PNG_fill_tile.last_rgb = rgb;
472
473         /* create new tile */
474         PNG_fill_tile.im = gdImageCreate(8, 8);
475         if (!PNG_fill_tile.im)
476             int_error(NO_CARET,"libgd: failed to create pattern-fill tile");
477
478         /* background */
479         background = gdImageColorAllocate(PNG_fill_tile.im, 255, 255, 255);
480         /* gdImageColorTransparent(PNG_fill_tile.im, background); */
481         gdImageFilledRectangle(PNG_fill_tile.im, 0, 0, 7, 7, background);
482
483         /* foreground */
484         foreground = gdImageColorAllocate(PNG_fill_tile.im,
485                 (rgb >> 16) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff);
486
487         switch (fillpar) {
488             case 0: /* no fill */
489             default:
490                 break;
491             case 1: /* cross-hatch */
492                 gdImageLine(PNG_fill_tile.im, 0, 0, 7, 7, foreground);
493                 gdImageLine(PNG_fill_tile.im, 0, 6, 6, 0, foreground);
494                 break;
495             case 2: /* double cross-hatch */
496                 gdImageLine(PNG_fill_tile.im, 0, 0, 7, 7, foreground);
497                 gdImageLine(PNG_fill_tile.im, 0, 6, 6, 0, foreground);
498                 gdImageLine(PNG_fill_tile.im, 0, 2, 2, 0, foreground);
499                 gdImageLine(PNG_fill_tile.im, 7, 3, 3, 7, foreground);
500                 gdImageLine(PNG_fill_tile.im, 4, 0, 7, 3, foreground);
501                 gdImageLine(PNG_fill_tile.im, 0, 4, 3, 7, foreground);
502                 break;
503             case 3: /* solid */
504                 gdImageFilledRectangle(PNG_fill_tile.im, 0, 0, 7, 7, foreground);
505                 break;
506             case 4:
507                 gdImageLine(PNG_fill_tile.im, 0, 0, 7, 7, foreground);
508                 break;
509             case 5:
510                 gdImageLine(PNG_fill_tile.im, 0, 7, 7, 0, foreground);
511                 break;
512             case 6:
513                 gdImageLine(PNG_fill_tile.im, 0, 0, 3, 7, foreground);
514                 gdImageLine(PNG_fill_tile.im, 4, 0, 7, 7, foreground);
515                 break;
516             case 7:
517                 gdImageLine(PNG_fill_tile.im, 0, 7, 3, 0, foreground);
518                 gdImageLine(PNG_fill_tile.im, 4, 7, 7, 0, foreground);
519                 break;
520             case 8:
521                 gdImageLine(PNG_fill_tile.im, 0, 0, 7, 3, foreground);
522                 gdImageLine(PNG_fill_tile.im, 0, 4, 7, 7, foreground);
523                 break;
524             case 9:
525                 gdImageLine(PNG_fill_tile.im, 0, 3, 7, 0, foreground);
526                 gdImageLine(PNG_fill_tile.im, 0, 7, 7, 4, foreground);
527                 break;
528         }
529     }
530
531     gdImageSetTile(png_state.image, PNG_fill_tile.im);
532     return (int)gdTiled;
533 }
534
535 static void
536 PNG_PointX(unsigned int x, unsigned int y)
537 {
538     gdImageLine(png_state.image, x - PNG_ps, y - PNG_ps,
539             x + PNG_ps, y + PNG_ps, png_state.color);
540     gdImageLine(png_state.image, x + PNG_ps, y - PNG_ps,
541             x - PNG_ps, y + PNG_ps, png_state.color);
542 }
543
544 static void
545 PNG_PointPlus(unsigned int x, unsigned int y)
546 {
547     gdImageLine(png_state.image, x - PNG_ps, y,
548             x + PNG_ps, y, png_state.color);
549     gdImageLine(png_state.image, x, y - PNG_ps,
550             x, y + PNG_ps, png_state.color);
551 }
552
553 static void
554 PNG_Triangle(
555     unsigned int x, unsigned int y,
556     int direction,
557     void (*draw_func)(gdImagePtr, gdPointPtr, int, int))
558 {
559     int delta  = (int)((1.33 * (double)PNG_ps) + 0.5);
560     int delta_ = (int)((0.67 * (double)PNG_ps) + 0.5);
561
562     gdPoint points[4];
563     points[0].x = x;
564     points[0].y = y - direction * delta;
565     points[1].x = x - delta;
566     points[1].y = y + direction * delta_;
567     points[2].x = x + delta;
568     points[2].y = y + direction * delta_;
569     points[3].x = points[0].x;
570     points[3].y = points[0].y;
571     draw_func(png_state.image, points, 4, png_state.color);
572 }
573
574 static void
575 PNG_Diamond(
576     unsigned int x, unsigned int y,
577     void (*draw_func)(gdImagePtr, gdPointPtr, int, int))
578 {
579     gdPoint points[5];
580     points[0].x = x;
581     points[0].y = y - PNG_ps;
582     points[1].x = x + PNG_ps;
583     points[1].y = y;
584     points[2].x = x;
585     points[2].y = y + PNG_ps;
586     points[3].x = x - PNG_ps;
587     points[3].y = y;
588     points[4].x = points[0].x;
589     points[4].y = points[0].y;
590     draw_func(png_state.image, points, 5, png_state.color);
591 }
592
593 /*
594  * _options()  Called when terminal type is selected.
595  * This procedure should parse options on the command line.  A list of the
596  * currently selected options should be stored in term_options[] in a form
597  * suitable for use with the set term command.  term_options[] is used by
598  * the save command.  Use options_null() if no options are available.
599  */
600 TERM_PUBLIC void
601 PNG_options()
602 {
603     struct value s;
604     int i;
605     char *string;
606     unsigned long color;
607     TBOOLEAN new_colors = FALSE;
608     TBOOLEAN gif_anim_option = FALSE; /* set to TRUE if an animated gif option given */
609
610     if (!PNG_initialized) {
611         PNG_initialized = TRUE;
612         term_options[0] = '\0';
613         term->h_char = PNG_HCHAR; /* Default to medium font */
614         png_state.default_font = gdfont;
615         png_state.n_colors = 0;
616         png_state.flags = 0;
617         png_state.ttffont = NULL;
618         png_state.default_ttffont = NULL;
619         png_state.default_ttfsize = 0;
620         png_state.justify = CENTRE;
621         png_state.TrueColor = FALSE;
622         PNG_linewidth_factor = 1.0;
623         png_state.capbutt = FALSE; /* to preserve previous default behavior */
624 #ifdef GD_NEED_LOCAL_FONT_POINTERS
625         gdFontSmall = gdFontGetSmall();
626         gdFontLarge = gdFontGetLarge();
627         gdFontMediumBold = gdFontGetMediumBold();
628         gdFontGiant = gdFontGetGiant();
629         gdFontTiny = gdFontGetTiny();
630 #endif
631     } else {
632         /* FIXME EAM - these should never happen! */
633         if (!png_state.default_font) {
634             fprintf(stderr,"gd.trm: caught initialization error\n");
635             png_state.default_font = gdfont;
636         }
637     }
638
639     /* Annoying hack to handle the case of 'set termoption' after */
640     /* we are already in animation mode.                          */
641     if (c_token == 2)
642         FPRINTF((stderr,"gif: Maintaining animation state\n"));
643     else {
644         /* Otherwise reset animation parameters */
645         if (png_state.previous_image)
646             gdImageDestroy(png_state.previous_image);
647         png_state.animate = FALSE;
648         png_state.previous_image = NULL;
649         png_state.frame_optimization = FALSE;
650         png_state.loop_count = 0;
651         /* And default font size */
652         term->h_char = PNG_HCHAR;
653         png_state.default_ttfsize = 0;
654     }
655
656     while (!END_OF_COMMAND) {
657         switch(lookup_table(&PNG_opts[0],c_token)) {
658         case PNG_TRANSPARENT:
659             png_state.flags |= PNG_USE_TRANSPARENT;
660             ++c_token;
661             break;
662         case PNG_NOTRANSPARENT:
663             png_state.flags &= ~PNG_USE_TRANSPARENT;
664             ++c_token;
665             break;
666         case PNG_INTERLACE:
667             png_state.flags |= PNG_USE_INTERLACE;
668             ++c_token;
669             break;
670         case PNG_NOINTERLACE:
671             png_state.flags &= ~PNG_USE_INTERLACE;
672             ++c_token;
673             break;
674         case PNG_CROP:
675             png_state.flags |= PNG_USE_CROP;
676             ++c_token;
677             break;
678         case PNG_NOCROP:
679             png_state.flags &= ~PNG_USE_CROP;
680             ++c_token;
681             break;
682         case PNG_TINY:
683 #ifdef HAVE_GD_TTF
684 # define UNSET_TTF_FONT \
685             free(png_state.ttffont); \
686             png_state.ttffont = NULL; \
687             png_state.default_ttfsize = 2 * term->h_char - 2;
688 #else
689 # define UNSET_TTF_FONT \
690             ; /* nothing to do */
691 #endif
692             png_state.default_font=gdFontTiny;
693             term->v_char = png_state.default_font->h;
694             term->h_char = png_state.default_font->w;
695             ++c_token;
696             UNSET_TTF_FONT;
697             break;
698         case PNG_SMALL:
699             png_state.default_font = gdFontSmall;
700             term->v_char = png_state.default_font->h;
701             term->h_char = png_state.default_font->w;
702             ++c_token;
703             UNSET_TTF_FONT;
704             break;
705         case PNG_MEDIUM:
706             png_state.default_font = gdFontMediumBold;
707             term->v_char = png_state.default_font->h;
708             term->h_char = png_state.default_font->w;
709             ++c_token;
710             UNSET_TTF_FONT;
711             break;
712         case PNG_LARGE:
713             png_state.default_font = gdFontLarge;
714             term->v_char = png_state.default_font->h;
715             term->h_char = png_state.default_font->w;
716             ++c_token;
717             UNSET_TTF_FONT;
718             break;
719         case PNG_GIANT:
720             png_state.default_font=gdFontGiant;
721             term->v_char = png_state.default_font->h;
722             term->h_char = png_state.default_font->w;
723             ++c_token;
724             UNSET_TTF_FONT;
725             break;
726         case PNG_FONT:
727             c_token++;
728 #ifdef HAVE_GD_TTF
729             if (END_OF_COMMAND) {
730                 free(png_state.ttffont);
731                 png_state.ttffont = NULL;
732                 png_state.default_ttfsize = 0;
733             } else {
734                 int brect[8];
735                 char *err;
736
737                 if (isstringvalue(c_token)) {
738                     char *s = try_to_get_string();
739                     char *comma = strrchr(s,',');
740                     double fontsize;
741                     if (comma && (1 == sscanf(comma+1,"%lf",&fontsize))) {
742                         png_state.default_ttfsize = (int)(fontsize+0.5);
743                         png_state.ttfsize = png_state.default_ttfsize;
744                         *comma = '\0';
745                     }
746                     if (*s) {
747                         free(png_state.ttffont);
748                         png_state.ttffont = s;
749                     } else {
750                         continue;
751                     }
752                 } else {
753                     free(png_state.ttffont);
754                     png_state.ttffont = gp_alloc(token_len(c_token)+1,"new font");
755                     copy_str(png_state.ttffont, c_token, token_len(c_token)+1);
756                     c_token++;
757                 }
758                 free(png_state.default_ttffont);
759                 png_state.default_ttffont = gp_strdup(png_state.ttffont);
760                 err = gdImageStringFT(NULL, &brect[0], 0,
761                         png_state.ttffont, (double)png_state.ttfsize,
762                         0.0, 0, 0, "test");
763                 if (err) {
764                         fprintf(stderr,
765                                 "%s when opening font %s, trying default\n",
766                                 err, png_state.ttffont);
767                         free(png_state.ttffont);
768                         free(png_state.default_ttffont);
769                         png_state.ttffont = NULL;
770                         png_state.default_ttffont = NULL;
771                 }
772             }
773 #else
774             c_token++;
775             fprintf(stderr,"No TTF font support, using internal non-scalable font\n");
776 #endif
777             break;
778         case PNG_SIZE:
779             c_token++;
780             if (END_OF_COMMAND) {
781                 PNG_XMAX = GREG_XMAX;
782                 PNG_YMAX = GREG_YMAX;
783                 PNG_explicit_size = FALSE;
784             } else {
785                 PNG_XMAX = real(const_express(&s));
786                 if (equals(c_token, ",")) {
787                     c_token++;
788                     PNG_YMAX = real(const_express(&s));
789                 }
790                 if (PNG_XMAX < 0)
791                     PNG_XMAX = GREG_XMAX;
792                 if (PNG_YMAX < 0)
793                     PNG_YMAX = GREG_YMAX;
794                 PNG_explicit_size = TRUE;
795             }
796             term->ymax = PNG_YMAX;
797             term->xmax = PNG_XMAX;
798             /* EAM Apr 2003 - same tic size on both x and y axes */
799             term->v_tic = (PNG_XMAX < PNG_YMAX) ? PNG_XMAX/100 : PNG_YMAX/100;
800             if (term->v_tic < 1)
801                 term->v_tic = 1;
802             term->h_tic = term->v_tic;
803             break;
804         case PNG_ENHANCED:
805             term->flags |= TERM_ENHANCED_TEXT;
806             term->put_text = ENHGD_put_text;
807             ++c_token;
808             break;
809         case PNG_NOENHANCED:
810             term->flags &= ~TERM_ENHANCED_TEXT;
811             term->put_text = PNG_put_text;
812             ++c_token;
813             break;
814         case PNG_TRUECOLOR:
815             png_state.TrueColor = TRUE;
816             c_token++;
817             break;
818         case PNG_NOTRUECOLOR:
819             png_state.TrueColor = FALSE;
820             c_token++;
821             break;
822         case PNG_LINEWIDTH:
823             c_token++;
824             PNG_linewidth_factor = real(const_express(&s));
825             if (PNG_linewidth_factor < 0)
826                 PNG_linewidth_factor = 1.0;
827             break;
828
829         /* parse gif animation options */
830         case GIF_ANIMATE:
831             if (strncmp("gif",term->name,3))
832                 int_error(c_token,"Only the gif terminal supports animation");
833             c_token++;
834             png_state.animate = TRUE;
835             png_state.frame_count = 0;
836             png_state.frame_delay = 10;
837             png_state.frame_optimization = FALSE;
838             gif_anim_option = 1;
839             break;
840         case GIF_DELAY:
841             if (strncmp("gif",term->name,3))
842                 int_error(c_token,"Only the gif terminal supports animation");
843             c_token++;
844             png_state.frame_delay = (int)real(const_express(&s));
845             if (png_state.frame_delay <= 0)
846                 png_state.frame_delay = 10;
847             gif_anim_option = 1;
848             break;
849         case GIF_LOOP:
850             if (strncmp("gif",term->name,3))
851                 int_error(c_token,"Only the gif terminal supports animation");
852             c_token++;
853             png_state.loop_count = (int)real(const_express(&s));
854             gif_anim_option = 1;
855             break;
856         case GIF_NOOPT:
857             if (strncmp("gif",term->name,3))
858                 int_error(c_token,"Only the gif terminal supports animation");
859             c_token++;
860             png_state.frame_optimization = FALSE;
861             gif_anim_option = 1;
862             break;
863         case GIF_OPT:
864             if (strncmp("gif",term->name,3))
865                 int_error(c_token,"Only the gif terminal supports animation");
866             c_token++;
867             png_state.frame_optimization = TRUE;
868             gif_anim_option = 1;
869             break;
870
871         case PNG_BUTT:
872             png_state.capbutt = TRUE;
873             c_token++;
874             break;
875
876         case PNG_ROUNDED:
877             png_state.capbutt = FALSE;
878             c_token++;
879             break;
880
881         case PNG_OTHER:
882         default:
883             /* not "size" */
884             string = gp_input_line + token[c_token].start_index;
885
886 #ifdef HAVE_GD_TTF
887             /* Check for explicit TTF font size */
888             if (sscanf(string, "%d", &i) == 1) {
889                 if (i > 0 && i < 999)
890                     png_state.default_ttfsize = i;
891                 else
892                     int_warn(c_token,"illegal font size");
893                 ++c_token;
894                 break;
895             }
896 #endif
897
898             if (sscanf(string, "x%lx", &color) != 1) {
899                 int_error(c_token, "invalid color spec, must be xRRGGBB");
900             } else if (png_state.n_colors == PNG_MAX_COLORS && new_colors) {
901                 int_warn(c_token, "too many colors, ignoring");
902                 ++c_token;
903             } else {
904                 if (!new_colors) {
905                     new_colors = TRUE;
906                     png_state.n_colors = 0;
907                 }
908                 png_state.rgb_table[png_state.n_colors++] = color;
909                 ++c_token;
910             }
911             break;
912         }
913     }
914
915 #ifndef GIF_ANIMATION /* animated gifs not supported by the current GD library */
916     if (gif_anim_option) {
917         png_state.animate = FALSE;
918         int_warn(NO_CARET, "gif animation options ignored (not compiled into this binary)");
919     }
920 #endif
921
922 #ifdef HAVE_GD_TTF
923     /* If no font has been chosen but there is a default, use it */
924     if (!png_state.ttffont) {
925         char *external_default = getenv("GNUPLOT_DEFAULT_GDFONT");
926         int brect[8];
927         char *err;
928
929         if (external_default)
930             png_state.ttffont = gp_strdup(external_default);
931         else    /* Might as well try some plausible font; it's no worse than failing immediately */
932             png_state.ttffont = gp_strdup("arial");
933
934         free(png_state.default_ttffont);
935         png_state.default_ttffont = gp_strdup(png_state.ttffont);
936         png_state.default_ttfsize = 2 * term->h_char - 2;
937         err = gdImageStringFT(NULL, &brect[0], 0,
938                 png_state.ttffont, (double)png_state.default_ttfsize,
939                 0.0, 0, 0, "test");
940         if (err) {
941                 fprintf(stderr,"%s when opening font \"%s\", using internal non-scalable font\n",
942                         err, png_state.ttffont);
943                 free(png_state.ttffont);
944                 free(png_state.default_ttffont);
945                 png_state.ttffont = NULL;
946                 png_state.default_ttffont = NULL;
947         }
948     }
949
950     /* If no explicit TTF font size found, generate default */
951     if (png_state.default_ttfsize == 0)
952         png_state.default_ttfsize = 2 * term->h_char - 2;
953     png_state.ttfsize = png_state.default_ttfsize;
954
955     /* Find approximate character width of selected TTF font   */
956     /* This is needed in order to set appropriate border width */
957     if (png_state.default_ttffont) {
958         int brect[8];
959         char *err;
960         err = gdImageStringFT(NULL, &brect[0], 0,
961                 png_state.default_ttffont, (double)png_state.default_ttfsize,
962                 0.0, 0, 0, "f00000000g");
963         if (!err) {
964             term->h_char = .11 * (float)(brect[2] - brect[0]) + 0.5;
965             term->v_char = 1.1 * (float)(brect[1] - brect[7]) + 0.5;
966         }
967     }
968 #endif
969
970     /* This code is shared by png, gif, and jpeg terminal types */
971     if (!strcmp(term->name,"jpeg"))
972         png_state.flags &= ~PNG_USE_TRANSPARENT;
973
974     /* now generate options string */
975
976     if (png_state.flags & PNG_USE_TRANSPARENT) {
977         strcat(term_options, "transparent ");
978     }
979     if (png_state.flags & PNG_USE_INTERLACE) {
980         strcat(term_options, "interlace ");
981     }
982     /* JPEG files are always 24-bit color */
983     if (strcmp(term->name, "jpeg") == 0)
984         png_state.TrueColor = TRUE;
985     else if (png_state.TrueColor) {
986         strcat(term_options, "truecolor ");
987     }
988     if (!(png_state.flags & PNG_USE_CROP)) {
989         strcat(term_options, "no");
990     }
991     strcat(term_options, "crop ");
992
993     if (term->flags & TERM_ENHANCED_TEXT) {
994         strcat(term_options, "enhanced ");
995     }
996
997     if (png_state.ttffont) {
998         sprintf(term_options + strlen(term_options),
999                 "font %s %d ", png_state.ttffont, png_state.ttfsize);
1000     } else switch (term->h_char) {
1001     case 5:
1002         strcat(term_options,"tiny ");
1003         break;
1004     case 6:
1005         strcat(term_options, "small ");
1006         break;
1007     case 7:
1008     default:
1009         strcat(term_options, "medium ");
1010         break;
1011     case 8:
1012         strcat(term_options, "large ");
1013         break;
1014     case 9:
1015         strcat(term_options,"giant ");
1016         break;
1017     }
1018
1019     if (PNG_linewidth_factor != 1.0)
1020         sprintf(term_options + strlen(term_options),
1021             "linewidth %3.1f ", PNG_linewidth_factor);
1022
1023     if (png_state.capbutt) {
1024         sprintf(term_options + strlen(term_options),
1025             "butt ");
1026     }
1027
1028     if (png_state.animate) {
1029         sprintf(term_options + strlen(term_options),
1030             "animate delay %d loop %d %soptimize ", 
1031             png_state.frame_delay, png_state.loop_count,
1032             png_state.frame_optimization ? "" : "no");
1033     }
1034
1035     if (PNG_explicit_size)
1036         sprintf(term_options + strlen(term_options),
1037             "size %d,%d ", PNG_XMAX, PNG_YMAX);
1038
1039     for (i = 0; strlen(term_options) + 9 < MAX_LINE_LEN &&
1040            i < png_state.n_colors; i++) {
1041         sprintf(term_options + strlen(term_options),
1042                 "x%06x ", png_state.rgb_table[i]);
1043     }
1044 }
1045
1046
1047 /*
1048  * _init()  Called once, when the device is first selected.  This procedure
1049  * should set up things that only need to be set once, like handshaking and
1050  * character sets etc...
1051  */
1052 TERM_PUBLIC void
1053 PNG_init()
1054 {
1055 int i;
1056
1057     png_state.linetype = 0;
1058     png_state.linewidth = 1;
1059     /* EAM - pre-define brushes to implement linewidth */
1060     for (i=2; i<=MAXLINEWIDTH; i++) {
1061         if (!((PNG_brush[i].im = gdImageCreate(i,i))))
1062             int_error(NO_CARET,"libgd: failed to create brush structure");
1063         PNG_brush[i].bgnd = gdImageColorAllocate( PNG_brush[i].im, 255, 255, 255 );
1064         gdImageFill( PNG_brush[i].im, 0, 0, PNG_brush[i].bgnd );
1065         gdImageColorTransparent( PNG_brush[i].im, PNG_brush[i].bgnd );
1066         PNG_brush[i].fgnd = gdImageColorAllocate( PNG_brush[i].im, 0, 0, 0 );
1067         PNG_brush[i].last_rgb = -99;  /* invalid index will force update on first use */
1068     }
1069     /* EAM - quick and dirty is to fill the entire brush (square nib).    */
1070     /*       might be better to approximate a circular nib by selectively */
1071     /*       coloring the individual pixels of the brush image.           */
1072     for (i=2; i<=MAXLINEWIDTH; i++) {
1073         gdImageFilledRectangle( PNG_brush[i].im, 0, 0, i-1, i-1, PNG_brush[i].fgnd );
1074     }
1075
1076 }
1077
1078 /*
1079  * _reset()  Called when gnuplot is exited, the output device changed or
1080  * the terminal type changed.  This procedure should reset the device,
1081  * possibly flushing a buffer somewhere or generating a form feed.
1082  */
1083 TERM_PUBLIC void
1084 PNG_reset()
1085 {
1086     int i;
1087     /* EAM - Clean up the brushes used for linewidth */
1088     for (i=2; i<=MAXLINEWIDTH; i++) {
1089         if (PNG_brush[i].im)
1090             gdImageDestroy(PNG_brush[i].im);
1091         PNG_brush[i].im = 0;
1092     }
1093     if (PNG_fill_tile.im) {
1094         gdImageDestroy(PNG_fill_tile.im);
1095         PNG_fill_tile.im = (gdImagePtr)0;
1096     }
1097 #ifdef GIF_ANIMATION
1098     if (png_state.animate) {
1099         gdImageGifAnimEnd(gpoutfile);
1100         png_state.frame_count = 0;
1101         png_state.animate = FALSE;
1102         fprintf(stderr,"End of animation sequence\n");
1103     }
1104 #endif
1105 }
1106
1107 #if 0
1108 /* use  #if 1  that's just for debugging */
1109 void
1110 PNG_show_current_palette()
1111 {
1112     int i;
1113
1114     fprintf(stderr, "*****\n SHOW THE PALETTE! total=%i\n",
1115             gdImageColorsTotal(png_state.image));
1116     for (i=0; i < gdImageColorsTotal(png_state.image); i++) {
1117         /* Use access macros to learn colors. */
1118         fprintf(stderr, "%i\tr=%d\t g=%d\tb=%d\n",
1119                 i,
1120                 gdImageRed(png_state.image,i),
1121                 gdImageGreen(png_state.image,i),
1122                 gdImageBlue(png_state.image,i));
1123     }
1124 }
1125 #endif
1126
1127 /*
1128 How this works: Gray interval [0;1] will be mapped to interval
1129 [0;sm_palette.colors-1] those r,g,b components are mapped by the array
1130 below palette.offset equals 0 since png_smooth_color[0..colors] are
1131 from ColorAllocate
1132 */
1133 static int png_smooth_color[gdMaxColors];
1134
1135 #if (0)
1136 /*
1137  * This is only needed in order to maintain png_state_rgb for pattern-fill,
1138  * and only for palette-based coloring. It doesn't seem worth the space or effort.
1139  * EAM November 2004
1140  */
1141 static int png_smooth_rgb[gdMaxColors];
1142 #endif
1143
1144   /* TODO: how to recover from a multiplot with two colour pm3d maps?
1145      They must use the same palette! Or palette size must be
1146      restricted to certain number of colours---a new user's option
1147   */
1148
1149 TERM_PUBLIC int PNG_make_palette (t_sm_palette *palette)
1150 {
1151     int i;
1152     if (palette == NULL) {
1153         /* If the output format is TrueColor there in no color limit */
1154         if (png_state.TrueColor)
1155             return(0);
1156
1157         /* return maximal number of colours in a PNG palette */
1158         i = gdMaxColors /*256*/ - gdImageColorsTotal(png_state.image);
1159         /* the latter is the number of currently allocated colours. We want
1160            to allocate the rest */
1161         /*BACK PLEASE  fprintf(stderr,"colors in PNG palette=%i\n",(int)gdMaxColors); */
1162         if (i == 0) {
1163             i = (sm_palette.colors <= 0) ? -1 : sm_palette.colors;
1164                 /* (no more colorus) : (previous palette (obviously multiplot mode)) */
1165 #if 0
1166             if (i > 0) fprintf(stderr,"reusing it again\n");
1167 #endif
1168         }
1169         return i;
1170     }
1171     if (0 == gdMaxColors /*256*/ - gdImageColorsTotal(png_state.image))
1172         return 0; /* reuse previous palette (without warning) */
1173     for (i = 0; i < sm_palette.colors; i++) {
1174         png_smooth_color[i] = gdImageColorAllocate(png_state.image,
1175             (int)( palette->color[i].r * 255 + 0.5 ), /* r,g,b values for png */
1176             (int)( palette->color[i].g * 255 + 0.5 ), /* terminal are [0;255] */
1177             (int)( palette->color[i].b * 255 + 0.5 ) );
1178 #if (0)
1179         png_smooth_rgb[i] = (((int)(palette->color[i].r * 255.) & 0xff) << 16)
1180                           + (((int)(palette->color[i].g * 255.) & 0xff) << 8)
1181                           +  ((int)(palette->color[i].b * 255.) & 0xff);
1182 #endif
1183         if (png_smooth_color[i] < 0) { /* this should never happen! take away? */
1184             FPRINTF((stderr,"png_smooth_color[i]<0 cannot happen"));
1185             exit(1);
1186         }
1187 #if 0
1188         fprintf(stderr,"ALLOCATED: i=%i\t=> pal_index=%i\tr=%g g=%g b=%g\n",
1189             i, png_smooth_color[i],
1190             palette->color[i].r, palette->color[i].g, palette->color[i].b );
1191 #endif
1192     }
1193     return 0;
1194 }
1195
1196
1197 TERM_PUBLIC
1198 void PNG_set_color (t_colorspec *colorspec)
1199 {
1200     double gray = colorspec->value;
1201
1202     if (colorspec->type == TC_LT) {
1203         int savetype = png_state.linetype;
1204         PNG_linetype(colorspec->lt);
1205         /* Harmless now; will be needed if we ever support dot/dash */
1206         png_state.linetype = savetype;
1207     }
1208
1209     if (colorspec->type == TC_RGB) {
1210         png_state.rgb = colorspec->lt;
1211         png_state.color = gdImageColorResolve(png_state.image,
1212             colorspec->lt >> 16, (colorspec->lt >> 8) & 0xff, colorspec->lt & 0xff);
1213     }
1214
1215     if (colorspec->type != TC_FRAC)
1216         return;
1217
1218     if (png_state.TrueColor) {
1219         rgb255_color color;
1220         rgb255maxcolors_from_gray(gray, &color);
1221         png_state.color = gdImageColorResolve(png_state.image,
1222             (int)color.r, (int)color.g, (int)color.b);
1223         png_state.rgb = (color.r << 16) + (color.g << 8) +color.b;
1224         return;
1225     } else {
1226         int png_color = (gray <= 0) ? 0 : (int)(gray * sm_palette.colors);
1227         if (png_color >= sm_palette.colors)
1228             png_color = sm_palette.colors - 1;
1229         /* map [0;1] to interval [0;png_smooth_colors-1] */
1230         png_state.color = png_smooth_color[ png_color ];
1231 #if (0)
1232         png_state.rgb = png_smooth_rgb[ png_color ];
1233 #endif
1234     }
1235 }
1236
1237 TERM_PUBLIC
1238 void PNG_filled_polygon(int points, gpiPoint *corners)
1239 {
1240     int i;
1241     int fillpar = corners->style >> 4;
1242     int color = png_state.color;
1243     
1244     /* since gpiPoint carries more than just x and y if
1245      * we have EXTENDED_COLOR_SPECS defined, we need to
1246      * copy it to the gdPointPtr struct; make it static
1247      * so that is faster (joze) */
1248     static gdPointPtr gd_corners = (gdPointPtr) 0;
1249     static unsigned int size = 0;
1250     if (points > size) {
1251         size = points;
1252         gd_corners = gp_realloc(gd_corners, sizeof(gdPoint) * size,
1253             "PNG_filled_polygon->gd_corners");
1254     }
1255     for (i = 0; i < points; i++) {
1256         gd_corners[i].x = corners[i].x;
1257         gd_corners[i].y = Y(corners[i].y);
1258     }
1259
1260     switch (corners->style & 0xf) {
1261         case FS_EMPTY: /* fill with background color */
1262             color = png_state.color_table[0];
1263             break;
1264         case FS_SOLID: /* solid fill */
1265             color = PNG_FillSolid(fillpar);
1266             break;
1267         case FS_PATTERN: /* pattern fill */
1268             color = PNG_FillPattern(fillpar);
1269             break;
1270         default:
1271             color = png_state.color;
1272             break;
1273     }
1274
1275     gdImageFilledPolygon(png_state.image, gd_corners, points, color);
1276
1277     /* easy, since gdPointPtr is the same as (gdiPoint*) */
1278     /* if someone someday needs this routine to be NON-DESTRUCTIVE, then change
1279        the following line to #if 1 */
1280 #if 0
1281     for (i = 0; i < points; i++)
1282         corners[i].y = Y(corners[i].y);
1283 #endif
1284 }
1285
1286 /*
1287  * This function is used for filledboxes
1288  * style parameter is some garbled hash combining fillstyle and filldensity
1289  */
1290 TERM_PUBLIC void
1291 PNG_boxfill(
1292     int style,
1293     unsigned int x, unsigned int y,
1294     unsigned int width, unsigned int height)
1295 {
1296     unsigned int x1, y1, x2, y2;
1297     int          color;
1298
1299     /* fillpar:
1300      * - solid   : 0 - 100
1301      * - pattern : 0 - 100
1302      */
1303     int fillpar = style >> 4;
1304
1305     style &= 0xf;
1306
1307     switch (style) {
1308         case FS_EMPTY: /* fill with background color */
1309             color = png_state.color_table[0];
1310             break;
1311         case FS_SOLID: /* solid fill */
1312             color = PNG_FillSolid(fillpar);
1313             break;
1314         case FS_PATTERN: /* pattern fill */
1315             color = PNG_FillPattern(fillpar);
1316             break;
1317         default:
1318             /* should never happen */
1319             color = png_state.color;
1320             break;
1321     }
1322
1323     x1 = x;
1324     x2 = x + width - 1;
1325     y2 = Y(y);
1326     y1 = y2 - height + 1;
1327     gdImageFilledRectangle(png_state.image, x1, y1, x2, y2, color);
1328 }
1329
1330 /*
1331  * _graphics()  Called just before a plot is going to be displayed.  This
1332  * procedure should set the device into graphics mode.  Devices which can't
1333  * be used as terminals (like plotters) will probably be in graphics mode
1334  * always and therefore won't need this.
1335  */
1336 TERM_PUBLIC void
1337 PNG_graphics()
1338 {
1339     int i;
1340     unsigned int rgb;
1341     double xscale = 1.;
1342     double yscale = 1.;
1343
1344 #ifdef BACKWARDS_COMPATIBLE
1345     /* We are deprecating the use of 'set size' to change the actual    */
1346     /* canvas size, in this case the size of the output file in pixels. */
1347     if (!PNG_explicit_size) {
1348         xscale *= xsize;
1349         yscale *= ysize;
1350     }
1351 #endif
1352
1353 #if (GD2_VERS >= 2)
1354     /* TrueColor images default to a black background; load white instead */
1355     if (png_state.TrueColor) {
1356         png_state.image = gdImageCreateTrueColor(
1357                         (int) (xscale * PNG_XMAX), (int) (yscale * PNG_YMAX));
1358         if (!png_state.image)
1359             int_error(NO_CARET,"libgd: failed to create output image structure");
1360         rgb = gdImageColorAllocate(png_state.image, 255, 255, 255);
1361         gdImageFill(png_state.image, 1, 1, rgb);
1362     } else
1363 #endif
1364         png_state.image = gdImageCreate(
1365                         (int) (xscale * PNG_XMAX), (int) (yscale * PNG_YMAX));
1366         if (!png_state.image)
1367             int_error(NO_CARET,"libgd: failed to create output image structure");
1368
1369     png_state.height = (yscale * PNG_YMAX) - 1;
1370     png_state.charw = term->h_char;     /* png_state.font->w; */
1371     png_state.charh = term->v_char;     /* png_state.font->h; */
1372     png_state.font = png_state.default_font;
1373     png_state.color = 0;
1374     for (i = png_state.n_colors; i < WEB_N_COLORS; i++)
1375         png_state.rgb_table[i] =
1376             (web_color_rgbs[i].r << 16) |
1377             (web_color_rgbs[i].g << 8) |
1378             web_color_rgbs[i].b;
1379     if (png_state.n_colors < WEB_N_COLORS)
1380         png_state.n_colors = WEB_N_COLORS;
1381     for (i = 0; i < png_state.n_colors; i++) {
1382         rgb = png_state.rgb_table[i];
1383         png_state.color_table[i] =
1384             gdImageColorAllocate(png_state.image, (rgb >> 16) & 0xff,
1385                                  (rgb >> 8) & 0xff, rgb & 0xff);
1386     }
1387     if (png_state.flags & PNG_USE_TRANSPARENT)
1388         gdImageColorTransparent(png_state.image,
1389                                 png_state.color_table[0]);
1390     else
1391         gdImageColorTransparent(png_state.image, -1);
1392
1393 }
1394
1395 /*
1396  * _text()  Called immediately after a plot is displayed.  This procedure
1397  * should set the device back into text mode if it is also a terminal, so
1398  * that commands can be seen as they're typed.  Again, this will probably
1399  * do nothing if the device can't be used as a terminal.
1400  */
1401 TERM_PUBLIC void
1402 PNG_text()
1403 {
1404     image_do_crop();
1405     if (png_state.flags & PNG_USE_INTERLACE)
1406         gdImageInterlace(png_state.image, 1);
1407     gdImagePng(png_state.image, gpoutfile);
1408     gdImageDestroy(png_state.image);
1409 }
1410
1411 /* _move(x,y)  Called at the start of a line.  The cursor should move to the
1412  * (x,y) position without drawing.
1413  */
1414 TERM_PUBLIC void
1415 PNG_move(unsigned int x, unsigned int y)
1416 {
1417     png_state.x = x;
1418     png_state.y = y;
1419 }
1420
1421 /* _vector(x,y)  Called when a line is to be drawn.  This should display a line
1422  * from the last (x,y) position given by _move() or _vector() to this new (x,y)
1423  * position.
1424  */
1425 TERM_PUBLIC void
1426 PNG_vector(unsigned int x, unsigned int y)
1427 {
1428     int lw;
1429
1430     if (png_state.linetype == -1) {
1431         int png_linetype_dotted[5];
1432         png_linetype_dotted[0] = png_state.color;
1433         png_linetype_dotted[1] = png_state.color;
1434         png_linetype_dotted[2] = gdTransparent;
1435         png_linetype_dotted[3] = gdTransparent;
1436         png_linetype_dotted[4] = gdTransparent;
1437
1438         gdImageSetStyle(png_state.image, png_linetype_dotted, 5);
1439         gdImageLine(png_state.image, png_state.x, Y(png_state.y),
1440                     x, Y(y), gdStyled);
1441     } else {
1442         if (png_state.linewidth == 1) {
1443 #if (GD2_VERS >= 2) && defined(gdAntiAliased) 
1444             gdImageSetThickness(png_state.image,1);
1445             gdImageSetAntiAliased(png_state.image, png_state.color);
1446             gdImageLine(png_state.image, png_state.x, Y(png_state.y),
1447                         x, Y(y), gdAntiAliased);
1448 #else
1449             gdImageLine(png_state.image, png_state.x, Y(png_state.y),
1450                         x, Y(y), png_state.color);
1451 #endif
1452 #if (GD2_VERS >= 2)
1453         } else if (png_state.capbutt){
1454
1455             gdImageSetThickness(png_state.image,png_state.linewidth);
1456             gdImageLine(png_state.image, png_state.x, Y(png_state.y),
1457                         x, Y(y), png_state.color);
1458 #endif
1459         }else{
1460
1461             /* EAM - Implement linewidth by selecting a pre-defined brush */
1462             /* The tricky bit is re-coloring the brush to the current color */
1463             lw   = png_state.linewidth;
1464             if (png_state.color != PNG_brush[lw].last_rgb) {
1465                 PNG_brush[lw].fgnd = gdImageColorResolve(PNG_brush[lw].im,
1466                     gdImageRed(png_state.image,png_state.color),
1467                     gdImageGreen(png_state.image,png_state.color),
1468                     gdImageBlue(png_state.image,png_state.color) );
1469                 PNG_brush[lw].last_rgb = png_state.color;
1470             }
1471             gdImageFilledRectangle( PNG_brush[lw].im, 0, 0, lw-1, lw-1, PNG_brush[lw].fgnd );
1472             gdImageSetBrush(png_state.image, PNG_brush[lw].im);
1473             gdImageLine(png_state.image, png_state.x, Y(png_state.y),
1474                         x, Y(y), gdBrushed );
1475         }
1476     }
1477     png_state.x = x;
1478     png_state.y = y;
1479 }
1480
1481 /* _linetype(lt)  Called to set the line type before text is displayed or
1482  * line(s) plotted.
1483  * Negative linetypes are defined in gadgets.h
1484  * lt 0 and upwards are used for plots 0 and upwards.
1485  * If _linetype() is called with lt greater than the available line types,
1486  * it should map it to one of the available line types.
1487  */
1488 TERM_PUBLIC void
1489 PNG_linetype(int type)
1490 {
1491     if (type >= (png_state.n_colors - 3))
1492         type %= (png_state.n_colors - 3);
1493     if (type <= LT_BACKGROUND) /* LT_NODRAW, LT_BACKGROUND, LT_UNDEFINED */
1494         type = -3;      /* Draw in background color */
1495
1496     png_state.color = png_state.color_table[type + 3];
1497     png_state.rgb = png_state.rgb_table[type + 3];
1498     png_state.linetype = type;
1499 }
1500
1501 /* Use the "brush" tools in the gd library to control line width.
1502  * Pre-define brushes for linewidths 2, 3, 4, 5, 6 (1 doesn't need a brush!).
1503  * Here we just remember the state.
1504  */
1505 TERM_PUBLIC void
1506 PNG_linewidth(double linewidth)
1507 {
1508     png_state.linewidth = (int)(PNG_linewidth_factor * linewidth+0.49);
1509     if (png_state.linewidth > MAXLINEWIDTH) png_state.linewidth = MAXLINEWIDTH;
1510     if (png_state.linewidth < 1) png_state.linewidth = 1;
1511 }
1512
1513 /* _put_text(x,y,str)  Called to display text at the (x,y) position,
1514  * while in graphics mode.   The text should be vertically (with respect
1515  * to the text) justified about (x,y).  The text is rotated according
1516  * to _text_angle and then horizontally (with respect to the text)
1517  * justified according to _justify_text.
1518  */
1519 #ifdef HAVE_GD_TTF
1520 TERM_PUBLIC void
1521 PNG_put_text(unsigned int x, unsigned int y, const char *string)
1522 {
1523     if (png_state.ttffont) {
1524         int brect[8]; char *err;
1525         /* Draw once with a NULL image to get the bounding rectangle */
1526         /* then draw it again, centered.                             */
1527         err = gdImageStringFT(NULL, brect, png_state.color,
1528                         png_state.ttffont, (double)png_state.ttfsize,
1529                         (double)png_state.angle * M_PI_2 / 90. ,
1530                         x, Y(y), (char *)string);
1531         if (err) {
1532             fprintf(stderr,"gdImageStringFT: %s while printing string %s with font %s\n",
1533                 err,string,png_state.ttffont);
1534         } else {
1535             x += sin((double)png_state.angle * M_PI_2/90.) * (double)png_state.charh/4.;
1536             y -= cos((double)png_state.angle * M_PI_2/90.) * (double)png_state.charh/4.;
1537             switch (png_state.justify) {
1538                 case RIGHT:
1539                                 x -= (brect[2]-brect[0]);
1540                                 y += (brect[3]-brect[1]);
1541                                 break;
1542                 case CENTRE:
1543                                 x -= (brect[2]-brect[0]) / 2.;
1544                                 y += (brect[3]-brect[1]) / 2.;
1545                                 break;
1546                 case LEFT:
1547                 default:        break;
1548             }
1549             err = gdImageStringFT(png_state.image, brect, png_state.color,
1550                         png_state.ttffont, (double)png_state.ttfsize,
1551                         (double)png_state.angle * M_PI_2 / 90.,
1552                         x, Y(y), (char *)string);
1553             if (err)
1554                 fprintf(stderr,"gdImageStringFT: %s while printing string %s with font %s\n",
1555                     err,string,png_state.ttffont);
1556         }
1557     } else if (png_state.angle != 0) {
1558         x -= png_state.charh / 2;
1559         switch (png_state.justify) {
1560             case RIGHT: y -= png_state.charw * strlen(string);
1561                         break;
1562             case CENTRE:y -= png_state.charw * strlen(string) / 2;
1563                         break;
1564             case LEFT:
1565             default:    break;
1566         }
1567         gdImageStringUp(png_state.image, png_state.font,
1568                         x, Y(y),
1569                         (unsigned char *)string, png_state.color);
1570     } else {
1571         y += png_state.charh / 2;
1572         switch (png_state.justify) {
1573             case RIGHT: x -= png_state.charw * strlen(string);
1574                         break;
1575             case CENTRE:x -= png_state.charw * strlen(string) / 2;
1576                         break;
1577             case LEFT:
1578             default:    break;
1579         }
1580         gdImageString(png_state.image, png_state.font,
1581                       x, Y(y),
1582                       (unsigned char *)string, png_state.color);
1583     }
1584 }
1585
1586 #else  /* not HAVE_GD_TTF */
1587
1588 TERM_PUBLIC void
1589 PNG_put_text(unsigned int x, unsigned int y, const char *string)
1590 {
1591     if (png_state.angle == 0) {
1592         y += png_state.charh / 2;
1593         gdImageString(png_state.image, png_state.font,
1594                       x, Y(y),
1595                       (unsigned char *)string, png_state.color);
1596     } else {
1597         x -= png_state.charh / 2;
1598         gdImageStringUp(png_state.image, png_state.font,
1599                         x, Y(y),
1600                         (unsigned char *)string, png_state.color);
1601     }
1602 }
1603
1604 #endif /* HAVE_GD_TTF */
1605
1606
1607 TERM_PUBLIC int
1608 PNG_text_angle(int ang)
1609 {
1610     while (ang < -180) ang += 360;      /* Should not be needed, but reported to */
1611     while (ang > 180) ang -= 360;       /* avoid a bug in some libgd versions    */
1612     png_state.angle = ang;
1613     return TRUE;
1614 }
1615
1616 TERM_PUBLIC int
1617 PNG_justify_text(enum JUSTIFY mode)
1618 {
1619 #ifdef HAVE_GD_TTF
1620     png_state.justify = mode;
1621     return TRUE;
1622 #else
1623     return null_justify_text(mode);
1624 #endif
1625 }
1626
1627 TERM_PUBLIC void
1628 PNG_point(unsigned int x, unsigned int y, int number)
1629 {
1630     int save_color = png_state.color;
1631
1632     if (number < 0) {
1633         gdImageSetPixel(png_state.image, x, Y(y), png_state.color);
1634         return;
1635     }
1636     /* Use current linewidth to draw the point symbol */
1637     if (png_state.linewidth > 1) {
1638         /* EAM - Implement linewidth by selecting a pre-defined brush */
1639         /* The tricky bit is re-coloring the brush to the current color */
1640         int lw   = png_state.linewidth;
1641         if (png_state.color != PNG_brush[lw].last_rgb) {
1642             PNG_brush[lw].fgnd = gdImageColorResolve(PNG_brush[lw].im,
1643                 gdImageRed(png_state.image,png_state.color),
1644                 gdImageGreen(png_state.image,png_state.color),
1645                 gdImageBlue(png_state.image,png_state.color) );
1646             PNG_brush[lw].last_rgb = png_state.color;
1647         }
1648         gdImageFilledRectangle( PNG_brush[lw].im, 0, 0, lw-1, lw-1, PNG_brush[lw].fgnd );
1649         gdImageSetBrush(png_state.image, PNG_brush[lw].im);
1650         png_state.color = gdBrushed;
1651     }
1652
1653     y = Y(y);
1654
1655     switch (number % 13) {
1656     case 0: /* plus */
1657     default:
1658         PNG_PointPlus(x, y);
1659         break;
1660     case 1: /* X */
1661         PNG_PointX(x, y);
1662         break;
1663     case 2: /* star */
1664         PNG_PointPlus(x, y);
1665         PNG_PointX(x, y);
1666         break;
1667     case 3: /* box */
1668         gdImageRectangle(png_state.image, x - PNG_ps, y - PNG_ps,
1669                          x + PNG_ps, y + PNG_ps, png_state.color);
1670         break;
1671     case 4: /* box                   filled */
1672         gdImageFilledRectangle(png_state.image, x - PNG_ps, y - PNG_ps,
1673                                x + PNG_ps, y + PNG_ps, png_state.color);
1674         break;
1675     case 5: /* circle */
1676         gdImageArc(png_state.image, x, y, 2 * PNG_ps, 2 * PNG_ps,
1677                    0, 360, png_state.color);
1678         break;
1679     case 6: /* circle (disk)         filled */
1680 #if (GD2_VERS >= 2)
1681         gdImageFilledArc(png_state.image, x, y, 2 * PNG_ps, 2 * PNG_ps,
1682                    0, 360, png_state.color, gdArc);
1683 #else
1684         gdImageArc(png_state.image, x, y, 2 * PNG_ps, 2 * PNG_ps,
1685                    0, 360, png_state.color);
1686         gdImageFillToBorder(png_state.image, x, y,
1687                             png_state.color, png_state.color);
1688 #endif
1689         break;
1690     case 7: /* triangle */
1691         PNG_Triangle(x, y, 1, gp_gdImagePolygon);
1692         break;
1693     case 8: /* triangle              filled */
1694         PNG_Triangle(x, y, 1, gp_gdImageFilledPolygon);
1695         break;
1696     case 9: /* upside down triangle */
1697         PNG_Triangle(x, y, -1, gp_gdImagePolygon);
1698         break;
1699     case 10: /* upside down triangle  filled */
1700         PNG_Triangle(x, y, -1, gp_gdImageFilledPolygon);
1701         break;
1702     case 11: /* diamond */
1703         PNG_Diamond(x, y, gp_gdImagePolygon);
1704         break;
1705     case 12: /* diamond               filled */
1706         PNG_Diamond(x, y, gp_gdImageFilledPolygon);
1707         break;
1708     }
1709
1710     png_state.color = save_color;
1711 }
1712
1713 TERM_PUBLIC int
1714 PNG_set_font(const char *fontname)
1715 {
1716     int  sep;
1717     int  size;
1718     gdFontPtr font = png_state.default_font;
1719     char *name = gp_strdup(fontname);
1720
1721     sep = strcspn(fontname,",");
1722     strncpy(name,fontname,sep);
1723     name[sep] = '\0';
1724     size = png_state.default_ttfsize;
1725     sscanf (&(fontname[sep+1]),"%d",&size);
1726
1727     if (!strcmp(name,"small"))
1728         font = gdFontSmall;
1729     else if (!strcmp(name,"medium"))
1730         font = gdFontMediumBold;
1731     else if (!strcmp(name,"large"))
1732         font = gdFontLarge;
1733     else if (!strcmp(name,"giant"))
1734         font = gdFontGiant;
1735     else if (!strcmp(name,"tiny"))
1736         font = gdFontTiny;
1737     else if (*name) {
1738         /* New ttf font */
1739         free(png_state.ttffont);
1740         png_state.ttffont = gp_strdup(name);
1741         png_state.ttfsize = size;
1742     } else {
1743         /* Restore initial default font */
1744         free(png_state.ttffont);
1745         png_state.ttffont = gp_strdup(png_state.default_ttffont);
1746         png_state.ttfsize = png_state.default_ttfsize;
1747     }
1748     free(name);
1749
1750     png_state.font  = font;
1751     png_state.charw = font->w;
1752     png_state.charh = font->h;
1753
1754 /* EAM 9-Feb-2003 Make new font size visible to higher level routines like write_multiline */
1755     term->h_char = font->w;
1756     term->v_char = font->h;
1757 #ifdef HAVE_GD_TTF
1758     /* Find approximate character width and height of selected TTF font */
1759     if (png_state.ttffont) {
1760         int brect[8];
1761         char *err;
1762         err = gdImageStringFT(NULL, &brect[0], 0,
1763                 png_state.ttffont, (double)png_state.ttfsize,
1764                 0.0, 0, 0, "f00000000g");
1765         if (!err) {
1766             term->h_char = .11 * (float)(brect[2] - brect[0]) + 0.5;
1767             term->v_char = 1.1 * (float)(brect[1] - brect[7]) + 0.5;
1768         }
1769     }
1770 #endif
1771
1772     return TRUE;
1773 }
1774
1775 TERM_PUBLIC void
1776 PNG_pointsize(double ptsize)
1777 {
1778     if (ptsize < 0)
1779         ptsize = 1;
1780     PNG_ps = (int)(((double)PNG_POINT_SCALE * ptsize) + 0.5);
1781 }
1782
1783 /*
1784  * Ethan A Merritt November 2003
1785  *      - Support for enhanced text mode
1786  * BUGS:
1787  *      - placement of overprinted characters is not correct;
1788  *        the overprinted text (pass 2) should be centered, not left-justified
1789  * PROBLEMS:
1790  *      - the Symbol font encoding didn't work in libgd until 2.0.21
1791  *      - Placement of superscripts and subscripts relies on information
1792  *        in the font description that is not always reliable
1793  *      - the TTF character encoding for non-keyboard characters does
1794  *        not always match the PostScript standard.
1795  *      - Spacing of rotated text is incorrect; I believe this is a due
1796  *        to a problem in the text rotation code (aspect ratio??).
1797  */
1798
1799 static TBOOLEAN ENHgd_opened_string;
1800
1801 /* used in determining height of processed text */
1802 static float ENHgd_base;
1803
1804 /* use these so that we don't over-write the current font settings in png_state */
1805 static double  ENHgd_fontsize;
1806 static char   *ENHgd_font;
1807
1808 static TBOOLEAN ENHgd_show = TRUE;
1809 static TBOOLEAN ENHgd_sizeonly = FALSE;
1810 static int ENHgd_overprint = 0;
1811 static TBOOLEAN ENHgd_widthflag = TRUE;
1812 static unsigned int ENHgd_xsave, ENHgd_ysave;
1813
1814 TERM_PUBLIC void
1815 ENHGD_OPEN(
1816     char *fontname,
1817     double fontsize, double base,
1818     TBOOLEAN widthflag,
1819     TBOOLEAN showflag,
1820     int overprint)
1821 {
1822     /* If the overprint code requests a save or restore, that's all we do */
1823     if (overprint == 3) {
1824         ENHgd_xsave = png_state.x;
1825         ENHgd_ysave = png_state.y;
1826         return;
1827     } else if (overprint == 4) {
1828         PNG_move(ENHgd_xsave, ENHgd_ysave);
1829         return;
1830     }
1831
1832     if (!ENHgd_opened_string) {
1833         ENHgd_opened_string = TRUE;
1834         enhanced_cur_text = &enhanced_text[0];
1835         ENHgd_font = fontname;
1836         ENHgd_fontsize = fontsize;
1837         ENHgd_base = base;
1838         ENHgd_show = showflag;
1839         ENHgd_overprint = overprint;
1840         ENHgd_widthflag = widthflag;
1841     }
1842 }
1843
1844 /* Write a string fragment and update the current position */
1845 TERM_PUBLIC void
1846 ENHGD_FLUSH()
1847 {
1848     int brect[8]; char *err;
1849     unsigned int x, y;
1850
1851         if (ENHgd_opened_string) {
1852             ENHgd_opened_string = FALSE;
1853             *enhanced_cur_text = '\0';
1854             x = png_state.x;
1855             y = png_state.y;
1856             x -= sin((double)png_state.angle * M_PI_2/90.) * ENHgd_base;
1857             y += cos((double)png_state.angle * M_PI_2/90.) * ENHgd_base;
1858             x += sin((double)png_state.angle * M_PI_2/90.) * (double)png_state.charh/4.;
1859             y -= cos((double)png_state.angle * M_PI_2/90.) * (double)png_state.charh/4.;
1860
1861 #ifdef gdFTEX_Adobe_Custom
1862             /* libgd defaults to UTF-8 encodings. We have limited options for   */
1863             /* over-riding this, but we can try                                 */
1864             if (ENHgd_font && !strcmp(ENHgd_font,"Symbol")) {
1865                 PNG_FONT_INFO.flags |= gdFTEX_CHARMAP;
1866                 PNG_FONT_INFO.charmap = gdFTEX_Adobe_Custom;
1867             } else {
1868                 PNG_FONT_INFO.flags &= ~gdFTEX_CHARMAP;
1869                 PNG_FONT_INFO.charmap = 0;   /* gdFTEX_Adobe_Custom */
1870             }
1871             err = gdImageStringFTEx(
1872                         (ENHgd_show && !ENHgd_sizeonly) ? png_state.image : NULL,
1873                         brect, png_state.color,
1874                         ENHgd_font, ENHgd_fontsize,
1875                         (double)png_state.angle * M_PI_2/90.,
1876                         x, Y(y), enhanced_text, &PNG_FONT_INFO);
1877 #else
1878             err = gdImageStringFT(
1879                         (ENHgd_show && !ENHgd_sizeonly) ? png_state.image : NULL,
1880                         brect, png_state.color,
1881                         ENHgd_font, ENHgd_fontsize,
1882                         (double)png_state.angle * M_PI_2/90.,
1883                         x, Y(y), enhanced_text);
1884 #endif
1885             if (err) 
1886                 fprintf(stderr,"gdImageStringFT: %s while printing string %s with font %s\n",
1887                     err,enhanced_text,ENHgd_font);
1888
1889             FPRINTF((stderr,"outputstring: %s boundingbox: %d %d %d %d\n",
1890                         enhanced_text, brect[6], brect[7], brect[2], brect[3]));
1891             if (ENHgd_overprint == 1) {
1892                 png_state.x += ((brect[2] - brect[0]))/2;
1893                 png_state.y -= (brect[3] - brect[1]);
1894             } else if (ENHgd_widthflag) {
1895                 png_state.x += (brect[2] - brect[0]);
1896                 png_state.y -= (brect[3] - brect[1]);
1897             }
1898         }
1899 }
1900
1901 TERM_PUBLIC void
1902 ENHGD_put_text(unsigned int x, unsigned int y, const char *str)
1903 {
1904     char *original_string = (char *)str;
1905
1906     if (ignore_enhanced_text || !png_state.ttffont) {
1907         PNG_put_text(x,y,str);
1908         return;
1909     }
1910
1911     if (!strlen(str))
1912         return;
1913
1914     /* if there are no magic characters, we should just be able
1915      * punt the string to PNG_put_text()
1916      */
1917     if (!strpbrk(str, "{}^_@&~")) {
1918         /* FIXME: do something to ensure default font is selected */
1919         PNG_put_text(x,y,str);
1920         return;
1921     }
1922
1923     PNG_move(x,y);
1924
1925     /* set up the global variables needed by enhanced_recursion() */
1926     enhanced_max_height = -1000;
1927     enhanced_min_height = 1000;
1928     enhanced_fontscale = 1.0;
1929     strncpy(enhanced_escape_format,"&#x%2.2x;",sizeof(enhanced_escape_format));
1930
1931     ENHgd_opened_string = FALSE;
1932     ENHgd_show = TRUE;
1933     ENHgd_overprint = 0;
1934
1935     /* EAM - post.trm wasn't doing this, but how else do they get initialized? */
1936         ENHgd_font = png_state.ttffont;
1937         ENHgd_fontsize = png_state.ttfsize;
1938
1939     /* EAM - Software text justification requires two passes */
1940     if (png_state.justify == RIGHT || png_state.justify == CENTRE)
1941         ENHgd_sizeonly = TRUE;
1942
1943     /* Set the recursion going. We say to keep going until a
1944      * closing brace, but we don't really expect to find one.
1945      * If the return value is not the nul-terminator of the
1946      * string, that can only mean that we did find an unmatched
1947      * closing brace in the string. We increment past it (else
1948      * we get stuck in an infinite loop) and try again.
1949      */
1950     while (*(str = enhanced_recursion((char *)str, TRUE,
1951                         ENHgd_font, ENHgd_fontsize,
1952                         0.0, TRUE, TRUE, 0))) {
1953         (term->enhanced_flush)();
1954
1955         /* I think we can only get here if *str == '}' */
1956             enh_err_check(str);
1957
1958         if (!*++str)
1959             break; /* end of string */
1960
1961         /* else carry on and process the rest of the string */
1962     }
1963
1964     enhanced_max_height += enhanced_min_height;
1965
1966     /* We can do text justification by running the entire top level string */
1967     /* through 2 times, with the ENHgd_sizeonly flag set the first time.   */
1968     /* After seeing where the final position is, we then offset the start  */
1969     /* point accordingly and run it again without the flag set.            */
1970     if (png_state.justify == RIGHT || png_state.justify == CENTRE) {
1971         int justification = png_state.justify;
1972         int x_offset = png_state.x - x;
1973         int y_offset = 0;
1974
1975         if (png_state.angle != 0)
1976             y_offset = png_state.y - y;
1977         png_state.justify = LEFT;
1978         ENHgd_sizeonly = FALSE;
1979
1980         if (justification == RIGHT) {
1981             ENHGD_put_text(x - x_offset, y - y_offset, original_string);
1982         } else if (justification == CENTRE) {
1983             ENHGD_put_text(x - x_offset/2, y - y_offset/2, original_string);
1984         }
1985         png_state.justify = justification;
1986     }
1987
1988 }
1989
1990 #undef gdfont
1991
1992 #ifdef WITH_IMAGE
1993
1994 TERM_PUBLIC void
1995 PNG_image (unsigned M, unsigned N, coordval * image, gpiPoint * corner, t_imagecolor color_mode)
1996 {
1997     int m, n, mout, nout;
1998     int x1,y1,x2,y2;
1999     int xclip1, xclip2, yclip1, yclip2;
2000     int pixel;
2001     gdImagePtr im;
2002
2003 #if (GD2_VERS >= 2)
2004     if (png_state.TrueColor) {
2005         im = gdImageCreateTrueColor(M, N);
2006         if (!im)
2007             int_error(NO_CARET,"libgd: failed to create image structure");
2008     } else
2009 #endif
2010         {
2011         im = gdImageCreate(M, N);
2012         if (!im)
2013             int_error(NO_CARET,"libgd: failed to create image structure");
2014         gdImagePaletteCopy(im, png_state.image);
2015     }
2016
2017     /* TrueColor 24-bit color mode */
2018     if (color_mode == IC_RGB) {
2019         for (n=0; n<N; n++) {
2020         for (m=0; m<M; m++) {
2021             rgb_color rgb1;
2022             rgb255_color rgb255;
2023             rgb1.r = *image++;
2024             rgb1.g = *image++;
2025             rgb1.b = *image++;
2026             rgb255_from_rgb1( rgb1, &rgb255 );
2027             pixel = gdImageColorResolve( im, 
2028                 (int)rgb255.r, (int)rgb255.g, (int)rgb255.b );
2029             gdImageSetPixel( im, m, n, pixel );
2030         }
2031         }
2032     /* Palette color lookup from gray value */
2033     } else {
2034         for (n=0; n<N; n++) {
2035         for (m=0; m<M; m++) {
2036             rgb255_color rgb;
2037             rgb255maxcolors_from_gray( *image++, &rgb );
2038             pixel = gdImageColorResolve( im,
2039                 (int)rgb.r, (int)rgb.g, (int)rgb.b );
2040             gdImageSetPixel( im, m, n, pixel );
2041         }
2042         }
2043     }
2044
2045 #if (GD2_VERS >= 2)
2046     /* Set clipping bound for area into which we will copy */
2047     xclip1 = GPMIN(corner[2].x, corner[3].x);
2048     xclip2 = GPMAX(corner[2].x, corner[3].x);
2049     yclip1 = GPMIN(Y(corner[2].y), Y(corner[3].y));
2050     yclip2 = GPMAX(Y(corner[2].y), Y(corner[3].y));
2051     gdImageGetClip(png_state.image, &x1, &y1, &x2, &y2);
2052     gdImageSetClip(png_state.image, xclip1, yclip1, xclip2, yclip2);
2053 #endif
2054
2055     /* Copy and resize onto requested region of plot */
2056     /* FIXME - WOULD gdImageCopyResampled() do a nicer job ??? */
2057     mout = abs( (int)corner[1].x - (int)corner[0].x );
2058     nout = abs( (int)corner[1].y - (int)corner[0].y );
2059     gdImageCopyResized(png_state.image, im,
2060         (corner[0].x), Y(corner[0].y),  /* Destination X, Y */
2061         0, 0,                           /* Source X, Y */
2062         mout, nout,                     /* Destination Width, Height */
2063         M, N                            /* Source Width, Height */
2064         );
2065     gdImageDestroy(im);
2066
2067 #if (GD2_VERS >= 2)
2068     /* Restore previous clipping, if any */
2069     gdImageSetClip(png_state.image, x1, y1, x2, y2);
2070 #endif
2071
2072 }
2073
2074 #endif
2075 #undef MAXLINEWIDTH
2076 #undef Y
2077
2078 #endif /* TERM_BODY */
2079 #ifdef TERM_TABLE
2080
2081 TERM_TABLE_START(png_driver)
2082     "png", "PNG images using libgd and TrueType fonts",
2083     GREG_XMAX, GREG_YMAX, PNG_VCHAR, PNG_HCHAR,
2084     PNG_TICSIZE, PNG_TICSIZE, PNG_options, PNG_init, PNG_reset,
2085     PNG_text, null_scale, PNG_graphics, PNG_move, PNG_vector,
2086     PNG_linetype, PNG_put_text, PNG_text_angle,
2087     PNG_justify_text, PNG_point, do_arrow, PNG_set_font,
2088     PNG_pointsize,
2089     TERM_BINARY,
2090     0 /*suspend*/, 0 /*resume*/,
2091     PNG_boxfill /*EAM - fillbox*/,
2092     PNG_linewidth /*EAM - linewidth*/
2093 #ifdef USE_MOUSE
2094     , 0, 0, 0, 0, 0 /* no mouse support */
2095 #endif
2096     , PNG_make_palette,
2097     0, /* previous_palette() ... no, single array of 256 colours for PNG */
2098     PNG_set_color,
2099     PNG_filled_polygon
2100 #ifdef WITH_IMAGE
2101     , PNG_image
2102 #endif
2103     , ENHGD_OPEN, ENHGD_FLUSH, do_enh_writec
2104 TERM_TABLE_END(png_driver)
2105
2106 #undef LAST_TERM
2107 #define LAST_TERM png_driver
2108
2109 #endif /* TERM_TABLE */
2110 #endif /* TERM_PROTO_ONLY */
2111
2112 #ifndef JPEG_HELP_ONLY
2113 #ifdef TERM_HELP
2114 START_HELP(png)
2115 "1 png",
2116 "?commands set terminal png",
2117 "?set terminal png",
2118 "?set term png",
2119 "?terminal png",
2120 "?term png",
2121 "?png",
2122 " Syntax:",
2123 "       set terminal png ",
2124 "              {{no}transparent} {{no}interlace}",
2125 "              {{no}truecolor} {rounded|butt}",
2126 "              {tiny | small | medium | large | giant}",
2127 "              {font <face> {<pointsize>}}",
2128 "              {size <x>,<y>} {{no}crop}",
2129 "              {{no}enhanced}",
2130 "              {<color0> <color1> <color2> ...}",
2131 "",
2132 " PNG images are created using libgd, with optional support for TrueType",
2133 " and Adobe Type 1 fonts via libfreetype. Version 1.8 or greater of libgd",
2134 " is required.",
2135 "",
2136 " `transparent` instructs the driver to generate transparent PNGs.  The first",
2137 " color will be the transparent one. Default is `notransparent`.",
2138 "",
2139 " `interlace` instructs the driver to generate interlaced PNGs.",
2140 " Default is `nointerlace`.",
2141 "",
2142 " `butt` instructs the driver to use a line drawing method that does",
2143 " not overshoot the desired end point of a line.  This setting is only",
2144 " applicable for line widths greater than 1.  This setting is most useful when",
2145 " drawing horizontal or vertical lines.  Default is `rounded`.",
2146 " Version 2.0 or greater of libgd is required.",
2147 "",
2148 " PNG plots may be conveniently viewed by piping the output to the",
2149 " 'display' program from the ImageMagick package as follows:",
2150 "                set term png",
2151 "                set output '| display png:-'",
2152 "",
2153 " View the output from successive plot commands interactively by hitting",
2154 " <space> in the display window.  To save a particular one to disk, left",
2155 " click in the display window and choose `save`.",
2156 "",
2157 " Five basic fonts are supported directly by the gd library. These are",
2158 " `tiny` (5x8 pixels), `small` (6x12 pixels), `medium`, (7x13 Bold), ",
2159 " `large` (8x16) or `giant` (9x15 pixels). These fonts cannot be scaled",
2160 " or rotated (pure horizontal or vertical text only).",
2161 "",
2162 "=fonts",
2163 " If gnuplot was built with support for TrueType (*.ttf) or Adobe Type 1 ",
2164 " (*.pfa) fonts, they may be selected using the 'font <face> {<pointsize>}' ",
2165 " option. <face> is either the full pathname to the font file, or a font ",
2166 " face name that is assumed to be the first part of a filename in one of the ",
2167 " directories listed in the GDFONTPATH environmental variable. That is, ",
2168 " 'set term png font \"Face\"' will look for a font file named either ",
2169 " <somedirectory>/Face.ttf or <somedirectory>/Face.pfa. Both TrueType and ",
2170 " Adobe Type 1 fonts are fully scalable and may be rotated through any angle.",
2171 " If no font is specified, gnuplot checks the environmental variable ",
2172 " GNUPLOT_DEFAULT_GDFONT to see if there is a preferred default font. ",
2173 "",
2174 " `enhanced` enables the enhanced text processing features, (subscripts, ",
2175 " superscripts and mixed fonts). See `enhanced` for more information. ",
2176 " The full enhanced mode syntax is supported by the PNG/JPEG driver itself,",
2177 " but some of these features are dependent on which version of the ",
2178 " underlying libgd library is present, and which fonts are available.",
2179 "",
2180 " The size <x,y> is given in pixels---it defaults to 640x480.  The number of",
2181 " pixels can be also modified by scaling with the `set size` command.",
2182 " `crop` trims blank space from the edges of the completed plot, resulting",
2183 " in a smaller final image size. Default is `nocrop`.",
2184 "",
2185 " Each color must be of the form 'xrrggbb', where x is the literal character",
2186 " 'x' and 'rrggbb' are the red, green and blue components in hex.  For example,",
2187 " 'x00ff00' is green.  The background color is set first, then the border",
2188 " colors, then the X & Y axis colors, then the plotting colors.  The maximum",
2189 " number of colors that can be set is 256.",
2190 "",
2191 " Examples:",
2192 "       set terminal png medium size 640,480 \\",
2193 "                        xffffff x000000 x404040 \\",
2194 "                        xff0000 xffa500 x66cdaa xcdb5cd \\",
2195 "                        xadd8e6 x0000ff xdda0dd x9500d3    # defaults",
2196 "",
2197 " which uses white for the non-transparent background, black for borders, gray",
2198 " for the axes, and red, orange, medium aquamarine, thistle 3, light blue, blue,",
2199 " plum and dark violet for eight plotting colors.",
2200 "",
2201 "       set terminal png font arial 14 size 800,600",
2202 "",
2203 " which searches for a TrueType font with face name 'arial' in the directory",
2204 " specified by the environment variable GDFONTPATH and 14pt font size.",
2205 "",
2206 "       set terminal png transparent xffffff \\",
2207 "                        x000000 x202020 x404040 x606060 \\",
2208 "                        x808080 xA0A0A0 xC0C0C0 xE0E0E0",
2209 "",
2210 " which uses white for the transparent background, black for borders, dark",
2211 " gray for axes, and a gray-scale for the six plotting colors.",
2212 ""
2213 END_HELP(png)
2214 #endif /* TERM_HELP */
2215 #endif /* JPEG_HELP_ONLY */
2216
2217 /*
2218  * JPEG support comes almost for free.
2219  * We just piggy-back on the PNG routines, since they both go via libgd
2220  */
2221 #ifdef HAVE_GD_JPEG
2222
2223 #ifdef TERM_REGISTER
2224 register_term(jpeg)
2225 #endif
2226
2227 #ifdef TERM_PROTO
2228 TERM_PUBLIC void JPEG_text __PROTO((void));
2229 #define GOT_NEXT_PROTO
2230 #endif
2231
2232 #ifndef TERM_PROTO_ONLY
2233
2234 #ifdef TERM_BODY
2235
2236 #include "gd.h"
2237 /*
2238  * All functions except the final write to file
2239  * are actually performed by the PNG driver code
2240  */
2241 TERM_PUBLIC void
2242 JPEG_text()
2243 {
2244 int quality = 90;
2245
2246     image_do_crop();
2247     if (png_state.flags & PNG_USE_INTERLACE)
2248         gdImageInterlace(png_state.image, 1);
2249     gdImageJpeg(png_state.image, gpoutfile, quality);
2250     gdImageDestroy(png_state.image);
2251 }
2252
2253 #endif /* TERM_BODY */
2254
2255 #ifdef TERM_TABLE
2256
2257 TERM_TABLE_START(jpeg_driver)
2258     "jpeg", "JPEG images using libgd and TrueType fonts",
2259     GREG_XMAX, GREG_YMAX, PNG_VCHAR, PNG_HCHAR,
2260     PNG_TICSIZE, PNG_TICSIZE, PNG_options, PNG_init, PNG_reset,
2261     JPEG_text, null_scale, PNG_graphics, PNG_move, PNG_vector,
2262     PNG_linetype, PNG_put_text, PNG_text_angle,
2263     PNG_justify_text, PNG_point, do_arrow, PNG_set_font,
2264     PNG_pointsize,
2265     TERM_CAN_MULTIPLOT | TERM_BINARY,
2266     0 /*suspend*/, 0 /*resume*/,
2267     PNG_boxfill /*EAM - fillbox*/,
2268     PNG_linewidth /*EAM - linewidth*/
2269 #ifdef USE_MOUSE
2270     , 0, 0, 0, 0, 0 /* no mouse support */
2271 #endif
2272     , PNG_make_palette,
2273     0, /* previous_palette() ... no, single array of 256 colours for PNG */
2274     PNG_set_color,
2275     PNG_filled_polygon
2276 #ifdef WITH_IMAGE
2277     , PNG_image
2278 #endif
2279     , ENHGD_OPEN, ENHGD_FLUSH, do_enh_writec
2280 TERM_TABLE_END(jpeg_driver)
2281
2282 #undef LAST_TERM
2283 #define LAST_TERM jpeg_driver
2284
2285 #endif /* TERM_TABLE */
2286 #endif /* TERM_PROTO_ONLY */
2287
2288
2289 #ifdef TERM_HELP
2290 START_HELP(jpeg)
2291 "1 jpeg",
2292 "?commands set terminal jpeg",
2293 "?set terminal jpeg",
2294 "?set term jpeg",
2295 "?terminal jpeg",
2296 "?term jpeg",
2297 "?jpeg",
2298 "",
2299 " Syntax:",
2300 "       set terminal jpeg ",
2301 "                        {{no}interlace}",
2302 "                        {tiny | small | medium | large | giant}",
2303 "                        {font <face> {<pointsize>}}",
2304 "                        {size <x>,<y>} {{no}crop}",
2305 "                        {{no}enhanced}",
2306 "                        {<color0> <color1> <color2> ...}",
2307 "",
2308 " JPEG images are created using libgd, with optional support for TrueType",
2309 " fonts via libfreetype.",
2310 "",
2311 " The `interlace` option creates a progressive JPEG image.",
2312 " Default is `nointerlace`.",
2313 "",
2314 " Five basic fonts are supported directly by the gd library. These are",
2315 " `tiny` (5x8 pixels), `small` (6x12 pixels), `medium`, (7x13 Bold), ",
2316 " `large` (8x16) or `giant` (9x15 pixels). These fonts cannot be scaled",
2317 " or rotated (pure horizontal or vertical text only).",
2318 "",
2319 "=fonts",
2320 " If gnuplot was built with support for TrueType (*.ttf) or Adobe Type 1 ",
2321 " (*.pfa) fonts, they may be selected using the 'font <face> {<pointsize>}' ",
2322 " option. <face> is either the full pathname to the font file, or a font ",
2323 " face name that is assumed to be the first part of a filename in one of the ",
2324 " directories listed in the GDFONTPATH environmental variable. That is, ",
2325 " 'set term jpeg font \"Face\"' will look for a font file named either ",
2326 " <somedirectory>/Face.ttf or <somedirectory>/Face.pfa. Both TrueType and ",
2327 " Adobe Type 1 fonts are fully scalable and may be rotated through any angle.",
2328 " If no font is specified, gnuplot checks the environmental variable ",
2329 " GNUPLOT_DEFAULT_GDFONT to see if there is a preferred default font. ",
2330 "",
2331 " `enhanced` enables the enhanced text processing features, (subscripts, ",
2332 " superscripts and mixed fonts). See `enhanced` for more information. ",
2333 " The full enhanced mode syntax is supported by the PNG/JPEG driver itself,",
2334 " but some of these features are dependent on which version of the ",
2335 " underlying libgd library is present, and which fonts are available.",
2336 "",
2337 " The size <x,y> is given in pixels---it defaults to 640x480.  The number of",
2338 " pixels can be also modified by scaling with the `set size` command.",
2339 " `crop` trims blank space from the edges of the completed plot, resulting",
2340 " in a smaller final image size. Default is `nocrop`.",
2341 "",
2342 " Each color must be of the form 'xrrggbb', where x is the literal character",
2343 " 'x' and 'rrggbb' are the red, green and blue components in hex.  For example,",
2344 " 'x00ff00' is green.  The background color is set first, then the border",
2345 " colors, then the X & Y axis colors, then the plotting colors.  The maximum",
2346 " number of colors that can be set is 256.",
2347 "",
2348 " Examples:",
2349 "       set terminal jpeg medium size 640,480 \\",
2350 "                        xffffff x000000 x404040 \\",
2351 "                        xff0000 xffa500 x66cdaa xcdb5cd \\",
2352 "                        xadd8e6 x0000ff xdda0dd x9500d3    # defaults",
2353 "",
2354 " which uses white for the non-transparent background, black for borders, gray",
2355 " for the axes, and red, orange, medium aquamarine, thistle 3, light blue, blue,",
2356 " plum and dark violet for eight plotting colors.",
2357 "",
2358 "       set terminal jpeg large font arial size 800,600",
2359 "",
2360 " which searches for a TrueType font with face name 'arial' in the directory",
2361 " specified by the environment variable GDFONTPATH and large (14pt) font size.",
2362 ""
2363 END_HELP(jpeg)
2364 #endif /* TERM_HELP */
2365 #endif /* HAVE_GD_JPEG */
2366
2367 #ifdef HAVE_GD_GIF
2368 /*
2369  * GIF support comes almost for free.
2370  * We just piggy-back on the PNG routines, since they both go via libgd.
2371  * Required libgd version is 2.0.28 or newer.
2372  */
2373 #ifdef HAVE_GD_GIF
2374
2375 #ifdef TERM_REGISTER
2376 register_term(gif)
2377 #endif
2378
2379 #ifdef TERM_PROTO
2380 TERM_PUBLIC void GIF_text __PROTO((void));
2381 #define GOT_NEXT_PROTO
2382 #endif
2383
2384 #ifndef TERM_PROTO_ONLY
2385
2386 #ifdef TERM_BODY
2387
2388 #include "gd.h"
2389 /*
2390  * All functions except the final write to file
2391  * are actually performed by the PNG driver code
2392  */
2393 TERM_PUBLIC void
2394 GIF_text()
2395 {
2396     image_do_crop();
2397
2398 #ifdef GIF_ANIMATION
2399     if (png_state.animate) {
2400         /* Note - using a global colormap saves space, but it breaks    */
2401         /* if later frames add new colors to the palette.               */
2402         if (png_state.frame_count == 0) {
2403             gdImageGifAnimBegin(png_state.image, gpoutfile, 
2404                 1, /* Load Global Colormap even if it isn't used */ 
2405                 png_state.loop_count );
2406         }
2407         gdImageGifAnimAdd(png_state.image, gpoutfile,
2408             png_state.frame_optimization ? 0  /* use global map  */
2409                                          : 1, /* use private map */
2410             0, 0 /* No offset */,
2411             png_state.frame_delay,
2412             (png_state.flags & PNG_USE_TRANSPARENT)
2413                         ? gdDisposalRestorePrevious 
2414                         : gdDisposalNone, 
2415             (png_state.frame_optimization && !(png_state.flags & PNG_USE_TRANSPARENT))
2416             ? png_state.previous_image : NULL);
2417         png_state.frame_count++;
2418         if (png_state.previous_image)
2419             gdImageDestroy(png_state.previous_image);
2420         png_state.previous_image = png_state.image;
2421         return;
2422     }
2423 #endif
2424
2425     gdImageGif(png_state.image, gpoutfile);
2426     gdImageDestroy(png_state.image);
2427 }
2428
2429 #endif /* TERM_BODY */
2430
2431 #ifdef TERM_TABLE
2432
2433 TERM_TABLE_START(gif_driver)
2434     "gif", "GIF images using libgd and TrueType fonts",
2435     GREG_XMAX, GREG_YMAX, PNG_VCHAR, PNG_HCHAR,
2436     PNG_TICSIZE, PNG_TICSIZE, PNG_options, PNG_init, PNG_reset,
2437     GIF_text, null_scale, PNG_graphics, PNG_move, PNG_vector,
2438     PNG_linetype, PNG_put_text, PNG_text_angle,
2439     PNG_justify_text, PNG_point, do_arrow, PNG_set_font,
2440     PNG_pointsize,
2441     TERM_CAN_MULTIPLOT | TERM_BINARY,
2442     0 /*suspend*/, 0 /*resume*/,
2443     PNG_boxfill /*EAM - fillbox*/,
2444     PNG_linewidth /*EAM - linewidth*/
2445 #ifdef USE_MOUSE
2446     , 0, 0, 0, 0, 0 /* no mouse support */
2447 #endif
2448     , PNG_make_palette,
2449     0, /* previous_palette() ... no, single array of 256 colours for PNG */
2450     PNG_set_color,
2451     PNG_filled_polygon
2452 #ifdef WITH_IMAGE
2453     , PNG_image
2454 #endif
2455     , ENHGD_OPEN, ENHGD_FLUSH, do_enh_writec
2456 TERM_TABLE_END(gif_driver)
2457
2458 #undef LAST_TERM
2459 #define LAST_TERM gif_driver
2460
2461 #endif /* TERM_TABLE */
2462 #endif /* TERM_PROTO_ONLY */
2463
2464
2465 #ifdef TERM_HELP
2466 START_HELP(gif)
2467 "1 gif",
2468 "?commands set terminal gif",
2469 "?set terminal gif",
2470 "?set term gif",
2471 "?terminal gif",
2472 "?term gif",
2473 "?gif",
2474 "",
2475 " Syntax:",
2476 "       set terminal gif ",
2477 "                        {tiny | small | medium | large | giant}",
2478 "                        {{no}transparent} {{no}enhanced}",
2479 "                        {font <face> {<pointsize>}}",
2480 "                        {animate {delay <time>} {{no}optimize}}",
2481 "                        {size <x>,<y>} {{no}crop}",
2482 "                        {<color0> <color1> <color2> ...}",
2483 "",
2484 " GIF images are created using libgd, with optional support for TrueType",
2485 " fonts via libfreetype.",
2486 "",
2487 " GIF plots may be conveniently viewed by piping the output to the",
2488 " 'display' program from the ImageMagick package as follows:",
2489 "                set term gif",
2490 "                set output '| display gif:-'",
2491 "",
2492 " View the output from successive plot commands interactively by hitting",
2493 " <space> in the display window.  To save a particular one to disk, left",
2494 " click in the display window and choose `save`.",
2495 "",
2496 " Five basic fonts are supported directly by the gd library. These are",
2497 " `tiny` (5x8 pixels), `small` (6x12 pixels), `medium`, (7x13 Bold), ",
2498 " `large` (8x16) or `giant` (9x15 pixels). These fonts cannot be scaled",
2499 " or rotated (pure horizontal or vertical text only).",
2500 "",
2501 " `transparent` instructs the driver to generate transparent GIFs.  The first",
2502 " color will be the transparent one. Default is `notransparent`.",
2503 "",
2504 " `enhanced` enables the enhanced text processing features, (subscripts, ",
2505 " superscripts and mixed fonts). See `enhanced` for more information. ",
2506 " The full enhanced mode syntax is supported by the PNG/GIF driver itself,",
2507 " but some of these features are dependent on which version of the ",
2508 " underlying libgd library is present, and which fonts are available.",
2509 "",
2510 "=fonts",
2511 " If your local gd library was built with support for TrueType  and Adobe",
2512 " Type 1 fonts, they may be selected using the 'font <face> {<pointsize>}' ",
2513 " option. <face> is either the full pathname to the font file, or a font ",
2514 " face name that is assumed to be the first part of a filename in one of the ",
2515 " directories listed in the GDFONTPATH environmental variable. That is, ",
2516 " 'set term gif font \"Face\"' will look for a font file named either ",
2517 " <somedirectory>/Face.ttf or <somedirectory>/Face.pfa. Both TrueType and ",
2518 " Adobe Type 1 fonts are fully scalable and may be rotated through any angle.",
2519 " If no font is specified, gnuplot checks the environmental variable ",
2520 " GNUPLOT_DEFAULT_GDFONT to see if there is a preferred default font. ",
2521 "",
2522 " The `animate` option is available only if your local gd library supports",
2523 " the creation of animated gifs. The default delay between display of",
2524 " successive images may be specified in units of 1/100 second (default 5).",
2525 " The actual delay may vary depending on the program used as a viewer.",
2526 " An animation sequence is terminated by the next `set output` or `set term`",
2527 " command.  The `optimize` option has two effects on the animation.",
2528 "",
2529 " 1) A single color map is used for the entire animation. This requires",
2530 " that all colors used in any frame of the animation are already",
2531 " defined in the first frame.",
2532 "",
2533 " 2) If possible, only the portions of a frame that differ from the",
2534 " previous frame are stored in the animation file.  This space saving",
2535 " may not be possible if the animation uses transparency.",
2536 "",
2537 " Both of these optimizations are intended to produce a smaller output file,",
2538 " but the decrease in size is probably only significant for long animations",
2539 " or very small frame sizes.",
2540 " The `nooptimize` option turns off both of the effects just described.",
2541 " Each frame is stored in its entirety along with a private color map.",
2542 " Note that it is possible to post-process a non-optimized animation",
2543 " using external utilities, and this post-processing can yield a smaller",
2544 " file than gnuplot's internal optimization mode.",
2545 " The default is `nooptimize`.",
2546 "",
2547 " The size <x,y> is given in pixels---it defaults to 640x480.  The number of",
2548 " pixels can be also modified by scaling with the `set size` command.",
2549 " `crop` trims blank space from the edges of the completed plot, resulting",
2550 " in a smaller final image size. Default is `nocrop`.",
2551 "",
2552 " Each color must be of the form 'xrrggbb', where x is the literal character",
2553 " 'x' and 'rrggbb' are the red, green and blue components in hex.  For example,",
2554 " 'x00ff00' is green.  The background color is set first, then the border",
2555 " colors, then the X & Y axis colors, then the plotting colors.  The maximum",
2556 " number of colors that can be set is 256.",
2557 "",
2558 " Examples:",
2559 "       set terminal gif medium size 640,480 \\",
2560 "                        xffffff x000000 x404040 \\",
2561 "                        xff0000 xffa500 x66cdaa xcdb5cd \\",
2562 "                        xadd8e6 x0000ff xdda0dd x9500d3    # defaults",
2563 "",
2564 " which uses white for the non-transparent background, black for borders, gray",
2565 " for the axes, and red, orange, medium aquamarine, thistle 3, light blue, blue,",
2566 " plum and dark violet for eight plotting colors.",
2567 "",
2568 "       set terminal gif font 'arial' 14 size 800,600",
2569 "",
2570 " which searches for a TrueType font with face name 'arial' in the directory",
2571 " specified by the environment variable GDFONTPATH and 14pt font size.",
2572 ""
2573 END_HELP(gif)
2574 #endif /* TERM_HELP */
2575 #endif /* HAVE_GD_GIF */
2576 #endif