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