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