PLplot  5.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
gd.c
Go to the documentation of this file.
1 // $Id: gd.c 12334 2013-05-04 16:43:33Z airwin $
2 //
3 // PNG, GIF, and JPEG device driver based on libgd
4 //
5 // Copyright (C) 2004 Joao Cardoso
6 // Copyright (C) 2002, 2003, 2004 Andrew Roach
7 //
8 // This file is part of PLplot.
9 //
10 // PLplot is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU Library General Public License as published
12 // by the Free Software Foundation; either version 2 of the License, or
13 // (at your option) any later version.
14 //
15 // PLplot is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Library General Public License for more details.
19 //
20 // You should have received a copy of the GNU Library General Public License
21 // along with PLplot; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 //
24 
25 // GIF SUPPORT
26 //
27 // Following the expiration of Unisys's worldwide patents on lzw compression
28 // GD 2.0.28+ have reinstated support for GIFs, and so support for this
29 // format has been added to the GD family of drivers. GIF's only support
30 // 1, 4 and 8 bit, so no truecolour. Why would you want GIFs though ? PNG is
31 // a far superior format, not only giving you 1,4,8 and 24 bit, but also
32 // better compression and just about all browsers now support them.
33 //
34 
35 //
36 // The GD drivers, PNG, GIF, and JPEG, support a number of different options
37 // depending on the version of GD installed.
38 //
39 // If you have installed GD Ver 2.+ you gain support for truecolour (24
40 // bit, 16 millionish) modes as well as different line widths. These
41 // capibilities are part of GD more so than the GD driver, so they aren't
42 // available in any 1.? versions of the driver.
43 //
44 // 24 bit support is, by default, set to "auto" if you have V2.+ of GD.
45 // What this means is the *driver* decides when to use 24 bit or 8 bit
46 // modes for PNG files. The logic is rather simple - if you have less than
47 // 257 colours, it is set to 8 bit mode, if more then it's in 24 bit mode.
48 // This should work fine for most people, most of the time, in most
49 // situations; however, it can be overridden in case it has to via the
50 // "-drvopt" command line switch. The png driver has two related settings:
51 // 8bit and
52 // 24bit
53 //
54 // If either of these command line toggles are set, that mode becomes the
55 // standard used regardless of the number of colours used. It can be envoked
56 // as follows:
57 // x08c -dev png -drvopt 8bit -fam -o 8bitpng
58 // or
59 // x08c -dev png -drvopt 24bit -fam -o 24bitpng
60 //
61 // NOTE:
62 // The 24 bit PNG file is an RGBA file, not RGB - it includes alpha channel
63 // (transparency). Transparency is set to opaque, but the fact it is an
64 // RGBA and not an RGB might cause some problems with some viewers.
65 // Sadly, I can't do anything about it... sorry.
66 //
67 // GIF files can only have 256 colours, so naturally truecolour mode is not
68 // supported for this sub-driver.
69 //
70 // Stuff for GD V1.? as well as V2.+
71 //
72 // optimise
73 //
74 // From version 1.17 of the GD driver, a command line option has been
75 // added to try and optimise the PNG files. If successful, the optimise
76 // command will create 4 bit (16 colour) PNGs instead of 8 bit (256 colour)
77 // ones. This results in slightly smaller files with no loss in any colour
78 // information. The function has no real memory overhead, but does have a
79 // slight speed hit in exchange for the optimisation. For example:
80 // x08c -dev png -drvopt 8bit,optimise -fam -o 8bitpng
81 // forces the png driver to make 8bit pngs, and will then optimise any PNG
82 // images with 16 or less colours into a 4 bit PNG. Note, this DOESN'T WORK
83 // WITH 24bit PNGs yet, and will never work with JPEGs.
84 //
85 //
86 // Also as of version 1.17 of the GD driver, the options for palette
87 // modification previously set with the command line option "-hack" have
88 // now been moved to two options settable from the -drvopt switch.
89 //
90 // def_black15
91 //
92 // -drvopt def_black15 sets index 15, usually white, to black if index 0,
93 // the background colour and usually black, has been set to white from the
94 // command line option -bg
95 //
96 // swp_red15
97 //
98 // -drvopt swp_red15 swaps index 15, usually white, with index 1, which is
99 // usually red. This might be desirable occasionally, but it is principally
100 // included for cases when the background has been set on the command line
101 // to white, and the "def_black15" option has been issued to redefine index
102 // 15 as black. By issuing a command like:
103 // x08c -dev png -bg ffffff -drvopt def_black15,swp_red15
104 // the driver will set the background to white, then redefine index 15 of
105 // cmap0, which is usually white to black, then swap index 2 (red) to 15
106 // (white originally, now black), so at the end of the day, the "default"
107 // plotting colour is now black. Why do all of this ? It is a very quick
108 // way of making a nice web-friendly png without having to redefine the
109 // cmaps within your program.
110 //
111 // smoothlines
112 //
113 // -drvopt smoothlines=1 turns on anti-aliased line and polygong drawing if
114 // you are using a 24bit mode. Unfortunately gd doesn't honour line
115 // width when anti-aliasing, so by default it is off.
116 //
117 
118 
119 #include "plDevs.h"
120 
121 #if defined ( PLD_png ) || defined ( PLD_jpeg ) || defined ( PLD_gif )
122 
123 #include "plplotP.h"
124 #include "drivers.h"
125 
126 #include <gd.h>
127 
128 // Device info
129 //
130 // Don't knoq if all this logic is necessary, but basically we are going to
131 // start with all three sub-drivers present, then work out way down to two
132 // and finally one of each.
133 //
134 
135 PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_gd =
136 #if defined ( PLD_png )
137  "png:PNG file:0:gd:39:png\n"
138 #endif
139 #if defined ( PLD_jpeg )
140  "jpeg:JPEG file:0:gd:40:jpeg\n"
141 #endif
142 #if defined ( PLD_gif )
143  "gif:GIF file:0:gd:47:gif\n"
144 #endif
145 ;
146 
147 #if GD2_VERS >= 2
148 #ifdef PL_HAVE_FREETYPE
149 #define SMOOTH_LINES_OK
150 #endif
151 #endif
152 
153 #ifdef PL_HAVE_FREETYPE
154 
155 //
156 // Freetype support has been added to the GD family of drivers using the
157 // plfreetype.c module, and implemented as a driver-specific optional extra
158 // invoked via the -drvopt command line toggle. It uses the
159 // "PLESC_HAS_TEXT" command for rendering within the driver.
160 //
161 // Freetype support is turned on/off at compile time by defining
162 // "PL_HAVE_FREETYPE".
163 //
164 // To give the user some level of control over the fonts that are used,
165 // environmental variables can be set to over-ride the definitions used by
166 // the five default plplot fonts.
167 //
168 // Freetype rendering is used with the command line "-drvopt text".
169 // Anti-aliased fonts can be used by issuing "-drvopt text,smooth"
170 //
171 
172 #include "plfreetype.h"
173 
174 #endif
175 
176 // Prototypes for functions in this file.
177 
178 static void fill_polygon( PLStream *pls );
179 static void setcmap( PLStream *pls );
180 static void plD_init_png_Dev( PLStream *pls );
181 static void plD_gd_optimise( PLStream *pls );
182 static void plD_black15_gd( PLStream *pls );
183 static void plD_red15_gd( PLStream *pls );
184 #ifdef PLD_gif
185 static void plD_init_gif_Dev( PLStream *pls );
186 #endif
187 
188 #ifdef PL_HAVE_FREETYPE
189 
190 static void plD_pixel_gd( PLStream *pls, short x, short y );
191 static PLINT plD_read_pixel_gd( PLStream *pls, short x, short y );
192 static void plD_set_pixel_gd( PLStream *pls, short x, short y, PLINT colour );
193 static void init_freetype_lv1( PLStream *pls );
194 static void init_freetype_lv2( PLStream *pls );
195 
196 #endif
197 
198 // top level declarations
199 
200 static int NCOLOURS = gdMaxColors;
201 
202 // In an attempt to fix a problem with the hidden line removal functions
203 // that results in hidden lines *not* being removed from "small" plot
204 // pages (ie, like a normal video screen), a "virtual" page of much
205 // greater size is used to trick the algorithm into working correctly.
206 // If, in future, this gets fixed on its own, then don't define
207 // "use_experimental_hidden_line_hack"
208 //
209 
210 #define use_experimental_hidden_line_hack
211 
212 // I think the current version of Freetype supports up to a maximum of
213 // 128 grey levels for text smoothing. You can get quite acceptable
214 // results with as few as 4 grey-levels. Uusually only about 5 get used
215 // anyway, but the question is where, in the "grey spectrum" will they be ?
216 // Who knows ? The following define lets you set a maximum limit on the
217 // number of grey-levels used. It is really only here for the 24bit mode
218 // and could be set to 255, but that would slow things down and use more
219 // memory. 64 seems to be a nice compromise, but if you want to change it,
220 // then change it here.
221 //
222 
223 #ifndef max_number_of_grey_levels_used_in_text_smoothing
224 #define max_number_of_grey_levels_used_in_text_smoothing 64
225 #endif
226 
227 // Not present in versions before 2.0
228 
229 #ifndef gdImagePalettePixel
230 #define gdImagePalettePixel( im, x, y ) ( im )->pixels[( y )][( x )]
231 #endif
232 
233 #if GD2_VERS >= 2
234 int plToGdAlpha( PLFLT a )
235 {
236  int tmp = (int) ( ( 1.0 - a ) * gdAlphaMax );
237  return tmp;
238 }
239 #endif
240 
241 // Struct to hold device-specific info.
242 
243 typedef struct
244 {
245  gdImagePtr im_out; // Graphics pointer
246  PLINT pngx;
247  PLINT pngy;
248 
249  int colour; // Current Colour
250  int totcol; // Total number of colours
251  int ncol1; // Actual size of ncol1 we got
252 
253  PLFLT scale; // scaling factor to "blow up" to
254  // the "virtual" page in removing hidden lines
255 
256  int optimise; // Flag used for 4bit pngs
257  int black15; // Flag used for forcing a black colour
258  int red15; // Flag for swapping red and 15
259 
260  unsigned char TRY_BLENDED_ANTIALIASING; // Flag to try and set up BLENDED ANTIALIASING
261 
262 #if GD2_VERS >= 2
263  int truecolour; // Flag to ALWAYS force 24 bit mode
264  int palette; // Flag to ALWAYS force 8 bit mode
265  unsigned char smooth; // Flag to ask for line smoothing
266 #endif
267 } png_Dev;
268 
269 void plD_init_png( PLStream * );
270 void plD_line_png( PLStream *, short, short, short, short );
271 void plD_polyline_png( PLStream *, short *, short *, PLINT );
272 void plD_eop_png( PLStream * );
273 void plD_eop_jpeg( PLStream * );
274 void plD_bop_png( PLStream * );
275 void plD_tidy_png( PLStream * );
276 void plD_state_png( PLStream *, PLINT );
277 void plD_esc_png( PLStream *, PLINT, void * );
278 #ifdef PLD_gif
279 void plD_init_gif( PLStream * );
280 void plD_eop_gif( PLStream * );
281 #endif
282 
283 #ifdef PLD_png
284 
286 {
287 #ifndef ENABLE_DYNDRIVERS
288  pdt->pl_MenuStr = "PNG file";
289  pdt->pl_DevName = "png";
290 #endif
292  pdt->pl_seq = 39;
293  pdt->pl_init = (plD_init_fp) plD_init_png;
294  pdt->pl_line = (plD_line_fp) plD_line_png;
295  pdt->pl_polyline = (plD_polyline_fp) plD_polyline_png;
296  pdt->pl_eop = (plD_eop_fp) plD_eop_png;
297  pdt->pl_bop = (plD_bop_fp) plD_bop_png;
298  pdt->pl_tidy = (plD_tidy_fp) plD_tidy_png;
299  pdt->pl_state = (plD_state_fp) plD_state_png;
300  pdt->pl_esc = (plD_esc_fp) plD_esc_png;
301 }
302 
303 #endif
304 
305 #ifdef PLD_jpeg
306 
308 {
309 #ifndef ENABLE_DYNDRIVERS
310  pdt->pl_MenuStr = "JPEG File";
311  pdt->pl_DevName = "jpeg";
312 #endif
314  pdt->pl_seq = 40;
315  pdt->pl_init = (plD_init_fp) plD_init_png;
316  pdt->pl_line = (plD_line_fp) plD_line_png;
317  pdt->pl_polyline = (plD_polyline_fp) plD_polyline_png;
318  pdt->pl_eop = (plD_eop_fp) plD_eop_jpeg;
319  pdt->pl_bop = (plD_bop_fp) plD_bop_png;
320  pdt->pl_tidy = (plD_tidy_fp) plD_tidy_png;
321  pdt->pl_state = (plD_state_fp) plD_state_png;
322  pdt->pl_esc = (plD_esc_fp) plD_esc_png;
323 }
324 #endif
325 
326 
327 #ifdef PLD_gif
328 
330 {
331 #ifndef ENABLE_DYNDRIVERS
332  pdt->pl_MenuStr = "GIF File";
333  pdt->pl_DevName = "gif";
334 #endif
336  pdt->pl_seq = 47;
337  pdt->pl_init = (plD_init_fp) plD_init_gif;
338  pdt->pl_line = (plD_line_fp) plD_line_png;
339  pdt->pl_polyline = (plD_polyline_fp) plD_polyline_png;
340  pdt->pl_eop = (plD_eop_fp) plD_eop_gif;
341  pdt->pl_bop = (plD_bop_fp) plD_bop_png;
342  pdt->pl_tidy = (plD_tidy_fp) plD_tidy_png;
343  pdt->pl_state = (plD_state_fp) plD_state_png;
344  pdt->pl_esc = (plD_esc_fp) plD_esc_png;
345 }
346 #endif
347 
348 
349 //--------------------------------------------------------------------------
350 // plD_init_png_Dev()
351 //
352 //--------------------------------------------------------------------------
353 
354 static void
355 plD_init_png_Dev( PLStream *pls )
356 {
357  png_Dev *dev;
358 
359 // Stuff for the driver options, these vars are copied into the driver
360 // structure so that everything is thread safe and reenterant.
361 //
362 
363  static int optimise = 0;
364  static int black15 = 0;
365  static int red15 = 0;
366 #if GD2_VERS >= 2
367  static int truecolour = 0;
368  static int palette = 0;
369  static int smooth_line = 0;
370 #endif
371 #ifdef PL_HAVE_FREETYPE
372  static int freetype = 1;
373  static int smooth_text = 1;
374  FT_Data *FT;
375 #endif
376 
377  DrvOpt gd_options[] = { { "optimise", DRV_INT, &optimise, "Optimise PNG palette when possible" },
378  { "def_black15", DRV_INT, &black15, "Define idx 15 as black. If the background is \"whiteish\" (from \"-bg\" option), force index 15 (traditionally white) to be \"black\"" },
379  { "swp_red15", DRV_INT, &red15, "Swap index 1 (usually red) and 1 (usually white); always done after \"black15\"; quite useful for quick changes to web pages" },
380 #if GD2_VERS >= 2
381  { "8bit", DRV_INT, &palette, "Palette (8 bit) mode" },
382  { "24bit", DRV_INT, &truecolour, "Truecolor (24 bit) mode" },
383  { "smoothlines", DRV_INT, &smooth_line, "Turn line Anti Aliasing on (1) or off (0)" },
384 #endif
385 #ifdef PL_HAVE_FREETYPE
386  { "text", DRV_INT, &freetype, "Use driver text (FreeType)" },
387  { "smooth", DRV_INT, &smooth_text, "Turn text smoothing on (1) or off (0)" },
388 #endif
389  { NULL, DRV_INT, NULL, NULL } };
390 
391 
392 // Allocate and initialize device-specific data
393 
394  if ( pls->dev != NULL )
395  free( (void *) pls->dev );
396 
397  pls->dev = calloc( 1, (size_t) sizeof ( png_Dev ) );
398  if ( pls->dev == NULL )
399  plexit( "plD_init_png_Dev: Out of memory." );
400 
401  dev = (png_Dev *) pls->dev;
402 
403  dev->colour = 1; // Set a fall back pen colour in case user doesn't
404 
405 
406 // Check for and set up driver options
407 
408  plParseDrvOpts( gd_options );
409 
410  dev->black15 = black15;
411  dev->red15 = red15;
412  dev->optimise = optimise;
413 
414 #if GD2_VERS >= 2
415 
416  dev->palette = palette;
417  dev->truecolour = truecolour;
418 
419 
420 
421  if ( ( dev->truecolour > 0 ) && ( dev->palette > 0 ) )
422  plwarn( "Selecting both \"truecolor\" AND \"palette\" driver options is contradictory, so\nI will just use my best judgment.\n" );
423  else if ( dev->truecolour > 0 )
424  NCOLOURS = 16777216;
425  else if ( ( dev->truecolour == 0 ) && ( dev->palette == 0 ) && ( ( pls->ncol1 + pls->ncol0 ) > NCOLOURS ) )
426  {
427  NCOLOURS = 16777216;
428  }
429 
430  if ( ( dev->palette == 0 ) && ( dev->optimise == 0 ) && ( smooth_line == 1 ) )
431  dev->smooth = 1; // Allow smoothing of lines if we have a truecolour device
432 
433 #endif
434 
435 #ifdef PL_HAVE_FREETYPE
436  if ( freetype )
437  {
438  pls->dev_text = 1; // want to draw text
439  pls->dev_unicode = 1; // want unicode
440 
441  // As long as we aren't optimising, we'll try to use better antialaising
442  // We can also only do this if the user wants smoothing, and hasn't
443  // selected a palette mode.
444  //
445 
446 
447  init_freetype_lv1( pls );
448  FT = (FT_Data *) pls->FT;
449  FT->want_smooth_text = smooth_text > 0 ? 1 : 0;
450  if ( ( dev->optimise == 0 ) && ( dev->palette == 0 ) && ( smooth_text != 0 ) )
451  {
452  FT->BLENDED_ANTIALIASING = 1;
453  dev->truecolour = 1;
454  }
455  }
456 
457 #endif
458 }
459 
460 //--------------------------------------------------------------------------
461 // plD_init_png()
462 //
463 // Initialize device.
464 //--------------------------------------------------------------------------
465 
466 void plD_init_png( PLStream *pls )
467 {
468  png_Dev *dev = NULL;
469 
470  pls->termin = 0; // Not an interactive device
471  pls->icol0 = 1;
472  pls->bytecnt = 0;
473  pls->page = 0;
474  pls->dev_fill0 = 1; // Can do solid fills
475 
476  if ( !pls->colorset )
477  pls->color = 1; // Is a color device
478 
479 // Initialize family file info
480  plFamInit( pls );
481 
482 // Prompt for a file name if not already set
483  plOpenFile( pls );
484 
485 // Allocate and initialize device-specific data
486  plD_init_png_Dev( pls );
487  dev = (png_Dev *) pls->dev;
488 
489  if ( pls->xlength <= 0 || pls->ylength <= 0 )
490  {
491 // use default width, height of 800x600 if not specifed by -geometry option
492 // or plspage
493  plspage( 0., 0., 800, 600, 0, 0 );
494  }
495 
496  pls->graphx = GRAPHICS_MODE;
497 
498  dev->pngx = pls->xlength - 1; // should I use -1 or not???
499  dev->pngy = pls->ylength - 1;
500 
501 #ifdef use_experimental_hidden_line_hack
502 
503  if ( dev->pngx > dev->pngy ) // Work out the scaling factor for the
504  { // "virtual" (oversized) page
505  dev->scale = (PLFLT) ( PIXELS_X - 1 ) / (PLFLT) dev->pngx;
506  }
507  else
508  {
509  dev->scale = (PLFLT) PIXELS_Y / (PLFLT) dev->pngy;
510  }
511 #else
512 
513  dev->scale = 1.;
514 
515 #endif
516 
517 
518  if ( pls->xdpi <= 0 )
519  {
520 // This corresponds to a typical monitor resolution of 4 pixels/mm.
521  plspage( 4. * 25.4, 4. * 25.4, 0, 0, 0, 0 );
522  }
523  else
524  {
525  pls->ydpi = pls->xdpi; // Set X and Y dpi's to the same value
526  }
527 // Convert DPI to pixels/mm
528  plP_setpxl( dev->scale * pls->xdpi / 25.4, dev->scale * pls->ydpi / 25.4 );
529 
530  plP_setphy( 0, dev->scale * dev->pngx, 0, dev->scale * dev->pngy );
531 
532 #ifdef PL_HAVE_FREETYPE
533  if ( pls->dev_text )
534  {
535  init_freetype_lv2( pls );
536  }
537 #endif
538 }
539 
540 
541 #ifdef PLD_gif
542 
543 //--------------------------------------------------------------------------
544 // plD_init_gif_Dev()
545 //
546 // We need a new initialiser for the GIF version of the GD driver because
547 // the GIF one does not support TRUECOLOUR
548 //--------------------------------------------------------------------------
549 
550 static void
551 plD_init_gif_Dev( PLStream *pls )
552 {
553  png_Dev *dev;
554 
555 // Stuff for the driver options, these vars are copied into the driver
556 // structure so that everything is thread safe and reenterant.
557 //
558 
559  static int black15 = 0;
560  static int red15 = 0;
561 #ifdef PL_HAVE_FREETYPE
562  static int freetype = 1;
563  static int smooth_text = 0;
564  FT_Data *FT;
565 #endif
566 
567  DrvOpt gd_options[] = { { "def_black15", DRV_INT, &black15, "Define idx 15 as black. If the background is \"whiteish\" (from \"-bg\" option), force index 15 (traditionally white) to be \"black\"" },
568  { "swp_red15", DRV_INT, &red15, "Swap index 1 (usually red) and 1 (usually white); always done after \"black15\"; quite useful for quick changes to web pages" },
569 #ifdef PL_HAVE_FREETYPE
570  { "text", DRV_INT, &freetype, "Use driver text (FreeType)" },
571  { "smooth", DRV_INT, &smooth_text, "Turn text smoothing on (1) or off (0)" },
572 #endif
573  { NULL, DRV_INT, NULL, NULL } };
574 
575 
576 // Allocate and initialize device-specific data
577 
578  if ( pls->dev != NULL )
579  free( (void *) pls->dev );
580 
581  pls->dev = calloc( 1, (size_t) sizeof ( png_Dev ) );
582  if ( pls->dev == NULL )
583  plexit( "plD_init_gif_Dev: Out of memory." );
584 
585  dev = (png_Dev *) pls->dev;
586 
587  dev->colour = 1; // Set a fall back pen colour in case user doesn't
588 
589 // Check for and set up driver options
590 
591  plParseDrvOpts( gd_options );
592 
593  dev->black15 = black15;
594  dev->red15 = red15;
595 
596  dev->optimise = 0; // Optimise does not work for GIFs... should, but it doesn't
597  dev->palette = 1; // Always use palette mode for GIF files
598  dev->truecolour = 0; // Never have truecolour in GIFS
599 
600 #ifdef PL_HAVE_FREETYPE
601  if ( freetype )
602  {
603  pls->dev_text = 1; // want to draw text
604  pls->dev_unicode = 1; // want unicode
605 
606  init_freetype_lv1( pls );
607  FT = (FT_Data *) pls->FT;
608 
609  FT->want_smooth_text = smooth_text > 0 ? 1 : 0;
610  }
611 
612 #endif
613 }
614 
615 //--------------------------------------------------------------------------
616 // plD_init_gif()
617 //
618 // Initialize device.
619 //--------------------------------------------------------------------------
620 
621 void plD_init_gif( PLStream *pls )
622 {
623  png_Dev *dev = NULL;
624 
625  pls->termin = 0; // Not an interactive device
626  pls->icol0 = 1;
627  pls->bytecnt = 0;
628  pls->page = 0;
629  pls->dev_fill0 = 1; // Can do solid fills
630 
631  if ( !pls->colorset )
632  pls->color = 1; // Is a color device
633 
634 // Initialize family file info
635  plFamInit( pls );
636 
637 // Prompt for a file name if not already set
638  plOpenFile( pls );
639 
640 // Allocate and initialize device-specific data
641  plD_init_gif_Dev( pls );
642  dev = (png_Dev *) pls->dev;
643 
644  if ( pls->xlength <= 0 || pls->ylength <= 0 )
645  {
646 // use default width, height of 800x600 if not specifed by -geometry option
647 // or plspage
648  plspage( 0., 0., 800, 600, 0, 0 );
649  }
650 
651  pls->graphx = GRAPHICS_MODE;
652 
653  dev->pngx = pls->xlength - 1; // should I use -1 or not???
654  dev->pngy = pls->ylength - 1;
655 
656 #ifdef use_experimental_hidden_line_hack
657 
658  if ( dev->pngx > dev->pngy ) // Work out the scaling factor for the
659  { // "virtual" (oversized) page
660  dev->scale = (PLFLT) ( PIXELS_X - 1 ) / (PLFLT) dev->pngx;
661  }
662  else
663  {
664  dev->scale = (PLFLT) PIXELS_Y / (PLFLT) dev->pngy;
665  }
666 #else
667 
668  dev->scale = 1.;
669 
670 #endif
671 
672 
673  if ( pls->xdpi <= 0 )
674  {
675 // This corresponds to a typical monitor resolution of 4 pixels/mm.
676  plspage( 4. * 25.4, 4. * 25.4, 0, 0, 0, 0 );
677  }
678  else
679  {
680  pls->ydpi = pls->xdpi; // Set X and Y dpi's to the same value
681  }
682 // Convert DPI to pixels/mm
683  plP_setpxl( dev->scale * pls->xdpi / 25.4, dev->scale * pls->ydpi / 25.4 );
684 
685  plP_setphy( 0, dev->scale * dev->pngx, 0, dev->scale * dev->pngy );
686 
687 #ifdef PL_HAVE_FREETYPE
688  if ( pls->dev_text )
689  {
690  init_freetype_lv2( pls );
691  }
692 #endif
693 }
694 
695 #endif
696 
697 
698 //--------------------------------------------------------------------------
699 // plD_line_png()
700 //
701 // Draw a line in the current color from (x1,y1) to (x2,y2).
702 //--------------------------------------------------------------------------
703 
704 void
705 plD_line_png( PLStream *pls, short x1a, short y1a, short x2a, short y2a )
706 {
707  png_Dev *dev = (png_Dev *) pls->dev;
708  int x1 = x1a / dev->scale, y1 = y1a / dev->scale, x2 = x2a / dev->scale, y2 = y2a / dev->scale;
709  y1 = dev->pngy - y1;
710  y2 = dev->pngy - y2;
711 
712  #ifdef SMOOTH_LINES_OK
713  if ( dev->smooth == 1 )
714  {
715  gdImageSetAntiAliased( dev->im_out, dev->colour );
716  gdImageLine( dev->im_out, x1, y1, x2, y2, gdAntiAliased );
717  }
718  else
719  {
720  gdImageLine( dev->im_out, x1, y1, x2, y2, dev->colour );
721  }
722  #else
723  gdImageLine( dev->im_out, x1, y1, x2, y2, dev->colour );
724  #endif
725 }
726 
727 //--------------------------------------------------------------------------
728 // plD_polyline_png()
729 //
730 // Draw a polyline in the current color.
731 //--------------------------------------------------------------------------
732 
733 void
734 plD_polyline_png( PLStream *pls, short *xa, short *ya, PLINT npts )
735 {
736  PLINT i;
737 
738  for ( i = 0; i < npts - 1; i++ )
739  plD_line_png( pls, xa[i], ya[i], xa[i + 1], ya[i + 1] );
740 }
741 
742 
743 //--------------------------------------------------------------------------
744 // fill_polygon()
745 //
746 // Fill polygon described in points pls->dev_x[] and pls->dev_y[].
747 //--------------------------------------------------------------------------
748 
749 static void
750 fill_polygon( PLStream *pls )
751 {
752  png_Dev *dev = (png_Dev *) pls->dev;
753 
754  int i;
755  gdPoint *points = NULL;
756 
757  if ( pls->dev_npts < 1 )
758  return;
759 
760  points = malloc( (size_t) pls->dev_npts * sizeof ( gdPoint ) );
761 
762  for ( i = 0; i < pls->dev_npts; i++ )
763  {
764  points[i].x = pls->dev_x[i] / dev->scale;
765  points[i].y = dev->pngy - ( pls->dev_y[i] / dev->scale );
766  }
767 
768  #ifdef SMOOTH_LINES_OK
769  if ( dev->smooth == 1 )
770  {
771  gdImageSetAntiAliased( dev->im_out, dev->colour );
772  gdImageFilledPolygon( dev->im_out, points, pls->dev_npts, gdAntiAliased );
773  }
774  else
775  {
776  gdImageFilledPolygon( dev->im_out, points, pls->dev_npts, dev->colour );
777  }
778  #else
779  gdImageFilledPolygon( dev->im_out, points, pls->dev_npts, dev->colour );
780  #endif
781 
782  free( points );
783 }
784 
785 //--------------------------------------------------------------------------
786 // setcmap()
787 //
788 // Sets up color palette.
789 //--------------------------------------------------------------------------
790 
791 static void
792 setcmap( PLStream *pls )
793 {
794  int i, ncol1 = pls->ncol1;
795  int ncol0 = pls->ncol0, total_colours;
796  PLColor cmap1col;
797  png_Dev *dev = (png_Dev *) pls->dev;
798  PLFLT tmp_colour_pos;
799 
800 //
801 // Yuckky fix to get rid of the previosuly allocated palette from the
802 // GD image
803 //
804 
805  if ( dev->im_out != NULL )
806  {
807  for ( i = 0; i < 256; i++ )
808  {
809  gdImageColorDeallocate( dev->im_out, i );
810  }
811  }
812 
813  if ( ncol0 > NCOLOURS / 2 ) // Check for ridiculous number of colours
814  { // in ncol0, and appropriately adjust the
815  plwarn( "Too many colours in cmap0." ); // number, issuing a
816  ncol0 = NCOLOURS / 2; // warning if it does
817  pls->ncol0 = ncol0;
818  }
819 
820  dev->totcol = 0; // Reset the number of colours counter to zero
821 
822  total_colours = ncol0 + ncol1; // Work out how many colours are wanted
823 
824  if ( total_colours > NCOLOURS ) // Do some rather modest error
825  { // checking to make sure that
826  total_colours = NCOLOURS; // we are not defining more colours
827  ncol1 = total_colours - ncol0; // than we have room for.
828 
829  if ( ncol1 <= 0 )
830  {
831  plexit( "Problem setting colourmap in PNG or JPEG driver." );
832  }
833  }
834 
835  dev->ncol1 = ncol1; // The actual size of ncol1, regardless of what was asked.
836  // This is dependent on colour slots available.
837  // It might well be the same as ncol1.
838  //
839 
840 // Initialize cmap 0 colors
841 
842  if ( ( ncol0 > 0 ) && ( dev->im_out != NULL ) ) // make sure the program actually asked for cmap0 first
843  {
844  for ( i = 0; i < ncol0; i++ )
845  {
846 #if GD2_VERS >= 2
847  gdImageColorAllocateAlpha( dev->im_out,
848  pls->cmap0[i].r, pls->cmap0[i].g, pls->cmap0[i].b,
849  plToGdAlpha( pls->cmap0[i].a ) );
850 #else
851  gdImageColorAllocate( dev->im_out,
852  pls->cmap0[i].r, pls->cmap0[i].g, pls->cmap0[i].b );
853 #endif
854  ++dev->totcol; // count the number of colours we use as we use them
855  }
856  }
857 
858 // Initialize any remaining slots for cmap1
859 
860 
861  if ( ( ncol1 > 0 ) && ( dev->im_out != NULL ) ) // make sure that we want to define cmap1 first
862  {
863  for ( i = 0; i < ncol1; i++ )
864  {
865  if ( ncol1 < pls->ncol1 ) // Check the dynamic range of colours
866  {
867  //
868  // Ok, now if we have less colour slots available than are being
869  // defined by pls->ncol1, then we still want to use the full
870  // dynamic range of cmap1 as best we can, so what we do is work
871  // out an approximation to the index in the full dynamic range
872  // in cases when pls->ncol1 exceeds the number of free colours.
873  //
874 
875  tmp_colour_pos = i > 0 ? pls->ncol1 * ( (PLFLT) i / ncol1 ) : 0;
876  plcol_interp( pls, &cmap1col, (int) tmp_colour_pos, pls->ncol1 );
877  }
878  else
879  {
880  plcol_interp( pls, &cmap1col, i, ncol1 );
881  }
882 
883 
884 #if GD2_VERS >= 2
885  gdImageColorAllocateAlpha( dev->im_out,
886  cmap1col.r, cmap1col.g, cmap1col.b,
887  plToGdAlpha( cmap1col.a ) );
888 #else
889  gdImageColorAllocate( dev->im_out,
890  cmap1col.r, cmap1col.g, cmap1col.b );
891 #endif
892 
893  ++dev->totcol; // count the number of colours we use as we go
894  }
895  }
896 }
897 
898 
899 //--------------------------------------------------------------------------
900 // plD_state_png()
901 //
902 // Handle change in PLStream state (color, pen width, fill attribute, etc).
903 //--------------------------------------------------------------------------
904 
905 void
906 plD_state_png( PLStream *pls, PLINT op )
907 {
908  png_Dev *dev = (png_Dev *) pls->dev;
909  PLFLT tmp_colour_pos;
910 #if GD2_VERS >= 2
911  long temp_col;
912 #endif
913 
914 
915  switch ( op )
916  {
917 #if GD2_VERS >= 2
918  case PLSTATE_WIDTH:
919  gdImageSetThickness( dev->im_out, pls->width );
920  break;
921 #endif
922 
923  case PLSTATE_COLOR0:
924 #if GD2_VERS >= 2
925 
926  if ( ( pls->icol0 == PL_RGB_COLOR ) || // Should never happen since PL_RGB_COLOR is depreciated, but here for backwards compatibility
927  ( gdImageTrueColor( dev->im_out ) ) ) // We will do this if we are in "TrueColour" mode
928  {
929  if ( ( dev->totcol < NCOLOURS ) || // See if there are slots left, if so we will allocate a new colour
930  ( gdImageTrueColor( dev->im_out ) ) ) // In TrueColour mode we allocate each colour as we come to it
931  {
932  // Next allocate a new colour to a temporary slot since what we do with it will vary depending on if its a palette index or truecolour
933 #if GD2_VERS >= 2
934  temp_col = gdImageColorAllocateAlpha( dev->im_out, pls->curcolor.r,
935  pls->curcolor.g, pls->curcolor.b,
936  plToGdAlpha( pls->curcolor.a ) );
937 #else
938  temp_col = gdImageColorAllocate( dev->im_out, pls->curcolor.r,
939  pls->curcolor.g, pls->curcolor.b );
940 #endif
941 
942  if ( gdImageTrueColor( dev->im_out ) )
943  dev->colour = temp_col; // If it's truecolour, then we will directly set dev->colour to our "new" colour
944  else
945  {
946  dev->colour = dev->totcol; // or else, we will just set it to the last colour
947  dev->totcol++; // Bump the total colours for next time round
948  }
949  }
950  }
951  else // just a normal colour allocate, so don't worry about the above stuff, just grab the index
952  {
953  dev->colour = pls->icol0;
954  }
955 
956 #else
957  dev->colour = pls->icol0;
958  if ( dev->colour == PL_RGB_COLOR )
959  {
960  if ( dev->totcol < NCOLOURS )
961  {
962 #if GD2_VERS >= 2
963  gdImageColorAllocateAlpha( dev->im_out, pls->curcolor.r,
964  pls->curcolor.g, pls->curcolor.b,
965  plToGdAlpha( pls->curcolor.a ) );
966 #else
967  gdImageColorAllocate( dev->im_out, pls->curcolor.r,
968  pls->curcolor.g, pls->curcolor.b );
969 #endif
970  dev->colour = dev->totcol;
971  }
972  }
973 #endif
974  break;
975 
976  case PLSTATE_COLOR1:
977 
978 #if GD2_VERS >= 2
979  if ( !gdImageTrueColor( dev->im_out ) )
980  {
981 #endif
982  //
983  // Start by checking to see if we have to compensate for cases where
984  // we don't have the full dynamic range of cmap1 at our disposal
985  //
986  if ( dev->ncol1 < pls->ncol1 )
987  {
988  tmp_colour_pos = dev->ncol1 * ( (PLFLT) pls->icol1 / ( pls->ncol1 > 0 ? pls->ncol1 : 1 ) );
989  dev->colour = pls->ncol0 + (int) tmp_colour_pos;
990  }
991  else
992  dev->colour = pls->ncol0 + pls->icol1;
993 #if GD2_VERS >= 2
994  }
995  else // it is a truecolour image
996  {
997 #if GD2_VERS >= 2
998  dev->colour = gdTrueColorAlpha( pls->curcolor.r, pls->curcolor.g,
999  pls->curcolor.b,
1000  plToGdAlpha( pls->curcolor.a ) );
1001 #else
1002  dev->colour = gdTrueColor( pls->curcolor.r, pls->curcolor.g,
1003  pls->curcolor.b );
1004 #endif
1005  }
1006 #endif
1007  break;
1008 
1009 
1010  case PLSTATE_CMAP0:
1011  case PLSTATE_CMAP1:
1012 
1013 #if GD2_VERS >= 2
1014  if ( ( dev->im_out != NULL ) && !gdImageTrueColor( dev->im_out ) )
1015  {
1016 #endif
1017 
1018  //
1019  // Code to redefine the entire palette
1020  //
1021 
1022 
1023  if ( pls->color )
1024  setcmap( pls );
1025 
1026 #if GD2_VERS >= 2
1027  }
1028 #endif
1029 
1030  break;
1031  }
1032 }
1033 
1034 
1035 //--------------------------------------------------------------------------
1036 // plD_esc_png()
1037 //
1038 // Escape function.
1039 //--------------------------------------------------------------------------
1040 
1041 void plD_esc_png( PLStream *pls, PLINT op, void *ptr )
1042 {
1043  switch ( op )
1044  {
1045  case PLESC_FILL: // fill
1046  fill_polygon( pls );
1047  break;
1048 
1049 #ifdef PL_HAVE_FREETYPE
1050  case PLESC_HAS_TEXT:
1051  plD_render_freetype_text( pls, (EscText *) ptr );
1052  break;
1053 #endif
1054  }
1055 }
1056 
1057 //--------------------------------------------------------------------------
1058 // plD_bop_png()
1059 //
1060 // Set up for the next page.
1061 // Advance to next family file if necessary (file output).
1062 //--------------------------------------------------------------------------
1063 
1064 void plD_bop_png( PLStream *pls )
1065 {
1066  png_Dev *dev;
1067 
1068  plGetFam( pls );
1069 // force new file if pls->family set for all subsequent calls to plGetFam
1070 // n.b. putting this after plGetFam call is important since plinit calls
1071 // bop, and you don't want the familying sequence started until after
1072 // that first call to bop.
1073 
1074 // n.b. pls->dev can change because of an indirect call to plD_init_png
1075 // from plGetFam if familying is enabled. Thus, wait to define dev until
1076 // now.
1077 
1078  dev = (png_Dev *) pls->dev;
1079 
1080  pls->famadv = 1;
1081 
1082  pls->page++;
1083 
1084  if ( dev->black15 )
1085  plD_black15_gd( pls );
1086  if ( dev->red15 )
1087  plD_red15_gd( pls );
1088 
1089 #if GD2_VERS >= 2
1090  if ( ( ( ( ( dev->truecolour > 0 ) && ( dev->palette > 0 ) ) || // In an EXTREMELY convaluted
1091  ( ( dev->truecolour == 0 ) && ( dev->palette == 0 ) ) ) && // manner, all this is just
1092  ( ( pls->ncol1 + pls->ncol0 ) <= 256 ) ) || // asking the question, do we
1093  ( ( ( dev->palette > 0 ) && ( dev->truecolour == 0 ) ) ) ) // want truecolour or not ?
1094  {
1095 #endif
1096 
1097  dev->im_out = gdImageCreate( pls->xlength, pls->ylength );
1098 
1099  setcmap( pls );
1100 
1101 #if GD2_VERS >= 2
1102 }
1103 else
1104 {
1105  dev->im_out = gdImageCreateTrueColor( pls->xlength, pls->ylength );
1107 
1108 //
1109 // In truecolour mode, the background colour GD makes is ALWAYS black, so to
1110 // "simulate" (stimulate?) a background colour other than black, we will just
1111 // draw a dirty big rectange covering the whole image and colour it in
1112 // whatever colour cmap0[0] happens to be.
1113 //
1114 // Question to C gurus: while it is slightly illogical and ugly, would:
1115 // if ((pls->cmap0[0].r+pls->cmap0[0].g+pls->cmap0[0].b)!=0)
1116 // be more computationally efficient than:
1117 // if ((pls->cmap0[0].r!=0)||(pls->cmap0[0].g!=0)||(pls->cmap0[0].b!=0))
1118 // ???
1119 //
1120 
1121  if ( ( pls->cmap0[0].r != 0 ) || ( pls->cmap0[0].g != 0 ) ||
1122  ( pls->cmap0[0].b != 0 ) || ( pls->cmap0[0].a != 0.0 ) )
1123  {
1124  gdImageFilledRectangle( dev->im_out, 0, 0, pls->xlength - 1, pls->ylength - 1,
1125  gdTrueColorAlpha( pls->cmap0[0].r, pls->cmap0[0].g,
1126  pls->cmap0[0].b,
1127  plToGdAlpha( pls->cmap0[0].a ) ) );
1128  }
1129 }
1130 
1131 
1132 // This ensures the line width is set correctly at the beginning of
1133 // each page
1134 
1135 plD_state_png( pls, PLSTATE_WIDTH );
1136 
1137 #endif
1138 }
1139 
1140 //--------------------------------------------------------------------------
1141 // plD_tidy_png()
1142 //
1143 // Close graphics file or otherwise clean up.
1144 //--------------------------------------------------------------------------
1145 
1146 void plD_tidy_png( PLStream *pls )
1147 {
1148 #ifdef PL_HAVE_FREETYPE
1149  if ( pls->dev_text )
1150  {
1151  plD_FreeType_Destroy( pls );
1152  }
1153 #endif
1154 
1155  plCloseFile( pls );
1156  free_mem( pls->dev );
1157 }
1158 
1159 //--------------------------------------------------------------------------
1160 // plD_black15_gd()
1161 //
1162 // This small function simply redefines index 15 of cmap0, which is
1163 // usually set to white, to black, but only if index 0, which is usually
1164 // black, has been redefined to white (for example, through -bg).
1165 //
1166 //--------------------------------------------------------------------------
1167 
1168 void plD_black15_gd( PLStream *pls )
1169 {
1170  if ( pls->ncol0 > 15 )
1171  {
1172  if ( ( pls->cmap0[0].r > 227 ) && ( pls->cmap0[0].g > 227 ) && ( pls->cmap0[0].b > 227 ) )
1173  {
1174  pls->cmap0[15].r = 0;
1175  pls->cmap0[15].g = 0;
1176  pls->cmap0[15].b = 0;
1177  }
1178  }
1179 }
1180 
1181 
1182 //--------------------------------------------------------------------------
1183 // plD_red15_gd()
1184 //
1185 //
1186 // This function swaps index 1, often the default plotting colour, with
1187 // index 15, the last defined colour.
1188 //
1189 // Colour 15 is usually white, and 1 is usually red, so swapping the two
1190 // might be desirable occasionally, but it is principally here for cases
1191 // when the background has been set on the command line to white, and the
1192 // "def_black15" option has been issued to redefine index 15 as black. By
1193 // issuing a command like
1194 //
1195 // ... -bg ffffff -drvopt def_black15,swp_red15
1196 //
1197 // the driver will set the background to white, then redefine index 15 of
1198 // cmap0, which is usually white to black, then swap index 2 (red) to 15
1199 // (white originally, now black), so at the end of the day, the "default"
1200 // plotting colour is now black. Why do all of this ? It is a very quick
1201 // way of making a nice web-friendly png without having to redefine the
1202 // cmaps within your program.
1203 //
1204 // If you don't like it, don't use it !
1205 //
1206 //--------------------------------------------------------------------------
1207 
1208 void plD_red15_gd( PLStream *pls )
1209 {
1210  char r = pls->cmap0[1].r;
1211  char g = pls->cmap0[1].g;
1212  char b = pls->cmap0[1].b;
1213 
1214  if ( pls->ncol0 > 15 )
1215  {
1216  pls->cmap0[1].r = pls->cmap0[15].r;
1217  pls->cmap0[1].g = pls->cmap0[15].r;
1218  pls->cmap0[1].b = pls->cmap0[15].r;
1219 
1220  pls->cmap0[15].r = r;
1221  pls->cmap0[15].g = g;
1222  pls->cmap0[15].b = b;
1223  }
1224 }
1225 
1226 
1227 //--------------------------------------------------------------------------
1228 // plD_gd_optimise()
1229 //
1230 //
1231 // This function pretty much does exactly what it says - it optimises the
1232 // PNG file. It does this by checking to see if all the allocated colours
1233 // were actually used. If they were not, then it deallocates them. This
1234 // function often results in the PNG file being saved as a 4 bit (16
1235 // colour) PNG rather than an 8 bit (256 colour) PNG. The file size
1236 // difference is not huge, not as great as for GIFs for example (I think
1237 // most of the saving comes from removing redundant entries from the
1238 // palette entry in the header); however some modest size savings occur.
1239 //
1240 // The function isn't always successful - the optimiser will always
1241 // deallocate unused colours as it finds them, but GD will only deallocate
1242 // them "for real" until 16 colours are used up, and then stop since it
1243 // doesn't make a difference if you have 17 colours or 255 colours. The
1244 // result of this is you may end up with an image using say, 130 colours,
1245 // but you will have 240 colour entries, some of which aren't used, and
1246 // aren't blanked out.
1247 //
1248 // Another side-effect of this function is the relative position of the
1249 // colour indices MAY shift as colours are deallocated. I really don't
1250 // think this should worry anyone, but if it does, don't optimise the
1251 // image !
1252 //
1253 //--------------------------------------------------------------------------
1254 
1255 void plD_gd_optimise( PLStream *pls )
1256 {
1257  png_Dev *dev = (png_Dev *) pls->dev;
1258  int i, j;
1259  char *bbuf;
1260 
1261  bbuf = calloc( 256, (size_t) 1 ); // Allocate a buffer to "check off" colours as they are used
1262  if ( bbuf == NULL )
1263  plexit( "plD_gd_optimise: Out of memory." );
1264 
1265  for ( i = 0; i < ( pls->xlength - 1 ); i++ ) // Walk through the image pixel by pixel
1266  { // checking to see what colour it is
1267  for ( j = 0; j < ( pls->ylength - 1 ); j++ ) // and adding it to the list of used colours
1268  {
1269  bbuf[gdImagePalettePixel( dev->im_out, i, j )] = 1;
1270  }
1271  }
1272 
1273  for ( i = 0; i < 256; i++ ) // next walk over the colours and deallocate
1274  { // unused ones
1275  if ( bbuf[i] == 0 )
1276  gdImageColorDeallocate( dev->im_out, i );
1277  }
1278 
1279  free( bbuf );
1280 }
1281 
1282 
1283 #ifdef PLD_png
1284 
1285 //--------------------------------------------------------------------------
1286 // plD_eop_png()
1287 //
1288 // End of page.
1289 //--------------------------------------------------------------------------
1290 
1291 void plD_eop_png( PLStream *pls )
1292 {
1293  png_Dev *dev = (png_Dev *) pls->dev;
1294  int im_size = 0;
1295  int png_compression;
1296  void *im_ptr = NULL;
1297  size_t nwrite;
1298 
1299  if ( pls->family || pls->page == 1 )
1300  {
1301  if ( dev->optimise )
1302  {
1303 #if GD2_VERS >= 2
1304  if ( ( ( ( ( dev->truecolour > 0 ) && ( dev->palette > 0 ) ) || // In an EXTREMELY convaluted
1305  ( ( dev->truecolour == 0 ) && ( dev->palette == 0 ) ) ) && // manner, all this is just
1306  ( ( pls->ncol1 + pls->ncol0 ) <= 256 ) ) || // asking the question, do we
1307  ( ( ( dev->palette > 0 ) && ( dev->truecolour == 0 ) ) ) ) // want truecolour or not ?
1308  {
1309 #endif
1310  plD_gd_optimise( pls );
1311 
1312 #if GD2_VERS >= 2
1313  }
1314 #endif
1315  }
1316 
1317 
1318  // image is written to output file by the driver
1319  // since if the gd.dll is linked to a different c
1320  // lib a crash occurs - this fix works also in Linux
1321  // gdImagePng(dev->im_out, pls->OutFile);
1322  #if GD2_VERS >= 2
1323 
1324  //Set the compression/quality level for PNG files.
1325  // pls->dev_compression values of 1-9 translate to the zlib compression values 1-9
1326  // pls->dev_compression values 10 <= compression <= 99 are divided by 10 to get the zlib
1327  // compression value. Values <=0 or greater than 99 are set to 90 which
1328  // translates to a zlib compression value of 9, the highest quality
1329  // of compression or smallest file size or largest computer time required
1330  // to achieve the compression. Smaller zlib compression values correspond
1331  // to lower qualities of compression (larger file size), but lower
1332  // computer times as well.
1333 
1334  png_compression = ( ( pls->dev_compression <= 0 ) || ( pls->dev_compression > 99 ) ) ? 90 : pls->dev_compression;
1335  png_compression = ( png_compression > 9 ) ? ( png_compression / 10 ) : png_compression;
1336  im_ptr = gdImagePngPtrEx( dev->im_out, &im_size, png_compression );
1337  #else
1338  im_ptr = gdImagePngPtr( dev->im_out, &im_size );
1339  #endif
1340  if ( im_ptr )
1341  {
1342  nwrite = fwrite( im_ptr, sizeof ( char ), im_size, pls->OutFile );
1343  if ( nwrite != im_size )
1344  plabort( "gd driver: Error writing png file" );
1345  gdFree( im_ptr );
1346  }
1347 
1348  gdImageDestroy( dev->im_out );
1349  dev->im_out = NULL;
1350  }
1351 }
1352 
1353 #endif
1354 
1355 #ifdef PL_HAVE_FREETYPE
1356 
1357 //--------------------------------------------------------------------------
1358 // void plD_pixel_gd (PLStream *pls, short x, short y)
1359 //
1360 // callback function, of type "plD_pixel_fp", which specifies how a single
1361 // pixel is set in the current colour.
1362 //--------------------------------------------------------------------------
1363 
1364 void plD_pixel_gd( PLStream *pls, short x, short y )
1365 {
1366  png_Dev *dev = (png_Dev *) pls->dev;
1367 
1368  gdImageSetPixel( dev->im_out, x, y, dev->colour );
1369 }
1370 
1371 //--------------------------------------------------------------------------
1372 // void plD_set_pixel_gd (PLStream *pls, short x, short y)
1373 //
1374 // callback function, of type "plD_pixel_fp", which specifies how a single
1375 // pixel is set directly to hardware, using the colour provided
1376 //--------------------------------------------------------------------------
1377 
1378 void plD_set_pixel_gd( PLStream *pls, short x, short y, PLINT colour )
1379 {
1380  png_Dev *dev = (png_Dev *) pls->dev;
1381  int R, G, B;
1382  int Colour;
1383 
1384  G = GetGValue( colour );
1385  R = GetRValue( colour );
1386  B = GetBValue( colour );
1387 
1388  Colour = gdImageColorResolve( dev->im_out, R, G, B );
1389  gdImageSetPixel( dev->im_out, x, y, Colour );
1390 }
1391 
1392 //--------------------------------------------------------------------------
1393 // PLINT plD_read_pixel_gd (PLStream *pls, short x, short y)
1394 //
1395 // callback function, of type "plD_read_pixel_gd", which specifies how a
1396 // single pixel's RGB is read (in the destination context), then
1397 // returns an RGB encoded int with the info for blending.
1398 //--------------------------------------------------------------------------
1399 
1400 PLINT plD_read_pixel_gd( PLStream *pls, short x, short y )
1401 {
1402  png_Dev *dev = (png_Dev *) pls->dev;
1403  PLINT colour;
1404  unsigned char R, G, B;
1405 
1406  colour = gdImageGetTrueColorPixel( dev->im_out, x, y );
1407 
1408  R = gdTrueColorGetRed( colour );
1409  G = gdTrueColorGetGreen( colour );
1410  B = gdTrueColorGetBlue( colour );
1411 
1412  colour = RGB( R, G, B );
1413  return ( colour );
1414 }
1415 
1416 
1417 //--------------------------------------------------------------------------
1418 // void init_freetype_lv1 (PLStream *pls)
1419 //
1420 // "level 1" initialisation of the freetype library.
1421 // "Level 1" initialisation calls plD_FreeType_init(pls) which allocates
1422 // memory to the pls->FT structure, then sets up the pixel callback
1423 // function.
1424 //--------------------------------------------------------------------------
1425 
1426 static void init_freetype_lv1( PLStream *pls )
1427 {
1428  FT_Data *FT;
1429 
1430  plD_FreeType_init( pls );
1431 
1432  FT = (FT_Data *) pls->FT;
1433  FT->pixel = (plD_pixel_fp) plD_pixel_gd;
1434  FT->read_pixel = (plD_read_pixel_fp) plD_read_pixel_gd;
1435  FT->set_pixel = (plD_set_pixel_fp) plD_set_pixel_gd;
1436 }
1437 
1438 //--------------------------------------------------------------------------
1439 // void init_freetype_lv2 (PLStream *pls)
1440 //
1441 // "Level 2" initialisation of the freetype library.
1442 // "Level 2" fills in a few setting that aren't public until after the
1443 // graphics sub-syetm has been initialised.
1444 // The "level 2" initialisation fills in a few things that are defined
1445 // later in the initialisation process for the GD driver.
1446 //
1447 // FT->scale is a scaling factor to convert co-ordinates. This is used by
1448 // the GD and other drivers to scale back a larger virtual page and this
1449 // eliminate the "hidden line removal bug". Set it to 1 if your device
1450 // doesn't have scaling.
1451 //
1452 // Some coordinate systems have zero on the bottom, others have zero on
1453 // the top. Freetype does it one way, and most everything else does it the
1454 // other. To make sure everything is working ok, we have to "flip" the
1455 // coordinates, and to do this we need to know how big in the Y dimension
1456 // the page is, and whether we have to invert the page or leave it alone.
1457 //
1458 // FT->ymax specifies the size of the page FT->invert_y=1 tells us to
1459 // invert the y-coordinates, FT->invert_y=0 will not invert the
1460 // coordinates.
1461 //--------------------------------------------------------------------------
1462 
1463 static void init_freetype_lv2( PLStream *pls )
1464 {
1465  png_Dev *dev = (png_Dev *) pls->dev;
1466  FT_Data *FT = (FT_Data *) pls->FT;
1467 
1468  FT->scale = dev->scale;
1469  FT->ymax = dev->pngy;
1470  FT->invert_y = 1;
1471  FT->smooth_text = 0;
1472 
1473  if ( ( FT->want_smooth_text == 1 ) && ( FT->BLENDED_ANTIALIASING == 0 ) ) // do we want to at least *try* for smoothing ?
1474  {
1475  FT->ncol0_org = pls->ncol0; // save a copy of the original size of ncol0
1476  FT->ncol0_xtra = NCOLOURS - ( pls->ncol1 + pls->ncol0 ); // work out how many free slots we have
1477  FT->ncol0_width = FT->ncol0_xtra / ( pls->ncol0 - 1 ); // find out how many different shades of anti-aliasing we can do
1478  if ( FT->ncol0_width > 4 ) // are there enough colour slots free for text smoothing ?
1479  {
1480  if ( FT->ncol0_width > max_number_of_grey_levels_used_in_text_smoothing )
1481  FT->ncol0_width = max_number_of_grey_levels_used_in_text_smoothing; // set a maximum number of shades
1482  plscmap0n( FT->ncol0_org + ( FT->ncol0_width * pls->ncol0 ) ); // redefine the size of cmap0
1483 // the level manipulations are to turn off the plP_state(PLSTATE_CMAP0)
1484 // call in plscmap0 which (a) leads to segfaults since the GD image is
1485 // not defined at this point and (b) would be inefficient in any case since
1486 // setcmap is always called later (see plD_bop_png) to update the driver
1487 // color palette to be consistent with cmap0.
1488  {
1489  PLINT level_save;
1490  level_save = pls->level;
1491  pls->level = 0;
1492  pl_set_extended_cmap0( pls, FT->ncol0_width, FT->ncol0_org ); // call the function to add the extra cmap0 entries and calculate stuff
1493  pls->level = level_save;
1494  }
1495  FT->smooth_text = 1; // Yippee ! We had success setting up the extended cmap0
1496  }
1497  else
1498  plwarn( "Insufficient colour slots available in CMAP0 to do text smoothing." );
1499  }
1500  else if ( ( FT->want_smooth_text == 1 ) && ( FT->BLENDED_ANTIALIASING == 1 ) ) // If we have a truecolour device, we wont even bother trying to change the palette
1501  {
1502  FT->smooth_text = 1;
1503  }
1504 }
1505 
1506 #endif
1507 
1508 
1509 #ifdef PLD_jpeg
1510 
1511 //--------------------------------------------------------------------------
1512 // plD_eop_jpeg()
1513 //
1514 // End of page.
1515 //--------------------------------------------------------------------------
1516 
1517 void plD_eop_jpeg( PLStream *pls )
1518 {
1519  png_Dev *dev = (png_Dev *) pls->dev;
1520  int im_size = 0;
1521  void *im_ptr = NULL;
1522  size_t nwrite;
1523  int jpeg_compression;
1524 
1525  if ( pls->family || pls->page == 1 )
1526  {
1527  // Set the compression/quality level for JPEG files
1528  // The higher the value, the bigger/better the image is
1529  //
1530  if ( ( pls->dev_compression <= 0 ) || ( pls->dev_compression > 99 ) )
1531  jpeg_compression = 90;
1532  else
1533  jpeg_compression = pls->dev_compression;
1534 
1535  // image is written to output file by the driver
1536  // since if the gd.dll is linked to a different c
1537  // lib a crash occurs - this fix works also in Linux
1538  // gdImageJpeg(dev->im_out, pls->OutFile, jpeg_compression);
1539  im_ptr = gdImageJpegPtr( dev->im_out, &im_size, jpeg_compression );
1540  if ( im_ptr )
1541  {
1542  nwrite = fwrite( im_ptr, sizeof ( char ), im_size, pls->OutFile );
1543  if ( nwrite != im_size )
1544  plabort( "gd driver: Error writing png file" );
1545  gdFree( im_ptr );
1546  }
1547 
1548  gdImageDestroy( dev->im_out );
1549  dev->im_out = NULL;
1550  }
1551 }
1552 
1553 #endif
1554 
1555 #ifdef PLD_gif
1556 
1557 //--------------------------------------------------------------------------
1558 // plD_eop_gif()
1559 //
1560 // End of page.
1561 //--------------------------------------------------------------------------
1562 
1563 void plD_eop_gif( PLStream *pls )
1564 {
1565  png_Dev *dev = (png_Dev *) pls->dev;
1566  int im_size = 0;
1567  void *im_ptr = NULL;
1568  size_t nwrite;
1569 
1570  if ( pls->family || pls->page == 1 )
1571  {
1572  // image is written to output file by the driver
1573  // since if the gd.dll is linked to a different c
1574  // lib a crash occurs - this fix works also in Linux
1575  // gdImageGif(dev->im_out, pls->OutFile);
1576  im_ptr = gdImageGifPtr( dev->im_out, &im_size );
1577  if ( im_ptr )
1578  {
1579  nwrite = fwrite( im_ptr, sizeof ( char ), im_size, pls->OutFile );
1580  if ( nwrite != im_size )
1581  plabort( "gd driver: Error writing png file" );
1582  gdFree( im_ptr );
1583  }
1584 
1585  gdImageDestroy( dev->im_out );
1586  dev->im_out = NULL;
1587  }
1588 }
1589 
1590 #endif
1591 
1592 
1593 //#endif
1594 
1595 
1596 #else
1597 int
1599 {
1600  return 0;
1601 }
1602 
1603 #endif // PNG