PLplot  5.10.0
gd.c
Go to the documentation of this file.
00001 //       PNG, GIF, and JPEG device driver based on libgd
00002 //
00003 // Copyright (C) 2004  Joao Cardoso
00004 // Copyright (C) 2002, 2003, 2004  Andrew Roach
00005 //
00006 // This file is part of PLplot.
00007 //
00008 // PLplot is free software; you can redistribute it and/or modify
00009 // it under the terms of the GNU Library General Public License as published
00010 // by the Free Software Foundation; either version 2 of the License, or
00011 // (at your option) any later version.
00012 //
00013 // PLplot is distributed in the hope that it will be useful,
00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 // GNU Library General Public License for more details.
00017 //
00018 // You should have received a copy of the GNU Library General Public License
00019 // along with PLplot; if not, write to the Free Software
00020 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00021 //
00022 
00023 //             GIF SUPPORT
00024 //
00025 //  Following the expiration of Unisys's worldwide patents on lzw compression
00026 //  GD 2.0.28+ have reinstated support for GIFs, and so support for this
00027 //  format has been added to the GD family of drivers. GIF's only support
00028 //  1, 4 and 8 bit, so no truecolour. Why would you want GIFs though ? PNG is
00029 //  a far superior format, not only giving you 1,4,8 and 24 bit, but also
00030 //  better compression and just about all browsers now support them.
00031 //
00032 
00033 //
00034 //  The GD drivers, PNG, GIF, and JPEG, support a number of different options
00035 //  depending on the version of GD installed.
00036 //
00037 //  If you have installed GD Ver 2.+ you gain support for truecolour (24
00038 //  bit, 16 millionish) modes as well as different line widths. These
00039 //  capibilities are part of GD more so than the GD driver, so they aren't
00040 //  available in any 1.? versions of the driver.
00041 //
00042 //  24 bit support is, by default, set to "auto" if you have V2.+ of GD.
00043 //  What this means is the *driver* decides when to use 24 bit or 8 bit
00044 //  modes for PNG files. The logic is rather simple - if you have less than
00045 //  257 colours, it is set to 8 bit mode, if more then it's in 24 bit mode.
00046 //  This should work fine for most people, most of the time, in most
00047 //  situations; however, it can be overridden in case it has to via the
00048 //  "-drvopt" command line switch. The png driver has two related settings:
00049 //              8bit    and
00050 //              24bit
00051 //
00052 //  If either of these command line toggles are set, that mode becomes the
00053 //  standard used regardless of the number of colours used. It can be envoked
00054 //  as follows:
00055 //                 x08c -dev png -drvopt 8bit -fam -o 8bitpng
00056 //                                      or
00057 //                 x08c -dev png -drvopt 24bit -fam -o 24bitpng
00058 //
00059 //  NOTE:
00060 //  The 24 bit PNG file is an RGBA file, not RGB - it includes alpha channel
00061 //  (transparency). Transparency is set to opaque, but the fact it is an
00062 //  RGBA and not an RGB might cause some problems with some viewers.
00063 //  Sadly, I can't do anything about it... sorry.
00064 //
00065 //  GIF files can only have 256 colours, so naturally truecolour mode is not
00066 //  supported for this sub-driver.
00067 //
00068 //  Stuff for GD V1.? as well as V2.+
00069 //
00070 //  optimise
00071 //
00072 //  From version 1.17 of the GD driver, a command line option has been
00073 //  added to try and optimise the PNG files. If successful, the optimise
00074 //  command will create 4 bit (16 colour) PNGs instead of 8 bit (256 colour)
00075 //  ones. This results in slightly smaller files with no loss in any colour
00076 //  information. The function has no real memory overhead, but does have a
00077 //  slight speed hit in exchange for the optimisation. For example:
00078 //         x08c -dev png -drvopt 8bit,optimise -fam -o 8bitpng
00079 //  forces the png driver to make 8bit pngs, and will then optimise any PNG
00080 //  images with 16 or less colours into a 4 bit PNG. Note, this DOESN'T WORK
00081 //  WITH 24bit PNGs yet, and will never work with JPEGs.
00082 //
00083 //
00084 //  Also as of version 1.17 of the GD driver, the options for palette
00085 //  modification previously set with the command line option "-hack" have
00086 //  now been moved to two options settable from the -drvopt switch.
00087 //
00088 //  def_black15
00089 //
00090 //  -drvopt def_black15 sets index 15, usually white, to black if index 0,
00091 //  the background colour and usually black, has been set to white from the
00092 //  command line option -bg
00093 //
00094 //  swp_red15
00095 //
00096 //  -drvopt swp_red15 swaps index 15, usually white, with index 1, which is
00097 //  usually red. This might be desirable occasionally, but it is principally
00098 //  included for cases when the background has been set on the command line
00099 //  to white, and the "def_black15" option has been issued to redefine index
00100 //  15 as black. By issuing a command like:
00101 //                 x08c -dev png -bg ffffff -drvopt def_black15,swp_red15
00102 //  the driver will set the background to white, then redefine index 15 of
00103 //  cmap0, which is usually white to black, then swap index 2 (red) to 15
00104 //  (white originally, now black), so at the end of the day, the "default"
00105 //  plotting colour is now black. Why do all of this ? It is a very quick
00106 //  way of making a nice web-friendly png without having to redefine the
00107 //  cmaps within your program.
00108 //
00109 //  smoothlines
00110 //
00111 //  -drvopt smoothlines=1 turns on anti-aliased line and polygong drawing if
00112 //  you are using a 24bit mode. Unfortunately gd doesn't honour line
00113 //  width when anti-aliasing, so by default it is off.
00114 //
00115 
00116 
00117 #include "plDevs.h"
00118 
00119 #if defined ( PLD_png ) || defined ( PLD_jpeg ) || defined ( PLD_gif )
00120 
00121 #include "plplotP.h"
00122 #include "drivers.h"
00123 
00124 #include <gd.h>
00125 
00126 //  Device info
00127 //
00128 //  Don't knoq if all this logic is necessary, but basically we are going to
00129 //  start with all three sub-drivers present, then work out way down to two
00130 //  and finally one of each.
00131 //
00132 
00133 PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_gd =
00134 #if defined ( PLD_png )
00135     "png:PNG file:0:gd:39:png\n"
00136 #endif
00137 #if defined ( PLD_jpeg )
00138     "jpeg:JPEG file:0:gd:40:jpeg\n"
00139 #endif
00140 #if defined ( PLD_gif )
00141     "gif:GIF file:0:gd:47:gif\n"
00142 #endif
00143 ;
00144 
00145 #if GD2_VERS >= 2
00146 #ifdef PL_HAVE_FREETYPE
00147 #define SMOOTH_LINES_OK
00148 #endif
00149 #endif
00150 
00151 #ifdef PL_HAVE_FREETYPE
00152 
00153 //
00154 //  Freetype support has been added to the GD family of drivers using the
00155 //  plfreetype.c module, and implemented as a driver-specific optional extra
00156 //  invoked via the -drvopt command line toggle. It uses the
00157 //  "PLESC_HAS_TEXT" command for rendering within the driver.
00158 //
00159 //  Freetype support is turned on/off at compile time by defining
00160 //  "PL_HAVE_FREETYPE".
00161 //
00162 //  To give the user some level of control over the fonts that are used,
00163 //  environmental variables can be set to over-ride the definitions used by
00164 //  the five default plplot fonts.
00165 //
00166 //  Freetype rendering is used with the command line "-drvopt text".
00167 //  Anti-aliased fonts can be used by issuing "-drvopt text,smooth"
00168 //
00169 
00170 #include "plfreetype.h"
00171 
00172 #endif
00173 
00174 // Prototypes for functions in this file.
00175 
00176 static void     fill_polygon( PLStream *pls );
00177 static void     setcmap( PLStream *pls );
00178 static void     plD_init_png_Dev( PLStream *pls );
00179 static void     plD_gd_optimise( PLStream *pls );
00180 static void     plD_black15_gd( PLStream *pls );
00181 static void     plD_red15_gd( PLStream *pls );
00182 #ifdef PLD_gif
00183 static void     plD_init_gif_Dev( PLStream *pls );
00184 #endif
00185 
00186 #ifdef PL_HAVE_FREETYPE
00187 
00188 static void plD_pixel_gd( PLStream *pls, short x, short y );
00189 static PLINT plD_read_pixel_gd( PLStream *pls, short x, short y );
00190 static void plD_set_pixel_gd( PLStream *pls, short x, short y, PLINT colour );
00191 static void init_freetype_lv1( PLStream *pls );
00192 static void init_freetype_lv2( PLStream *pls );
00193 
00194 #endif
00195 
00196 // top level declarations
00197 
00198 static int NCOLOURS = gdMaxColors;
00199 
00200 // In an attempt to fix a problem with the hidden line removal functions
00201 // that results in hidden lines *not* being removed from "small" plot
00202 // pages (ie, like a normal video screen), a "virtual" page of much
00203 // greater size is used to trick the algorithm into working correctly.
00204 // If, in future, this gets fixed on its own, then don't define
00205 // "use_experimental_hidden_line_hack"
00206 //
00207 
00208 #define use_experimental_hidden_line_hack
00209 
00210 // I think the current version of Freetype supports up to a maximum of
00211 // 128 grey levels for text smoothing. You can get quite acceptable
00212 // results with as few as 4 grey-levels. Uusually only about 5 get used
00213 // anyway, but the question is where, in the "grey spectrum" will they be ?
00214 // Who knows ? The following define lets you set a maximum limit on the
00215 // number of grey-levels used. It is really only here for the 24bit mode
00216 // and could be set to 255, but that would slow things down and use more
00217 // memory. 64 seems to be a nice compromise, but if you want to change it,
00218 // then change it here.
00219 //
00220 
00221 #ifndef max_number_of_grey_levels_used_in_text_smoothing
00222 #define max_number_of_grey_levels_used_in_text_smoothing    64
00223 #endif
00224 
00225 // Not present in versions before 2.0
00226 
00227 #ifndef gdImagePalettePixel
00228 #define gdImagePalettePixel( im, x, y )    ( im )->pixels[( y )][( x )]
00229 #endif
00230 
00231 #if GD2_VERS >= 2
00232 int plToGdAlpha( PLFLT a )
00233 {
00234     int tmp = (int) ( ( 1.0 - a ) * gdAlphaMax );
00235     return tmp;
00236 }
00237 #endif
00238 
00239 // Struct to hold device-specific info.
00240 
00241 typedef struct
00242 {
00243     gdImagePtr    im_out;                       // Graphics pointer
00244     PLINT         pngx;
00245     PLINT         pngy;
00246 
00247     int           colour;                        // Current Colour
00248     int           totcol;                        // Total number of colours
00249     int           ncol1;                         // Actual size of ncol1 we got
00250 
00251     PLFLT         scale;                         // scaling factor to "blow up" to
00252                                                  // the "virtual" page in removing hidden lines
00253 
00254     int           optimise;                      // Flag used for 4bit pngs
00255     int           black15;                       // Flag used for forcing a black colour
00256     int           red15;                         // Flag for swapping red and 15
00257 
00258     unsigned char TRY_BLENDED_ANTIALIASING;      // Flag to try and set up BLENDED ANTIALIASING
00259 
00260 #if GD2_VERS >= 2
00261     int           truecolour;                   // Flag to ALWAYS force 24 bit mode
00262     int           palette;                      // Flag to ALWAYS force  8 bit mode
00263     unsigned char smooth;                       // Flag to ask for line smoothing
00264 #endif
00265 } png_Dev;
00266 
00267 void plD_init_png( PLStream * );
00268 void plD_line_png( PLStream *, short, short, short, short );
00269 void plD_polyline_png( PLStream *, short *, short *, PLINT );
00270 void plD_eop_png( PLStream * );
00271 void plD_eop_jpeg( PLStream * );
00272 void plD_bop_png( PLStream * );
00273 void plD_tidy_png( PLStream * );
00274 void plD_state_png( PLStream *, PLINT );
00275 void plD_esc_png( PLStream *, PLINT, void * );
00276 #ifdef PLD_gif
00277 void plD_init_gif( PLStream * );
00278 void plD_eop_gif( PLStream * );
00279 #endif
00280 
00281 #ifdef PLD_png
00282 
00283 void plD_dispatch_init_png( PLDispatchTable *pdt )
00284 {
00285 #ifndef ENABLE_DYNDRIVERS
00286     pdt->pl_MenuStr = "PNG file";
00287     pdt->pl_DevName = "png";
00288 #endif
00289     pdt->pl_type     = plDevType_FileOriented;
00290     pdt->pl_seq      = 39;
00291     pdt->pl_init     = (plD_init_fp) plD_init_png;
00292     pdt->pl_line     = (plD_line_fp) plD_line_png;
00293     pdt->pl_polyline = (plD_polyline_fp) plD_polyline_png;
00294     pdt->pl_eop      = (plD_eop_fp) plD_eop_png;
00295     pdt->pl_bop      = (plD_bop_fp) plD_bop_png;
00296     pdt->pl_tidy     = (plD_tidy_fp) plD_tidy_png;
00297     pdt->pl_state    = (plD_state_fp) plD_state_png;
00298     pdt->pl_esc      = (plD_esc_fp) plD_esc_png;
00299 }
00300 
00301 #endif
00302 
00303 #ifdef PLD_jpeg
00304 
00305 void plD_dispatch_init_jpeg( PLDispatchTable *pdt )
00306 {
00307 #ifndef ENABLE_DYNDRIVERS
00308     pdt->pl_MenuStr = "JPEG File";
00309     pdt->pl_DevName = "jpeg";
00310 #endif
00311     pdt->pl_type     = plDevType_FileOriented;
00312     pdt->pl_seq      = 40;
00313     pdt->pl_init     = (plD_init_fp) plD_init_png;
00314     pdt->pl_line     = (plD_line_fp) plD_line_png;
00315     pdt->pl_polyline = (plD_polyline_fp) plD_polyline_png;
00316     pdt->pl_eop      = (plD_eop_fp) plD_eop_jpeg;
00317     pdt->pl_bop      = (plD_bop_fp) plD_bop_png;
00318     pdt->pl_tidy     = (plD_tidy_fp) plD_tidy_png;
00319     pdt->pl_state    = (plD_state_fp) plD_state_png;
00320     pdt->pl_esc      = (plD_esc_fp) plD_esc_png;
00321 }
00322 #endif
00323 
00324 
00325 #ifdef PLD_gif
00326 
00327 void plD_dispatch_init_gif( PLDispatchTable *pdt )
00328 {
00329 #ifndef ENABLE_DYNDRIVERS
00330     pdt->pl_MenuStr = "GIF File";
00331     pdt->pl_DevName = "gif";
00332 #endif
00333     pdt->pl_type     = plDevType_FileOriented;
00334     pdt->pl_seq      = 47;
00335     pdt->pl_init     = (plD_init_fp) plD_init_gif;
00336     pdt->pl_line     = (plD_line_fp) plD_line_png;
00337     pdt->pl_polyline = (plD_polyline_fp) plD_polyline_png;
00338     pdt->pl_eop      = (plD_eop_fp) plD_eop_gif;
00339     pdt->pl_bop      = (plD_bop_fp) plD_bop_png;
00340     pdt->pl_tidy     = (plD_tidy_fp) plD_tidy_png;
00341     pdt->pl_state    = (plD_state_fp) plD_state_png;
00342     pdt->pl_esc      = (plD_esc_fp) plD_esc_png;
00343 }
00344 #endif
00345 
00346 
00347 //--------------------------------------------------------------------------
00348 // plD_init_png_Dev()
00349 //
00350 //--------------------------------------------------------------------------
00351 
00352 static void
00353 plD_init_png_Dev( PLStream *pls )
00354 {
00355     png_Dev *dev;
00356 
00357 //  Stuff for the driver options, these vars are copied into the driver
00358 //  structure so that everything is thread safe and reenterant.
00359 //
00360 
00361     static int optimise = 0;
00362     static int black15  = 0;
00363     static int red15    = 0;
00364 #if GD2_VERS >= 2
00365     static int truecolour  = 0;
00366     static int palette     = 0;
00367     static int smooth_line = 0;
00368 #endif
00369 #ifdef PL_HAVE_FREETYPE
00370     static int freetype    = 1;
00371     static int smooth_text = 1;
00372     FT_Data    *FT;
00373 #endif
00374 
00375     DrvOpt gd_options[] = { { "optimise",    DRV_INT, &optimise,    "Optimise PNG palette when possible"                                                                                                    },
00376                             { "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\"" },
00377                             { "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"          },
00378 #if GD2_VERS >= 2
00379                             { "8bit",        DRV_INT, &palette,     "Palette (8 bit) mode"                                                                                                                  },
00380                             { "24bit",       DRV_INT, &truecolour,  "Truecolor (24 bit) mode"                                                                                                               },
00381                             { "smoothlines", DRV_INT, &smooth_line, "Turn line Anti Aliasing on (1) or off (0)"                                                                                             },
00382 #endif
00383 #ifdef PL_HAVE_FREETYPE
00384                             { "text",        DRV_INT, &freetype,    "Use driver text (FreeType)"                                                                                                            },
00385                             { "smooth",      DRV_INT, &smooth_text, "Turn text smoothing on (1) or off (0)"                                                                                                 },
00386 #endif
00387                             { NULL,          DRV_INT, NULL,         NULL                                                                                                                                    } };
00388 
00389 
00390 // Allocate and initialize device-specific data
00391 
00392     if ( pls->dev != NULL )
00393         free( (void *) pls->dev );
00394 
00395     pls->dev = calloc( 1, (size_t) sizeof ( png_Dev ) );
00396     if ( pls->dev == NULL )
00397         plexit( "plD_init_png_Dev: Out of memory." );
00398 
00399     dev = (png_Dev *) pls->dev;
00400 
00401     dev->colour = 1;  // Set a fall back pen colour in case user doesn't
00402 
00403 
00404 // Check for and set up driver options
00405 
00406     plParseDrvOpts( gd_options );
00407 
00408     dev->black15  = black15;
00409     dev->red15    = red15;
00410     dev->optimise = optimise;
00411 
00412 #if GD2_VERS >= 2
00413 
00414     dev->palette    = palette;
00415     dev->truecolour = truecolour;
00416 
00417 
00418 
00419     if ( ( dev->truecolour > 0 ) && ( dev->palette > 0 ) )
00420         plwarn( "Selecting both \"truecolor\" AND \"palette\" driver options is contradictory, so\nI will just use my best judgment.\n" );
00421     else if ( dev->truecolour > 0 )
00422         NCOLOURS = 16777216;
00423     else if ( ( dev->truecolour == 0 ) && ( dev->palette == 0 ) && ( ( pls->ncol1 + pls->ncol0 ) > NCOLOURS ) )
00424     {
00425         NCOLOURS = 16777216;
00426     }
00427 
00428     if ( ( dev->palette == 0 ) && ( dev->optimise == 0 ) && ( smooth_line == 1 ) )
00429         dev->smooth = 1;                                                                            // Allow smoothing of lines if we have a truecolour device
00430 
00431 #endif
00432 
00433 #ifdef PL_HAVE_FREETYPE
00434     if ( freetype )
00435     {
00436         pls->dev_text    = 1; // want to draw text
00437         pls->dev_unicode = 1; // want unicode
00438 
00439         // As long as we aren't optimising, we'll try to use better antialaising
00440         // We can also only do this if the user wants smoothing, and hasn't
00441         // selected a palette mode.
00442         //
00443 
00444 
00445         init_freetype_lv1( pls );
00446         FT = (FT_Data *) pls->FT;
00447         FT->want_smooth_text = smooth_text > 0 ? 1 : 0;
00448         if ( ( dev->optimise == 0 ) && ( dev->palette == 0 ) && ( smooth_text != 0 ) )
00449         {
00450             FT->BLENDED_ANTIALIASING = 1;
00451             dev->truecolour          = 1;
00452         }
00453     }
00454 
00455 #endif
00456 }
00457 
00458 //--------------------------------------------------------------------------
00459 // plD_init_png()
00460 //
00461 // Initialize device.
00462 //--------------------------------------------------------------------------
00463 
00464 void plD_init_png( PLStream *pls )
00465 {
00466     png_Dev *dev = NULL;
00467 
00468     pls->termin    = 0;         // Not an interactive device
00469     pls->icol0     = 1;
00470     pls->bytecnt   = 0;
00471     pls->page      = 0;
00472     pls->dev_fill0 = 1;         // Can do solid fills
00473 
00474     if ( !pls->colorset )
00475         pls->color = 1;         // Is a color device
00476 
00477 // Initialize family file info
00478     plFamInit( pls );
00479 
00480 // Prompt for a file name if not already set
00481     plOpenFile( pls );
00482 
00483 // Allocate and initialize device-specific data
00484     plD_init_png_Dev( pls );
00485     dev = (png_Dev *) pls->dev;
00486 
00487     if ( pls->xlength <= 0 || pls->ylength <= 0 )
00488     {
00489 // use default width, height of 800x600 if not specifed by -geometry option
00490 // or plspage
00491         plspage( 0., 0., 800, 600, 0, 0 );
00492     }
00493 
00494     pls->graphx = GRAPHICS_MODE;
00495 
00496     dev->pngx = pls->xlength - 1;       // should I use -1 or not???
00497     dev->pngy = pls->ylength - 1;
00498 
00499 #ifdef use_experimental_hidden_line_hack
00500 
00501     if ( dev->pngx > dev->pngy ) // Work out the scaling factor for the
00502     {                            // "virtual" (oversized) page
00503         dev->scale = (PLFLT) ( PIXELS_X - 1 ) / (PLFLT) dev->pngx;
00504     }
00505     else
00506     {
00507         dev->scale = (PLFLT) PIXELS_Y / (PLFLT) dev->pngy;
00508     }
00509 #else
00510 
00511     dev->scale = 1.;
00512 
00513 #endif
00514 
00515 
00516     if ( pls->xdpi <= 0 )
00517     {
00518 // This corresponds to a typical monitor resolution of 4 pixels/mm.
00519         plspage( 4. * 25.4, 4. * 25.4, 0, 0, 0, 0 );
00520     }
00521     else
00522     {
00523         pls->ydpi = pls->xdpi;        // Set X and Y dpi's to the same value
00524     }
00525 // Convert DPI to pixels/mm
00526     plP_setpxl( dev->scale * pls->xdpi / 25.4, dev->scale * pls->ydpi / 25.4 );
00527 
00528     plP_setphy( 0, dev->scale * dev->pngx, 0, dev->scale * dev->pngy );
00529 
00530 #ifdef PL_HAVE_FREETYPE
00531     if ( pls->dev_text )
00532     {
00533         init_freetype_lv2( pls );
00534     }
00535 #endif
00536 }
00537 
00538 
00539 #ifdef PLD_gif
00540 
00541 //--------------------------------------------------------------------------
00542 // plD_init_gif_Dev()
00543 //
00544 // We need a new initialiser for the GIF version of the GD driver because
00545 // the GIF one does not support TRUECOLOUR
00546 //--------------------------------------------------------------------------
00547 
00548 static void
00549 plD_init_gif_Dev( PLStream *pls )
00550 {
00551     png_Dev *dev;
00552 
00553 //  Stuff for the driver options, these vars are copied into the driver
00554 //  structure so that everything is thread safe and reenterant.
00555 //
00556 
00557     static int black15 = 0;
00558     static int red15   = 0;
00559 #ifdef PL_HAVE_FREETYPE
00560     static int freetype    = 1;
00561     static int smooth_text = 0;
00562     FT_Data    *FT;
00563 #endif
00564 
00565     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\"" },
00566                             { "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"          },
00567 #ifdef PL_HAVE_FREETYPE
00568                             { "text",        DRV_INT, &freetype,    "Use driver text (FreeType)"                                                                                                            },
00569                             { "smooth",      DRV_INT, &smooth_text, "Turn text smoothing on (1) or off (0)"                                                                                                 },
00570 #endif
00571                             { NULL,          DRV_INT, NULL,         NULL                                                                                                                                    } };
00572 
00573 
00574 // Allocate and initialize device-specific data
00575 
00576     if ( pls->dev != NULL )
00577         free( (void *) pls->dev );
00578 
00579     pls->dev = calloc( 1, (size_t) sizeof ( png_Dev ) );
00580     if ( pls->dev == NULL )
00581         plexit( "plD_init_gif_Dev: Out of memory." );
00582 
00583     dev = (png_Dev *) pls->dev;
00584 
00585     dev->colour = 1;  // Set a fall back pen colour in case user doesn't
00586 
00587 // Check for and set up driver options
00588 
00589     plParseDrvOpts( gd_options );
00590 
00591     dev->black15 = black15;
00592     dev->red15   = red15;
00593 
00594     dev->optimise   = 0;  // Optimise does not work for GIFs... should, but it doesn't
00595     dev->palette    = 1;  // Always use palette mode for GIF files
00596     dev->truecolour = 0;  // Never have truecolour in GIFS
00597 
00598 #ifdef PL_HAVE_FREETYPE
00599     if ( freetype )
00600     {
00601         pls->dev_text    = 1; // want to draw text
00602         pls->dev_unicode = 1; // want unicode
00603 
00604         init_freetype_lv1( pls );
00605         FT = (FT_Data *) pls->FT;
00606 
00607         FT->want_smooth_text = smooth_text > 0 ? 1 : 0;
00608     }
00609 
00610 #endif
00611 }
00612 
00613 //--------------------------------------------------------------------------
00614 // plD_init_gif()
00615 //
00616 // Initialize device.
00617 //--------------------------------------------------------------------------
00618 
00619 void plD_init_gif( PLStream *pls )
00620 {
00621     png_Dev *dev = NULL;
00622 
00623     pls->termin    = 0;         // Not an interactive device
00624     pls->icol0     = 1;
00625     pls->bytecnt   = 0;
00626     pls->page      = 0;
00627     pls->dev_fill0 = 1;         // Can do solid fills
00628 
00629     if ( !pls->colorset )
00630         pls->color = 1;         // Is a color device
00631 
00632 // Initialize family file info
00633     plFamInit( pls );
00634 
00635 // Prompt for a file name if not already set
00636     plOpenFile( pls );
00637 
00638 // Allocate and initialize device-specific data
00639     plD_init_gif_Dev( pls );
00640     dev = (png_Dev *) pls->dev;
00641 
00642     if ( pls->xlength <= 0 || pls->ylength <= 0 )
00643     {
00644 // use default width, height of 800x600 if not specifed by -geometry option
00645 // or plspage
00646         plspage( 0., 0., 800, 600, 0, 0 );
00647     }
00648 
00649     pls->graphx = GRAPHICS_MODE;
00650 
00651     dev->pngx = pls->xlength - 1;       // should I use -1 or not???
00652     dev->pngy = pls->ylength - 1;
00653 
00654 #ifdef use_experimental_hidden_line_hack
00655 
00656     if ( dev->pngx > dev->pngy ) // Work out the scaling factor for the
00657     {                            // "virtual" (oversized) page
00658         dev->scale = (PLFLT) ( PIXELS_X - 1 ) / (PLFLT) dev->pngx;
00659     }
00660     else
00661     {
00662         dev->scale = (PLFLT) PIXELS_Y / (PLFLT) dev->pngy;
00663     }
00664 #else
00665 
00666     dev->scale = 1.;
00667 
00668 #endif
00669 
00670 
00671     if ( pls->xdpi <= 0 )
00672     {
00673 // This corresponds to a typical monitor resolution of 4 pixels/mm.
00674         plspage( 4. * 25.4, 4. * 25.4, 0, 0, 0, 0 );
00675     }
00676     else
00677     {
00678         pls->ydpi = pls->xdpi;        // Set X and Y dpi's to the same value
00679     }
00680 // Convert DPI to pixels/mm
00681     plP_setpxl( dev->scale * pls->xdpi / 25.4, dev->scale * pls->ydpi / 25.4 );
00682 
00683     plP_setphy( 0, dev->scale * dev->pngx, 0, dev->scale * dev->pngy );
00684 
00685 #ifdef PL_HAVE_FREETYPE
00686     if ( pls->dev_text )
00687     {
00688         init_freetype_lv2( pls );
00689     }
00690 #endif
00691 }
00692 
00693 #endif
00694 
00695 
00696 //--------------------------------------------------------------------------
00697 // plD_line_png()
00698 //
00699 // Draw a line in the current color from (x1,y1) to (x2,y2).
00700 //--------------------------------------------------------------------------
00701 
00702 void
00703 plD_line_png( PLStream *pls, short x1a, short y1a, short x2a, short y2a )
00704 {
00705     png_Dev *dev = (png_Dev *) pls->dev;
00706     int     x1   = x1a / dev->scale, y1 = y1a / dev->scale, x2 = x2a / dev->scale, y2 = y2a / dev->scale;
00707     y1 = dev->pngy - y1;
00708     y2 = dev->pngy - y2;
00709 
00710     #ifdef SMOOTH_LINES_OK
00711     if ( dev->smooth == 1 )
00712     {
00713         gdImageSetAntiAliased( dev->im_out, dev->colour );
00714         gdImageLine( dev->im_out, x1, y1, x2, y2, gdAntiAliased );
00715     }
00716     else
00717     {
00718         gdImageLine( dev->im_out, x1, y1, x2, y2, dev->colour );
00719     }
00720     #else
00721     gdImageLine( dev->im_out, x1, y1, x2, y2, dev->colour );
00722     #endif
00723 }
00724 
00725 //--------------------------------------------------------------------------
00726 // plD_polyline_png()
00727 //
00728 // Draw a polyline in the current color.
00729 //--------------------------------------------------------------------------
00730 
00731 void
00732 plD_polyline_png( PLStream *pls, short *xa, short *ya, PLINT npts )
00733 {
00734     PLINT i;
00735 
00736     for ( i = 0; i < npts - 1; i++ )
00737         plD_line_png( pls, xa[i], ya[i], xa[i + 1], ya[i + 1] );
00738 }
00739 
00740 
00741 //--------------------------------------------------------------------------
00742 // fill_polygon()
00743 //
00744 // Fill polygon described in points pls->dev_x[] and pls->dev_y[].
00745 //--------------------------------------------------------------------------
00746 
00747 static void
00748 fill_polygon( PLStream *pls )
00749 {
00750     png_Dev *dev = (png_Dev *) pls->dev;
00751 
00752     int     i;
00753     gdPoint *points = NULL;
00754 
00755     if ( pls->dev_npts < 1 )
00756         return;
00757 
00758     points = malloc( (size_t) pls->dev_npts * sizeof ( gdPoint ) );
00759 
00760     for ( i = 0; i < pls->dev_npts; i++ )
00761     {
00762         points[i].x = pls->dev_x[i] / dev->scale;
00763         points[i].y = dev->pngy - ( pls->dev_y[i] / dev->scale );
00764     }
00765 
00766     #ifdef SMOOTH_LINES_OK
00767     if ( dev->smooth == 1 )
00768     {
00769         gdImageSetAntiAliased( dev->im_out, dev->colour );
00770         gdImageFilledPolygon( dev->im_out, points, pls->dev_npts, gdAntiAliased );
00771     }
00772     else
00773     {
00774         gdImageFilledPolygon( dev->im_out, points, pls->dev_npts, dev->colour );
00775     }
00776     #else
00777     gdImageFilledPolygon( dev->im_out, points, pls->dev_npts, dev->colour );
00778     #endif
00779 
00780     free( points );
00781 }
00782 
00783 //--------------------------------------------------------------------------
00784 // setcmap()
00785 //
00786 // Sets up color palette.
00787 //--------------------------------------------------------------------------
00788 
00789 static void
00790 setcmap( PLStream *pls )
00791 {
00792     int     i, ncol1 = pls->ncol1;
00793     int     ncol0 = pls->ncol0, total_colours;
00794     PLColor cmap1col;
00795     png_Dev *dev = (png_Dev *) pls->dev;
00796     PLFLT   tmp_colour_pos;
00797 
00798 //
00799 // Yuckky fix to get rid of the previosuly allocated palette from the
00800 // GD image
00801 //
00802 
00803     if ( dev->im_out != NULL )
00804     {
00805         for ( i = 0; i < 256; i++ )
00806         {
00807             gdImageColorDeallocate( dev->im_out, i );
00808         }
00809     }
00810 
00811     if ( ncol0 > NCOLOURS / 2 )                     // Check for ridiculous number of colours
00812     {                                               // in ncol0, and appropriately adjust the
00813         plwarn( "Too many colours in cmap0." );     // number, issuing a
00814         ncol0      = NCOLOURS / 2;                  // warning if it does
00815         pls->ncol0 = ncol0;
00816     }
00817 
00818     dev->totcol = 0;                           // Reset the number of colours counter to zero
00819 
00820     total_colours = ncol0 + ncol1;             // Work out how many colours are wanted
00821 
00822     if ( total_colours > NCOLOURS )            // Do some rather modest error
00823     {                                          // checking to make sure that
00824         total_colours = NCOLOURS;              // we are not defining more colours
00825         ncol1         = total_colours - ncol0; // than we have room for.
00826 
00827         if ( ncol1 <= 0 )
00828         {
00829             plexit( "Problem setting colourmap in PNG or JPEG driver." );
00830         }
00831     }
00832 
00833     dev->ncol1 = ncol1; // The actual size of ncol1, regardless of what was asked.
00834                         // This is dependent on colour slots available.
00835                         // It might well be the same as ncol1.
00836                         //
00837 
00838 // Initialize cmap 0 colors
00839 
00840     if ( ( ncol0 > 0 ) && ( dev->im_out != NULL ) ) // make sure the program actually asked for cmap0 first
00841     {
00842         for ( i = 0; i < ncol0; i++ )
00843         {
00844 #if GD2_VERS >= 2
00845             gdImageColorAllocateAlpha( dev->im_out,
00846                 pls->cmap0[i].r, pls->cmap0[i].g, pls->cmap0[i].b,
00847                 plToGdAlpha( pls->cmap0[i].a ) );
00848 #else
00849             gdImageColorAllocate( dev->im_out,
00850                 pls->cmap0[i].r, pls->cmap0[i].g, pls->cmap0[i].b );
00851 #endif
00852             ++dev->totcol; // count the number of colours we use as we use them
00853         }
00854     }
00855 
00856 // Initialize any remaining slots for cmap1
00857 
00858 
00859     if ( ( ncol1 > 0 ) && ( dev->im_out != NULL ) ) // make sure that we want to define cmap1 first
00860     {
00861         for ( i = 0; i < ncol1; i++ )
00862         {
00863             if ( ncol1 < pls->ncol1 ) // Check the dynamic range of colours
00864             {
00865                 //
00866                 // Ok, now if we have less colour slots available than are being
00867                 // defined by pls->ncol1, then we still want to use the full
00868                 // dynamic range of cmap1 as best we can, so what we do is work
00869                 // out an approximation to the index in the full dynamic range
00870                 // in cases when pls->ncol1 exceeds the number of free colours.
00871                 //
00872 
00873                 tmp_colour_pos = i > 0 ? pls->ncol1 * ( (PLFLT) i / ncol1 ) : 0;
00874                 plcol_interp( pls, &cmap1col, (int) tmp_colour_pos, pls->ncol1 );
00875             }
00876             else
00877             {
00878                 plcol_interp( pls, &cmap1col, i, ncol1 );
00879             }
00880 
00881 
00882 #if GD2_VERS >= 2
00883             gdImageColorAllocateAlpha( dev->im_out,
00884                 cmap1col.r, cmap1col.g, cmap1col.b,
00885                 plToGdAlpha( cmap1col.a ) );
00886 #else
00887             gdImageColorAllocate( dev->im_out,
00888                 cmap1col.r, cmap1col.g, cmap1col.b );
00889 #endif
00890 
00891             ++dev->totcol; // count the number of colours we use as we go
00892         }
00893     }
00894 }
00895 
00896 
00897 //--------------------------------------------------------------------------
00898 // plD_state_png()
00899 //
00900 // Handle change in PLStream state (color, pen width, fill attribute, etc).
00901 //--------------------------------------------------------------------------
00902 
00903 void
00904 plD_state_png( PLStream *pls, PLINT op )
00905 {
00906     png_Dev *dev = (png_Dev *) pls->dev;
00907     PLFLT   tmp_colour_pos;
00908 #if GD2_VERS >= 2
00909     long    temp_col;
00910 #endif
00911 
00912 
00913     switch ( op )
00914     {
00915 #if GD2_VERS >= 2
00916     case PLSTATE_WIDTH:
00917         gdImageSetThickness( dev->im_out, pls->width );
00918         break;
00919 #endif
00920 
00921     case PLSTATE_COLOR0:
00922 #if GD2_VERS >= 2
00923 
00924         if ( ( pls->icol0 == PL_RGB_COLOR ) ||         //  Should never happen since PL_RGB_COLOR is depreciated, but here for backwards compatibility
00925              ( gdImageTrueColor( dev->im_out ) ) )     //  We will do this if we are in "TrueColour" mode
00926         {
00927             if ( ( dev->totcol < NCOLOURS ) ||         // See if there are slots left, if so we will allocate a new colour
00928                  ( gdImageTrueColor( dev->im_out ) ) ) // In TrueColour mode we allocate each colour as we come to it
00929             {
00930                 // 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
00931 #if GD2_VERS >= 2
00932                 temp_col = gdImageColorAllocateAlpha( dev->im_out, pls->curcolor.r,
00933                     pls->curcolor.g, pls->curcolor.b,
00934                     plToGdAlpha( pls->curcolor.a ) );
00935 #else
00936                 temp_col = gdImageColorAllocate( dev->im_out, pls->curcolor.r,
00937                     pls->curcolor.g, pls->curcolor.b );
00938 #endif
00939 
00940                 if ( gdImageTrueColor( dev->im_out ) )
00941                     dev->colour = temp_col;     // If it's truecolour, then we will directly set dev->colour to our "new" colour
00942                 else
00943                 {
00944                     dev->colour = dev->totcol;   // or else, we will just set it to the last colour
00945                     dev->totcol++;               // Bump the total colours for next time round
00946                 }
00947             }
00948         }
00949         else   // just a normal colour allocate, so don't worry about the above stuff, just grab the index
00950         {
00951             dev->colour = pls->icol0;
00952         }
00953 
00954 #else
00955         dev->colour = pls->icol0;
00956         if ( dev->colour == PL_RGB_COLOR )
00957         {
00958             if ( dev->totcol < NCOLOURS )
00959             {
00960 #if GD2_VERS >= 2
00961                 gdImageColorAllocateAlpha( dev->im_out, pls->curcolor.r,
00962                     pls->curcolor.g, pls->curcolor.b,
00963                     plToGdAlpha( pls->curcolor.a ) );
00964 #else
00965                 gdImageColorAllocate( dev->im_out, pls->curcolor.r,
00966                     pls->curcolor.g, pls->curcolor.b );
00967 #endif
00968                 dev->colour = dev->totcol;
00969             }
00970         }
00971 #endif
00972         break;
00973 
00974     case PLSTATE_COLOR1:
00975 
00976 #if GD2_VERS >= 2
00977         if ( !gdImageTrueColor( dev->im_out ) )
00978         {
00979 #endif
00980         //
00981         // Start by checking to see if we have to compensate for cases where
00982         // we don't have the full dynamic range of cmap1 at our disposal
00983         //
00984         if ( dev->ncol1 < pls->ncol1 )
00985         {
00986             tmp_colour_pos = dev->ncol1 * ( (PLFLT) pls->icol1 / ( pls->ncol1 > 0 ? pls->ncol1 : 1 ) );
00987             dev->colour    = pls->ncol0 + (int) tmp_colour_pos;
00988         }
00989         else
00990             dev->colour = pls->ncol0 + pls->icol1;
00991 #if GD2_VERS >= 2
00992     }
00993     else        // it is a truecolour image
00994     {
00995 #if GD2_VERS >= 2
00996         dev->colour = gdTrueColorAlpha( pls->curcolor.r, pls->curcolor.g,
00997             pls->curcolor.b,
00998             plToGdAlpha( pls->curcolor.a ) );
00999 #else
01000         dev->colour = gdTrueColor( pls->curcolor.r, pls->curcolor.g,
01001             pls->curcolor.b );
01002 #endif
01003     }
01004 #endif
01005         break;
01006 
01007 
01008     case PLSTATE_CMAP0:
01009     case PLSTATE_CMAP1:
01010 
01011 #if GD2_VERS >= 2
01012         if ( ( dev->im_out != NULL ) && !gdImageTrueColor( dev->im_out ) )
01013         {
01014 #endif
01015 
01016         //
01017         //  Code to redefine the entire palette
01018         //
01019 
01020 
01021         if ( pls->color )
01022             setcmap( pls );
01023 
01024 #if GD2_VERS >= 2
01025     }
01026 #endif
01027 
01028         break;
01029     }
01030 }
01031 
01032 
01033 //--------------------------------------------------------------------------
01034 // plD_esc_png()
01035 //
01036 // Escape function.
01037 //--------------------------------------------------------------------------
01038 
01039 void plD_esc_png( PLStream *pls, PLINT op, void *ptr )
01040 {
01041     switch ( op )
01042     {
01043     case PLESC_FILL:    // fill
01044         fill_polygon( pls );
01045         break;
01046 
01047 #ifdef PL_HAVE_FREETYPE
01048     case PLESC_HAS_TEXT:
01049         plD_render_freetype_text( pls, (EscText *) ptr );
01050         break;
01051 #endif
01052     }
01053 }
01054 
01055 //--------------------------------------------------------------------------
01056 // plD_bop_png()
01057 //
01058 // Set up for the next page.
01059 // Advance to next family file if necessary (file output).
01060 //--------------------------------------------------------------------------
01061 
01062 void plD_bop_png( PLStream *pls )
01063 {
01064     png_Dev *dev;
01065 
01066     plGetFam( pls );
01067 // force new file if pls->family set for all subsequent calls to plGetFam
01068 // n.b. putting this after plGetFam call is important since plinit calls
01069 // bop, and you don't want the familying sequence started until after
01070 // that first call to bop.
01071 
01072 // n.b. pls->dev can change because of an indirect call to plD_init_png
01073 // from plGetFam if familying is enabled.  Thus, wait to define dev until
01074 // now.
01075 
01076     dev = (png_Dev *) pls->dev;
01077 
01078     pls->famadv = 1;
01079 
01080     pls->page++;
01081 
01082     if ( dev->black15 )
01083         plD_black15_gd( pls );
01084     if ( dev->red15 )
01085         plD_red15_gd( pls );
01086 
01087 #if GD2_VERS >= 2
01088     if ( ( ( ( ( dev->truecolour > 0 ) && ( dev->palette > 0 ) ) ||     // In an EXTREMELY convaluted
01089              ( ( dev->truecolour == 0 ) && ( dev->palette == 0 ) ) ) && // manner, all this is just
01090            ( ( pls->ncol1 + pls->ncol0 ) <= 256 ) ) ||                  // asking the question, do we
01091          ( ( ( dev->palette > 0 ) && ( dev->truecolour == 0 ) ) ) )     // want truecolour or not ?
01092     {
01093 #endif
01094 
01095     dev->im_out = gdImageCreate( pls->xlength, pls->ylength );
01096 
01097     setcmap( pls );
01098 
01099 #if GD2_VERS >= 2
01100 }
01101 else
01102 {
01103     dev->im_out = gdImageCreateTrueColor( pls->xlength, pls->ylength );
01104     plP_state( PLSTATE_COLOR0 );
01105 
01106 //
01107 // In truecolour mode, the background colour GD makes is ALWAYS black, so to
01108 // "simulate" (stimulate?) a background colour other than black, we will just
01109 // draw a dirty big rectange covering the whole image and colour it in
01110 // whatever colour cmap0[0] happens to be.
01111 //
01112 // Question to C gurus: while it is slightly illogical and ugly, would:
01113 //   if ((pls->cmap0[0].r+pls->cmap0[0].g+pls->cmap0[0].b)!=0)
01114 // be more computationally efficient than:
01115 //   if ((pls->cmap0[0].r!=0)||(pls->cmap0[0].g!=0)||(pls->cmap0[0].b!=0))
01116 //  ???
01117 //
01118 
01119     if ( ( pls->cmap0[0].r != 0 ) || ( pls->cmap0[0].g != 0 ) ||
01120          ( pls->cmap0[0].b != 0 ) || ( pls->cmap0[0].a != 0.0 ) )
01121     {
01122         gdImageFilledRectangle( dev->im_out, 0, 0, pls->xlength - 1, pls->ylength - 1,
01123             gdTrueColorAlpha( pls->cmap0[0].r, pls->cmap0[0].g,
01124                 pls->cmap0[0].b,
01125                 plToGdAlpha( pls->cmap0[0].a ) ) );
01126     }
01127 }
01128 
01129 
01130 // This ensures the line width is set correctly at the beginning of
01131 //    each page
01132 
01133 plD_state_png( pls, PLSTATE_WIDTH );
01134 
01135 #endif
01136 }
01137 
01138 //--------------------------------------------------------------------------
01139 // plD_tidy_png()
01140 //
01141 // Close graphics file or otherwise clean up.
01142 //--------------------------------------------------------------------------
01143 
01144 void plD_tidy_png( PLStream *pls )
01145 {
01146 #ifdef PL_HAVE_FREETYPE
01147     if ( pls->dev_text )
01148     {
01149         plD_FreeType_Destroy( pls );
01150     }
01151 #endif
01152 
01153     plCloseFile( pls );
01154     free_mem( pls->dev );
01155 }
01156 
01157 //--------------------------------------------------------------------------
01158 // plD_black15_gd()
01159 //
01160 //  This small function simply redefines index 15 of cmap0, which is
01161 //  usually set to white, to black, but only if index 0, which is usually
01162 //  black, has been redefined to white (for example, through -bg).
01163 //
01164 //--------------------------------------------------------------------------
01165 
01166 void plD_black15_gd( PLStream *pls )
01167 {
01168     if ( pls->ncol0 > 15 )
01169     {
01170         if ( ( pls->cmap0[0].r > 227 ) && ( pls->cmap0[0].g > 227 ) && ( pls->cmap0[0].b > 227 ) )
01171         {
01172             pls->cmap0[15].r = 0;
01173             pls->cmap0[15].g = 0;
01174             pls->cmap0[15].b = 0;
01175         }
01176     }
01177 }
01178 
01179 
01180 //--------------------------------------------------------------------------
01181 // plD_red15_gd()
01182 //
01183 //
01184 //  This function swaps index 1, often the default plotting colour, with
01185 //  index 15, the last defined colour.
01186 //
01187 //  Colour 15 is usually white, and 1 is usually red, so swapping the two
01188 //  might be desirable occasionally, but it is principally here for cases
01189 //  when the background has been set on the command line to white, and the
01190 //  "def_black15" option has been issued to redefine index 15 as black. By
01191 //  issuing a command like
01192 //
01193 //      ... -bg ffffff -drvopt def_black15,swp_red15
01194 //
01195 //  the driver will set the background to white, then redefine index 15 of
01196 //  cmap0, which is usually white to black, then swap index 2 (red) to 15
01197 //  (white originally, now black), so at the end of the day, the "default"
01198 //  plotting colour is now black. Why do all of this ? It is a very quick
01199 //  way of making a nice web-friendly png without having to redefine the
01200 //  cmaps within your program.
01201 //
01202 //  If you don't like it, don't use it !
01203 //
01204 //--------------------------------------------------------------------------
01205 
01206 void plD_red15_gd( PLStream *pls )
01207 {
01208     char r = pls->cmap0[1].r;
01209     char g = pls->cmap0[1].g;
01210     char b = pls->cmap0[1].b;
01211 
01212     if ( pls->ncol0 > 15 )
01213     {
01214         pls->cmap0[1].r = pls->cmap0[15].r;
01215         pls->cmap0[1].g = pls->cmap0[15].r;
01216         pls->cmap0[1].b = pls->cmap0[15].r;
01217 
01218         pls->cmap0[15].r = r;
01219         pls->cmap0[15].g = g;
01220         pls->cmap0[15].b = b;
01221     }
01222 }
01223 
01224 
01225 //--------------------------------------------------------------------------
01226 // plD_gd_optimise()
01227 //
01228 //
01229 //  This function pretty much does exactly what it says - it optimises the
01230 //  PNG file. It does this by checking to see if all the allocated colours
01231 //  were actually used. If they were not, then it deallocates them. This
01232 //  function often results in the PNG file being saved as a 4 bit (16
01233 //  colour) PNG rather than an 8 bit (256 colour) PNG. The file size
01234 //  difference is not huge, not as great as for GIFs for example (I think
01235 //  most of the saving comes from removing redundant entries from the
01236 //  palette entry in the header); however some modest size savings occur.
01237 //
01238 //  The function isn't always successful - the optimiser will always
01239 //  deallocate unused colours as it finds them, but GD will only deallocate
01240 //  them "for real" until 16 colours are used up, and then stop since it
01241 //  doesn't make a difference if you have 17 colours or 255 colours. The
01242 //  result of this is you may end up with an image using say, 130 colours,
01243 //  but you will have 240 colour entries, some of which aren't used, and
01244 //  aren't blanked out.
01245 //
01246 //  Another side-effect of this function is the relative position of the
01247 //  colour indices MAY shift as colours are deallocated. I really don't
01248 //  think this should worry anyone, but if it does, don't optimise the
01249 //  image !
01250 //
01251 //--------------------------------------------------------------------------
01252 
01253 void plD_gd_optimise( PLStream *pls )
01254 {
01255     png_Dev *dev = (png_Dev *) pls->dev;
01256     int i, j;
01257     char *bbuf;
01258 
01259     bbuf = calloc( 256, (size_t) 1 ); // Allocate a buffer to "check off" colours as they are used
01260     if ( bbuf == NULL )
01261         plexit( "plD_gd_optimise: Out of memory." );
01262 
01263     for ( i = 0; i < ( pls->xlength - 1 ); i++ )        // Walk through the image pixel by pixel
01264     {                                                   // checking to see what colour it is
01265         for ( j = 0; j < ( pls->ylength - 1 ); j++ )    // and adding it to the list of used colours
01266         {
01267             bbuf[gdImagePalettePixel( dev->im_out, i, j )] = 1;
01268         }
01269     }
01270 
01271     for ( i = 0; i < 256; i++ ) // next walk over the colours and deallocate
01272     {                           // unused ones
01273         if ( bbuf[i] == 0 )
01274             gdImageColorDeallocate( dev->im_out, i );
01275     }
01276 
01277     free( bbuf );
01278 }
01279 
01280 
01281 #ifdef PLD_png
01282 
01283 //--------------------------------------------------------------------------
01284 // plD_eop_png()
01285 //
01286 // End of page.
01287 //--------------------------------------------------------------------------
01288 
01289 void plD_eop_png( PLStream *pls )
01290 {
01291     png_Dev *dev = (png_Dev *) pls->dev;
01292     int im_size  = 0;
01293     int png_compression;
01294     void *im_ptr = NULL;
01295     size_t nwrite;
01296 
01297     if ( pls->family || pls->page == 1 )
01298     {
01299         if ( dev->optimise )
01300         {
01301 #if GD2_VERS >= 2
01302             if ( ( ( ( ( dev->truecolour > 0 ) && ( dev->palette > 0 ) ) ||     // In an EXTREMELY convaluted
01303                      ( ( dev->truecolour == 0 ) && ( dev->palette == 0 ) ) ) && // manner, all this is just
01304                    ( ( pls->ncol1 + pls->ncol0 ) <= 256 ) ) ||                  // asking the question, do we
01305                  ( ( ( dev->palette > 0 ) && ( dev->truecolour == 0 ) ) ) )     // want truecolour or not ?
01306             {
01307 #endif
01308             plD_gd_optimise( pls );
01309 
01310 #if GD2_VERS >= 2
01311         }
01312 #endif
01313         }
01314 
01315 
01316         // image is written to output file by the driver
01317         // since if the gd.dll is linked to a different c
01318         // lib a crash occurs - this fix works also in Linux
01319         // gdImagePng(dev->im_out, pls->OutFile);
01320        #if GD2_VERS >= 2
01321 
01322         //Set the compression/quality level for PNG files.
01323         // pls->dev_compression values of 1-9 translate to the zlib compression values 1-9
01324         // pls->dev_compression values 10 <= compression <= 99 are divided by 10 to get the zlib
01325         // compression value. Values <=0 or greater than 99 are set to 90 which
01326         // translates to a zlib compression value of 9, the highest quality
01327         // of compression or smallest file size or largest computer time required
01328         // to achieve the compression.  Smaller zlib compression values correspond
01329         // to lower qualities of compression (larger file size), but lower
01330         // computer times as well.
01331 
01332         png_compression = ( ( pls->dev_compression <= 0 ) || ( pls->dev_compression > 99 ) ) ? 90 : pls->dev_compression;
01333         png_compression = ( png_compression > 9 ) ? ( png_compression / 10 ) : png_compression;
01334         im_ptr          = gdImagePngPtrEx( dev->im_out, &im_size, png_compression );
01335        #else
01336         im_ptr = gdImagePngPtr( dev->im_out, &im_size );
01337        #endif
01338         if ( im_ptr )
01339         {
01340             nwrite = fwrite( im_ptr, sizeof ( char ), im_size, pls->OutFile );
01341             if ( nwrite != im_size )
01342                 plabort( "gd driver: Error writing png file" );
01343             gdFree( im_ptr );
01344         }
01345 
01346         gdImageDestroy( dev->im_out );
01347         dev->im_out = NULL;
01348     }
01349 }
01350 
01351 #endif
01352 
01353 #ifdef PL_HAVE_FREETYPE
01354 
01355 //--------------------------------------------------------------------------
01356 //  void plD_pixel_gd (PLStream *pls, short x, short y)
01357 //
01358 //  callback function, of type "plD_pixel_fp", which specifies how a single
01359 //  pixel is set in the current colour.
01360 //--------------------------------------------------------------------------
01361 
01362 void plD_pixel_gd( PLStream *pls, short x, short y )
01363 {
01364     png_Dev *dev = (png_Dev *) pls->dev;
01365 
01366     gdImageSetPixel( dev->im_out, x, y, dev->colour );
01367 }
01368 
01369 //--------------------------------------------------------------------------
01370 //  void plD_set_pixel_gd (PLStream *pls, short x, short y)
01371 //
01372 //  callback function, of type "plD_pixel_fp", which specifies how a single
01373 //  pixel is set directly to hardware, using the colour provided
01374 //--------------------------------------------------------------------------
01375 
01376 void plD_set_pixel_gd( PLStream *pls, short x, short y, PLINT colour )
01377 {
01378     png_Dev *dev = (png_Dev *) pls->dev;
01379     int R, G, B;
01380     int Colour;
01381 
01382     G = GetGValue( colour );
01383     R = GetRValue( colour );
01384     B = GetBValue( colour );
01385 
01386     Colour = gdImageColorResolve( dev->im_out, R, G, B );
01387     gdImageSetPixel( dev->im_out, x, y, Colour );
01388 }
01389 
01390 //--------------------------------------------------------------------------
01391 //  PLINT plD_read_pixel_gd (PLStream *pls, short x, short y)
01392 //
01393 //  callback function, of type "plD_read_pixel_gd", which specifies how a
01394 //  single pixel's RGB is read (in the destination context), then
01395 //  returns an RGB encoded int with the info for blending.
01396 //--------------------------------------------------------------------------
01397 
01398 PLINT plD_read_pixel_gd( PLStream *pls, short x, short y )
01399 {
01400     png_Dev *dev = (png_Dev *) pls->dev;
01401     PLINT colour;
01402     unsigned char R, G, B;
01403 
01404     colour = gdImageGetTrueColorPixel( dev->im_out, x, y );
01405 
01406     R = gdTrueColorGetRed( colour );
01407     G = gdTrueColorGetGreen( colour );
01408     B = gdTrueColorGetBlue( colour );
01409 
01410     colour = RGB( R, G, B );
01411     return ( colour );
01412 }
01413 
01414 
01415 //--------------------------------------------------------------------------
01416 //  void init_freetype_lv1 (PLStream *pls)
01417 //
01418 //  "level 1" initialisation of the freetype library.
01419 //  "Level 1" initialisation calls plD_FreeType_init(pls) which allocates
01420 //  memory to the pls->FT structure, then sets up the pixel callback
01421 //  function.
01422 //--------------------------------------------------------------------------
01423 
01424 static void init_freetype_lv1( PLStream *pls )
01425 {
01426     FT_Data *FT;
01427 
01428     plD_FreeType_init( pls );
01429 
01430     FT             = (FT_Data *) pls->FT;
01431     FT->pixel      = (plD_pixel_fp) plD_pixel_gd;
01432     FT->read_pixel = (plD_read_pixel_fp) plD_read_pixel_gd;
01433     FT->set_pixel  = (plD_set_pixel_fp) plD_set_pixel_gd;
01434 }
01435 
01436 //--------------------------------------------------------------------------
01437 //  void init_freetype_lv2 (PLStream *pls)
01438 //
01439 //  "Level 2" initialisation of the freetype library.
01440 //  "Level 2" fills in a few setting that aren't public until after the
01441 //  graphics sub-syetm has been initialised.
01442 //  The "level 2" initialisation fills in a few things that are defined
01443 //  later in the initialisation process for the GD driver.
01444 //
01445 //  FT->scale is a scaling factor to convert co-ordinates. This is used by
01446 //  the GD and other drivers to scale back a larger virtual page and this
01447 //  eliminate the "hidden line removal bug". Set it to 1 if your device
01448 //  doesn't have scaling.
01449 //
01450 //  Some coordinate systems have zero on the bottom, others have zero on
01451 //  the top. Freetype does it one way, and most everything else does it the
01452 //  other. To make sure everything is working ok, we have to "flip" the
01453 //  coordinates, and to do this we need to know how big in the Y dimension
01454 //  the page is, and whether we have to invert the page or leave it alone.
01455 //
01456 //  FT->ymax specifies the size of the page FT->invert_y=1 tells us to
01457 //  invert the y-coordinates, FT->invert_y=0 will not invert the
01458 //  coordinates.
01459 //--------------------------------------------------------------------------
01460 
01461 static void init_freetype_lv2( PLStream *pls )
01462 {
01463     png_Dev *dev = (png_Dev *) pls->dev;
01464     FT_Data *FT  = (FT_Data *) pls->FT;
01465 
01466     FT->scale       = dev->scale;
01467     FT->ymax        = dev->pngy;
01468     FT->invert_y    = 1;
01469     FT->smooth_text = 0;
01470 
01471     if ( ( FT->want_smooth_text == 1 ) && ( FT->BLENDED_ANTIALIASING == 0 ) ) // do we want to at least *try* for smoothing ?
01472     {
01473         FT->ncol0_org   = pls->ncol0;                                         // save a copy of the original size of ncol0
01474         FT->ncol0_xtra  = NCOLOURS - ( pls->ncol1 + pls->ncol0 );             // work out how many free slots we have
01475         FT->ncol0_width = FT->ncol0_xtra / ( pls->ncol0 - 1 );                // find out how many different shades of anti-aliasing we can do
01476         if ( FT->ncol0_width > 4 )                                            // are there enough colour slots free for text smoothing ?
01477         {
01478             if ( FT->ncol0_width > max_number_of_grey_levels_used_in_text_smoothing )
01479                 FT->ncol0_width = max_number_of_grey_levels_used_in_text_smoothing;           // set a maximum number of shades
01480             plscmap0n( FT->ncol0_org + ( FT->ncol0_width * pls->ncol0 ) );                    // redefine the size of cmap0
01481 // the level manipulations are to turn off the plP_state(PLSTATE_CMAP0)
01482 // call in plscmap0 which (a) leads to segfaults since the GD image is
01483 // not defined at this point and (b) would be inefficient in any case since
01484 // setcmap is always called later (see plD_bop_png) to update the driver
01485 // color palette to be consistent with cmap0.
01486             {
01487                 PLINT level_save;
01488                 level_save = pls->level;
01489                 pls->level = 0;
01490                 pl_set_extended_cmap0( pls, FT->ncol0_width, FT->ncol0_org ); // call the function to add the extra cmap0 entries and calculate stuff
01491                 pls->level = level_save;
01492             }
01493             FT->smooth_text = 1; // Yippee ! We had success setting up the extended cmap0
01494         }
01495         else
01496             plwarn( "Insufficient colour slots available in CMAP0 to do text smoothing." );
01497     }
01498     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
01499     {
01500         FT->smooth_text = 1;
01501     }
01502 }
01503 
01504 #endif
01505 
01506 
01507 #ifdef PLD_jpeg
01508 
01509 //--------------------------------------------------------------------------
01510 // plD_eop_jpeg()
01511 //
01512 // End of page.
01513 //--------------------------------------------------------------------------
01514 
01515 void plD_eop_jpeg( PLStream *pls )
01516 {
01517     png_Dev *dev = (png_Dev *) pls->dev;
01518     int im_size  = 0;
01519     void *im_ptr = NULL;
01520     size_t nwrite;
01521     int jpeg_compression;
01522 
01523     if ( pls->family || pls->page == 1 )
01524     {
01525         //  Set the compression/quality level for JPEG files
01526         //  The higher the value, the bigger/better the image is
01527         //
01528         if ( ( pls->dev_compression <= 0 ) || ( pls->dev_compression > 99 ) )
01529             jpeg_compression = 90;
01530         else
01531             jpeg_compression = pls->dev_compression;
01532 
01533         // image is written to output file by the driver
01534         // since if the gd.dll is linked to a different c
01535         // lib a crash occurs - this fix works also in Linux
01536         // gdImageJpeg(dev->im_out, pls->OutFile, jpeg_compression);
01537         im_ptr = gdImageJpegPtr( dev->im_out, &im_size, jpeg_compression );
01538         if ( im_ptr )
01539         {
01540             nwrite = fwrite( im_ptr, sizeof ( char ), im_size, pls->OutFile );
01541             if ( nwrite != im_size )
01542                 plabort( "gd driver: Error writing png file" );
01543             gdFree( im_ptr );
01544         }
01545 
01546         gdImageDestroy( dev->im_out );
01547         dev->im_out = NULL;
01548     }
01549 }
01550 
01551 #endif
01552 
01553 #ifdef PLD_gif
01554 
01555 //--------------------------------------------------------------------------
01556 // plD_eop_gif()
01557 //
01558 // End of page.
01559 //--------------------------------------------------------------------------
01560 
01561 void plD_eop_gif( PLStream *pls )
01562 {
01563     png_Dev *dev = (png_Dev *) pls->dev;
01564     int im_size  = 0;
01565     void *im_ptr = NULL;
01566     size_t nwrite;
01567 
01568     if ( pls->family || pls->page == 1 )
01569     {
01570         // image is written to output file by the driver
01571         // since if the gd.dll is linked to a different c
01572         // lib a crash occurs - this fix works also in Linux
01573         // gdImageGif(dev->im_out, pls->OutFile);
01574         im_ptr = gdImageGifPtr( dev->im_out, &im_size );
01575         if ( im_ptr )
01576         {
01577             nwrite = fwrite( im_ptr, sizeof ( char ), im_size, pls->OutFile );
01578             if ( nwrite != im_size )
01579                 plabort( "gd driver: Error writing png file" );
01580             gdFree( im_ptr );
01581         }
01582 
01583         gdImageDestroy( dev->im_out );
01584         dev->im_out = NULL;
01585     }
01586 }
01587 
01588 #endif
01589 
01590 
01591 //#endif
01592 
01593 
01594 #else
01595 int
01596 pldummy_png()
01597 {
01598     return 0;
01599 }
01600 
01601 #endif                          // PNG
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines