• Main Page
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

gd.c

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

Generated on Wed Oct 12 2011 20:42:21 for PLplot by  doxygen 1.7.1