Back to index

plt-scheme  4.2.1
gd_drv.c
Go to the documentation of this file.
00001 /* $Id: gd_drv.c,v 1.1 2004/03/01 20:54:46 cozmic Exp $
00002 
00003          PNG and JPEG device driver based on libgd
00004 */
00005 
00006 /*
00007  *  The GD drivers, PNG and JPEG, support a number of different options
00008  *  depending on the version of GD installed.
00009  *
00010  *  If you have installed GD Ver 2.+ you gain support for truecolour (24
00011  *  bit, 16 millionish) modes as well as different line widths. These
00012  *  capibilities are part of GD more so than the GD driver, so they aren't
00013  *  available in any 1.? versions of the driver.
00014  *
00015  *  24 bit support is, by default, set to "auto" if you have V2.+ of GD.
00016  *  What this means is the *driver* decides when to use 24 bit or 8 bit
00017  *  modes for PNG files. The logic is rather simple - if you have less than
00018  *  257 colours, it is set to 8 bit mode, if more then it's in 24 bit mode.
00019  *  This should work fine for most people, most of the time, in most
00020  *  situations; however, it can be overridden in case it has to via the
00021  *  "-drvopt" command line switch. The png driver has two related settings:
00022  *              8bit    and
00023  *              24bit
00024  *
00025  *  If either of these command line toggles are set, that mode becomes the
00026  *  standard used regardless of the number of colours used. It can be envoked
00027  *  as follows:
00028  *                 x08c -dev png -drvopt 8bit -fam -o 8bitpng
00029  *                                      or
00030  *                 x08c -dev png -drvopt 24bit -fam -o 24bitpng
00031  *
00032  *  NOTE:
00033  *  The 24 bit PNG file is an RGBA file, not RGB - it includes alpha channel
00034  *  (transparency). Transparency is set to opaque, but the fact it is an
00035  *  RGBA and not an RGB might cause some problems with some viewers.
00036  *  Sadly, I can't do anything about it... sorry.
00037  *
00038  *
00039  *  Stuff for GD V1.? as well as V2.+
00040  *
00041  *  optimise
00042  *
00043  *  From version 1.17 of the GD driver, a command line option has been
00044  *  added to try and optimise the PNG files. If successful, the optimise
00045  *  command will create 4 bit (16 colour) PNGs instead of 8 bit (256 colour)
00046  *  ones. This results in slightly smaller files with no loss in any colour
00047  *  information. The function has no real memory overhead, but does have a
00048  *  slight speed hit in exchange for the optimisation. For example:
00049  *         x08c -dev png -drvopt 8bit,optimise -fam -o 8bitpng
00050  *  forces the png driver to make 8bit pngs, and will then optimise any PNG
00051  *  images with 16 or less colours into a 4 bit PNG. Note, this DOESN'T WORK
00052  *  WITH 24bit PNGs yet, and will never work with JPEGs.
00053  *
00054  *
00055  *  Also as of version 1.17 of the GD driver, the options for palette
00056  *  modification previously set with the command line option "-hack" have
00057  *  now been moved to two options settable from the -drvopt switch.
00058  *
00059  *  def_black15
00060  *
00061  *  -drvopt def_black15 sets index 15, usually white, to black if index 0,
00062  *  the background colour and usually black, has been set to white from the
00063  *  command line option -bg
00064  *
00065  *  swp_red15
00066  *
00067  *  -drvopt swp_red15 swaps index 15, usually white, with index 1, which is
00068  *  usually red. This might be desirable occasionally, but it is principally
00069  *  included for cases when the background has been set on the command line
00070  *  to white, and the "def_black15" option has been issued to redefine index
00071  *  15 as black. By issuing a command like:
00072  *                 x08c -dev png -bg ffffff -drvopt def_black15,swp_red15
00073  *  the driver will set the background to white, then redefine index 15 of
00074  *  cmap0, which is usually white to black, then swap index 2 (red) to 15
00075  *  (white originally, now black), so at the end of the day, the "default"
00076  *  plotting colour is now black. Why do all of this ? It is a very quick
00077  *  way of making a nice web-friendly png without having to redefine the
00078  *  cmaps within your program.
00079  *
00080  */
00081 
00082 
00083 #include "plDevs.h"
00084 
00085 #if defined(PLD_png) || defined(PLD_jpeg)
00086 
00087 #include "plplotP.h"
00088 #include "drivers.h"
00089 
00090 #include "gd.h"
00091 
00092 /* Device info */
00093 char* plD_DEVICE_INFO_gd = "jpeg:JPEG file:0:gd:40:jpeg\n"
00094                        "png:PNG file:0:gd:39:png";
00095 
00096 
00097 #ifdef HAVE_FREETYPE
00098 
00099 /*
00100  *  Freetype support has been added to the GD family of drivers using the
00101  *  plfreetype.c module, and implemented as a driver-specific optional extra
00102  *  invoked via the -drvopt command line toggle. It uses the
00103  *  "PLESC_HAS_TEXT" command for rendering within the driver.
00104  *
00105  *  Freetype support is turned on/off at compile time by defining
00106  *  "HAVE_FREETYPE".
00107  *
00108  *  To give the user some level of control over the fonts that are used,
00109  *  environmental variables can be set to over-ride the definitions used by
00110  *  the five default plplot fonts.
00111  *
00112  *  Freetype rendering is used with the command line "-drvopt text".
00113  *  Anti-aliased fonts can be used by issuing "-drvopt text,smooth"
00114  */
00115 
00116 #include "plfreetype.h"
00117 
00118 #endif
00119 
00120 /* Prototypes for functions in this file. */
00121 
00122 static void   fill_polygon  (PLStream *pls);
00123 static void   setcmap              (PLStream *pls);
00124 static void     plD_init_png_Dev(PLStream *pls);
00125 static void     plD_gd_optimise (PLStream *pls);
00126 static void     plD_black15_gd  (PLStream *pls);
00127 static void     plD_red15_gd    (PLStream *pls);
00128 
00129 #ifdef HAVE_FREETYPE
00130 
00131 static void plD_pixel_gd (PLStream *pls, short x, short y);
00132 static void init_freetype_lv1 (PLStream *pls);
00133 static void init_freetype_lv2 (PLStream *pls);
00134 
00135 extern void plD_FreeType_init(PLStream *pls);
00136 extern void plD_render_freetype_text (PLStream *pls, EscText *args);
00137 extern void plD_FreeType_Destroy(PLStream *pls);
00138 extern void pl_set_extended_cmap0(PLStream *pls, int ncol0_width, int ncol0_org);
00139 
00140 #endif
00141 
00142 /* top level declarations */
00143 
00144 static int NCOLOURS=gdMaxColors;
00145 
00146 /* In an attempt to fix a problem with the hidden line removal functions
00147  * that results in hidden lines *not* being removed from "small" plot
00148  * pages (ie, like a normal video screen), a "virtual" page of much
00149  * greater size is used to trick the algorithm into working correctly.
00150  * If, in future, this gets fixed on its own, then don't define
00151  * "use_experimental_hidden_line_hack"
00152  */
00153 
00154 #define use_experimental_hidden_line_hack
00155 
00156 /* I think the current version of Freetype supports up to a maximum of
00157  * 128 grey levels for text smoothing. You can get quite acceptable
00158  * results with as few as 4 grey-levels. Uusually only about 5 get used
00159  * anyway, but the question is where, in the "grey spectrum" will they be ?
00160  * Who knows ? The following define lets you set a maximum limit on the
00161  * number of grey-levels used. It is really only here for the 24bit mode
00162  * and could be set to 255, but that would slow things down and use more
00163  * memory. 64 seems to be a nice compromise, but if you want to change it,
00164  * then change it here.
00165  */
00166 
00167 #ifndef max_number_of_grey_levels_used_in_text_smoothing 
00168 #define max_number_of_grey_levels_used_in_text_smoothing 64
00169 #endif
00170 
00171 /* Not present in versions before 2.0 */
00172 
00173 #ifndef gdImagePalettePixel
00174 #define gdImagePalettePixel(  im, x, y )   (im)->pixels[(y)][(x)]
00175 #endif
00176 
00177 /* Struct to hold device-specific info. */
00178 
00179 typedef struct {
00180 
00181        gdImagePtr im_out;                      /* Graphics pointer */
00182         PLINT pngx;
00183         PLINT pngy;
00184 
00185         int colour;                             /* Current Colour               */
00186         int totcol;                             /* Total number of colours      */
00187         int ncol1;                              /* Actual size of ncol1 we got  */
00188 
00189        int scale;                              /* scaling factor to "blow up" to */
00190                                                 /* the "virtual" page in removing hidden lines*/
00191 
00192        int optimise;                           /* Flag used for 4bit pngs */
00193         int black15;                            /* Flag used for forcing a black colour */
00194         int red15;                              /* Flag for swapping red and 15 */
00195 
00196 #if GD2_VERS >= 2
00197         int truecolour;                         /* Flag to ALWAYS force 24 bit mode */
00198         int palette;                            /* Flag to ALWAYS force  8 bit mode */
00199 #endif
00200 
00201 } png_Dev;
00202 
00203 void plD_init_png           (PLStream *);
00204 void plD_line_png           (PLStream *, short, short, short, short);
00205 void plD_polyline_png              (PLStream *, short *, short *, PLINT);
00206 void plD_eop_png            (PLStream *);
00207 void plD_eop_jpeg           (PLStream *);
00208 void plD_bop_png            (PLStream *);
00209 void plD_tidy_png           (PLStream *);
00210 void plD_state_png          (PLStream *, PLINT);
00211 void plD_esc_png            (PLStream *, PLINT, void *);
00212 
00213 #ifdef PLD_png
00214 
00215 void plD_dispatch_init_png( PLDispatchTable *pdt )
00216 {
00217     pdt->pl_MenuStr  = "PNG file";
00218     pdt->pl_DevName  = "png";
00219     pdt->pl_type     = plDevType_FileOriented;
00220     pdt->pl_seq      = 39;
00221     pdt->pl_init     = (plD_init_fp)     plD_init_png;
00222     pdt->pl_line     = (plD_line_fp)     plD_line_png;
00223     pdt->pl_polyline = (plD_polyline_fp) plD_polyline_png;
00224     pdt->pl_eop      = (plD_eop_fp)      plD_eop_png;
00225     pdt->pl_bop      = (plD_bop_fp)      plD_bop_png;
00226     pdt->pl_tidy     = (plD_tidy_fp)     plD_tidy_png;
00227     pdt->pl_state    = (plD_state_fp)    plD_state_png;
00228     pdt->pl_esc      = (plD_esc_fp)      plD_esc_png;
00229 }
00230 
00231 #endif
00232 
00233 #ifdef PLD_jpeg
00234 
00235 void plD_dispatch_init_jpeg( PLDispatchTable *pdt )
00236 {
00237     pdt->pl_MenuStr  = "JPEG File";
00238     pdt->pl_DevName  = "jpeg";
00239     pdt->pl_type     = plDevType_FileOriented;
00240     pdt->pl_seq      = 40;
00241     pdt->pl_init     = (plD_init_fp)     plD_init_png;
00242     pdt->pl_line     = (plD_line_fp)     plD_line_png;
00243     pdt->pl_polyline = (plD_polyline_fp) plD_polyline_png;
00244     pdt->pl_eop      = (plD_eop_fp)      plD_eop_jpeg;
00245     pdt->pl_bop      = (plD_bop_fp)      plD_bop_png;
00246     pdt->pl_tidy     = (plD_tidy_fp)     plD_tidy_png;
00247     pdt->pl_state    = (plD_state_fp)    plD_state_png;
00248     pdt->pl_esc      = (plD_esc_fp)      plD_esc_png;
00249 }
00250 #endif
00251 
00252 /*--------------------------------------------------------------------------*\
00253  * plD_init_png_Dev()
00254  *
00255 \*--------------------------------------------------------------------------*/
00256 
00257 static void
00258 plD_init_png_Dev(PLStream *pls)
00259 {
00260     png_Dev *dev;
00261     int i;
00262 
00263 /*  Stuff for the driver options, these vars are copied into the driver
00264  *  structure so that everything is thread safe and reenterant.
00265  */
00266 
00267     int optimise=0;
00268     int black15=0;
00269     int red15=0;
00270 #if GD2_VERS >= 2
00271     int truecolour=0;
00272     int palette=0;
00273 #endif
00274 #ifdef HAVE_FREETYPE
00275     int freetype=0;
00276     int smooth_text=0;
00277     FT_Data *FT;
00278 #endif
00279 
00280     DrvOpt gd_options[] = {{"optimise", DRV_INT, &optimise, "Optimise PNG palette when possible"},
00281                               {"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\""},
00282                               {"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"},
00283 #if GD2_VERS >= 2
00284                               {"8bit", DRV_INT, &palette, "Palette (8 bit) mode"},
00285                               {"24bit", DRV_INT, &truecolour, "Truecolor (24 bit) mode"},
00286 #endif
00287 #ifdef HAVE_FREETYPE
00288                               {"text", DRV_INT, &freetype, "Use driver text (FreeType)"},
00289                               {"smooth", DRV_INT, &smooth_text, "Turn text smoothing on (1) or off (0)"},
00290 #endif
00291                            {NULL, DRV_INT, NULL, NULL}};
00292 
00293 
00294 /* Allocate and initialize device-specific data */
00295 
00296     if (pls->dev != NULL)
00297        free((void *) pls->dev);
00298 
00299     pls->dev = calloc(1, (size_t) sizeof(png_Dev));
00300     if (pls->dev == NULL)
00301        plexit("plD_init_png_Dev: Out of memory.");
00302 
00303     dev = (png_Dev *) pls->dev;
00304 
00305     dev->colour=1;  /* Set a fall back pen colour in case user doesn't */
00306 
00307 /*
00308  *  Set the compression/quality level for JPEG files
00309  *  The higher the value, the bigger/better the image is
00310  */
00311     if ( (pls->dev_compression<=0)||(pls->dev_compression>99) )
00312        pls->dev_compression=90;
00313 
00314 /* Check for and set up driver options */
00315 
00316     plParseDrvOpts(gd_options);
00317 
00318     dev->black15=black15;
00319     dev->red15=red15;
00320     dev->optimise=optimise;
00321 
00322 #if GD2_VERS >= 2
00323 
00324     dev->palette=palette;
00325     dev->truecolour=truecolour;
00326     
00327     if ((dev->truecolour>0) && (dev->palette>0))
00328        plwarn("Selecting both \"truecolor\" AND \"palette\" driver options is contradictory, so\nI will just use my best judgment.\n");
00329     else if (dev->truecolour>0)
00330        NCOLOURS=16777216;
00331     else if ((dev->truecolour==0)&&(dev->palette==0)&&((pls->ncol1+pls->ncol0)>NCOLOURS))
00332        {
00333         NCOLOURS=16777216;
00334        }
00335 
00336 #endif
00337 
00338 #ifdef HAVE_FREETYPE
00339 if (freetype)
00340    {
00341     pls->dev_text = 1; /* want to draw text */
00342     init_freetype_lv1(pls);
00343     FT=(FT_Data *)pls->FT;
00344     FT->want_smooth_text=smooth_text;
00345    }
00346 
00347 #endif
00348 }
00349 
00350 /*----------------------------------------------------------------------*\
00351  * plD_init_png()
00352  *
00353  * Initialize device.
00354 \*----------------------------------------------------------------------*/
00355 
00356 void plD_init_png(PLStream *pls)
00357 {
00358     png_Dev *dev=NULL;
00359 
00360     pls->termin = 0;            /* Not an interactive device */
00361     pls->icol0 = 1;
00362     pls->bytecnt = 0;
00363     pls->page = 0;
00364     pls->dev_fill0 = 1;         /* Can do solid fills */
00365 
00366     if (!pls->colorset)
00367        pls->color = 1;         /* Is a color device */
00368 
00369 /* Initialize family file info */
00370     plFamInit(pls);
00371 
00372 /* Prompt for a file name if not already set */
00373     plOpenFile(pls);
00374 
00375 /* Allocate and initialize device-specific data */
00376     plD_init_png_Dev(pls);
00377     dev=(png_Dev *)pls->dev;
00378 
00379       if (pls->xlength <= 0 || pls->ylength <=0)
00380       {
00381 /* use default width, height of 800x600 if not specifed by -geometry option
00382  * or plspage */
00383         plspage(0., 0., 800, 600, 0, 0);
00384       }
00385 
00386      pls->graphx = GRAPHICS_MODE;
00387 
00388      dev->pngx = pls->xlength - 1; /* should I use -1 or not??? */
00389      dev->pngy = pls->ylength - 1;
00390 
00391 #ifdef use_experimental_hidden_line_hack
00392 
00393      if (dev->pngx>dev->pngy)    /* Work out the scaling factor for the  */
00394         {                        /* "virtual" (oversized) page           */
00395         dev->scale=PIXELS_X/dev->pngx;
00396         }
00397      else
00398         {
00399         dev->scale=PIXELS_Y/dev->pngy;
00400         }
00401 #else
00402 
00403      dev->scale=1;
00404 
00405 #endif
00406 
00407 
00408      if (pls->xdpi<=0)
00409      {
00410 /* This corresponds to a typical monitor resolution of 4 pixels/mm. */
00411         plspage(4.*25.4, 4.*25.4, 0, 0, 0, 0);
00412      }
00413      else
00414      {
00415         pls->ydpi=pls->xdpi;        /* Set X and Y dpi's to the same value */
00416      }
00417 /* Convert DPI to pixels/mm */
00418      plP_setpxl(dev->scale*pls->xdpi/25.4,dev->scale*pls->ydpi/25.4);
00419 
00420      plP_setphy(0, dev->scale*dev->pngx, 0, dev->scale*dev->pngy);
00421 
00422 #ifdef HAVE_FREETYPE
00423 if (pls->dev_text)
00424    {
00425     init_freetype_lv2(pls);
00426    }
00427 #endif
00428 
00429 }
00430 
00431 /*----------------------------------------------------------------------*\
00432  * plD_line_png()
00433  *
00434  * Draw a line in the current color from (x1,y1) to (x2,y2).
00435 \*----------------------------------------------------------------------*/
00436 
00437 void
00438 plD_line_png(PLStream *pls, short x1a, short y1a, short x2a, short y2a)
00439 {
00440     png_Dev *dev=(png_Dev *)pls->dev;
00441     int x1 = x1a/dev->scale, y1 = y1a/dev->scale, x2 = x2a/dev->scale, y2 = y2a/dev->scale;
00442     y1 = dev->pngy - y1;
00443     y2 = dev->pngy - y2;
00444 
00445     gdImageLine(dev->im_out, x1, y1, x2, y2, dev->colour);
00446 
00447 }
00448 
00449 /*----------------------------------------------------------------------*\
00450  * plD_polyline_png()
00451  *
00452  * Draw a polyline in the current color.
00453 \*----------------------------------------------------------------------*/
00454 
00455 void
00456 plD_polyline_png(PLStream *pls, short *xa, short *ya, PLINT npts)
00457 {
00458     PLINT i;
00459 
00460     for (i = 0; i < npts - 1; i++)
00461        plD_line_png(pls, xa[i], ya[i], xa[i + 1], ya[i + 1]);
00462 }
00463 
00464 
00465 /*----------------------------------------------------------------------*\
00466  * fill_polygon()
00467  *
00468  * Fill polygon described in points pls->dev_x[] and pls->dev_y[].
00469 \*----------------------------------------------------------------------*/
00470 
00471 static void
00472 fill_polygon(PLStream *pls)
00473 {
00474 png_Dev *dev=(png_Dev *)pls->dev;
00475 
00476     int i;
00477     gdPoint *points=NULL;
00478 
00479     if (pls->dev_npts < 1)
00480        return;
00481 
00482      points = malloc((size_t)pls->dev_npts * sizeof(gdPoint));
00483 
00484      for (i = 0; i < pls->dev_npts; i++)
00485          {
00486           points[i].x = pls->dev_x[i]/dev->scale;
00487           points[i].y = dev->pngy - (pls->dev_y[i]/dev->scale);
00488          }
00489 
00490    gdImageFilledPolygon(dev->im_out, points, pls->dev_npts, dev->colour);
00491    free(points);
00492 
00493 }
00494 
00495 /*----------------------------------------------------------------------*\
00496  * setcmap()
00497  *
00498  * Sets up color palette.
00499 \*----------------------------------------------------------------------*/
00500 
00501 static void
00502 setcmap(PLStream *pls)
00503 {
00504     int i, ncol1=pls->ncol1;
00505     int ncol0=pls->ncol0, total_colours;
00506     PLColor cmap1col;
00507     png_Dev *dev=(png_Dev *)pls->dev;
00508     PLFLT tmp_colour_pos;
00509 
00510 /*
00511  * Yuckky fix to get rid of the previosuly allocated palette from the 
00512  * GD image
00513  */
00514 
00515     for (i=0;i<256;i++) 
00516       { 
00517        gdImageColorDeallocate(dev->im_out,i);
00518       }
00519 
00520     if (ncol0>NCOLOURS/2)     /* Check for ridiculous number of colours */
00521        {                      /* in ncol0, and appropriately adjust the */
00522         plwarn("Too many colours in cmap0.");     /* number, issuing a  */
00523         ncol0=NCOLOURS/2;                         /* warning if it does */
00524         pls->ncol0=ncol0;
00525        }
00526 
00527     dev->totcol=0;       /* Reset the number of colours counter to zero */
00528 
00529     total_colours=ncol0+ncol1;  /* Work out how many colours are wanted */
00530 
00531     if (total_colours>NCOLOURS)     /* Do some rather modest error      */
00532        {                            /* checking to make sure that       */
00533         total_colours=NCOLOURS;     /* we are not defining more colours */
00534         ncol1=total_colours-ncol0;  /* than we have room for.           */
00535 
00536         if (ncol1<=0)
00537            {
00538             plexit("Problem setting colourmap in PNG or JPEG driver.");
00539            }
00540        }
00541 
00542  dev->ncol1=ncol1;  /* The actual size of ncol1, regardless of what was asked.
00543                      * This is dependent on colour slots available.
00544                      * It might well be the same as ncol1.
00545                      */
00546 
00547 /* Initialize cmap 0 colors */
00548 
00549 if (ncol0>0)  /* make sure the program actually asked for cmap0 first */
00550    {
00551     for (i = 0; i < ncol0; i++)
00552         {
00553         gdImageColorAllocate(dev->im_out,
00554                              pls->cmap0[i].r, pls->cmap0[i].g, pls->cmap0[i].b);
00555         ++dev->totcol; /* count the number of colours we use as we use them */
00556         }
00557 
00558 
00559   }
00560 
00561 /* Initialize any remaining slots for cmap1 */
00562 
00563 
00564 if (ncol1>0)    /* make sure that we want to define cmap1 first */
00565    {
00566     for (i = 0; i < ncol1; i++)
00567         {
00568 
00569          if (ncol1<pls->ncol1)       /* Check the dynamic range of colours */
00570             {
00571 
00572              /*
00573               * Ok, now if we have less colour slots available than are being
00574               * defined by pls->ncol1, then we still want to use the full
00575               * dynamic range of cmap1 as best we can, so what we do is work
00576               * out an approximation to the index in the full dynamic range
00577               * in cases when pls->ncol1 exceeds the number of free colours.
00578               */
00579 
00580              tmp_colour_pos= i>0 ? pls->ncol1*((PLFLT)i/ncol1) : 0;
00581              plcol_interp(pls, &cmap1col, (int) tmp_colour_pos, pls->ncol1);
00582 
00583             }
00584          else
00585             {
00586              plcol_interp(pls, &cmap1col, i, ncol1);
00587             }
00588 
00589 
00590          gdImageColorAllocate(dev->im_out,
00591                                    cmap1col.r, cmap1col.g, cmap1col.b);
00592 
00593          ++dev->totcol; /* count the number of colours we use as we go */
00594         }
00595    }
00596 }
00597 
00598 
00599 /*----------------------------------------------------------------------*\
00600  * plD_state_png()
00601  *
00602  * Handle change in PLStream state (color, pen width, fill attribute, etc).
00603 \*----------------------------------------------------------------------*/
00604 
00605 void
00606 plD_state_png(PLStream *pls, PLINT op)
00607 {
00608 png_Dev *dev=(png_Dev *)pls->dev;
00609 PLFLT tmp_colour_pos;
00610 #if GD2_VERS >= 2
00611 long temp_col;
00612 #endif
00613 
00614 
00615     switch (op) {
00616 
00617 #if GD2_VERS >= 2
00618     case PLSTATE_WIDTH:
00619         gdImageSetThickness(dev->im_out, pls->width);
00620        break;
00621 #endif
00622 
00623     case PLSTATE_COLOR0:
00624 #if GD2_VERS >= 2
00625 
00626        if ( (pls->icol0 == PL_RGB_COLOR)||     /*  Should never happen since PL_RGB_COLOR is depreciated, but here for backwards compatibility */
00627              (gdImageTrueColor(dev->im_out)) )  /*  We will do this if we are in "TrueColour" mode */
00628            {
00629            if ( (dev->totcol < NCOLOURS)||         /* See if there are slots left, if so we will allocate a new colour */
00630                  (gdImageTrueColor(dev->im_out)) )  /* In TrueColour mode we allocate each colour as we come to it */
00631               {
00632                /* Next allocate a new colour to a temporary slot since what we do with it will varay depending on if its a pallter index or truecolour */
00633                 temp_col=gdImageColorAllocate(dev->im_out,pls->curcolor.r,
00634                                              pls->curcolor.g, pls->curcolor.b);
00635 
00636                 if (gdImageTrueColor(dev->im_out))
00637                     dev->colour = temp_col;     /* If it's truecolour, then we will directly set dev->colour to our "new" colour */
00638                 else
00639                     {
00640                      dev->colour = dev->totcol;  /* or else, we will just set it to the last colour */
00641                      dev->totcol++;              /* Bump the total colours for next time round */
00642                     }
00643               }
00644 
00645            }
00646          else  /* just a normal colour allocate, so don't worry about the above stuff, just grab the index */
00647            {
00648             dev->colour = pls->icol0;
00649            }
00650 
00651 #else
00652        dev->colour = pls->icol0;
00653        if (dev->colour == PL_RGB_COLOR)
00654            {
00655            if (dev->totcol < NCOLOURS)
00656               {
00657                 gdImageColorAllocate(dev->im_out,pls->curcolor.r, pls->curcolor.g,  pls->curcolor.b);
00658               dev->colour = dev->totcol;
00659               }
00660 
00661            }
00662 #endif
00663        break;
00664 
00665     case PLSTATE_COLOR1:
00666 
00667 #if GD2_VERS >= 2
00668        if (!gdImageTrueColor(dev->im_out))
00669           {
00670 #endif
00671            /*
00672             * Start by checking to see if we have to compensate for cases where
00673             * we don't have the full dynamic range of cmap1 at our disposal
00674             */
00675            if (dev->ncol1<pls->ncol1)
00676               {
00677                tmp_colour_pos=dev->ncol1*((PLFLT)pls->icol1/(pls->ncol1>0 ? pls->ncol1 : 1));
00678                dev->colour = pls->ncol0 + (int)tmp_colour_pos;
00679               }
00680            else
00681               dev->colour = pls->ncol0 + pls->icol1;
00682 #if GD2_VERS >= 2
00683            }
00684         else    /* it is a truecolour image */
00685            {
00686              dev->colour = gdTrueColor(pls->curcolor.r, pls->curcolor.g, pls->curcolor.b);
00687            }
00688 #endif
00689        break;
00690 
00691 
00692     case PLSTATE_CMAP0:
00693     case PLSTATE_CMAP1:
00694 
00695 #if GD2_VERS >= 2
00696        if (!gdImageTrueColor(dev->im_out))
00697           {
00698 #endif
00699 
00700     /*
00701      *  Code to redefine the entire palette
00702      */
00703 
00704 
00705        if (pls->color)
00706            setcmap(pls);
00707 
00708 #if GD2_VERS >= 2
00709 }
00710 #endif
00711 
00712        break;
00713     }
00714 }
00715 
00716 
00717 /*----------------------------------------------------------------------*\
00718  * plD_esc_png()
00719  *
00720  * Escape function.
00721 \*----------------------------------------------------------------------*/
00722 
00723 void plD_esc_png(PLStream *pls, PLINT op, void *ptr)
00724 {
00725     switch (op) {
00726 
00727       case PLESC_FILL:  /* fill */
00728        fill_polygon(pls);
00729        break;
00730 
00731 #ifdef HAVE_FREETYPE
00732      case PLESC_HAS_TEXT:
00733         plD_render_freetype_text(pls, (EscText *)ptr);
00734         break;
00735 #endif
00736 
00737     }
00738 }
00739 
00740 /*----------------------------------------------------------------------*\
00741  * plD_bop_png()
00742  *
00743  * Set up for the next page.
00744  * Advance to next family file if necessary (file output).
00745 \*----------------------------------------------------------------------*/
00746 
00747 void plD_bop_png(PLStream *pls)
00748 {
00749     png_Dev *dev;
00750 
00751     plGetFam(pls);
00752 /* force new file if pls->family set for all subsequent calls to plGetFam
00753  * n.b. putting this after plGetFam call is important since plinit calls
00754  * bop, and you don't want the familying sequence started until after
00755  * that first call to bop.*/
00756 
00757 /* n.b. pls->dev can change because of an indirect call to plD_init_png
00758  * from plGetFam if familying is enabled.  Thus, wait to define dev until
00759  * now. */
00760 
00761     dev = (png_Dev *) pls->dev;
00762 
00763     pls->famadv = 1;
00764 
00765     pls->page++;
00766 
00767 if (dev->black15) plD_black15_gd(pls);
00768 if (dev->red15) plD_red15_gd(pls);
00769 
00770 #if GD2_VERS >= 2
00771   if ( ( ((dev->truecolour>0) && (dev->palette>0))||     /* In an EXTREMELY convaluted */
00772          ((dev->truecolour==0) && (dev->palette==0))&&   /* manner, all this is just   */
00773           ((pls->ncol1+pls->ncol0)<=256) )||             /* asking the question, do we */
00774        ( ((dev->palette>0)&&(dev->truecolour==0)) )  )   /* want truecolour or not ?   */
00775         {
00776 #endif
00777 
00778            dev->im_out = gdImageCreate(pls->xlength, pls->ylength);
00779 
00780            setcmap(pls);
00781 
00782 #if GD2_VERS >= 2
00783          }
00784        else
00785          {
00786          dev->im_out = gdImageCreateTrueColor(pls->xlength, pls->ylength);
00787 
00788 /*
00789  * In truecolour mode, the background colour GD makes is ALWAYS black, so to
00790  * "simulate" (stimulate?) a background colour other than black, we will just
00791  * draw a dirty big rectange covering the whole image and colour it in
00792  * whatever colour cmap0[0] happens to be.
00793  *
00794  * Question to C gurus: while it is slightly illogical and ugly, would:
00795  *   if ((pls->cmap0[0].r+pls->cmap0[0].g+pls->cmap0[0].b)!=0)
00796  * be more computationally efficient than:
00797  *   if ((pls->cmap0[0].r!=0)||(pls->cmap0[0].g!=0)||(pls->cmap0[0].b!=0))
00798  *  ???
00799  */
00800 
00801          if ( (pls->cmap0[0].r!=0)||(pls->cmap0[0].g!=0)||
00802               (pls->cmap0[0].b!=0) )
00803             {
00804              gdImageFilledRectangle(dev->im_out,0,0, pls->xlength-1, pls->ylength-1,
00805                                     gdTrueColor(pls->cmap0[0].r,pls->cmap0[0].g,
00806                                                 pls->cmap0[0].b));
00807             }
00808 
00809          }
00810 
00811 
00812 /* This ensures the line width is set correctly at the beginning of
00813  *    each page */
00814 
00815    plD_state_png(pls, PLSTATE_WIDTH);
00816 
00817 #endif
00818 
00819 
00820 }
00821 
00822 /*----------------------------------------------------------------------*\
00823  * plD_tidy_png()
00824  *
00825  * Close graphics file or otherwise clean up.
00826 \*----------------------------------------------------------------------*/
00827 
00828 void plD_tidy_png(PLStream *pls)
00829 {
00830 
00831 #ifdef HAVE_FREETYPE
00832    if (pls->dev_text)
00833      {
00834        FT_Data *FT=(FT_Data *)pls->FT;
00835        plscmap0n(FT->ncol0_org);
00836        plD_FreeType_Destroy(pls);
00837      }
00838 #endif
00839 
00840    fclose(pls->OutFile);
00841    free_mem(pls->dev);
00842 }
00843 
00844 /*----------------------------------------------------------------------*\
00845  * plD_black15_gd()
00846  *
00847  *  This small function simply redefines index 15 of cmap0, which is
00848  *  usually set to white, to black, but only if index 0, which is usually
00849  *  black, has been redefined to white (for example, through -bg).
00850  *
00851 \*----------------------------------------------------------------------*/
00852 
00853 void plD_black15_gd(PLStream *pls)
00854 {
00855 
00856 if (pls->ncol0>15)
00857    {
00858     if ((pls->cmap0[0].r>227)&&(pls->cmap0[0].g>227)&&(pls->cmap0[0].b>227))
00859        {
00860         pls->cmap0[15].r=0;
00861         pls->cmap0[15].g=0;
00862         pls->cmap0[15].b=0;
00863       }
00864    }
00865 }
00866 
00867 
00868 /*----------------------------------------------------------------------*\
00869  * plD_red15_gd()
00870  *
00871  *
00872  *  This function swaps index 1, often the default plotting colour, with
00873  *  index 15, the last defined colour.
00874  *
00875  *  Colour 15 is usually white, and 1 is usually red, so swapping the two
00876  *  might be desirable occasionally, but it is principally here for cases
00877  *  when the background has been set on the command line to white, and the
00878  *  "def_black15" option has been issued to redefine index 15 as black. By
00879  *  issuing a command like
00880  *
00881  *      ... -bg ffffff -drvopt def_black15,swp_red15
00882  *
00883  *  the driver will set the background to white, then redefine index 15 of
00884  *  cmap0, which is usually white to black, then swap index 2 (red) to 15
00885  *  (white originally, now black), so at the end of the day, the "default"
00886  *  plotting colour is now black. Why do all of this ? It is a very quick
00887  *  way of making a nice web-friendly png without having to redefine the
00888  *  cmaps within your program.
00889  *
00890  *  If you don't like it, don't use it !
00891  *
00892 \*----------------------------------------------------------------------*/
00893 
00894 void plD_red15_gd(PLStream *pls)
00895 {
00896 char r=pls->cmap0[1].r;
00897 char g=pls->cmap0[1].g;
00898 char b=pls->cmap0[1].b;
00899 
00900 if (pls->ncol0>15)
00901    {
00902     pls->cmap0[1].r=pls->cmap0[15].r;
00903     pls->cmap0[1].g=pls->cmap0[15].r;
00904     pls->cmap0[1].b=pls->cmap0[15].r;
00905 
00906     pls->cmap0[15].r=r;
00907     pls->cmap0[15].g=g;
00908     pls->cmap0[15].b=b;
00909    }
00910 }
00911 
00912 
00913 /*----------------------------------------------------------------------*\
00914  * plD_gd_optimise()
00915  *
00916  *
00917  *  This function pretty much does exactly what it says - it optimises the
00918  *  PNG file. It does this by checking to see if all the allocated colours
00919  *  were actually used. If they were not, then it deallocates them. This
00920  *  function often results in the PNG file being saved as a 4 bit (16
00921  *  colour) PNG rather than an 8 bit (256 colour) PNG. The file size
00922  *  difference is not huge, not as great as for GIFs for example (I think
00923  *  most of the saving comes from removing redundant entries from the
00924  *  palette entry in the header); however some modest size savings occur.
00925  *
00926  *  The function isn't always successful - the optimiser will always
00927  *  deallocate unused colours as it finds them, but GD will only deallocate
00928  *  them "for real" until 16 colours are used up, and then stop since it
00929  *  doesn't make a difference if you have 17 colours or 255 colours. The
00930  *  result of this is you may end up with an image using say, 130 colours,
00931  *  but you will have 240 colour entries, some of which aren't used, and
00932  *  aren't blanked out.
00933  *
00934  *  Another side-effect of this function is the relative position of the
00935  *  colour indices MAY shift as colours are deallocated. I really don't
00936  *  think this should worry anyone, but if it does, don't optimise the
00937  *  image !
00938  *
00939 \*----------------------------------------------------------------------*/
00940 
00941 void plD_gd_optimise(PLStream *pls)
00942 {
00943 png_Dev *dev=(png_Dev *)pls->dev;
00944 int i,j;
00945 char *bbuf;
00946 
00947 bbuf=calloc(256,(size_t) 1);    /* Allocate a buffer to "check off" colours as they are used */
00948 if (bbuf==NULL) plexit("plD_gd_optimise: Out of memory.");
00949 
00950     for(i=0;i<(pls->xlength-1);i++)        /* Walk through the image pixel by pixel */
00951        {                                   /* checking to see what colour it is */
00952         for(j=0;j<(pls->ylength-1);j++)    /* and adding it to the list of used colours */
00953            {
00954             bbuf[gdImagePalettePixel(dev->im_out, i, j)]=1;
00955            }
00956        }
00957 
00958 for (i=0;i<256;i++)     /* next walk over the colours and deallocate */
00959     {                   /* unused ones */
00960     if (bbuf[i]==0) gdImageColorDeallocate(dev->im_out,i);
00961     }
00962 
00963 free(bbuf);
00964 }
00965 
00966 
00967 #ifdef PLD_png
00968 
00969 /*----------------------------------------------------------------------*\
00970  * plD_eop_png()
00971  *
00972  * End of page.
00973 \*----------------------------------------------------------------------*/
00974 
00975 void plD_eop_png(PLStream *pls)
00976 {
00977 png_Dev *dev=(png_Dev *)pls->dev;
00978 int i;
00979 
00980     if (pls->family || pls->page == 1) {
00981 
00982    if (dev->optimise)
00983      {
00984 #if GD2_VERS >= 2
00985       if ( ( ((dev->truecolour>0) && (dev->palette>0))||     /* In an EXTREMELY convaluted */
00986              ((dev->truecolour==0) && (dev->palette==0))&&   /* manner, all this is just   */
00987               ((pls->ncol1+pls->ncol0)<=256) )||             /* asking the question, do we */
00988            ( ((dev->palette>0)&&(dev->truecolour==0)) )  )   /* want truecolour or not ?   */
00989             {
00990 #endif
00991              plD_gd_optimise(pls);
00992 
00993 #if GD2_VERS >= 2
00994             }
00995 #endif
00996         }
00997 
00998        gdImagePng(dev->im_out, pls->OutFile);
00999 
01000        gdImageDestroy(dev->im_out);
01001     }
01002 }
01003 
01004 #endif
01005 
01006 #ifdef HAVE_FREETYPE
01007 
01008 /*----------------------------------------------------------------------*\
01009  *  void plD_pixel_gd (PLStream *pls, short x, short y)
01010  *
01011  *  callback function, of type "plD_pixel_fp", which specifies how a single
01012  *  pixel is set in the current colour.
01013 \*----------------------------------------------------------------------*/
01014 
01015 void plD_pixel_gd (PLStream *pls, short x, short y)
01016 {
01017 png_Dev *dev=(png_Dev *)pls->dev;
01018 
01019    gdImageSetPixel(dev->im_out, x, y,dev->colour);
01020 
01021 }
01022 
01023 /*----------------------------------------------------------------------*\
01024  *  void init_freetype_lv1 (PLStream *pls)
01025  *
01026  *  "level 1" initialisation of the freetype library.
01027  *  "Level 1" initialisation calls plD_FreeType_init(pls) which allocates
01028  *  memory to the pls->FT structure, then sets up the pixel callback
01029  *  function.
01030 \*----------------------------------------------------------------------*/
01031 
01032 static void init_freetype_lv1 (PLStream *pls)
01033 {
01034 FT_Data *FT;
01035 
01036 plD_FreeType_init(pls);
01037 
01038 FT=(FT_Data *)pls->FT;
01039 FT->pixel= (plD_pixel_fp)plD_pixel_gd;
01040 
01041 
01042 }
01043 
01044 /*----------------------------------------------------------------------*\
01045  *  void init_freetype_lv2 (PLStream *pls)
01046  *
01047  *  "Level 2" initialisation of the freetype library.
01048  *  "Level 2" fills in a few setting that aren't public until after the
01049  *  graphics sub-syetm has been initialised.
01050  *  The "level 2" initialisation fills in a few things that are defined
01051  *  later in the initialisation process for the GD driver.
01052  *
01053  *  FT->scale is a scaling factor to convert co-ordinates. This is used by
01054  *  the GD and other drivers to scale back a larger virtual page and this
01055  *  eliminate the "hidden line removal bug". Set it to 1 if your device
01056  *  doesn't have scaling.
01057  *
01058  *  Some coordinate systems have zero on the bottom, others have zero on
01059  *  the top. Freetype does it one way, and most everything else does it the
01060  *  other. To make sure everything is working ok, we have to "flip" the
01061  *  coordinates, and to do this we need to know how big in the Y dimension
01062  *  the page is, and whether we have to invert the page or leave it alone.
01063  *
01064  *  FT->ymax specifies the size of the page FT->invert_y=1 tells us to
01065  *  invert the y-coordinates, FT->invert_y=0 will not invert the
01066  *  coordinates.
01067 \*----------------------------------------------------------------------*/
01068 
01069 static void init_freetype_lv2 (PLStream *pls)
01070 {
01071 png_Dev *dev=(png_Dev *)pls->dev;
01072 FT_Data *FT=(FT_Data *)pls->FT;
01073 
01074 FT->scale=dev->scale;
01075 FT->ymax=dev->pngy;
01076 FT->invert_y=1;
01077 FT->smooth_text=0;
01078 
01079 if (FT->want_smooth_text==1)    /* do we want to at least *try* for smoothing ? */
01080    {
01081     FT->ncol0_org=pls->ncol0;                                   /* save a copy of the original size of ncol0 */
01082     FT->ncol0_xtra=NCOLOURS-(pls->ncol1+pls->ncol0);            /* work out how many free slots we have */
01083     FT->ncol0_width=FT->ncol0_xtra/(pls->ncol0-1);              /* find out how many different shades of anti-aliasing we can do */
01084     if (FT->ncol0_width>4)     /* are there enough colour slots free for text smoothing ? */
01085        {
01086         if (FT->ncol0_width>max_number_of_grey_levels_used_in_text_smoothing) 
01087            FT->ncol0_width=max_number_of_grey_levels_used_in_text_smoothing;                 /* set a maximum number of shades */
01088         plscmap0n(FT->ncol0_org+(FT->ncol0_width*pls->ncol0));      /* redefine the size of cmap0 */
01089 /* the level manipulations are to turn off the plP_state(PLSTATE_CMAP0)
01090  * call in plscmap0 which (a) leads to segfaults since the GD image is
01091  * not defined at this point and (b) would be inefficient in any case since
01092  * setcmap is always called later (see plD_bop_png) to update the driver
01093  * color palette to be consistent with cmap0. */
01094          {
01095           PLINT level_save;
01096           level_save = pls->level;
01097           pls->level = 0;
01098           pl_set_extended_cmap0(pls, FT->ncol0_width, FT->ncol0_org); /* call the function to add the extra cmap0 entries and calculate stuff */
01099           pls->level = level_save;
01100          }
01101         FT->smooth_text=1;      /* Yippee ! We had success setting up the extended cmap0 */
01102       }
01103     else
01104       plwarn("Insufficient colour slots available in CMAP0 to do text smoothing.");
01105    }
01106 }
01107 
01108 #endif
01109 
01110 
01111 #ifdef PLD_jpeg
01112 
01113 /*----------------------------------------------------------------------*\
01114  * plD_eop_jpeg()
01115  *
01116  * End of page.
01117 \*----------------------------------------------------------------------*/
01118 
01119 void plD_eop_jpeg(PLStream *pls)
01120 {
01121 png_Dev *dev=(png_Dev *)pls->dev;
01122 int i;
01123 
01124     if (pls->family || pls->page == 1) {
01125       gdImageJpeg(dev->im_out, pls->OutFile, pls->dev_compression);
01126 
01127       gdImageDestroy(dev->im_out);
01128     }
01129 }
01130 
01131 #endif
01132 
01133 /*#endif*/
01134 
01135 
01136 #else
01137 int
01138 pldummy_png()
01139 {
01140     return 0;
01141 }
01142 
01143 #endif                      /* PNG */