plplot-5.13.0/drivers/000755 001752 001752 00000000000 13150160227 016317 5ustar00softwaresoftware000000 000000 plplot-5.13.0/drivers/aqt.c000644 001752 001752 00000065723 13150160115 017261 0ustar00softwaresoftware000000 000000 // March 12, 2005 // // PLplot driver for AquaTerm and Mac OS X. // // Copyright (C) Per Persson // Copyright (C) 2005 Hazen Babcock // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // //--------------------------------------------- // Header files, defines and local variables // --------------------------------------------- // OS X specific header files #import #import // PLplot header files #include "plplotP.h" #include "drivers.h" // constants #define SCALE 0.1 #define AQT_Default_X 720 #define AQT_Default_Y 540 #define DPI 72.0 #define MAX_STRING_LEN 1000 // local variables static NSAutoreleasePool *arpool; // Objective-C autorelease pool static id adapter; // Adapter object PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_aqt = "aqt:AquaTerm (Mac OS X):1:aqt:50:aqt\n"; static int currentPlot = 0; static int maxWindows = 30; static int windowXSize = 0; static int windowYSize = 0; static bool didTests = false; static bool hasShear = false; static bool hasAlpha = false; // font stuff // // AquaTerm font look-up table // // The table is initialized with lowest common denominator truetype // fonts that (I hope) most Macs will have. // #define AQT_N_FontLookup 30 static FCI_to_FontName_Table AQT_FontLookup[AQT_N_FontLookup] = { { PL_FCI_MARK | 0x000, (unsigned char *) "Helvetica" }, { PL_FCI_MARK | 0x001, (unsigned char *) "Times-Roman" }, { PL_FCI_MARK | 0x002, (unsigned char *) "Courier" }, { PL_FCI_MARK | 0x003, (unsigned char *) "Times-Roman" }, { PL_FCI_MARK | 0x004, (unsigned char *) "LucidaGrande Regular" }, { PL_FCI_MARK | 0x010, (unsigned char *) "Helvetica-Oblique" }, { PL_FCI_MARK | 0x011, (unsigned char *) "Times-Italic" }, { PL_FCI_MARK | 0x012, (unsigned char *) "Courier-Oblique" }, { PL_FCI_MARK | 0x013, (unsigned char *) "Times-Italic" }, { PL_FCI_MARK | 0x014, (unsigned char *) "LucidaGrande Regular" }, { PL_FCI_MARK | 0x020, (unsigned char *) "Helvetica-Oblique" }, { PL_FCI_MARK | 0x021, (unsigned char *) "Times-Italic" }, { PL_FCI_MARK | 0x022, (unsigned char *) "Courier-Oblique" }, { PL_FCI_MARK | 0x023, (unsigned char *) "Times-Italic" }, { PL_FCI_MARK | 0x024, (unsigned char *) "LucidaGrande Regular" }, { PL_FCI_MARK | 0x100, (unsigned char *) "Helvetica-Bold" }, { PL_FCI_MARK | 0x101, (unsigned char *) "Times-Bold" }, { PL_FCI_MARK | 0x102, (unsigned char *) "Courier-Bold" }, { PL_FCI_MARK | 0x103, (unsigned char *) "Times-Bold" }, { PL_FCI_MARK | 0x104, (unsigned char *) "LucidaGrande Regular" }, { PL_FCI_MARK | 0x110, (unsigned char *) "Helvetica-BoldOblique" }, { PL_FCI_MARK | 0x111, (unsigned char *) "Times-BoldItalic" }, { PL_FCI_MARK | 0x112, (unsigned char *) "Courier-BoldOblique" }, { PL_FCI_MARK | 0x113, (unsigned char *) "Times-BoldItalic" }, { PL_FCI_MARK | 0x114, (unsigned char *) "LucidaGrande Regular" }, { PL_FCI_MARK | 0x120, (unsigned char *) "Helvetica-BoldOblique" }, { PL_FCI_MARK | 0x121, (unsigned char *) "Times-BoldItalic" }, { PL_FCI_MARK | 0x122, (unsigned char *) "Courier-BoldOblique" }, { PL_FCI_MARK | 0x123, (unsigned char *) "Times-BoldItalic" }, { PL_FCI_MARK | 0x124, (unsigned char *) "LucidaGrande Regular" } }; // // AquaTerm font environment variables // // When the driver is initialized it will check to see if // the user has opted to overide one of the above fonts by // setting one of the environment variables below. // // This list must be in the same order with the same number of // elements as the above list // // These are the same environment variable names as would be used // on a linux system, but they have a slightly different meaning. // Since AquaTerm will find the font for us (if it can) given // just the font name, you should only set the environment // variable to the font name. You don't need to provide // a path. If you installed the font using Font Book, AquaTerm // should not have any trouble finding it. // // FIXME: Would it be better to use different environment variable // names then plfreetype.c? If not, then it probably isn't // ideal to have two different copies of the same list of // environment variable names. // const char *aqt_font_env_names[AQT_N_FontLookup] = { "PLPLOT_FREETYPE_SANS_FONT", "PLPLOT_FREETYPE_SERIF_FONT", "PLPLOT_FREETYPE_MONO_FONT", "PLPLOT_FREETYPE_SCRIPT_FONT", "PLPLOT_FREETYPE_SYMBOL_FONT", "PLPLOT_FREETYPE_SANS_ITALIC_FONT", "PLPLOT_FREETYPE_SERIF_ITALIC_FONT", "PLPLOT_FREETYPE_MONO_ITALIC_FONT", "PLPLOT_FREETYPE_SCRIPT_ITALIC_FONT", "PLPLOT_FREETYPE_SYMBOL_ITALIC_FONT", "PLPLOT_FREETYPE_SANS_OBLIQUE_FONT", "PLPLOT_FREETYPE_SERIF_OBLIQUE_FONT", "PLPLOT_FREETYPE_MONO_OBLIQUE_FONT", "PLPLOT_FREETYPE_SCRIPT_OBLIQUE_FONT", "PLPLOT_FREETYPE_SYMBOL_OBLIQUE_FONT", "PLPLOT_FREETYPE_SANS_BOLD_FONT", "PLPLOT_FREETYPE_SERIF_BOLD_FONT", "PLPLOT_FREETYPE_MONO_BOLD_FONT", "PLPLOT_FREETYPE_SCRIPT_BOLD_FONT", "PLPLOT_FREETYPE_SYMBOL_BOLD_FONT", "PLPLOT_FREETYPE_SANS_BOLD_ITALIC_FONT", "PLPLOT_FREETYPE_SERIF_BOLD_ITALIC_FONT", "PLPLOT_FREETYPE_MONO_BOLD_ITALIC_FONT", "PLPLOT_FREETYPE_SCRIPT_BOLD_ITALIC_FONT", "PLPLOT_FREETYPE_SYMBOL_BOLD_ITALIC_FONT", "PLPLOT_FREETYPE_SANS_BOLD_OBLIQUE_FONT", "PLPLOT_FREETYPE_SERIF_BOLD_OBLIQUE_FONT", "PLPLOT_FREETYPE_MONO_BOLD_OBLIQUE_FONT", "PLPLOT_FREETYPE_SCRIPT_BOLD_OBLIQUE_FONT", "PLPLOT_FREETYPE_SYMBOL_BOLD_OBLIQUE_FONT" }; // Debugging extras static inline void NOOP_( id x, ... ) { ; } #ifdef LOGGING #define LOG NSLog #else #define LOG NOOP_ #endif // LOGGING //----------------------------------------------- // function declarations // ----------------------------------------------- // helper functions static void get_cursor( PLStream *, PLGraphicsIn * ); static void proc_str( PLStream *, EscText * ); NSMutableAttributedString * create_string( const PLUNICODE *, int, PLFLT ); static void set_font_and_size( NSMutableAttributedString *, PLUNICODE, PLFLT, int ); static void check_font_environment_variables( void ); // PLplot interface functions void plD_dispatch_init_aqt( PLDispatchTable *pdt ); void plD_init_aqt( PLStream * ); void plD_line_aqt( PLStream *, short, short, short, short ); void plD_polyline_aqt( PLStream *, short *, short *, PLINT ); void plD_eop_aqt( PLStream * ); void plD_bop_aqt( PLStream * ); void plD_tidy_aqt( PLStream * ); void plD_state_aqt( PLStream *, PLINT ); void plD_esc_aqt( PLStream *, PLINT, void * ); //-------------------------------------------------------------------------- // dispatch_init_init() // // Initialize device dispatch table //-------------------------------------------------------------------------- void plD_dispatch_init_aqt( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "AquaTerm - Mac OS X"; pdt->pl_DevName = "aqt"; #endif pdt->pl_type = plDevType_Interactive; pdt->pl_seq = 1; pdt->pl_init = (plD_init_fp) plD_init_aqt; pdt->pl_line = (plD_line_fp) plD_line_aqt; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_aqt; pdt->pl_eop = (plD_eop_fp) plD_eop_aqt; pdt->pl_bop = (plD_bop_fp) plD_bop_aqt; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_aqt; pdt->pl_state = (plD_state_fp) plD_state_aqt; pdt->pl_esc = (plD_esc_fp) plD_esc_aqt; } //-------------------------------------------------------------------------- // aqt_init() // // Initialize device //-------------------------------------------------------------------------- void plD_init_aqt( PLStream *pls ) { if ( arpool == NULL ) // Make sure we don't leak mem by allocating every time { arpool = [[NSAutoreleasePool alloc] init]; adapter = [[AQTAdapter alloc] init]; } [adapter setBackgroundColorRed : 0.5 green : 0.5 blue : 0.5]; pls->termin = 1; // interactive device pls->dev_flush = 1; // Handle our own flushes pls->color = 1; // supports color pls->width = 1; pls->verbose = 1; pls->bytecnt = 0; pls->debug = 1; pls->dev_text = 1; // handles text pls->dev_unicode = 1; // wants text as unicode pls->page = 0; pls->dev_fill0 = 1; // supports hardware solid fills pls->dev_fill1 = 1; pls->graphx = GRAPHICS_MODE; if ( !pls->colorset ) pls->color = 1; // Set up device parameters plP_setpxl( DPI / 25.4 / SCALE, DPI / 25.4 / SCALE ); // Pixels/mm. // Set the bounds for plotting. default is AQT_Default_X x AQT_Default_Y unless otherwise specified. if ( pls->xlength <= 0 || pls->ylength <= 0 ) { windowXSize = AQT_Default_X; windowYSize = AQT_Default_Y; plP_setphy( (PLINT) 0, (PLINT) ( AQT_Default_X / SCALE ), (PLINT) 0, (PLINT) ( AQT_Default_Y / SCALE ) ); } else { windowXSize = pls->xlength; windowYSize = pls->ylength; plP_setphy( (PLINT) 0, (PLINT) ( pls->xlength / SCALE ), (PLINT) 0, (PLINT) ( pls->ylength / SCALE ) ); } // check font environment variables & update font table as necessary check_font_environment_variables(); // Check to see if the users version of aquaterm supports sheared labels. // If it isn't available 3D plots will look a little strange but things should otherwise be okay. if ( !didTests ) { hasShear = [adapter respondsToSelector:@selector( addLabel:atPoint:angle:shearAngle:align: )]; hasAlpha = [adapter respondsToSelector:@selector( setColorRed:green:blue:alpha: )]; didTests = true; } } //-------------------------------------------------------------------------- // aqt_bop() // // Set up for the next page. //-------------------------------------------------------------------------- void plD_bop_aqt( PLStream *pls ) { currentPlot = currentPlot >= maxWindows ? 0 : currentPlot; [adapter openPlotWithIndex : currentPlot++]; [adapter setPlotSize : NSMakeSize( windowXSize, windowYSize )]; [adapter setLinewidth : 1.0]; if ( hasAlpha ) { [adapter setColorRed : (float) ( pls->curcolor.r / 255. ) green : (float) ( pls->curcolor.g / 255. ) blue : (float) ( pls->curcolor.b / 255. ) alpha : (float) ( pls->curcolor.a )]; } else { [adapter setColorRed : (float) ( pls->curcolor.r / 255. ) green : (float) ( pls->curcolor.g / 255. ) blue : (float) ( pls->curcolor.b / 255. )]; } pls->page++; } //-------------------------------------------------------------------------- // aqt_line() // // Draw a line in the current color from (x1,y1) to (x2,y2). //-------------------------------------------------------------------------- void plD_line_aqt( PLStream *pls, short x1a, short y1a, short x2a, short y2a ) { [adapter moveToPoint : NSMakePoint( (float) x1a * SCALE, (float) y1a * SCALE )]; [adapter addLineToPoint : NSMakePoint( (float) x2a * SCALE, (float) y2a * SCALE )]; } //-------------------------------------------------------------------------- // aqt_polyline() // // Draw a polyline in the current color. //-------------------------------------------------------------------------- void plD_polyline_aqt( PLStream *pls, short *xa, short *ya, PLINT npts ) { int i; for ( i = 0; i < npts - 1; i++ ) plD_line_aqt( pls, xa[i], ya[i], xa[i + 1], ya[i + 1] ); } //-------------------------------------------------------------------------- // aqt_eop() // // End of page //-------------------------------------------------------------------------- void plD_eop_aqt( PLStream *pls ) { [arpool release]; // prevents a memory leak by freeing everything in // the auto-release pool when the plot is closed. arpool = [[NSAutoreleasePool alloc] init]; [adapter renderPlot]; } //-------------------------------------------------------------------------- // aqt_tidy() // // Close graphics file or otherwise clean up. //-------------------------------------------------------------------------- void plD_tidy_aqt( PLStream *pls ) { [adapter closePlot]; } //-------------------------------------------------------------------------- // plD_state_aqt() // // Handle change in PLStream state (color, pen width, fill attribute, etc). //-------------------------------------------------------------------------- void plD_state_aqt( PLStream *pls, PLINT op ) { int i; float r, g, b; switch ( op ) { case PLSTATE_WIDTH: [adapter setLinewidth : (float) pls->width]; break; case PLSTATE_COLOR0: // this seems to work, but that isn't to say that it is done right... if ( hasAlpha ) { [adapter setBackgroundColorRed : (float) ( plsc->cmap0[0].r / 255.0 ) green : (float) ( plsc->cmap0[0].g / 255.0 ) blue : (float) ( plsc->cmap0[0].b / 255.0 ) alpha : (float) ( plsc->cmap0[0].a )]; } else { [adapter setBackgroundColorRed : (float) ( plsc->cmap0[0].r / 255.0 ) green : (float) ( plsc->cmap0[0].g / 255.0 ) blue : (float) ( plsc->cmap0[0].b / 255.0 )]; } case PLSTATE_COLOR1: case PLSTATE_FILL: if ( hasAlpha ) { [adapter setColorRed : (float) ( pls->curcolor.r / 255. ) green : (float) ( pls->curcolor.g / 255. ) blue : (float) ( pls->curcolor.b / 255. ) alpha : (float) ( pls->curcolor.a )]; } else { [adapter setColorRed : (float) ( pls->curcolor.r / 255. ) green : (float) ( pls->curcolor.g / 255. ) blue : (float) ( pls->curcolor.b / 255. )]; } break; case PLSTATE_CMAP0: break; case PLSTATE_CMAP1: break; } } //-------------------------------------------------------------------------- // aqt_esc() // // Escape function. // // Functions: // // PLESC_EH Handle pending events // PLESC_EXPOSE Force an expose // PLESC_FILL Fill polygon // PLESC_FLUSH Flush X event buffer // PLESC_GETC Get coordinates upon mouse click // PLESC_REDRAW Force a redraw // PLESC_RESIZE Force a resize //-------------------------------------------------------------------------- void plD_esc_aqt( PLStream *pls, PLINT op, void *ptr ) { int i; switch ( op ) { case PLESC_EXPOSE: // handle window expose break; case PLESC_RESIZE: // handle window resize break; case PLESC_REDRAW: // handle window redraw break; case PLESC_TEXT: // switch to text screen break; case PLESC_GRAPH: // switch to graphics screen break; case PLESC_FILL: // fill polygon [adapter moveToVertexPoint : NSMakePoint( pls->dev_x[0] * SCALE, pls->dev_y[0] * SCALE )]; for ( i = 1; i < pls->dev_npts; i++ ) { [adapter addEdgeToVertexPoint : NSMakePoint( pls->dev_x[i] * SCALE, pls->dev_y[i] * SCALE )]; } ; break; case PLESC_DI: // handle DI command break; case PLESC_FLUSH: // flush output [adapter renderPlot]; break; case PLESC_EH: // handle Window events break; case PLESC_GETC: // get cursor position [adapter renderPlot]; // needed to give the user something to click on get_cursor( pls, (PLGraphicsIn *) ptr ); break; case PLESC_SWIN: // set window parameters break; case PLESC_HAS_TEXT: proc_str( pls, (EscText *) ptr ); break; } } //-------------------------------------------------------------------------- // get_cursor() // // returns the location of the next mouse click //-------------------------------------------------------------------------- void get_cursor( PLStream *pls, PLGraphicsIn *gin ) { int scanned, x, y, button; NSString *temp; plGinInit( gin ); temp = [adapter waitNextEvent]; scanned = sscanf([temp cString], "1:{%d, %d}:%d", &x, &y, &button ); if ( scanned == 3 ) // check that we did actually get a reasonable event string { gin->button = button; gin->pX = x; gin->pY = y; gin->dX = (PLFLT) x / ( (PLFLT) ( pls->xlength ) ); gin->dY = (PLFLT) y / ( (PLFLT) ( pls->ylength ) ); } else // just return zeroes if we did not { printf( "AquaTerm did not return a valid mouse location!\n" ); gin->button = 0; gin->pX = 0; gin->pY = 0; gin->dX = 0.0; gin->dY = 0.0; } } //-------------------------------------------------------------------------- // proc_str() // // Processes strings for display. The actual parsing of the unicode // string is handled by the sub-routine create_string. //-------------------------------------------------------------------------- void proc_str( PLStream *pls, EscText *args ) { PLFLT a1, ft_ht, angle, shear, stride; PLINT clxmin, clxmax, clymin, clymax; int i, jst, ref; NSMutableAttributedString *str; // check that we got unicode, warning message and return if not if ( args->unicode_array_len == 0 ) { printf( "Non unicode string passed to AquaTerm driver, ignoring\n" ); return; } // check that unicode string isn't longer then the max we allow if ( args->unicode_array_len >= MAX_STRING_LEN ) { printf( "Sorry, the AquaTerm driver only handles strings of length < %d\n", MAX_STRING_LEN ); return; } // set the font height - the 1.2 factor was trial and error ft_ht = 1.2 * pls->chrht * DPI / 25.4; // ft_ht in points. ht is in mm // given transform, calculate rotation angle & shear angle plRotationShear( args->xform, &angle, &shear, &stride ); angle *= 180.0 / PI; shear *= -180.0 / PI; // text justification, AquaTerm only supports 3 options, so we round appropriately if ( args->just < 0.33 ) jst = AQTAlignLeft; // left else if ( args->just > 0.66 ) jst = AQTAlignRight; // right else jst = AQTAlignCenter; // center // set the baseline of the string // Middle and Bottom are set to Middle since this seems to be what PLplot expects // as judged by where it renders the symbols in example 1. if ( args->base == 2 ) // Top ref = AQTAlignTop; else if ( args->base == 1 ) // Bottom ref = AQTAlignMiddle; else ref = AQTAlignMiddle; // Middle // create an appropriately formatted, etc... unicode string str = create_string( args->unicode_array, args->unicode_array_len, ft_ht ); // display the string if ( hasAlpha ) { [adapter setColorRed : (float) ( pls->curcolor.r / 255. ) green : (float) ( pls->curcolor.g / 255. ) blue : (float) ( pls->curcolor.b / 255. ) alpha : (float) ( pls->curcolor.a )]; } else { [adapter setColorRed : (float) ( pls->curcolor.r / 255. ) green : (float) ( pls->curcolor.g / 255. ) blue : (float) ( pls->curcolor.b / 255. )]; } if ( hasShear ) { [adapter addLabel : str atPoint : NSMakePoint( (float) args->x * SCALE, (float) args->y * SCALE ) angle : angle shearAngle : shear align : ( jst | ref )]; } else { [adapter addLabel : str atPoint : NSMakePoint( (float) args->x * SCALE, (float) args->y * SCALE ) angle : angle align : ( jst | ref )]; } [str release]; } //-------------------------------------------------------------------------- // create_string() // // create a NSMutableAttributedString from the plplot ucs4 string // // assumptions : // 1. font changes are unicode >= PL_FCI_MARK // 2. we'll never have to deal with a string longer then MAX_STRING_LEN characters // 3. means we desired as a character & not actually as // 4. there are no two character sequences... i.e. fn is now covered by fci // //-------------------------------------------------------------------------- NSMutableAttributedString * create_string( const PLUNICODE *ucs4, int ucs4_len, PLFLT font_height ) { PLUNICODE fci; char plplot_esc; int i; int cur_loc; int utf8_len; int updown; char dummy[MAX_STRING_LEN + 1]; char *font; char utf8[5]; NSMutableAttributedString *str; updown = 0; // initialize the attributed string for ( i = 0; i < MAX_STRING_LEN; i++ ) dummy[i] = 'i'; dummy[MAX_STRING_LEN] = '\0'; str = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithCString:dummy]]; // get plplot escape character & current font plgesc( &plplot_esc ); plgfci( &fci ); // set the font for the string based on the current font & size set_font_and_size( str, fci, font_height, 0 ); // parse plplot ucs4 string cur_loc = 0; i = 0; while ( i < ucs4_len ) { if ( ucs4[i] < PL_FCI_MARK ) // not a font change { if ( ucs4[i] != (PLUNICODE) plplot_esc ) // a character to display { ucs4_to_utf8( ucs4[i], utf8 ); [str replaceCharactersInRange : NSMakeRange( cur_loc, 1 ) withString :[NSString stringWithUTF8String : utf8]]; i++; cur_loc++; continue; } i++; if ( ucs4[i] == (PLUNICODE) plplot_esc ) { ucs4_to_utf8( ucs4[i], utf8 ); [str replaceCharactersInRange : NSMakeRange( cur_loc, 1 ) withString :[NSString stringWithUTF8String : utf8]]; i++; cur_loc++; continue; } else { if ( ucs4[i] == (PLUNICODE) 'f' ) // font change { i++; printf( "hmm, unicode string apparently not following fci convention...\n" ); } if ( ucs4[i] == (PLUNICODE) 'd' ) // Subscript { updown--; [str addAttribute : @ "NSSuperScript" value :[NSNumber numberWithInt : updown] range : NSMakeRange( cur_loc, ( MAX_STRING_LEN - cur_loc ) )]; } if ( ucs4[i] == (PLUNICODE) 'u' ) // Superscript { updown++; [str addAttribute : @ "NSSuperScript" value :[NSNumber numberWithInt : updown] range : NSMakeRange( cur_loc, ( MAX_STRING_LEN - cur_loc ) )]; } i++; } } else // a font change { set_font_and_size( str, ucs4[i], font_height, cur_loc ); i++; } } // trim string to appropriate final length [str deleteCharactersInRange : NSMakeRange( cur_loc, ( MAX_STRING_LEN - cur_loc ) )]; return str; } //-------------------------------------------------------------------------- // set_font_and_size // // set the font & size of a attributable string object //-------------------------------------------------------------------------- void set_font_and_size( NSMutableAttributedString * str, PLUNICODE fci, PLFLT font_height, int cur_loc ) { char *font; font = plP_FCI2FontName( fci, AQT_FontLookup, AQT_N_FontLookup ); // check whether that font exists & if not, use standard font instead if ( font == NULL ) { printf( "AquaTerm : Warning, could not find font given by fci = 0x%x\n", fci ); font = "Helvetica"; } /* font = "FreeSerif"; *//* force the font for debugging purposes */ // printf("Font at %d is : %s\n", cur_loc, font); [str addAttribute : @ "AQTFontname" value :[NSString stringWithCString : font] range : NSMakeRange( cur_loc, ( MAX_STRING_LEN - cur_loc ) )]; [str addAttribute : @ "AQTFontsize" value :[NSNumber numberWithFloat : font_height] range : NSMakeRange( cur_loc, ( MAX_STRING_LEN - cur_loc ) )]; } //-------------------------------------------------------------------------- // check_font_environment_variables // // Checks to see if any font environment variables are defined. // If a font environment variable is defined, then the appropriate // element of the default font table is replaced with the font name // string specified by the environment variable. //-------------------------------------------------------------------------- void check_font_environment_variables( void ) { int i; char *new_font; char *begin; char *end; for ( i = 0; i < AQT_N_FontLookup; i++ ) { if ( ( new_font = getenv( aqt_font_env_names[i] ) ) != NULL ) { // If the user is just blindly following the suggestions in // the plplot examples then we might get a font name with // a path and extension. We need to remove that since it // isn't relevant and will only cause trouble. We warn them // AquaTerm was not expecting a path or extension. begin = strrchr( new_font, '/' ); end = strrchr( new_font, '.' ); if ( end != NULL ) { printf( "Aquaterm : Warning, removing extension from font name : %s\n", new_font ); *end = '\0'; } if ( begin != NULL ) { printf( "AquaTerm : Warning, removing path from font name : %s\n", new_font ); new_font = begin + 1; } // printf("new font : %s\n", new_font); AQT_FontLookup[i].pfont = (unsigned char *) new_font; } } } plplot-5.13.0/drivers/tk.c000644 001752 001752 00000201056 13150160115 017101 0ustar00softwaresoftware000000 000000 // PLplot Tcl/Tk and Tcl-DP device drivers. // Should be broken up somewhat better to allow use of DP w/o X. // // Maurice LeBrun // 30-Apr-93 // // Copyright (C) 2004 Maurice LeBrun // Copyright (C) 2004 Joao Cardoso // Copyright (C) 2004 Andrew Ross // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // // #define DEBUG_ENTER // #define DEBUG #include "plDevs.h" #ifdef PLD_tk #define NEED_PLDEBUG #include "pltkd.h" #include "pltcl.h" #include "tcpip.h" #include "drivers.h" #include "metadefs.h" #include "plevent.h" #include #if PL_HAVE_UNISTD_H # include #endif #include #if HAVE_SYS_WAIT_H # include #endif #include #include #include #include #ifdef PLD_dp # include #endif // Device info PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_tk = "tk:Tcl/TK Window:1:tk:7:tk\n"; // Number of instructions to skip between updates #define MAX_INSTR 100 // Pixels/mm #define PHYSICAL 0 // Enables physical scaling.. // These need to be distinguished since the handling is slightly different. #define LOCATE_INVOKED_VIA_API 1 #define LOCATE_INVOKED_VIA_DRIVER 2 #define STR_LEN 10 #define CMD_LEN 100 // A handy command wrapper #define tk_wr( code ) \ if ( code ) { abort_session( pls, "Unable to write to PDFstrm" ); } //-------------------------------------------------------------------------- // Function prototypes // Driver entry and dispatch setup void plD_dispatch_init_tk( PLDispatchTable *pdt ); void plD_init_tk( PLStream * ); void plD_line_tk( PLStream *, short, short, short, short ); void plD_polyline_tk( PLStream *, short *, short *, PLINT ); void plD_eop_tk( PLStream * ); void plD_bop_tk( PLStream * ); void plD_tidy_tk( PLStream * ); void plD_state_tk( PLStream *, PLINT ); void plD_esc_tk( PLStream *, PLINT, void * ); void plD_init_dp( PLStream *pls ); // various static void init( PLStream *pls ); static void tk_start( PLStream *pls ); static void tk_stop( PLStream *pls ); static void tk_di( PLStream *pls ); static void tk_fill( PLStream *pls ); static void WaitForPage( PLStream *pls ); static void CheckForEvents( PLStream *pls ); static void HandleEvents( PLStream *pls ); static void init_server( PLStream *pls ); static void launch_server( PLStream *pls ); static void flush_output( PLStream *pls ); static void plwindow_init( PLStream *pls ); static void link_init( PLStream *pls ); static void GetCursor( PLStream *pls, PLGraphicsIn *ptr ); static void tk_XorMod( PLStream *pls, PLINT *ptr ); static void set_windowname( PLStream *pls ); // performs Tk-driver-specific initialization static int pltkdriver_Init( PLStream *pls ); // Tcl/TK utility commands static void tk_wait( PLStream *pls, const char * ); static void abort_session( PLStream *pls, const char * ); static void server_cmd( PLStream *pls, const char *, int ); static void tcl_cmd( PLStream *pls, const char * ); static void copybuf( PLStream *pls, const char *cmd ); static int pltk_toplevel( Tk_Window *w, Tcl_Interp *interp ); static void ProcessKey( PLStream *pls ); static void ProcessButton( PLStream *pls ); static void LocateKey( PLStream *pls ); static void LocateButton( PLStream *pls ); static void Locate( PLStream *pls ); // These are internal TCL commands static int Abort( ClientData, Tcl_Interp *, int, char ** ); static int Plfinfo( ClientData, Tcl_Interp *, int, char ** ); static int KeyEH( ClientData, Tcl_Interp *, int, char ** ); static int ButtonEH( ClientData, Tcl_Interp *, int, char ** ); static int LookupTkKeyEvent( PLStream *pls, Tcl_Interp *interp, int argc, char **argv ); static int LookupTkButtonEvent( PLStream *pls, Tcl_Interp *interp, int argc, char **argv ); static char *drvoptcmd = NULL; // tcl command from command line option parsing static DrvOpt tk_options[] = { { "tcl_cmd", DRV_STR, &drvoptcmd, "Execute tcl command" }, { NULL, DRV_INT, NULL, NULL } }; void plD_dispatch_init_tk( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "Tcl/TK Window"; pdt->pl_DevName = "tk"; #endif pdt->pl_type = plDevType_Interactive; pdt->pl_seq = 7; pdt->pl_init = (plD_init_fp) plD_init_tk; pdt->pl_line = (plD_line_fp) plD_line_tk; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_tk; pdt->pl_eop = (plD_eop_fp) plD_eop_tk; pdt->pl_bop = (plD_bop_fp) plD_bop_tk; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_tk; pdt->pl_state = (plD_state_fp) plD_state_tk; pdt->pl_esc = (plD_esc_fp) plD_esc_tk; } //-------------------------------------------------------------------------- // plD_init_dp() // plD_init_tk() // init_tk() // // Initialize device. // TK-dependent stuff done in tk_start(). You can set the display by // calling plsfnam() with the display name as the (string) argument. //-------------------------------------------------------------------------- void plD_init_tk( PLStream *pls ) { pls->dp = 0; plParseDrvOpts( tk_options ); init( pls ); } void plD_init_dp( PLStream *pls ) { #ifdef PLD_dp pls->dp = 1; #else fprintf( stderr, "The Tcl-DP driver hasn't been installed!\n" ); pls->dp = 0; #endif init( pls ); } static void tk_wr_header( PLStream *pls, const char *header ) { tk_wr( pdf_wr_header( pls->pdfs, header ) ); } static void init( PLStream *pls ) { U_CHAR c = (U_CHAR) INITIALIZE; TkDev *dev; PLFLT pxlx, pxly; int xmin = 0; int xmax = PIXELS_X - 1; int ymin = 0; int ymax = PIXELS_Y - 1; dbug_enter( "plD_init_tk" ); pls->color = 1; // Is a color device pls->termin = 1; // Is an interactive terminal pls->dev_di = 1; // Handle driver interface commands pls->dev_flush = 1; // Handle our own flushes pls->dev_fill0 = 1; // Handle solid fills pls->dev_fill1 = 1; // Driver handles pattern fills pls->server_nokill = 1; // don't kill if ^C pls->dev_xor = 1; // device support xor mode // Activate plot buffer. To programmatically save a file we can't call // plreplot(), but instead one must send a command to plserver. As there is // no API call for this, the user must use the plserver "save/print" menu // entries. Activating the plot buffer enables the normal // plmkstrm/plcpstrm/plreplot/plend1 way of saving plots. // pls->plbuf_write = 1; // Specify buffer size if not yet set (can be changed by -bufmax option). // A small buffer works best for socket communication if ( pls->bufmax == 0 ) { if ( pls->dp ) pls->bufmax = 450; else pls->bufmax = 3500; } // Allocate and initialize device-specific data if ( pls->dev != NULL ) free( (void *) pls->dev ); pls->dev = calloc( 1, (size_t) sizeof ( TkDev ) ); if ( pls->dev == NULL ) plexit( "plD_init_tk: Out of memory." ); dev = (TkDev *) pls->dev; dev->iodev = (PLiodev *) calloc( 1, (size_t) sizeof ( PLiodev ) ); if ( dev->iodev == NULL ) plexit( "plD_init_tk: Out of memory." ); dev->exit_eventloop = FALSE; // Variables used in querying plserver for events dev->instr = 0; dev->max_instr = MAX_INSTR; // Start interpreter and spawn server process tk_start( pls ); // Get ready for plotting dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; #if PHYSICAL pxlx = (double) PIXELS_X / dev->width * DPMM; pxly = (double) PIXELS_Y / dev->height * DPMM; #else pxlx = (double) PIXELS_X / LPAGE_X; pxly = (double) PIXELS_Y / LPAGE_Y; #endif plP_setpxl( pxlx, pxly ); plP_setphy( xmin, xmax, ymin, ymax ); // Send init info tk_wr( pdf_wr_1byte( pls->pdfs, c ) ); // The header and version fields are useful when the client & server // reside on different machines tk_wr_header( pls, PLSERV_HEADER ); tk_wr_header( pls, PLSERV_VERSION ); tk_wr_header( pls, "xmin" ); tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) xmin ) ); tk_wr_header( pls, "xmax" ); tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) xmax ) ); tk_wr_header( pls, "ymin" ); tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) ymin ) ); tk_wr_header( pls, "ymax" ); tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) ymax ) ); tk_wr_header( pls, "" ); // Write color map state info plD_state_tk( pls, PLSTATE_CMAP0 ); plD_state_tk( pls, PLSTATE_CMAP1 ); // Good place to make sure the data transfer is working OK flush_output( pls ); } //-------------------------------------------------------------------------- // plD_line_tk() // // Draw a line in the current color from (x1,y1) to (x2,y2). //-------------------------------------------------------------------------- void plD_line_tk( PLStream *pls, short x1, short y1, short x2, short y2 ) { U_CHAR c; U_SHORT xy[4]; TkDev *dev = (TkDev *) pls->dev; CheckForEvents( pls ); if ( x1 == dev->xold && y1 == dev->yold ) { c = (U_CHAR) LINETO; tk_wr( pdf_wr_1byte( pls->pdfs, c ) ); xy[0] = (U_SHORT) x2; xy[1] = (U_SHORT) y2; tk_wr( pdf_wr_2nbytes( pls->pdfs, xy, 2 ) ); } else { c = (U_CHAR) LINE; tk_wr( pdf_wr_1byte( pls->pdfs, c ) ); xy[0] = (U_SHORT) x1; xy[1] = (U_SHORT) y1; xy[2] = (U_SHORT) x2; xy[3] = (U_SHORT) y2; tk_wr( pdf_wr_2nbytes( pls->pdfs, xy, 4 ) ); } dev->xold = x2; dev->yold = y2; if ( pls->pdfs->bp > (size_t) pls->bufmax ) flush_output( pls ); } //-------------------------------------------------------------------------- // plD_polyline_tk() // // Draw a polyline in the current color from (x1,y1) to (x2,y2). //-------------------------------------------------------------------------- void plD_polyline_tk( PLStream *pls, short *xa, short *ya, PLINT npts ) { U_CHAR c = (U_CHAR) POLYLINE; TkDev *dev = (TkDev *) pls->dev; CheckForEvents( pls ); tk_wr( pdf_wr_1byte( pls->pdfs, c ) ); tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) npts ) ); tk_wr( pdf_wr_2nbytes( pls->pdfs, (U_SHORT *) xa, npts ) ); tk_wr( pdf_wr_2nbytes( pls->pdfs, (U_SHORT *) ya, npts ) ); dev->xold = xa[npts - 1]; dev->yold = ya[npts - 1]; if ( pls->pdfs->bp > (size_t) pls->bufmax ) flush_output( pls ); } //-------------------------------------------------------------------------- // plD_eop_tk() // // End of page. // User must hit to continue. //-------------------------------------------------------------------------- void plD_eop_tk( PLStream *pls ) { U_CHAR c = (U_CHAR) EOP; dbug_enter( "plD_eop_tk" ); tk_wr( pdf_wr_1byte( pls->pdfs, c ) ); flush_output( pls ); if ( !pls->nopause ) WaitForPage( pls ); } //-------------------------------------------------------------------------- // plD_bop_tk() // // Set up for the next page. //-------------------------------------------------------------------------- void plD_bop_tk( PLStream *pls ) { U_CHAR c = (U_CHAR) BOP; TkDev *dev = (TkDev *) pls->dev; dbug_enter( "plD_bop_tk" ); dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; pls->page++; tk_wr( pdf_wr_1byte( pls->pdfs, c ) ); } //-------------------------------------------------------------------------- // plD_tidy_tk() // // Close graphics file //-------------------------------------------------------------------------- void plD_tidy_tk( PLStream *pls ) { TkDev *dev = (TkDev *) pls->dev; dbug_enter( "plD_tidy_tk" ); if ( dev != NULL ) tk_stop( pls ); } //-------------------------------------------------------------------------- // plD_state_tk() // // Handle change in PLStream state (color, pen width, fill attribute, etc). //-------------------------------------------------------------------------- void plD_state_tk( PLStream *pls, PLINT op ) { U_CHAR c = (U_CHAR) CHANGE_STATE; int i; dbug_enter( "plD_state_tk" ); tk_wr( pdf_wr_1byte( pls->pdfs, c ) ); tk_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) op ) ); switch ( op ) { case PLSTATE_WIDTH: tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) ( pls->width ) ) ); break; case PLSTATE_COLOR0: tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->icol0 ) ); if ( pls->icol0 == PL_RGB_COLOR ) { tk_wr( pdf_wr_1byte( pls->pdfs, pls->curcolor.r ) ); tk_wr( pdf_wr_1byte( pls->pdfs, pls->curcolor.g ) ); tk_wr( pdf_wr_1byte( pls->pdfs, pls->curcolor.b ) ); } break; case PLSTATE_COLOR1: tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->icol1 ) ); break; case PLSTATE_FILL: tk_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) pls->patt ) ); break; case PLSTATE_CMAP0: tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->ncol0 ) ); for ( i = 0; i < pls->ncol0; i++ ) { tk_wr( pdf_wr_1byte( pls->pdfs, pls->cmap0[i].r ) ); tk_wr( pdf_wr_1byte( pls->pdfs, pls->cmap0[i].g ) ); tk_wr( pdf_wr_1byte( pls->pdfs, pls->cmap0[i].b ) ); } break; case PLSTATE_CMAP1: tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->ncol1 ) ); for ( i = 0; i < pls->ncol1; i++ ) { tk_wr( pdf_wr_1byte( pls->pdfs, pls->cmap1[i].r ) ); tk_wr( pdf_wr_1byte( pls->pdfs, pls->cmap1[i].g ) ); tk_wr( pdf_wr_1byte( pls->pdfs, pls->cmap1[i].b ) ); } // Need to send over the control points too! tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->ncp1 ) ); for ( i = 0; i < pls->ncp1; i++ ) { tk_wr( pdf_wr_ieeef( pls->pdfs, (float) pls->cmap1cp[i].h ) ); tk_wr( pdf_wr_ieeef( pls->pdfs, (float) pls->cmap1cp[i].l ) ); tk_wr( pdf_wr_ieeef( pls->pdfs, (float) pls->cmap1cp[i].s ) ); tk_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) pls->cmap1cp[i].alt_hue_path ) ); } break; } if ( pls->pdfs->bp > (size_t) pls->bufmax ) flush_output( pls ); } //-------------------------------------------------------------------------- // plD_esc_tk() // // Escape function. // Functions: // // PLESC_EXPOSE Force an expose (just passes token) // PLESC_RESIZE Force a resize (just passes token) // PLESC_REDRAW Force a redraw // PLESC_FLUSH Flush X event buffer // PLESC_FILL Fill polygon // PLESC_EH Handle events only // PLESC_XORMOD Xor mode // //-------------------------------------------------------------------------- void plD_esc_tk( PLStream *pls, PLINT op, void *ptr ) { U_CHAR c = (U_CHAR) ESCAPE; dbug_enter( "plD_esc_tk" ); switch ( op ) { case PLESC_DI: tk_wr( pdf_wr_1byte( pls->pdfs, c ) ); tk_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) op ) ); tk_di( pls ); break; case PLESC_EH: tk_wr( pdf_wr_1byte( pls->pdfs, c ) ); tk_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) op ) ); HandleEvents( pls ); break; case PLESC_FLUSH: tk_wr( pdf_wr_1byte( pls->pdfs, c ) ); tk_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) op ) ); flush_output( pls ); break; case PLESC_FILL: tk_wr( pdf_wr_1byte( pls->pdfs, c ) ); tk_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) op ) ); tk_fill( pls ); break; case PLESC_GETC: GetCursor( pls, (PLGraphicsIn *) ptr ); break; case PLESC_XORMOD: tk_XorMod( pls, (PLINT *) ptr ); break; default: tk_wr( pdf_wr_1byte( pls->pdfs, c ) ); tk_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) op ) ); } } //-------------------------------------------------------------------------- // tk_XorMod() // // enter (mod = 1) or leave (mod = 0) xor mode // //-------------------------------------------------------------------------- static void tk_XorMod( PLStream *pls, PLINT *ptr ) { if ( *ptr != 0 ) server_cmd( pls, "$plwidget cmd plxormod 1 st", 1 ); else server_cmd( pls, "$plwidget cmd plxormod 0 st", 1 ); } //-------------------------------------------------------------------------- // GetCursor() // // Waits for a graphics input event and returns coordinates. //-------------------------------------------------------------------------- static void GetCursor( PLStream *pls, PLGraphicsIn *ptr ) { TkDev *dev = (TkDev *) pls->dev; PLGraphicsIn *gin = &( dev->gin ); // Initialize plGinInit( gin ); dev->locate_mode = LOCATE_INVOKED_VIA_API; plD_esc_tk( pls, PLESC_FLUSH, NULL ); server_cmd( pls, "$plwidget configure -xhairs on", 1 ); // Run event loop until a point is selected while ( gin->pX < 0 && dev->locate_mode ) { Tk_DoOneEvent( 0 ); } // Clean up server_cmd( pls, "$plwidget configure -xhairs off", 1 ); *ptr = *gin; } //-------------------------------------------------------------------------- // tk_di // // Process driver interface command. // Just send the command to the remote PLplot library. //-------------------------------------------------------------------------- static void tk_di( PLStream *pls ) { TkDev *dev = (TkDev *) pls->dev; char str[STR_LEN]; dbug_enter( "tk_di" ); // Safety feature, should never happen if ( dev == NULL ) { plabort( "tk_di: Illegal call to driver (not yet initialized)" ); return; } // Flush the buffer before proceeding flush_output( pls ); // Change orientation if ( pls->difilt & PLDI_ORI ) { snprintf( str, STR_LEN, "%f", pls->diorot ); Tcl_SetVar( dev->interp, "rot", str, 0 ); server_cmd( pls, "$plwidget cmd plsetopt -ori $rot", 1 ); pls->difilt &= ~PLDI_ORI; } // Change window into plot space if ( pls->difilt & PLDI_PLT ) { snprintf( str, STR_LEN, "%f", pls->dipxmin ); Tcl_SetVar( dev->interp, "xl", str, 0 ); snprintf( str, STR_LEN, "%f", pls->dipymin ); Tcl_SetVar( dev->interp, "yl", str, 0 ); snprintf( str, STR_LEN, "%f", pls->dipxmax ); Tcl_SetVar( dev->interp, "xr", str, 0 ); snprintf( str, STR_LEN, "%f", pls->dipymax ); Tcl_SetVar( dev->interp, "yr", str, 0 ); server_cmd( pls, "$plwidget cmd plsetopt -wplt $xl,$yl,$xr,$yr", 1 ); pls->difilt &= ~PLDI_PLT; } // Change window into device space if ( pls->difilt & PLDI_DEV ) { snprintf( str, STR_LEN, "%f", pls->mar ); Tcl_SetVar( dev->interp, "mar", str, 0 ); snprintf( str, STR_LEN, "%f", pls->aspect ); Tcl_SetVar( dev->interp, "aspect", str, 0 ); snprintf( str, STR_LEN, "%f", pls->jx ); Tcl_SetVar( dev->interp, "jx", str, 0 ); snprintf( str, STR_LEN, "%f", pls->jy ); Tcl_SetVar( dev->interp, "jy", str, 0 ); server_cmd( pls, "$plwidget cmd plsetopt -mar $mar", 1 ); server_cmd( pls, "$plwidget cmd plsetopt -a $aspect", 1 ); server_cmd( pls, "$plwidget cmd plsetopt -jx $jx", 1 ); server_cmd( pls, "$plwidget cmd plsetopt -jy $jy", 1 ); pls->difilt &= ~PLDI_DEV; } // Update view server_cmd( pls, "update", 1 ); server_cmd( pls, "plw::update_view $plwindow", 1 ); } //-------------------------------------------------------------------------- // tk_fill() // // Fill polygon described in points pls->dev_x[] and pls->dev_y[]. //-------------------------------------------------------------------------- static void tk_fill( PLStream *pls ) { PLDev *dev = (PLDev *) pls->dev; dbug_enter( "tk_fill" ); tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->dev_npts ) ); tk_wr( pdf_wr_2nbytes( pls->pdfs, (U_SHORT *) pls->dev_x, pls->dev_npts ) ); tk_wr( pdf_wr_2nbytes( pls->pdfs, (U_SHORT *) pls->dev_y, pls->dev_npts ) ); dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; } //-------------------------------------------------------------------------- // tk_start // // Create TCL interpreter and spawn off server process. // Each stream that uses the tk driver gets its own interpreter. //-------------------------------------------------------------------------- static void tk_start( PLStream *pls ) { TkDev *dev = (TkDev *) pls->dev; dbug_enter( "tk_start" ); // Instantiate a TCL interpreter, and get rid of the exec command dev->interp = Tcl_CreateInterp(); if ( Tcl_Init( dev->interp ) != TCL_OK ) { fprintf( stderr, "%s\n", Tcl_GetStringResult( dev->interp ) ); abort_session( pls, "Unable to initialize Tcl" ); } tcl_cmd( pls, "rename exec {}" ); // Set top level window name & initialize set_windowname( pls ); if ( pls->dp ) { Tcl_SetVar( dev->interp, "dp", "1", TCL_GLOBAL_ONLY ); dev->updatecmd = "dp_update"; } else { Tcl_SetVar( dev->interp, "dp", "0", TCL_GLOBAL_ONLY ); // tk_init needs this. Use pls->FileName first, then DISPLAY, then :0.0 if ( pls->FileName != NULL ) Tcl_SetVar2( dev->interp, "env", "DISPLAY", pls->FileName, TCL_GLOBAL_ONLY ); else if ( getenv( "DISPLAY" ) != NULL ) Tcl_SetVar2( dev->interp, "env", "DISPLAY", getenv( "DISPLAY" ), TCL_GLOBAL_ONLY ); // tk_init need this else Tcl_SetVar2( dev->interp, "env", "DISPLAY", "unix:0.0", TCL_GLOBAL_ONLY ); // tk_init need this dev->updatecmd = "update"; if ( pltk_toplevel( &dev->w, dev->interp ) ) abort_session( pls, "Unable to create top-level window" ); } // Eval startup procs if ( pltkdriver_Init( pls ) != TCL_OK ) { abort_session( pls, "" ); } if ( pls->debug ) tcl_cmd( pls, "global auto_path; puts \"auto_path: $auto_path\"" ); // Other initializations. // Autoloaded, so the user can customize it if desired tcl_cmd( pls, "plclient_init" ); // A different way to customize the interface. // E.g. used by plrender to add a back page button. if ( drvoptcmd ) tcl_cmd( pls, drvoptcmd ); // Initialize server process init_server( pls ); // By now we should be done with all autoloaded procs, so blow away // the open command just in case security has been compromised tcl_cmd( pls, "rename open {}" ); tcl_cmd( pls, "rename rename {}" ); // Initialize widgets plwindow_init( pls ); // Initialize data link link_init( pls ); return; } //-------------------------------------------------------------------------- // tk_stop // // Normal termination & cleanup. //-------------------------------------------------------------------------- static void tk_stop( PLStream *pls ) { TkDev *dev = (TkDev *) pls->dev; dbug_enter( "tk_stop" ); // Safety check for out of control code if ( dev->pass_thru ) return; dev->pass_thru = 1; // Kill plserver tcl_cmd( pls, "plclient_link_end" ); // Wait for child process to complete if ( dev->child_pid ) { waitpid( dev->child_pid, NULL, 0 ); // // problems if parent has not caught/ignore SIGCHLD. Returns -1 and errno=EINTR // if (waitpid(dev->child_pid, NULL, 0) != dev->child_pid) // fprintf(stderr, "tk_stop: waidpid error"); // } // Blow away interpreter Tcl_DeleteInterp( dev->interp ); dev->interp = NULL; // Free up memory and other miscellanea pdf_close( pls->pdfs ); if ( dev->iodev != NULL ) { if ( dev->iodev->file != NULL ) plCloseFile( pls ); free( (void *) dev->iodev ); } free_mem( dev->cmdbuf ); } //-------------------------------------------------------------------------- // abort_session // // Terminates with an error. // Cleanup is done here, and once pls->level is cleared the driver will // never be called again. //-------------------------------------------------------------------------- static void abort_session( PLStream *pls, const char *msg ) { TkDev *dev = (TkDev *) pls->dev; dbug_enter( "abort_session" ); // Safety check for out of control code if ( dev->pass_thru ) return; tk_stop( pls ); pls->level = 0; plexit( msg ); } //-------------------------------------------------------------------------- // pltkdriver_Init // // Performs PLplot/TK driver-specific Tcl initialization. //-------------------------------------------------------------------------- static int pltkdriver_Init( PLStream *pls ) { TkDev *dev = (TkDev *) pls->dev; Tcl_Interp *interp = (Tcl_Interp *) dev->interp; // // Call the init procedures for included packages. Each call should // look like this: // // if (Mod_Init(interp) == TCL_ERROR) { // return TCL_ERROR; // } // // where "Mod" is the name of the module. // if ( Tcl_Init( interp ) == TCL_ERROR ) { return TCL_ERROR; } #ifdef PLD_dp if ( pls->dp ) { if ( Tdp_Init( interp ) == TCL_ERROR ) { return TCL_ERROR; } } #endif // // Call Tcl_CreateCommand for application-specific commands, if // they weren't already created by the init procedures called above. // Tcl_CreateCommand( interp, "wait_until", (Tcl_CmdProc *) plWait_Until, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL ); #ifdef PLD_dp if ( pls->dp ) { Tcl_CreateCommand( interp, "host_id", (Tcl_CmdProc *) plHost_ID, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL ); } #endif Tcl_CreateCommand( interp, "abort", (Tcl_CmdProc *) Abort, (ClientData) pls, (Tcl_CmdDeleteProc *) NULL ); Tcl_CreateCommand( interp, "plfinfo", (Tcl_CmdProc *) Plfinfo, (ClientData) pls, (Tcl_CmdDeleteProc *) NULL ); Tcl_CreateCommand( interp, "keypress", (Tcl_CmdProc *) KeyEH, (ClientData) pls, (Tcl_CmdDeleteProc *) NULL ); Tcl_CreateCommand( interp, "buttonpress", (Tcl_CmdProc *) ButtonEH, (ClientData) pls, (Tcl_CmdDeleteProc *) NULL ); // Set some relevant interpreter variables if ( !pls->dp ) tcl_cmd( pls, "set client_name [winfo name .]" ); if ( pls->server_name != NULL ) Tcl_SetVar( interp, "server_name", pls->server_name, 0 ); if ( pls->server_host != NULL ) Tcl_SetVar( interp, "server_host", pls->server_host, 0 ); if ( pls->server_port != NULL ) Tcl_SetVar( interp, "server_port", pls->server_port, 0 ); // Set up auto_path if ( pls_auto_path( interp ) == TCL_ERROR ) return TCL_ERROR; return TCL_OK; } //-------------------------------------------------------------------------- // init_server // // Starts interaction with server process, launching it if necessary. // // There are several possibilities we must account for, depending on the // message protocol, input flags, and whether plserver is already running // or not. From the point of view of the code, they are: // // 1. Driver: tk // Flags: // Meaning: need to start up plserver (same host) // Actions: fork plserver, passing it our TK main window name // for communication. Once started, plserver will send // back its main window name. // // 2. Driver: dp // Flags: // Meaning: need to start up plserver (same host) // Actions: fork plserver, passing it our Tcl-DP communication port // for communication. Once started, plserver will send // back its created message port number. // // 3. Driver: tk // Flags: -server_name // Meaning: plserver already running (same host) // Actions: communicate to plserver our TK main window name. // // 4. Driver: dp // Flags: -server_port // Meaning: plserver already running (same host) // Actions: communicate to plserver our Tcl-DP port number. // // 5. Driver: dp // Flags: -server_host // Meaning: need to start up plserver (remote host) // Actions: rsh (remsh) plserver, passing it our host ID and Tcl-DP // port for communication. Once started, plserver will send // back its created message port number. // // 6. Driver: dp // Flags: -server_host -server_port // Meaning: plserver already running (remote host) // Actions: communicate to remote plserver our host ID and Tcl-DP // port number. // // For a bit more flexibility, you can change the name of the process // invoked from "plserver" to something else, using the -plserver flag. // // The startup procedure involves some rather involved handshaking between // client and server. This is made easier by using the Tcl variables: // // client_host client_port server_host server_port // // when using Tcl-DP sends and // // client_name server_name // // when using TK sends. The global Tcl variables // // client server // // are used as the defining identification for the client and server // respectively -- they denote the main window name when TK sends are used // and the respective process's listening socket when Tcl-DP sends are // used. Note that in the former case, $client is just the same as // $client_name. In addition, since the server may need to communicate // with many different client processes, every command to the server // contains the sender's client id (so it knows how to report back if // necessary). Thus the Tk driver's interpreter must know both $server as // well as $client. It is most convenient to set $client from the server, // as a way to signal that communication has been set up and it is safe to // proceed. // // Often it is necessary to use constructs such as [list $server] instead // of just $server. This occurs since you could have multiple copies // running on the display (resulting in names of the form "plserver #2", // etc). Embedding such a string in a "[list ...]" construct prevents the // string from being interpreted as two separate strings. //-------------------------------------------------------------------------- static void init_server( PLStream *pls ) { int server_exists = 0; dbug_enter( "init_server" ); pldebug( "init_server", "%s -- PID: %d, PGID: %d, PPID: %d\n", __FILE__, (int) getpid(), (int) getpgrp(), (int) getppid() ); // If no means of communication provided, need to launch plserver if ( ( !pls->dp && pls->server_name != NULL ) || ( pls->dp && pls->server_port != NULL ) ) server_exists = 1; // So launch it if ( !server_exists ) launch_server( pls ); // Set up communication channel to server if ( pls->dp ) { tcl_cmd( pls, "set server [dp_MakeRPCClient $server_host $server_port]" ); } else { tcl_cmd( pls, "set server $server_name" ); } // If server didn't need launching, contact it here if ( server_exists ) tcl_cmd( pls, "plclient_link_init" ); } //-------------------------------------------------------------------------- // launch_server // // Launches plserver, locally or remotely. //-------------------------------------------------------------------------- static void launch_server( PLStream *pls ) { TkDev *dev = (TkDev *) pls->dev; const char *argv[20]; char *plserver_exec = NULL, *ptr; char *tmp = NULL; int i; dbug_enter( "launch_server" ); if ( pls->plserver == NULL ) pls->plserver = plstrdup( "plserver" ); // Build argument list i = 0; // If we're doing a rsh, need to set up its arguments first. if ( pls->dp && pls->server_host != NULL ) { argv[i++] = pls->server_host; // Host name for rsh if ( pls->user != NULL ) { argv[i++] = "-l"; argv[i++] = pls->user; // User name on remote node } } // The invoked executable name comes next argv[i++] = pls->plserver; // The rest are arguments to plserver argv[i++] = "-child"; // Tell plserver its ancestry argv[i++] = "-e"; // Startup script argv[i++] = "plserver_init"; // aaahhh. This is it! Without the next statements, control is either // in tk or octave, because tcl/tk was in interative mode (I think). // This had the inconvenient of having to press the enter key or cliking a // mouse button in the plot window after every plot. // // This couldn't be done with // Tcl_SetVar(dev->interp, "tcl_interactive", "0", TCL_GLOBAL_ONLY); // after plserver has been launched? It doesnt work, hoewever. // Tk_CreateFileHandler (0, TK_READABLE, NULL, 0) doesnt work also // argv[i++] = "-file"; // Startup file if ( pls->tk_file ) argv[i++] = pls->tk_file; else argv[i++] = "/dev/null"; // // Give interpreter the base name of the plwindow. // Useful to know the interpreter name // if ( pls->plwindow != NULL ) { char *t; argv[i++] = "-name"; // plserver name tmp = plstrdup( pls->plwindow + 1 ); // get rid of the initial dot argv[i++] = tmp; if ( ( t = strchr( tmp, '.' ) ) != NULL ) *t = '\0'; // and keep only the base name } else { argv[i++] = "-name"; // plserver name argv[i++] = pls->program; } if ( pls->auto_path != NULL ) { argv[i++] = "-auto_path"; // Additional directory(s) argv[i++] = pls->auto_path; // to autoload } if ( pls->geometry != NULL ) { argv[i++] = "-geometry"; // Top level window geometry argv[i++] = pls->geometry; } // If communicating via Tcl-DP, specify communications port id // If communicating via TK send, specify main window name if ( pls->dp ) { argv[i++] = "-client_host"; argv[i++] = Tcl_GetVar( dev->interp, "client_host", TCL_GLOBAL_ONLY ); argv[i++] = "-client_port"; argv[i++] = Tcl_GetVar( dev->interp, "client_port", TCL_GLOBAL_ONLY ); if ( pls->user != NULL ) { argv[i++] = "-l"; argv[i++] = pls->user; } } else { argv[i++] = "-client_name"; argv[i++] = Tcl_GetVar( dev->interp, "client_name", TCL_GLOBAL_ONLY ); } // The display absolutely must be set if invoking a remote server (by rsh) // Use the DISPLAY environmental, if set. Otherwise use the remote host. if ( pls->FileName != NULL ) { argv[i++] = "-display"; argv[i++] = pls->FileName; } else if ( pls->dp && pls->server_host != NULL ) { argv[i++] = "-display"; if ( ( ptr = getenv( "DISPLAY" ) ) != NULL ) argv[i++] = ptr; else argv[i++] = "unix:0.0"; } // Add terminating null argv[i++] = NULL; #ifdef DEBUG if ( pls->debug ) { int j; fprintf( stderr, "argument list: \n " ); for ( j = 0; j < i; j++ ) fprintf( stderr, "%s ", argv[j] ); fprintf( stderr, "\n" ); } #endif // Start server process // It's a fork/rsh if on a remote machine if ( pls->dp && pls->server_host != NULL ) { if ( ( dev->child_pid = fork() ) < 0 ) { abort_session( pls, "Unable to fork server process" ); } else if ( dev->child_pid == 0 ) { fprintf( stderr, "Starting up %s on node %s\n", pls->plserver, pls->server_host ); if ( execvp( "rsh", (char * const *) argv ) ) { perror( "Unable to exec server process" ); _exit( 1 ); } } } // Running locally, so its a fork/exec else { plserver_exec = plFindCommand( pls->plserver ); if ( ( plserver_exec == NULL ) || ( dev->child_pid = fork() ) < 0 ) { abort_session( pls, "Unable to fork server process" ); } else if ( dev->child_pid == 0 ) { // Don't kill plserver on a ^C if pls->server_nokill is set if ( pls->server_nokill ) { sigset_t set; sigemptyset( &set ); sigaddset( &set, SIGINT ); if ( sigprocmask( SIG_BLOCK, &set, 0 ) < 0 ) fprintf( stderr, "PLplot: sigprocmask failure\n" ); } pldebug( "launch_server", "Starting up %s\n", plserver_exec ); if ( execv( plserver_exec, (char * const *) argv ) ) { fprintf( stderr, "Unable to exec server process.\n" ); _exit( 1 ); } } free_mem( plserver_exec ); } free_mem( tmp ); // Wait for server to set up return communication channel tk_wait( pls, "[info exists client]" ); } //-------------------------------------------------------------------------- // plwindow_init // // Configures the widget hierarchy we are sending the data stream to. // // If a widget name (identifying the actual widget or a container widget) // hasn't been supplied already we assume it needs to be created. // // In order to achieve maximum flexibility, the PLplot tk driver requires // only that certain TCL procs must be defined in the server interpreter. // These can be used to set up the desired widget configuration. The procs // invoked from this driver currently include: // // $plw_create_proc Creates the widget environment // $plw_start_proc Does any remaining startup necessary // $plw_end_proc Prepares for shutdown // $plw_flash_proc Invoked when waiting for page advance // // Since all of these are interpreter variables, they can be trivially // changed by the user. // // Each of these utility procs is called with a widget name ($plwindow) // as argument. "plwindow" is set from the value of pls->plwindow, and // if null is generated from the name of the client main window (to // ensure uniqueness). $plwindow usually indicates the container frame // for the actual PLplot widget, but can be arbitrary -- as long as the // usage in all the TCL procs is consistent. // // In order that the TK driver be able to invoke the actual PLplot // widget, the proc "$plw_create_proc" deposits the widget name in the local // interpreter variable "plwidget". //-------------------------------------------------------------------------- static void plwindow_init( PLStream *pls ) { TkDev *dev = (TkDev *) pls->dev; char command[CMD_LEN]; unsigned int bg; char *tmp; int i, n; dbug_enter( "plwindow_init" ); // Set tcl plwindow variable to be pls->plwindow with a . prepended and // and with ' ' replaced by '_' and all other '.' by '_' to avoid // quoting and bad window name problems. Also avoid name starting with // an upper case letter. n = (int) strlen( pls->plwindow ) + 1; tmp = (char *) malloc( sizeof ( char ) * (size_t) ( n + 1 ) ); sprintf( tmp, ".%s", pls->plwindow ); for ( i = 1; i < n; i++ ) { if ( ( tmp[i] == ' ' ) || ( tmp[i] == '.' ) ) tmp[i] = '_'; } if ( isupper( tmp[1] ) ) tmp[1] = tolower( tmp[1] ); Tcl_SetVar( dev->interp, "plwindow", tmp, 0 ); free( tmp ); // Create the plframe widget & anything else you want with it. server_cmd( pls, "$plw_create_proc $plwindow [list $client]", 1 ); tk_wait( pls, "[info exists plwidget]" ); // Now we should have the actual PLplot widget name in $plwidget // Configure remote PLplot stream. // Configure background color if anything other than black // The default color is handled from a resource setting in plconfig.tcl bg = (unsigned int) ( pls->cmap0[0].b | ( pls->cmap0[0].g << 8 ) | ( pls->cmap0[0].r << 16 ) ); if ( bg > 0 ) { snprintf( command, CMD_LEN, "$plwidget configure -plbg #%06x", bg ); server_cmd( pls, command, 0 ); } // nopixmap option if ( pls->nopixmap ) server_cmd( pls, "$plwidget cmd plsetopt -nopixmap", 0 ); // debugging if ( pls->debug ) server_cmd( pls, "$plwidget cmd plsetopt -debug", 0 ); // double buffering if ( pls->db ) server_cmd( pls, "$plwidget cmd plsetopt -db", 0 ); // color map options if ( pls->ncol0 ) { snprintf( command, CMD_LEN, "$plwidget cmd plsetopt -ncol0 %d", pls->ncol0 ); server_cmd( pls, command, 0 ); } if ( pls->ncol1 ) { snprintf( command, CMD_LEN, "$plwidget cmd plsetopt -ncol1 %d", pls->ncol1 ); server_cmd( pls, command, 0 ); } // Start up remote PLplot server_cmd( pls, "$plw_start_proc $plwindow", 1 ); tk_wait( pls, "[info exists widget_is_ready]" ); } //-------------------------------------------------------------------------- // set_windowname // // Set up top level window name. Use pls->program, modified appropriately. //-------------------------------------------------------------------------- static void set_windowname( PLStream *pls ) { const char *pname; int i; size_t maxlen; // Set to "plclient" if not initialized via plargs or otherwise if ( pls->program == NULL ) pls->program = plstrdup( "plclient" ); // Eliminate any leading path specification pname = strrchr( pls->program, '/' ); if ( pname ) pname++; else pname = pls->program; if ( pls->plwindow == NULL ) // dont override -plwindow cmd line option { maxlen = strlen( pname ) + 10; pls->plwindow = (char *) malloc( maxlen * sizeof ( char ) ); // Allow for multiple widgets created by multiple streams if ( pls->ipls == 0 ) snprintf( pls->plwindow, maxlen, ".%s", pname ); else snprintf( pls->plwindow, maxlen, ".%s_%d", pname, (int) pls->ipls ); // Replace any ' 's with '_'s to avoid quoting problems. // Replace any '.'s (except leading) with '_'s to avoid bad window names. for ( i = 0; i < (int) strlen( pls->plwindow ); i++ ) { if ( pls->plwindow[i] == ' ' ) pls->plwindow[i] = '_'; if ( i == 0 ) continue; if ( pls->plwindow[i] == '.' ) pls->plwindow[i] = '_'; } } } //-------------------------------------------------------------------------- // link_init // // Initializes the link between the client and the PLplot widget for // data transfer. Defaults to a FIFO when the TK driver is selected and // a socket when the DP driver is selected. //-------------------------------------------------------------------------- static void link_init( PLStream *pls ) { TkDev *dev = (TkDev *) pls->dev; PLiodev *iodev = (PLiodev *) dev->iodev; size_t bufmax = (size_t) ( pls->bufmax * 1.2 ); const char *dirname = NULL; dbug_enter( "link_init" ); // Create FIFO for data transfer to the plframe widget if ( !pls->dp ) { // This uses the pl_create_tempfifo function to create // the fifo in a safe manner by first creating a private // temporary directory. iodev->fileName = pl_create_tempfifo( (const char **) &iodev->fileName, &dirname ); if ( dirname == NULL || iodev->fileName == NULL ) abort_session( pls, "mkfifo error" ); // Tell plframe widget to open FIFO (for reading). Tcl_SetVar( dev->interp, "fifoname", iodev->fileName, 0 ); server_cmd( pls, "$plwidget openlink fifo $fifoname", 1 ); // Open the FIFO for writing // This will block until the server opens it for reading if ( ( iodev->fd = open( iodev->fileName, O_WRONLY ) ) == -1 ) abort_session( pls, "Error opening fifo for write" ); // Create stream interface (C file handle) to FIFO iodev->type = 0; iodev->typeName = "fifo"; iodev->file = fdopen( iodev->fd, "wb" ); // Unlink FIFO so that it isn't left around if program crashes. // This also ensures no other program can mess with it. if ( unlink( iodev->fileName ) == -1 ) abort_session( pls, "Error removing fifo" ); free( (void *) iodev->fileName ); iodev->fileName = NULL; if ( rmdir( dirname ) == -1 ) abort_session( pls, "Error removing temporary directory" ); free( (void *) dirname ); } // Create socket for data transfer to the plframe widget else { iodev->type = 1; iodev->typeName = "socket"; tcl_cmd( pls, "plclient_dp_init" ); iodev->fileHandle = Tcl_GetVar( dev->interp, "data_sock", 0 ); if ( Tcl_GetOpenFile( dev->interp, iodev->fileHandle, 0, 1, ( ClientData ) & iodev->file ) != TCL_OK ) { fprintf( stderr, "Cannot get file info:\n\t %s\n", Tcl_GetStringResult( dev->interp ) ); abort_session( pls, "" ); } iodev->fd = fileno( iodev->file ); } // Create data buffer pls->pdfs = pdf_bopen( NULL, (size_t) bufmax ); } //-------------------------------------------------------------------------- // WaitForPage() // // Waits for a page advance. //-------------------------------------------------------------------------- static void WaitForPage( PLStream *pls ) { TkDev *dev = (TkDev *) pls->dev; dbug_enter( "WaitForPage" ); while ( !dev->exit_eventloop ) { Tk_DoOneEvent( 0 ); } dev->exit_eventloop = 0; } //-------------------------------------------------------------------------- // CheckForEvents() // // A front-end to HandleEvents(), which is only called if certain conditions // are satisfied: // // - only check for events and process them every dev->max_instr times this // function is called (good for performance since performing an update is // a nontrivial performance hit). //-------------------------------------------------------------------------- static void CheckForEvents( PLStream *pls ) { TkDev *dev = (TkDev *) pls->dev; if ( ++dev->instr % dev->max_instr == 0 ) { dev->instr = 0; HandleEvents( pls ); } } //-------------------------------------------------------------------------- // HandleEvents() // // Just a front-end to the update command, for use when not actually waiting // for an event but only checking the event queue. //-------------------------------------------------------------------------- static void HandleEvents( PLStream *pls ) { TkDev *dev = (TkDev *) pls->dev; dbug_enter( "HandleEvents" ); Tcl_VarEval( dev->interp, dev->updatecmd, (char **) NULL ); } //-------------------------------------------------------------------------- // flush_output() // // Sends graphics instructions to the {FIFO|socket} via a packet send. // // The packet i/o routines are modified versions of the ones from the // Tcl-DP package. They have been altered to take a pointer to a PDFstrm // struct, and read-to or write-from pdfs->buffer. The length of the // buffer is stored in pdfs->bp (the original Tcl-DP routine assumes the // message is character data and uses strlen). Also, they can // send/receive from either a fifo or a socket. //-------------------------------------------------------------------------- static void flush_output( PLStream *pls ) { TkDev *dev = (TkDev *) pls->dev; PDFstrm *pdfs = (PDFstrm *) pls->pdfs; dbug_enter( "flush_output" ); HandleEvents( pls ); // Send packet -- plserver filehandler will be invoked automatically. if ( pdfs->bp > 0 ) { #ifdef DEBUG_ENTER pldebug( "flush_output", "%s: Flushing buffer, bytes = %ld\n", __FILE__, pdfs->bp ); #endif if ( pl_PacketSend( dev->interp, dev->iodev, pls->pdfs ) ) { fprintf( stderr, "Packet send failed:\n\t %s\n", Tcl_GetStringResult( dev->interp ) ); abort_session( pls, "" ); } pdfs->bp = 0; } } //-------------------------------------------------------------------------- // Abort // // Just a TCL front-end to abort_session(). //-------------------------------------------------------------------------- static int Abort( ClientData clientData, Tcl_Interp *PL_UNUSED( interp ), int PL_UNUSED( argc ), char **PL_UNUSED( argv ) ) { PLStream *pls = (PLStream *) clientData; dbug_enter( "Abort" ); abort_session( pls, "" ); return TCL_OK; } //-------------------------------------------------------------------------- // Plfinfo // // Sends info about the server plframe. Usually issued after some // modification to the plframe is made by the user, such as a resize. //-------------------------------------------------------------------------- static int Plfinfo( ClientData clientData, Tcl_Interp *interp, int argc, char **argv ) { PLStream *pls = (PLStream *) clientData; TkDev *dev = (TkDev *) pls->dev; int result = TCL_OK; dbug_enter( "Plfinfo" ); if ( argc < 3 ) { Tcl_AppendResult( interp, "wrong # args: should be \"", " plfinfo wx wy\"", (char *) NULL ); result = TCL_ERROR; } else { dev->width = (unsigned int) atoi( argv[1] ); dev->height = (unsigned int) atoi( argv[2] ); #if PHYSICAL { PLFLT pxlx = (double) PIXELS_X / dev->width * DPMM; PLFLT pxly = (double) PIXELS_Y / dev->height * DPMM; plP_setpxl( pxlx, pxly ); } #endif } return result; } //-------------------------------------------------------------------------- // KeyEH() // // This TCL command handles keyboard events. //-------------------------------------------------------------------------- static int KeyEH( ClientData clientData, Tcl_Interp *interp, int argc, char **argv ) { PLStream *pls = (PLStream *) clientData; TkDev *dev = (TkDev *) pls->dev; int result; dbug_enter( "KeyEH" ); if ( ( result = LookupTkKeyEvent( pls, interp, argc, argv ) ) != TCL_OK ) return result; if ( dev->locate_mode ) LocateKey( pls ); else ProcessKey( pls ); return result; } //-------------------------------------------------------------------------- // ButtonEH() // // This TCL command handles button events. //-------------------------------------------------------------------------- static int ButtonEH( ClientData clientData, Tcl_Interp *interp, int argc, char **argv ) { PLStream *pls = (PLStream *) clientData; TkDev *dev = (TkDev *) pls->dev; int result; dbug_enter( "ButtonEH" ); if ( ( result = LookupTkButtonEvent( pls, interp, argc, argv ) ) != TCL_OK ) return result; if ( dev->locate_mode ) LocateButton( pls ); else ProcessButton( pls ); return result; } //-------------------------------------------------------------------------- // LookupTkKeyEvent() // // Fills in the PLGraphicsIn from a Tk KeyEvent. // // Contents of argv array: // command name // keysym value // keysym state // absolute x coordinate of cursor // absolute y coordinate of cursor // relative x coordinate (normalized to [0.0 1.0]) // relative y coordinate (normalized to [0.0 1.0]) // keysym name // ASCII equivalent (optional) // // Note that the keysym name is only used for debugging, and the string is // not always passed (i.e. the character may not have an ASCII // representation). //-------------------------------------------------------------------------- static int LookupTkKeyEvent( PLStream *pls, Tcl_Interp *interp, int argc, char **argv ) { TkDev *dev = (TkDev *) pls->dev; PLGraphicsIn *gin = &( dev->gin ); char *keyname; dbug_enter( "LookupTkKeyEvent" ); if ( argc < 8 ) { Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0], " key-value state pX pY dX dY key-name ?ascii-value?\"", (char *) NULL ); return TCL_ERROR; } gin->keysym = (unsigned int) atol( argv[1] ); gin->state = (unsigned int) atol( argv[2] ); gin->pX = atoi( argv[3] ); gin->pY = atoi( argv[4] ); gin->dX = atof( argv[5] ); gin->dY = atof( argv[6] ); keyname = argv[7]; gin->string[0] = '\0'; if ( argc > 8 ) { gin->string[0] = argv[8][0]; gin->string[1] = '\0'; } // Fix up keysym value -- see notes in xwin.c about key representation switch ( gin->keysym ) { case XK_BackSpace: case XK_Tab: case XK_Linefeed: case XK_Return: case XK_Escape: case XK_Delete: gin->keysym &= 0xFF; break; } pldebug( "LookupTkKeyEvent", "KeyEH: stream: %d, Keyname %s, hex %x, ASCII: %s\n", (int) pls->ipls, keyname, (unsigned int) gin->keysym, gin->string ); return TCL_OK; } //-------------------------------------------------------------------------- // LookupTkButtonEvent() // // Fills in the PLGraphicsIn from a Tk ButtonEvent. // // Contents of argv array: // command name // button number // state (decimal string) // absolute x coordinate // absolute y coordinate // relative x coordinate (normalized to [0.0 1.0]) // relative y coordinate (normalized to [0.0 1.0]) //-------------------------------------------------------------------------- static int LookupTkButtonEvent( PLStream *pls, Tcl_Interp *interp, int argc, char **argv ) { TkDev *dev = (TkDev *) pls->dev; PLGraphicsIn *gin = &( dev->gin ); dbug_enter( "LookupTkButtonEvent" ); if ( argc != 7 ) { Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0], " button-number state pX pY dX dY\"", (char *) NULL ); return TCL_ERROR; } gin->button = (unsigned int) atol( argv[1] ); gin->state = (unsigned int) atol( argv[2] ); gin->pX = atoi( argv[3] ); gin->pY = atoi( argv[4] ); gin->dX = atof( argv[5] ); gin->dY = atof( argv[6] ); gin->keysym = 0x20; pldebug( "LookupTkButtonEvent", "button %d, state %d, pX: %d, pY: %d, dX: %f, dY: %f\n", gin->button, gin->state, gin->pX, gin->pY, gin->dX, gin->dY ); return TCL_OK; } //-------------------------------------------------------------------------- // ProcessKey() // // Process keyboard events other than locate input. //-------------------------------------------------------------------------- static void ProcessKey( PLStream *pls ) { TkDev *dev = (TkDev *) pls->dev; PLGraphicsIn *gin = &( dev->gin ); dbug_enter( "ProcessKey" ); // Call user keypress event handler. Since this is called first, the user // can disable all internal event handling by setting key.keysym to 0. // if ( pls->KeyEH != NULL ) ( *pls->KeyEH )( gin, pls->KeyEH_data, &dev->exit_eventloop ); // Handle internal events switch ( gin->keysym ) { case PLK_Return: case PLK_Linefeed: case PLK_Next: // Advance to next page (i.e. terminate event loop) on a // Check for both and for portability, also a dev->exit_eventloop = TRUE; break; case 'Q': // Terminate on a 'Q' (not 'q', since it's too easy to hit by mistake) tcl_cmd( pls, "abort" ); break; case 'L': // Begin locate mode dev->locate_mode = LOCATE_INVOKED_VIA_DRIVER; server_cmd( pls, "$plwidget configure -xhairs on", 1 ); break; } } //-------------------------------------------------------------------------- // ProcessButton() // // Process ButtonPress events other than locate input. // On: // Button1: nothing (except when in locate mode, see ButtonLocate) // Button2: nothing // Button3: set page advance flag //-------------------------------------------------------------------------- static void ProcessButton( PLStream *pls ) { TkDev *dev = (TkDev *) pls->dev; PLGraphicsIn *gin = &( dev->gin ); dbug_enter( "ButtonEH" ); // Call user event handler. Since this is called first, the user can // disable all PLplot internal event handling by setting gin->button to 0. // if ( pls->ButtonEH != NULL ) ( *pls->ButtonEH )( gin, pls->ButtonEH_data, &dev->exit_eventloop ); // Handle internal events switch ( gin->button ) { case Button3: dev->exit_eventloop = TRUE; break; } } //-------------------------------------------------------------------------- // LocateKey() // // Front-end to locate handler for KeyPress events. // Only provides for: // // Ends locate mode //-------------------------------------------------------------------------- static void LocateKey( PLStream *pls ) { TkDev *dev = (TkDev *) pls->dev; PLGraphicsIn *gin = &( dev->gin ); // End locate mode on if ( gin->keysym == PLK_Escape ) { dev->locate_mode = 0; server_cmd( pls, "$plwidget configure -xhairs off", 1 ); plGinInit( gin ); } else { Locate( pls ); } } //-------------------------------------------------------------------------- // LocateButton() // // Front-end to locate handler for ButtonPress events. // Only passes control to Locate() for Button1 presses. //-------------------------------------------------------------------------- static void LocateButton( PLStream *pls ) { TkDev *dev = (TkDev *) pls->dev; PLGraphicsIn *gin = &( dev->gin ); switch ( gin->button ) { case Button1: Locate( pls ); break; } } //-------------------------------------------------------------------------- // Locate() // // Handles locate mode events. // // In locate mode: move cursor to desired location and select by pressing a // key or by clicking on the mouse (if available). Typically the world // coordinates of the selected point are reported. // // There are two ways to enter Locate mode -- via the API, or via a driver // command. The API entry point is the call plGetCursor(), which initiates // locate mode and does not return until input has been obtained. The // driver entry point is by entering a 'L' while the driver is waiting for // events. // // Locate mode input is reported in one of three ways: // 1. Through a returned PLGraphicsIn structure, when user has specified a // locate handler via (*pls->LocateEH). // 2. Through a returned PLGraphicsIn structure, when locate mode is invoked // by a plGetCursor() call. // 3. Through writes to stdout, when locate mode is invoked by a driver // command and the user has not supplied a locate handler. // // Hitting will at all times end locate mode. Other keys will // typically be interpreted as locator input. Selecting a point out of // bounds will end locate mode unless the user overrides with a supplied // Locate handler. //-------------------------------------------------------------------------- static void Locate( PLStream *pls ) { TkDev *dev = (TkDev *) pls->dev; PLGraphicsIn *gin = &( dev->gin ); // Call user locate mode handler if provided if ( pls->LocateEH != NULL ) ( *pls->LocateEH )( gin, pls->LocateEH_data, &dev->locate_mode ); // Use default procedure else { // Try to locate cursor if ( plTranslateCursor( gin ) ) { // If invoked by the API, we're done // Otherwise send report to stdout if ( dev->locate_mode == LOCATE_INVOKED_VIA_DRIVER ) { pltext(); if ( gin->keysym < 0xFF && isprint( gin->keysym ) ) printf( "%f %f %c\n", gin->wX, gin->wY, gin->keysym ); else printf( "%f %f 0x%02x\n", gin->wX, gin->wY, gin->keysym ); plgra(); } } else { // Selected point is out of bounds, so end locate mode dev->locate_mode = 0; server_cmd( pls, "$plwidget configure -xhairs off", 1 ); } } } //-------------------------------------------------------------------------- // // pltk_toplevel -- // // Create top level window without mapping it. // // Results: // Returns 1 on error. // // Side effects: // Returns window ID as *w. // //-------------------------------------------------------------------------- static int pltk_toplevel( Tk_Window *PL_UNUSED( w ), Tcl_Interp *interp ) { static char wcmd[] = "wm withdraw ."; // Create the main window without mapping it if ( Tk_Init( interp ) ) { fprintf( stderr, "tk_init:%s\n", Tcl_GetStringResult( interp ) ); return 1; } Tcl_VarEval( interp, wcmd, (char *) NULL ); return 0; } //-------------------------------------------------------------------------- // tk_wait() // // Waits for the specified expression to evaluate to true before // proceeding. While we are waiting to proceed, all events (for this // or other interpreters) are handled. // // Use a static string buffer to hold the command, to ensure it's in // writable memory (grrr...). //-------------------------------------------------------------------------- static void tk_wait( PLStream *pls, const char *cmd ) { TkDev *dev = (TkDev *) pls->dev; int result = 0; dbug_enter( "tk_wait" ); copybuf( pls, cmd ); for (;; ) { if ( Tcl_ExprBoolean( dev->interp, dev->cmdbuf, &result ) ) { fprintf( stderr, "tk_wait command \"%s\" failed:\n\t %s\n", cmd, Tcl_GetStringResult( dev->interp ) ); break; } if ( result ) break; Tk_DoOneEvent( 0 ); } } //-------------------------------------------------------------------------- // server_cmd // // Sends specified command to server, aborting on an error. // If nowait is set, the command is issued in the background. // // If commands MUST proceed in a certain order (e.g. initialization), it // is safest to NOT run them in the background. // // In order to protect args that have embedded spaces in them, I enclose // the entire command in a [list ...], but for TK sends ONLY. If done with // Tcl-DP RPC, the sent command is no longer recognized. Evidently an // extra scan of the line is done with TK sends for some reason. //-------------------------------------------------------------------------- static void server_cmd( PLStream *pls, const char *cmd, int nowait ) { TkDev *dev = (TkDev *) pls->dev; static char dpsend_cmd0[] = "dp_RPC $server "; static char dpsend_cmd1[] = "dp_RDO $server "; static char tksend_cmd0[] = "send $server "; static char tksend_cmd1[] = "send $server after 1 "; int result; dbug_enter( "server_cmd" ); pldebug( "server_cmd", "Sending command: %s\n", cmd ); if ( pls->dp ) { if ( nowait ) result = Tcl_VarEval( dev->interp, dpsend_cmd1, cmd, (char **) NULL ); else result = Tcl_VarEval( dev->interp, dpsend_cmd0, cmd, (char **) NULL ); } else { if ( nowait ) result = Tcl_VarEval( dev->interp, tksend_cmd1, "[list ", cmd, "]", (char **) NULL ); else result = Tcl_VarEval( dev->interp, tksend_cmd0, "[list ", cmd, "]", (char **) NULL ); } if ( result != TCL_OK ) { fprintf( stderr, "Server command \"%s\" failed:\n\t %s\n", cmd, Tcl_GetStringResult( dev->interp ) ); abort_session( pls, "" ); } } //-------------------------------------------------------------------------- // tcl_cmd // // Evals the specified command, aborting on an error. //-------------------------------------------------------------------------- static void tcl_cmd( PLStream *pls, const char *cmd ) { TkDev *dev = (TkDev *) pls->dev; dbug_enter( "tcl_cmd" ); pldebug( "tcl_cmd", "Evaluating command: %s\n", cmd ); if ( Tcl_VarEval( dev->interp, cmd, (char **) NULL ) != TCL_OK ) { fprintf( stderr, "TCL command \"%s\" failed:\n\t %s\n", cmd, Tcl_GetStringResult( dev->interp ) ); abort_session( pls, "" ); } } //-------------------------------------------------------------------------- // copybuf // // Puts command in a static string buffer, to ensure it's in writable // memory (grrr...). //-------------------------------------------------------------------------- static void copybuf( PLStream *pls, const char *cmd ) { TkDev *dev = (TkDev *) pls->dev; if ( dev->cmdbuf == NULL ) { dev->cmdbuf_len = 100; dev->cmdbuf = (char *) malloc( dev->cmdbuf_len ); } if ( strlen( cmd ) >= dev->cmdbuf_len ) { free( (void *) dev->cmdbuf ); dev->cmdbuf_len = strlen( cmd ) + 20; dev->cmdbuf = (char *) malloc( dev->cmdbuf_len ); } strcpy( dev->cmdbuf, cmd ); } //-------------------------------------------------------------------------- #else int pldummy_tk() { return 0; } #endif // PLD_tk plplot-5.13.0/drivers/cairo.driver_info.in000644 001752 001752 00000000705 13150160115 022247 0ustar00softwaresoftware000000 000000 xcairo:Cairo X Windows Driver:1:cairo:100:xcairo pdfcairo:Cairo PDF Driver:0:cairo:101:pdfcairo pscairo:Cairo PS Driver:0:cairo:102:pscairo epscairo:Cairo EPS Driver:0:cairo:103:epscairo svgcairo:Cairo SVG Driver:0:cairo:104:svgcairo pngcairo:Cairo PNG Driver:0:cairo:105:pngcairo memcairo:Cairo Memory Driver:0:cairo:106:memcairo extcairo:Cairo External Context Driver:0:cairo:107:extcairo wincairo:Cairo Microscoft Windows Driver:0:cairo:108:wincairo plplot-5.13.0/drivers/test-drv-info.c000644 001752 001752 00000004623 13150160115 021165 0ustar00softwaresoftware000000 000000 // Get device info from PLplot driver module // // Copyright (C) 2003 Rafael Laboissiere // Copyright (C) 2004 Joao Cardoso // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify it under // the terms of the GNU Library General Public License as published by the // Free Software Foundation; either version 2 of the License, or (at your // option) any later version. // // PLplot is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library // General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with the GNU C Library; see the file COPYING.LIB. If not, write to // the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, // MA 02110-1301, USA. // #include "plplotP.h" #ifndef LTDL_WIN32 #include #else #include "ltdl_win32.h" #endif #include #include #include #define SYM_LEN 300 #define DRVSPEC_LEN 400 // function prototype RETSIGTYPE catch_segv( int sig ); // SEGV signal handler RETSIGTYPE catch_segv( int PL_UNUSED( sig ) ) { fprintf( stderr, "libltdl error: %s\n", lt_dlerror() ); exit( 1 ); } int main( int PL_UNUSED( argc ), char* argv[] ) { lt_dlhandle dlhand; char sym[SYM_LEN]; char * drvnam = argv[1]; char drvspec[ DRVSPEC_LEN ]; char ** info; // Establish a handler for SIGSEGV signals. signal( SIGSEGV, catch_segv ); lt_dlinit(); #if defined ( LTDL_WIN32 ) || defined ( __CYGWIN__ ) snprintf( drvspec, DRVSPEC_LEN, "%s", drvnam ); #else snprintf( drvspec, DRVSPEC_LEN, "%s/%s", plGetDrvDir(), drvnam ); #endif // LTDL_WIN32 dlhand = lt_dlopenext( drvspec ); if ( dlhand == NULL ) { fprintf( stderr, "Could not open driver module %s\n" "libltdl error: %s\n", drvspec, lt_dlerror() ); exit( 1 ); } snprintf( sym, SYM_LEN, "plD_DEVICE_INFO_%s", drvnam ); info = (char **) lt_dlsym( dlhand, sym ); if ( info != NULL ) { printf( "%s", *info ); exit( 0 ); } else { fprintf( stderr, "Could not read symbol %s in driver module %s\n" "libltdl error: %s\n", sym, drvspec, lt_dlerror() ); exit( 1 ); } } plplot-5.13.0/drivers/deprecated_wxwidgets_app.cpp000644 001752 001752 00000100001 13150160115 024054 0ustar00softwaresoftware000000 000000 // Copyright (C) 2008 Werner Smekal // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // TODO: // - Add dialog to get width and height from user for plot size to save. // // wxwidgets headers #include "wx/wx.h" #include "plDevs.h" #ifdef PLD_wxwidgets // plplot headers #include "plplotP.h" #include "drivers.h" #include "plevent.h" // std and driver headers #include "deprecated_wxwidgets.h" // Application icon as XPM // This free icon was taken from http://2pt3.com/news/twotone-icons-for-free/ static const char *graph[] = { // columns rows colors chars-per-pixel "16 16 4 2", " c black", ". c #BA1825", "X c gray100", "UX c None", // pixels "UX. . . . . . . . . . . . . . UX", ". . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . ", ". . . . . . . . . . . X X . . . ", ". . . . . . . . . . . X X . . . ", ". . . . . . . . . . . X X . . . ", ". . . . . X X . . . . X X . . . ", ". . . . . X X . . . . X X . . . ", ". . . . . X X . X X . X X . . . ", ". . . . . X X . X X . X X . . . ", ". . . . . X X . X X . X X . . . ", ". . . . . X X . X X . X X . . . ", ". . . X X X X X X X X X X . . . ", ". . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . ", "UX. . . . . . . . . . . . . . UX" }; struct dev_entry dev_entries[] = { { wxT( "wxbmp" ), wxT( "bmp (wx)..." ), wxT( "Save this plot as bmp!" ), wxT( "bmp files (*.bmp)|*.bmp" ), true }, { wxT( "wxpng" ), wxT( "png (wx)..." ), wxT( "Save this plot as png" ), wxT( "png files (*.png)|*.png" ), true }, { wxT( "wxpcx" ), wxT( "pcx (wx)..." ), wxT( "Save this plot as pcx!" ), wxT( "pcx files (*.pcx)|*.pcx" ), true }, { wxT( "wxjpeg" ), wxT( "jpeg (wx)..." ), wxT( "Save this plot as jpeg!" ), wxT( "jpg files (*.jpg;*.jpeg)|*.jpg;*.jpeg" ), true }, { wxT( "wxtiff" ), wxT( "tiff (wx)..." ), wxT( "Save this plot as tiff!" ), wxT( "tiff files (*.tif;*.tiff)|*.tif;*.tiff" ), true }, { wxT( "wxpnm" ), wxT( "pnm (wx)..." ), wxT( "Save this plot as pnm!" ), wxT( "pnm files (*.pnm)|*.pnm" ), true }, { wxT( "pngcairo" ), wxT( "png (cairo)..." ), wxT( "Save this plot as png using cairo!" ), wxT( "png files (*.png)|*.png" ), true }, { wxT( "pdfcairo" ), wxT( "pdf (cairo)..." ), wxT( "Save this plot as pdf using cairo!" ), wxT( "pdf files (*.pdf)|*.pdf" ), false }, { wxT( "ps" ), wxT( "postscript..." ), wxT( "Save this plot as postscript!" ), wxT( "ps files (*.ps)|*.ps" ), false }, { wxT( "psc" ), wxT( "color postscript..." ), wxT( "Save this plot as color postscript!" ), wxT( "ps files (*.ps;*.psc)|*.ps;*.psc" ), false }, { wxT( "pscairo" ), wxT( "color postscript (cairo)..." ), wxT( "Save this plot as color postscript using cairo!" ), wxT( "ps files (*.ps;*.psc)|*.ps;*.psc" ), false }, { wxT( "svg" ), wxT( "svg..." ), wxT( "Save this plot as svg!" ), wxT( "svg files (*.svg)|*.svg" ), false }, { wxT( "svgcairo" ), wxT( "svg (cairo)..." ), wxT( "Save this plot as svg using cairo!" ), wxT( "svg files (*.svg)|*.svg" ), false }, { wxT( "xfig" ), wxT( "xfig..." ), wxT( "Save this plot as xfig!" ), wxT( "fig files (*.fig)|*.fig" ), false } }; // Application implementation IMPLEMENT_PLAPP_NO_MAIN( wxPLplotApp ) // event table for the app BEGIN_EVENT_TABLE( wxPLplotApp, wxApp ) EVT_IDLE( wxPLplotApp::OnIdle ) END_EVENT_TABLE() // event table for frames BEGIN_EVENT_TABLE( wxPLplotFrame, wxFrame ) EVT_MENU( -1, wxPLplotFrame::OnMenu ) // handle all menu events EVT_CLOSE( wxPLplotFrame::OnClose ) END_EVENT_TABLE() // event table for the plot widget BEGIN_EVENT_TABLE( wxPLplotWindow, wxWindow ) EVT_PAINT( wxPLplotWindow::OnPaint ) // (re)draw the plot in window EVT_CHAR( wxPLplotWindow::OnChar ) EVT_IDLE( wxPLplotWindow::OnIdle ) EVT_MOUSE_EVENTS( wxPLplotWindow::OnMouse ) EVT_ERASE_BACKGROUND( wxPLplotWindow::OnErase ) EVT_SIZE( wxPLplotWindow::OnSize ) EVT_MAXIMIZE( wxPLplotWindow::OnMaximize ) END_EVENT_TABLE() // event table for the size dialog BEGIN_EVENT_TABLE( wxGetSizeDialog, wxDialog ) END_EVENT_TABLE() //-------------------------------------------------------------------------- // bool wxPLplotApp::OnInit() // // This method is called before the applications gets control. //-------------------------------------------------------------------------- bool wxPLplotApp::OnInit() { // Log_Verbose( "wxPLplotApp::OnInit" ); exit = false; advance = false; #if wxUSE_LIBPNG wxImage::AddHandler( new wxPNGHandler ); #endif #if wxUSE_LIBJPEG wxImage::AddHandler( new wxJPEGHandler ); #endif #if wxUSE_PCX wxImage::AddHandler( new wxPCXHandler ); #endif #if wxUSE_LIBTIFF wxImage::AddHandler( new wxTIFFHandler ); #endif #if wxUSE_PNM wxImage::AddHandler( new wxPNMHandler ); #endif return true; } //-------------------------------------------------------------------------- // void wxPLplotApp::SetRefreshFlag( bool flag ) // // XXX - missing //-------------------------------------------------------------------------- void wxPLplotApp::SetRefreshFlag( bool flag ) { // Log_Verbose( "wxPLplotApp::SetRefreshFlag" ); for ( size_t i = 0; i < FrameArray.GetCount(); i++ ) FrameArray[i]->SetRefreshFlag( flag ); } //-------------------------------------------------------------------------- // void wxPLplotApp::OnIdle( wxIdleEvent& WXUNUSED(event) ) // // XXX - missing //-------------------------------------------------------------------------- void wxPLplotApp::OnIdle( wxIdleEvent& WXUNUSED( event ) ) { // Log_Verbose( "wxPLplotApp::OnIdle" ); bool refresh = false; if ( exit ) ExitMainLoop(); for ( size_t i = 0; i < FrameArray.GetCount(); i++ ) refresh |= FrameArray[i]->GetRefreshFlag(); if ( advance && !refresh ) ExitMainLoop(); } //-------------------------------------------------------------------------- // wxPLplotFrame::wxPLplotFrame( const wxString& title, PLStream *pls ) // // Constructor of wxPLplotFrame, where we create the menu and add the // wxPLplotWindow. We need also to know the current PLStream. //-------------------------------------------------------------------------- wxPLplotFrame::wxPLplotFrame( const wxString& title, PLStream *pls ) : wxFrame( NULL, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX | wxRESIZE_BORDER | wxCLIP_CHILDREN ) { // Log_Verbose( "wxPLplotFrame::wxPLplotFrame" ); m_dev = (wxPLDevBase *) pls->dev; m_panel = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxCLIP_CHILDREN ); wxBoxSizer* box = new wxBoxSizer( wxVERTICAL ); m_window = new wxPLplotWindow( m_panel, pls ); box->Add( m_window, 1, wxALL | wxEXPAND, 0 ); m_panel->SetSizer( box ); m_window->SetFocus(); wxMenu* saveMenu = new wxMenu; saveMenu->Append( wxPL_Save, dev_entries[0].dev_menu_short, dev_entries[0].dev_menu_long ); #if wxUSE_LIBPNG saveMenu->Append( wxPL_Save + 1, dev_entries[1].dev_menu_short, dev_entries[1].dev_menu_long ); #endif #if wxUSE_PCX saveMenu->Append( wxPL_Save + 2, dev_entries[2].dev_menu_short, dev_entries[2].dev_menu_long ); #endif #if wxUSE_LIBJPEG saveMenu->Append( wxPL_Save + 3, dev_entries[3].dev_menu_short, dev_entries[3].dev_menu_long ); #endif #if wxUSE_LIBTIFF saveMenu->Append( wxPL_Save + 4, dev_entries[4].dev_menu_short, dev_entries[4].dev_menu_long ); #endif #if wxUSE_PNM saveMenu->Append( wxPL_Save + 5, dev_entries[5].dev_menu_short, dev_entries[5].dev_menu_long ); #endif for ( size_t j = 6; j < sizeof ( dev_entries ) / sizeof ( dev_entry ); j++ ) for ( int i = 0; i < m_dev->ndev; i++ ) { if ( !strcmp( m_dev->devName[i], dev_entries[j].dev_name.mb_str() ) ) saveMenu->Append( wxPL_Save + j, dev_entries[j].dev_menu_short, dev_entries[j].dev_menu_long ); } wxMenu* fileMenu = new wxMenu; #if ( wxMAJOR_VERSION <= 2 ) & ( wxMINOR_VERSION <= 6 ) fileMenu->Append( -1, wxT( "Save plot as..." ), saveMenu, wxT( "Save this plot as ...!" ) ); #else fileMenu->AppendSubMenu( saveMenu, wxT( "Save plot as..." ), wxT( "Save this plot as ...!" ) ); #endif fileMenu->Append( wxID_EXIT, wxT( "E&xit\tAlt-X" ), wxT( "Exit wxWidgets PLplot App" ) ); wxMenu* orientationMenu = new wxMenu; orientationMenu->Append( wxPL_Orientation_0, wxT( "0 deg." ), wxT( "Orientation 0 deg." ) ); orientationMenu->Append( wxPL_Orientation_90, wxT( "90 deg." ), wxT( "Orientation 90 deg." ) ); orientationMenu->Append( wxPL_Orientation_180, wxT( "180 deg." ), wxT( "Orientation 180 deg." ) ); orientationMenu->Append( wxPL_Orientation_270, wxT( "270 deg." ), wxT( "Orientation 270 deg." ) ); wxMenu* plotMenu = new wxMenu; plotMenu->Append( wxPL_Locate, wxT( "Locate\tL" ), wxT( "Enter locate mode" ) ); // only add the orientation menu for hershey text processing if ( !pls->dev_text ) { #if ( wxMAJOR_VERSION <= 2 ) & ( wxMINOR_VERSION <= 6 ) plotMenu->Append( -1, wxT( "Set Orientation to..." ), orientationMenu, wxT( "Set the Orientation of the plot!" ) ); #else plotMenu->AppendSubMenu( orientationMenu, wxT( "Set Orientation to..." ), wxT( "Set the Orientation of the plot!" ) ); #endif } wxMenuBar* menuBar = new wxMenuBar(); menuBar->Append( fileMenu, wxT( "&File" ) ); menuBar->Append( plotMenu, wxT( "&Plot" ) ); SetMenuBar( menuBar ); SetIcon( wxIcon( graph ) ); } //-------------------------------------------------------------------------- // void wxPLplotFrame::OnMenu( wxCommandEvent& event ) // // Event method, which is called if user //-------------------------------------------------------------------------- void wxPLplotFrame::OnMenu( wxCommandEvent& event ) { // Log_Verbose( "wxPLplotFrame::OnMenu" ); switch ( event.GetId() ) { case wxID_EXIT: m_dev->exit = true; wxPLGetApp().ExitMainLoop(); break; case wxPL_Orientation_0: case wxPL_Orientation_90: case wxPL_Orientation_180: case wxPL_Orientation_270: m_window->SetOrientation( event.GetId() - wxPL_Orientation_0 ); break; case wxPL_Locate: if ( m_dev->locate_mode ) { if ( m_dev->locate_mode == LOCATE_INVOKED_VIA_API ) wxPLGetApp().SetAdvanceFlag(); m_dev->locate_mode = 0; m_dev->draw_xhair = false; } else { m_dev->locate_mode = LOCATE_INVOKED_VIA_DRIVER; m_dev->draw_xhair = true; } break; } size_t index = event.GetId() - wxPL_Save; if ( ( event.GetId() >= wxPL_Save ) && ( index < sizeof ( dev_entries ) / sizeof ( dev_entry ) ) ) { int width = 800; int height = 600; bool proceed = false; // ask for geometry in pixels only for image devices if ( dev_entries[index].pixelDevice ) { wxGetSizeDialog sizeDialog( this, -1, wxT( "Size of plot" ), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER, width, height ); if ( sizeDialog.ShowModal() == wxID_OK ) { width = sizeDialog.getWidth(); height = sizeDialog.getHeight(); proceed = true; } } else proceed = true; if ( proceed ) { wxFileDialog dialog( this, wxT( "Save plot as " ) + dev_entries[index].dev_name, wxT( "" ), wxT( "" ), dev_entries[index].dev_file_app + wxT( "|All Files (*.*)|*.*" ), #if ( wxMAJOR_VERSION <= 2 ) & ( wxMINOR_VERSION <= 6 ) wxSAVE | wxOVERWRITE_PROMPT ); #else wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); #endif if ( dialog.ShowModal() == wxID_OK ) { const wxCharBuffer buf1 = dialog.GetPath().mb_str(); const wxCharBuffer buf2 = dev_entries[index].dev_name.mb_str(); SavePlot( (const char *) buf1, (const char *) buf2, width, height ); } } } } //-------------------------------------------------------------------------- // void wxPLplotFrame::OnClose( wxCloseEvent& event ) // // Event method, which is called if user //-------------------------------------------------------------------------- void wxPLplotFrame::OnClose( wxCloseEvent& /* event */ ) { // Log_Verbose( "wxPLplotFrame::OnClose" ); m_dev->exit = true; wxPLGetApp().ExitMainLoop(); } //-------------------------------------------------------------------------- // bool wxPLplotFrame::SavePlot( const char* filename, cost char* dev, int width, // int height ) // // This function saves the current plot to a file (filename) using a // device (devname) with given width and height. There is no test if // the device really exists. //-------------------------------------------------------------------------- bool wxPLplotFrame::SavePlot( const char* filename, const char* devname, int width, int height ) { int pls, pls_save; if ( !strcmp( devname, "wxbmp" ) || !strcmp( devname, "wxpng" ) || !strcmp( devname, "wxpcx" ) || !strcmp( devname, "wxjpeg" ) || !strcmp( devname, "wxtiff" ) || !strcmp( devname, "wxpnm" ) ) { wxMemoryDC memDC; wxBitmap bitmap( width, height, -1 ); memDC.SelectObject( bitmap ); plgstrm( &pls ); plmkstrm( &pls_save ); plsdev( "wxwidgets" ); plspage( 0.0, 0.0, width, height, 0, 0 ); plsetopt( "-drvopt", "backend=0" ); plinit(); pl_cmd( PLESC_DEVINIT, (void *) &memDC ); plcpstrm( pls, 0 ); pladv( 0 ); plreplot(); plend1(); plsstrm( pls ); wxBitmapType type; if ( !strcmp( devname, "wxbmp" ) ) type = wxBITMAP_TYPE_BMP; #if wxUSE_LIBPNG else if ( !strcmp( devname, "wxpng" ) ) type = wxBITMAP_TYPE_PNG; #endif #if wxUSE_PCX else if ( !strcmp( devname, "wxpcx" ) ) type = wxBITMAP_TYPE_PCX; #endif #if wxUSE_LIBJPEG else if ( !strcmp( devname, "wxjpeg" ) ) type = wxBITMAP_TYPE_JPEG; #endif #if wxUSE_LIBTIFF else if ( !strcmp( devname, "wxtiff" ) ) type = wxBITMAP_TYPE_TIF; #endif #if wxUSE_PNM else if ( !strcmp( devname, "wxpnm" ) ) type = wxBITMAP_TYPE_PNM; #endif else type = wxBITMAP_TYPE_BMP; bool status = bitmap.SaveFile( wxString( filename, *wxConvCurrent ), type ); if ( !status ) { char buf[512]; snprintf( buf, 512, "File %s couldn't be saved", filename ); plabort( buf ); return false; } } else { plgstrm( &pls ); plmkstrm( &pls_save ); plsdev( devname ); //plspage( 0., 0., width, height, 0, 0 ); plsfnam( filename ); plcpstrm( pls, 0 ); pladv( 0 ); plreplot(); plend1(); plsstrm( pls ); } return true; } //-------------------------------------------------------------------------- // wxPLplotWindow::wxPLplotWindow( const wxString& title ) // // Constructor of wxPLplotFrame, where we create the menu. //-------------------------------------------------------------------------- wxPLplotWindow::wxPLplotWindow( wxWindow* parent, PLStream *pls ) : wxWindow( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER | wxWANTS_CHARS | wxCLIP_CHILDREN ) { // Log_Verbose( "wxPLplotWindow::wxPLplotWindow" ); m_pls = pls; m_dev = (wxPLDevBase *) pls->dev; refresh = false; mouse_x = old_mouse_x = -1; mouse_y = old_mouse_y = -1; xhair_drawn = false; SetBackgroundStyle( wxBG_STYLE_CUSTOM ); } //-------------------------------------------------------------------------- // void wxPLplotWindow::OnPaint( wxPaintEvent& WXUNUSED(event) ) // // Event method where the plots are actually drawn. Since the plots // are already drawn into bitmaps, which just copy them into to client // area. This method is also called, if (part) of the client area was // invalidated and a refresh is necessary. //-------------------------------------------------------------------------- void wxPLplotWindow::OnPaint( wxPaintEvent& WXUNUSED( event ) ) { // Log_Verbose( "wxPLplotWindow::OnPaint" ); // copy bitmap into client area wxPaintDC dc( this ); // only update damaged regions int vX, vY, vW, vH; wxRegionIterator upd( GetUpdateRegion() ); // remove the xhair before updating if ( m_dev->draw_xhair && upd && xhair_drawn ) { dc.SetLogicalFunction( wxINVERT ); dc.CrossHair( old_mouse_x, old_mouse_y ); dc.SetLogicalFunction( wxCOPY ); xhair_drawn = false; old_mouse_x = old_mouse_y = -1; } while ( upd ) { vX = upd.GetX(); vY = upd.GetY(); vW = upd.GetW(); vH = upd.GetH(); //printf( "Clipping region: x=%d, y=%d, width=%d, height=%d, counter=%d\n", vX, vY, vW, vH, counter++ ); m_dev->BlitRectangle( &dc, vX, vY, vW, vH ); upd++; } if ( m_dev->draw_xhair && !xhair_drawn ) { dc.SetLogicalFunction( wxINVERT ); dc.CrossHair( mouse_x, mouse_y ); dc.SetLogicalFunction( wxCOPY ); old_mouse_x = mouse_x; old_mouse_y = mouse_y; xhair_drawn = true; } } //-------------------------------------------------------------------------- // void wxPLplotWindow::OnChar( wxKeyEvent& event ) // // Handle key events. //-------------------------------------------------------------------------- void wxPLplotWindow::OnChar( wxKeyEvent& event ) { // Log_Verbose( "wxPLplotWindow::OnChar" ); PLGraphicsIn *gin = &( m_dev->gin ); int width, height; GetClientSize( &width, &height ); gin->pX = mouse_x; gin->pY = mouse_y; gin->dX = (PLFLT) mouse_x / ( width - 1 ); gin->dY = 1.0 - (PLFLT) mouse_y / ( height - 1 ); // gin->state = keyEvent->state; int keycode = event.GetKeyCode(); gin->string[0] = (char) keycode; gin->string[1] = '\0'; // ESCAPE, RETURN, etc. are already in ASCII equivalent gin->keysym = keycode; if ( m_dev->locate_mode ) { // End locate mode on if ( gin->keysym == PLK_Escape ) { if ( m_dev->locate_mode == LOCATE_INVOKED_VIA_API ) wxPLGetApp().SetAdvanceFlag(); m_dev->locate_mode = 0; m_dev->draw_xhair = false; DrawCrosshair(); plGinInit( gin ); } Locate(); } else { // Call user keypress event handler. Since this is called first, the user // can disable all internal event handling by setting gin.keysym to 0. if ( m_pls->KeyEH != NULL ) { int advance = 0; ( *m_pls->KeyEH )( gin, m_pls->KeyEH_data, &advance ); if ( advance ) wxPLGetApp().SetAdvanceFlag(); } switch ( gin->keysym ) { case 'L': m_dev->locate_mode = LOCATE_INVOKED_VIA_DRIVER; m_dev->draw_xhair = true; DrawCrosshair(); break; case 'Q': case PLK_Escape: m_dev->exit = true; wxPLGetApp().SetExitFlag(); break; case PLK_Return: case WXK_SPACE: case WXK_RIGHT: wxPLGetApp().SetAdvanceFlag(); break; default: break; } } event.Skip(); } //-------------------------------------------------------------------------- // void wxPLplotWindow::OnIdle( wxIdleEvent& WXUNUSED(event) ) // // If there is no pending event, maybe the canvas needs to be refreshed. //-------------------------------------------------------------------------- void wxPLplotWindow::OnIdle( wxIdleEvent& WXUNUSED( event ) ) { // Log_Verbose( "wxPLplotWindow::OnIdle" ); if ( refresh ) { if ( !m_dev->newclipregion ) { static wxRect rect; rect.x = m_dev->clipminx; rect.y = m_dev->clipminy; rect.width = m_dev->clipmaxx - m_dev->clipminx + 1; rect.height = m_dev->clipmaxy - m_dev->clipminy + 1; #if ( wxMAJOR_VERSION <= 2 ) & ( wxMINOR_VERSION <= 5 ) RefreshRect( rect ); #else RefreshRect( rect, false ); // don't erase background #endif m_dev->newclipregion = true; m_dev->clipminx = m_dev->width; m_dev->clipmaxx = 0; m_dev->clipminy = m_dev->height; m_dev->clipmaxy = 0; } else Refresh( false ); refresh = false; } } //-------------------------------------------------------------------------- // void wxPLplotWindow::OnErase( wxEraseEvent &WXUNUSED(event) ) // // Do nothing here to prevent flickering. //-------------------------------------------------------------------------- void wxPLplotWindow::OnErase( wxEraseEvent &WXUNUSED( event ) ) { // Log_Verbose( "wxPLplotWindow::OnErase" ); } //-------------------------------------------------------------------------- // void wxPLplotWindow::OnSize( wxSizeEvent & WXUNUSED(event) ) // // Allocate a bigger bitmap if necessary and redo the plot if the // window size was changed. //-------------------------------------------------------------------------- void wxPLplotWindow::OnSize( wxSizeEvent & WXUNUSED( event ) ) { // Log_Verbose( "wxPLplotWindow::OnSize" ); int width, height; GetClientSize( &width, &height ); if ( m_dev->waiting ) { if ( ( width != m_dev->width ) || ( height != m_dev->height ) ) { // get a new bitmap if new size is bigger as bitmap size if ( ( width > m_dev->bm_width ) || ( height > m_dev->bm_height ) ) { m_dev->bm_width = m_dev->bm_width > width ? m_dev->bm_width : width; m_dev->bm_height = m_dev->bm_height > height ? m_dev->bm_height : height; } wx_set_size( m_pls, width, height ); m_dev->resizing = true; plRemakePlot( m_pls ); m_dev->resizing = false; Refresh(); } } } //-------------------------------------------------------------------------- // wxPLplotWindow::OnMaximize( wxMaximizeEvent & WXUNUSED(event) ) // // Add a size event if the Window is maximized. //-------------------------------------------------------------------------- void wxPLplotWindow::OnMaximize( wxMaximizeEvent & WXUNUSED( event ) ) { // Log_Verbose( "wxPLplotWindow::OnMax" ); wxSizeEvent event( GetClientSize() ); AddPendingEvent( event ); } //-------------------------------------------------------------------------- // void wxPLplotWindow::OnMouse( wxMouseEvent &event ) // // Handle mouse events. //-------------------------------------------------------------------------- void wxPLplotWindow::OnMouse( wxMouseEvent &event ) { // Log_Verbose( "wxPLplotWindow::OnMouse" ); wxPoint pos( event.GetPosition() ); mouse_x = pos.x; mouse_y = pos.y; if ( event.ButtonDown() ) { PLGraphicsIn *gin = &( m_dev->gin ); int width, height; GetClientSize( &width, &height ); gin->pX = mouse_x; gin->pY = mouse_y; gin->dX = (PLFLT) mouse_x / ( width - 1 ); gin->dY = 1.0 - (PLFLT) mouse_y / ( height - 1 ); if ( event.LeftDown() ) { gin->button = 1; // X11/X.h: #define Button1 1 gin->state = 1 << 8; // X11/X.h: #define Button1Mask (1<<8) } else if ( event.MiddleDown() ) { gin->button = 2; // X11/X.h: #define Button2 2 gin->state = 1 << 9; // X11/X.h: #define Button2Mask (1<<9) } else if ( event.RightDown() ) { gin->button = 3; // X11/X.h: #define Button3 3 gin->state = 1 << 10; // X11/X.h: #define Button3Mask (1<<10) } gin->keysym = 0x20; // keysym for button event from xwin.c if ( m_dev->locate_mode ) Locate(); else { // Call user event handler. Since this is called first, the user can // disable all PLplot internal event handling by setting gin->button to 0. if ( m_pls->ButtonEH != NULL ) { int advance = 0; ( *m_pls->ButtonEH )( gin, m_pls->ButtonEH_data, &advance ); if ( advance ) wxPLGetApp().SetAdvanceFlag(); } // Handle internal events switch ( gin->button ) { case 3: // on right mouse button advance wxPLGetApp().SetAdvanceFlag(); break; default: break; } } } DrawCrosshair(); } //-------------------------------------------------------------------------- // void wxPLplotWindow::Locate( void ) // // Take care of Locate mode, called by OnChar() and OnMouse(). //-------------------------------------------------------------------------- void wxPLplotWindow::Locate( void ) { // Log_Verbose( "wxPLplotWindow::Locate" ); PLGraphicsIn *gin = &( m_dev->gin ); // Some event (key, button) occured, and if the locate mode // was initiated by the API we need to return back to the // user program if ( m_dev->locate_mode == LOCATE_INVOKED_VIA_API ) wxPLGetApp().SetAdvanceFlag(); // Call user locate mode handler if provided if ( m_pls->LocateEH != NULL ) { int locate_mode = m_dev->locate_mode; ( *m_pls->LocateEH )( gin, m_pls->LocateEH_data, &locate_mode ); if ( !locate_mode ) { m_dev->locate_mode = 0; m_dev->draw_xhair = false; } } else { if ( plTranslateCursor( gin ) ) { // If invoked by the API, we're done // Otherwise send report to stdout if ( m_dev->locate_mode == LOCATE_INVOKED_VIA_DRIVER ) { if ( gin->keysym < 0xFF && isprint( gin->keysym ) ) printf( "%f %f %c\n", gin->wX, gin->wY, gin->keysym ); else printf( "%f %f 0x%02x\n", gin->wX, gin->wY, gin->keysym ); } } else { // Selected point is out of bounds, so end locate mode m_dev->locate_mode = 0; m_dev->draw_xhair = false; } } DrawCrosshair(); } //-------------------------------------------------------------------------- // void wxPLplotWindow::DrawCrosshair() // // Draw a cross hair (inverted lines). //-------------------------------------------------------------------------- void wxPLplotWindow::DrawCrosshair() { // draw cross hair wxClientDC dc( this ); if ( m_dev->draw_xhair ) { if ( ( mouse_x != old_mouse_x ) || ( mouse_y != old_mouse_y ) ) { dc.SetLogicalFunction( wxINVERT ); if ( xhair_drawn ) dc.CrossHair( old_mouse_x, old_mouse_y ); dc.CrossHair( mouse_x, mouse_y ); dc.SetLogicalFunction( wxCOPY ); old_mouse_x = mouse_x; old_mouse_y = mouse_y; xhair_drawn = true; } } else { if ( xhair_drawn ) { dc.SetLogicalFunction( wxINVERT ); dc.CrossHair( old_mouse_x, old_mouse_y ); dc.SetLogicalFunction( wxCOPY ); xhair_drawn = false; old_mouse_x = old_mouse_y = -1; } } } //-------------------------------------------------------------------------- // void wxPLplotWindow::SetOrientation( int rot ) // // Set the orientation of the plot. //-------------------------------------------------------------------------- void wxPLplotWindow::SetOrientation( int rot ) { PLINT bgr, bgg, bgb; // red, green, blue //plsstrm( m_pls ); plsdiori( rot ); m_dev->resizing = true; plgcolbg( &bgr, &bgg, &bgb ); // get background color information m_dev->ClearBackground( bgr, bgg, bgb ); plRemakePlot( m_pls ); m_dev->resizing = false; Refresh(); } //-------------------------------------------------------------------------- // wxGetSizeDialog::wxGetSizeDialog( wxWindow *parent, ... ) // // Constructor of GetSizeDialog. //-------------------------------------------------------------------------- wxGetSizeDialog::wxGetSizeDialog( wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &position, const wxSize& size, long style, int width, int height ) : wxDialog( parent, id, title, position, size, style ) { wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL ); wxStaticBoxSizer *staticSizer = new wxStaticBoxSizer( new wxStaticBox( this, -1, wxT( "Size of plot" ) ), wxVERTICAL ); wxFlexGridSizer *flexSizer = new wxFlexGridSizer( 2, 0, 0 ); flexSizer->AddGrowableCol( 1 ); wxStaticText *textWidth = new wxStaticText( this, -1, wxT( "Width [pixels]:" ), wxDefaultPosition, wxDefaultSize, 0 ); flexSizer->Add( textWidth, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxALL, 5 ); spinControlWidth = new wxSpinCtrl( this, -1, wxString::Format( wxT( "%d" ), width ), wxDefaultPosition, wxSize( 100, -1 ), wxSP_ARROW_KEYS, 10, 4096, width ); flexSizer->Add( spinControlWidth, 0, wxGROW | wxALIGN_CENTER_VERTICAL | wxALL, 5 ); wxStaticText *textHeight = new wxStaticText( this, -1, wxT( "Height [pixels]:" ), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT ); flexSizer->Add( textHeight, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxALL, 5 ); spinControlHeight = new wxSpinCtrl( this, -1, wxString::Format( wxT( "%d" ), height ), wxDefaultPosition, wxSize( 100, -1 ), wxSP_ARROW_KEYS, 10, 4096, height ); flexSizer->Add( spinControlHeight, 0, wxGROW | wxALIGN_CENTER_VERTICAL | wxALL, 5 ); staticSizer->Add( flexSizer, 0, wxGROW | wxALIGN_CENTER_VERTICAL | wxALL, 5 ); sizer->Add( staticSizer, 0, wxGROW | wxALIGN_CENTER_VERTICAL | wxALL, 5 ); wxBoxSizer *buttonSizer = new wxBoxSizer( wxHORIZONTAL ); wxButton *buttonOK = new wxButton( this, wxID_OK, wxT( "OK" ), wxDefaultPosition, wxDefaultSize, 0 ); buttonSizer->Add( buttonOK, 0, wxALIGN_CENTER | wxALL | wxEXPAND, 5 ); buttonSizer->Add( 20, 20, 1, wxALIGN_CENTER | wxALL, 5 ); wxButton *buttonCancel = new wxButton( this, wxID_CANCEL, wxT( "Cancel" ), wxDefaultPosition, wxDefaultSize, 0 ); buttonSizer->Add( buttonCancel, 0, wxALIGN_CENTER | wxALL | wxEXPAND, 5 ); sizer->Add( buttonSizer, 0, wxGROW | wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 15 ); this->SetSizer( sizer ); sizer->SetSizeHints( this ); } #endif // PLD_wxwidgets plplot-5.13.0/drivers/deprecated_wxwidgets_gc.cpp000644 001752 001752 00000044630 13150160115 023704 0ustar00softwaresoftware000000 000000 // Copyright (C) 2008 Werner Smekal // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // TODO: // - text clipping // - implement AddToClipRegion for text correctly // // wxwidgets headers #include #include "plDevs.h" // plplot headers #include "plplotP.h" // std and driver headers #include "deprecated_wxwidgets.h" #include #include // only compile code if wxGraphicsContext available #if wxUSE_GRAPHICS_CONTEXT wxPLDevGC::wxPLDevGC( void ) : wxPLDevBase( wxBACKEND_GC ) { // Log_Verbose( "%s", __FUNCTION__ ); m_dc = NULL; m_bitmap = NULL; m_context = NULL; m_font = NULL; underlined = false; } wxPLDevGC::~wxPLDevGC() { // Log_Verbose( "%s", __FUNCTION__ ); if ( ownGUI ) { if ( m_dc ) { ( (wxMemoryDC *) m_dc )->SelectObject( wxNullBitmap ); delete m_dc; } if ( m_bitmap ) delete m_bitmap; } delete m_font; delete m_context; } void wxPLDevGC::DrawLine( short x1a, short y1a, short x2a, short y2a ) { // Log_Verbose( "%s", __FUNCTION__ ); wxDouble x1 = x1a / scalex; wxDouble y1 = height - y1a / scaley; wxDouble x2 = x2a / scalex; wxDouble y2 = height - y2a / scaley; wxGraphicsPath path = m_context->CreatePath(); path.MoveToPoint( x1, y1 ); path.AddLineToPoint( x2, y2 ); m_context->StrokePath( path ); AddtoClipRegion( (int) x1, (int) y1, (int) x2, (int) y2 ); } void wxPLDevGC::DrawPolyline( short *xa, short *ya, PLINT npts ) { // Log_Verbose( "%s", __FUNCTION__ ); wxGraphicsPath path = m_context->CreatePath(); path.MoveToPoint( xa[0] / scalex, height - ya[0] / scaley ); for ( PLINT i = 1; i < npts; i++ ) path.AddLineToPoint( xa[i] / scalex, height - ya[i] / scaley ); m_context->StrokePath( path ); wxDouble x, y, w, h; path.GetBox( &x, &y, &w, &h ); AddtoClipRegion( (int) x, (int) y, (int) ( x + w ), (int) ( y + h ) ); } void wxPLDevGC::ClearBackground( PLINT bgr, PLINT bgg, PLINT bgb, PLINT x1, PLINT y1, PLINT x2, PLINT y2 ) { // Log_Verbose( "%s", __FUNCTION__ ); wxDouble x1a, y1a, x2a, y2a; if ( x1 < 0 ) x1a = 0; else x1a = x1 / scalex; if ( y1 < 0 ) y1a = 0; else y1a = height - y1 / scaley; if ( x2 < 0 ) x2a = width; else x2a = x2 / scalex; if ( y2 < 0 ) y2a = height; else y2a = height - y2 / scaley; m_context->SetPen( *( wxThePenList->FindOrCreatePen( wxColour( bgr, bgg, bgb ), 1, wxSOLID ) ) ); m_context->SetBrush( wxBrush( wxColour( bgr, bgg, bgb ) ) ); m_context->DrawRectangle( x1a, y1a, x2a - x1a, y2a - y1a ); m_context->SetPen( *( wxThePenList->FindOrCreatePen( wxColour( mColorRedStroke, mColorGreenStroke, mColorBlueStroke, mStrokeOpacity ), 1, wxSOLID ) ) ); m_context->SetBrush( wxBrush( wxColour( mColorRedFill, mColorGreenFill, mColorBlueFill, mStrokeOpacity ) ) ); AddtoClipRegion( (int) x1a, (int) y1a, (int) x2a, (int) y2a ); } void wxPLDevGC::FillPolygon( PLStream *pls ) { // Log_Verbose( "%s", __FUNCTION__ ); bool isRect = false; short* x = pls->dev_x; short* y = pls->dev_y; if ( pls->dev_npts == 4 ) // Check if it's a rectangle. If so, it can be made faster to display { if ( x[0] == x[1] && x[2] == x[3] && y[0] == y[3] && y[1] == y[2] ) isRect = true; else if ( x[0] == x[3] && x[1] == x[2] && y[0] == y[1] && y[2] == y[3] ) isRect = true; } if ( pls->dev_npts == 5 ) { if ( x[0] == x[4] && y[0] == y[4] ) { if ( x[0] == x[1] && x[2] == x[3] && y[0] == y[3] && y[1] == y[2] ) isRect = true; else if ( x[0] == x[3] && x[1] == x[2] && y[0] == y[1] && y[2] == y[3] ) isRect = true; } } if ( isRect ) //isRect) { { double x1, y1, x2, y2, x0, y0, w, h; x1 = x[0] / scalex; x2 = x[2] / scalex; y1 = height - y[0] / scaley; y2 = height - y[2] / scaley; if ( x1 < x2 ) { x0 = x1; w = x2 - x1; } else { x0 = x2; w = x1 - x2; } if ( y1 < y2 ) { y0 = y1; h = y2 - y1; } else { y0 = y2; h = y1 - y2; } m_context->DrawRectangle( x0, y0, w, h ); AddtoClipRegion( (int) x0, (int) y0, (int) w, (int) h ); } else { wxGraphicsPath path = m_context->CreatePath(); path.MoveToPoint( x[0] / scalex, height - y[0] / scaley ); for ( int i = 1; i < pls->dev_npts; i++ ) path.AddLineToPoint( x[i] / scalex, height - y[i] / scaley ); path.CloseSubpath(); if ( pls->dev_eofill ) m_context->DrawPath( path, wxODDEVEN_RULE ); else m_context->DrawPath( path, wxWINDING_RULE ); wxDouble x, y, w, h; path.GetBox( &x, &y, &w, &h ); AddtoClipRegion( (int) x, (int) y, (int) ( x + w ), (int) ( y + h ) ); } } void wxPLDevGC::BlitRectangle( wxDC* dc, int vX, int vY, int vW, int vH ) { // Log_Verbose( "%s", __FUNCTION__ ); if ( m_dc ) dc->Blit( vX, vY, vW, vH, m_dc, vX, vY ); } void wxPLDevGC::CreateCanvas() { // Log_Verbose( "%s", __FUNCTION__ ); if ( ownGUI ) { if ( !m_dc ) m_dc = new wxMemoryDC(); ( (wxMemoryDC *) m_dc )->SelectObject( wxNullBitmap ); // deselect bitmap if ( m_bitmap ) delete m_bitmap; m_bitmap = new wxBitmap( bm_width, bm_height, 32 ); ( (wxMemoryDC *) m_dc )->SelectObject( *m_bitmap ); // select new bitmap } if ( m_dc ) { delete m_context; m_context = wxGraphicsContext::Create( *( (wxMemoryDC *) m_dc ) ); } } void wxPLDevGC::SetWidth( PLStream *pls ) { // Log_Verbose( "%s", __FUNCTION__ ); m_context->SetPen( *( wxThePenList->FindOrCreatePen( wxColour( mColorRedStroke, mColorGreenStroke, mColorBlueStroke, mStrokeOpacity ), pls->width > 0 ? pls->width : 1, wxSOLID ) ) ); } void wxPLDevGC::SetColor0( PLStream *pls ) { // Log_Verbose( "%s", __FUNCTION__ ); mColorRedStroke = pls->curcolor.r; mColorGreenStroke = pls->curcolor.g; mColorBlueStroke = pls->curcolor.b; mColorRedFill = pls->curcolor.r; mColorGreenFill = pls->curcolor.g; mColorBlueFill = pls->curcolor.b; mStrokeOpacity = (unsigned char) ( pls->curcolor.a * 255 ); m_context->SetPen( *( wxThePenList->FindOrCreatePen( wxColour( mColorRedStroke, mColorGreenStroke, mColorBlueStroke, mStrokeOpacity ), pls->width > 0 ? pls->width : 1, wxSOLID ) ) ); m_context->SetBrush( wxBrush( wxColour( mColorRedFill, mColorGreenFill, mColorBlueFill, mStrokeOpacity ) ) ); } void wxPLDevGC::SetColor1( PLStream *pls ) { // Log_Verbose( "%s", __FUNCTION__ ); mColorRedStroke = pls->curcolor.r; mColorGreenStroke = pls->curcolor.g; mColorBlueStroke = pls->curcolor.b; mColorRedFill = pls->curcolor.r; mColorGreenFill = pls->curcolor.g; mColorBlueFill = pls->curcolor.b; mStrokeOpacity = (unsigned char) ( pls->curcolor.a * 255 ); m_context->SetPen( *( wxThePenList->FindOrCreatePen( wxColour( mColorRedStroke, mColorGreenStroke, mColorBlueStroke, mStrokeOpacity ), pls->width > 0 ? pls->width : 1, wxSOLID ) ) ); m_context->SetBrush( wxBrush( wxColour( mColorRedFill, mColorGreenFill, mColorBlueFill, mStrokeOpacity ) ) ); } //-------------------------------------------------------------------------- // void wx_set_dc( PLStream* pls, wxDC* dc ) // // Adds a dc to the stream. The associated device is attached to the canvas // as the property "dev". //-------------------------------------------------------------------------- void wxPLDevGC::SetExternalBuffer( void* dc ) { // Log_Verbose( "%s", __FUNCTION__ ); m_dc = (wxDC *) dc; // Add the dc to the device m_context = wxGraphicsContext::Create( *( (wxMemoryDC *) m_dc ) ); ready = true; ownGUI = false; } #ifdef PL_HAVE_FREETYPE void wxPLDevGC::PutPixel( short x, short y, PLINT color ) { // Log_Verbose( "%s", __FUNCTION__ ); const wxPen oldpen = m_dc->GetPen(); m_context->SetPen( *( wxThePenList->FindOrCreatePen( wxColour( GetRValue( color ), GetGValue( color ), GetBValue( color ) ), 1, wxSOLID ) ) ); //m_context->DrawPoint( x, y ); AddtoClipRegion( x, y, x, y ); m_context->SetPen( oldpen ); } void wxPLDevGC::PutPixel( short x, short y ) { // Log_Verbose( "%s", __FUNCTION__ ); //m_dc->DrawPoint( x, y ); AddtoClipRegion( x, y, x, y ); } PLINT wxPLDevGC::GetPixel( short x, short y ) { // Log_Verbose( "%s", __FUNCTION__ ); #ifdef __WXGTK__ // Cast function parameters to (void) to silence compiler warnings about unused parameters (void) x; (void) y; // The GetPixel method is incredible slow for wxGTK. Therefore we set the colour // always to the background color, since this is the case anyway 99% of the time. PLINT bgr, bgg, bgb; // red, green, blue plgcolbg( &bgr, &bgg, &bgb ); // get background color information return RGB( bgr, bgg, bgb ); #else wxColour col; m_dc->GetPixel( x, y, &col ); return RGB( col.Red(), col.Green(), col.Blue() ); #endif } #endif // PL_HAVE_FREETYPE void wxPLDevGC::PSDrawTextToDC( char* utf8_string, bool drawText ) { // Log_Verbose( "%s", __FUNCTION__ ); wxDouble w, h, d, l; wxString str( wxConvUTF8.cMB2WC( utf8_string ), *wxConvCurrent ); w = 0; m_context->GetTextExtent( str, &w, &h, &d, &l ); if ( drawText ) { m_context->DrawText( str, 0, -yOffset / scaley ); m_context->Translate( w, 0 ); } textWidth += static_cast( w ); //keep track of the height of superscript text, the depth of subscript //text and the height of regular text if ( yOffset > 0.0001 ) { //determine the height the text would have if it were full size double currentOffset = yOffset; double currentHeight = h; while ( currentOffset > 0.0001 ) { currentOffset -= scaley * fontSize * fontScale / 2.; currentHeight *= 1.25; } textHeight = textHeight > ( currentHeight ) ? textHeight : static_cast( ( currentHeight ) ); //work out the height including superscript superscriptHeight = superscriptHeight > ( currentHeight + yOffset / scaley ) ? superscriptHeight : static_cast( ( currentHeight + yOffset / scaley ) ); } else if ( yOffset < -0.0001 ) { //determine the height the text would have if it were full size double currentOffset = yOffset; double currentHeight = h; double currentDepth = d; while ( currentOffset < -0.0001 ) { currentOffset += scaley * fontSize * fontScale * 1.25 / 2.; currentHeight *= 1.25; currentDepth *= 1.25; } textHeight = textHeight > currentHeight ? textHeight : static_cast( ( currentHeight ) ); //work out the additional depth for subscript note an assumption has been made //that the font size of (non-superscript and non-subscript) text is the same //along a line. Currently there is no escape to change font size mid string //so this should be fine subscriptDepth = subscriptDepth > ( ( -yOffset / scaley + h + d ) - ( currentDepth + textHeight ) ) ? subscriptDepth : static_cast( ( -yOffset / scaley + h + d ) - ( currentDepth + textHeight ) ); subscriptDepth = subscriptDepth > 0 ? subscriptDepth : 0; } else textHeight = textHeight > h ? textHeight : static_cast( h ); memset( utf8_string, '\0', max_string_length ); } void wxPLDevGC::PSSetFont( PLUNICODE fci ) { // Log_Verbose( "%s", __FUNCTION__ ); unsigned char fontFamily, fontStyle, fontWeight; plP_fci2hex( fci, &fontFamily, PL_FCI_FAMILY ); plP_fci2hex( fci, &fontStyle, PL_FCI_STYLE ); plP_fci2hex( fci, &fontWeight, PL_FCI_WEIGHT ); if ( m_font ) delete m_font; m_font = wxFont::New( static_cast( fontSize * fontScale ), fontFamilyLookup[fontFamily], fontStyleLookup[fontStyle] | fontWeightLookup[fontWeight] ); m_font->SetUnderlined( underlined ); m_context->SetFont( *m_font, wxColour( textRed, textGreen, textBlue ) ); } void wxPLDevGC::ProcessString( PLStream* pls, EscText* args ) { // Log_Verbose( "%s", __FUNCTION__ ); // Check that we got unicode, warning message and return if not if ( args->unicode_array_len == 0 ) { printf( "Non unicode string passed to a cairo driver, ignoring\n" ); return; } // Check that unicode string isn't longer then the max we allow if ( args->unicode_array_len >= max_string_length ) { printf( "Sorry, the wxWidgets drivers only handles strings of length < %d\n", max_string_length ); return; } // Calculate the font size (in pixels) fontSize = pls->chrht * VIRTUAL_PIXELS_PER_MM / scaley * 1.3; // Use PLplot core routine to get the corners of the clipping rectangle PLINT rcx[4], rcy[4]; difilt_clip( rcx, rcy ); #ifdef __WXOSX_COCOA__ wxPoint topLeft( width, height ), bottomRight( -1, -1 ); for ( int i = 0; i < 4; i++ ) { topLeft.x = topLeft.x > ( rcx[i] / scalex ) ? ( rcx[i] / scalex ) : topLeft.x; topLeft.y = topLeft.y > ( height - rcy[i] / scaley ) ? ( height - rcy[i] / scaley ) : topLeft.y; bottomRight.x = bottomRight.x < ( rcx[i] / scalex ) ? ( rcx[i] / scalex ) : bottomRight.x; bottomRight.y = bottomRight.y < ( height - rcy[i] / scaley ) ? ( height - rcy[i] / scaley ) : bottomRight.y; } m_context->Clip( wxRegion( topLeft.x, topLeft.y, bottomRight.x - topLeft.x, bottomRight.y - topLeft.y ) ); // m_context->Clip( wxRegion( topLeft, bottomRight) ); // this wxRegion constructor doesn't work in wxWidgets 2.9.2/Cocoa #else wxPoint cpoints[4]; for ( int i = 0; i < 4; i++ ) { cpoints[i].x = rcx[i] / scalex; cpoints[i].y = height - rcy[i] / scaley; } m_context->Clip( wxRegion( 4, cpoints ) ); #endif // text color textRed = pls->curcolor.r; textGreen = pls->curcolor.g; textBlue = pls->curcolor.b; // calculate rotation of text plRotationShear( args->xform, &rotation, &shear, &stride ); rotation -= pls->diorot * M_PI / 2.0; cos_rot = cos( rotation ); sin_rot = sin( rotation ); cos_shear = cos( shear ); sin_shear = sin( shear ); PLUNICODE *lineStart = args->unicode_array; int lineLen = 0; bool lineFeed = false; bool carriageReturn = false; wxCoord paraHeight = 0; // Get the curent font fontScale = 1.0; yOffset = 0.0; plgfci( &fci ); PSSetFont( fci ); while ( lineStart != args->unicode_array + args->unicode_array_len ) { while ( lineStart + lineLen != args->unicode_array + args->unicode_array_len && *( lineStart + lineLen ) != (PLUNICODE) '\n' ) { lineLen++; } //set line feed for the beginning of this line and //carriage return for the end lineFeed = carriageReturn; carriageReturn = lineStart + lineLen != args->unicode_array + args->unicode_array_len && *( lineStart + lineLen ) == (PLUNICODE) ( '\n' ); if ( lineFeed ) paraHeight += textHeight + subscriptDepth; //remember the text parameters so they can be restored double startingFontScale = fontScale; double startingYOffset = yOffset; PLUNICODE startingFci = fci; // determine extent of text PSDrawText( lineStart, lineLen, false ); if ( lineFeed && superscriptHeight > textHeight ) paraHeight += superscriptHeight - textHeight; // actually draw text, resetting the font first fontScale = startingFontScale; yOffset = startingYOffset; fci = startingFci; PSSetFont( fci ); m_context->PushState(); //save current position m_context->Translate( args->x / scalex, height - args->y / scaley ); //move to text starting position wxGraphicsMatrix matrix = m_context->CreateMatrix( cos_rot * stride, -sin_rot * stride, cos_rot * sin_shear + sin_rot * cos_shear, -sin_rot * sin_shear + cos_rot * cos_shear, 0.0, 0.0 ); //create rotation transformation matrix m_context->ConcatTransform( matrix ); //rotate m_context->Translate( -args->just * textWidth, -0.5 * textHeight + paraHeight * lineSpacing ); //move to set alignment PSDrawText( lineStart, lineLen, true ); //draw text m_context->PopState(); //return to original position lineStart += lineLen; if ( carriageReturn ) lineStart++; lineLen = 0; } AddtoClipRegion( 0, 0, width, height ); m_context->ResetClip(); } #endif plplot-5.13.0/drivers/mem.driver_info.in000644 001752 001752 00000000056 13150160115 021727 0ustar00softwaresoftware000000 000000 mem:User-supplied memory device:-1:mem:46:mem plplot-5.13.0/drivers/README.drivers000644 001752 001752 00000005755 13150160115 020664 0ustar00softwaresoftware000000 000000 This document is a quick overview of building and configuring drivers from the perspective of what system files have to be modified. (For actual details about how to construct a new device driver read the source for some of the drivers in plplot/drivers. For some additional details about the core paging and familying code see ../src/README.pages. Finally, there are several useful sections in the DocBook documentation [see, for example, "Output Devices" and "Driver Functions" in Chapter 3] that you should look at.) The following are some short notes on what PLplot source tree files need to be added or modified in order to add a device (which we'll call ) to a device driver (which we'll call ). The following PLplot source-tree files are affected: 1. drivers/.driver_info.in 2. cmake/modules/drivers-init.cmake 3. include/drivers.h 4. include/plDevs.h.in 5. include/plcore.h 6. plplot-test/plplot-test.sh.in 7. examples/plplot_configure.cmake_installed_examples.in 8. include/pldll.h.in 1. Add a line to drivers/.driver_info.in consisting of the following colon-separated fields: ::::: This should be the exact duplicate of the corresponding entry in the driver source code for plD_DEVICE_INFO_. 2. Add the following line to cmake/modules/drivers-init.cmake in set(DRIVERS_DEVICE_LIST... "::ON::" where that file documents how the last two fields should be set depending on the characteristics of the device you are adding. 3. Add the following line to include/drivers.h: PLDLLIMPEXP_DRIVER void plD_dispatch_init_ ( PLDispatchTable *pdt ); 4. Add the following line to include/plDevs.h.in #cmakedefine PLD_ 5. Add the following 3 lines to include/plcore.h: #if defined(PLD_) && !defined(ENABLE_DYNDRIVERS) plD_dispatch_init_, #endif 6. For each interactive and file device of the driver add the following line to plplot_test/plplot-test.sh.in: PLD_=@PLD_@ Note, that the interactive and file devices are dealt with in separate places in that script so be careful where you put the above change. 7. Add the following line to examples/plplot_configure.cmake_installed_examples.in: set(PLD_ @PLD_@) 8. For dynamic drivers (the most likely situation), add _EXPORTS to the #ifdef ENABLE_DYNDRIVERS statement of include/pldll.h.in If the driver requires additional additional files or external libraries for compiling and linking then you should add a file called .cmake to cmake/modules and add a call to this file in the file cmake/modules/drivers.cmake (include()). The file .cmake should consist of cmake instructions for finding and the files and libraries and setting the appropriate environment variables (PLD_, _COMPILE_FLAGS and _LINK_FLAGS). The code for the driver itself should be in a file called .c or .cpp in the drivers directory. plplot-5.13.0/drivers/wingcc.driver_info.in000644 001752 001752 00000000045 13150160115 022421 0ustar00softwaresoftware000000 000000 wingcc:Win32 (GCC):1:wingcc:9:wingcc plplot-5.13.0/drivers/wingcc.c000644 001752 001752 00000151212 13150160115 017733 0ustar00softwaresoftware000000 000000 // PLplot WIN32 under GCC device driver. // // Copyright (C) 2004 Andrew Roach // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // #include "plDevs.h" #ifdef PLD_wingcc #include #include #if !defined ( __CYGWIN__ ) #include #else #include #define _T( a ) __TEXT( a ) #endif #ifdef _WIN64 #define GWL_USERDATA GWLP_USERDATA #define GCL_HCURSOR GCLP_HCURSOR #endif #include "plplotP.h" #include "drivers.h" #include "plevent.h" #ifdef PL_HAVE_FREETYPE // // Freetype support has been added to the wingcc driver using the // plfreetype.c module, and implemented as a driver-specific optional extra // invoked via the -drvopt command line toggle. It uses the // "PLESC_HAS_TEXT" command for rendering within the driver. // // Freetype support is turned on/off at compile time by defining // "PL_HAVE_FREETYPE". // // To give the user some level of control over the fonts that are used, // environmental variables can be set to over-ride the definitions used by // the five default plplot fonts. // // Freetype rendering is used with the command line "-drvopt text". // Anti-aliased fonts can be used by issuing "-drvopt text,smooth" // #include "plfreetype.h" #ifndef max_number_of_grey_levels_used_in_text_smoothing #define max_number_of_grey_levels_used_in_text_smoothing 64 #endif #endif // Device info PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_wingcc = "wingcc:Win32 (GCC):1:wingcc:9:wingcc\n"; // Struct to hold device-specific info. // NOTE: This struct is being used externally by the GNU Data Language // project. They have copied the struct definition into their code // in order to access the hwnd, hdc, and waiting members. Until an // alternative method can be devised, changes to this struct should be // avoided--at a minimum new member should be placed after the waiting // member, which should avoid breaking GDL (barring a memory alignment // optimization by the compiler). typedef struct { PLFLT scale; // scaling factor to "blow up" to the "virtual" page in removing hidden lines PLINT width; // Window width (which can change) PLINT height; // Window Height PLFLT PRNT_scale; PLINT PRNT_width; PLINT PRNT_height; char FT_smooth_text; // // WIN32 API variables // COLORREF colour; // Current Colour COLORREF oldcolour; // Used for high-speed background erasing MSG msg; // A Win32 message structure. WNDCLASSEX wndclass; // An extended window class structure. HWND hwnd; // Handle for the main window. HPEN pen; // Windows pen used for drawing HDC hdc; // Driver Context HDC hdc2; // Driver Context II - used for Blitting HDC SCRN_hdc; // The screen's context HDC PRNT_hdc; // used for printing PAINTSTRUCT ps; // used to paint the client area of a window owned by that application RECT rect; // defines the coordinates of the upper-left and lower-right corners of a rectangle RECT oldrect; // used for re-sizing comparisons RECT paintrect; // used for painting etc... HBRUSH fillbrush; // brush used for fills HCURSOR cursor; // Current windows cursor for this window HBITMAP bitmap; // Bitmap of current display; used for fast redraws via blitting HGDIOBJ oldobject; // Used for tracking objects probably not really needed but HMENU PopupMenu; PLINT draw_mode; char truecolour; // Flag to indicate 24 bit mode char waiting; // Flag to indicate drawing is done, and it is waiting; // we only do a windows redraw if plplot is plotting char enterresize; // Used to keep track of reszing messages from windows char already_erased; // Used to track first and only first backgroudn erases struct wingcc_Dev *push; // A copy of the entire structure used when printing // We push and pop it off a virtual stack } wingcc_Dev; void plD_dispatch_init_wingcc( PLDispatchTable *pdt ); void plD_init_wingcc( PLStream * ); void plD_line_wingcc( PLStream *, short, short, short, short ); void plD_polyline_wingcc( PLStream *, short *, short *, PLINT ); void plD_eop_wingcc( PLStream * ); void plD_bop_wingcc( PLStream * ); void plD_tidy_wingcc( PLStream * ); void plD_wait_wingcc( PLStream * ); void plD_state_wingcc( PLStream *, PLINT ); void plD_esc_wingcc( PLStream *, PLINT, void * ); #ifdef PL_HAVE_FREETYPE static void plD_pixel_wingcc( PLStream *pls, short x, short y ); static void plD_pixelV_wingcc( PLStream *pls, short x, short y ); static PLINT plD_read_pixel_wingcc( PLStream *pls, short x, short y ); static void plD_set_pixel_wingcc( PLStream *pls, short x, short y, PLINT colour ); static void plD_set_pixelV_wingcc( PLStream *pls, short x, short y, PLINT colour ); static void init_freetype_lv1( PLStream *pls ); static void init_freetype_lv2( PLStream *pls ); #endif //-------------------------------------------------------------------------- // Local Function definitions and function-like defines //-------------------------------------------------------------------------- static int GetRegValue( TCHAR *key_name, TCHAR *key_word, char *buffer, int size ); static int SetRegValue( TCHAR *key_name, TCHAR *key_word, char *buffer, int dwType, int size ); static void Resize( PLStream *pls ); static void plD_fill_polygon_wingcc( PLStream *pls ); static void CopySCRtoBMP( PLStream *pls ); static void PrintPage( PLStream *pls ); static void UpdatePageMetrics( PLStream *pls, char flag ); #define SetRegStringValue( a, b, c ) SetRegValue( a, b, c, REG_SZ, strlen( c ) + 1 ) #define SetRegBinaryValue( a, b, c, d ) SetRegValue( a, b, (char *) c, REG_BINARY, d ) #define SetRegIntValue( a, b, c ) SetRegValue( a, b, (char *) c, REG_DWORD, 4 ) #define GetRegStringValue( a, b, c, d ) GetRegValue( a, b, c, d ) #define GetRegIntValue( a, b, c ) GetRegValue( a, b, (char *) c, 4 ) #define GetRegBinaryValue( a, b, c, d ) GetRegValue( a, b, (char *) c, d ) //-------------------------------------------------------------------------- // Some debugging macros //-------------------------------------------------------------------------- #if defined ( _MSC_VER ) #define Debug( a ) do { if ( pls->debug ) { fprintf( stderr, ( a ) ); } } while ( 0 ) #define Debug2( a, b ) do { if ( pls->debug ) { fprintf( stderr, ( a ), ( b ) ); } } while ( 0 ) #define Debug3( a, b, c ) do { if ( pls->debug ) { fprintf( stderr, ( a ), ( b ), ( c ) ); } } while ( 0 ) #elif defined ( __BORLANDC__ ) #define Debug if ( pls->debug ) printf #define Debug2 if ( pls->debug ) printf #define Debug3 if ( pls->debug ) printf #else #define Verbose( ... ) do { if ( pls->verbose ) { fprintf( stderr, __VA_ARGS__ ); } } while ( 0 ) #define Debug( ... ) do { if ( pls->debug ) { fprintf( stderr, __VA_ARGS__ ); } } while ( 0 ) #define Debug2( ... ) do { if ( pls->debug ) { fprintf( stderr, __VA_ARGS__ ); } } while ( 0 ) #define Debug3( ... ) do { if ( pls->debug ) { fprintf( stderr, __VA_ARGS__ ); } } while ( 0 ) #endif #define ReportWinError() do { \ LPVOID lpMsgBuf; \ FormatMessage( \ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, \ NULL, GetLastError(), \ MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), (LPTSTR) &lpMsgBuf, 0, NULL ); \ MessageBox( NULL, lpMsgBuf, "GetLastError", MB_OK | MB_ICONINFORMATION ); \ LocalFree( lpMsgBuf ); } while ( 0 ) #define CrossHairCursor() do { \ dev->cursor = LoadCursor( NULL, IDC_CROSS ); \ SetClassLong( dev->hwnd, GCL_HCURSOR, (long) dev->cursor ); \ SetCursor( dev->cursor ); } while ( 0 ) #define NormalCursor() do { \ dev->cursor = LoadCursor( NULL, IDC_ARROW ); \ SetClassLongPtr( dev->hwnd, GCL_HCURSOR, (LONG_PTR) dev->cursor ); \ SetCursor( dev->cursor ); } while ( 0 ) #define BusyCursor() do { \ dev->cursor = LoadCursor( NULL, IDC_WAIT ); \ SetClassLongPtr( dev->hwnd, GCL_HCURSOR, (LONG_PTR) dev->cursor ); \ SetCursor( dev->cursor ); } while ( 0 ) #define PopupPrint 0x08A1 #define PopupNextPage 0x08A2 #define PopupQuit 0x08A3 void plD_dispatch_init_wingcc( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "Win32 GCC device"; pdt->pl_DevName = "wingcc"; #endif pdt->pl_type = plDevType_Interactive; pdt->pl_seq = 9; pdt->pl_init = (plD_init_fp) plD_init_wingcc; pdt->pl_line = (plD_line_fp) plD_line_wingcc; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_wingcc; pdt->pl_eop = (plD_eop_fp) plD_eop_wingcc; pdt->pl_bop = (plD_bop_fp) plD_bop_wingcc; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_wingcc; pdt->pl_state = (plD_state_fp) plD_state_wingcc; pdt->pl_esc = (plD_esc_fp) plD_esc_wingcc; pdt->pl_wait = (plD_wait_fp) plD_wait_wingcc; } static TCHAR* szWndClass = _T( "PlplotWin" ); //-------------------------------------------------------------------------- // This is the window function for the plot window. Whenever a message is // dispatched using DispatchMessage (or sent with SendMessage) this function // gets called with the contents of the message. //-------------------------------------------------------------------------- LRESULT CALLBACK PlplotWndProc( HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) { PLStream *pls = NULL; wingcc_Dev *dev = NULL; // // The window carries a 32bit user defined pointer which points to the // plplot stream (pls). This is used for tracking the window. // Unfortunately, this is "attached" to the window AFTER it is created // so we can not initialise PLStream or wingcc_Dev "blindly" because // they may not yet have been initialised. // WM_CREATE is called before we get to initialise those variables, so // we wont try to set them. // if ( nMsg == WM_CREATE ) { return ( 0 ); } else { #ifndef _WIN64 #undef GetWindowLongPtr #define GetWindowLongPtr GetWindowLong #endif pls = (PLStream *) GetWindowLongPtr( hwnd, GWL_USERDATA ); // Try to get the address to pls for this window if ( pls ) // If we got it, then we will initialise this windows plplot private data area { dev = (wingcc_Dev *) pls->dev; } } // // Process the windows messages // // Everything except WM_CREATE is done here and it is generally hoped that // pls and dev are defined already by this stage. // That will be true MOST of the time. Some times WM_PAINT will be called // before we get to initialise the user data area of the window with the // pointer to the windows plplot stream // switch ( nMsg ) { case WM_DESTROY: if ( dev ) Debug( "WM_DESTROY\t" ); PostQuitMessage( 0 ); return ( 0 ); break; case WM_PAINT: if ( dev ) { Debug( "WM_PAINT\t" ); if ( GetUpdateRect( dev->hwnd, &dev->paintrect, TRUE ) ) { BusyCursor(); BeginPaint( dev->hwnd, &dev->ps ); if ( ( dev->waiting == 1 ) && ( dev->already_erased == 1 ) ) { Debug( "Remaking\t" ); if ( dev->ps.fErase ) { dev->oldcolour = SetBkColor( dev->hdc, RGB( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b ) ); ExtTextOut( dev->hdc, 0, 0, ETO_OPAQUE, &dev->rect, _T( "" ), 0, 0 ); SetBkColor( dev->hdc, dev->oldcolour ); } plRemakePlot( pls ); CopySCRtoBMP( pls ); dev->already_erased++; } else if ( ( dev->waiting == 1 ) && ( dev->already_erased == 2 ) ) { dev->oldobject = SelectObject( dev->hdc2, dev->bitmap ); BitBlt( dev->hdc, dev->paintrect.left, dev->paintrect.top, dev->paintrect.right, dev->paintrect.bottom, dev->hdc2, dev->paintrect.left, dev->paintrect.top, SRCCOPY ); SelectObject( dev->hdc2, dev->oldobject ); } EndPaint( dev->hwnd, &dev->ps ); NormalCursor(); return ( 0 ); } } return ( 1 ); break; case WM_SIZE: if ( dev ) { Debug( "WM_SIZE\t" ); if ( dev->enterresize == 0 ) Resize( pls ); } return ( 0 ); break; case WM_ENTERSIZEMOVE: if ( dev ) { Debug( "WM_ENTERSIZEMOVE\t" ); dev->enterresize = 1; } return ( 0 ); break; case WM_EXITSIZEMOVE: if ( dev ) { Debug( "WM_EXITSIZEMOVE\t" ); Resize( pls ); dev->enterresize = 0; // Reset the variables that track sizing ops } return ( 0 ); break; case WM_ERASEBKGND: if ( dev ) { if ( dev->already_erased == 0 ) { Debug( "WM_ERASEBKGND\t" ); // // This is a new "High Speed" way of filling in the background. // supposidely this executes faster than creating a brush and // filling a rectangle - go figure ? // dev->oldcolour = SetBkColor( dev->hdc, RGB( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b ) ); ExtTextOut( dev->hdc, 0, 0, ETO_OPAQUE, &dev->rect, _T( "" ), 0, 0 ); SetBkColor( dev->hdc, dev->oldcolour ); dev->already_erased = 1; return ( 1 ); } } return ( 0 ); break; case WM_COMMAND: if ( dev ) Debug( "WM_COMMAND\t" ); return ( 0 ); break; } // If we don't handle a message completely we hand it to the system // provided default window function. return DefWindowProc( hwnd, nMsg, wParam, lParam ); } //-------------------------------------------------------------------------- // plD_init_wingcc() // // Initialize device (terminal). //-------------------------------------------------------------------------- void plD_init_wingcc( PLStream *pls ) { wingcc_Dev *dev; #ifdef PL_HAVE_FREETYPE static int freetype = 0; static int smooth_text = 0; static int save_reg = 0; FT_Data *FT; // // Variables used for reading the registary keys // might eventually add a user defined pallette here, but for now it just does freetype // TCHAR key_name[] = _T( "Software\\PLplot\\wingcc" ); TCHAR Keyword_text[] = _T( "freetype" ); TCHAR Keyword_smooth[] = _T( "smooth" ); #endif DrvOpt wingcc_options[] = { #ifdef PL_HAVE_FREETYPE { "text", DRV_INT, &freetype, "Use driver text (FreeType)" }, { "smooth", DRV_INT, &smooth_text, "Turn text smoothing on (1) or off (0)" }, { "save", DRV_INT, &save_reg, "Save defaults to registary" }, #endif { NULL, DRV_INT, NULL, NULL } }; // // Variable for storing the program name // TCHAR *program; int programlength; // Allocate and initialize device-specific data if ( pls->dev != NULL ) free( (void *) pls->dev ); pls->dev = calloc( 1, (size_t) sizeof ( wingcc_Dev ) ); if ( pls->dev == NULL ) plexit( "plD_init_wingcc_Dev: Out of memory." ); dev = (wingcc_Dev *) pls->dev; pls->icol0 = 1; // Set a fall back pen colour in case user doesn't pls->termin = 1; // interactive device pls->graphx = GRAPHICS_MODE; // No text mode for this driver (at least for now, might add a console window if I ever figure it out and have the inclination) pls->dev_fill0 = 1; // driver can do solid area fills pls->dev_xor = 1; // driver supports xor mode pls->dev_clear = 0; // driver does not support clear - what is the proper API? pls->dev_dash = 0; // driver can not do dashed lines (yet) pls->plbuf_write = 1; // driver uses the buffer for redraws if ( !pls->colorset ) pls->color = 1; #ifdef PL_HAVE_FREETYPE // // Read registry to see if the user has set up default values // for text and smoothing. These will be overriden by anything that // might be given on the command line, so we will load the // values right into the same memory slots we pass to plParseDrvOpts // GetRegIntValue( key_name, Keyword_text, &freetype ); GetRegIntValue( key_name, Keyword_smooth, &smooth_text ); #endif // Check for and set up driver options plParseDrvOpts( wingcc_options ); #ifdef PL_HAVE_FREETYPE // // We will now save the settings to the registary if the user wants // if ( save_reg == 1 ) { SetRegIntValue( key_name, Keyword_text, &freetype ); SetRegIntValue( key_name, Keyword_smooth, &smooth_text ); } #endif // Set up device parameters if ( pls->xlength <= 0 || pls->ylength <= 0 ) { // use default width, height of 800x600 if not specifed by -geometry option // or plspage plspage( 0., 0., 800, 600, 0, 0 ); } dev->width = pls->xlength - 1; // should I use -1 or not??? dev->height = pls->ylength - 1; // // Begin initialising the window // // Initialize the entire structure to zero. memset( &dev->wndclass, 0, sizeof ( WNDCLASSEX ) ); // This class is called WinTestWin dev->wndclass.lpszClassName = szWndClass; // cbSize gives the size of the structure for extensibility. dev->wndclass.cbSize = sizeof ( WNDCLASSEX ); // All windows of this class redraw when resized. dev->wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC | CS_PARENTDC; // All windows of this class use the WndProc window function. dev->wndclass.lpfnWndProc = PlplotWndProc; // This class is used with the current program instance. dev->wndclass.hInstance = GetModuleHandle( NULL ); // Use standard application icon and arrow cursor provided by the OS dev->wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION ); dev->wndclass.hIconSm = LoadIcon( NULL, IDI_APPLICATION ); dev->wndclass.hCursor = LoadCursor( NULL, IDC_ARROW ); // Color the background white dev->wndclass.hbrBackground = NULL; dev->wndclass.cbWndExtra = sizeof ( pls ); // // Now register the window class for use. // RegisterClassEx( &dev->wndclass ); // //convert the program name to wide char if needed // #ifdef UNICODE printf( pls->program ); programlength = strlen( pls->program ) + 1; program = malloc( programlength * sizeof ( TCHAR ) ); MultiByteToWideChar( CP_UTF8, 0, pls->program, programlength, program, programlength ); #else program = pls->program; #endif // // Create our main window using that window class. // dev->hwnd = CreateWindowEx( WS_EX_WINDOWEDGE + WS_EX_LEFT, szWndClass, // Class name program, // Caption WS_OVERLAPPEDWINDOW, // Style pls->xoffset, // Initial x (use default) pls->yoffset, // Initial y (use default) pls->xlength, // Initial x size (use default) pls->ylength, // Initial y size (use default) NULL, // No parent window NULL, // No menu dev->wndclass.hInstance, // This program instance NULL // Creation parameters ); #ifdef UNICODE free( program ); #endif // // Attach a pointer to the stream to the window's user area // this pointer will be used by the windows call back for // process this window // #ifdef _WIN64 SetWindowLongPtr( dev->hwnd, GWL_USERDATA, (LONG_PTR) pls ); #else SetWindowLong( dev->hwnd, GWL_USERDATA, (LONG) pls ); #endif dev->SCRN_hdc = dev->hdc = GetDC( dev->hwnd ); // // Setup the popup menu // dev->PopupMenu = CreatePopupMenu(); AppendMenu( dev->PopupMenu, MF_STRING, PopupPrint, _T( "Print" ) ); AppendMenu( dev->PopupMenu, MF_STRING, PopupNextPage, _T( "Next Page" ) ); AppendMenu( dev->PopupMenu, MF_STRING, PopupQuit, _T( "Quit" ) ); #ifdef PL_HAVE_FREETYPE if ( freetype ) { pls->dev_text = 1; // want to draw text pls->dev_unicode = 1; // want unicode init_freetype_lv1( pls ); FT = (FT_Data *) pls->FT; FT->want_smooth_text = smooth_text; } #endif plD_state_wingcc( pls, PLSTATE_COLOR0 ); // // Display the window which we just created (using the nShow // passed by the OS, which allows for start minimized and that // sort of thing). // ShowWindow( dev->hwnd, SW_SHOWDEFAULT ); SetForegroundWindow( dev->hwnd ); // // Set up the DPI etc... // if ( pls->xdpi <= 0 ) // Get DPI from windows { plspage( GetDeviceCaps( dev->hdc, HORZRES ) / GetDeviceCaps( dev->hdc, HORZSIZE ) * 25.4, GetDeviceCaps( dev->hdc, VERTRES ) / GetDeviceCaps( dev->hdc, VERTSIZE ) * 25.4, 0, 0, 0, 0 ); } else { pls->ydpi = pls->xdpi; // Set X and Y dpi's to the same value } // // Now we have to find out, from windows, just how big our drawing area is // when we specified the page size earlier on, that includes the borders, // title bar etc... so now that windows has done all its initialisations, // we will ask how big the drawing area is, and tell plplot // GetClientRect( dev->hwnd, &dev->rect ); dev->width = dev->rect.right; dev->height = dev->rect.bottom; if ( dev->width > dev->height ) // Work out the scaling factor for the { // "virtual" (oversized) page dev->scale = (PLFLT) ( PIXELS_X - 1 ) / dev->width; } else { dev->scale = (PLFLT) PIXELS_Y / dev->height; } Debug2( "Scale = %f (FLT)\n", dev->scale ); plP_setpxl( dev->scale * pls->xdpi / 25.4, dev->scale * pls->ydpi / 25.4 ); plP_setphy( 0, (PLINT) ( dev->scale * dev->width ), 0, (PLINT) ( dev->scale * dev->height ) ); // Set fill rule. if ( pls->dev_eofill ) SetPolyFillMode( dev->hdc, ALTERNATE ); else SetPolyFillMode( dev->hdc, WINDING ); #ifdef PL_HAVE_FREETYPE if ( pls->dev_text ) { init_freetype_lv2( pls ); } #endif } //-------------------------------------------------------------------------- // plD_line_wingcc() // // Draw a line in the current color from (x1,y1) to (x2,y2). //-------------------------------------------------------------------------- void plD_line_wingcc( PLStream *pls, short x1a, short y1a, short x2a, short y2a ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; POINT points[2]; points[0].x = (LONG) ( x1a / dev->scale ); points[1].x = (LONG) ( x2a / dev->scale ); points[0].y = (LONG) ( dev->height - ( y1a / dev->scale ) ); points[1].y = (LONG) ( dev->height - ( y2a / dev->scale ) ); dev->oldobject = SelectObject( dev->hdc, dev->pen ); if ( points[0].x != points[1].x || points[0].y != points[1].y ) { Polyline( dev->hdc, points, 2 ); } else { SetPixel( dev->hdc, points[0].x, points[0].y, dev->colour ); } SelectObject( dev->hdc, dev->oldobject ); } //-------------------------------------------------------------------------- // plD_polyline_wingcc() // // Draw a polyline in the current color. //-------------------------------------------------------------------------- void plD_polyline_wingcc( PLStream *pls, short *xa, short *ya, PLINT npts ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; int i; POINT *points = NULL; if ( npts > 0 ) { points = GlobalAlloc( GMEM_ZEROINIT | GMEM_FIXED, (size_t) npts * sizeof ( POINT ) ); if ( points != NULL ) { for ( i = 0; i < npts; i++ ) { points[i].x = (LONG) ( xa[i] / dev->scale ); points[i].y = (LONG) ( dev->height - ( ya[i] / dev->scale ) ); } dev->oldobject = SelectObject( dev->hdc, dev->pen ); Polyline( dev->hdc, points, npts ); SelectObject( dev->hdc, dev->oldobject ); GlobalFree( points ); } else { plexit( "Could not allocate memory to \"plD_polyline_wingcc\"\n" ); } } } //-------------------------------------------------------------------------- // plD_fill_polygon_wingcc() // // Fill polygon described in points pls->dev_x[] and pls->dev_y[]. //-------------------------------------------------------------------------- static void plD_fill_polygon_wingcc( PLStream *pls ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; int i; POINT *points = NULL; HPEN hpen, hpenOld; if ( pls->dev_npts > 0 ) { points = GlobalAlloc( GMEM_ZEROINIT, (size_t) pls->dev_npts * sizeof ( POINT ) ); if ( points == NULL ) plexit( "Could not allocate memory to \"plD_fill_polygon_wingcc\"\n" ); for ( i = 0; i < pls->dev_npts; i++ ) { points[i].x = (PLINT) ( pls->dev_x[i] / dev->scale ); points[i].y = (PLINT) ( dev->height - ( pls->dev_y[i] / dev->scale ) ); } dev->fillbrush = CreateSolidBrush( dev->colour ); hpen = CreatePen( PS_SOLID, 1, dev->colour ); dev->oldobject = SelectObject( dev->hdc, dev->fillbrush ); hpenOld = SelectObject( dev->hdc, hpen ); Polygon( dev->hdc, points, pls->dev_npts ); SelectObject( dev->hdc, dev->oldobject ); DeleteObject( dev->fillbrush ); SelectObject( dev->hdc, hpenOld ); DeleteObject( hpen ); GlobalFree( points ); } } //-------------------------------------------------------------------------- // static void CopySCRtoBMP(PLStream *pls) // Function copies the screen contents into a bitmap which is // later used for fast redraws of the screen (when it gets corrupted) // rather than remaking the plot from the plot buffer. //-------------------------------------------------------------------------- static void CopySCRtoBMP( PLStream *pls ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; // // Clean up the old bitmap and DC // if ( dev->hdc2 != NULL ) DeleteDC( dev->hdc2 ); if ( dev->bitmap != NULL ) DeleteObject( dev->bitmap ); dev->hdc2 = CreateCompatibleDC( dev->hdc ); GetClientRect( dev->hwnd, &dev->rect ); dev->bitmap = CreateCompatibleBitmap( dev->hdc, dev->rect.right, dev->rect.bottom ); dev->oldobject = SelectObject( dev->hdc2, dev->bitmap ); BitBlt( dev->hdc2, 0, 0, dev->rect.right, dev->rect.bottom, dev->hdc, 0, 0, SRCCOPY ); SelectObject( dev->hdc2, dev->oldobject ); } void plD_eop_wingcc( PLStream *pls ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; Debug( "End of the page\n" ); CopySCRtoBMP( pls ); dev->already_erased = 2; NormalCursor(); } //-------------------------------------------------------------------------- // Beginning of the new page //-------------------------------------------------------------------------- void plD_bop_wingcc( PLStream *pls ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; #ifdef PL_HAVE_FREETYPE FT_Data *FT = (FT_Data *) pls->FT; #endif Debug( "Start of Page\t" ); // // Turn the cursor to a busy sign, clear the page by "invalidating" it // then reset the colours and pen width // BusyCursor(); dev->already_erased = 0; RedrawWindow( dev->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ERASENOW ); plD_state_wingcc( pls, PLSTATE_COLOR0 ); } void plD_tidy_wingcc( PLStream *pls ) { wingcc_Dev *dev = NULL; #ifdef PL_HAVE_FREETYPE if ( pls->dev_text ) { FT_Data *FT = (FT_Data *) pls->FT; plscmap0n( FT->ncol0_org ); plD_FreeType_Destroy( pls ); } #endif Debug( "plD_tidy_wingcc" ); if ( pls->dev != NULL ) { dev = (wingcc_Dev *) pls->dev; DeleteMenu( dev->PopupMenu, PopupPrint, 0 ); DeleteMenu( dev->PopupMenu, PopupNextPage, 0 ); DeleteMenu( dev->PopupMenu, PopupQuit, 0 ); DestroyMenu( dev->PopupMenu ); if ( dev->hdc2 != NULL ) DeleteDC( dev->hdc2 ); if ( dev->hdc != NULL ) ReleaseDC( dev->hwnd, dev->hdc ); if ( dev->bitmap != NULL ) DeleteObject( dev->bitmap ); free_mem( pls->dev ); } } void plD_wait_wingcc( PLStream * pls ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; Debug( "Wait for user input\n" ); dev->waiting = 1; while ( dev->waiting == 1 && GetMessage( &dev->msg, NULL, 0, 0 ) ) { TranslateMessage( &dev->msg ); switch ( (int) dev->msg.message ) { case WM_RBUTTONDOWN: case WM_CONTEXTMENU: TrackPopupMenu( dev->PopupMenu, TPM_CENTERALIGN | TPM_RIGHTBUTTON, LOWORD( dev->msg.lParam ), HIWORD( dev->msg.lParam ), 0, dev->hwnd, NULL ); break; case WM_CHAR: if ( ( (TCHAR) ( dev->msg.wParam ) == 32 ) || ( (TCHAR) ( dev->msg.wParam ) == 13 ) ) { dev->waiting = 0; } else if ( ( (TCHAR) ( dev->msg.wParam ) == 27 ) || ( (TCHAR) ( dev->msg.wParam ) == 'q' ) || ( (TCHAR) ( dev->msg.wParam ) == 'Q' ) ) { dev->waiting = 0; PostQuitMessage( 0 ); } break; case WM_LBUTTONDBLCLK: Debug( "WM_LBUTTONDBLCLK\t" ); dev->waiting = 0; break; case WM_COMMAND: switch ( LOWORD( dev->msg.wParam ) ) { case PopupPrint: Debug( "PopupPrint" ); PrintPage( pls ); break; case PopupNextPage: Debug( "PopupNextPage" ); dev->waiting = 0; break; case PopupQuit: Debug( "PopupQuit" ); dev->waiting = 0; PostQuitMessage( 0 ); break; } break; default: DispatchMessage( &dev->msg ); break; } } } //-------------------------------------------------------------------------- // plD_state_wingcc() // // Handle change in PLStream state (color, pen width, fill attribute, etc). //-------------------------------------------------------------------------- void plD_state_wingcc( PLStream *pls, PLINT op ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; switch ( op ) { case PLSTATE_COLOR0: case PLSTATE_COLOR1: dev->colour = RGB( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b ); break; case PLSTATE_CMAP0: case PLSTATE_CMAP1: dev->colour = RGB( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b ); break; } if ( dev->pen != NULL ) DeleteObject( dev->pen ); dev->pen = CreatePen( PS_SOLID, pls->width, dev->colour ); } //-------------------------------------------------------------------------- // GetCursorCmd() // // Handle events connected to selecting points (modelled after xwin) //-------------------------------------------------------------------------- static void GetCursorCmd( PLStream *pls, PLGraphicsIn *gin ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; HCURSOR crosshair; HCURSOR previous; plGinInit( gin ); crosshair = LoadCursor( GetModuleHandle( NULL ), IDC_CROSS ); previous = SetCursor( crosshair ); while ( gin->pX < 0 ) { GetMessage( &dev->msg, NULL, 0, 0 ); TranslateMessage( &dev->msg ); switch ( (int) dev->msg.message ) { case WM_LBUTTONDOWN: if ( dev->msg.wParam & MK_LBUTTON ) { gin->pX = dev->msg.pt.x; gin->pY = dev->msg.pt.y; gin->dX = (PLFLT) gin->pX / ( dev->width - 1 ); gin->dY = 1.0 - (PLFLT) gin->pY / ( dev->height - 1 ); gin->button = 1; // AM: there is no macro to indicate the pressed button! gin->state = 0; // AM: is there an equivalent under Windows? gin->keysym = 0x20; } break; case WM_CHAR: gin->pX = dev->msg.pt.x; gin->pY = dev->msg.pt.y; gin->dX = (PLFLT) gin->pX / ( dev->width - 1 ); gin->dY = 1.0 - (PLFLT) gin->pY / ( dev->height - 1 ); gin->button = 0; gin->state = 0; gin->keysym = dev->msg.wParam; break; } } // Restore the previous cursor SetCursor( previous ); // if ( GetCursorPos(&p) ) // { // if ( ScreenToClient( dev->hwnd, &p ) ) // { // // Fill the fields, but actually we need to run the event loop // // We need to call GetMessage() in a loop. Unclear as yet to the // // actual interface: key/button presses? // } // } } //-------------------------------------------------------------------------- // plD_esc_wingcc() // // Handle PLplot escapes //-------------------------------------------------------------------------- void plD_esc_wingcc( PLStream *pls, PLINT op, void *ptr ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; switch ( op ) { case PLESC_GETC: GetCursorCmd( pls, (PLGraphicsIn *) ptr ); break; case PLESC_FILL: plD_fill_polygon_wingcc( pls ); break; case PLESC_DOUBLEBUFFERING: break; case PLESC_XORMOD: if ( *(PLINT *) ( ptr ) == 0 ) SetROP2( dev->hdc, R2_COPYPEN ); else SetROP2( dev->hdc, R2_XORPEN ); break; #ifdef PL_HAVE_FREETYPE case PLESC_HAS_TEXT: plD_render_freetype_text( pls, (EscText *) ptr ); break; // case PLESC_LIKES_UNICODE: // plD_render_freetype_sym(pls, (EscText *)ptr); // break; #endif } } //-------------------------------------------------------------------------- // static void Resize( PLStream *pls ) // // This function calculates how to resize a window after a message has been // received from windows telling us the window has been changed. // It tries to recalculate the scale of the window so everything works out // just right. // The window is only resized if plplot has finished all of its plotting. // That means that if you resize while a picture is being plotted, // unpredictable results may result. The reason I do this is because the // resize function calls redraw window, which replays the whole plot. //-------------------------------------------------------------------------- static void Resize( PLStream *pls ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; #ifdef PL_HAVE_FREETYPE FT_Data *FT = (FT_Data *) pls->FT; #endif Debug( "Resizing" ); if ( dev->waiting == 1 ) // Only resize the window IF plplot has finished with it { memcpy( &dev->oldrect, &dev->rect, sizeof ( RECT ) ); GetClientRect( dev->hwnd, &dev->rect ); Debug3( "[%d %d]", dev->rect.right, dev->rect.bottom ); if ( ( dev->rect.right > 0 ) && ( dev->rect.bottom > 0 ) ) // Check to make sure it isn't just minimised (i.e. zero size) { if ( memcmp( &dev->rect, &dev->oldrect, sizeof ( RECT ) ) != 0 ) // See if the window's changed size or not { dev->already_erased = 0; dev->width = dev->rect.right; dev->height = dev->rect.bottom; if ( dev->width > dev->height ) // Work out the scaling factor for the { // "virtual" (oversized) page dev->scale = (PLFLT) ( PIXELS_X - 1 ) / dev->width; } else { dev->scale = (PLFLT) PIXELS_Y / dev->height; } #ifdef PL_HAVE_FREETYPE if ( FT ) { FT->scale = dev->scale; FT->ymax = dev->height; } #endif } RedrawWindow( dev->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ERASENOW ); } else { memcpy( &dev->rect, &dev->oldrect, sizeof ( RECT ) ); // restore the old size to current size since the window is minimised } } } //-------------------------------------------------------------------------- // int SetRegValue(char *key_name, char *key_word, char *buffer,int dwType, int size) // // Function set the registry; if registary entry does not exist, it is // created. Actually, the key is created before it is set just to make sure // that is is there ! //-------------------------------------------------------------------------- static int SetRegValue( TCHAR *key_name, TCHAR *key_word, char *buffer, int dwType, int size ) { int j = 0; DWORD lpdwDisposition; HKEY hKey; j = RegCreateKeyEx( HKEY_CURRENT_USER, key_name, 0, // reserved NULL, // address of class string REG_OPTION_NON_VOLATILE, // special options flag KEY_WRITE, // desired security access NULL, // address of key security structure &hKey, // address of buffer for opened handle &lpdwDisposition // address of disposition value buffer ); if ( j == ERROR_SUCCESS ) { RegSetValueEx( hKey, key_word, 0, dwType, buffer, size ); RegCloseKey( hKey ); } return ( j ); } //-------------------------------------------------------------------------- // int GetRegValue(char *key_name, char *key_word, char *buffer, int size) // // Function reads the registry and gets a string value from it // buffer must be allocated by the caller, and the size is given in the size // paramater. // Return code is 1 for success, and 0 for failure. //-------------------------------------------------------------------------- static int GetRegValue( TCHAR *key_name, TCHAR *key_word, char *buffer, int size ) { int ret = 0; HKEY hKey; int dwType; int dwSize = size; if ( RegOpenKeyEx( HKEY_CURRENT_USER, key_name, 0, KEY_READ, &hKey ) == ERROR_SUCCESS ) { if ( RegQueryValueEx( hKey, key_word, 0, (LPDWORD) &dwType, buffer, (LPDWORD) &dwSize ) == ERROR_SUCCESS ) { ret = 1; } RegCloseKey( hKey ); } return ( ret ); } #ifdef PL_HAVE_FREETYPE //-------------------------------------------------------------------------- // void plD_pixel_wingcc (PLStream *pls, short x, short y) // // callback function, of type "plD_pixel_fp", which specifies how a single // pixel is set in the current colour. //-------------------------------------------------------------------------- static void plD_pixel_wingcc( PLStream *pls, short x, short y ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; SetPixel( dev->hdc, x, y, dev->colour ); } static void plD_pixelV_wingcc( PLStream *pls, short x, short y ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; SetPixelV( dev->hdc, x, y, dev->colour ); } //-------------------------------------------------------------------------- // void plD_set_pixelV_wingcc (PLStream *pls, short x, short y,PLINT colour) // // callback function, of type "plD_set_pixel_fp", which specifies how a // single pixel is set in the s[ecified colour. This colour // by-passes plplot's internal table, and directly 'hits the hardware'. //-------------------------------------------------------------------------- static void plD_set_pixel_wingcc( PLStream *pls, short x, short y, PLINT colour ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; SetPixel( dev->hdc, x, y, colour ); } static void plD_set_pixelV_wingcc( PLStream *pls, short x, short y, PLINT colour ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; SetPixelV( dev->hdc, x, y, colour ); } //-------------------------------------------------------------------------- // void plD_read_pixel_wingcc (PLStream *pls, short x, short y) // // callback function, of type "plD_pixel_fp", which specifies how a single // pixel is read. //-------------------------------------------------------------------------- static PLINT plD_read_pixel_wingcc( PLStream *pls, short x, short y ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; return ( GetPixel( dev->hdc, x, y ) ); } //-------------------------------------------------------------------------- // void init_freetype_lv1 (PLStream *pls) // // "level 1" initialisation of the freetype library. // "Level 1" initialisation calls plD_FreeType_init(pls) which allocates // memory to the pls->FT structure, then sets up the pixel callback // function. //-------------------------------------------------------------------------- static void init_freetype_lv1( PLStream *pls ) { FT_Data *FT; int x; wingcc_Dev *dev = (wingcc_Dev *) pls->dev; plD_FreeType_init( pls ); FT = (FT_Data *) pls->FT; // // Work out if our device support "fast" pixel setting // and if so, use that instead of "slow" pixel setting // x = GetDeviceCaps( dev->hdc, RASTERCAPS ); if ( x & RC_BITBLT ) FT->pixel = (plD_pixel_fp) plD_pixelV_wingcc; else FT->pixel = (plD_pixel_fp) plD_pixel_wingcc; // // See if we have a 24 bit device (or better), in which case // we can use the better antialaaising. // if ( GetDeviceCaps( dev->hdc, BITSPIXEL ) > 24 ) { FT->BLENDED_ANTIALIASING = 1; FT->read_pixel = (plD_read_pixel_fp) plD_read_pixel_wingcc; if ( x & RC_BITBLT ) FT->set_pixel = (plD_set_pixel_fp) plD_set_pixelV_wingcc; else FT->set_pixel = (plD_set_pixel_fp) plD_set_pixel_wingcc; } } //-------------------------------------------------------------------------- // void init_freetype_lv2 (PLStream *pls) // // "Level 2" initialisation of the freetype library. // "Level 2" fills in a few setting that aren't public until after the // graphics sub-system has been initialised. // The "level 2" initialisation fills in a few things that are defined // later in the initialisation process for the GD driver. // // FT->scale is a scaling factor to convert co-ordinates. This is used by // the GD and other drivers to scale back a larger virtual page and this // eliminate the "hidden line removal bug". Set it to 1 if your device // doesn't have scaling. // // Some coordinate systems have zero on the bottom, others have zero on // the top. Freetype does it one way, and most everything else does it the // other. To make sure everything is working ok, we have to "flip" the // coordinates, and to do this we need to know how big in the Y dimension // the page is, and whether we have to invert the page or leave it alone. // // FT->ymax specifies the size of the page FT->invert_y=1 tells us to // invert the y-coordinates, FT->invert_y=0 will not invert the // coordinates. //-------------------------------------------------------------------------- static void init_freetype_lv2( PLStream *pls ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; FT_Data *FT = (FT_Data *) pls->FT; FT->scale = dev->scale; FT->ymax = dev->height; FT->invert_y = 1; if ( ( FT->want_smooth_text == 1 ) && ( FT->BLENDED_ANTIALIASING == 0 ) ) // do we want to at least *try* for smoothing ? { FT->ncol0_org = pls->ncol0; // save a copy of the original size of ncol0 FT->ncol0_xtra = 16777216 - ( pls->ncol1 + pls->ncol0 ); // work out how many free slots we have FT->ncol0_width = max_number_of_grey_levels_used_in_text_smoothing; // find out how many different shades of anti-aliasing we can do FT->ncol0_width = max_number_of_grey_levels_used_in_text_smoothing; // set a maximum number of shades plscmap0n( FT->ncol0_org + ( FT->ncol0_width * pls->ncol0 ) ); // redefine the size of cmap0 // the level manipulations are to turn off the plP_state(PLSTATE_CMAP0) // call in plscmap0 which (a) leads to segfaults since the GD image is // not defined at this point and (b) would be inefficient in any case since // setcmap is always called later (see plD_bop_png) to update the driver // color palette to be consistent with cmap0. { PLINT level_save; level_save = pls->level; pls->level = 0; pl_set_extended_cmap0( pls, FT->ncol0_width, FT->ncol0_org ); // call the function to add the extra cmap0 entries and calculate stuff pls->level = level_save; } FT->smooth_text = 1; // Yippee ! We had success setting up the extended cmap0 } 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 { FT->smooth_text = 1; } } #endif //-------------------------------------------------------------------------- // static void UpdatePageMetrics ( PLStream *pls, char flag ) // // UpdatePageMetrics is a simple function which simply gets new vales for // a changed DC, be it swapping from printer to screen or vice-versa. // The flag variable is used to tell the function if it is updating // from the printer (1) or screen (0). //-------------------------------------------------------------------------- static void UpdatePageMetrics( PLStream *pls, char flag ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; #ifdef PL_HAVE_FREETYPE FT_Data *FT = (FT_Data *) pls->FT; #endif if ( flag == 1 ) { dev->width = GetDeviceCaps( dev->hdc, HORZRES ); // Get the page size from the printer dev->height = GetDeviceCaps( dev->hdc, VERTRES ); } else { GetClientRect( dev->hwnd, &dev->rect ); dev->width = dev->rect.right; dev->height = dev->rect.bottom; } if ( dev->width > dev->height ) // Work out the scaling factor for the { // "virtual" (oversized) page dev->scale = (PLFLT) ( PIXELS_X - 1 ) / dev->width; } else { dev->scale = (PLFLT) PIXELS_Y / dev->height; } #ifdef PL_HAVE_FREETYPE if ( FT ) // If we are using freetype, then set it up next { FT->scale = dev->scale; FT->ymax = dev->height; if ( GetDeviceCaps( dev->hdc, RASTERCAPS ) & RC_BITBLT ) FT->pixel = (plD_pixel_fp) plD_pixelV_wingcc; else FT->pixel = (plD_pixel_fp) plD_pixel_wingcc; } #endif pls->xdpi = GetDeviceCaps( dev->hdc, HORZRES ) / GetDeviceCaps( dev->hdc, HORZSIZE ) * 25.4; pls->ydpi = GetDeviceCaps( dev->hdc, VERTRES ) / GetDeviceCaps( dev->hdc, VERTSIZE ) * 25.4; plP_setpxl( dev->scale * pls->xdpi / 25.4, dev->scale * pls->ydpi / 25.4 ); plP_setphy( 0, (PLINT) ( dev->scale * dev->width ), 0, (PLINT) ( dev->scale * dev->height ) ); } //-------------------------------------------------------------------------- // static void PrintPage ( PLStream *pls ) // // Function brings up a standard printer dialog and, after the user // has selected a printer, replots the current page to the windows // printer. //-------------------------------------------------------------------------- static void PrintPage( PLStream *pls ) { wingcc_Dev *dev = (wingcc_Dev *) pls->dev; #ifdef PL_HAVE_FREETYPE FT_Data *FT = (FT_Data *) pls->FT; #endif PRINTDLG Printer; DOCINFO docinfo; // // Reset the docinfo structure to 0 and set it's fields up // This structure is used to supply a name to the print queue // ZeroMemory( &docinfo, sizeof ( docinfo ) ); docinfo.cbSize = sizeof ( docinfo ); docinfo.lpszDocName = _T( "Plplot Page" ); // // Reset out printer structure to zero and initialise it // ZeroMemory( &Printer, sizeof ( PRINTDLG ) ); Printer.lStructSize = sizeof ( PRINTDLG ); Printer.hwndOwner = dev->hwnd; Printer.Flags = PD_NOPAGENUMS | PD_NOSELECTION | PD_RETURNDC; Printer.nCopies = 1; // // Call the printer dialog function. // If the user has clicked on "Print", then we will continue // processing and print out the page. // if ( PrintDlg( &Printer ) != 0 ) { // // Before doing anything, we will take some backup copies // of the existing values for page size and the like, because // all we are going to do is a quick and dirty modification // of plplot's internals to match the new page size and hope // it all works out ok. After that, we will manip the values, // and when all is done, restore them. // if ( ( dev->push = GlobalAlloc( GMEM_ZEROINIT, sizeof ( wingcc_Dev ) ) ) != NULL ) { BusyCursor(); memcpy( dev->push, dev, sizeof ( wingcc_Dev ) ); dev->hdc = dev->PRNT_hdc = Printer.hDC; // Copy the printer HDC UpdatePageMetrics( pls, 1 ); #ifdef PL_HAVE_FREETYPE if ( FT ) // If we are using freetype, then set it up next { dev->FT_smooth_text = FT->smooth_text; // When printing, we don't want smoothing FT->smooth_text = 0; } #endif // // Now the stuff that actually does the printing !! // StartDoc( dev->hdc, &docinfo ); plRemakePlot( pls ); EndDoc( dev->hdc ); // // Now to undo everything back to what it was for the screen // dev->hdc = dev->SCRN_hdc; // Reset the screen HDC to the default UpdatePageMetrics( pls, 0 ); #ifdef PL_HAVE_FREETYPE if ( FT ) // If we are using freetype, then set it up next { FT->smooth_text = dev->FT_smooth_text; } #endif memcpy( dev, dev->push, sizeof ( wingcc_Dev ) ); // POP our "stack" now to restore the values GlobalFree( dev->push ); NormalCursor(); RedrawWindow( dev->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ERASENOW ); } } } #else int pldummy_wingcc() { return ( 0 ); } #endif // PLD_wingccdev plplot-5.13.0/drivers/tkwin.c000644 001752 001752 00000155065 13150160115 017627 0ustar00softwaresoftware000000 000000 // PLplot Tk device driver. // // Copyright (C) 2004 Maurice LeBrun // Copyright (C) 2004 Joao Cardoso // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // This device driver is designed to be used by a PlPlotter, and in fact requires // the existence of an enclosing PlPlotter. // // The idea is that this should develop into a completely cross-platform driver // for use by the cross platform Tk system. // // #include "plDevs.h" #define DEBUG #ifdef PLD_tkwin #define NEED_PLDEBUG #include "plplotP.h" #include "pltkwd.h" #include "drivers.h" #include "plevent.h" #define _TCLINT #ifdef USE_TCL_STUBS // Unfortunately, tkInt.h ends up loading Malloc.h under Windows // So we have to deal with this mess #undef malloc #undef free #undef realloc #undef calloc #if defined ( __WIN32__ ) || defined ( MAC_TCL ) #include #else #include #endif #define malloc ckalloc #define free( m ) ckfree( (char *) m ) #define realloc ckrealloc #define calloc ckcalloc #else #if defined ( __WIN32__ ) || defined ( MAC_TCL ) #include #else #include #endif #endif #ifdef ckalloc #undef ckalloc #define ckalloc malloc #endif #ifdef ckfree #undef ckfree #define ckfree free #endif #ifdef free #undef free #endif // Device info PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_tkwin = "tkwin:New tk driver:1:tkwin:45:tkwin\n"; void * ckcalloc( size_t nmemb, size_t size ); // // We want to use the 'pure Tk' interface. On Unix we can use // some direct calls to X instead of Tk, if we want, although // that code hasn't been tested for some time. So this define // is required on Windows/MacOS and perhaps optional on Unix. // #define USE_TK #ifdef __WIN32__ #define XSynchronize( display, bool ) { display->request++; } #define XSync( display, bool ) { display->request++; } #define XFlush( display ) #endif // Dummy definition of PlPlotter containing first few fields typedef struct PlPlotter { Tk_Window tkwin; // Window that embodies the frame. NULL // means that the window has been destroyed // but the data structures haven't yet been // cleaned up. // Display *display; // Display containing widget. Used, among // other things, so that resources can be // freed even after tkwin has gone away. // Tcl_Interp *interp; // Interpreter associated with // widget. Used to delete widget // command. // } PlPlotter; void CopyColour( XColor* from, XColor* to ); void Tkw_StoreColor( PLStream* pls, TkwDisplay* tkwd, XColor* col ); static int pltk_AreWeGrayscale( PlPlotter *plf ); void PlplotterAtEop( Tcl_Interp *interp, register PlPlotter *plPlotterPtr ); void PlplotterAtBop( Tcl_Interp *interp, register PlPlotter *plPlotterPtr ); static int synchronize = 0; // change to 1 for synchronized operation // for debugging only // Number of instructions to skip between querying the X server for events #define MAX_INSTR 20 // Pixels/mm #define PHYSICAL 0 // Enables physical scaling.. // Set constants for dealing with colormap. In brief: // // ccmap When set, turns on custom color map // // XWM_COLORS Number of low "pixel" values to copy. // CMAP0_COLORS Color map 0 entries. // CMAP1_COLORS Color map 1 entries. // MAX_COLORS Maximum colors period. // // See Init_CustomCmap() and Init_DefaultCmap() for more info. // Set ccmap at your own risk -- still under development. // // plplot_tkwin_ccmap is statically defined in pltkwd.h. Note that // plplotter.c also includes that header and uses that variable. #define XWM_COLORS 70 #define CMAP0_COLORS 16 #define CMAP1_COLORS 50 #define MAX_COLORS 256 #ifndef USE_TK // Variables to hold RGB components of given colormap. // Used in an ugly hack to get past some X11R5 and TK limitations. static int sxwm_colors_set; static XColor sxwm_colors[MAX_COLORS]; #endif // Keep pointers to all the displays in use static TkwDisplay *tkwDisplay[PLTKDISPLAYS]; #if !defined ( MAC_TCL ) && !defined ( __WIN32__ ) static unsigned char CreatePixmapStatus; static int CreatePixmapErrorHandler( Display *display, XErrorEvent *error ); #endif // Function prototypes // Initialization static void Init( PLStream *pls ); static void InitColors( PLStream *pls ); static void AllocCustomMap( PLStream *pls ); static void AllocCmap0( PLStream *pls ); static void AllocCmap1( PLStream *pls ); static void CreatePixmap( PLStream *pls ); static void GetVisual( PLStream *pls ); static void AllocBGFG( PLStream *pls ); // Escape function commands static void ExposeCmd( PLStream *pls, PLDisplay *ptr ); static void RedrawCmd( PLStream *pls ); static void ResizeCmd( PLStream *pls, PLDisplay *ptr ); #ifndef USE_TK static void GetCursorCmd( PLStream *pls, PLGraphicsIn *ptr ); #endif static void FillPolygonCmd( PLStream *pls ); #ifdef USING_PLESC_COPY static void CopyCommand( PLStream *pls ); #endif // Miscellaneous static void StoreCmap0( PLStream *pls ); static void StoreCmap1( PLStream *pls ); static void WaitForPage( PLStream *pls ); void plD_dispatch_init_tkwin( PLDispatchTable *pdt ); void plD_init_tkwin( PLStream * ); void plD_line_tkwin( PLStream *, short, short, short, short ); void plD_polyline_tkwin( PLStream *, short *, short *, PLINT ); void plD_eop_tkwin( PLStream * ); void plD_bop_tkwin( PLStream * ); void plD_tidy_tkwin( PLStream * ); void plD_state_tkwin( PLStream *, PLINT ); void plD_esc_tkwin( PLStream *, PLINT, void * ); void plD_wait_tkwin( PLStream * ); void plD_open_tkwin( PLStream *pls ); void plD_dispatch_init_tkwin( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "PLplot Tk plotter"; pdt->pl_DevName = "tkwin"; #endif pdt->pl_type = plDevType_Interactive; pdt->pl_seq = 45; pdt->pl_init = (plD_init_fp) plD_init_tkwin; pdt->pl_line = (plD_line_fp) plD_line_tkwin; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_tkwin; pdt->pl_eop = (plD_eop_fp) plD_eop_tkwin; pdt->pl_bop = (plD_bop_fp) plD_bop_tkwin; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_tkwin; pdt->pl_state = (plD_state_fp) plD_state_tkwin; pdt->pl_esc = (plD_esc_fp) plD_esc_tkwin; pdt->pl_wait = (plD_wait_fp) plD_wait_tkwin; } //-------------------------------------------------------------------------- // plD_init_tkwin() // // Initialize device. // Tk-dependent stuff done in plD_open_tkwin() and Init(). //-------------------------------------------------------------------------- void plD_init_tkwin( PLStream *pls ) { TkwDev *dev; float pxlx, pxly; int xmin = 0; int xmax = PIXELS_X - 1; int ymin = 0; int ymax = PIXELS_Y - 1; dbug_enter( "plD_init_tkw" ); pls->termin = 1; // Is an interactive terminal pls->dev_flush = 1; // Handle our own flushes pls->dev_fill0 = 1; // Handle solid fills pls->plbuf_write = 1; // Activate plot buffer // The real meat of the initialization done here if ( pls->dev == NULL ) plD_open_tkwin( pls ); dev = (TkwDev *) pls->dev; Init( pls ); // Get ready for plotting dev->xlen = (short) ( xmax - xmin ); dev->ylen = (short) ( ymax - ymin ); dev->xscale_init = (double) dev->init_width / (double) dev->xlen; dev->yscale_init = (double) dev->init_height / (double) dev->ylen; dev->xscale = dev->xscale_init; dev->yscale = dev->yscale_init; #if PHYSICAL pxlx = (PLFLT) ( (double) PIXELS_X / dev->width * DPMM ); pxly = (PLFLT) ( (double) PIXELS_Y / dev->height * DPMM ); #else pxlx = (PLFLT) ( (double) PIXELS_X / LPAGE_X ); pxly = (PLFLT) ( (double) PIXELS_Y / LPAGE_Y ); #endif plP_setpxl( pxlx, pxly ); plP_setphy( xmin, xmax, ymin, ymax ); } //-------------------------------------------------------------------------- // plD_open_tkwin() // // Performs basic driver initialization, without actually opening or // modifying a window. May be called by the outside world before plinit // in case the caller needs early access to the driver internals (not // very common -- currently only used externally by plplotter). //-------------------------------------------------------------------------- void plD_open_tkwin( PLStream *pls ) { TkwDev *dev; TkwDisplay *tkwd; int i; dbug_enter( "plD_open_tkw" ); // Allocate and initialize device-specific data if ( pls->dev != NULL ) plwarn( "plD_open_tkw: device pointer is already set" ); pls->dev = (TkwDev *) calloc( 1, (size_t) sizeof ( TkwDev ) ); if ( pls->dev == NULL ) plexit( "plD_init_tkw: Out of memory." ); dev = (TkwDev *) pls->dev; // Variables used in querying the X server for events dev->instr = 0; dev->max_instr = MAX_INSTR; // See if display matches any already in use, and if so use that dev->tkwd = NULL; for ( i = 0; i < PLTKDISPLAYS; i++ ) { if ( tkwDisplay[i] == NULL ) { continue; } else if ( pls->FileName == NULL && tkwDisplay[i]->displayName == NULL ) { dev->tkwd = tkwDisplay[i]; break; } else if ( pls->FileName == NULL || tkwDisplay[i]->displayName == NULL ) { continue; } else if ( strcmp( tkwDisplay[i]->displayName, pls->FileName ) == 0 ) { dev->tkwd = tkwDisplay[i]; break; } } // If no display matched, create a new one if ( dev->tkwd == NULL ) { dev->tkwd = (TkwDisplay *) calloc( 1, (size_t) sizeof ( TkwDisplay ) ); if ( dev->tkwd == NULL ) plexit( "Init: Out of memory." ); for ( i = 0; i < PLTKDISPLAYS; i++ ) { if ( tkwDisplay[i] == NULL ) break; } if ( i == PLTKDISPLAYS ) plexit( "Init: Out of tkwDisplay's." ); tkwDisplay[i] = tkwd = (TkwDisplay *) dev->tkwd; tkwd->nstreams = 1; // // If we don't have a tk widget we're being called on, then // abort operations now // if ( pls->plPlotterPtr == NULL ) { plexit( "No tk plframe widget to connect to" ); } // Old version for MacOS Tk8.0 // // char deflt[] = "Macintosh:0"; // pls->FileName = deflt; // tkwd->display = (Display*) TkpOpenDisplay(pls->FileName); // // Open display #if defined ( MAC_TCL ) || defined ( __WIN32__ ) if ( !pls->FileName ) { // // Need to strdup because Tk has allocated the screen name, // but we will actually 'free' it later ourselves, and therefore // need to own the memory. // pls->FileName = plstrdup( TkGetDefaultScreenName( NULL, NULL ) ); } tkwd->display = pls->plPlotterPtr->display; #else tkwd->display = XOpenDisplay( pls->FileName ); #endif if ( tkwd->display == NULL ) { plexit( "Can't open display" ); } tkwd->displayName = pls->FileName; tkwd->screen = DefaultScreen( tkwd->display ); if ( synchronize ) { XSynchronize( tkwd->display, 1 ); } // Get colormap and visual tkwd->map = Tk_Colormap( pls->plPlotterPtr->tkwin ); GetVisual( pls ); // // Figure out if we have a color display or not. // Default is color IF the user hasn't specified and IF the output device is // not grayscale. // if ( pls->colorset ) tkwd->color = pls->color; else { pls->color = 1; tkwd->color = !pltk_AreWeGrayscale( pls->plPlotterPtr ); } // Allocate & set background and foreground colors AllocBGFG( pls ); pltkwin_setBGFG( pls ); } // Display matched, so use existing display data else { tkwd = (TkwDisplay *) dev->tkwd; tkwd->nstreams++; } tkwd->ixwd = i; } //-------------------------------------------------------------------------- // plD_line_tkwin() // // Draw a line in the current color from (x1,y1) to (x2,y2). //-------------------------------------------------------------------------- void plD_line_tkwin( PLStream *pls, short x1a, short y1a, short x2a, short y2a ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; int x1 = x1a, y1 = y1a, x2 = x2a, y2 = y2a; if ( dev->flags & 1 ) return; y1 = dev->ylen - y1; y2 = dev->ylen - y2; x1 = (int) ( x1 * dev->xscale ); x2 = (int) ( x2 * dev->xscale ); y1 = (int) ( y1 * dev->yscale ); y2 = (int) ( y2 * dev->yscale ); if ( dev->write_to_window ) XDrawLine( tkwd->display, dev->window, dev->gc, x1, y1, x2, y2 ); if ( dev->write_to_pixmap ) XDrawLine( tkwd->display, dev->pixmap, dev->gc, x1, y1, x2, y2 ); } //-------------------------------------------------------------------------- // plD_polyline_tkwin() // // Draw a polyline in the current color from (x1,y1) to (x2,y2). //-------------------------------------------------------------------------- void plD_polyline_tkwin( PLStream *pls, short *xa, short *ya, PLINT npts ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; PLINT i; XPoint _pts[PL_MAXPOLY]; XPoint *pts; if ( dev->flags & 1 ) return; if ( npts > PL_MAXPOLY ) { pts = (XPoint *) malloc( sizeof ( XPoint ) * (size_t) npts ); } else { pts = _pts; } for ( i = 0; i < npts; i++ ) { pts[i].x = (short) ( dev->xscale * xa[i] ); pts[i].y = (short) ( dev->yscale * ( dev->ylen - ya[i] ) ); } if ( dev->write_to_window ) XDrawLines( tkwd->display, dev->window, dev->gc, pts, npts, CoordModeOrigin ); if ( dev->write_to_pixmap ) XDrawLines( tkwd->display, dev->pixmap, dev->gc, pts, npts, CoordModeOrigin ); if ( npts > PL_MAXPOLY ) { free( pts ); } } //-------------------------------------------------------------------------- // plD_eop_tkwin() // // End of page. //-------------------------------------------------------------------------- void plD_eop_tkwin( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; dbug_enter( "plD_eop_tkw" ); if ( dev->flags & 1 ) return; XFlush( tkwd->display ); if ( pls->db ) ExposeCmd( pls, NULL ); } //-------------------------------------------------------------------------- // plD_wait_tkwin() // // User must hit return (or third mouse button) to continue. //-------------------------------------------------------------------------- void plD_wait_tkwin( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; dbug_enter( "plD_wait_tkw" ); if ( dev->flags & 1 ) return; WaitForPage( pls ); } //-------------------------------------------------------------------------- // WaitForPage() // // This routine waits for the user to advance the plot, while handling // all other events. //-------------------------------------------------------------------------- static void WaitForPage( PLStream *pls ) { PlPlotter *plf = pls->plPlotterPtr; TkwDev *dev = (TkwDev *) pls->dev; dbug_enter( "WaitForPage" ); dev->flags &= 1; if ( plf == NULL ) { plwarn( "WaitForPage: Illegal call --- driver can't find enclosing PlPlotter" ); return; } PlplotterAtEop( plf->interp, plf ); while ( !( dev->flags ) && !Tcl_InterpDeleted( plf->interp ) && ( Tk_GetNumMainWindows() > 0 ) ) { Tcl_DoOneEvent( 0 ); } if ( Tcl_InterpDeleted( plf->interp ) || ( Tk_GetNumMainWindows() <= 0 ) ) { dev->flags |= 1; } dev->flags &= 1; } //-------------------------------------------------------------------------- // plD_bop_tkwin() // // Set up for the next page. //-------------------------------------------------------------------------- void plD_bop_tkwin( PLStream *pls ) { PlPlotter *plf = pls->plPlotterPtr; TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; XRectangle xrect; xrect.x = 0; xrect.y = 0; xrect.width = (short unsigned) dev->width; xrect.height = (short unsigned) dev->height; dbug_enter( "plD_bop_tkw" ); if ( dev->flags & 1 ) return; if ( dev->write_to_window ) { #ifdef MAC_TCL // MacTk only has these X calls XSetForeground( tkwd->display, dev->gc, tkwd->cmap0[0].pixel ); XFillRectangles( tkwd->display, dev->window, dev->gc, &xrect, 1 ); XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel ); #else XClearWindow( tkwd->display, dev->window ); #endif } if ( dev->write_to_pixmap ) { XSetForeground( tkwd->display, dev->gc, tkwd->cmap0[0].pixel ); XFillRectangles( tkwd->display, dev->pixmap, dev->gc, &xrect, 1 ); XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel ); } XSync( tkwd->display, 0 ); pls->page++; PlplotterAtBop( plf->interp, plf ); } //-------------------------------------------------------------------------- // plD_tidy_tkwin() // // Close graphics file //-------------------------------------------------------------------------- void plD_tidy_tkwin( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; dbug_enter( "plD_tidy_tkw" ); tkwd->nstreams--; if ( tkwd->nstreams == 0 ) { int ixwd = tkwd->ixwd; XFreeGC( tkwd->display, dev->gc ); #if !defined ( MAC_TCL ) && !defined ( __WIN32__ ) XCloseDisplay( tkwd->display ); #endif free_mem( tkwDisplay[ixwd] ); } // // Vince removed this November 1999. It seems as if a simple // 'plframe .p ; destroy .p' leaves a temporary buf file open // if we clear this flag here. It should be checked and then // cleared by whoever called us. An alternative fix would // be to carry out the check/tidy here. The plframe widget // handles this stuff for us. // // pls->plbuf_write = 0; } //-------------------------------------------------------------------------- // plD_state_tkwin() // // Handle change in PLStream state (color, pen width, fill attribute, etc). //-------------------------------------------------------------------------- void plD_state_tkwin( PLStream *pls, PLINT op ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; dbug_enter( "plD_state_tkw" ); if ( dev->flags & 1 ) return; switch ( op ) { case PLSTATE_WIDTH: break; case PLSTATE_COLOR0: { int icol0 = pls->icol0; if ( tkwd->color ) { if ( icol0 == PL_RGB_COLOR ) { PLColor_to_TkColor( &pls->curcolor, &dev->curcolor ); Tkw_StoreColor( pls, tkwd, &dev->curcolor ); } else { dev->curcolor = tkwd->cmap0[icol0]; } XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel ); } else { dev->curcolor = tkwd->fgcolor; XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel ); } break; } case PLSTATE_COLOR1: { int icol1; if ( tkwd->ncol1 == 0 ) AllocCmap1( pls ); if ( tkwd->ncol1 < 2 ) break; icol1 = ( pls->icol1 * ( tkwd->ncol1 - 1 ) ) / ( pls->ncol1 - 1 ); if ( tkwd->color ) dev->curcolor = tkwd->cmap1[icol1]; else dev->curcolor = tkwd->fgcolor; XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel ); break; } case PLSTATE_CMAP0: pltkwin_setBGFG( pls ); StoreCmap0( pls ); break; case PLSTATE_CMAP1: StoreCmap1( pls ); break; } } //-------------------------------------------------------------------------- // plD_esc_tkwin() // // Escape function. // // Functions: // // PLESC_EH Handle pending events // PLESC_EXPOSE Force an expose // PLESC_FILL Fill polygon // PLESC_FLUSH Flush X event buffer // PLESC_GETC Get coordinates upon mouse click // PLESC_REDRAW Force a redraw // PLESC_RESIZE Force a resize //-------------------------------------------------------------------------- void plD_esc_tkwin( PLStream *pls, PLINT op, void *ptr ) { TkwDev *dev = (TkwDev *) pls->dev; #ifndef USE_TK TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; #endif dbug_enter( "plD_esc_tkw" ); if ( dev->flags & 1 ) return; switch ( op ) { case PLESC_EH: #ifndef USE_TK HandleEvents( pls ); #endif break; case PLESC_EXPOSE: ExposeCmd( pls, (PLDisplay *) ptr ); break; case PLESC_FILL: FillPolygonCmd( pls ); break; case PLESC_FLUSH: #ifndef USE_TK HandleEvents( pls ); XFlush( tkwd->display ); #endif break; case PLESC_GETC: #ifndef USE_TK GetCursorCmd( pls, (PLGraphicsIn *) ptr ); #endif break; case PLESC_REDRAW: RedrawCmd( pls ); break; case PLESC_RESIZE: ResizeCmd( pls, (PLDisplay *) ptr ); break; // Added by Vince, disabled by default since we want a minimal patch #ifdef USING_PLESC_COPY case PLESC_COPY: CopyCommand( pls ); break; #endif } } #ifdef USING_PLESC_COPY //-------------------------------------------------------------------------- // CopyCommand() // // Copy a rectangle to a new part of the image. // Points described in first 3 elements of pls->dev_x[] and pls->dev_y[]. //-------------------------------------------------------------------------- static void CopyCommand( PLStream *pls ) { int x0, w, x1, y0, h, y1; TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; x0 = (int) ( dev->xscale * pls->dev_x[0] ); x1 = (int) ( dev->xscale * pls->dev_x[2] ); y0 = (int) ( dev->yscale * ( dev->ylen - pls->dev_y[0] ) ); y1 = (int) ( dev->yscale * ( dev->ylen - pls->dev_y[2] ) ); w = (int) ( dev->xscale * ( pls->dev_x[1] - pls->dev_x[0] ) ); h = (int) ( -dev->yscale * ( pls->dev_y[1] - pls->dev_y[0] ) ); if ( dev->write_to_window ) XCopyArea( tkwd->display, dev->window, dev->window, dev->gc, x0, y0, w, h, x1, y1 ); if ( dev->write_to_pixmap ) XCopyArea( tkwd->display, dev->pixmap, dev->pixmap, dev->gc, x0, y0, w, h, x1, y1 ); } #endif //-------------------------------------------------------------------------- // FillPolygonCmd() // // Fill polygon described in points pls->dev_x[] and pls->dev_y[]. // Only solid color fill supported. //-------------------------------------------------------------------------- static void FillPolygonCmd( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; XPoint _pts[PL_MAXPOLY]; XPoint *pts; int i; if ( pls->dev_npts > PL_MAXPOLY ) { pts = (XPoint *) malloc( sizeof ( XPoint ) * (size_t) ( pls->dev_npts ) ); } else { pts = _pts; } for ( i = 0; i < pls->dev_npts; i++ ) { pts[i].x = (short) ( dev->xscale * pls->dev_x[i] ); pts[i].y = (short) ( dev->yscale * ( dev->ylen - pls->dev_y[i] ) ); } // Fill polygons if ( dev->write_to_window ) XFillPolygon( tkwd->display, dev->window, dev->gc, pts, pls->dev_npts, Nonconvex, CoordModeOrigin ); if ( dev->write_to_pixmap ) XFillPolygon( tkwd->display, dev->pixmap, dev->gc, pts, pls->dev_npts, Nonconvex, CoordModeOrigin ); // If in debug mode, draw outline of boxes being filled #ifdef DEBUG if ( pls->debug ) { XSetForeground( tkwd->display, dev->gc, tkwd->fgcolor.pixel ); if ( dev->write_to_window ) XDrawLines( tkwd->display, dev->window, dev->gc, pts, pls->dev_npts, CoordModeOrigin ); if ( dev->write_to_pixmap ) XDrawLines( tkwd->display, dev->pixmap, dev->gc, pts, pls->dev_npts, CoordModeOrigin ); XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel ); } #endif if ( pls->dev_npts > PL_MAXPOLY ) { free( pts ); } } //-------------------------------------------------------------------------- // Init() // // Xlib initialization routine. // // Controlling routine for X window creation and/or initialization. // The user may customize the window in the following ways: // // display: pls->OutFile (use plsfnam() or -display option) // size: pls->xlength, pls->ylength (use plspage() or -geo option) // bg color: pls->cmap0[0] (use plscolbg() or -bg option) //-------------------------------------------------------------------------- static void Init( PLStream *pls ) { PlPlotter *plf; TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; dbug_enter( "Init" ); dev->window = (Window) pls->window_id; plf = pls->plPlotterPtr; if ( plf == NULL ) { plwarn( "Init: Illegal call --- driver can't find enclosing PlPlotter" ); return; } // Initialize colors InitColors( pls ); #ifndef MAC_TCL XSetWindowColormap( tkwd->display, dev->window, tkwd->map ); #else #endif // Set up GC for ordinary draws if ( !dev->gc ) dev->gc = XCreateGC( tkwd->display, dev->window, 0, 0 ); // Set up GC for rubber-band draws if ( !tkwd->gcXor ) { XGCValues gcValues; unsigned long mask; gcValues.background = tkwd->cmap0[0].pixel; gcValues.foreground = 0xFF; gcValues.function = GXxor; mask = GCForeground | GCBackground | GCFunction; tkwd->gcXor = XCreateGC( tkwd->display, dev->window, mask, &gcValues ); } // Get initial drawing area dimensions dev->width = (unsigned int) Tk_Width( plf->tkwin ); dev->height = (unsigned int) Tk_Height( plf->tkwin ); dev->border = (unsigned int) Tk_InternalBorderWidth( plf->tkwin ); tkwd->depth = (unsigned int) Tk_Depth( plf->tkwin ); dev->init_width = dev->width; dev->init_height = dev->height; // Set up flags that determine what we are writing to // If nopixmap is set, ignore db if ( pls->nopixmap ) { dev->write_to_pixmap = 0; pls->db = 0; } else { dev->write_to_pixmap = 1; } dev->write_to_window = !pls->db; // Create pixmap for holding plot image (for expose events). if ( dev->write_to_pixmap ) CreatePixmap( pls ); // Set drawing color plD_state_tkwin( pls, PLSTATE_COLOR0 ); XSetWindowBackground( tkwd->display, dev->window, tkwd->cmap0[0].pixel ); XSetBackground( tkwd->display, dev->gc, tkwd->cmap0[0].pixel ); } //-------------------------------------------------------------------------- // ExposeCmd() // // Event handler routine for expose events. // These are "pure" exposures (no resize), so don't need to clear window. //-------------------------------------------------------------------------- static void ExposeCmd( PLStream *pls, PLDisplay *pldis ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; int x, y, width, height; dbug_enter( "ExposeCmd" ); // Return if plD_init_tkw hasn't been called yet if ( dev == NULL ) { plwarn( "ExposeCmd: Illegal call -- driver uninitialized" ); return; } // Exposed area. If unspecified, the entire window is used. if ( pldis == NULL ) { x = 0; y = 0; width = (int) dev->width; height = (int) dev->height; } else { x = (int) pldis->x; y = (int) pldis->y; width = (int) pldis->width; height = (int) pldis->height; } // Usual case: refresh window from pixmap // DEBUG option: draws rectangle around refreshed region XSync( tkwd->display, 0 ); if ( dev->write_to_pixmap ) { XCopyArea( tkwd->display, dev->pixmap, dev->window, dev->gc, x, y, (unsigned int) width, (unsigned int) height, x, y ); XSync( tkwd->display, 0 ); #ifdef DEBUG if ( pls->debug ) { XPoint pts[5]; int x0 = x, x1 = x + width, y0 = y, y1 = y + height; pts[0].x = (short) x0; pts[0].y = (short) y0; pts[1].x = (short) x1; pts[1].y = (short) y0; pts[2].x = (short) x1; pts[2].y = (short) y1; pts[3].x = (short) x0; pts[3].y = (short) y1; pts[4].x = (short) x0; pts[4].y = (short) y0; XDrawLines( tkwd->display, dev->window, dev->gc, pts, 5, CoordModeOrigin ); } #endif } else { plRemakePlot( pls ); XFlush( tkwd->display ); } } //-------------------------------------------------------------------------- // ResizeCmd() // // Event handler routine for resize events. //-------------------------------------------------------------------------- static void ResizeCmd( PLStream *pls, PLDisplay *pldis ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; int write_to_window = dev->write_to_window; dbug_enter( "ResizeCmd" ); // Return if plD_init_tkw hasn't been called yet if ( dev == NULL ) { plwarn( "ResizeCmd: Illegal call -- driver uninitialized" ); return; } // Return if pointer to window not specified. if ( pldis == NULL ) { plwarn( "ResizeCmd: Illegal call -- window pointer uninitialized" ); return; } // Reset current window bounds dev->width = pldis->width; dev->height = pldis->height; dev->xscale = dev->width / (double) dev->init_width; dev->yscale = dev->height / (double) dev->init_height; dev->xscale = dev->xscale * dev->xscale_init; dev->yscale = dev->yscale * dev->yscale_init; #if PHYSICAL { float pxlx = (double) PIXELS_X / dev->width * DPMM; float pxly = (double) PIXELS_Y / dev->height * DPMM; plP_setpxl( pxlx, pxly ); } #endif // Note: the following order MUST be obeyed -- if you instead redraw into // the window and then copy it to the pixmap, off-screen parts of the window // may contain garbage which is then transferred to the pixmap (and thus // will not go away after an expose). // // Resize pixmap using new dimensions if ( dev->write_to_pixmap ) { dev->write_to_window = 0; #if defined ( __WIN32__ ) || defined ( MAC_TCL ) Tk_FreePixmap( tkwd->display, dev->pixmap ); #else // Vince's original driver code used // Tk_FreePixmap(tkwd->display, dev->pixmap); //which is defined in tk-8.3 (and 8.2?) source as //void // Tk_FreePixmap(display, pixmap) // Display *display; // Pixmap pixmap; // { // XFreePixmap(display, pixmap); // Tk_FreeXId(display, (XID) pixmap); // } // But that bombed under Linux and tcl/tk8.2 so now just call // XFreePixmap directly. (Not recommended as permanent solution // because you eventually run out of resources according to man // page if you don't call Tk_FreeXId.) Vince is still looking into // how to resolve this problem. // XFreePixmap( tkwd->display, dev->pixmap ); #endif CreatePixmap( pls ); } // Initialize & redraw (to pixmap, if available). plD_bop_tkwin( pls ); plRemakePlot( pls ); XSync( tkwd->display, 0 ); // If pixmap available, fake an expose if ( dev->write_to_pixmap ) { dev->write_to_window = write_to_window; XCopyArea( tkwd->display, dev->pixmap, dev->window, dev->gc, 0, 0, dev->width, dev->height, 0, 0 ); XSync( tkwd->display, 0 ); } } //-------------------------------------------------------------------------- // RedrawCmd() // // Handles page redraw without resize (pixmap does not get reallocated). // Calling this makes sure all necessary housekeeping gets done. //-------------------------------------------------------------------------- static void RedrawCmd( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; int write_to_window = dev->write_to_window; dbug_enter( "RedrawCmd" ); // Return if plD_init_tkw hasn't been called yet if ( dev == NULL ) { plwarn( "RedrawCmd: Illegal call -- driver uninitialized" ); return; } // Initialize & redraw (to pixmap, if available). if ( dev->write_to_pixmap ) dev->write_to_window = 0; plD_bop_tkwin( pls ); plRemakePlot( pls ); XSync( tkwd->display, 0 ); dev->write_to_window = write_to_window; // If pixmap available, fake an expose if ( dev->write_to_pixmap ) { XCopyArea( tkwd->display, dev->pixmap, dev->window, dev->gc, 0, 0, dev->width, dev->height, 0, 0 ); XSync( tkwd->display, 0 ); } } //-------------------------------------------------------------------------- // CreatePixmap() // // This routine creates a pixmap, doing error trapping in case there // isn't enough memory on the server. //-------------------------------------------------------------------------- static void CreatePixmap( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; Tk_Window tkwin = pls->plPlotterPtr->tkwin; #if !defined ( MAC_TCL ) && !defined ( __WIN32__ ) int ( *oldErrorHandler )( Display *, XErrorEvent * ); oldErrorHandler = XSetErrorHandler( CreatePixmapErrorHandler ); CreatePixmapStatus = Success; #endif #ifdef MAC_TCL // MAC_TCL's version of XCreatePixmap doesn't like 0 by 0 maps if ( dev->width == 0 ) { dev->width = 10; } if ( dev->height == 0 ) { dev->height = 10; } #endif pldebug( "CreatePixmap", "creating pixmap: width = %d, height = %d, depth = %d\n", dev->width, dev->height, tkwd->depth ); // // dev->pixmap = Tk_GetPixmap(tkwd->display, dev->window, // dev->width, dev->height, tkwd->depth); // // // Vince's original driver code used Tk_Display(tkwin) for first argument, // but that bombed on an Linux tcl/tk 8.2 machine. Something was wrong // with that value. Thus, we now use tkwd->display, and that works well. // Vince is looking into why Tk_Display(tkwin) is badly defined under 8.2. // old code: // // dev->pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), // Tk_Width(tkwin), Tk_Height(tkwin), // DefaultDepthOfScreen(Tk_Screen(tkwin))); // dev->pixmap = Tk_GetPixmap( tkwd->display, Tk_WindowId( tkwin ), Tk_Width( tkwin ), Tk_Height( tkwin ), DefaultDepthOfScreen( Tk_Screen( tkwin ) ) ); XSync( tkwd->display, 0 ); #if !defined ( MAC_TCL ) && !defined ( __WIN32__ ) if ( CreatePixmapStatus != Success ) { dev->write_to_pixmap = 0; dev->write_to_window = 1; pls->db = 0; fprintf( stderr, "\n\ Warning: pixmap could not be allocated (insufficient memory on server).\n\ Driver will redraw the entire plot to handle expose events.\n" ); } XSetErrorHandler( oldErrorHandler ); #endif } //-------------------------------------------------------------------------- // GetVisual() // // Get visual info. In order to safely use a visual other than that of // the parent (which hopefully is that returned by DefaultVisual), you // must first find (using XGetRGBColormaps) or create a colormap matching // this visual and then set the colormap window attribute in the // XCreateWindow attributes and valuemask arguments. I don't do this // right now, so this is turned off by default. //-------------------------------------------------------------------------- static void GetVisual( PLStream *pls ) { int depth; TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; dbug_enter( "GetVisual" ); tkwd->visual = Tk_GetVisual( pls->plPlotterPtr->interp, pls->plPlotterPtr->tkwin, "best", &depth, NULL ); tkwd->depth = (unsigned int) depth; } //-------------------------------------------------------------------------- // AllocBGFG() // // Allocate background & foreground colors. If possible, I choose pixel // values such that the fg pixel is the xor of the bg pixel, to make // rubber-banding easy to see. //-------------------------------------------------------------------------- static void AllocBGFG( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; #ifndef USE_TK int i, j, npixels; unsigned long plane_masks[1], pixels[MAX_COLORS]; #endif dbug_enter( "AllocBGFG" ); // If not on a color system, just return if ( !tkwd->color ) return; #ifndef USE_TK // Allocate r/w color cell for background if ( XAllocColorCells( tkwd->display, tkwd->map, False, plane_masks, 0, pixels, 1 ) ) { tkwd->cmap0[0].pixel = pixels[0]; } else { plexit( "couldn't allocate background color cell" ); } // Allocate as many colors as we can npixels = MAX_COLORS; for (;; ) { if ( XAllocColorCells( tkwd->display, tkwd->map, False, plane_masks, 0, pixels, npixels ) ) break; npixels--; if ( npixels == 0 ) break; } // Find the color with pixel = xor of the bg color pixel. // If a match isn't found, the last pixel allocated is used. for ( i = 0; i < npixels - 1; i++ ) { if ( pixels[i] == ( ~tkwd->cmap0[0].pixel & 0xFF ) ) break; } // Use this color cell for our foreground color. Then free the rest. tkwd->fgcolor.pixel = pixels[i]; for ( j = 0; j < npixels; j++ ) { if ( j != i ) XFreeColors( tkwd->display, tkwd->map, &pixels[j], 1, 0 ); } #endif } //-------------------------------------------------------------------------- // pltkwin_setBGFG() // // Set background & foreground colors. Foreground over background should // have high contrast. //-------------------------------------------------------------------------- void pltkwin_setBGFG( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; PLColor fgcolor; int gslevbg, gslevfg; dbug_enter( "pltkwin_setBGFG" ); // // Set background color. // // Background defaults to black on color screens, white on grayscale (many // grayscale monitors have poor contrast, and black-on-white looks better). // if ( !tkwd->color ) { pls->cmap0[0].r = pls->cmap0[0].g = pls->cmap0[0].b = 0xFF; } gslevbg = (int) ( ( (long) pls->cmap0[0].r + (long) pls->cmap0[0].g + (long) pls->cmap0[0].b ) / 3 ); PLColor_to_TkColor( &pls->cmap0[0], &tkwd->cmap0[0] ); // // Set foreground color. // // Used for grayscale output, since otherwise the plots can become nearly // unreadable (i.e. if colors get mapped onto grayscale values). In this // case it becomes the grayscale level for all draws, and is taken to be // black if the background is light, and white if the background is dark. // Note that white/black allocations never fail. // if ( gslevbg > 0x7F ) gslevfg = 0; else gslevfg = 0xFF; fgcolor.r = fgcolor.g = fgcolor.b = (unsigned char) gslevfg; PLColor_to_TkColor( &fgcolor, &tkwd->fgcolor ); // Now store #ifndef USE_TK if ( tkwd->color ) { XStoreColor( tkwd->display, tkwd->map, &tkwd->fgcolor ); XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap0[0] ); } else { XAllocColor( tkwd->display, tkwd->map, &tkwd->cmap0[0] ); XAllocColor( tkwd->display, tkwd->map, &tkwd->fgcolor ); } #else Tkw_StoreColor( pls, tkwd, &tkwd->cmap0[0] ); Tkw_StoreColor( pls, tkwd, &tkwd->fgcolor ); #endif } //-------------------------------------------------------------------------- // InitColors() // // Does all color initialization. //-------------------------------------------------------------------------- static void InitColors( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; dbug_enter( "InitColors" ); // Allocate and initialize color maps. // Defer cmap1 allocation until it's actually used if ( tkwd->color ) { if ( plplot_tkwin_ccmap ) { AllocCustomMap( pls ); } else { AllocCmap0( pls ); } } } //-------------------------------------------------------------------------- // AllocCustomMap() // // Initializes custom color map and all the cruft that goes with it. // // Assuming all color X displays do 256 colors, the breakdown is as follows: // // XWM_COLORS Number of low "pixel" values to copy. These are typically // allocated first, thus are in use by the window manager. I // copy them to reduce flicker. // // CMAP0_COLORS Color map 0 entries. I allocate these both in the default // colormap and the custom colormap to reduce flicker. // // CMAP1_COLORS Color map 1 entries. There should be as many as practical // available for smooth shading. On the order of 50-100 is // pretty reasonable. You don't really need all 256, // especially if all you're going to do is to print it to // postscript (which doesn't have any intrinsic limitation on // the number of colors). // // It's important to leave some extra colors unallocated for Tk. In // particular the palette tools require a fair amount. I recommend leaving // at least 40 or so free. //-------------------------------------------------------------------------- static void AllocCustomMap( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; XColor xwm_colors[MAX_COLORS]; int i; #ifndef USE_TK int npixels; unsigned long plane_masks[1], pixels[MAX_COLORS]; #endif dbug_enter( "AllocCustomMap" ); // Determine current default colors for ( i = 0; i < MAX_COLORS; i++ ) { xwm_colors[i].pixel = (long unsigned) i; } #ifndef MAC_TCL XQueryColors( tkwd->display, tkwd->map, xwm_colors, MAX_COLORS ); #endif // Allocate cmap0 colors in the default colormap. // The custom cmap0 colors are later stored at the same pixel values. // This is a really cool trick to reduce the flicker when changing colormaps. // AllocCmap0( pls ); XAllocColor( tkwd->display, tkwd->map, &tkwd->fgcolor ); // Create new color map tkwd->map = XCreateColormap( tkwd->display, DefaultRootWindow( tkwd->display ), tkwd->visual, AllocNone ); // Now allocate all colors so we can fill the ones we want to copy #ifndef USE_TK npixels = MAX_COLORS; for (;; ) { if ( XAllocColorCells( tkwd->display, tkwd->map, False, plane_masks, 0, pixels, npixels ) ) break; npixels--; if ( npixels == 0 ) plexit( "couldn't allocate any colors" ); } // Fill the low colors since those are in use by the window manager for ( i = 0; i < XWM_COLORS; i++ ) { XStoreColor( tkwd->display, tkwd->map, &xwm_colors[i] ); pixels[xwm_colors[i].pixel] = 0; } // Fill the ones we will use in cmap0 for ( i = 0; i < tkwd->ncol0; i++ ) { XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap0[i] ); pixels[tkwd->cmap0[i].pixel] = 0; } // Finally, if the colormap was saved by an external agent, see if there are // any differences from the current default map and save those! A very cool // (or sick, depending on how you look at it) trick to get over some X and // Tk limitations. // if ( sxwm_colors_set ) { for ( i = 0; i < MAX_COLORS; i++ ) { if ( ( xwm_colors[i].red != sxwm_colors[i].red ) || ( xwm_colors[i].green != sxwm_colors[i].green ) || ( xwm_colors[i].blue != sxwm_colors[i].blue ) ) { if ( pixels[i] != 0 ) { XStoreColor( tkwd->display, tkwd->map, &xwm_colors[i] ); pixels[i] = 0; } } } } // Now free the ones we're not interested in for ( i = 0; i < npixels; i++ ) { if ( pixels[i] != 0 ) XFreeColors( tkwd->display, tkwd->map, &pixels[i], 1, 0 ); } #endif // Allocate colors in cmap 1 AllocCmap1( pls ); } //-------------------------------------------------------------------------- // AllocCmap0() // // Allocate & initialize cmap0 entries. //-------------------------------------------------------------------------- static void AllocCmap0( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; #ifndef USE_TK int npixels; int i; unsigned long plane_masks[1], pixels[MAX_COLORS]; #endif dbug_enter( "AllocCmap0" ); // Allocate and assign colors in cmap 0 #ifndef USE_TK npixels = pls->ncol0 - 1; for (;; ) { if ( XAllocColorCells( tkwd->display, tkwd->map, False, plane_masks, 0, &pixels[1], npixels ) ) break; npixels--; if ( npixels == 0 ) plexit( "couldn't allocate any colors" ); } tkwd->ncol0 = npixels + 1; for ( i = 1; i < tkwd->ncol0; i++ ) { tkwd->cmap0[i].pixel = pixels[i]; } #else // We use the Tk color scheme tkwd->ncol0 = pls->ncol0; #endif StoreCmap0( pls ); } //-------------------------------------------------------------------------- // AllocCmap1() // // Allocate & initialize cmap1 entries. If using the default color map, // must severely limit number of colors otherwise TK won't have enough. //-------------------------------------------------------------------------- static void AllocCmap1( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; int npixels; #ifndef USE_TK int i, j; unsigned long plane_masks[1], pixels[MAX_COLORS]; #endif dbug_enter( "AllocCmap1" ); // Allocate colors in cmap 1 npixels = MAX( 2, MIN( CMAP1_COLORS, pls->ncol1 ) ); #ifndef USE_TK for (;; ) { if ( XAllocColorCells( tkwd->display, tkwd->map, False, plane_masks, 0, pixels, npixels ) ) break; npixels--; if ( npixels == 0 ) break; } if ( npixels < 2 ) { tkwd->ncol1 = -1; fprintf( stderr, "Warning: unable to allocate sufficient colors in cmap1\n" ); return; } else { tkwd->ncol1 = npixels; if ( pls->verbose ) fprintf( stderr, "AllocCmap1 (xwin.c): Allocated %d colors in cmap1\n", npixels ); } // Don't assign pixels sequentially, to avoid strange problems with xor GC's // Skipping by 2 seems to do the job best for ( j = i = 0; i < tkwd->ncol1; i++ ) { while ( pixels[j] == 0 ) j++; tkwd->cmap1[i].pixel = pixels[j]; pixels[j] = 0; j += 2; if ( j >= tkwd->ncol1 ) j = 0; } #else tkwd->ncol1 = npixels; #endif StoreCmap1( pls ); } //-------------------------------------------------------------------------- // StoreCmap0() // // Stores cmap 0 entries in X-server colormap. //-------------------------------------------------------------------------- static void StoreCmap0( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; int i; if ( !tkwd->color ) return; for ( i = 1; i < tkwd->ncol0; i++ ) { PLColor_to_TkColor( &pls->cmap0[i], &tkwd->cmap0[i] ); #ifndef USE_TK XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap0[i] ); #else Tkw_StoreColor( pls, tkwd, &tkwd->cmap0[i] ); #endif } } void CopyColour( XColor* from, XColor* to ) { to->pixel = from->pixel; to->red = from->red; to->blue = from->blue; to->green = from->green; to->flags = from->flags; } //-------------------------------------------------------------------------- // StoreCmap1() // // Stores cmap 1 entries in X-server colormap. //-------------------------------------------------------------------------- static void StoreCmap1( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; PLColor cmap1color; int i; if ( !tkwd->color ) return; for ( i = 0; i < tkwd->ncol1; i++ ) { plcol_interp( pls, &cmap1color, i, tkwd->ncol1 ); PLColor_to_TkColor( &cmap1color, &tkwd->cmap1[i] ); #ifndef USE_TK XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap1[i] ); #else Tkw_StoreColor( pls, tkwd, &tkwd->cmap1[i] ); #endif } } void Tkw_StoreColor( PLStream* pls, TkwDisplay* tkwd, XColor* col ) { XColor *xc; #ifndef USE_TK XStoreColor( tkwd->display, tkwd->map, col ); #else (void) tkwd; // tkwd unused in this case // We're probably losing memory here xc = Tk_GetColorByValue( pls->plPlotterPtr->tkwin, col ); CopyColour( xc, col ); #endif } //-------------------------------------------------------------------------- // void PLColor_to_TkColor() // // Copies the supplied PLColor to an XColor, padding with bits as necessary // (a PLColor uses 8 bits for color storage, while an XColor uses 16 bits). // The argument types follow the same order as in the function name. //-------------------------------------------------------------------------- #define ToXColor( a ) ( ( ( 0xFF & ( a ) ) << 8 ) | ( a ) ) #define ToPLColor( a ) ( ( (U_LONG) a ) >> 8 ) void PLColor_to_TkColor( PLColor *plcolor, XColor *xcolor ) { xcolor->red = (short unsigned) ToXColor( plcolor->r ); xcolor->green = (short unsigned) ToXColor( plcolor->g ); xcolor->blue = (short unsigned) ToXColor( plcolor->b ); xcolor->flags = DoRed | DoGreen | DoBlue; } //-------------------------------------------------------------------------- // void PLColor_from_TkColor() // // Copies the supplied XColor to a PLColor, stripping off bits as // necessary. See the previous routine for more info. //-------------------------------------------------------------------------- void PLColor_from_TkColor( PLColor *plcolor, XColor *xcolor ) { plcolor->r = (unsigned char) ToPLColor( xcolor->red ); plcolor->g = (unsigned char) ToPLColor( xcolor->green ); plcolor->b = (unsigned char) ToPLColor( xcolor->blue ); } //-------------------------------------------------------------------------- // void PLColor_from_TkColor_Changed() // // Copies the supplied XColor to a PLColor, stripping off bits as // necessary. See the previous routine for more info. // // Returns 1 if the color was different from the old one. //-------------------------------------------------------------------------- int PLColor_from_TkColor_Changed( PLColor *plcolor, XColor *xcolor ) { int changed = 0; int color; color = ToPLColor( xcolor->red ); if ( plcolor->r != color ) { changed = 1; plcolor->r = (unsigned char) color; } color = ToPLColor( xcolor->green ); if ( plcolor->g != color ) { changed = 1; plcolor->g = (unsigned char) color; } color = ToPLColor( xcolor->blue ); if ( plcolor->b != color ) { changed = 1; plcolor->b = (unsigned char) color; } return changed; } //-------------------------------------------------------------------------- // int pltk_AreWeGrayscale(PlPlotter *plf) // // Determines if we're using a monochrome or grayscale device. // gmf 11-8-91; Courtesy of Paul Martz of Evans and Sutherland. // Changed July 1996 by Vince: now uses Tk to check the enclosing PlPlotter //-------------------------------------------------------------------------- static int pltk_AreWeGrayscale( PlPlotter *plf ) { #if defined ( __cplusplus ) || defined ( c_plusplus ) #define THING c_class #else #define THING class #endif Visual* visual; // get the window's Visual visual = Tk_Visual( plf->tkwin ); if ( ( visual->THING != GrayScale ) && ( visual->THING != StaticGray ) ) return ( 0 ); // if we got this far, only StaticGray and GrayScale classes available return ( 1 ); } #if !defined ( MAC_TCL ) && !defined ( __WIN32__ ) //-------------------------------------------------------------------------- // CreatePixmapErrorHandler() // // Error handler used in CreatePixmap() to catch errors in allocating // storage for pixmap. This way we can nicely substitute redraws for // pixmap copies if the server has insufficient memory. //-------------------------------------------------------------------------- static int CreatePixmapErrorHandler( Display *display, XErrorEvent *error ) { if ( error->error_code == BadAlloc ) { CreatePixmapStatus = error->error_code; } else { char buffer[256]; XGetErrorText( display, error->error_code, buffer, 256 ); fprintf( stderr, "Error in XCreatePixmap: %s.\n", buffer ); } return 1; } #endif #else int pldummy_tkwin() { return 0; } #endif // PLD_tkwin void * ckcalloc( size_t nmemb, size_t size ) { long *ptr; long *p; size *= nmemb; ptr = (long *) malloc( size ); if ( !ptr ) return ( 0 ); #if !__POWERPC__ for ( size = ( size / sizeof ( long ) ) + 1, p = ptr; --size; ) *p++ = 0; #else for ( size = ( size / sizeof ( long ) ) + 1, p = ptr - 1; --size; ) *++p = 0; #endif return ( ptr ); } plplot-5.13.0/drivers/deprecated_wxwidgets.cpp000644 001752 001752 00000127315 13150160115 023235 0ustar00softwaresoftware000000 000000 // Copyright (C) 2005 Werner Smekal, Sjaak Verdoold // Copyright (C) 2005 Germain Carrera Corraleche // Copyright (C) 1999 Frank Huebner // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // TODO: // - NA // // wxwidgets headers #include #include #include #include "plDevs.h" // plplot headers #include "plplotP.h" #include "drivers.h" // C/C++ headers #include #include "deprecated_wxwidgets.h" // Static function prototypes #ifdef PL_HAVE_FREETYPE static void plD_pixel_wxwidgets( PLStream *pls, short x, short y ); static PLINT plD_read_pixel_wxwidgets( PLStream *pls, short x, short y ); static void plD_set_pixel_wxwidgets( PLStream *pls, short x, short y, PLINT colour ); static void init_freetype_lv1( PLStream *pls ); static void init_freetype_lv2( PLStream *pls ); #endif // private functions needed by the wxwidgets Driver static void install_buffer( PLStream *pls ); static void wxRunApp( PLStream *pls, bool runonce = false ); static void GetCursorCmd( PLStream *pls, PLGraphicsIn *ptr ); static void fill_polygon( PLStream *pls ); #ifdef __WXMAC__ #include extern "C" { void CPSEnableForegroundOperation( ProcessSerialNumber* psn ); } #endif DECLARE_PLAPP( wxPLplotApp ) //-------------------------------------------------------------------------- // void Log_Verbose( const char *fmt, ... ) // // Print verbose debug message to stderr (printf style). //-------------------------------------------------------------------------- void Log_Verbose( const char *fmt, ... ) { #ifdef _DEBUG_VERBOSE va_list args; va_start( args, fmt ); fprintf( stderr, "Verbose: " ); vfprintf( stderr, fmt, args ); fprintf( stderr, "\n" ); va_end( args ); fflush( stderr ); #else (void) fmt; // Cast to void to silence compiler warnings about unused paraemeter #endif } //-------------------------------------------------------------------------- // void Log_Debug( const char *fmt, ... ) // // Print debug message to stderr (printf style). //-------------------------------------------------------------------------- void Log_Debug( const char *fmt, ... ) { #ifdef _DEBUG va_list args; va_start( args, fmt ); fprintf( stderr, "Debug: " ); vfprintf( stderr, fmt, args ); fprintf( stderr, "\n" ); va_end( args ); fflush( stderr ); #else (void) fmt; // Cast to void to silence compiler warnings about unused paraemeter #endif } //-------------------------------------------------------------------------- // In the following you'll find the driver functions which are // are needed by the plplot core. //-------------------------------------------------------------------------- // Device info #ifdef __cplusplus extern "C" { #endif PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_wxwidgets = #ifdef PLD_wxwidgets "wxwidgets:wxWidgets Driver:1:wxwidgets:51:wxwidgets\n" #endif #ifdef PLD_wxpng "wxpng:wxWidgets PNG Driver:0:wxwidgets:52:wxpng\n" #endif ; #ifdef __cplusplus } #endif //-------------------------------------------------------------------------- // wxPLDevBase::wxPLDevBase( void ) // // Contructor of base class of wxPLDev classes. //-------------------------------------------------------------------------- wxPLDevBase::wxPLDevBase( int bcknd ) : backend( bcknd ) { // Log_Verbose( "wxPLDevBase::wxPLDevBase()" ); ready = false; ownGUI = false; waiting = false; resizing = false; exit = false; comcount = 0; m_frame = NULL; xpos = 0; ypos = 0; // width, height are set in plD_init_wxwidgets // bm_width, bm_height are set in install_buffer // xmin, xmax, ymin, ymax are set in plD_init_wxwidgets // scalex, scaley are set in plD_init_wxwidgets plstate_width = false; plstate_color0 = false; plstate_color1 = false; locate_mode = 0; draw_xhair = false; newclipregion = true; clipminx = 1024; clipmaxx = 0; clipminy = 800; clipmaxy = 0; freetype = 0; smooth_text = 0; devName = (const char **) malloc( NDEV * sizeof ( char** ) ); memset( devName, '\0', NDEV * sizeof ( char** ) ); devDesc = (const char **) malloc( NDEV * sizeof ( char** ) ); memset( devDesc, '\0', NDEV * sizeof ( char** ) ); ndev = NDEV; lineSpacing = 1.0; } wxPLDevBase::~wxPLDevBase( void ) { if ( devDesc ) free( devDesc ); if ( devName ) free( devName ); } void wxPLDevBase::AddtoClipRegion( int x1, int y1, int x2, int y2 ) { newclipregion = false; if ( x1 < x2 ) { if ( x1 < clipminx ) clipminx = x1; if ( x2 > clipmaxx ) clipmaxx = x2; } else { if ( x2 < clipminx ) clipminx = x2; if ( x1 > clipmaxx ) clipmaxx = x1; } if ( y1 < y2 ) { if ( y1 < clipminy ) clipminy = y1; if ( y2 > clipmaxy ) clipmaxy = y2; } else { if ( y2 < clipminy ) clipminy = y2; if ( y1 > clipmaxy ) clipmaxy = y1; } } void wxPLDevBase::PSDrawText( PLUNICODE* ucs4, int ucs4Len, bool drawText ) { int i = 0; char utf8_string[max_string_length]; char utf8[5]; memset( utf8_string, '\0', max_string_length ); // Get PLplot escape character char plplotEsc; plgesc( &plplotEsc ); //Reset the size metrics textWidth = 0; textHeight = 0; superscriptHeight = 0; subscriptDepth = 0; while ( i < ucs4Len ) { if ( ucs4[i] < PL_FCI_MARK ) // not a font change { if ( ucs4[i] != (PLUNICODE) plplotEsc ) // a character to display { ucs4_to_utf8( ucs4[i], utf8 ); strncat( utf8_string, utf8, sizeof ( utf8_string ) - strlen( utf8_string ) - 1 ); i++; continue; } i++; if ( ucs4[i] == (PLUNICODE) plplotEsc ) // a escape character to display { ucs4_to_utf8( ucs4[i], utf8 ); strncat( utf8_string, utf8, sizeof ( utf8_string ) - strlen( utf8_string ) - 1 ); i++; continue; } else { if ( ucs4[i] == (PLUNICODE) 'u' ) // Superscript { // draw string so far PSDrawTextToDC( utf8_string, drawText ); // change font scale if ( yOffset < -0.0001 ) fontScale *= 1.25; // Subscript scaling parameter else fontScale *= 0.8; // Subscript scaling parameter PSSetFont( fci ); yOffset += scaley * fontSize * fontScale / 2.; } if ( ucs4[i] == (PLUNICODE) 'd' ) // Subscript { // draw string so far PSDrawTextToDC( utf8_string, drawText ); // change font scale double old_fontScale = fontScale; if ( yOffset > 0.0001 ) fontScale *= 1.25; // Subscript scaling parameter else fontScale *= 0.8; // Subscript scaling parameter PSSetFont( fci ); yOffset -= scaley * fontSize * old_fontScale / 2.; } if ( ucs4[i] == (PLUNICODE) '-' ) // underline { // draw string so far PSDrawTextToDC( utf8_string, drawText ); underlined = !underlined; PSSetFont( fci ); } if ( ucs4[i] == (PLUNICODE) '+' ) // overline { // not implemented yet } i++; } } else // a font change { // draw string so far PSDrawTextToDC( utf8_string, drawText ); // get new font fci = ucs4[i]; PSSetFont( fci ); i++; } } PSDrawTextToDC( utf8_string, drawText ); } //-------------------------------------------------------------------------- // void common_init( PLStream *pls ) // // Basic initialization for all devices. //-------------------------------------------------------------------------- wxPLDevBase* common_init( PLStream *pls ) { // Log_Verbose( "common_init()" ); wxPLDevBase* dev; PLFLT downscale, downscale2; // default options static PLINT freetype = -1; static PLINT smooth_text = 1; static PLINT text = -1; static PLINT hrshsym = 0; // default backend uses wxGraphicsContext, if not available // the agg library will be used, if not available the basic // backend will be used. static PLINT backend = wxBACKEND_DC; #if wxUSE_GRAPHICS_CONTEXT backend = wxBACKEND_GC; #else #ifdef HAVE_AGG backend = wxBACKEND_AGG; #endif #endif DrvOpt wx_options[] = { #ifdef PL_HAVE_FREETYPE { "freetype", DRV_INT, &freetype, "Use FreeType library" }, { "smooth", DRV_INT, &smooth_text, "Turn text smoothing on (1) or off (0)" }, #endif { "hrshsym", DRV_INT, &hrshsym, "Use Hershey symbol set (hrshsym=0|1)" }, { "backend", DRV_INT, &backend, "Choose backend: (0) standard, (1) using AGG library, (2) using wxGraphicsContext" }, { "text", DRV_INT, &text, "Use own text routines (text=0|1)" }, { NULL, DRV_INT, NULL, NULL } }; // Check for and set up driver options plParseDrvOpts( wx_options ); // allocate memory for the device storage switch ( backend ) { case wxBACKEND_GC: // in case wxGraphicsContext isn't available, the next backend (agg // if available) in this list will be used #if wxUSE_GRAPHICS_CONTEXT dev = new wxPLDevGC; // by default the own text routines are used for wxGC if ( text == -1 ) text = 1; freetype = 0; // this backend is vector oriented and doesn't know pixels break; #endif case wxBACKEND_AGG: // in case the agg library isn't available, the standard backend // will be used #ifdef HAVE_AGG dev = new wxPLDevAGG; // by default the freetype text routines are used for wxAGG text = 0; // text processing doesn't work yet for the AGG backend if ( freetype == -1 ) freetype = 1; break; #endif default: dev = new wxPLDevDC; // by default the own text routines are used for wxDC if ( text == -1 ) { if ( freetype != 1 ) text = 1; else text = 0; } if ( freetype == -1 ) freetype = 0; break; } if ( dev == NULL ) { plexit( "Insufficient memory" ); } pls->dev = (void *) dev; // be verbose and write out debug messages #ifdef _DEBUG pls->verbose = 1; pls->debug = 1; #endif pls->color = 1; // Is a color device pls->dev_flush = 1; // Handles flushes pls->dev_fill0 = 1; // Can handle solid fills pls->dev_fill1 = 0; // Can't handle pattern fills pls->dev_dash = 0; pls->dev_clear = 1; // driver supports clear if ( text ) { pls->dev_text = 1; // want to draw text pls->dev_unicode = 1; // want unicode if ( hrshsym ) pls->dev_hrshsym = 1; } #ifdef PL_HAVE_FREETYPE // own text routines have higher priority over freetype // if text and freetype option are set to 1 if ( !text ) { dev->smooth_text = smooth_text; dev->freetype = freetype; } if ( dev->freetype ) { pls->dev_text = 1; // want to draw text pls->dev_unicode = 1; // want unicode if ( hrshsym ) pls->dev_hrshsym = 1; init_freetype_lv1( pls ); FT_Data* FT = (FT_Data *) pls->FT; FT->want_smooth_text = smooth_text; } #endif // initialize frame size and position if ( pls->xlength <= 0 || pls->ylength <= 0 ) plspage( 0.0, 0.0, (PLINT) ( CANVAS_WIDTH * DEVICE_PIXELS_PER_IN ), (PLINT) ( CANVAS_HEIGHT * DEVICE_PIXELS_PER_IN ), 0, 0 ); dev->width = pls->xlength; dev->height = pls->ylength; dev->clipminx = pls->xlength; dev->clipminy = pls->ylength; if ( pls->xoffset != 0 || pls->yoffset != 0 ) { dev->xpos = (int) ( pls->xoffset ); dev->ypos = (int) ( pls->yoffset ); } // If portrait mode, apply a rotation and set freeaspect if ( pls->portrait ) { plsdiori( (PLFLT) ( 4 - ORIENTATION ) ); pls->freeaspect = 1; } // Set the number of pixels per mm plP_setpxl( (PLFLT) VIRTUAL_PIXELS_PER_MM, (PLFLT) VIRTUAL_PIXELS_PER_MM ); // Set up physical limits of plotting device (in drawing units) downscale = (double) dev->width / (double) ( PIXELS_X - 1 ); downscale2 = (double) dev->height / (double) PIXELS_Y; if ( downscale < downscale2 ) downscale = downscale2; plP_setphy( (PLINT) 0, (PLINT) ( dev->width / downscale ), (PLINT) 0, (PLINT) ( dev->height / downscale ) ); // get physical device limits coordinates plP_gphy( &dev->xmin, &dev->xmax, &dev->ymin, &dev->ymax ); // setting scale factors dev->scalex = (PLFLT) ( dev->xmax - dev->xmin ) / ( dev->width ); dev->scaley = (PLFLT) ( dev->ymax - dev->ymin ) / ( dev->height ); // set dpi plspage( VIRTUAL_PIXELS_PER_IN / dev->scalex, VIRTUAL_PIXELS_PER_IN / dev->scaley, 0, 0, 0, 0 ); #ifdef PL_HAVE_FREETYPE if ( dev->freetype ) init_freetype_lv2( pls ); #endif // find out what file drivers are available plgFileDevs( &dev->devDesc, &dev->devName, &dev->ndev ); return dev; } #ifdef PLD_wxwidgets //-------------------------------------------------------------------------- // void plD_dispatch_init_wxwidgets( PLDispatchTable *pdt ) // // Make wxwidgets driver functions known to plplot. //-------------------------------------------------------------------------- void plD_dispatch_init_wxwidgets( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "wxWidgets DC"; pdt->pl_DevName = "wxwidgets"; #endif pdt->pl_type = plDevType_Interactive; pdt->pl_seq = 51; pdt->pl_init = (plD_init_fp) plD_init_wxwidgets; pdt->pl_line = (plD_line_fp) plD_line_wxwidgets; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_wxwidgets; pdt->pl_eop = (plD_eop_fp) plD_eop_wxwidgets; pdt->pl_bop = (plD_bop_fp) plD_bop_wxwidgets; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_wxwidgets; pdt->pl_state = (plD_state_fp) plD_state_wxwidgets; pdt->pl_esc = (plD_esc_fp) plD_esc_wxwidgets; } //-------------------------------------------------------------------------- // plD_init_wxwidgets( PLStream* pls ) // // Initialize wxWidgets device. //-------------------------------------------------------------------------- void plD_init_wxwidgets( PLStream* pls ) { // Log_Verbose( "plD_init_wxwidgets()" ); wxPLDevBase* dev; dev = common_init( pls ); pls->plbuf_write = 1; // use the plot buffer! pls->termin = 1; // interactive device pls->graphx = GRAPHICS_MODE; // No text mode for this driver (at least for now, might add a console window if I ever figure it out and have the inclination) dev->showGUI = true; dev->bitmapType = (wxBitmapType) 0; } #endif // PLD_wxwidgets #ifdef PLD_wxpng //-------------------------------------------------------------------------- // void plD_dispatch_init_wxpng( PLDispatchTable *pdt ) // // Make wxpng driver functions known to plplot. //-------------------------------------------------------------------------- void plD_dispatch_init_wxpng( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "wxWidgets PNG driver"; pdt->pl_DevName = "wxpng"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 52; pdt->pl_init = (plD_init_fp) plD_init_wxpng; pdt->pl_line = (plD_line_fp) plD_line_wxwidgets; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_wxwidgets; pdt->pl_eop = (plD_eop_fp) plD_eop_wxwidgets; pdt->pl_bop = (plD_bop_fp) plD_bop_wxwidgets; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_wxwidgets; pdt->pl_state = (plD_state_fp) plD_state_wxwidgets; pdt->pl_esc = (plD_esc_fp) plD_esc_wxwidgets; } //-------------------------------------------------------------------------- // void plD_init_wxpng( PLStream *pls ) // // Initialize wxpng device. //-------------------------------------------------------------------------- void plD_init_wxpng( PLStream *pls ) { // Log_Verbose( "plD_init_wxwidgets()" ); wxPLDevBase* dev; dev = common_init( pls ); // Initialize family file info plFamInit( pls ); // Prompt for a file name if not already set. plOpenFile( pls ); pls->plbuf_write = 1; // use the plot buffer! pls->dev_flush = 0; // No need for flushes pls->termin = 0; // file oriented device pls->graphx = GRAPHICS_MODE; // No text mode for this driver (at least for now, might add a console window if I ever figure it out and have the inclination) pls->page = 0; dev->showGUI = false; dev->bitmapType = wxBITMAP_TYPE_PNG; } #endif // PLD_wxpng //-------------------------------------------------------------------------- // void plD_line_wxwidgets( PLStream *pls, short x1a, short y1a, // short x2a, short y2a ) // // Draws a line from (x1a, y1a) to (x2a, y2a). //-------------------------------------------------------------------------- void plD_line_wxwidgets( PLStream *pls, short x1a, short y1a, short x2a, short y2a ) { // Log_Verbose( "plD_line_wxwidgets(x1a=%d, y1a=%d, x2a=%d, y2a=%d)", x1a, y1a, x2a, y2a ); wxPLDevBase* dev = (wxPLDevBase *) pls->dev; if ( !( dev->ready ) ) install_buffer( pls ); dev->DrawLine( x1a, y1a, x2a, y2a ); if ( !( dev->resizing ) && dev->ownGUI ) { dev->comcount++; if ( dev->comcount > MAX_COMCOUNT ) { wxRunApp( pls, true ); dev->comcount = 0; } } } //-------------------------------------------------------------------------- // void plD_polyline_wxwidgets( PLStream *pls, short *xa, short *ya, // PLINT npts ) // // Draw a poly line - points are in xa and ya arrays. //-------------------------------------------------------------------------- void plD_polyline_wxwidgets( PLStream *pls, short *xa, short *ya, PLINT npts ) { // Log_Verbose( "plD_polyline_wxwidgets()" ); // should be changed to use the wxDC::DrawLines function? wxPLDevBase* dev = (wxPLDevBase *) pls->dev; if ( !( dev->ready ) ) install_buffer( pls ); dev->DrawPolyline( xa, ya, npts ); if ( !( dev->resizing ) && dev->ownGUI ) { dev->comcount++; if ( dev->comcount > MAX_COMCOUNT ) { wxRunApp( pls, true ); dev->comcount = 0; } } } //-------------------------------------------------------------------------- // void plD_eop_wxwidgets( PLStream *pls ) // // End of Page. This function is called if a "end of page" is send by the // user. This command is ignored if we have the plot embedded in a // wxWidgets application, otherwise the application created by the device // takes over. //-------------------------------------------------------------------------- void plD_eop_wxwidgets( PLStream *pls ) { // Log_Verbose( "plD_eop_wxwidgets()" ); wxPLDevBase* dev = (wxPLDevBase *) pls->dev; if ( dev->bitmapType ) { wxMemoryDC memDC; wxBitmap bitmap( dev->width, dev->height, -1 ); memDC.SelectObject( bitmap ); dev->BlitRectangle( &memDC, 0, 0, dev->width, dev->height ); wxImage buffer = bitmap.ConvertToImage(); wxFFileOutputStream fstream( pls->OutFile ); if ( !( buffer.SaveFile( fstream, dev->bitmapType ) ) ) puts( "Troubles saving file!" ); memDC.SelectObject( wxNullBitmap ); } if ( dev->ownGUI && !dev->resizing ) { if ( pls->nopause || !dev->showGUI ) wxRunApp( pls, true ); else wxRunApp( pls ); } } //-------------------------------------------------------------------------- // void plD_bop_wxwidgets( PLStream *pls ) // // Begin of page. Before any plot command, this function is called, If we // have already a dc the background is cleared in background color and some // state calls are resent - this is because at the first call of this // function, a dc does most likely not exist, but state calls are recorded // and when a new dc is created this function is called again. //-------------------------------------------------------------------------- void plD_bop_wxwidgets( PLStream *pls ) { // Log_Verbose( "plD_bop_wxwidgets()" ); wxPLDevBase* dev = (wxPLDevBase *) pls->dev; if ( dev->ready ) { //if( pls->termin==0 ) { // plGetFam( pls ); // force new file if pls->family set for all subsequent calls to plGetFam // n.b. putting this after plGetFam call is important since plinit calls // bop, and you don't want the familying sequence started until after // that first call to bop. // n.b. pls->dev can change because of an indirect call to plD_init_png // from plGetFam if familying is enabled. Thus, wait to define dev until // now. //dev = (wxPLDevBase*)pls->dev; // // pls->famadv = 1; // pls->page++; // } // clear background PLINT bgr, bgg, bgb; // red, green, blue plgcolbg( &bgr, &bgg, &bgb ); // get background color information dev->ClearBackground( bgr, bgg, bgb ); // Replay escape calls that come in before PLESC_DEVINIT. All of them // required a DC that didn't exist yet. // if ( dev->plstate_width ) plD_state_wxwidgets( pls, PLSTATE_WIDTH ); dev->plstate_width = false; if ( dev->plstate_color0 ) plD_state_wxwidgets( pls, PLSTATE_COLOR0 ); dev->plstate_color0 = false; if ( dev->plstate_color1 ) plD_state_wxwidgets( pls, PLSTATE_COLOR1 ); dev->plstate_color1 = false; // why this? xwin driver has this // pls->page++; } } //-------------------------------------------------------------------------- // void plD_tidy_wxwidgets( PLStream *pls ) // // This function is called, if all plots are done. //-------------------------------------------------------------------------- void plD_tidy_wxwidgets( PLStream *pls ) { // Log_Verbose( "plD_tidy_wxwidgets()" ); wxPLDevBase* dev = (wxPLDevBase *) pls->dev; #ifdef PL_HAVE_FREETYPE if ( dev->freetype ) { FT_Data *FT = (FT_Data *) pls->FT; plscmap0n( FT->ncol0_org ); plD_FreeType_Destroy( pls ); } #endif if ( dev->ownGUI ) { wxPLGetApp().RemoveFrame( dev->m_frame ); if ( !wxPLGetApp().FrameCount() ) wxUninitialize(); } delete dev; pls->dev = NULL; // since in plcore.c pls->dev is free_mem'd } //-------------------------------------------------------------------------- // void plD_state_wxwidgets( PLStream *pls, PLINT op ) // // Handler for several state codes. Here we take care of setting the width // and color of the pen. //-------------------------------------------------------------------------- void plD_state_wxwidgets( PLStream *pls, PLINT op ) { // Log_Verbose( "plD_state_wxwidgets(op=%d)", op ); wxPLDevBase* dev = (wxPLDevBase *) pls->dev; switch ( op ) { case PLSTATE_WIDTH: // 1 if ( dev->ready ) dev->SetWidth( pls ); else dev->plstate_width = true; break; case PLSTATE_COLOR0: // 2 if ( dev->ready ) dev->SetColor0( pls ); else dev->plstate_color0 = true; break; case PLSTATE_COLOR1: // 3 if ( dev->ready ) dev->SetColor1( pls ); else dev->plstate_color1 = true; break; //For all these state changes we don't need to do anything //and if they occur before/during initialization we don't //want to call install_buffer case PLSTATE_FILL: case PLSTATE_CMAP0: case PLSTATE_CMAP1: case PLSTATE_CHR: case PLSTATE_SYM: break; default: if ( !( dev->ready ) ) install_buffer( pls ); } } //-------------------------------------------------------------------------- // void plD_esc_wxwidgets( PLStream *pls, PLINT op, void *ptr ) // // Handler for several escape codes. Here we take care of filled polygons, // XOR or copy mode, initialize device (install dc from outside), and if // there is freetype support, rerendering of text. //-------------------------------------------------------------------------- void plD_esc_wxwidgets( PLStream *pls, PLINT op, void *ptr ) { // Log_Verbose( "plD_esc_wxwidgets(op=%d, ptr=%x)", op, ptr ); wxPLDevBase* dev = (wxPLDevBase *) pls->dev; switch ( op ) { case PLESC_FILL: fill_polygon( pls ); break; case PLESC_XORMOD: // switch between wxXOR and wxCOPY // if( dev->ready ) { // if( dev->m_dc->GetLogicalFunction() == wxCOPY ) // dev->m_dc->SetLogicalFunction( wxXOR ); // else if( dev->m_dc->GetLogicalFunction() == wxXOR ) // dev->m_dc->SetLogicalFunction( wxCOPY ); // } break; case PLESC_DEVINIT: dev->SetExternalBuffer( ptr ); // replay begin of page call and state settings plD_bop_wxwidgets( pls ); break; case PLESC_HAS_TEXT: if ( !( dev->ready ) ) install_buffer( pls ); if ( dev->freetype ) { #ifdef PL_HAVE_FREETYPE plD_render_freetype_text( pls, (EscText *) ptr ); #endif } else dev->ProcessString( pls, (EscText *) ptr ); break; case PLESC_RESIZE: { wxSize* size = (wxSize *) ptr; wx_set_size( pls, size->GetWidth(), size->GetHeight() ); } break; case PLESC_CLEAR: if ( !( dev->ready ) ) install_buffer( pls ); // Since the plot is updated only every MAX_COMCOUNT commands (usually 5000) // before we clear the screen we need to show the plot at least once :) if ( !( dev->resizing ) && dev->ownGUI ) { wxRunApp( pls, true ); dev->comcount = 0; } dev->ClearBackground( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b, pls->sppxmi, pls->sppymi, pls->sppxma, pls->sppyma ); break; case PLESC_FLUSH: // forced update of the window if ( !( dev->resizing ) && dev->ownGUI ) { wxRunApp( pls, true ); dev->comcount = 0; } break; case PLESC_GETC: if ( dev->ownGUI ) GetCursorCmd( pls, (PLGraphicsIn *) ptr ); break; case PLESC_GETBACKEND: *( (int *) ptr ) = dev->backend; break; default: break; } } //-------------------------------------------------------------------------- // static void fill_polygon( PLStream *pls ) // // Fill polygon described in points pls->dev_x[] and pls->dev_y[]. //-------------------------------------------------------------------------- static void fill_polygon( PLStream *pls ) { // Log_Verbose( "fill_polygon(), npts=%d, x[0]=%d, y[0]=%d", pls->dev_npts, pls->dev_y[0], pls->dev_y[0] ); wxPLDevBase* dev = (wxPLDevBase *) pls->dev; if ( !( dev->ready ) ) install_buffer( pls ); dev->FillPolygon( pls ); if ( !( dev->resizing ) && dev->ownGUI ) { dev->comcount += 10; if ( dev->comcount > MAX_COMCOUNT ) { wxRunApp( pls, true ); dev->comcount = 0; } } } //-------------------------------------------------------------------------- // void wx_set_size( PLStream* pls, int width, int height ) // // Adds a dc to the stream. The associated device is attached to the canvas // as the property "dev". //-------------------------------------------------------------------------- void wx_set_size( PLStream* pls, int width, int height ) { // TODO: buffer must be resized here or in wxplotstream // Log_Verbose( "wx_set_size()" ); wxPLDevBase* dev = (wxPLDevBase *) pls->dev; // set new size and scale parameters dev->width = width; dev->height = height; dev->scalex = (PLFLT) ( dev->xmax - dev->xmin ) / dev->width; dev->scaley = (PLFLT) ( dev->ymax - dev->ymin ) / dev->height; // recalculate the dpi used in calculation of fontsize pls->xdpi = VIRTUAL_PIXELS_PER_IN / dev->scalex; pls->ydpi = VIRTUAL_PIXELS_PER_IN / dev->scaley; // clear background if we have a dc, since it's invalid (TODO: why, since in bop // it must be cleared anyway?) if ( dev->ready ) { PLINT bgr, bgg, bgb; // red, green, blue plgcolbg( &bgr, &bgg, &bgb ); // get background color information dev->CreateCanvas(); dev->ClearBackground( bgr, bgg, bgb ); } // freetype parameters must also be changed #ifdef PL_HAVE_FREETYPE if ( dev->freetype ) { FT_Data *FT = (FT_Data *) pls->FT; FT->scalex = dev->scalex; FT->scaley = dev->scaley; FT->ymax = dev->height; } #endif } //-------------------------------------------------------------------------- // int plD_errorexithandler_wxwidgets( const char *errormessage ) // // If an PLplot error occurs, this function shows a dialog regarding // this error and than exits. //-------------------------------------------------------------------------- int plD_errorexithandler_wxwidgets( const char *errormessage ) { if ( errormessage[0] ) { wxMessageDialog dialog( 0, wxString( errormessage, *wxConvCurrent ), wxString( "wxWidgets PLplot App error", *wxConvCurrent ), wxOK | wxICON_ERROR ); dialog.ShowModal(); } return 0; } //-------------------------------------------------------------------------- // void plD_erroraborthandler_wxwidgets( const char *errormessage ) // // If PLplot aborts, this function shows a dialog regarding // this error. //-------------------------------------------------------------------------- void plD_erroraborthandler_wxwidgets( const char *errormessage ) { if ( errormessage[0] ) { wxMessageDialog dialog( 0, ( wxString( errormessage, *wxConvCurrent ) + wxString( " aborting operation...", *wxConvCurrent ) ), wxString( "wxWidgets PLplot App abort", *wxConvCurrent ), wxOK | wxICON_ERROR ); dialog.ShowModal(); } } #ifdef PL_HAVE_FREETYPE //-------------------------------------------------------------------------- // static void plD_pixel_wxwidgets( PLStream *pls, short x, short y ) // // callback function, of type "plD_pixel_fp", which specifies how a single // pixel is set in the current colour. //-------------------------------------------------------------------------- static void plD_pixel_wxwidgets( PLStream *pls, short x, short y ) { // Log_Verbose( "plD_pixel_wxwidgets" ); wxPLDevBase *dev = (wxPLDevBase *) pls->dev; if ( !( dev->ready ) ) install_buffer( pls ); dev->PutPixel( x, y ); if ( !( dev->resizing ) && dev->ownGUI ) { dev->comcount++; if ( dev->comcount > MAX_COMCOUNT ) { wxRunApp( pls, true ); dev->comcount = 0; } } } //-------------------------------------------------------------------------- // static void plD_pixel_wxwidgets( PLStream *pls, short x, short y ) // // callback function, of type "plD_pixel_fp", which specifies how a single // pixel is set in the current colour. //-------------------------------------------------------------------------- static void plD_set_pixel_wxwidgets( PLStream *pls, short x, short y, PLINT colour ) { // Log_Verbose( "plD_set_pixel_wxwidgets" ); wxPLDevBase *dev = (wxPLDevBase *) pls->dev; if ( !( dev->ready ) ) install_buffer( pls ); dev->PutPixel( x, y, colour ); if ( !( dev->resizing ) && dev->ownGUI ) { dev->comcount++; if ( dev->comcount > MAX_COMCOUNT ) { wxRunApp( pls, true ); dev->comcount = 0; } } } //-------------------------------------------------------------------------- // void plD_read_pixel_wxwidgets (PLStream *pls, short x, short y) // // callback function, of type "plD_pixel_fp", which specifies how a single // pixel is read. //-------------------------------------------------------------------------- static PLINT plD_read_pixel_wxwidgets( PLStream *pls, short x, short y ) { // Log_Verbose( "plD_read_pixel_wxwidgets" ); wxPLDevBase *dev = (wxPLDevBase *) pls->dev; if ( !( dev->ready ) ) install_buffer( pls ); return dev->GetPixel( x, y ); } //-------------------------------------------------------------------------- // void init_freetype_lv1 (PLStream *pls) // // "level 1" initialisation of the freetype library. // "Level 1" initialisation calls plD_FreeType_init(pls) which allocates // memory to the pls->FT structure, then sets up the pixel callback // function. //-------------------------------------------------------------------------- static void init_freetype_lv1( PLStream *pls ) { // Log_Verbose( "init_freetype_lv1" ); plD_FreeType_init( pls ); FT_Data *FT = (FT_Data *) pls->FT; FT->pixel = (plD_pixel_fp) plD_pixel_wxwidgets; // // See if we have a 24 bit device (or better), in which case // we can use the better antialising. // // the bitmap we are using in the antialized case has always // 32 bit depth FT->BLENDED_ANTIALIASING = 1; FT->read_pixel = (plD_read_pixel_fp) plD_read_pixel_wxwidgets; FT->set_pixel = (plD_set_pixel_fp) plD_set_pixel_wxwidgets; } //-------------------------------------------------------------------------- // void init_freetype_lv2 (PLStream *pls) // // "Level 2" initialisation of the freetype library. // "Level 2" fills in a few setting that aren't public until after the // graphics sub-syetm has been initialised. // The "level 2" initialisation fills in a few things that are defined // later in the initialisation process for the GD driver. // // FT->scale is a scaling factor to convert co-ordinates. This is used by // the GD and other drivers to scale back a larger virtual page and this // eliminate the "hidden line removal bug". Set it to 1 if your device // doesn't have scaling. // // Some coordinate systems have zero on the bottom, others have zero on // the top. Freetype does it one way, and most everything else does it the // other. To make sure everything is working ok, we have to "flip" the // coordinates, and to do this we need to know how big in the Y dimension // the page is, and whether we have to invert the page or leave it alone. // // FT->ymax specifies the size of the page FT->invert_y=1 tells us to // invert the y-coordinates, FT->invert_y=0 will not invert the // coordinates. //-------------------------------------------------------------------------- static void init_freetype_lv2( PLStream *pls ) { // Log_Verbose( "init_freetype_lv2" ); wxPLDevBase *dev = (wxPLDevBase *) pls->dev; FT_Data *FT = (FT_Data *) pls->FT; FT->scalex = dev->scalex; FT->scaley = dev->scaley; FT->ymax = dev->height; FT->invert_y = 1; FT->smooth_text = 0; if ( ( FT->want_smooth_text == 1 ) && ( FT->BLENDED_ANTIALIASING == 0 ) ) // do we want to at least *try* for smoothing ? { FT->ncol0_org = pls->ncol0; // save a copy of the original size of ncol0 FT->ncol0_xtra = 16777216 - ( pls->ncol1 + pls->ncol0 ); // work out how many free slots we have FT->ncol0_width = FT->ncol0_xtra / ( pls->ncol0 - 1 ); // find out how many different shades of anti-aliasing we can do if ( FT->ncol0_width > 4 ) // are there enough colour slots free for text smoothing ? { if ( FT->ncol0_width > max_number_of_grey_levels_used_in_text_smoothing ) FT->ncol0_width = max_number_of_grey_levels_used_in_text_smoothing; // set a maximum number of shades plscmap0n( FT->ncol0_org + ( FT->ncol0_width * pls->ncol0 ) ); // redefine the size of cmap0 // the level manipulations are to turn off the plP_state(PLSTATE_CMAP0) // call in plscmap0 which (a) leads to segfaults since the GD image is // not defined at this point and (b) would be inefficient in any case since // setcmap is always called later (see plD_bop_png) to update the driver // color palette to be consistent with cmap0. { PLINT level_save; level_save = pls->level; pls->level = 0; pl_set_extended_cmap0( pls, FT->ncol0_width, FT->ncol0_org ); // call the function to add the extra cmap0 entries and calculate stuff pls->level = level_save; } FT->smooth_text = 1; // Yippee ! We had success setting up the extended cmap0 } else plwarn( "Insufficient colour slots available in CMAP0 to do text smoothing." ); } 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 { FT->smooth_text = 1; } } #endif //-------------------------------------------------------------------------- // GetCursorCmd() // // Waits for a graphics input event and returns coordinates. //-------------------------------------------------------------------------- static void GetCursorCmd( PLStream* pls, PLGraphicsIn* ptr ) { // Log_Verbose( "GetCursorCmd" ); wxPLDevBase *dev = (wxPLDevBase *) pls->dev; PLGraphicsIn *gin = &( dev->gin ); // Initialize plGinInit( gin ); dev->locate_mode = LOCATE_INVOKED_VIA_API; dev->draw_xhair = true; // Run event loop until a point is selected wxRunApp( pls, false ); *ptr = *gin; if ( dev->locate_mode ) { dev->locate_mode = 0; dev->draw_xhair = false; } } //-------------------------------------------------------------------------- // This part includes wxWidgets specific functions, which allow to // open a window from the command line, if needed. //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- // void install_buffer( PLStream *pls ) // // If this driver is called from a command line executable (and not // from within a wxWidgets program), this function prepares a DC and a // bitmap to plot into. //-------------------------------------------------------------------------- static void install_buffer( PLStream *pls ) { // Log_Verbose( "install_buffer" ); wxPLDevBase * dev = (wxPLDevBase *) pls->dev; static bool initApp = false; if ( !initApp ) { // this hack enables to have a GUI on Mac OSX even if the // program was called from the command line (and isn't a bundle) #ifdef __WXMAC__ ProcessSerialNumber psn; GetCurrentProcess( &psn ); CPSEnableForegroundOperation( &psn ); SetFrontProcess( &psn ); #endif // initialize wxWidgets wxInitialize(); wxLog::GetActiveTarget(); wxTRY { wxPLGetApp().CallOnInit(); } wxCATCH_ALL( wxPLGetApp().OnUnhandledException(); plexit( "Can't init wxWidgets!" ); ) initApp = true; } wxString title( pls->plwindow, *wxConvCurrent ); switch ( dev->backend ) { case wxBACKEND_DC: title += wxT( " - wxWidgets (basic)" ); break; case wxBACKEND_GC: title += wxT( " - wxWidgets (wxGC)" ); break; case wxBACKEND_AGG: title += wxT( " - wxWidgets (AGG)" ); break; default: break; } dev->m_frame = new wxPLplotFrame( title, pls ); wxPLGetApp().AddFrame( dev->m_frame ); // set size and position of window dev->m_frame->SetClientSize( dev->width, dev->height ); if ( dev->xpos != 0 || dev->ypos != 0 ) dev->m_frame->SetSize( dev->xpos, dev->ypos, wxDefaultCoord, wxDefaultCoord, wxSIZE_USE_EXISTING ); if ( dev->showGUI ) { dev->m_frame->Show( true ); dev->m_frame->Raise(); } else dev->m_frame->Show( false ); // get a DC and a bitmap or an imagebuffer dev->ownGUI = true; dev->bm_width = dev->width; dev->bm_height = dev->height; dev->CreateCanvas(); dev->ready = true; // Set wx error handler for various errors in plplot plsexit( plD_errorexithandler_wxwidgets ); plsabort( plD_erroraborthandler_wxwidgets ); // replay command we may have missed plD_bop_wxwidgets( pls ); } //-------------------------------------------------------------------------- // void wxRunApp( PLStream *pls, bool runonce ) // // This is a hacked wxEntry-function, so that wxUninitialize is not // called twice. Here we actually start the wxApplication. //-------------------------------------------------------------------------- static void wxRunApp( PLStream *pls, bool runonce ) { // Log_Verbose( "wxRunApp" ); wxPLDevBase* dev = (wxPLDevBase *) pls->dev; dev->waiting = true; wxTRY { class CallOnExit { public: // only call OnExit if exit is true (i.e. due an exception) ~CallOnExit() { if ( exit ) wxPLGetApp().OnExit();} bool exit; } callOnExit; callOnExit.exit = true; wxPLGetApp().SetAdvanceFlag( runonce ); wxPLGetApp().SetRefreshFlag(); // add an idle event is necessary for Linux (wxGTK2) // but not for Windows, but it doesn't harm wxIdleEvent event; wxPLGetApp().AddPendingEvent( event ); wxPLGetApp().OnRun(); // start wxWidgets application callOnExit.exit = false; } wxCATCH_ALL( wxPLGetApp().OnUnhandledException(); plexit( "Problem running wxWidgets!" ); ) if ( dev->exit ) { wxPLGetApp().OnExit(); plexit( "" ); } dev->waiting = false; } plplot-5.13.0/drivers/ntk.driver_info.in000644 001752 001752 00000000037 13150160115 021744 0ustar00softwaresoftware000000 000000 ntk:New tk driver:1:ntk:43:ntk plplot-5.13.0/drivers/pdf.driver_info.in000644 001752 001752 00000000056 13150160115 021722 0ustar00softwaresoftware000000 000000 pdf:Portable Document Format PDF:1:pdf:58:pdf plplot-5.13.0/drivers/xfig.driver_info.in000644 001752 001752 00000000035 13150160115 022103 0ustar00softwaresoftware000000 000000 xfig:Fig file:0:xfig:31:xfig plplot-5.13.0/drivers/wingdi.driver_info.in000644 001752 001752 00000000045 13150160115 022430 0ustar00softwaresoftware000000 000000 wingdi:Windows GDI:1:wingdi:11:wingdiplplot-5.13.0/drivers/plmeta.c000644 001752 001752 00000062277 13150160115 017757 0ustar00softwaresoftware000000 000000 // Copyright (C) 1991, 1992, 1993, 1994, 1995 Geoffrey Furnish // Copyright (C) 1991, 1992, 1993, 1994, 1995 Maurice LeBrun // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // //-------------------------------------------------------------------------- // // This is a metafile writer for PLplot. // // #include "plDevs.h" //#define DEBUG #ifdef PLD_plmeta #define NEED_PLDEBUG #include "plplotP.h" #include "drivers.h" #include "metadefs.h" #include // Device info PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_plmeta = "plmeta:PLplot Native Meta-File:0:plmeta:26:plm\n"; void plD_dispatch_init_plm( PLDispatchTable *pdt ); void plD_init_plm( PLStream * ); void plD_line_plm( PLStream *, short, short, short, short ); void plD_polyline_plm( PLStream *, short *, short *, PLINT ); void plD_eop_plm( PLStream * ); void plD_bop_plm( PLStream * ); void plD_tidy_plm( PLStream * ); void plD_state_plm( PLStream *, PLINT ); void plD_esc_plm( PLStream *, PLINT, void * ); // Struct to hold device-specific info. // Used for constructing error messages #define BUFFER_LEN 256 // Function prototypes static void WriteFileHeader( PLStream *pls ); static void UpdatePrevPagehdr( PLStream *pls ); static void WritePageInfo( PLStream *pls, FPOS_T pp_offset ); static void UpdateIndex( PLStream *pls, FPOS_T cp_offset ); static void plm_fill( PLStream *pls ); static void plm_swin( PLStream *pls ); static void plm_text( PLStream *pls, EscText *args ); // A little function to help with debugging #ifdef DEBUG #define DEBUG_PRINT_LOCATION( a ) PrintLocation( pls, a ) static void PrintLocation( PLStream *pls, char *tag ) { int isfile = ( pls->output_type == 0 ); if ( isfile ) { FILE *file = pls->OutFile; FPOS_T current_offset; if ( pl_fgetpos( file, ¤t_offset ) ) plexit( "PrintLocation (plmeta.c): fgetpos call failed" ); pldebug( tag, "at offset %d in file %s\n", (int) current_offset, pls->FileName ); } } #else #define DEBUG_PRINT_LOCATION( a ) #endif void plD_dispatch_init_plm( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "PLplot Native Meta-File"; pdt->pl_DevName = "plmeta"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 26; pdt->pl_init = (plD_init_fp) plD_init_plm; pdt->pl_line = (plD_line_fp) plD_line_plm; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_plm; pdt->pl_eop = (plD_eop_fp) plD_eop_plm; pdt->pl_bop = (plD_bop_fp) plD_bop_plm; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_plm; pdt->pl_state = (plD_state_fp) plD_state_plm; pdt->pl_esc = (plD_esc_fp) plD_esc_plm; } //-------------------------------------------------------------------------- // plD_init_plm() // // Initialize device. //-------------------------------------------------------------------------- void plD_init_plm( PLStream *pls ) { PLmDev *dev; dbug_enter( "plD_init_plm" ); pls->color = 1; // Is a color device pls->dev_fill0 = 1; // Handle solid fills pls->dev_fill1 = 1; // Driver handles pattern fills if ( strncmp( PLMETA_VERSION, "2005", 4 ) == 0 ) { pls->dev_text = 0; // Disable text handling by the driver pls->dev_unicode = 0; // Disable unicode support pls->dev_hrshsym = 0; } else { // NOTE: This breaks compatibility with the 2005 version of // the plot metafile format // Unicode support is not needed because the plmeta driver // stores the unprocessed string data that was passed to PLplot. // However, we turn it on to force unicode representation of the // plot symbols in plsym.c rather than vectorization. pls->dev_text = 1; // Enable text handling by the driver pls->dev_unicode = 1; // Enable unicode support pls->dev_hrshsym = 0; // Disable vectorizaton of Hershey symbols } // Initialize family file info plFamInit( pls ); // Prompt for a file name if not already set plOpenFile( pls ); pls->pdfs = pdf_finit( pls->OutFile ); // Allocate and initialize device-specific data pls->dev = calloc( 1, (size_t) sizeof ( PLmDev ) ); if ( pls->dev == NULL ) plexit( "plD_init_plm: Out of memory." ); dev = (PLmDev *) pls->dev; dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; dev->xmin = 0; dev->xmax = PIXELS_X - 1; dev->ymin = 0; dev->ymax = PIXELS_Y - 1; dev->pxlx = (double) PIXELS_X / (double) LPAGE_X; dev->pxly = (double) PIXELS_Y / (double) LPAGE_Y; plP_setpxl( dev->pxlx, dev->pxly ); plP_setphy( dev->xmin, dev->xmax, dev->ymin, dev->ymax ); // Write Metafile header. WriteFileHeader( pls ); // Write color map state info plD_state_plm( pls, PLSTATE_CMAP0 ); plD_state_plm( pls, PLSTATE_CMAP1 ); // Write initialization command. DEBUG_PRINT_LOCATION( "before init" ); plm_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) INITIALIZE ) ); } //-------------------------------------------------------------------------- // plD_line_plm() // // Draw a line in the current color from (x1,y1) to (x2,y2). //-------------------------------------------------------------------------- void plD_line_plm( PLStream *pls, short x1, short y1, short x2, short y2 ) { PLmDev *dev = (PLmDev *) pls->dev; U_SHORT xy[4]; // dbug_enter("plD_line_plm"); // Failsafe check #ifdef DEBUG if ( x1 < dev->xmin || x1 > dev->xmax || x2 < dev->xmin || x2 > dev->xmax || y1 < dev->ymin || y1 > dev->ymax || y2 < dev->ymin || y2 > dev->ymax ) { pldebug( "plD_line_plm", "coordinates out of bounds -- \nActual: (%i,%i), (%i,%i) Bounds: (%i,%i,%i,%i)\n", x1, y1, x2, y2, dev->xmin, dev->xmax, dev->ymin, dev->ymax ); } #endif // If continuation of previous line send the LINETO command, which uses // the previous (x,y) point as it's starting location. This results in a // storage reduction of not quite 50%, since the instruction length for // a LINETO is 5/9 of that for the LINE command, and given that most // graphics applications use this command heavily. // // Still not quite as efficient as tektronix format since we also send the // command each time (so shortest command is 25% larger), but a lot easier // to implement than the tek method. // if ( x1 == dev->xold && y1 == dev->yold ) { plm_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) LINETO ) ); xy[0] = x2; xy[1] = y2; plm_wr( pdf_wr_2nbytes( pls->pdfs, xy, 2 ) ); } else { plm_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) LINE ) ); xy[0] = x1; xy[1] = y1; xy[2] = x2; xy[3] = y2; plm_wr( pdf_wr_2nbytes( pls->pdfs, xy, 4 ) ); } dev->xold = x2; dev->yold = y2; } //-------------------------------------------------------------------------- // plD_polyline_plm() // // Draw a polyline in the current color. //-------------------------------------------------------------------------- void plD_polyline_plm( PLStream *pls, short *xa, short *ya, PLINT npts ) { PLmDev *dev = (PLmDev *) pls->dev; dbug_enter( "plD_polyline_plm" ); plm_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) POLYLINE ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) npts ) ); plm_wr( pdf_wr_2nbytes( pls->pdfs, (U_SHORT *) xa, npts ) ); plm_wr( pdf_wr_2nbytes( pls->pdfs, (U_SHORT *) ya, npts ) ); dev->xold = xa[npts - 1]; dev->yold = ya[npts - 1]; } //-------------------------------------------------------------------------- // plD_eop_plm() // // End of page. //-------------------------------------------------------------------------- void plD_eop_plm( PLStream *pls ) { plm_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) EOP ) ); } //-------------------------------------------------------------------------- // plD_bop_plm() // // Set up for the next page. // // Page header layout as follows: // // BOP (U_CHAR) // page number (U_SHORT) // prev page offset (U_LONG) // next page offset (U_LONG) // // Each call after the first is responsible for updating the table of // contents and the next page offset from the previous page. //-------------------------------------------------------------------------- void plD_bop_plm( PLStream *pls ) { PLmDev *dev = (PLmDev *) pls->dev; int isfile = ( pls->output_type == 0 ); FPOS_T pp_offset = dev->lp_offset; dbug_enter( "plD_bop_plm" ); dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; // Update previous page header if ( isfile ) UpdatePrevPagehdr( pls ); // Start next family file if necessary. pls->bytecnt = pls->pdfs->bp; plGetFam( pls ); // Update page counter pls->page++; // Update table of contents info & write new page header. WritePageInfo( pls, pp_offset ); } //-------------------------------------------------------------------------- // plD_tidy_plm() // // Close graphics file //-------------------------------------------------------------------------- void plD_tidy_plm( PLStream *pls ) { dbug_enter( "plD_tidy_plm" ); plm_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) CLOSE ) ); pdf_close( pls->pdfs ); free_mem( pls->dev ); } //-------------------------------------------------------------------------- // plD_state_plm() // // Handle change in PLStream state (color, pen width, fill attribute, etc). //-------------------------------------------------------------------------- void plD_state_plm( PLStream *pls, PLINT op ) { int i; dbug_enter( "plD_state_plm" ); plm_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) CHANGE_STATE ) ); plm_wr( pdf_wr_1byte( pls->pdfs, op ) ); switch ( op ) { case PLSTATE_WIDTH: plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) ( pls->width ) ) ); break; case PLSTATE_COLOR0: plm_wr( pdf_wr_2bytes( pls->pdfs, (short) pls->icol0 ) ); if ( pls->icol0 == PL_RGB_COLOR ) { plm_wr( pdf_wr_1byte( pls->pdfs, pls->curcolor.r ) ); plm_wr( pdf_wr_1byte( pls->pdfs, pls->curcolor.g ) ); plm_wr( pdf_wr_1byte( pls->pdfs, pls->curcolor.b ) ); } break; case PLSTATE_COLOR1: plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->icol1 ) ); break; case PLSTATE_FILL: plm_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) pls->patt ) ); break; case PLSTATE_CMAP0: plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->ncol0 ) ); for ( i = 0; i < pls->ncol0; i++ ) { plm_wr( pdf_wr_1byte( pls->pdfs, pls->cmap0[i].r ) ); plm_wr( pdf_wr_1byte( pls->pdfs, pls->cmap0[i].g ) ); plm_wr( pdf_wr_1byte( pls->pdfs, pls->cmap0[i].b ) ); } break; case PLSTATE_CMAP1: plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->ncol1 ) ); for ( i = 0; i < pls->ncol1; i++ ) { plm_wr( pdf_wr_1byte( pls->pdfs, pls->cmap1[i].r ) ); plm_wr( pdf_wr_1byte( pls->pdfs, pls->cmap1[i].g ) ); plm_wr( pdf_wr_1byte( pls->pdfs, pls->cmap1[i].b ) ); } break; case PLSTATE_CHR: // save the chrdef and chrht parameters if ( strncmp( PLMETA_VERSION, "2005", 4 ) != 0 ) { plm_wr( pdf_wr_ieeef( pls->pdfs, (float) pls->chrdef ) ); plm_wr( pdf_wr_ieeef( pls->pdfs, (float) pls->chrht ) ); } break; case PLSTATE_SYM: // save the symdef and symht parameters if ( strncmp( PLMETA_VERSION, "2005", 4 ) != 0 ) { plm_wr( pdf_wr_ieeef( pls->pdfs, (float) pls->symdef ) ); plm_wr( pdf_wr_ieeef( pls->pdfs, (float) pls->symht ) ); } break; } } //-------------------------------------------------------------------------- // plD_esc_plm() // // Escape function. Note that any data written must be in device // independent form to maintain the transportability of the metafile. // // Functions: // // PLESC_FILL Fill polygon // PLESC_SWIN Set window parameters // //-------------------------------------------------------------------------- void plD_esc_plm( PLStream *pls, PLINT op, void *ptr ) { dbug_enter( "plD_esc_plm" ); plm_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) ESCAPE ) ); plm_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) op ) ); switch ( op ) { case PLESC_FILL: plm_fill( pls ); break; case PLESC_SWIN: plm_swin( pls ); break; // Unicode and non-Unicode text handling case PLESC_HAS_TEXT: plm_text( pls, (EscText *) ptr ); break; // Alternate unicode text handling case PLESC_BEGIN_TEXT: case PLESC_TEXT_CHAR: case PLESC_CONTROL_CHAR: case PLESC_END_TEXT: // NOP these for now until a decision is made // which method should be implemented for metafiles plwarn( "plmeta: Alternate Unicode text handling is not implemented" ); break; } } //-------------------------------------------------------------------------- // Private functions //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- // plm_fill() // // Fill polygon described in points pls->dev_x[] and pls->dev_y[]. //-------------------------------------------------------------------------- static void plm_fill( PLStream *pls ) { PLmDev *dev = (PLmDev *) pls->dev; dbug_enter( "plm_fill" ); plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->dev_npts ) ); plm_wr( pdf_wr_2nbytes( pls->pdfs, (U_SHORT *) pls->dev_x, pls->dev_npts ) ); plm_wr( pdf_wr_2nbytes( pls->pdfs, (U_SHORT *) pls->dev_y, pls->dev_npts ) ); dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; } //-------------------------------------------------------------------------- // plm_swin() // // Set window parameters. // Each parameter or group of parameters is tagged to make backward // compatibility easier. //-------------------------------------------------------------------------- static void plm_swin( PLStream *pls ) { dbug_enter( "plm_swin" ); } //-------------------------------------------------------------------------- // plm_text() // // Stores the text into the metafile. //-------------------------------------------------------------------------- static void plm_text( PLStream *pls, EscText *args ) { PLmDev *dev = (PLmDev *) pls->dev; size_t len; // Write state information needed to render the text plm_wr( pdf_wr_ieeef( pls->pdfs, pls->chrht ) ); plm_wr( pdf_wr_ieeef( pls->pdfs, pls->diorot ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, pls->clpxmi ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, pls->clpxma ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, pls->clpymi ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, pls->clpyma ) ); // Write the text layout information plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) args->base ) ); plm_wr( pdf_wr_ieeef( pls->pdfs, (float) args->just ) ); // Do we have a rotation shear that needs to be saved if ( args->xform != NULL ) { plm_wr( pdf_wr_ieeef( pls->pdfs, (float) args->xform[0] ) ); plm_wr( pdf_wr_ieeef( pls->pdfs, (float) args->xform[1] ) ); plm_wr( pdf_wr_ieeef( pls->pdfs, (float) args->xform[2] ) ); plm_wr( pdf_wr_ieeef( pls->pdfs, (float) args->xform[3] ) ); } else { plwarn( "plmeta: transformation matrix undefined, using a guess" ); plm_wr( pdf_wr_ieeef( pls->pdfs, (float) 1.0 ) ); plm_wr( pdf_wr_ieeef( pls->pdfs, (float) 0.0 ) ); plm_wr( pdf_wr_ieeef( pls->pdfs, (float) 0.0 ) ); plm_wr( pdf_wr_ieeef( pls->pdfs, (float) 1.0 ) ); } plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) args->x ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) args->y ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) args->refx ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) args->refy ) ); plm_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) args->font_face ) ); plm_wr( pdf_wr_4bytes( pls->pdfs, (int) args->text_type ) ); // Was a text string passed or a plot symbol? if ( args->text_type == PL_STRING_TEXT ) { // Text string len = strlen( args->string ); plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) len ) ); if ( len > 0 ) plm_wr( pdf_wr_string( pls->pdfs, args->string ) ); } else { // Plot symbol plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) args->symbol ) ); } // Clear the last known position dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; } //-------------------------------------------------------------------------- // Provide a consistent method for handling a failed fsetpos. //-------------------------------------------------------------------------- static void handle_fsetpos_failed( const char *where, const char *which, FPOS_T position ) { char buffer[BUFFER_LEN]; // Format a standard message detailing the failure location snprintf( buffer, BUFFER_LEN, "%s: fsetpos to %s (%lu) failed", where, which, (unsigned long) position ); plexit( buffer ); } //-------------------------------------------------------------------------- // WriteFileHeader() // // Writes Metafile header. //-------------------------------------------------------------------------- static void WriteFileHeader( PLStream *pls ) { PLmDev *dev = (PLmDev *) pls->dev; FILE *file = pls->OutFile; int isfile = ( pls->output_type == 0 ); dbug_enter( "WriteFileHeader(PLStream *pls" ); plm_wr( pdf_wr_header( pls->pdfs, PLMETA_HEADER ) ); plm_wr( pdf_wr_header( pls->pdfs, PLMETA_VERSION ) ); // Write file index info. Right now only number of pages. // The order here is critical if ( isfile ) { // Save the position of the pages field so that it can // be updated when a new page is created if ( pl_fgetpos( file, &dev->index_offset ) ) plexit( "WriteFileHeader: fgetpos call failed" ); } plm_wr( pdf_wr_header( pls->pdfs, "pages" ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) 0 ) ); // Write initialization info. Tag via strings to make backward // compatibility with old metafiles as easy as possible. plm_wr( pdf_wr_header( pls->pdfs, "xmin" ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) dev->xmin ) ); plm_wr( pdf_wr_header( pls->pdfs, "xmax" ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) dev->xmax ) ); plm_wr( pdf_wr_header( pls->pdfs, "ymin" ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) dev->ymin ) ); plm_wr( pdf_wr_header( pls->pdfs, "ymax" ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) dev->ymax ) ); plm_wr( pdf_wr_header( pls->pdfs, "pxlx" ) ); plm_wr( pdf_wr_ieeef( pls->pdfs, (float) dev->pxlx ) ); plm_wr( pdf_wr_header( pls->pdfs, "pxly" ) ); plm_wr( pdf_wr_ieeef( pls->pdfs, (float) dev->pxly ) ); // Geometry info, needed to properly transmit e.g. aspect ratio, via the // length params. Not sure if the others are useful, but they're included // for completeness. plm_wr( pdf_wr_header( pls->pdfs, "xdpi" ) ); plm_wr( pdf_wr_ieeef( pls->pdfs, (float) pls->xdpi ) ); plm_wr( pdf_wr_header( pls->pdfs, "ydpi" ) ); plm_wr( pdf_wr_ieeef( pls->pdfs, (float) pls->ydpi ) ); plm_wr( pdf_wr_header( pls->pdfs, "xlength" ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->xlength ) ); plm_wr( pdf_wr_header( pls->pdfs, "ylength" ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->ylength ) ); plm_wr( pdf_wr_header( pls->pdfs, "xoffset" ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->xoffset ) ); plm_wr( pdf_wr_header( pls->pdfs, "yoffset" ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->yoffset ) ); plm_wr( pdf_wr_header( pls->pdfs, "" ) ); } //-------------------------------------------------------------------------- // WritePageInfo() // // Update table of contents info & write new page header. //-------------------------------------------------------------------------- static void WritePageInfo( PLStream *pls, FPOS_T pp_offset ) { PLmDev *dev = (PLmDev *) pls->dev; FILE *file = pls->OutFile; int isfile = ( pls->output_type == 0 ); U_CHAR c; FPOS_T cp_offset = 0; // Update table of contents. if ( isfile ) { // Get the position of this page in order to update the index if ( pl_fgetpos( file, &cp_offset ) ) plexit( "WritePageInfo (plmeta.c): fgetpos call failed" ); UpdateIndex( pls, cp_offset ); } // Write new page header if ( dev->notfirst ) c = BOP; else { c = BOP0; dev->notfirst = 1; } plm_wr( pdf_wr_1byte( pls->pdfs, c ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->page ) ); plm_wr( pdf_wr_4bytes( pls->pdfs, (U_LONG) pp_offset ) ); plm_wr( pdf_wr_4bytes( pls->pdfs, (U_LONG) 0 ) ); // Update last page offset with current page value dev->lp_offset = cp_offset; // Write some page state information just to make things nice later on // Eventually there will be more plD_state_plm( pls, PLSTATE_COLOR0 ); } //-------------------------------------------------------------------------- // UpdatePrevPagehdr() // // Update previous page header. //-------------------------------------------------------------------------- static void UpdatePrevPagehdr( PLStream *pls ) { PLmDev *dev = (PLmDev *) pls->dev; FILE *file = pls->OutFile; FPOS_T cp_offset = 0; fflush( file ); // Determine where we are if ( pl_fgetpos( file, &cp_offset ) ) plexit( "plD_bop_plm: fgetpos call failed" ); // Seek back to previous page header. if ( dev->lp_offset > 0 ) { FPOS_T fwbyte_offset = 0; pldebug( "UpdatePrevPagehdr 1 (plmeta.c)", "Location: %d, seeking to: %d\n", (int) cp_offset, (int) dev->lp_offset ); // The forward byte offset is located exactly 7 bytes after the BOP fwbyte_offset = dev->lp_offset + 7; if ( pl_fsetpos( file, &fwbyte_offset ) ) { handle_fsetpos_failed( "UpdatePrevPagehdr", "fwbyte_offset", fwbyte_offset ); } // DEBUG: verify current location #ifdef DEBUG if ( pl_fgetpos( file, &fwbyte_offset ) ) plexit( "UpdatePrevPagehdr (plmeta.c): fgetpos call failed" ); pldebug( "UpdatePrevPagehdr 2 (plmeta.c)", "Now at: %d, to write: %d\n", (int) fwbyte_offset, (int) cp_offset ); #endif // Write forward byte offset into previous page header. plm_wr( pdf_wr_4bytes( pls->pdfs, (U_LONG) cp_offset ) ); fflush( file ); // DEBUG: move back to before the write & read it to verify #ifdef DEBUG if ( pl_fsetpos( file, &fwbyte_offset ) ) { handle_fsetpos_failed( "UpdatePrevPagehdr", "fwbyte_offset", fwbyte_offset ); } { U_LONG read_offset; plm_rd( pdf_rd_4bytes( pls->pdfs, &read_offset ) ); pldebug( "UpdatePrevPagehdr 3 (plmeta.c)", "Value read as: %d\n", read_offset ); } #endif // Return to current page offset if ( pl_fsetpos( file, &cp_offset ) ) { handle_fsetpos_failed( "UpdatePrevPagehdr", "cp_offset", cp_offset ); } } } //-------------------------------------------------------------------------- // UpdateIndex() // // Update file index. //-------------------------------------------------------------------------- static void UpdateIndex( PLStream *pls, FPOS_T cp_offset ) { PLmDev *dev = (PLmDev *) pls->dev; FILE *file = pls->OutFile; // Update file index. Right now only number of pages. // The ordering here is critical if ( dev->index_offset > 0 ) { pldebug( "UpdateIndex (plmeta.c)", "Location: %d, seeking to: %d\n", (int) cp_offset, (int) dev->lp_offset ); if ( pl_fsetpos( file, &dev->index_offset ) ) { handle_fsetpos_failed( "UpdateIndex", "index_offset", dev->index_offset ); } plm_wr( pdf_wr_header( pls->pdfs, "pages" ) ); plm_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->page ) ); pldebug( "UpdateIndex (plmeta.c)", "Location: %d, seeking to: %d\n", (int) dev->lp_offset, (int) cp_offset ); if ( pl_fsetpos( file, &cp_offset ) ) { handle_fsetpos_failed( "UpdateIndex", "cp_offset", cp_offset ); } } } #else int pldummy_plmeta() { return 0; } #endif // PLD_plmeta plplot-5.13.0/drivers/xfig.c000644 001752 001752 00000040733 13150160115 017423 0ustar00softwaresoftware000000 000000 // PLplot xfig device driver. // #include "plDevs.h" #ifdef PLD_xfig #include "plplotP.h" #include "drivers.h" // Device info PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_xfig = "xfig:Fig file:0:xfig:31:xfig\n"; typedef struct { PLINT xold, yold; PLINT xmin, xmax; PLINT ymin, ymax; PLFLT xscale_dev, yscale_dev; int *buffptr, bufflen; int count; int curwid; int curcol; int firstline; long cmap0_pos, cmap1_pos; int cmap0_ncol, cmap1_ncol; int offset, offset_inc; } xfig_Dev; // Function prototypes void plD_dispatch_init_xfig( PLDispatchTable *pdt ); void plD_init_xfig( PLStream * ); void plD_line_xfig( PLStream *, short, short, short, short ); void plD_polyline_xfig( PLStream *, short *, short *, PLINT ); void plD_eop_xfig( PLStream * ); void plD_bop_xfig( PLStream * ); void plD_tidy_xfig( PLStream * ); void plD_state_xfig( PLStream *, PLINT ); void plD_esc_xfig( PLStream *, PLINT, void * ); static void flushbuffer( PLStream * ); // top level declarations #define FIGX 297 // portrait A4 mm #define FIGY 210 #define DPI 1200 // it looks like xfig-3.2.3c has a bug. A4 papersize is 297x210 mm, // and at 1200 dpi this gives 14031x9921 dots. In a file saved from // xfig, with a box of A4 size, the reported sizes are 13365x9450 #define BSIZE 25 #define XFIG_COLBASE 33 // xfig first user color, plplot colormap0[0], // the background color static void stcmap0( PLStream * ); static void stcmap1( PLStream * ); static void proc_str( PLStream *, EscText * ); static int text = 0; static DrvOpt xfig_options[] = { { "text", DRV_INT, &text, "Use Postscript text (text=1|0)" }, { NULL, DRV_INT, NULL, NULL } }; void plD_dispatch_init_xfig( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "Xfig file"; pdt->pl_DevName = "xfig"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 31; pdt->pl_init = (plD_init_fp) plD_init_xfig; pdt->pl_line = (plD_line_fp) plD_line_xfig; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_xfig; pdt->pl_eop = (plD_eop_fp) plD_eop_xfig; pdt->pl_bop = (plD_bop_fp) plD_bop_xfig; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_xfig; pdt->pl_state = (plD_state_fp) plD_state_xfig; pdt->pl_esc = (plD_esc_fp) plD_esc_xfig; } //-------------------------------------------------------------------------- // plD_init_xfig() // // Initialize device. //-------------------------------------------------------------------------- void plD_init_xfig( PLStream *pls ) { xfig_Dev *dev; plParseDrvOpts( xfig_options ); if ( text ) pls->dev_text = 1; // want to draw text // Initialize family file info plFamInit( pls ); // Prompt for a file name if not already set plOpenFile( pls ); // Allocate and initialize device-specific data if ( pls->dev != NULL ) free( (void *) pls->dev ); pls->dev = calloc( 1, (size_t) sizeof ( xfig_Dev ) ); if ( pls->dev == NULL ) plexit( "plD_init_xfig: cannot allocate memory\n" ); dev = (xfig_Dev *) pls->dev; dev->curwid = pls->width < 1 ? 1 : (int) pls->width; dev->firstline = 1; dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; dev->xmin = 0; dev->xmax = FIGX; dev->ymin = 0; dev->ymax = FIGY; dev->xscale_dev = DPI / 25.4; dev->yscale_dev = DPI / 25.4; dev->offset_inc = dev->ymax * (PLINT) dev->yscale_dev; dev->offset = -dev->offset_inc; pls->dev_fill0 = 1; // Handle solid fills if ( !pls->colorset ) pls->color = 1; // Is a color device plP_setpxl( dev->xscale_dev, dev->xscale_dev ); // dpmm -- dots per mm plP_setphy( 0, (PLINT) ( FIGX * dev->xscale_dev ), 0, (PLINT) ( FIGY * dev->yscale_dev ) ); // physical dimension in mm // Write out header fprintf( pls->OutFile, "#FIG 3.2\n" ); fprintf( pls->OutFile, "Landscape\n" ); fprintf( pls->OutFile, "Center\n" ); fprintf( pls->OutFile, "Metric\n" ); fprintf( pls->OutFile, "A4\n" ); fprintf( pls->OutFile, "100.0\n" ); fprintf( pls->OutFile, "Single\n" ); fprintf( pls->OutFile, "-2\n" ); fprintf( pls->OutFile, "%d 2\n", DPI ); // user defined colors, for colormap0 dev->cmap0_ncol = 2 * pls->ncol0; // allow for a maximum of 2x the default cmap0 entries dev->cmap0_pos = ftell( pls->OutFile ); stcmap0( pls ); // user defined colors, for colormap1 dev->cmap1_ncol = 2 * pls->ncol1; // allow for a maximum of 2x the default cmap1 entries dev->cmap1_pos = ftell( pls->OutFile ); stcmap1( pls ); dev->bufflen = 2 * BSIZE; dev->buffptr = (int *) malloc( sizeof ( int ) * (size_t) ( dev->bufflen ) ); if ( dev->buffptr == NULL ) plexit( "plD_init_xfig: Out of memory!" ); } void stcmap0( PLStream *pls ) { xfig_Dev *dev; long cur_pos; int i; dev = (xfig_Dev *) pls->dev; if ( pls->ncol0 > dev->cmap0_ncol ) plwarn( "Too many colors for cmap0. Preallocate using command line '-ncol0 n.\n'" ); cur_pos = ftell( pls->OutFile ); if ( fseek( pls->OutFile, dev->cmap0_pos, SEEK_SET ) ) plexit( "Sorry, only file based output, no pipes.\n" ); // fill the colormap for ( i = 0; i < pls->ncol0; i++ ) fprintf( pls->OutFile, "0 %d #%.2x%.2x%.2x\n", i + XFIG_COLBASE, pls->cmap0[i].r, pls->cmap0[i].g, pls->cmap0[i].b ); // fill the nonspecified entries colormap for ( i = pls->ncol0; i < dev->cmap0_ncol; i++ ) fprintf( pls->OutFile, "0 %d #000000\n", i + XFIG_COLBASE ); if ( cur_pos != dev->cmap0_pos ) fseek( pls->OutFile, cur_pos, SEEK_SET ); } void stcmap1( PLStream *pls ) { xfig_Dev *dev; long cur_pos; int i; dev = (xfig_Dev *) pls->dev; if ( pls->ncol1 > dev->cmap1_ncol ) plwarn( "Too many colors for cmap1. Preallocate using command line '-ncol1 n.\n'" ); cur_pos = ftell( pls->OutFile ); if ( fseek( pls->OutFile, dev->cmap1_pos, SEEK_SET ) ) plexit( "Sorry, only file based output, no pipes.\n" ); // fill the colormap for ( i = 0; i < pls->ncol1; i++ ) fprintf( pls->OutFile, "0 %d #%.2x%.2x%.2x\n", i + XFIG_COLBASE + dev->cmap0_ncol, pls->cmap1[i].r, pls->cmap1[i].g, pls->cmap1[i].b ); // fill the nonspecified entries colormap for ( i = pls->ncol1; i < dev->cmap1_ncol; i++ ) fprintf( pls->OutFile, "0 %d #000000\n", i + XFIG_COLBASE + dev->cmap0_ncol ); if ( cur_pos != dev->cmap1_pos ) fseek( pls->OutFile, cur_pos, SEEK_SET ); } //-------------------------------------------------------------------------- // plD_line_xfig() // // Draw a line in the current color from (x1,y1) to (x2,y2). //-------------------------------------------------------------------------- void plD_line_xfig( PLStream *pls, short x1a, short y1a, short x2a, short y2a ) { xfig_Dev *dev = (xfig_Dev *) pls->dev; int x1 = x1a, y1 = y1a, x2 = x2a, y2 = y2a; int *tempptr; int count; // If starting point of this line is the same as the ending point of // the previous line then don't raise the pen. (This really speeds up // plotting and reduces the size of the file. if ( dev->firstline ) { count = 0; *( dev->buffptr + count++ ) = x1; *( dev->buffptr + count++ ) = y1; *( dev->buffptr + count++ ) = x2; *( dev->buffptr + count++ ) = y2; dev->firstline = 0; } else if ( x1 == dev->xold && y1 == dev->yold ) { count = dev->count; if ( count + 2 >= dev->bufflen ) { dev->bufflen += 2 * BSIZE; tempptr = (int *) realloc( (void *) dev->buffptr, (size_t) ( dev->bufflen ) * sizeof ( int ) ); if ( tempptr == NULL ) { free( (void *) dev->buffptr ); plexit( "plD_line_xfig: Out of memory!" ); } dev->buffptr = tempptr; } *( dev->buffptr + count++ ) = x2; *( dev->buffptr + count++ ) = y2; } else { flushbuffer( pls ); count = dev->count; *( dev->buffptr + count++ ) = x1; *( dev->buffptr + count++ ) = y1; *( dev->buffptr + count++ ) = x2; *( dev->buffptr + count++ ) = y2; } dev->count = count; dev->xold = x2; dev->yold = y2; } //-------------------------------------------------------------------------- // plD_polyline_xfig() // // Draw a polyline in the current color. //-------------------------------------------------------------------------- void plD_polyline_xfig( PLStream *pls, short *xa, short *ya, PLINT npts ) { PLINT i; for ( i = 0; i < npts - 1; i++ ) plD_line_xfig( pls, xa[i], ya[i], xa[i + 1], ya[i + 1] ); } //-------------------------------------------------------------------------- // plD_eop_xfig() // // End of page. //-------------------------------------------------------------------------- void plD_eop_xfig( PLStream *pls ) { xfig_Dev *dev = (xfig_Dev *) pls->dev; if ( !dev->firstline ) flushbuffer( pls ); } //-------------------------------------------------------------------------- // plD_bop_xfig() // // Set up for the next page. // Advance to next family file if necessary (file output). //-------------------------------------------------------------------------- void plD_bop_xfig( PLStream *pls ) { xfig_Dev *dev; if ( !pls->termin ) plGetFam( pls ); dev = (xfig_Dev *) pls->dev; dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; dev->firstline = 1; pls->famadv = 1; pls->page++; dev->offset += dev->offset_inc; flushbuffer( pls ); // create background FIXME -- sync with orientation in header and pls->diorot dev->curcol = XFIG_COLBASE; // colormap entry 0, background fprintf( pls->OutFile, "2 1 0 1 %d %d 50 0 20 0.0 0 0 -1 0 0 5\n", dev->curcol, dev->curcol ); fprintf( pls->OutFile, "%d %d %d %d %d %d %d %d %d %d\n", 0, dev->offset, 0, (int) ( FIGY * dev->yscale_dev ) + dev->offset, (int) ( FIGX * dev->xscale_dev ), (int) ( FIGY * dev->yscale_dev ) + dev->offset, (int) ( FIGX * dev->xscale_dev ), dev->offset, 0, dev->offset ); } //-------------------------------------------------------------------------- // plD_tidy_xfig() // // Close graphics file or otherwise clean up. //-------------------------------------------------------------------------- void plD_tidy_xfig( PLStream *pls ) { xfig_Dev *dev = (xfig_Dev *) pls->dev; flushbuffer( pls ); free( (void *) dev->buffptr ); plCloseFile( pls ); } //-------------------------------------------------------------------------- // plD_state_xfig() // // Handle change in PLStream state (color, pen width, fill attribute, etc). //-------------------------------------------------------------------------- void plD_state_xfig( PLStream *pls, PLINT op ) { xfig_Dev *dev = (xfig_Dev *) pls->dev; switch ( op ) { case PLSTATE_WIDTH: flushbuffer( pls ); dev->firstline = 1; dev->curwid = pls->width < 1 ? 1 : (int) pls->width; break; case PLSTATE_COLOR0: flushbuffer( pls ); dev->curcol = pls->icol0 + XFIG_COLBASE; break; case PLSTATE_COLOR1: flushbuffer( pls ); dev->curcol = pls->icol1 + XFIG_COLBASE + pls->ncol0; break; case PLSTATE_CMAP0: stcmap0( pls ); break; case PLSTATE_CMAP1: stcmap1( pls ); break; } } //-------------------------------------------------------------------------- // plD_esc_xfig() // // Escape function. // Preliminary fill support for colormap0 //-------------------------------------------------------------------------- void plD_esc_xfig( PLStream *pls, PLINT op, void *ptr ) { xfig_Dev *dev = pls->dev; int i, npts; switch ( op ) { case PLESC_FILL: npts = pls->dev_npts; flushbuffer( pls ); fprintf( pls->OutFile, "2 1 0 1 %d %d 50 0 20 0.0 0 0 0 0 0 %d\n", dev->curcol, dev->curcol, npts ); for ( i = 0; i < npts; i++ ) fprintf( pls->OutFile, "%d %d ", pls->dev_x[i], dev->offset + dev->ymax * (int) dev->xscale_dev - pls->dev_y[i] ); fprintf( pls->OutFile, "\n" ); break; case PLESC_HAS_TEXT: proc_str( pls, ptr ); break; } } //-------------------------------------------------------------------------- // Utility functions. //-------------------------------------------------------------------------- static void flushbuffer( PLStream *pls ) { xfig_Dev *dev = pls->dev; int i = 0; if ( dev->count == 0 ) return; fprintf( pls->OutFile, "2 1 0 %d %d 0 50 0 -1 0.0 0 0 0 0 0 %d\n", dev->curwid, dev->curcol, dev->count / 2 ); while ( i < dev->count ) { fprintf( pls->OutFile, "%d %d ", *( dev->buffptr + i ), dev->offset + dev->ymax * (int) dev->yscale_dev - *( dev->buffptr + i + 1 ) ); i += 2; } fprintf( pls->OutFile, "\n" ); dev->count = 0; } void proc_str( PLStream *pls, EscText *args ) { PLFLT *t = args->xform; PLFLT a1, alpha, ft_ht, angle, ref; xfig_Dev *dev = (xfig_Dev *) pls->dev; PLINT clxmin, clxmax, clymin, clymax; int jst, font; // font height ft_ht = pls->chrht * 72.0 / 25.4; // ft_ht in points. ht is in mm // calculate baseline text angle angle = pls->diorot * 90.; a1 = acos( t[0] ) * 180. / PI; if ( t[2] > 0. ) alpha = a1 - angle; else alpha = 360. - a1 - angle; alpha = alpha * PI / 180.; // TODO: parse string for format (escape) characters // parse_str(args->string, return_string); // apply transformations difilt( &args->x, &args->y, 1, &clxmin, &clxmax, &clymin, &clymax ); // check clip limits. For now, only the reference point of the string is checked; // but the the whole string should be checked -- using a postscript construct // such as gsave/clip/grestore. This method can also be applied to the xfig and // pstex drivers. Zoom side effect: the font size must be adjusted! if ( args->x < clxmin || args->x > clxmax || args->y < clymin || args->y > clymax ) return; // // Text justification. Left, center and right justification, which // are the more common options, are supported; variable justification is // only approximate, based on plplot computation of it's string lenght // if ( args->just == 0.5 ) jst = 1; // center else if ( args->just == 1. ) jst = 2; // right else { jst = 0; // left args->x = args->refx; // use hints provided by plplot args->y = args->refy; } // // Reference point (center baseline of string, not latex character reference point). // If base = 0, it is aligned with the center of the text box // If base = 1, it is aligned with the baseline of the text box // If base = 2, it is aligned with the top of the text box // Currently plplot only uses base=0 // xfig use base=1 // if ( args->base == 2 ) // not supported by plplot ref = -DPI / 72. * ft_ht / 2.; // half font height in xfig unities (1/1200 inches) else if ( args->base == 1 ) ref = 0.; else ref = DPI / 72. * ft_ht / 2.; // rotate point in xfig is lower left corner, compensate args->y = (PLINT) ( dev->offset + dev->ymax * (int) dev->xscale_dev - ( args->y - ref * cos( alpha ) ) ); args->x = (PLINT) ( args->x + ref * sin( alpha ) ); // // font family, serie and shape. Currently not supported by plplot // // Use Postscript Times // 1: Normal font // 2: Roman font // 3: Italic font // 4: sans serif // switch ( pls->cfont ) { case ( 1 ): font = 0; break; case ( 2 ): font = 1; break; case ( 3 ): font = 3; break; case ( 4 ): font = 4; break; default: font = 0; } fprintf( pls->OutFile, "4 %d %d 50 0 %d %f %f 4 1 1 %d %d %s\\001\n", jst, dev->curcol, font, 1.8 /*!*/ * ft_ht, alpha, args->x, args->y, args->string ); } #else int pldummy_xfig() { return 0; } #endif // PLD_xfig plplot-5.13.0/drivers/README.wxwidgets000644 001752 001752 00000022310 13150160115 021215 0ustar00softwaresoftware000000 000000 Installation instructions for Unix 1) Install the wxwidgets library either by compiling it yourself or using apt/rpm (2.6.0 is needed) + compile it yourself: - Download wxWidgets 2.6.1 from http://www.wxwidgets.org (wxGTK package) - Untar it to a folder and cd into the main dir. - Make a directory called "buildgtk". Cd into buildgtk - Run: ../configure --disable-debug --enable-gtk2 --enable-monolithic --enable-shared - Run: make - Run: sudo make install (library and developer files are installed to /usr/local) 2) Install at least automake 1.82 and libtool 1.4 3) download plplot 5.5.3 from cvs 4) build plplot library - cd into plplot main directory - run: cf/bootstrap.sh - run: ./configure --enable-wxwidgets - run: make - run: sudo make install - run: cd examples/c - run: make x01c - run: ./x01c Installation instructions for Mac OS X 1) Install the wxwidgets library either by compiling it yourself or using the opendarwin port + Mac OS X G++ Compiler - Download wxWidgets 2.6.1 from http://www.wxwidgets.org (wxMAC package). - Untar it to a folder and cd into the main dir. - Make a directory called "buildosx". cd into "buildosx". - Run: ../configure --disable-debug --enable-monolithic --enable-shared - Run: make - Run: sudo make install (library and developer files are installed to /usr/local) OR - download and install the port package from http://darwinports.opendarwin.org - add PATH=/opt/local/bin:$PATH MANPATH=/opt/local/share/man:$MANPATH INFOPATH=/opt/local/share/info:$INFOPATH to your ".profile" file in your home directory. - run: sudo port -d selfupdate - run: sudo port install wxWidgets (library and developer files are installed to /opt/local) 2) Install at least automake 1.82 and libtool 1.4, e.g. via opendarwin port: - download and install the port package from http://darwinports.opendarwin.org - add PATH=/opt/local/bin:$PATH MANPATH=/opt/local/share/man:$MANPATH INFOPATH=/opt/local/share/info:$INFOPATH to your ".profile" file in your home directory. - run: sudo port -d selfupdate - run: sudo port install automake - run: sudo port install libtool - run: sudo ln -s /opt/local/bin/glibtool /opt/local/bin/libtool - run: sudo ln -s /opt/local/bin/glibtoolize /opt/local/bin/libtoolize 3) download plplot 5.5.3 from cvs 4) build plplot library - cd into plplot main directory - run: cf/bootstrap.sh - run: ./configure --enable-wxwidgets --with-wxwidgets-bindir=/opt/local/bin (port) OR run: ./configure --enable-wxwidgets (self compiled in /usr/local) - run: make - run: sudo make install - run: cd examples/c - run: make x01c - run: ./x01c --------- Using the driver in a wxWidgets Application ------------------------------------- The wxWidgets driver is already capable of redirecting the plot to any canvas (wxDC), which can also be provided by a wxApp. The API is not quite ready for release, but it's easy to implement. First we need to inherit a class from plstream #include "plplotP.h" #include "plstream.h" #include "wx/dc.h" class wxPLplotstream : public plstream { public: wxPLplotstream( wxDC *dc, int width, int height ); //!< Constructor. void set_stream(); //!< Calls some code before every PLplot command. void SetSize( int width, int height ); //!< Set new size of plot area. void RenewPlot(); //!< Redo plot. private: wxDC* m_dc; //!< Pointer to wxDC to plot into. int m_width; //!< Width of dc/plot area. int m_height; //!< Height of dc/plot area. }; wxPLplotstream::wxPLplotstream( wxDC *dc, int width, int height ) : m_dc(dc), m_width(width), m_height(height) { ::plstream(); sdev( "wxwidgets" ); spage( 0.0, 0.0, m_width, m_height, 0, 0 ); SetOpt( "text", "1" ); // use freetype? SetOpt( "smooth", "1" ); // antialiased text? init(); plP_esc( PLESC_DEVINIT, (void*)m_dc ); } void wxPLplotstream::set_stream() { plstream::set_stream(); } void wxPLplotstream::SetSize( int width, int height ) { m_width=width; m_height=height; plP_esc( PLESC_CLEAR, NULL ); wxSize size( m_width, m_height ); plP_esc( PLESC_RESIZE, (void*)&size ); } void wxPLplotstream::RenewPlot() { plP_esc( PLESC_CLEAR, NULL ); replot(); } In the wxWidgets application a wxMemoryDC must be created (e.g. in the constructor of a wxWindow) and made known to the driver, e.g. MemPlotDC = new wxMemoryDC; MemPlotDCBitmap = new wxBitmap( 640, 400, -1 ); MemPlotDC->SelectObject( *MemPlotDCBitmap ); my_stream = new wxPLplotstream( (wxDC*)MemPlotDC, MemPlotDC_width, MemPlotDC_height ); The OnPaint() event handler looks like this (double buffering is used here) void plotwindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) { int width, height; GetSize( &width, &height ); // Check if we window was resized (or dc is invalid) if( (my_stream == NULL) || (MemPlotDC_width!=width) || (MemPlotDC_height!=height) ) { MemPlotDC->SelectObject( wxNullBitmap ); if( MemPlotDCBitmap ) delete MemPlotDCBitmap; MemPlotDCBitmap = new wxBitmap( width, height, -1 ); MemPlotDC->SelectObject( *MemPlotDCBitmap ); my_stream->SetSize( width, height ); my_stream->replot(); MemPlotDC_width = width; MemPlotDC_height = height; } wxPaintDC dc( this ); dc.SetClippingRegion( GetUpdateRegion() ); dc.BeginDrawing(); dc.Blit( 0, 0, width, height, MemPlotDC, 0, 0 ); dc.EndDrawing(); } The whole PLplot API is then available via the my_stream object. --------- don't bother with stuff below this line ----------------------------------------- Installation instructions for Win32 - install wxWidgets 2.6.1 (see instructions below) - NOT WORKING NOW: install gd library and/or freetype library (see instructions below) - download the PLplot library from plplot.sf.net (version 5.5.3) - Remark (Mac OS X): You need automake 1.8.2 and libtool 1.4 in order to compile PLplot. Download and install fink (fink.sf.net) and install these programs. - untar or unzip it in a suitable directory - unzip or untar the wxPLplot package in sys/win32 - cd into sys/win32/wxplplot - WIN32: . Set WXWIN environment variable (see wxWidgets instructions). . For all compilers you need the mingw32-make program (www.mingw.org). . Check the settings in config.mak. . Run: mingw32-make COMPILER=bcc . or: mingw32-make COMPILER=gcc . Run: cd examples . Run: mingw32-make COMPILER=bcc . or: mingw32-make COMPILER=gcc Install the gd library for png, gif and jpeg drivers (Win32, NOT WORKING NOW) - goto http://www.boutell.com/gd/ and download the precompiled Windows DLL at the bottom ( http://www.boutell.com/gd/http/gdwin32.zip ) - extract to sys/win32/wxplplot - enter sys/win32/wxplplot/gdwin32 and make a library from the dll Borland BCC 5.5: implib -a bgd_bcc.lib bgd.dll VC++: lib /machine:i386 /def:bgd.def MingW: already in gdwin32 (libbgd.a) Install the freetype library (Win32, NOT WORKING NOW) Install wxWidgets 2.6.1 + Win32 MingW Compiler - Download the necessary files from www.mingw.org. Either the whole package mingw-3.1.0 or the canditate releases from at least gcc-core, gcc-g++, binutils, mingw-runtime, w32api, mingw-utils, mingw32-make and gdb. Install them in a directory and set the path file accordingly. - Download wxWidgets 2.6.1 from http://www.wxwidgets.org (wxMSW package). - Unzip it to a folder and set the WXWIN variable accordingly (system wide in System Settings/System). - Goto %WXWIN%/build/msw. - Run all or some of the following commands: mingw32-make -f makefile.gcc BUILD=debug MONOLITHIC=1 SHARED=1 The wxWidgets (debug, shared) library will be build. mingw32-make -f makefile.gcc BUILD=release MONOLITHIC=1 SHARED=1 The wxWidgets (release, shared) library will be build. mingw32-make -f makefile.gcc BUILD=debug MONOLITHIC=1 SHARED=0 The wxWidgets (debug, static) library will be build. mingw32-make -f makefile.gcc BUILD=release MONOLITHIC=1 SHARED=0. The wxWidgets (release, static) library will be build. - Copy both dlls from %%WXWIN%/lib/gcc_dll to a directory, where they can be found (e.g. sys/win32/wxplplot/examples directory). - The same WXWIN variable must than be set for the wxplplot makefile. + Win32 Borland free command line tools (BCC 5.5.2) - Install the Borland Compiler. - Download wxWidgets 2.6.1 from http://www.wxwidgets.org (wxMSW package) - Unzip it to a folder and set the WXWIN variable accordingly (system wide in System Settings/System). - Goto %WXWIN%/build/msw. - Run all or some of the following commands: make -f makefile.bcc -DBUILD=debug -DMONOLITHIC=1 -DSHARED=1 The wxWidgets (debug, shared) library will be build. make -f makefile.bcc -DBUILD=release -DMONOLITHIC=1 -DSHARED=1 The wxWidgets (release, shared) library will be build. make -f makefile.bcc -DBUILD=debug -DMONOLITHIC=1 -DSHARED=0 The wxWidgets (debug, static) library will be build. make -f makefile.bcc -DBUILD=release -DMONOLITHIC=1 -DSHARED=0. The wxWidgets (release, static) library will be build. - Copy both dlls from %%WXWIN%/lib/gcc_dll to a directory, where they can be found (e.g. sys/win32/wxplplot/examples directory). - The same WXWIN variable must than be set for the wxplplot makefile. plplot-5.13.0/drivers/mem.c000644 001752 001752 00000012207 13150160115 017237 0ustar00softwaresoftware000000 000000 // PLplot MEM (in user-supplied memory) device driver. // The idea here is that the user will specify the Y by X by RGB // area in which to plot using the plsmem function (added by me). // // This is a bare-bones driver which allows one to plot on an existing // image in memory. This is useful if the user has an image in memory // that he wants to decorate with PLPLOT. // // Contributed by Doug Hunt // Included in PLplot by Rafael Laboissiere on Sat Feb 22 18:34:06 CET 2003 // #include "plDevs.h" #ifdef PLD_mem #include "plplotP.h" #include "drivers.h" // Device info PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_mem = "mem:User-supplied memory device:-1:mem:46:mem\n"; void plD_dispatch_init_mem( PLDispatchTable *pdt ); void plD_init_mem( PLStream * ); void plD_line_mem( PLStream *, short, short, short, short ); void plD_polyline_mem( PLStream *, short *, short *, PLINT ); void plD_eop_mem( PLStream * ); void plD_bop_mem( PLStream * ); void plD_tidy_mem( PLStream * ); void plD_state_mem( PLStream *, PLINT ); void plD_esc_mem( PLStream *, PLINT, void * ); #undef MAX #undef ABS #define MAX( a, b ) ( ( a > b ) ? a : b ) #define ABS( a ) ( ( a < 0 ) ? -a : a ) #define MAX_INTENSITY 255 void plD_dispatch_init_mem( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "User-supplied memory device"; pdt->pl_DevName = "mem"; #endif pdt->pl_type = plDevType_Null; pdt->pl_seq = 45; pdt->pl_init = (plD_init_fp) plD_init_mem; pdt->pl_line = (plD_line_fp) plD_line_mem; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_mem; pdt->pl_eop = (plD_eop_fp) plD_eop_mem; pdt->pl_bop = (plD_bop_fp) plD_bop_mem; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_mem; pdt->pl_state = (plD_state_fp) plD_state_mem; pdt->pl_esc = (plD_esc_fp) plD_esc_mem; } //-------------------------------------------------------------------------- // plD_init_mem() // // Initialize device (terminal). //-------------------------------------------------------------------------- void plD_init_mem( PLStream *pls ) { // plsmem must have already been called to set pls->dev to the // user supplied plotting area. The dimensions of the plot area // have also been set by plsmem. Verify this. // if ( ( pls->phyxma == 0 ) || ( pls->dev == NULL ) ) { plexit( "Must call plsmem first to set user plotting area!" ); } if ( pls->dev_mem_alpha == 1 ) { plexit( "The mem driver does not support alpha values! Use plsmem!" ); } plP_setpxl( (PLFLT) 4, (PLFLT) 4 ); // rough pixels/mm on *my* screen pls->color = 1; // Is a color device pls->dev_fill0 = 0; // Handle solid fills pls->dev_fill1 = 0; // Use PLplot core fallback for pattern fills pls->nopause = 1; // Don't pause between frames } #define sign( a ) ( ( a < 0 ) ? -1 : ( ( a == 0 ) ? 0 : 1 ) ) void plD_line_mem( PLStream *pls, short x1a, short y1a, short x2a, short y2a ) { int i; PLINT idx; int x1 = x1a, y1 = y1a, x2 = x2a, y2 = y2a; PLINT x1b, y1b, x2b, y2b; PLFLT length, fx, fy, dx, dy; unsigned char *mem = (unsigned char *) pls->dev; PLINT xm = pls->phyxma; PLINT ym = pls->phyyma; // Take mirror image, since (0,0) must be at top left y1 = ym - ( y1 - 0 ); y2 = ym - ( y2 - 0 ); x1b = x1, x2b = x2, y1b = y1, y2b = y2; length = (PLFLT) sqrt( (double) ( ( x2b - x1b ) * ( x2b - x1b ) + ( y2b - y1b ) * ( y2b - y1b ) ) ); if ( length == 0. ) length = 1.; dx = ( x2 - x1 ) / length; dy = ( y2 - y1 ) / length; fx = x1; fy = y1; mem[3 * xm * y1 + 3 * x1 + 0] = pls->curcolor.r; mem[3 * xm * y1 + 3 * x1 + 1] = pls->curcolor.g; mem[3 * xm * y1 + 3 * x1 + 2] = pls->curcolor.b; mem[3 * xm * y2 + 3 * x2 + 0] = pls->curcolor.r; mem[3 * xm * y2 + 3 * x2 + 1] = pls->curcolor.g; mem[3 * xm * y2 + 3 * x2 + 2] = pls->curcolor.b; for ( i = 1; i <= (int) length; i++ ) { fx += dx; fy += dy; idx = 3 * xm * (PLINT) fy + 3 * (PLINT) fx; mem[idx + 0] = pls->curcolor.r; mem[idx + 1] = pls->curcolor.g; mem[idx + 2] = pls->curcolor.b; } } void plD_polyline_mem( PLStream *pls, short *xa, short *ya, PLINT npts ) { int i; for ( i = 0; i < npts - 1; i++ ) plD_line_mem( pls, xa[i], ya[i], xa[i + 1], ya[i + 1] ); } void plD_eop_mem( PLStream *pls ) { // Set the 'dev' member (which holds the user supplied memory image) // to NULL here so it won't be freed when PLplot is closed. // (the user is responsible for freeing it when ready). // pls->dev = NULL; } void plD_bop_mem( PLStream * PL_UNUSED( pls ) ) { // Nothing to do here } void plD_tidy_mem( PLStream * PL_UNUSED( pls ) ) { // Nothing to do here } void plD_state_mem( PLStream * PL_UNUSED( pls ), PLINT PL_UNUSED( op ) ) { // Nothing to do here } void plD_esc_mem( PLStream *PL_UNUSED( pls ), PLINT PL_UNUSED( op ), void * PL_UNUSED( ptr ) ) { // Nothing to do here } #endif // PLD_mem plplot-5.13.0/drivers/wxwidgets.cpp000644 001752 001752 00000043157 13150160115 021056 0ustar00softwaresoftware000000 000000 // Copyright (C) 2015 Phil Rosenberg // Copyright (C) 2005 Werner Smekal, Sjaak Verdoold // Copyright (C) 2005 Germain Carrera Corraleche // Copyright (C) 1999 Frank Huebner // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // TODO: // - NA // // wxwidgets headers #include #include #include #include "plDevs.h" // plplot headers #include "plplotP.h" #include "drivers.h" // C/C++ headers #include #include "wxwidgets.h" bool g_weInitializedWx = false; #ifdef __WXMAC__ #include extern "C" { void CPSEnableForegroundOperation( ProcessSerialNumber* psn ); } #endif //-------------------------------------------------------------------------- // void Log_Verbose( const char *fmt, ... ) // // Print verbose debug message to stderr (printf style). //-------------------------------------------------------------------------- void Log_Verbose( const char *fmt, ... ) { #ifdef _DEBUG_VERBOSE va_list args; va_start( args, fmt ); fprintf( stderr, "Verbose: " ); vfprintf( stderr, fmt, args ); fprintf( stderr, "\n" ); va_end( args ); fflush( stderr ); #else (void) fmt; // Cast to void to silence compiler warnings about unused parameter #endif } //-------------------------------------------------------------------------- // void Log_Debug( const char *fmt, ... ) // // Print debug message to stderr (printf style). //-------------------------------------------------------------------------- void Log_Debug( const char *fmt, ... ) { #ifdef _DEBUG va_list args; va_start( args, fmt ); fprintf( stderr, "Debug: " ); vfprintf( stderr, fmt, args ); fprintf( stderr, "\n" ); va_end( args ); fflush( stderr ); #else (void) fmt; // Cast to void to silence compiler warnings about unused parameter #endif } //-------------------------------------------------------------------------- // In the following you'll find the driver functions which are // needed by the plplot core. //-------------------------------------------------------------------------- // Device info #ifdef __cplusplus extern "C" { #endif PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_wxwidgets = #ifdef PLD_wxwidgets "wxwidgets:wxWidgets Driver:1:wxwidgets:51:wxwidgets\n" #endif #ifdef PLD_wxpng "wxpng:wxWidgets PNG Driver:0:wxwidgets:52:wxpng\n" #endif ; #ifdef __cplusplus } #endif #ifdef PLD_wxwidgets //-------------------------------------------------------------------------- // void plD_dispatch_init_wxwidgets( PLDispatchTable *pdt ) // // Make wxwidgets driver functions known to plplot. //-------------------------------------------------------------------------- void plD_dispatch_init_wxwidgets( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "wxWidgets DC"; pdt->pl_DevName = "wxwidgets"; #endif pdt->pl_type = plDevType_Interactive; pdt->pl_seq = 51; pdt->pl_init = (plD_init_fp) plD_init_wxwidgets; pdt->pl_line = (plD_line_fp) plD_line_wxwidgets; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_wxwidgets; pdt->pl_eop = (plD_eop_fp) plD_eop_wxwidgets; pdt->pl_wait = (plD_wait_fp) plD_wait_wxwidgets; pdt->pl_bop = (plD_bop_fp) plD_bop_wxwidgets; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_wxwidgets; pdt->pl_state = (plD_state_fp) plD_state_wxwidgets; pdt->pl_esc = (plD_esc_fp) plD_esc_wxwidgets; } //-------------------------------------------------------------------------- // plD_init_wxwidgets( PLStream* pls ) // // Initialize wxWidgets device. //-------------------------------------------------------------------------- void plD_init_wxwidgets( PLStream* pls ) { PLPLOT_wxLogDebug( "plD_init_wxwidgets(): enter" ); try { wxPLDevice *device = (wxPLDevice *) pls->dev; if ( device ) throw( "plD_init_wxwidgets called when a initialization has already occurred." ); //initialise wxWidgets - this is required in order to allow some wxWidgets functions to //be called from within the driver when the user isn't passing a wxDC in. See e.g //http://stackoverflow.com/questions/208373 if ( !wxTheApp ) { wxApp::SetInstance( new wxApp() ); int argc = 0; char* argv[1]; g_weInitializedWx = wxEntryStart( argc, (char **) NULL ); if ( !g_weInitializedWx ) throw( "plD_init_wxWidgets could not initialise wxWidgets" ); } else g_weInitializedWx = false; // default options static PLINT text = 1; static PLINT hrshsym = 0; static char *mfo = NULL; DrvOpt wx_options[] = { { "hrshsym", DRV_INT, &hrshsym, "Use Hershey symbol set (hrshsym=0|1)" }, { "text", DRV_INT, &text, "Use own text routines (text=0|1)" }, { "mfo", DRV_STR, &mfo, "output metafile" }, { NULL, DRV_INT, NULL, NULL } }; // Check for and set up driver options plParseDrvOpts( wx_options ); // by default the own text routines are used for wxDC if ( text == -1 ) text = 0; // create the new device device = new wxPLDevice( pls, mfo, text, hrshsym ); // If portrait mode, apply a rotation and set freeaspect if ( pls->portrait ) { plsdiori( (PLFLT) ( 4 - ORIENTATION ) ); pls->freeaspect = 1; } pls->has_string_length = 1; } catch ( const char *message ) { plabort( message ); plabort( "error in plD_init_wxwidgets." ); pls->dev = NULL; } catch ( ... ) { plabort( "unknown error in plD_init_wxwidgets." ); } PLPLOT_wxLogDebug( "plD_init_wxwidgets(): leave" ); } #endif // PLD_wxwidgets #ifdef PLD_wxpng //-------------------------------------------------------------------------- // void plD_dispatch_init_wxpng( PLDispatchTable *pdt ) // // Make wxpng driver functions known to plplot. //-------------------------------------------------------------------------- void plD_dispatch_init_wxpng( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "wxWidgets PNG driver"; pdt->pl_DevName = "wxpng"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 52; pdt->pl_init = (plD_init_fp) plD_init_wxpng; pdt->pl_line = (plD_line_fp) plD_line_wxwidgets; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_wxwidgets; pdt->pl_eop = (plD_eop_fp) plD_eop_wxwidgets; pdt->pl_bop = (plD_bop_fp) plD_bop_wxwidgets; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_wxwidgets; pdt->pl_state = (plD_state_fp) plD_state_wxwidgets; pdt->pl_esc = (plD_esc_fp) plD_esc_wxwidgets; } //-------------------------------------------------------------------------- // void plD_init_wxpng( PLStream *pls ) // // Initialize wxpng device. //-------------------------------------------------------------------------- void plD_init_wxpng( PLStream *pls ) { PLPLOT_wxLogDebug( "plD_init_wxpng()" ); wxPLDevBase* dev; dev = common_init( pls ); // Initialize family file info plFamInit( pls ); // Prompt for a file name if not already set. plOpenFile( pls ); pls->plbuf_write = 1; // use the plot buffer! pls->dev_flush = 0; // No need for flushes pls->termin = 0; // file oriented device pls->graphx = GRAPHICS_MODE; // No text mode for this driver (at least for now, might add a console window if I ever figure it out and have the inclination) pls->page = 0; dev->showGUI = false; dev->bitmapType = wxBITMAP_TYPE_PNG; } #endif // PLD_wxpng //-------------------------------------------------------------------------- // void plD_line_wxwidgets( PLStream *pls, short x1a, short y1a, // short x2a, short y2a ) // // Draws a line from (x1a, y1a) to (x2a, y2a). //-------------------------------------------------------------------------- void plD_line_wxwidgets( PLStream *pls, short x1a, short y1a, short x2a, short y2a ) { // Log_Verbose( "plD_line_wxwidgets(x1a=%d, y1a=%d, x2a=%d, y2a=%d)", x1a, y1a, x2a, y2a ); try { wxPLDevice *device = (wxPLDevice *) pls->dev; if ( !device ) throw( "plD_line_wxwidgets called before initialization." ); device->DrawLine( x1a, y1a, x2a, y2a ); } catch ( const char* message ) { plabort( message ); plabort( "error in plD_line_wxwidgets." ); } catch ( ... ) { plabort( "unknown error in plD_line_wxwidgets." ); } } //-------------------------------------------------------------------------- // void plD_polyline_wxwidgets( PLStream *pls, short *xa, short *ya, // PLINT npts ) // // Draw a poly line - points are in xa and ya arrays. //-------------------------------------------------------------------------- void plD_polyline_wxwidgets( PLStream *pls, short *xa, short *ya, PLINT npts ) { // PLPLOT_wxLogDebug( "plD_polyline_wxwidgets()" ); try { wxPLDevice *device = (wxPLDevice *) pls->dev; if ( !device ) throw( "plD_polyline_wxwidgets called before initialization." ); device->DrawPolyline( xa, ya, npts ); } catch ( const char* message ) { plabort( message ); plabort( "error in plD_polyline_wxwidgets." ); } catch ( ... ) { plabort( "unknown error in plD_polyline_wxwidgets." ); } } //-------------------------------------------------------------------------- // void plD_eop_wxwidgets( PLStream *pls ) // // End of Page. This function is called if a "end of page" is send by the // user. This command is ignored if we have the plot embedded in a // wxWidgets application, otherwise the application created by the device // takes over. //-------------------------------------------------------------------------- void plD_eop_wxwidgets( PLStream *pls ) { // PLPLOT_wxLogDebug( "plD_eop_wxwidgets()" ); try { wxPLDevice *device = (wxPLDevice *) pls->dev; if ( !device ) throw( "plD_eop_wxwidgets called before initialization." ); if ( pls->nopause ) device->EndPage( pls ); } catch ( const char* message ) { plabort( message ); plabort( "error in plD_eop_wxwidgets." ); } catch ( ... ) { plabort( "unknown error in plD_eop_wxwidgets." ); } } //-------------------------------------------------------------------------- // void plD_wait_wxwidgets( PLStream *pls ) // // Wait for user input. This command is ignored if we have the plot embedded in a // wxWidgets application, otherwise the application created by the device // takes over. //-------------------------------------------------------------------------- void plD_wait_wxwidgets( PLStream *pls ) { // PLPLOT_wxLogDebug( "plD_wait_wxwidgets()" ); try { wxPLDevice *device = (wxPLDevice *) pls->dev; if ( !device ) throw( "plD_wait_wxwidgets called before initialization." ); device->EndPage( pls ); } catch ( const char* message ) { plabort( message ); plabort( "error in plD_wait_wxwidgets." ); } catch ( ... ) { plabort( "unknown error in plD_wait_wxwidgets." ); } } //-------------------------------------------------------------------------- // void plD_bop_wxwidgets( PLStream *pls ) // // Begin of page. Before any plot command, this function is called, If we // have already a dc the background is cleared in background color and some // state calls are resent - this is because at the first call of this // function, a dc does most likely not exist, but state calls are recorded // and when a new dc is created this function is called again. //-------------------------------------------------------------------------- void plD_bop_wxwidgets( PLStream *pls ) { // PLPLOT_wxLogDebug( "plD_bop_wxwidgets()" ); try { wxPLDevice *device = (wxPLDevice *) pls->dev; if ( !device ) throw( "plD_bop_wxwidgets called before initialization." ); device->BeginPage( pls ); } catch ( const char* message ) { plabort( message ); plabort( "error in plD_bop_wxwidgets." ); } catch ( ... ) { plabort( "unknown error in plD_bop_wxwidgets." ); } } //-------------------------------------------------------------------------- // void plD_tidy_wxwidgets( PLStream *pls ) // // This function is called, if all plots are done. //-------------------------------------------------------------------------- void plD_tidy_wxwidgets( PLStream *pls ) { // PLPLOT_wxLogDebug( "plD_tidy_wxwidgets()" ); if ( !pls->dev ) return; try { wxPLDevice *device = (wxPLDevice *) pls->dev; if ( device ) { device->PreDestructorTidy( pls ); delete device; } pls->dev = NULL; //so it doesn't get freed elsewhere if ( g_weInitializedWx ) wxEntryCleanup(); } catch ( const char* message ) { plabort( message ); plabort( "error in plD_tidy_wxwidgets." ); } catch ( ... ) { plabort( "unknown error in plD_tidy_wxwidgets." ); } } //-------------------------------------------------------------------------- // void plD_state_wxwidgets( PLStream *pls, PLINT op ) // // Handler for several state codes. Here we take care of setting the width // and color of the pen. //-------------------------------------------------------------------------- void plD_state_wxwidgets( PLStream *pls, PLINT op ) { // Log_Verbose( "plD_state_wxwidgets(op=%d)", op ); try { wxPLDevice *device = (wxPLDevice *) pls->dev; if ( !device ) return; //we can call state functions before initialization so just return switch ( op ) { case PLSTATE_WIDTH: // 1 device->SetWidth( pls ); break; case PLSTATE_COLOR0: // 2 device->SetColor( pls ); break; case PLSTATE_COLOR1: // 3 device->SetColor( pls ); break; } } catch ( const char* message ) { plabort( message ); plabort( "error in plD_state_wxwidgets." ); } catch ( ... ) { plabort( "unknown error in plD_state_wxwidgets." ); } } //-------------------------------------------------------------------------- // void plD_esc_wxwidgets( PLStream *pls, PLINT op, void *ptr ) // // Handler for several escape codes. Here we take care of filled polygons, // XOR or copy mode, initialize device (install dc from outside), and if // there is freetype support, re-rendering of text. //-------------------------------------------------------------------------- void plD_esc_wxwidgets( PLStream *pls, PLINT op, void *ptr ) { // Log_Verbose( "plD_esc_wxwidgets(op=%d, ptr=%x)", op, ptr ); if ( !pls->dev ) return; try { wxPLDevice *device = (wxPLDevice *) pls->dev; if ( !device ) throw( "plD_esc_wxwidgets called before initialization." ); switch ( op ) { case PLESC_FILL: device->FillPolygon( pls ); break; case PLESC_XORMOD: // switch between wxXOR and wxCOPY // if( dev->ready ) { // if( dev->m_dc->GetLogicalFunction() == wxCOPY ) // dev->m_dc->SetLogicalFunction( wxXOR ); // else if( dev->m_dc->GetLogicalFunction() == wxXOR ) // dev->m_dc->SetLogicalFunction( wxCOPY ); // } break; case PLESC_DEVINIT: device->SetDC( pls, (wxDC *) ptr ); break; case PLESC_HAS_TEXT: device->drawText( pls, (EscText *) ptr ); break; case PLESC_RESIZE: { wxSize* size = (wxSize *) ptr; device->SetSize( pls, size->GetWidth(), size->GetHeight() ); } break; case PLESC_CLEAR: device->ClearBackground( pls, pls->sppxmi, pls->sppymi, pls->sppxma, pls->sppyma ); break; case PLESC_FLUSH: // forced update of the window device->Flush( pls ); break; case PLESC_GETC: device->Locate( pls, (PLGraphicsIn *) ptr ); break; case PLESC_FIXASPECT: device->FixAspectRatio( *( (bool *) ptr ) ); break; default: break; } } catch ( const char* message ) { plabort( message ); plabort( "error in plD_tidy_wxwidgets." ); } catch ( ... ) { plabort( "unknown error in plD_tidy_wxwidgets." ); } } plplot-5.13.0/drivers/gd.driver_info.in000644 001752 001752 00000000116 13150160115 021540 0ustar00softwaresoftware000000 000000 png:PNG file:0:gd:39:png jpeg:JPEG file:0:gd:40:jpeg gif:GIF file:0:gd:47:gif plplot-5.13.0/drivers/cgm.c000644 001752 001752 00000061611 13150160115 017232 0ustar00softwaresoftware000000 000000 // PLplot cgm device driver. // // // This driver generates CGM (computer graphics metafiles) files (bit of a // tautology that... 'computer graphics metaFILES FILES' - oh well). // // The CGM format is vector-based and is widely used as an interchange // format between drawing and plotting programs. Although I have never // looked at them, there are apparently both Java applets and browser // plug-ins for displaying CGM files on web pages. // // This plplot driver supports lines, polylines (they make a difference to // CGM files), fills, and line widths. It is limited to 256 colours, which // should not be a problem. The plplot CGM driver's source (cgm.c) was // derived largely from the gd driver (gd.c). // // The plplot driver requires libcd.a. libcd.a is very similar to libgd.a // and has a similar licencing agreement behind it. Unlike libgd, // development of libcd seems to have crawled to a halt back in 1998 with // V1.3 of the library. The original host site for the library no longer // exists, so probably the best source of the library presently is: // // http://www.pa.msu.edu/reference/cgmdraw_ref.html // http://www.pa.msu.edu/ftp/pub/unix/ // // // Two options are supported by the driver via the -drvopt command line // toggle. // // By default CGM files don't have a background as such. The driver adds // support for different backgrounds by colouring in a large rectangle // underneath everything else. If for some reason you want the "raw plotted // junk" and aren't really interested in having an obtrusive piece of paper // in the back, use the command line toggle "-drvopt no_paper=1" to turn off // this background paper. // // By default the CGM files generated by this driver try to make edges of // polygons (ie fills) "invisible", which is something CGM files can do. // Some programs (ie CoreDraw) ignore this field and draw edges in anyway. // By setting "-drvopt force_edges=1" the driver will render edges on all // filled polygons, and will set their colour to the same as the polygon. // Most drivers should not need this, but if you see lines that you don't // think you should be seeing in your viewer, try this toggle. // // // Driver supports a hack that manipulates the colour palette when // a light background is selected. This is basically to make sure // there are not two "whites" when -bg ffffff is issued at the // command line. // // Related to this change, there is an ability to swap the "new" // black colour (index 15) with the red colour (index 2) by issuing // the command line "-hack" option. I like this for web pages, because // I think that black looks nicer than red (on white) for default // plotting. That is why it can be enabled with -hack, in case you // don't like it working this way. // // For me, these two changes make it easy to switch from a "screen friendly" // black background with red first plotting colour, to a "web friendly" // white background with a black first plotting colour. // // These features are enabled on a driver level by defining // "SWAP_BALCK_WHEN_WHITE". If you wan't the driver to behave 100% like other // drivers, comment out the define // #define SWAP_BALCK_WHEN_WHITE #include "plDevs.h" #ifdef PLD_cgm #include "plplotP.h" #include "drivers.h" #include // Device info PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_cgm = "cgm:CGM file:0:cgm:44:cgm\n"; // Prototypes for functions in this file. static void fill_polygon( PLStream *pls ); static void setcmap( PLStream *pls ); static void plD_init_cgm_Dev( PLStream *pls ); // top level declarations // In an attempt to fix a problem with the hidden line removal functions // that results in hidden lines *not* being removed from "small" plot // pages (ie, like a normal video screen), a "virtual" page of much // greater size is used to trick the algorithm into working correctly. // If, in future, this gets fixed on its own, then don't define // "use_experimental_hidden_line_hack" // #define use_experimental_hidden_line_hack static int force_edges = 0; static int disable_background = 0; static DrvOpt cgm_options[] = { { "force_edges", DRV_INT, &force_edges, "Force edges to be drawn on filled polygongs (0|1)" }, { "no_paper", DRV_INT, &disable_background, "Disable background (0|1)" }, { NULL, DRV_INT, NULL, NULL } }; // Struct to hold device-specific info. typedef struct { cdImagePtr im_out; // Graphics pointer PLINT cgmx; PLINT cgmy; // GD does "funny" things with the colour map. // It can't guarantee that the colours will be where you think they are. // So we need this "colour_index" table to store where the colour we // requested happens to be. Messy, but it works. // int colour_index[256]; // Colour "index" table // // I use two colours for both fill and line drawing - a "last colour" and // "current colour". The driver only switches colours if they have changed // and are used. If no fills are ever done, then the instruction to set the // fill colour is never sent to the CGM file. Should make for smaller and // more efficient files (I guess). // int colour; // Current Colour int last_line_colour; // Last line colour used int fill_colour; // Current Fill colour int last_fill_colour; // Last Fill colour used int totcol; // Total number of colours int ncol1; // Actual size of ncol1 we got PLFLT scale; // scaling factor to "blow up" to // the "virtual" page in removing hidden lines int force_edges; // Forces edges to be drawn in fills int disable_background; // Turns off background rectangle } cgm_Dev; void plD_init_cgm( PLStream * ); void plD_line_cgm( PLStream *, short, short, short, short ); void plD_polyline_cgm( PLStream *, short *, short *, PLINT ); void plD_eop_cgm( PLStream * ); void plD_bop_cgm( PLStream * ); void plD_tidy_cgm( PLStream * ); void plD_state_cgm( PLStream *, PLINT ); void plD_esc_cgm( PLStream *, PLINT, void * ); void plD_dispatch_init_cgm( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "CGM (Computer Graphics metafile) file"; pdt->pl_DevName = "cgm"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 44; pdt->pl_init = (plD_init_fp) plD_init_cgm; pdt->pl_line = (plD_line_fp) plD_line_cgm; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_cgm; pdt->pl_eop = (plD_eop_fp) plD_eop_cgm; pdt->pl_bop = (plD_bop_fp) plD_bop_cgm; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_cgm; pdt->pl_state = (plD_state_fp) plD_state_cgm; pdt->pl_esc = (plD_esc_fp) plD_esc_cgm; } //-------------------------------------------------------------------------- // plD_init_cgm_Dev() // //-------------------------------------------------------------------------- static void plD_init_cgm_Dev( PLStream *pls ) { cgm_Dev *dev; // Allocate and initialize device-specific data if ( pls->dev != NULL ) free( (void *) pls->dev ); pls->dev = calloc( 1, (size_t) sizeof ( cgm_Dev ) ); if ( pls->dev == NULL ) plexit( "plD_init_cgm_Dev: Out of memory." ); dev = (cgm_Dev *) pls->dev; // Check for and set up driver options plParseDrvOpts( cgm_options ); dev->force_edges = force_edges; // force edges (for corel draw etc...) dev->disable_background = disable_background; // Disable background dev->colour = 1; // Set a fall back pen colour in case user doesn't dev->fill_colour = dev->colour; // initially set fill and line colour the same dev->last_fill_colour = -1; // set to -1 = unallocated dev->last_line_colour = -1; // set to -1 = unallocated } //-------------------------------------------------------------------------- // plD_init_cgm() // // Initialize device. //-------------------------------------------------------------------------- void plD_init_cgm( PLStream *pls ) { cgm_Dev *dev = NULL; pls->termin = 0; // Not an interactive device pls->icol0 = 1; pls->bytecnt = 0; pls->page = 0; pls->dev_fill0 = 1; // Can do solid fills if ( !pls->colorset ) pls->color = 1; // Is a color device if ( pls->width < 1 ) pls->width = 1; // set a legal line width // Initialize family file info plFamInit( pls ); // Prompt for a file name if not already set plOpenFile( pls ); // Allocate and initialize device-specific data plD_init_cgm_Dev( pls ); dev = (cgm_Dev *) pls->dev; // set dpi and page size defaults if the user has not already set // these with -dpi or -geometry command line options or with // plspage. if ( pls->xdpi <= 0. || pls->ydpi <= 0. ) { // Use recommended default pixels per inch. plspage( PLPLOT_DEFAULT_PIXELS_PER_INCH, PLPLOT_DEFAULT_PIXELS_PER_INCH, 0, 0, 0, 0 ); } if ( pls->xlength == 0 || pls->ylength == 0 ) { // Use recommended default pixel width and height. plspage( 0., 0., PLPLOT_DEFAULT_WIDTH_PIXELS, PLPLOT_DEFAULT_HEIGHT_PIXELS, 0, 0 ); } pls->graphx = GRAPHICS_MODE; dev->cgmx = pls->xlength - 1; // should I use -1 or not??? dev->cgmy = pls->ylength - 1; #ifdef use_experimental_hidden_line_hack if ( dev->cgmx > dev->cgmy ) // Work out the scaling factor for the { // "virtual" (oversized) page dev->scale = (PLFLT) ( PIXELS_X - 1 ) / (PLFLT) dev->cgmx; } else { dev->scale = (PLFLT) PIXELS_Y / (PLFLT) dev->cgmy; } #else dev->scale = 1.; #endif // Convert DPI to pixels/mm plP_setpxl( dev->scale * pls->xdpi / PLPLOT_MM_PER_INCH, dev->scale * pls->ydpi / PLPLOT_MM_PER_INCH ); plP_setphy( 0, dev->scale * dev->cgmx, 0, dev->scale * dev->cgmy ); } //-------------------------------------------------------------------------- // plD_line_cgm() // // Draw a line in the current color from (x1,y1) to (x2,y2). //-------------------------------------------------------------------------- void plD_line_cgm( PLStream *pls, short x1a, short y1a, short x2a, short y2a ) { cgm_Dev *dev = (cgm_Dev *) pls->dev; int x1 = x1a / dev->scale, y1 = y1a / dev->scale, x2 = x2a / dev->scale, y2 = y2a / dev->scale; y1 = y1; y2 = y2; // // Determine if the colour has changed since the last time a line was // drawn. If it has, then set the colour NOW otherwise, keep on going like // "nuthin happened". // if ( dev->last_line_colour != dev->colour ) { cdSetLineColor( dev->im_out, dev->colour ); dev->last_line_colour = dev->colour; } cdLine( dev->im_out, x1, y1, x2, y2 ); } //-------------------------------------------------------------------------- // plD_polyline_cgm() // // Draw a polyline in the current color. //-------------------------------------------------------------------------- void plD_polyline_cgm( PLStream *pls, short *xa, short *ya, PLINT npts ) { cgm_Dev *dev = (cgm_Dev *) pls->dev; PLINT i; cdPoint *points = NULL; if ( ( points = calloc( npts, (size_t) sizeof ( cdPoint ) ) ) == NULL ) { plexit( "Memory allocation error in \"plD_polyline_cgm\"" ); } for ( i = 0; i < npts; i++ ) { points[i].x = xa[i] / dev->scale; points[i].y = ( ya[i] / dev->scale ); } // // Determine if the colour has changed since the last time a line was // drawn. If it has, then set the colour NOW otherwise, keep on going like // "nuthin happened". // if ( dev->last_line_colour != dev->colour ) { cdSetLineColor( dev->im_out, dev->colour ); dev->last_line_colour = dev->colour; } cdPolyLine( dev->im_out, points, npts ); free( points ); } //-------------------------------------------------------------------------- // fill_polygon() // // Fill polygon described in points pls->dev_x[] and pls->dev_y[]. //-------------------------------------------------------------------------- static void fill_polygon( PLStream *pls ) { cgm_Dev *dev = (cgm_Dev *) pls->dev; PLINT i; cdPoint *points = NULL; if ( pls->dev_npts < 1 ) return; if ( ( points = calloc( pls->dev_npts, (size_t) sizeof ( cdPoint ) ) ) == NULL ) { plexit( "Memory allocation error in \"plD_fill_polygon_cgm\"" ); } for ( i = 0; i < pls->dev_npts; i++ ) { points[i].x = pls->dev_x[i] / dev->scale; points[i].y = ( pls->dev_y[i] / dev->scale ); } // // Determine if the fill colour has changed since the last time a fill was // done. If it has, then set the colour NOW otherwise, keep on going like // "nuthin happened". If it's the first time, we will know 'cause the the // "last_fill_colour" will be -1. // if ( ( dev->fill_colour != dev->last_fill_colour ) || ( dev->force_edges == 1 ) ) { cdSetFillColor( dev->im_out, dev->fill_colour ); // // Due to a bug in cd V1.3, we have to set the edge colour to the fill // colour. This is despite telling the library edges should be invisible. // Seems the invisible edges only work with rectangles. // if ( dev->force_edges == 1 ) { cdSetEdgeColor( dev->im_out, dev->fill_colour ); cdSetEdgeVis( dev->im_out, 1 ); } dev->last_fill_colour = dev->fill_colour; } cdPolygon( dev->im_out, points, pls->dev_npts ); if ( dev->force_edges == 1 ) cdSetEdgeVis( dev->im_out, 0 ); // Turn edges off now free( points ); } //-------------------------------------------------------------------------- // setcmap() // // Sets up color palette. //-------------------------------------------------------------------------- static void setcmap( PLStream *pls ) { int i, ncol1 = pls->ncol1; int ncol0 = pls->ncol0, total_colours; PLColor cmap1col; cgm_Dev *dev = (cgm_Dev *) pls->dev; PLFLT tmp_colour_pos; cdImageColorClear( dev->im_out ); // UNDOCUMENTED FUNCTION TO RESET THE // INTERNAL COLOUR TABLE OF THE // CD DRIVER. Seems to work and fix // the errors if ( ncol0 > cdMaxColors / 2 ) // Check for ridiculous number of colours { // in ncol0, and appropriately adjust the plwarn( "Too many colours in cmap0." ); // number, issuing a ncol0 = cdMaxColors / 2; // warning if it does pls->ncol0 = ncol0; } dev->totcol = 0; // Reset the number of colours counter to zero total_colours = ncol0 + ncol1; // Work out how many colours are wanted if ( total_colours > cdMaxColors ) // Do some rather modest error { // checking to make sure that total_colours = cdMaxColors; // we are not defining more colours ncol1 = total_colours - ncol0; // than we have room for. if ( ncol1 <= 0 ) { plexit( "Problem setting colourmap in CGM driver." ); } } dev->ncol1 = ncol1; // The actual size of ncol1, regardless of what was asked. // This is dependent on colour slots available. // It might well be the same as ncol1. // // Initialize cmap 0 colors if ( ncol0 > 0 ) // make sure the program actually asked for cmap0 first { #ifdef SWAP_BALCK_WHEN_WHITE // // Do a kludge to add a "black" colour back to the palette if the // background is "almost white" (ie changed through -bg). // // Also includes an "optional" change to swap the red colour (1) with the // black colour (15), which is off by default. (I don't like the red being // the 'default' colour "1" on a "white" background, or for that matter // yellow being "2", but I can live more with yellow at number two.) // Just use "-hack" from the command line to make it take effect. // // if ( ( pls->ncol0 > 15 ) && ( pls->cmap0[0].r > 227 ) && ( pls->cmap0[0].g > 227 ) && ( pls->cmap0[0].b > 227 ) ) { if ( pls->hack != 1 ) // just set colour 15 to black { pls->cmap0[15].r = 0; pls->cmap0[15].g = 0; pls->cmap0[15].b = 0; } else // swap colour 15 and colour 1 { pls->cmap0[15].r = pls->cmap0[1].r; pls->cmap0[15].g = pls->cmap0[1].g; pls->cmap0[15].b = pls->cmap0[1].b; pls->cmap0[1].r = 0; pls->cmap0[1].g = 0; pls->cmap0[1].b = 0; } } #endif for ( i = 0; i < ncol0; i++ ) { if ( ( dev->colour_index[i] = cdImageColorAllocate( dev->im_out, pls->cmap0[i].r, pls->cmap0[i].g, pls->cmap0[i].b ) ) == -1 ) { plwarn( "Problem setting cmap0 in CGM driver." ); } else ++dev->totcol; // count the number of colours we use as we use them } } // Initialize any remaining slots for cmap1 if ( ncol1 > 0 ) // make sure that we want to define cmap1 first { for ( i = 0; i < ncol1; i++ ) { if ( ncol1 < pls->ncol1 ) // Check the dynamic range of colours { // // Ok, now if we have less colour slots available than are being // defined by pls->ncol1, then we still want to use the full // dynamic range of cmap1 as best we can, so what we do is work // out an approximation to the index in the full dynamic range // in cases when pls->ncol1 exceeds the number of free colours. // tmp_colour_pos = i > 0 ? pls->ncol1 * ( (PLFLT) i / ncol1 ) : 0; plcol_interp( pls, &cmap1col, (int) tmp_colour_pos, pls->ncol1 ); } else { plcol_interp( pls, &cmap1col, i, ncol1 ); } if ( ( dev->colour_index[i + pls->ncol0] = cdImageColorAllocate( dev->im_out, cmap1col.r, cmap1col.g, cmap1col.b ) ) == -1 ) { plwarn( "Problem setting cmap1 in CGM driver." ); } else ++dev->totcol; // count the number of colours we use as we use them } } } //-------------------------------------------------------------------------- // plD_state_cgm() // // Handle change in PLStream state (color, pen width, fill attribute, etc). //-------------------------------------------------------------------------- void plD_state_cgm( PLStream *pls, PLINT op ) { cgm_Dev *dev = (cgm_Dev *) pls->dev; PLFLT tmp_colour_pos; switch ( op ) { case PLSTATE_WIDTH: cdSetLineWidth( dev->im_out, pls->width ); break; case PLSTATE_COLOR0: dev->colour = pls->icol0; if ( dev->colour == PL_RGB_COLOR ) { int r = pls->curcolor.r; int g = pls->curcolor.g; int b = pls->curcolor.b; if ( dev->totcol < cdMaxColors ) { if ( ( dev->colour_index[++dev->totcol] = cdImageColorAllocate( dev->im_out, r, g, b ) ) == -1 ) plwarn( "Problem changing colour in \"PLSTATE_COLOR0\"" ); else dev->colour = dev->totcol; } } dev->fill_colour = dev->colour; break; case PLSTATE_COLOR1: // // Start by checking to see if we have to compensate for cases where // we don't have the full dynamic range of cmap1 at our disposal // if ( dev->ncol1 < pls->ncol1 ) { tmp_colour_pos = dev->ncol1 * ( (PLFLT) pls->icol1 / ( pls->ncol1 > 0 ? pls->ncol1 : 1 ) ); dev->colour = pls->ncol0 + (int) tmp_colour_pos; } else dev->colour = pls->ncol0 + pls->icol1; dev->fill_colour = dev->colour; break; case PLSTATE_CMAP0: case PLSTATE_CMAP1: // // Code to redefine the entire palette // if ( pls->color ) setcmap( pls ); break; } } //-------------------------------------------------------------------------- // plD_esc_cgm() // // Escape function. //-------------------------------------------------------------------------- void plD_esc_cgm( PLStream *pls, PLINT op, void *ptr ) { switch ( op ) { case PLESC_FILL: // fill fill_polygon( pls ); break; } } //-------------------------------------------------------------------------- // plD_bop_cgm() // // Set up for the next page. // Advance to next family file if necessary (file output). //-------------------------------------------------------------------------- void plD_bop_cgm( PLStream *pls ) { cgm_Dev *dev; plGetFam( pls ); // force new file if pls->family set for all subsequent calls to plGetFam // n.b. putting this after plGetFam call is important since plinit calls // bop, and you don't want the familying sequence started until after // that first call to bop. pls->famadv = 1; pls->page++; // n.b. pls->dev can change because of an indirect call to plD_init_cgm // from plGetFam if familying is enabled. Thus, wait to define dev until // now. dev = (cgm_Dev *) pls->dev; if ( pls->page == 1 ) { dev->im_out = cdImageCreate( pls->xlength, pls->ylength ); } else if ( pls->family != 1 ) { cdCgmNewPic( dev->im_out, 0 ); } setcmap( pls ); // Continue to initialise the driver cdSetFillStyle( dev->im_out, 1 ); // Set solid fills // // Due to a border being drawn around the edge of the image, we also // manually, then turn them off again to make edges turn off. By // default the driver thinks they are off, so when we tell the driver // to turn them off it says "they are already off, I wont do anything" // but in reality they are on by default. So what we do is turn them ON // manually, then turn them OFF later. // // Due to a boarder being drawn around the edge of the image, we also // want the edges turned on so we can lay down a rectangle coloured in // the background colour at the start. Once we have drawn our // background box, we then turn edges off for the rest of the page. // cdSetEdgeVis( dev->im_out, 1 ); // turn edges on so we can turn them off! if ( dev->disable_background != 1 ) { cdSetEdgeWidth( dev->im_out, pls->xlength / 5 ); // set edge to *really* wide so we can cover the edge of the page completelt cdSetEdgeColor( dev->im_out, 0 ); // set the edge colour to the background colour so we can make a coloured page cdSetFillColor( dev->im_out, 0 ); // set fill colour to background colour so we have a coloured page cdRectangle( dev->im_out, 0, 0, pls->xlength - 1, pls->ylength - 1 ); // Draw a coloured rectangle to act as our "paper" } cdSetEdgeVis( dev->im_out, 0 ); // Turn edges off now cdSetEdgeWidth( dev->im_out, 0 ); // Just to be 100% sure cdSetLineType( dev->im_out, 1 ); // set solid lines cdSetLineWidth( dev->im_out, pls->width ); // set initial line width for each page } //-------------------------------------------------------------------------- // plD_tidy_cgm() // // Close graphics file or otherwise clean up. //-------------------------------------------------------------------------- void plD_tidy_cgm( PLStream *pls ) { cgm_Dev *dev = (cgm_Dev *) pls->dev; if ( pls->family != 1 ) { cdImageCgm( dev->im_out, pls->OutFile ); } cdImageDestroy( dev->im_out ); plCloseFile( pls ); free_mem( pls->dev ); } //-------------------------------------------------------------------------- // plD_eop_cgm() // // End of page. //-------------------------------------------------------------------------- void plD_eop_cgm( PLStream *pls ) { cgm_Dev *dev = (cgm_Dev *) pls->dev; int i; if ( pls->family == 1 ) { cdImageCgm( dev->im_out, pls->OutFile ); } for ( i = 0; i < cdMaxColors; ++i ) dev->colour_index[i] = -1; dev->fill_colour = dev->colour; // initially set fill and line colour the same dev->last_fill_colour = -1; // set to -1 = unallocated dev->last_line_colour = -1; // set to -1 = unallocated } //#endif #else int pldummy_cgm() { return 0; } #endif // cgm plplot-5.13.0/drivers/ps.c000644 001752 001752 00000116010 13150160115 017100 0ustar00softwaresoftware000000 000000 // PLplot PostScript device driver. // // Copyright (C) 1992, 2001 Geoffrey Furnish // Copyright (C) 1992, 1993, 1994, 1995, 2001 Maurice LeBrun // Copyright (C) 2000-2014 Alan W. Irwin // Copyright (C) 2001, 2002 Joao Cardoso // Copyright (C) 2001, 2003, 2004 Rafael Laboissiere // Copyright (C) 2004, 2005 Thomas J. Duck // Copyright (C) 2005 Andrew Ross // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // #include "plDevs.h" #define DEBUG #ifdef PLD_ps #define NEED_PLDEBUG #include "plplotP.h" #include "drivers.h" #include "ps.h" #include #include #include "plunicode-type1.h" #include "plfci-type1.h" // Define macro to truncate small values to zero - prevents // printf printing -0.000 #define TRMFLT( a ) ( ( fabs( a ) < 5.0e-4 ) ? 0.0 : ( a ) ) // Device info PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_ps = "ps:PostScript File (monochrome):0:ps:29:psm\n" "psc:PostScript File (color):0:ps:30:psc\n"; // Prototypes for functions in this file. void plD_dispatch_init_psm( PLDispatchTable *pdt ); void plD_dispatch_init_psc( PLDispatchTable *pdt ); static char *ps_getdate( void ); static void ps_init( PLStream * ); static void fill_polygon( PLStream *pls ); static void proc_str( PLStream *, EscText * ); static void esc_purge( unsigned char *, unsigned char * ); #define OUTBUF_LEN 128 static char outbuf[OUTBUF_LEN]; static int text = 1; static int color; static int hrshsym = 1; static DrvOpt ps_options[] = { { "text", DRV_INT, &text, "Use Postscript text (text=0|1)" }, { "color", DRV_INT, &color, "Use color (color=0|1)" }, { "hrshsym", DRV_INT, &hrshsym, "Use Hershey symbol set (hrshsym=0|1)" }, { NULL, DRV_INT, NULL, NULL } }; static unsigned char plunicode2type1( const PLUNICODE index, const Unicode_to_Type1_table lookup[], const int number_of_entries ); static const char * get_font( PSDev* dev, PLUNICODE fci ); // text > 0 uses some postscript tricks, namely a transformation matrix // that scales, rotates (with slanting) and offsets text strings. // It has yet some bugs for 3d plots. static void ps_dispatch_init_helper( PLDispatchTable *pdt, const char *menustr, const char *devnam, int type, int seq, plD_init_fp init ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = (char *) menustr; pdt->pl_DevName = (char *) devnam; #else (void) menustr; // Cast to void to silence compiler warnings about unused parameters (void) devnam; #endif pdt->pl_type = type; pdt->pl_seq = seq; pdt->pl_init = init; pdt->pl_line = (plD_line_fp) plD_line_ps; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_ps; pdt->pl_eop = (plD_eop_fp) plD_eop_ps; pdt->pl_bop = (plD_bop_fp) plD_bop_ps; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_ps; pdt->pl_state = (plD_state_fp) plD_state_ps; pdt->pl_esc = (plD_esc_fp) plD_esc_ps; } void plD_dispatch_init_psm( PLDispatchTable *pdt ) { ps_dispatch_init_helper( pdt, "PostScript File (monochrome)", "ps", plDevType_FileOriented, 29, (plD_init_fp) plD_init_psm ); } void plD_dispatch_init_psc( PLDispatchTable *pdt ) { ps_dispatch_init_helper( pdt, "PostScript File (color)", "psc", plDevType_FileOriented, 30, (plD_init_fp) plD_init_psc ); } //-------------------------------------------------------------------------- // plD_init_ps() // // Initialize device. //-------------------------------------------------------------------------- void plD_init_psm( PLStream *pls ) { color = 0; pls->color = 0; // Not a color device plParseDrvOpts( ps_options ); if ( color ) pls->color = 1; // But user wants color ps_init( pls ); } void plD_init_psc( PLStream *pls ) { color = 1; pls->color = 1; // Is a color device plParseDrvOpts( ps_options ); if ( !color ) pls->color = 0; // But user does not want color ps_init( pls ); } static void ps_init( PLStream *pls ) { PSDev *dev; PLFLT pxlx, pxly; // Set default values - 7.5 x 10 [inches] (72 points = 1 inch) if ( pls->xlength <= 0 || pls->ylength <= 0 ) { pls->xlength = 540; pls->ylength = 720; pls->xoffset = 32; pls->yoffset = 32; } if ( pls->xdpi <= 0 ) pls->xdpi = 72.; if ( pls->ydpi <= 0 ) pls->ydpi = 72.; pxlx = YPSSIZE / LPAGE_X; pxly = XPSSIZE / LPAGE_Y; if ( text ) { pls->dev_text = 1; // want to draw text pls->dev_unicode = 1; // want unicode if ( hrshsym ) pls->dev_hrshsym = 1; // want Hershey symbols } pls->dev_fill0 = 1; // Can do solid fills // Initialize family file info plFamInit( pls ); // Prompt for a file name if not already set plOpenFile( pls ); // Allocate and initialize device-specific data if ( pls->dev != NULL ) free( (void *) pls->dev ); pls->dev = calloc( 1, (size_t) sizeof ( PSDev ) ); if ( pls->dev == NULL ) plexit( "ps_init: Out of memory." ); dev = (PSDev *) pls->dev; dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; plP_setpxl( pxlx, pxly ); dev->llx = XPSSIZE; dev->lly = YPSSIZE; dev->urx = 0; dev->ury = 0; dev->ptcnt = 0; // Rotate by 90 degrees since portrait mode addressing is used dev->xmin = 0; dev->ymin = 0; dev->xmax = PSY; dev->ymax = PSX; dev->xlen = dev->xmax - dev->xmin; dev->ylen = dev->ymax - dev->ymin; plP_setphy( dev->xmin, dev->xmax, dev->ymin, dev->ymax ); // If portrait mode is specified, then set up an additional rotation // transformation with aspect ratio allowed to adjust via freeaspect. // Default orientation is landscape (ORIENTATION == 3 or 90 deg rotation // counter-clockwise from portrait). (Legacy PLplot used seascape // which was equivalent to ORIENTATION == 1 or 90 deg clockwise rotation // from portrait.) if ( pls->portrait ) { plsdiori( (PLFLT) ( 4 - ORIENTATION ) ); pls->freeaspect = 1; } // Header comments into PostScript file fprintf( OF, "%%!PS-Adobe-2.0 EPSF-2.0\n" ); fprintf( OF, "%%%%BoundingBox: \n" ); fprintf( OF, "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" ); fprintf( OF, "%%%%Title: PLplot Graph\n" ); fprintf( OF, "%%%%Creator: PLplot Version %s\n", PLPLOT_VERSION ); fprintf( OF, "%%%%CreationDate: %s\n", ps_getdate() ); fprintf( OF, "%%%%Pages: (atend)\n" ); fprintf( OF, "%%%%EndComments\n\n" ); // Definitions // Save VM state fprintf( OF, "/PSSave save def\n" ); // Define a dictionary and start using it fprintf( OF, "/PSDict 200 dict def\n" ); fprintf( OF, "PSDict begin\n" ); fprintf( OF, "/@restore /restore load def\n" ); fprintf( OF, "/restore\n" ); fprintf( OF, " {vmstatus pop\n" ); fprintf( OF, " dup @VMused lt {pop @VMused} if\n" ); fprintf( OF, " exch pop exch @restore /@VMused exch def\n" ); fprintf( OF, " } def\n" ); fprintf( OF, "/@pri\n" ); fprintf( OF, " {\n" ); fprintf( OF, " ( ) print\n" ); fprintf( OF, " ( ) cvs print\n" ); fprintf( OF, " } def\n" ); // n @copies - fprintf( OF, "/@copies\n" ); fprintf( OF, " {\n" ); fprintf( OF, " /#copies exch def\n" ); fprintf( OF, " } def\n" ); // - @start - -- start everything fprintf( OF, "/@start\n" ); fprintf( OF, " {\n" ); fprintf( OF, " vmstatus pop /@VMused exch def pop\n" ); fprintf( OF, " } def\n" ); // - @end - -- finished fprintf( OF, "/@end\n" ); fprintf( OF, " {flush\n" ); fprintf( OF, " end\n" ); fprintf( OF, " PSSave restore\n" ); fprintf( OF, " } def\n" ); // bop - -- begin a new page // Only fill background if we are using color and if the bg isn't white fprintf( OF, "/bop\n" ); fprintf( OF, " {\n" ); fprintf( OF, " /SaveImage save def\n" ); fprintf( OF, " } def\n" ); // - eop - -- end a page fprintf( OF, "/eop\n" ); fprintf( OF, " {\n" ); fprintf( OF, " showpage\n" ); fprintf( OF, " SaveImage restore\n" ); fprintf( OF, " } def\n" ); // Set line parameters fprintf( OF, "/@line\n" ); fprintf( OF, " {0 setlinecap\n" ); fprintf( OF, " 0 setlinejoin\n" ); fprintf( OF, " 1 setmiterlimit\n" ); fprintf( OF, " } def\n" ); // d @hsize - horizontal clipping dimension fprintf( OF, "/@hsize {/hs exch def} def\n" ); fprintf( OF, "/@vsize {/vs exch def} def\n" ); // d @hoffset - shift for the plots fprintf( OF, "/@hoffset {/ho exch def} def\n" ); fprintf( OF, "/@voffset {/vo exch def} def\n" ); // Set line width fprintf( OF, "/lw %d def\n", (int) ( ( pls->width < MIN_WIDTH ) ? DEF_WIDTH : ( pls->width > MAX_WIDTH ) ? MAX_WIDTH : pls->width ) ); // Setup user specified offsets, scales, sizes for clipping fprintf( OF, "/@SetPlot\n" ); fprintf( OF, " {\n" ); fprintf( OF, " ho vo translate\n" ); fprintf( OF, " XScale YScale scale\n" ); fprintf( OF, " lw setlinewidth\n" ); fprintf( OF, " } def\n" ); // Setup x & y scales fprintf( OF, "/XScale\n" ); fprintf( OF, " {hs %d div} def\n", YPSSIZE ); fprintf( OF, "/YScale\n" ); fprintf( OF, " {vs %d div} def\n", XPSSIZE ); // Macro definitions of common instructions, to keep output small fprintf( OF, "/M {moveto} def\n" ); fprintf( OF, "/D {lineto} def\n" ); fprintf( OF, "/A {0.5 0 360 arc} def\n" ); fprintf( OF, "/S {stroke} def\n" ); fprintf( OF, "/Z {stroke newpath} def\n" ); // Modify to use fill and stroke for better output with // anti-aliasing //fprintf(OF, "/F {fill} def\n"); if ( pls->dev_eofill ) fprintf( OF, "/F {closepath gsave eofill grestore stroke} def " ); else fprintf( OF, "/F {closepath gsave fill grestore stroke} def " ); fprintf( OF, "/N {newpath} def" ); fprintf( OF, "/C {setrgbcolor} def\n" ); fprintf( OF, "/G {setgray} def\n" ); fprintf( OF, "/W {setlinewidth} def\n" ); fprintf( OF, "/SF {selectfont} def\n" ); fprintf( OF, "/R {rotate} def\n" ); fprintf( OF, "/SW {stringwidth 2 index mul exch 2 index mul exch rmoveto pop} bind def\n" ); fprintf( OF, "/B {Z %d %d M %d %d D %d %d D %d %d D %d %d closepath} def\n", 0, 0, 0, PSY, PSX, PSY, PSX, 0, 0, 0 ); fprintf( OF, "/CL {newpath M D D D closepath clip} def\n" ); // End of dictionary definition fprintf( OF, "end\n\n" ); // Set up the plots fprintf( OF, "PSDict begin\n" ); fprintf( OF, "@start\n" ); fprintf( OF, "%d @copies\n", COPIES ); fprintf( OF, "@line\n" ); fprintf( OF, "%d @hsize\n", YSIZE ); fprintf( OF, "%d @vsize\n", XSIZE ); fprintf( OF, "%d @hoffset\n", YOFFSET ); fprintf( OF, "%d @voffset\n", XOFFSET ); fprintf( OF, "@SetPlot\n\n" ); } //-------------------------------------------------------------------------- // plD_line_ps() // // Draw a line in the current color from (x1,y1) to (x2,y2). //-------------------------------------------------------------------------- void plD_line_ps( PLStream *pls, short x1a, short y1a, short x2a, short y2a ) { PSDev *dev = (PSDev *) pls->dev; PLINT x1 = x1a, y1 = y1a, x2 = x2a, y2 = y2a; // Rotate by 90 degrees plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &x1, &y1 ); plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &x2, &y2 ); if ( x1 == dev->xold && y1 == dev->yold && dev->ptcnt < 40 ) { if ( pls->linepos + 12 > LINELENGTH ) { putc( '\n', OF ); pls->linepos = 0; } else putc( ' ', OF ); snprintf( outbuf, OUTBUF_LEN, "%d %d D", x2, y2 ); dev->ptcnt++; pls->linepos += 12; } else { fprintf( OF, " Z\n" ); pls->linepos = 0; if ( x1 == x2 && y1 == y2 ) // must be a single dot, draw a circle snprintf( outbuf, OUTBUF_LEN, "%d %d A", x1, y1 ); else snprintf( outbuf, OUTBUF_LEN, "%d %d M %d %d D", x1, y1, x2, y2 ); dev->llx = MIN( dev->llx, x1 ); dev->lly = MIN( dev->lly, y1 ); dev->urx = MAX( dev->urx, x1 ); dev->ury = MAX( dev->ury, y1 ); dev->ptcnt = 1; pls->linepos += 24; } dev->llx = MIN( dev->llx, x2 ); dev->lly = MIN( dev->lly, y2 ); dev->urx = MAX( dev->urx, x2 ); dev->ury = MAX( dev->ury, y2 ); fprintf( OF, "%s", outbuf ); pls->bytecnt += 1 + (PLINT) strlen( outbuf ); dev->xold = x2; dev->yold = y2; } //-------------------------------------------------------------------------- // plD_polyline_ps() // // Draw a polyline in the current color. //-------------------------------------------------------------------------- void plD_polyline_ps( PLStream *pls, short *xa, short *ya, PLINT npts ) { PLINT i; for ( i = 0; i < npts - 1; i++ ) plD_line_ps( pls, xa[i], ya[i], xa[i + 1], ya[i + 1] ); } //-------------------------------------------------------------------------- // plD_eop_ps() // // End of page. //-------------------------------------------------------------------------- void plD_eop_ps( PLStream *pls ) { fprintf( OF, " S\neop\n" ); } //-------------------------------------------------------------------------- // plD_bop_ps() // // Set up for the next page. // Advance to next family file if necessary (file output). //-------------------------------------------------------------------------- void plD_bop_ps( PLStream *pls ) { PSDev *dev = (PSDev *) pls->dev; dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; if ( !pls->termin ) plGetFam( pls ); pls->page++; if ( pls->family ) fprintf( OF, "%%%%Page: %d %d\n", (int) pls->page, 1 ); else fprintf( OF, "%%%%Page: %d %d\n", (int) pls->page, (int) pls->page ); fprintf( OF, "bop\n" ); if ( pls->color ) { PLFLT r, g, b; if ( pls->cmap0[0].r != 0xFF || pls->cmap0[0].g != 0xFF || pls->cmap0[0].b != 0xFF ) { r = ( (PLFLT) pls->cmap0[0].r ) / 255.; g = ( (PLFLT) pls->cmap0[0].g ) / 255.; b = ( (PLFLT) pls->cmap0[0].b ) / 255.; fprintf( OF, "B %.4f %.4f %.4f C F\n", r, g, b ); } } pls->linepos = 0; // This ensures the color and line width are set correctly at the beginning of // each page plD_state_ps( pls, PLSTATE_COLOR0 ); plD_state_ps( pls, PLSTATE_WIDTH ); } //-------------------------------------------------------------------------- // plD_tidy_ps() // // Close graphics file or otherwise clean up. //-------------------------------------------------------------------------- void plD_tidy_ps( PLStream *pls ) { PSDev *dev = (PSDev *) pls->dev; fprintf( OF, "\n%%%%Trailer\n" ); dev->llx /= ENLARGE; dev->lly /= ENLARGE; dev->urx /= ENLARGE; dev->ury /= ENLARGE; dev->llx += YOFFSET; dev->lly += XOFFSET; dev->urx += YOFFSET; dev->ury += XOFFSET; // changed for correct Bounding boundaries Jan Thorbecke okt 1993 // occurs from the integer truncation -- postscript uses fp arithmetic dev->urx += 1; dev->ury += 1; if ( pls->family ) fprintf( OF, "%%%%Pages: %d\n", (int) 1 ); else fprintf( OF, "%%%%Pages: %d\n", (int) pls->page ); fprintf( OF, "@end\n" ); fprintf( OF, "%%%%EOF\n" ); // Backtrack to write the BoundingBox at the beginning // Some applications don't like it atend rewind( OF ); fprintf( OF, "%%!PS-Adobe-2.0 EPSF-2.0\n" ); fprintf( OF, "%%%%BoundingBox: %d %d %d %d\n", dev->llx, dev->lly, dev->urx, dev->ury ); plCloseFile( pls ); } //-------------------------------------------------------------------------- // plD_state_ps() // // Handle change in PLStream state (color, pen width, fill attribute, etc). //-------------------------------------------------------------------------- void plD_state_ps( PLStream *pls, PLINT op ) { PSDev *dev = (PSDev *) pls->dev; switch ( op ) { case PLSTATE_WIDTH: { int width = (int) ( ( pls->width < MIN_WIDTH ) ? DEF_WIDTH : ( pls->width > MAX_WIDTH ) ? MAX_WIDTH : pls->width ); fprintf( OF, " S\n%d W", width ); dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; break; } case PLSTATE_COLOR0: if ( !pls->color ) { fprintf( OF, " S\n%.4f G", ( pls->icol0 ? 0.0 : 1.0 ) ); // Reinitialize current point location. if ( dev->xold != PL_UNDEFINED && dev->yold != PL_UNDEFINED ) fprintf( OF, " %d %d M \n", (int) dev->xold, (int) dev->yold ); break; } // else fallthrough case PLSTATE_COLOR1: if ( pls->color ) { PLFLT r = ( (PLFLT) pls->curcolor.r ) / 255.0; PLFLT g = ( (PLFLT) pls->curcolor.g ) / 255.0; PLFLT b = ( (PLFLT) pls->curcolor.b ) / 255.0; fprintf( OF, " S\n%.4f %.4f %.4f C", r, g, b ); } else { PLFLT r = ( (PLFLT) pls->curcolor.r ) / 255.0; fprintf( OF, " S\n%.4f G", 1.0 - r ); } // Reinitialize current point location. if ( dev->xold != PL_UNDEFINED && dev->yold != PL_UNDEFINED ) fprintf( OF, " %d %d M \n", (int) dev->xold, (int) dev->yold ); break; } } //-------------------------------------------------------------------------- // plD_esc_ps() // // Escape function. //-------------------------------------------------------------------------- void plD_esc_ps( PLStream *pls, PLINT op, void *ptr ) { switch ( op ) { case PLESC_FILL: fill_polygon( pls ); break; case PLESC_HAS_TEXT: proc_str( pls, (EscText *) ptr ); break; } } //-------------------------------------------------------------------------- // fill_polygon() // // Fill polygon described in points pls->dev_x[] and pls->dev_y[]. // Only solid color fill supported. //-------------------------------------------------------------------------- static void fill_polygon( PLStream *pls ) { PSDev *dev = (PSDev *) pls->dev; PLINT n, ix = 0, iy = 0; PLINT x, y; fprintf( OF, " Z\n" ); for ( n = 0; n < pls->dev_npts; n++ ) { x = pls->dev_x[ix++]; y = pls->dev_y[iy++]; // Rotate by 90 degrees plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &x, &y ); // First time through start with a x y moveto if ( n == 0 ) { snprintf( outbuf, OUTBUF_LEN, "N %d %d M", x, y ); dev->llx = MIN( dev->llx, x ); dev->lly = MIN( dev->lly, y ); dev->urx = MAX( dev->urx, x ); dev->ury = MAX( dev->ury, y ); fprintf( OF, "%s", outbuf ); pls->bytecnt += (PLINT) strlen( outbuf ); continue; } if ( pls->linepos + 21 > LINELENGTH ) { putc( '\n', OF ); pls->linepos = 0; } else putc( ' ', OF ); pls->bytecnt++; snprintf( outbuf, OUTBUF_LEN, "%d %d D", x, y ); dev->llx = MIN( dev->llx, x ); dev->lly = MIN( dev->lly, y ); dev->urx = MAX( dev->urx, x ); dev->ury = MAX( dev->ury, y ); fprintf( OF, "%s", outbuf ); pls->bytecnt += (PLINT) strlen( outbuf ); pls->linepos += 21; } dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; fprintf( OF, " F " ); } //-------------------------------------------------------------------------- // ps_getdate() // // Get the date and time //-------------------------------------------------------------------------- static char * ps_getdate( void ) { int len; time_t t; char *p; t = time( (time_t *) 0 ); p = ctime( &t ); len = (int) strlen( p ); *( p + len - 1 ) = '\0'; // zap the newline character return p; } // 0.8 should mimic the offset of first superscript/subscript level // implemented in plstr (plsym.c) for Hershey fonts. However, when // comparing with -dev xwin and -dev xcairo results changing this // factor to 0.6 appears to offset the centers of the letters // appropriately while 0.8 gives much poorer agreement with the // other devices. # define RISE_FACTOR 0.6 //-------------------------------------------------------------------------- // proc_str() // // Prints postscript strings. // N.B. Now unicode only, no string access! // //-------------------------------------------------------------------------- void proc_str( PLStream *pls, EscText *args ) { PLFLT *t = args->xform, tt[4]; // Transform matrices PLFLT theta, shear, stride; // Rotation angle and shear from the matrix PLFLT ft_ht, offset; // Font height and offset PLFLT cs, sn, l1, l2; PSDev *dev = (PSDev *) pls->dev; const char *font; char esc; // Be generous. Used to store lots of font changes which take // 3 characters per change. #define PROC_STR_STRING_LENGTH 1000 unsigned char *strp, str[PROC_STR_STRING_LENGTH], *cur_strp, cur_str[PROC_STR_STRING_LENGTH]; float font_factor = 1.4f; PLINT clxmin, clxmax, clymin, clymax; // Clip limits PLINT clipx[4], clipy[4]; // Current clip limits PLFLT scale = 1., up = 0.; // Font scaling and shifting parameters int i = 0; // String index // unicode only! so test for it. if ( args->unicode_array_len > 0 ) { int j, s, f; const char *fonts[PROC_STR_STRING_LENGTH]; const PLUNICODE *cur_text; PLUNICODE fci, fci_save; PLFLT old_sscale, sscale, old_soffset, soffset, ddup; PLINT level = 0; // translate from unicode into type 1 font index. // // Choose the font family, style, variant, and weight using // the FCI (font characterization integer). // plgesc( &esc ); plgfci( &fci ); fci_save = fci; font = get_font( dev, fci ); cur_text = args->unicode_array; for ( f = s = j = 0; j < args->unicode_array_len; j++ ) { if ( cur_text[j] & PL_FCI_MARK ) { // process an FCI by saving it and escaping cur_str // with an escff to make it a 2-character escape // that is not used in legacy Hershey code // if ( ( f < PROC_STR_STRING_LENGTH ) && ( s + 3 < PROC_STR_STRING_LENGTH ) ) { fci_save = cur_text[j]; fonts[f++] = get_font( dev, fci_save ); cur_str[s++] = (unsigned char) esc; cur_str[s++] = 'f'; cur_str[s++] = 'f'; } } else if ( s + 4 < PROC_STR_STRING_LENGTH ) { #undef PL_TEST_TYPE1 #ifdef PL_TEST_TYPE1 // Use this test case only to conveniently view Type1 font // possibilities (as in test_type1.py example). // This functionality is useless other than for this test case. PLINT ifamily, istyle, iweight; plgfont( &ifamily, &istyle, &iweight ); if ( 0 <= cur_text[j] && cur_text[j] < 256 ) cur_str[s++] = cur_text[j]; else cur_str[s++] = 32; // Overwrite font just for this special case. if ( ifamily == PL_FCI_SYMBOL ) font = get_font( dev, 0 ); else font = get_font( dev, fci ); #else cur_str[s] = plunicode2type1( cur_text[j], dev->lookup, dev->nlookup ); if ( cur_text[j] != ' ' && cur_str[s] == ' ' ) { // failed lookup. if ( !dev->if_symbol_font ) { // failed standard font lookup. Use symbol // font instead which will return a blank if // that fails as well. fonts[f++] = get_font( dev, 0 ); cur_str[s++] = (unsigned char) esc; cur_str[s++] = 'f'; cur_str[s++] = 'f'; cur_str[s++] = plunicode2type1( cur_text[j], dev->lookup, dev->nlookup ); } else { // failed symbol font lookup. Use last standard // font instead which will return a blank if // that fails as well. fonts[f++] = get_font( dev, fci_save ); cur_str[s++] = (unsigned char) esc; cur_str[s++] = 'f'; cur_str[s++] = 'f'; cur_str[s++] = plunicode2type1( cur_text[j], dev->lookup, dev->nlookup ); } } else { // lookup succeeded. s++; } #endif pldebug( "proc_str", "unicode = 0x%x, type 1 code = %d\n", cur_text[j], cur_str[s - 1] ); } } cur_str[s] = '\0'; // finish previous polyline dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; // Determine the font height ft_ht = pls->chrht * 72.0 / 25.4; // ft_ht in points, ht is in mm // The transform matrix has only rotations and shears; extract them plRotationShear( t, &theta, &shear, &stride ); cs = cos( theta ); sn = sin( theta ); tt[0] = t[0] * cs + t[2] * sn; tt[1] = t[1] * cs + t[3] * sn; tt[2] = -t[0] * sn + t[2] * cs; tt[3] = -t[1] * sn + t[3] * cs; // // Reference point conventions: // If base = 0, it is aligned with the center of the text box // If base = 1, it is aligned with the baseline of the text box // If base = 2, it is aligned with the top of the text box // // Currently plplot only uses base=0 // Postscript uses base=1 // // We must calculate the difference between the two and apply the offset. // if ( args->base == 2 ) // not supported by plplot offset = ENLARGE * ft_ht / 2.; // half font height else if ( args->base == 1 ) offset = 0.; else offset = -ENLARGE * ft_ht / 2.; // Determine the adjustment for page orientation theta -= PI / 2. * pls->diorot; args->y += (PLINT) ( offset * cos( theta ) ); args->x -= (PLINT) ( offset * sin( theta ) ); // ps driver is rotated by default plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &( args->x ), &( args->y ) ); // Correct for the fact ps driver uses landscape by default theta += PI / 2.; // Output // Set clipping clipx[0] = pls->clpxmi; clipx[2] = pls->clpxma; clipy[0] = pls->clpymi; clipy[2] = pls->clpyma; clipx[1] = clipx[2]; clipy[1] = clipy[0]; clipx[3] = clipx[0]; clipy[3] = clipy[2]; difilt( clipx, clipy, 4, &clxmin, &clxmax, &clymin, &clymax ); plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &clipx[0], &clipy[0] ); plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &clipx[1], &clipy[1] ); plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &clipx[2], &clipy[2] ); plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &clipx[3], &clipy[3] ); fprintf( OF, " gsave %d %d %d %d %d %d %d %d CL\n", clipx[0], clipy[0], clipx[1], clipy[1], clipx[2], clipy[2], clipx[3], clipy[3] ); // move to string reference point fprintf( OF, " %d %d M\n", args->x, args->y ); // Save the current position and set the string rotation fprintf( OF, "gsave %.3f R\n", TRMFLT( theta * 180. / PI ) ); // Purge escape sequences from string, so that postscript can find it's // length. The string length is computed with the current font, and can // thus be wrong if there are font change escape sequences in the string // esc_purge( str, cur_str ); fprintf( OF, "/%s %.3f SF\n", font, TRMFLT( font_factor * ENLARGE * ft_ht ) ); // Output string, while escaping the '(', ')' and '\' characters. // this string is output for measurement purposes only. // fprintf( OF, "%.3f (", TRMFLT( -args->just ) ); while ( str[i] != '\0' ) { if ( str[i] == '(' || str[i] == ')' || str[i] == '\\' ) fprintf( OF, "\\%c", str[i] ); else fprintf( OF, "%c", str[i] ); i++; } fprintf( OF, ") SW\n" ); // Parse string for PLplot escape sequences and print everything out cur_strp = cur_str; f = 0; do { strp = str; if ( *cur_strp == esc ) { cur_strp++; if ( *cur_strp == esc ) // { *strp++ = *cur_strp++; } else if ( *cur_strp == 'f' ) { cur_strp++; if ( *cur_strp++ != 'f' ) { // escff occurs because of logic above. But any suffix // other than "f" should never happen. plabort( "proc_str, internal PLplot logic error;" "wrong escf escape sequence" ); return; } font = fonts[f++]; pldebug( "proc_str", "string-specified fci = 0x%x, font name = %s\n", fci, font ); continue; } else switch ( *cur_strp++ ) { case 'd': //subscript case 'D': plP_script_scale( FALSE, &level, &old_sscale, &sscale, &old_soffset, &soffset ); scale = sscale; // The correction for the difference in magnitude // between the baseline and middle coordinate systems // for subscripts should be // -0.5*(base font size - superscript/subscript font size). ddup = -0.5 * ( 1.0 - sscale ); up = -font_factor * ENLARGE * ft_ht * ( RISE_FACTOR * soffset + ddup ); break; case 'u': //superscript case 'U': plP_script_scale( TRUE, &level, &old_sscale, &sscale, &old_soffset, &soffset ); scale = sscale; // The correction for the difference in magnitude // between the baseline and middle coordinate systems // for superscripts should be // 0.5*(base font size - superscript/subscript font size). ddup = 0.5 * ( 1.0 - sscale ); up = font_factor * ENLARGE * ft_ht * ( RISE_FACTOR * soffset + ddup ); break; // ignore the next sequences case '+': case '-': case 'b': case 'B': plwarn( "'+', '-', and 'b/B' text escape sequences not processed." ); break; } } // copy from current to next token, adding a postscript escape // char '\' if necessary // while ( *cur_strp && *cur_strp != esc ) { if ( *cur_strp == '(' || *cur_strp == ')' || *cur_strp == '\\' ) *strp++ = '\\'; *strp++ = *cur_strp++; } *strp = '\0'; if ( fabs( up ) < 0.001 ) up = 0.; // Watch out for small differences // Apply the scaling and the shear fprintf( OF, "/%s [%.3f %.3f %.3f %.3f 0 0] SF\n", font, TRMFLT( tt[0] * font_factor * ENLARGE * ft_ht * scale ), TRMFLT( tt[2] * font_factor * ENLARGE * ft_ht * scale ), TRMFLT( tt[1] * font_factor * ENLARGE * ft_ht * scale ), TRMFLT( tt[3] * font_factor * ENLARGE * ft_ht * scale ) ); // if up/down escape sequences, save current point and adjust baseline; // take the shear into account if ( up != 0. ) fprintf( OF, "gsave %.3f %.3f rmoveto\n", TRMFLT( up * tt[1] ), TRMFLT( up * tt[3] ) ); // print the string fprintf( OF, "(%s) show\n", str ); // back to baseline if ( up != 0. ) fprintf( OF, "grestore (%s) stringwidth rmoveto\n", str ); } while ( *cur_strp ); fprintf( OF, "grestore\n" ); fprintf( OF, "grestore\n" ); // // keep driver happy -- needed for background and orientation. // arghhh! can't calculate it, as I only have the string reference // point, not its extent! // Still a hack - but at least it takes into account the string // length and justification. Character width is assumed to be // 0.6 * character height. Add on an extra 1.5 * character height // for safety. // cs = cos( theta ); sn = sin( theta ); l1 = -i * args->just; l2 = i * ( 1. - args->just ); // Factor of 0.6 is an empirical fudge to convert character // height to average character width l1 *= 0.6; l2 *= 0.6; dev->llx = (int) ( MIN( dev->llx, args->x + ( MIN( l1 * cs, l2 * cs ) - 1.5 ) * font_factor * ft_ht * ENLARGE ) ); dev->lly = (int) ( MIN( dev->lly, args->y + ( MIN( l1 * sn, l2 * sn ) - 1.5 ) * font_factor * ft_ht * ENLARGE ) ); dev->urx = (int) ( MAX( dev->urx, args->x + ( MAX( l1 * cs, l2 * cs ) + 1.5 ) * font_factor * ft_ht * ENLARGE ) ); dev->ury = (int) ( MAX( dev->ury, args->y + ( MAX( l1 * sn, l2 * sn ) + 1.5 ) * font_factor * ft_ht * ENLARGE ) ); } } static void esc_purge( unsigned char *dstr, unsigned char *sstr ) { char esc; plgesc( &esc ); while ( *sstr ) { if ( *sstr != esc ) { *dstr++ = *sstr++; continue; } sstr++; if ( *sstr == esc ) { *dstr++ = *sstr++; continue; } else { switch ( *sstr++ ) { case 'f': sstr++; break; // two chars sequence default: break; // single char escape } } } *dstr = '\0'; } //-------------------------------------------------------------------------- // unsigned char plunicode2type1 (const PLUNICODE index, // const Unicode_to_Type1_table lookup[], const int number_of_entries) // // Function takes an input unicode index, looks through the lookup // table (which must be sorted by PLUNICODE Unicode), then returns the // corresponding Type1 code in the lookup table. If the Unicode index is // not present the returned value is 32 (which is normally a blank // for Type 1 fonts). //-------------------------------------------------------------------------- static unsigned char plunicode2type1( const PLUNICODE index, const Unicode_to_Type1_table lookup[], const int nlookup ) { int jlo = -1, jmid, jhi = nlookup; while ( jhi - jlo > 1 ) { // Note that although jlo or jhi can be just outside valid // range (see initialization above) because of while condition // jlo < jmid < jhi and jmid must be in valid range. // jmid = ( jlo + jhi ) / 2; if ( index > lookup[jmid].Unicode ) jlo = jmid; else if ( index < lookup[jmid].Unicode ) jhi = jmid; else // We have found it! // index == lookup[jmid].Unicode // return ( lookup[jmid].Type1 ); } // jlo is invalid or it is valid and index > lookup[jlo].Unicode. // jhi is invalid or it is valid and index < lookup[jhi].Unicode. // All these conditions together imply index cannot be found in lookup. // Mark with ' ' (which is normally the index for blank in type 1 fonts). // return ( ' ' ); } //-------------------------------------------------------------------------- // get_font( PSDev* dev, PLUNICODE fci ) // // Sets the Type1 font. //-------------------------------------------------------------------------- static const char * get_font( PSDev* dev, PLUNICODE fci ) { const char *font; // fci = 0 is a special value indicating the Type 1 Symbol font // is desired. This value cannot be confused with a normal FCI value // because it doesn't have the PL_FCI_MARK. if ( fci == 0 ) { font = "Symbol"; dev->nlookup = number_of_entries_in_unicode_to_symbol_table; dev->lookup = unicode_to_symbol_lookup_table; dev->if_symbol_font = 1; } else { // convert the fci to Base14/Type1 font information font = plP_FCI2FontName( fci, Type1Lookup, N_Type1Lookup ); dev->nlookup = number_of_entries_in_unicode_to_standard_table; dev->lookup = unicode_to_standard_lookup_table; dev->if_symbol_font = 0; } pldebug( "set_font", "fci = 0x%x, font name = %s\n", fci, font ); return ( font ); } #else int pldummy_ps() { return 0; } #endif // PLD_ps plplot-5.13.0/drivers/wxwidgets.h000644 001752 001752 00000022741 13150160115 020517 0ustar00softwaresoftware000000 000000 // Copyright (C) 2008 Werner Smekal // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #ifndef __WXWIDGETS_H__ #define __WXWIDGETS_H__ #include #include // plplot headers #include "plplotP.h" #include "wxwidgets_comms.h" // some special wxWidgets headers #include #include #include class wxPLplotFrame; // A font class which encapsulates the PLplot font metrics and // a wxFont object. Importantly however the creation of the // wxFont is delayed until it is actually requested. This is // useful because on Linux in wxWidgets 3.0 creation of a wxFont // in a console mode application caused a crash. class Font { public: Font(); Font( PLUNICODE fci, PLFLT size, bool underlined, bool createFontOnConstruction = false ); wxFont getWxFont(); PLUNICODE getFci() const { return m_fci; } PLFLT getSize() const { return m_size; } bool getUnderlined() const { return m_underlined; } private: void createFont(); wxFont m_font; PLUNICODE m_fci; PLFLT m_size; bool m_underlined; bool m_hasFont; }; //check equivalence of two fonts. Note that a font created //with the default constructor always compares false to any //other font and that whether the wxFont has been created is //not included in the test. bool operator ==( const Font &lhs, const Font &rhs ); class FontGrabber { public: FontGrabber(); Font GetFont( PLUNICODE fci, PLFLT scaledFontSize, bool underlined ); bool lastWasCached( ){ return m_lastWasCached; } private: Font m_prevFont; bool m_lastWasCached; }; class PlDevice { public: PlDevice(); virtual ~PlDevice() {} virtual void DrawLine( short x1a, short y1a, short x2a, short y2a ) {} virtual void DrawPolyline( short *xa, short *ya, PLINT npts ){} virtual void ClearBackground( PLStream* pls, PLINT x1 = -1, PLINT y1 = -1, PLINT x2 = -1, PLINT y2 = -1 ){} virtual void FillPolygon( PLStream *pls ){} virtual void SetWidth( PLStream *pls ){} virtual void SetColor( PLStream *pls ){} virtual void SetDC( PLStream *pls, wxDC* dc ){} virtual void EndPage( PLStream* pls ){} virtual void BeginPage( PLStream* pls ){} virtual void SetSize( PLStream* pls, int width, int height ){} virtual void FixAspectRatio( bool fix ){} virtual void Locate( PLStream* pls, PLGraphicsIn *graphicsIn ){} virtual void Flush( PLStream* pls ){} virtual void PreDestructorTidy( PLStream *pls ){} void drawText( PLStream* pls, EscText* args ); private: void DrawTextLine( PLUNICODE* ucs4, int ucs4Len, wxCoord xOrigin, wxCoord yOrigin, wxCoord x, wxCoord y, PLFLT *transform, PLFLT baseFontSize, bool drawText, bool &underlined, PLUNICODE &fci, unsigned char red, unsigned char green, unsigned char blue, PLFLT alpha, wxCoord &textWidth, wxCoord &textHeight, wxCoord &textDepth ); virtual void DrawTextSection( wxString section, wxCoord xOrigin, wxCoord yOrigin, wxCoord x, wxCoord y, PLFLT *transform, PLFLT scaledFontSize, bool drawText, bool underlined, PLUNICODE fci, unsigned char red, unsigned char green, unsigned char blue, PLFLT alpha, PLFLT &yScale, wxCoord §ionWidth, wxCoord §ionHeight, wxCoord §ionDepth ) {} PLUNICODE m_prevSymbol; PLFLT m_prevBaseFontSize; PLINT m_prevLevel; PLUNICODE m_prevFci; wxCoord m_prevSymbolWidth; wxCoord m_prevSymbolHeight; wxCoord m_prevSymbolDepth; }; // base device class class wxPLDevice : public PlDevice { public: wxPLDevice( PLStream *pls, char * mfo, PLINT text, PLINT hrshsym ); virtual ~wxPLDevice( void ); void DrawLine( short x1a, short y1a, short x2a, short y2a ); void DrawPolyline( short *xa, short *ya, PLINT npts ); void ClearBackground( PLStream* pls, PLINT x1 = -1, PLINT y1 = -1, PLINT x2 = -1, PLINT y2 = -1 ); void FillPolygon( PLStream *pls ); void SetWidth( PLStream *pls ); void SetColor( PLStream *pls ); void SetDC( PLStream *pls, wxDC* dc ); void EndPage( PLStream* pls ); void BeginPage( PLStream* pls ); void SetSize( PLStream* pls, int width, int height ); void FixAspectRatio( bool fix ); void Locate( PLStream* pls, PLGraphicsIn *graphicsIn ); void Flush( PLStream* pls ); void PreDestructorTidy( PLStream *pls ); private: void DrawTextSection( wxString section, wxCoord xOrigin, wxCoord yOrigin, wxCoord x, wxCoord y, PLFLT *transform, PLFLT scaledFontSize, bool drawText, bool underlined, PLUNICODE fci, unsigned char red, unsigned char green, unsigned char blue, PLFLT alpha, PLFLT &yScale, wxCoord §ionWidth, wxCoord §ionHeight, wxCoord §ionDepth ); void TransmitBuffer( PLStream* pls, unsigned char transmissionType ); void SetupMemoryMap(); wxRegion GetClipRegion(); //The DC we will draw on if given by the user wxDC *m_dc; bool m_useDcTextTransform; //for the gcdc case we may need to store the graphics context for use // with text transformations wxGraphicsContext *m_gc; wxPen m_pen; wxBrush m_brush; //A device context specifically for checking the size of text for use with //the interactive viewer. wxImage m_interactiveTextImage; wxGCDC *m_interactiveTextGcdc; //Size and Scale //As far as plplot is concerned the size of the window is SHRT_MAX by //SHRT_MAX which gives us the best resolution. const PLFLT m_plplotEdgeLength; PLFLT m_width; //native width PLFLT m_height; //native height PLFLT m_xScale; //conversion from native width to plplotEdgeLength PLFLT m_yScale; //conversion from native height to plplotEdgeLength PLFLT m_xAspect; //values which when multiplied by m_plplotEdgeLength give an aspect PLFLT m_yAspect; //ratio equal to the native aspect ratio, the biggest of which is 1.0 PLFLT m_scale; //MAX(m_scalex, m_scaley) bool m_fixedAspect; // font variables static const int m_max_string_length = 500; //bool m_underlined; FontGrabber m_fontGrabber; //wxCoord m_textWidth, m_textHeight, m_textDescent, m_textLeading; //PLUNICODE m_fci; //Text positioning related variables //wxCoord m_superscriptHeight; //distance between superscript top and baseline //wxCoord m_subscriptDepth; //distance between subscript base and baseline PLFLT m_lineSpacing; //PLFLT m_yOffset; //PLINT m_posX; //PLINT m_posY; //PLFLT m_rotation; //variables for dealing with sending/receiving commands //via a memory map char m_mfo[PLPLOT_MAX_PATH]; #ifdef PL_WXWIDGETS_IPC3 // Private variable to hold all components of a MemoryMapHeader struct for a wxPLDevice instance. MemoryMapHeader m_header; #else PLNamedMutex m_mutex; #endif size_t m_localBufferPosition; PLMemoryMap m_outputMemoryMap; }; struct dev_entry { wxString dev_name; wxString dev_menu_short; wxString dev_menu_long; wxString dev_file_app; bool pixelDevice; }; //-------------------------------------------------------------------------- // Declarations for the device. //-------------------------------------------------------------------------- void plD_init_wxwidgets( PLStream * ); void plD_init_wxpng( PLStream * ); void plD_line_wxwidgets( PLStream *, short, short, short, short ); void plD_polyline_wxwidgets( PLStream *, short *, short *, PLINT ); void plD_eop_wxwidgets( PLStream * ); void plD_wait_wxwidgets( PLStream * ); void plD_bop_wxwidgets( PLStream * ); void plD_tidy_wxwidgets( PLStream * ); void plD_state_wxwidgets( PLStream *, PLINT ); void plD_esc_wxwidgets( PLStream *, PLINT, void * ); void wx_set_dc( PLStream* pls, wxDC* dc ); void wx_set_buffer( PLStream* pls, wxImage* buffer ); //-------------------------------------------------------------------------- // Debug functions //-------------------------------------------------------------------------- // define if you want debug output // #define _DEBUG // // #define _DEBUG_VERBOSE // void Log_Verbose( const char *fmt, ... ); void Log_Debug( const char *fmt, ... ); //-------------------------------------------------------------------------- // Font style and weight lookup tables //-------------------------------------------------------------------------- const wxFontFamily fontFamilyLookup[5] = { wxFONTFAMILY_SWISS, // sans-serif wxFONTFAMILY_ROMAN, // serif wxFONTFAMILY_TELETYPE, // monospace wxFONTFAMILY_SCRIPT, // script wxFONTFAMILY_SWISS // symbol }; const int fontStyleLookup[3] = { wxFONTSTYLE_NORMAL, // upright wxFONTSTYLE_ITALIC, // italic wxFONTSTYLE_SLANT // oblique }; const int fontWeightLookup[2] = { wxFONTWEIGHT_NORMAL, // medium wxFONTWEIGHT_BOLD // bold }; #endif // __WXWIDGETS_H__ plplot-5.13.0/drivers/CMakeLists.txt000644 001752 001752 00000022257 13150160115 021063 0ustar00softwaresoftware000000 000000 # drivers/CMakeLists.txt for PLplot ### ### Process this file with cmake to produce Makefile ### # Copyright (C) 2006-2016 Alan W. Irwin # # This file is part of PLplot. # # PLplot is free software; you can redistribute it and/or modify # it under the terms of the GNU Library General Public License as published # by the Free Software Foundation; version 2 of the License. # # PLplot is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Library General Public License for more details. # # You should have received a copy of the GNU Library General Public License # along with PLplot; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA install( FILES README.drivers README.wxwidgets DESTINATION ${DOC_DIR} ) if(ENABLE_DYNDRIVERS) include_directories( ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/lib/qsastime ${CMAKE_SOURCE_DIR}/lib/nistcd ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/include ) option(TEST_DYNDRIVERS "Test dynamic drivers" ON) if(TEST_DYNDRIVERS AND NOT CMAKE_CROSSCOMPILING) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test_dyndrivers_dir) set_source_files_properties( test-drv-info.c PROPERTIES COMPILE_FLAGS "-I${LTDL_INCLUDE_DIR}" ) add_executable(test-drv-info test-drv-info.c) if(BUILD_SHARED_LIBS) set_target_properties(test-drv-info PROPERTIES COMPILE_DEFINITIONS "USINGDLL" ) endif(BUILD_SHARED_LIBS) target_link_libraries(test-drv-info plplot ${LTDL_LIBRARIES} ) # in windows we move test-drv-info to the dll directory # otherwise we use the RPATH functionality if(USE_DLL_SUBDIRECTORY) set_target_properties( test-drv-info PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/dll ) else(USE_DLL_SUBDIRECTORY) set(test-drv-info_RPATH ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_BINARY_DIR}/src ${CMAKE_BINARY_DIR}/lib/csa ${CMAKE_BINARY_DIR}/lib/nn ${CMAKE_BINARY_DIR}/lib/qsastime ${libplplot_RPATH} ) set_target_properties( test-drv-info PROPERTIES INSTALL_RPATH "${test-drv-info_RPATH}" BUILD_WITH_INSTALL_RPATH ON ) endif(USE_DLL_SUBDIRECTORY) set(test_dyndrivers_TDEPENDS test-drv-info) endif(TEST_DYNDRIVERS AND NOT CMAKE_CROSSCOMPILING) foreach(SOURCE_ROOT_NAME ${DRIVERS_LIST}) #message("${SOURCE_ROOT_NAME}_SOURCE = ${${SOURCE_ROOT_NAME}_SOURCE}") if(${SOURCE_ROOT_NAME}_COMPILE_FLAGS) set_source_files_properties( ${${SOURCE_ROOT_NAME}_SOURCE} PROPERTIES COMPILE_FLAGS "${${SOURCE_ROOT_NAME}_COMPILE_FLAGS}" ) #message("${SOURCE_ROOT_NAME}_COMPILE_FLAGS = ${${SOURCE_ROOT_NAME}_COMPILE_FLAGS}") endif(${SOURCE_ROOT_NAME}_COMPILE_FLAGS) # ${SOURCE_ROOT_NAME}_LINK_FLAGS is ideally a list of the full path names # to libraries determined with find_library. However, the list can also # include link flags such as the -L and -l form of specifying libraries, # but that way of doing things only works on Unix, and even for Unix, # cmake does not correctly set the rpath for the build tree results # for non-standard locations with the -L and -l forms. #message("${SOURCE_ROOT_NAME}_LINK_FLAGS = ${${SOURCE_ROOT_NAME}_LINK_FLAGS}") # ${SOURCE_ROOT_NAME}_TARGETS is a list of PLplot CMake targets that the # device driver depends on. #message("${SOURCE_ROOT_NAME}_TARGETS = ${${SOURCE_ROOT_NAME}_TARGETS}") if(SOURCE_ROOT_NAME STREQUAL "qt") if(ANY_QT_DEVICE) add_library(${SOURCE_ROOT_NAME} MODULE ${${SOURCE_ROOT_NAME}_SOURCE}) if(PLPLOT_USE_QT5) # According to advice from Steve Kelly on the Cmake list, the Qt5 # Gui component is a dependency of the Qt5 Svg component so is not # needed here, but I will leave it in since it is informative. target_link_libraries( ${SOURCE_ROOT_NAME} plplot ${MATH_LIB} ${${SOURCE_ROOT_NAME}_TARGETS} Qt5::Svg Qt5::Gui Qt5::PrintSupport ) else(PLPLOT_USE_QT5) if(NOT QT_LIBRARIES) message(FATAL_ERROR "Internal build system inconsistency. QT_LIBRARIESis empty but it should be populated") endif(NOT QT_LIBRARIES) target_link_libraries( ${SOURCE_ROOT_NAME} plplot ${MATH_LIB} ${QT_LIBRARIES} ${${SOURCE_ROOT_NAME}_TARGETS} ) # Update the target COMPILE_DEFINITIONS and INCLUDE_DIRECTORIES set_qt4_target_properties(${SOURCE_ROOT_NAME}) endif(PLPLOT_USE_QT5) else(ANY_QT_DEVICE) message(FATAL_ERROR "Internal build system inconsistency. Attempt to build dynamic qt device when ANY_QT_DEVICE is false.") endif(ANY_QT_DEVICE) elseif(SOURCE_ROOT_NAME STREQUAL "wxwidgets") add_library(${SOURCE_ROOT_NAME} MODULE ${${SOURCE_ROOT_NAME}_SOURCE}) target_link_libraries( ${SOURCE_ROOT_NAME} plplot ${MATH_LIB} ${RT_LIB} ${${SOURCE_ROOT_NAME}_LINK_FLAGS} ${${SOURCE_ROOT_NAME}_TARGETS} ) if(PLD_wxwidgets OR PLD_wxpng) set_property(TARGET ${SOURCE_ROOT_NAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${CMAKE_BINARY_DIR}/bindings/wxwidgets ) endif(PLD_wxwidgets OR PLD_wxpng) else(SOURCE_ROOT_NAME STREQUAL "qt") add_library(${SOURCE_ROOT_NAME} MODULE ${${SOURCE_ROOT_NAME}_SOURCE}) target_link_libraries( ${SOURCE_ROOT_NAME} plplot ${MATH_LIB} ${${SOURCE_ROOT_NAME}_LINK_FLAGS} ${${SOURCE_ROOT_NAME}_TARGETS} ) endif(SOURCE_ROOT_NAME STREQUAL "qt") # ${SOURCE_ROOT_NAME}_RPATH originally set in cmake/modules files for # each driver in ${DRIVERS_LIST}. This is only used for the # install-tree rpath since cmake handles the build-tree rpath # automatically (so long as full pathnames to libraries are used). # Order is important here because of /usr/lib concerns. set( ${SOURCE_ROOT_NAME}_RPATH ${LIB_DIR} ${${SOURCE_ROOT_NAME}_RPATH} ${libplplot_RPATH} ) # Our implementation of dynamic loading using libltdl assumes # the prefix should always be nothing (rather than lib) and the suffix # should be set in a platform-dependent manner in order for libltdl # to find the dynamic device. if(WIN32_OR_CYGWIN) # strace showed that .dll was a must for libltdl on Cygwin. set(DYNAMIC_SUFFIX ".dll") else(WIN32_OR_CYGWIN) # So far this works on all non-Cygwin systems, but only Linux and # Mac OS X have been tested so far. set(DYNAMIC_SUFFIX ".so") endif(WIN32_OR_CYGWIN) #message("${SOURCE_ROOT_NAME}_RPATH = ${${SOURCE_ROOT_NAME}_RPATH}") if(USE_RPATH) set_target_properties( ${SOURCE_ROOT_NAME} PROPERTIES PREFIX "" SUFFIX ${DYNAMIC_SUFFIX} INSTALL_RPATH "${${SOURCE_ROOT_NAME}_RPATH}" ) else(USE_RPATH) set_target_properties( ${SOURCE_ROOT_NAME} PROPERTIES PREFIX "" SUFFIX ${DYNAMIC_SUFFIX} ) endif(USE_RPATH) if(BUILD_SHARED_LIBS) set_target_properties(${SOURCE_ROOT_NAME} PROPERTIES COMPILE_DEFINITIONS "USINGDLL" ) endif(BUILD_SHARED_LIBS) if(TEST_DYNDRIVERS AND NOT CMAKE_CROSSCOMPILING) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/test_dyndrivers_dir/${SOURCE_ROOT_NAME}.driver_info COMMAND test-drv-info ${SOURCE_ROOT_NAME} > ${CMAKE_CURRENT_BINARY_DIR}/test_dyndrivers_dir/${SOURCE_ROOT_NAME}.driver_info COMMAND ${CMAKE_COMMAND} -E compare_files ${CMAKE_CURRENT_BINARY_DIR}/test_dyndrivers_dir/${SOURCE_ROOT_NAME}.driver_info ${CMAKE_CURRENT_BINARY_DIR}/${SOURCE_ROOT_NAME}.driver_info DEPENDS ${SOURCE_ROOT_NAME} test-drv-info ) add_custom_target(test_${SOURCE_ROOT_NAME}_dyndriver DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/test_dyndrivers_dir/${SOURCE_ROOT_NAME}.driver_info ) set_property(GLOBAL PROPERTY FILE_DEPENDS_${SOURCE_ROOT_NAME}_dyndriver ${CMAKE_CURRENT_BINARY_DIR}/test_dyndrivers_dir/${SOURCE_ROOT_NAME}.driver_info ) add_dependencies(test_${SOURCE_ROOT_NAME}_dyndriver test-drv-info ${SOURCE_ROOT_NAME} ) list(APPEND test_dyndrivers_TDEPENDS test_${SOURCE_ROOT_NAME}_dyndriver) endif(TEST_DYNDRIVERS AND NOT CMAKE_CROSSCOMPILING) install(TARGETS ${SOURCE_ROOT_NAME} EXPORT export_plplot DESTINATION ${DRV_DIR} ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${SOURCE_ROOT_NAME}.driver_info DESTINATION ${DRV_DIR} ) endforeach(SOURCE_ROOT_NAME ${DRIVERS_LIST}) # The tk device driver depends internally on the xwin device driver. # Therefore make target tk depend on target xwin so # xwin will always be built first. if(TARGET tk) if(TARGET xwin) add_dependencies(tk xwin) else(TARGET xwin) message(FATAL_ERROR "Internal build system inconsistency where the tk target but not the xwin target are configured.") endif(TARGET xwin) endif(TARGET tk) if(PLD_tkwin AND USE_TCL_TK_STUBS) set_target_properties( tkwin PROPERTIES COMPILE_DEFINITIONS "USE_TCL_STUBS;USE_TK_STUBS" ) endif(PLD_tkwin AND USE_TCL_TK_STUBS) if(TEST_DYNDRIVERS AND NOT CMAKE_CROSSCOMPILING) add_custom_target(test_dyndrivers ALL ) add_dependencies(test_dyndrivers ${test_dyndrivers_TDEPENDS}) endif(TEST_DYNDRIVERS AND NOT CMAKE_CROSSCOMPILING) endif(ENABLE_DYNDRIVERS) plplot-5.13.0/drivers/svg.c000644 001752 001752 00000123114 13150160115 017260 0ustar00softwaresoftware000000 000000 // November 8, 2006 // // PLplot driver for SVG 1.1 (http://www.w3.org/Graphics/SVG/) // // Copyright (C) 2006 Hazen Babcock // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // //--------------------------------------------- // Header files, defines and local variables // --------------------------------------------- #include #include // PLplot header files #include "plplotP.h" #include "drivers.h" // constants #define SVG_Default_X 720 #define SVG_Default_Y 540 #define POINTS_PER_INCH 72 #define MAX_STRING_LEN 1000 // This has been generated empirically by looking carefully at results from // examples 1 and 2. #define FONT_SIZE_RATIO 1.34 #define FONT_SHIFT_RATIO 0.705 #define FONT_SHIFT_OFFSET 0.5 // local variables PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_svg = "svg:Scalable Vector Graphics (SVG 1.1):1:svg:57:svg\n"; static int already_warned = 0; static int text_clipping = 1; static DrvOpt svg_options[] = { { "text_clipping", DRV_INT, &text_clipping, "Use text clipping (text_clipping=0|1)" } }; typedef struct { short textClipping; int which_clip; int canvasXSize; int canvasYSize; PLFLT scale; int svgIndent; FILE *svgFile; int gradient_index; // char curColor[7]; } SVG; // font stuff // Debugging extras //----------------------------------------------- // function declarations // ----------------------------------------------- // Functions for writing XML SVG tags to a file static void svg_open( SVG *, const char * ); static void svg_open_end( SVG * ); static void svg_attr_value( SVG *, const char *, const char * ); static void svg_attr_values( SVG *, const char *, const char *, ... ); static void svg_close( SVG *, const char * ); static void svg_general( SVG *, const char * ); static void svg_indent( SVG * ); static void svg_stroke_width( PLStream * ); static void svg_stroke_color( PLStream * ); static void svg_fill_color( PLStream * ); static void svg_fill_background_color( PLStream * ); static int svg_family_check( PLStream * ); // General static void poly_line( PLStream *, short *, short *, PLINT, short ); static void gradient( PLStream *, short *, short *, PLINT ); static void write_hex( FILE *, unsigned char ); static void write_unicode( FILE *, PLUNICODE ); static void specify_font( FILE *, PLUNICODE ); // String processing static void proc_str( PLStream *, EscText * ); // PLplot interface functions void plD_dispatch_init_svg( PLDispatchTable *pdt ); void plD_init_svg( PLStream * ); void plD_line_svg( PLStream *, short, short, short, short ); void plD_polyline_svg( PLStream *, short *, short *, PLINT ); void plD_eop_svg( PLStream * ); void plD_bop_svg( PLStream * ); void plD_tidy_svg( PLStream * ); void plD_state_svg( PLStream *, PLINT ); void plD_esc_svg( PLStream *, PLINT, void * ); //-------------------------------------------------------------------------- // dispatch_init_init() // // Initialize device dispatch table //-------------------------------------------------------------------------- void plD_dispatch_init_svg( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "Scalable Vector Graphics (SVG 1.1)"; pdt->pl_DevName = "svg"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 57; pdt->pl_init = (plD_init_fp) plD_init_svg; pdt->pl_line = (plD_line_fp) plD_line_svg; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_svg; pdt->pl_eop = (plD_eop_fp) plD_eop_svg; pdt->pl_bop = (plD_bop_fp) plD_bop_svg; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_svg; pdt->pl_state = (plD_state_fp) plD_state_svg; pdt->pl_esc = (plD_esc_fp) plD_esc_svg; } //-------------------------------------------------------------------------- // svg_init() // // Initialize device //-------------------------------------------------------------------------- void plD_init_svg( PLStream *pls ) { SVG *aStream; pls->termin = 0; // not an interactive device pls->color = 1; // supports color pls->width = 1; pls->verbose = 1; pls->bytecnt = 0; //pls->debug = 1; pls->dev_text = 1; // handles text pls->dev_unicode = 1; // wants text as unicode pls->page = 0; pls->dev_fill0 = 1; // driver generates solid fills pls->dev_fill1 = 0; // Use PLplot core fallback for pattern fills pls->dev_gradient = 1; // driver renders gradient pls->graphx = GRAPHICS_MODE; if ( !pls->colorset ) pls->color = 1; // Initialize family file info plFamInit( pls ); // Prompt for a file name if not already set plOpenFile( pls ); // Allocate and initialize device-specific data if ( pls->dev != NULL ) free( (void *) pls->dev ); pls->dev = calloc( 1, (size_t) sizeof ( SVG ) ); if ( pls->dev == NULL ) plexit( "plD_init_svg: Out of memory." ); aStream = (SVG *) pls->dev; // Set the bounds for plotting in points (unit of 1/72 of an inch). Default is SVG_Default_X points x SVG_Default_Y points unless otherwise specified by plspage or -geometry option. if ( pls->xlength <= 0 || pls->ylength <= 0 ) { aStream->canvasXSize = SVG_Default_X; aStream->canvasYSize = SVG_Default_Y; } else { aStream->canvasXSize = pls->xlength; aStream->canvasYSize = pls->ylength; } // Calculate ratio of (larger) internal PLplot coordinates to external // coordinates used for svg file. if ( aStream->canvasXSize > aStream->canvasYSize ) aStream->scale = (PLFLT) ( PIXELS_X - 1 ) / (PLFLT) aStream->canvasXSize; else aStream->scale = (PLFLT) PIXELS_Y / (PLFLT) aStream->canvasYSize; plP_setphy( (PLINT) 0, (PLINT) ( aStream->scale * aStream->canvasXSize ), (PLINT) 0, (PLINT) ( aStream->scale * aStream->canvasYSize ) ); // Scaled points. plP_setpxl( aStream->scale * POINTS_PER_INCH / 25.4, aStream->scale * POINTS_PER_INCH / 25.4 ); // Scaled points/mm. aStream->svgFile = pls->OutFile; // Handle the text clipping option plParseDrvOpts( svg_options ); // Turn on text clipping if the user desires this if ( text_clipping ) { aStream->textClipping = 1; } aStream->textClipping = (short) text_clipping; aStream->svgIndent = 0; aStream->gradient_index = 0; svg_general( aStream, "\n" ); svg_general( aStream, "\n" ); } //-------------------------------------------------------------------------- // svg_bop() // // Set up for the next page. //-------------------------------------------------------------------------- void plD_bop_svg( PLStream *pls ) { SVG *aStream; // Plot familying stuff. Not really understood, just copying gd.c plGetFam( pls ); // n.b. pls->dev can change because of an indirect call to plD_init_svg // from plGetFam if familying is enabled. Thus, wait to define aStream until // now. aStream = pls->dev; pls->famadv = 1; pls->page++; if ( svg_family_check( pls ) ) { return; } // write opening svg tag for the new page svg_open( aStream, "svg" ); svg_attr_value( aStream, "xmlns", "http://www.w3.org/2000/svg" ); svg_attr_value( aStream, "xmlns:xlink", "http://www.w3.org/1999/xlink" ); svg_attr_value( aStream, "version", "1.1" ); // svg_attr_values("width", "%dcm", (int)((double)canvasXSize/POINTS_PER_INCH * 2.54)); // svg_attr_values("height", "%dcm", (int)((double)canvasYSize/POINTS_PER_INCH * 2.54)); svg_attr_values( aStream, "width", "%dpt", aStream->canvasXSize ); svg_attr_values( aStream, "height", "%dpt", aStream->canvasYSize ); svg_attr_values( aStream, "viewBox", "%d %d %d %d", 0, 0, aStream->canvasXSize, aStream->canvasYSize ); svg_general( aStream, ">\n" ); // set the background by drawing a rectangle that is the size of // of the canvas and filling it with the background color. svg_open( aStream, "rect" ); svg_attr_values( aStream, "x", "%d", 0 ); svg_attr_values( aStream, "y", "%d", 0 ); svg_attr_values( aStream, "width", "%d", aStream->canvasXSize ); svg_attr_values( aStream, "height", "%d", aStream->canvasYSize ); svg_attr_value( aStream, "stroke", "none" ); svg_fill_background_color( pls ); svg_open_end( aStream ); // invert the coordinate system so that PLplot graphs appear right side up svg_open( aStream, "g" ); svg_attr_values( aStream, "transform", "matrix(1 0 0 -1 0 %d)", aStream->canvasYSize ); svg_general( aStream, ">\n" ); } //-------------------------------------------------------------------------- // svg_line() // // Draw a line in the current color from (x1,y1) to (x2,y2). //-------------------------------------------------------------------------- void plD_line_svg( PLStream *pls, short x1a, short y1a, short x2a, short y2a ) { SVG *aStream; aStream = pls->dev; if ( svg_family_check( pls ) ) { return; } svg_open( aStream, "polyline" ); svg_stroke_width( pls ); svg_stroke_color( pls ); svg_attr_value( aStream, "fill", "none" ); // svg_attr_value(aStream, "shape-rendering", "crispEdges"); svg_attr_values( aStream, "points", "%r,%r %r,%r", (double) x1a / aStream->scale, (double) y1a / aStream->scale, (double) x2a / aStream->scale, (double) y2a / aStream->scale ); svg_open_end( aStream ); } //-------------------------------------------------------------------------- // svg_polyline() // // Draw a polyline in the current color. //-------------------------------------------------------------------------- void plD_polyline_svg( PLStream *pls, short *xa, short *ya, PLINT npts ) { if ( svg_family_check( pls ) ) { return; } poly_line( pls, xa, ya, npts, 0 ); } //-------------------------------------------------------------------------- // svg_eop() // // End of page //-------------------------------------------------------------------------- void plD_eop_svg( PLStream *pls ) { SVG *aStream; aStream = pls->dev; if ( svg_family_check( pls ) ) { return; } // write the closing svg tag svg_close( aStream, "g" ); svg_close( aStream, "svg" ); } //-------------------------------------------------------------------------- // svg_tidy() // // Close graphics file or otherwise clean up. //-------------------------------------------------------------------------- void plD_tidy_svg( PLStream *pls ) { if ( svg_family_check( pls ) ) { return; } plCloseFile( pls ); } //-------------------------------------------------------------------------- // plD_state_svg() // // Handle change in PLStream state (color, pen width, fill attribute, etc). // // Nothing is done here because these attributes are aquired from // PLStream for each element that is drawn. //-------------------------------------------------------------------------- void plD_state_svg( PLStream *PL_UNUSED( pls ), PLINT PL_UNUSED( op ) ) { } //-------------------------------------------------------------------------- // svg_esc() // // Escape function. //-------------------------------------------------------------------------- void plD_esc_svg( PLStream *pls, PLINT op, void *ptr ) { if ( svg_family_check( pls ) ) { return; } switch ( op ) { case PLESC_FILL: // fill polygon poly_line( pls, pls->dev_x, pls->dev_y, pls->dev_npts, 1 ); break; case PLESC_GRADIENT: // render gradient inside polygon gradient( pls, pls->dev_x, pls->dev_y, pls->dev_npts ); break; case PLESC_HAS_TEXT: // render text proc_str( pls, (EscText *) ptr ); break; } } //-------------------------------------------------------------------------- // poly_line() // // Handles drawing filled and unfilled polygons //-------------------------------------------------------------------------- void poly_line( PLStream *pls, short *xa, short *ya, PLINT npts, short fill ) { int i; SVG *aStream; aStream = pls->dev; svg_open( aStream, "polyline" ); if ( fill ) { // Two adjacent regions will put non-zero width boundary strokes on top // of each other on their common boundary. Thus, a stroke on the boundary // of a filled region is generally a bad idea when the fill is partially // opaque because the partial opacity of the two boundary strokes which // are on top of each other will mutually interfere and produce a // bad-looking result. On the other hand, for completely opaque fills // a boundary stroke is a good idea since if it is of sufficient width // it will keep the background from leaking through at the anti-aliased // edges of filled regions that have a common boundary with other // filled regions. if ( pls->curcolor.a < 0.99 ) { svg_attr_value( aStream, "stroke", "none" ); } else { svg_stroke_width( pls ); svg_stroke_color( pls ); } svg_fill_color( pls ); if ( pls->dev_eofill ) svg_attr_value( aStream, "fill-rule", "evenodd" ); else svg_attr_value( aStream, "fill-rule", "nonzero" ); } else { svg_stroke_width( pls ); svg_stroke_color( pls ); svg_attr_value( aStream, "fill", "none" ); } //svg_attr_value(aStream, "shape-rendering", "crispEdges"); svg_indent( aStream ); fprintf( aStream->svgFile, "points=\"" ); for ( i = 0; i < npts; i++ ) { fprintf( aStream->svgFile, "%.2f,%.2f ", (double) xa[i] / aStream->scale, (double) ya[i] / aStream->scale ); if ( ( ( i + 1 ) % 10 ) == 0 ) { fprintf( aStream->svgFile, "\n" ); svg_indent( aStream ); } } fprintf( aStream->svgFile, "\"/>\n" ); aStream->svgIndent -= 2; } //-------------------------------------------------------------------------- // gradient() // // Draws gradient //-------------------------------------------------------------------------- void gradient( PLStream *pls, short *xa, short *ya, PLINT npts ) { int i; // 27 should be the maximum needed below, but be generous. char buffer[50]; SVG *aStream; aStream = pls->dev; svg_open( aStream, "g>" ); svg_open( aStream, "defs>" ); svg_open( aStream, "linearGradient" ); // Allows ~2^31 unique gradient id's, gradient_index incremented below. sprintf( buffer, "MyGradient%010d", aStream->gradient_index ); svg_attr_value( aStream, "id", buffer ); svg_attr_value( aStream, "gradientUnits", "userSpaceOnUse" ); sprintf( buffer, "%.2f", pls->xgradient[0] / aStream->scale ); svg_attr_value( aStream, "x1", buffer ); sprintf( buffer, "%.2f", pls->ygradient[0] / aStream->scale ); svg_attr_value( aStream, "y1", buffer ); sprintf( buffer, "%.2f", pls->xgradient[1] / aStream->scale ); svg_attr_value( aStream, "x2", buffer ); sprintf( buffer, "%.2f", pls->ygradient[1] / aStream->scale ); svg_attr_value( aStream, "y2", buffer ); svg_general( aStream, ">\n" ); for ( i = 0; i < pls->ncol1; i++ ) { svg_indent( aStream ); fprintf( aStream->svgFile, "ncol1 - 1 ) ); fprintf( aStream->svgFile, "stop-color=\"#" ); write_hex( aStream->svgFile, pls->cmap1[i].r ); write_hex( aStream->svgFile, pls->cmap1[i].g ); write_hex( aStream->svgFile, pls->cmap1[i].b ); fprintf( aStream->svgFile, "\" " ); fprintf( aStream->svgFile, "stop-opacity=\"%.3f\"/>\n", pls->cmap1[i].a ); } svg_close( aStream, "linearGradient" ); svg_close( aStream, "defs" ); svg_open( aStream, "polyline" ); sprintf( buffer, "url(#MyGradient%010d)", aStream->gradient_index++ ); svg_attr_value( aStream, "fill", buffer ); svg_indent( aStream ); fprintf( aStream->svgFile, "points=\"" ); for ( i = 0; i < npts; i++ ) { fprintf( aStream->svgFile, "%.2f,%.2f ", (double) xa[i] / aStream->scale, (double) ya[i] / aStream->scale ); if ( ( ( i + 1 ) % 10 ) == 0 ) { fprintf( aStream->svgFile, "\n" ); svg_indent( aStream ); } } fprintf( aStream->svgFile, "\"/>\n" ); aStream->svgIndent -= 2; svg_close( aStream, "g" ); } //-------------------------------------------------------------------------- // proc_str() // // Processes strings for display. // // NOTE: // // (1) This was tested on Firefox and Camino where it seemed to display // text properly. However, it isn't obvious to me that these browsers // conform to the specification. Basically the issue is that some of // the text properties (i.e. dy) that you specify inside a tspan element // remain in force until the end of the text element. It would seem to // me that they should only apply inside the tspan tag. To get around // this, and because it was easier anyway, I used what is essentially // a list of tspan tags rather than a tree of tspan tags. Perhaps // better described as a tree with one branch? // // (2) To deal with the some whitespace annoyances, the entire text // element must be written on a single line. If there are lots of // format characters then this line might end up being too long // for some SVG implementations. // // (3) Text placement is not ideal. Vertical offset seems to be // particularly troublesome. // // (4) See additional notes in specify_font re. to sans / serif // //-------------------------------------------------------------------------- void proc_str( PLStream *pls, EscText *args ) { char plplot_esc; short i; short totalTags = 1; short ucs4Len = (short) args->unicode_array_len; double ftHt, scaled_offset, scaled_ftHt; PLUNICODE fci; PLINT rcx[4], rcy[4]; static PLINT prev_rcx[4], prev_rcy[4]; PLFLT rotation, shear, stride, cos_rot, sin_rot, sin_shear, cos_shear; PLFLT t[4]; int glyph_size, sum_glyph_size; short if_write; // PLFLT *t = args->xform; PLUNICODE *ucs4 = args->unicode_array; SVG *aStream; PLFLT old_sscale, sscale, old_soffset, soffset, old_dup, ddup; PLINT level; PLINT same_clip; // check that we got unicode if ( ucs4Len == 0 ) { printf( "Non unicode string passed to SVG driver, ignoring\n" ); return; } // get plplot escape character and the current font plgesc( &plplot_esc ); plgfci( &fci ); // determine the font height in points. ftHt = FONT_SIZE_RATIO * pls->chrht * POINTS_PER_INCH / 25.4; // Setup & apply text clipping area if desired aStream = (SVG *) pls->dev; if ( aStream->textClipping ) { // Use PLplot core routine difilt_clip to appropriately // transform the coordinates of the clipping rectangle difilt_clip( rcx, rcy ); same_clip = TRUE; if ( aStream->which_clip == 0 ) { same_clip = FALSE; } else { for ( i = 0; i < 4; i++ ) { if ( rcx[i] != prev_rcx[i] || rcy[i] != prev_rcy[i] ) same_clip = FALSE; } } if ( !same_clip ) { svg_open( aStream, "clipPath" ); svg_attr_values( aStream, "id", "text-clipping%d", aStream->which_clip ); svg_general( aStream, ">\n" ); // Output a polygon to represent the clipping region. svg_open( aStream, "polygon" ); svg_attr_values( aStream, "points", "%f,%f %f,%f %f,%f %f,%f", ( (PLFLT) rcx[0] ) / aStream->scale, ( (PLFLT) rcy[0] ) / aStream->scale, ( (PLFLT) rcx[1] ) / aStream->scale, ( (PLFLT) rcy[1] ) / aStream->scale, ( (PLFLT) rcx[2] ) / aStream->scale, ( (PLFLT) rcy[2] ) / aStream->scale, ( (PLFLT) rcx[3] ) / aStream->scale, ( (PLFLT) rcy[3] ) / aStream->scale ); svg_open_end( aStream ); svg_close( aStream, "clipPath" ); for ( i = 0; i < 4; i++ ) { prev_rcx[i] = rcx[i]; prev_rcy[i] = rcy[i]; } aStream->which_clip++; } svg_open( aStream, "g" ); svg_attr_values( aStream, "clip-path", "url(#text-clipping%d)", aStream->which_clip - 1 ); svg_general( aStream, ">\n" ); } // This draws the clipping region on the screen which can // be very helpful for debugging. // // svg_open(aStream, "polygon"); // svg_attr_values(aStream, // "points", // "%f,%f %f,%f %f,%f %f,%f", // ((PLFLT)rcx[0])/aStream->scale, // ((PLFLT)rcy[0])/aStream->scale, // ((PLFLT)rcx[1])/aStream->scale, // ((PLFLT)rcy[1])/aStream->scale, // ((PLFLT)rcx[2])/aStream->scale, // ((PLFLT)rcy[2])/aStream->scale, // ((PLFLT)rcx[3])/aStream->scale, // ((PLFLT)rcy[3])/aStream->scale); // svg_stroke_width(pls); // svg_stroke_color(pls); // svg_attr_value(aStream, "fill", "none"); // svg_open_end(aStream); // // Calculate the tranformation matrix for SVG based on the // transformation matrix provided by PLplot. plRotationShear( args->xform, &rotation, &shear, &stride ); // N.B. Experimentally, I (AWI) have found the svg rotation angle is // the negative of the libcairo rotation angle, and the svg shear angle // is pi minus the libcairo shear angle. rotation -= pls->diorot * PI / 2.0; cos_rot = cos( rotation ); sin_rot = -sin( rotation ); sin_shear = sin( shear ); cos_shear = -cos( shear ); t[0] = cos_rot * stride; t[1] = -sin_rot * stride; t[2] = cos_rot * sin_shear + sin_rot * cos_shear; t[3] = -sin_rot * sin_shear + cos_rot * cos_shear; //-------------- // open text tag // -------------- svg_open( aStream, "text" ); svg_attr_value( aStream, "dominant-baseline", "no-change" ); // set font color svg_fill_color( pls ); // white space preserving mode svg_attr_value( aStream, "xml:space", "preserve" ); // set the font size svg_attr_values( aStream, "font-size", "%d", (int) ftHt ); // Apply coordinate transform for text display. // The transformation also defines the location of the text in x and y. svg_attr_values( aStream, "transform", "matrix(%f %f %f %f %f %f)", t[0], t[1], t[2], t[3], (double) ( args->x / aStream->scale ), (double) ( args->y / aStream->scale ) ); //---------------------------------------------------------- // Write the text with formatting // We just keep stacking up tspan tags, then close them all // after we have written out all of the text. // ---------------------------------------------------------- // For if_write = 0, we write nothing and instead accumulate the // sum_glyph_size from the fontsize of the individual glyphs which // is then used to figure out the initial x position from text-anchor and // args->just that is used to write out the SVG xml for if_write = 1. glyph_size = (int) ftHt; sum_glyph_size = 0; if_write = 0; while ( if_write < 2 ) { if ( if_write == 1 ) { //printf("number of characters = %f\n", sum_glyph_size/ftHt); // The above coordinate transform defines the _raw_ x position of the // text without justification so this attribute value depends on // text-anchor and args->just*sum_glyph_size // N.B. sum_glyph_size calculation only correct for monospaced fonts // so generally sum_glyph_size will be overestimated by various amounts // depending on what glyphs are to be rendered, the font, etc. However, // this correction is differential respect to the end points or the // middle so you should be okay so long as you don't deviate too far // from those anchor points. if ( args->just < 0.33 ) { svg_attr_value( aStream, "text-anchor", "start" ); // left justification svg_attr_values( aStream, "x", "%f", (double) ( -args->just * sum_glyph_size ) ); } else if ( args->just > 0.66 ) { svg_attr_value( aStream, "text-anchor", "end" ); // right justification svg_attr_values( aStream, "x", "%f", (double) ( ( 1. - args->just ) * sum_glyph_size ) ); } else { svg_attr_value( aStream, "text-anchor", "middle" ); // center svg_attr_values( aStream, "x", "%f", (double) ( ( 0.5 - args->just ) * sum_glyph_size ) ); } // The text goes at zero in y since the above // coordinate transform defines the y position of the text svg_attr_values( aStream, "y", "%f", FONT_SHIFT_RATIO * 0.5 * ftHt + FONT_SHIFT_OFFSET ); fprintf( aStream->svgFile, ">" ); // specify the initial font specify_font( aStream->svgFile, fci ); } i = 0; scaled_ftHt = ftHt; level = 0; ddup = 0.; while ( i < ucs4Len ) { if ( ucs4[i] < PL_FCI_MARK ) // not a font change { if ( ucs4[i] != (PLUNICODE) plplot_esc ) // a character to display { if ( if_write ) { write_unicode( aStream->svgFile, ucs4[i] ); } else { sum_glyph_size += glyph_size; } i++; continue; } i++; if ( ucs4[i] == (PLUNICODE) plplot_esc ) // a escape character to display { if ( if_write ) { write_unicode( aStream->svgFile, ucs4[i] ); } else { sum_glyph_size += glyph_size; } i++; continue; } else { // super/subscript logic follows that in plstr routine (plsym.c) // for Hershey fonts. Factor of FONT_SHIFT_RATIO*0.80 is empirical // adjustment. if ( ucs4[i] == (PLUNICODE) 'u' ) // Superscript { plP_script_scale( TRUE, &level, &old_sscale, &sscale, &old_soffset, &soffset ); // The correction for the difference in magnitude // between the baseline and middle coordinate systems // for superscripts should be // 0.5*(base font size - superscript/subscript font size). old_dup = ddup; ddup = 0.5 * ( 1.0 - sscale ); if ( level <= 0 ) { scaled_offset = FONT_SHIFT_RATIO * ftHt * ( 0.80 * ( soffset - old_soffset ) - ( ddup - old_dup ) ); } else { scaled_offset = -FONT_SHIFT_RATIO * ftHt * ( 0.80 * ( soffset - old_soffset ) + ( ddup - old_dup ) ); } scaled_ftHt = sscale * ftHt; if ( if_write ) { totalTags++; fprintf( aStream->svgFile, "", scaled_offset, (int) scaled_ftHt ); } else { glyph_size = (int) scaled_ftHt; } } if ( ucs4[i] == (PLUNICODE) 'd' ) // Subscript { plP_script_scale( FALSE, &level, &old_sscale, &sscale, &old_soffset, &soffset ); // The correction for the difference in magnitude // between the baseline and middle coordinate systems // for superscripts should be // 0.5*(base font size - superscript/subscript font size). old_dup = ddup; ddup = 0.5 * ( 1.0 - sscale ); if ( level < 0 ) { scaled_offset = FONT_SHIFT_RATIO * ftHt * ( 0.80 * ( soffset - old_soffset ) - ( ddup - old_dup ) ); } else { scaled_offset = -FONT_SHIFT_RATIO * ftHt * ( 0.80 * ( soffset - old_soffset ) + ( ddup - old_dup ) ); } scaled_ftHt = sscale * ftHt; if ( if_write ) { totalTags++; fprintf( aStream->svgFile, "", scaled_offset, (int) scaled_ftHt ); } else { glyph_size = (int) scaled_ftHt; } } i++; } } else // a font change { if ( if_write ) { specify_font( aStream->svgFile, ucs4[i] ); totalTags++; } i++; } } if_write++; } //---------------------------------------------- // close out all the tspan tags and the text tag // ---------------------------------------------- for ( i = 0; i < totalTags; i++ ) { fprintf( aStream->svgFile, "" ); } // The following commented out (by AWI) because it is a bad idea to // put line ends in the middle of a text tag. This was the key to // all the text rendering issues we had. //fprintf(svgFile,"\n"); // For the same reason use fprintf and svgIndent -= 2; // to close the text tag rather than svg_close("text"); since // we don't want indentation spaces entering the text. // svg_close("text"); fprintf( aStream->svgFile, "\n" ); aStream->svgIndent -= 2; if ( aStream->textClipping ) { svg_close( aStream, "g" ); } } //-------------------------------------------------------------------------- // svg_open () // // Used to open a new XML expression, sets the indent level appropriately //-------------------------------------------------------------------------- void svg_open( SVG *aStream, const char *tag ) { svg_indent( aStream ); fprintf( aStream->svgFile, "<%s\n", tag ); aStream->svgIndent += 2; } //-------------------------------------------------------------------------- // svg_open_end () // // Used to end the opening of a new XML expression i.e. add // the final ">". //-------------------------------------------------------------------------- void svg_open_end( SVG *aStream ) { svg_indent( aStream ); fprintf( aStream->svgFile, "/>\n" ); aStream->svgIndent -= 2; } //-------------------------------------------------------------------------- // svg_attr_value () // // Prints two strings to svgFile as a XML attribute value pair // i.e. foo="bar" //-------------------------------------------------------------------------- void svg_attr_value( SVG *aStream, const char *attribute, const char *value ) { svg_indent( aStream ); fprintf( aStream->svgFile, "%s=\"%s\"\n", attribute, value ); } //-------------------------------------------------------------------------- // svg_attr_values () // // Prints a string and a bunch of numbers / strings as a XML attribute // value pair i.e. foo="0 10 1.0 5.3 bar" // // This function is derived from an example in // "The C Programming Language" by Kernighan and Ritchie. // //-------------------------------------------------------------------------- void svg_attr_values( SVG *aStream, const char *attribute, const char *format, ... ) { va_list ap; const char *p, *sval; int ival; double dval; svg_indent( aStream ); fprintf( aStream->svgFile, "%s=\"", attribute ); va_start( ap, format ); for ( p = format; *p; p++ ) { if ( *p != '%' ) { fprintf( aStream->svgFile, "%c", *p ); continue; } switch ( *++p ) { case 'd': ival = va_arg( ap, int ); fprintf( aStream->svgFile, "%d", ival ); break; case 'f': dval = va_arg( ap, double ); fprintf( aStream->svgFile, "%f", dval ); break; case 'r': // r is non-standard, but use it here to format rounded value dval = va_arg( ap, double ); fprintf( aStream->svgFile, "%.2f", dval ); break; case 's': sval = va_arg( ap, char * ); fprintf( aStream->svgFile, "%s", sval ); break; default: fprintf( aStream->svgFile, "%c", *p ); break; } } fprintf( aStream->svgFile, "\"\n" ); va_end( ap ); } //-------------------------------------------------------------------------- // svg_close () // // Used to close a XML expression, sets the indent level appropriately //-------------------------------------------------------------------------- void svg_close( SVG *aStream, const char *tag ) { aStream->svgIndent -= 2; svg_indent( aStream ); if ( strlen( tag ) > 0 ) { fprintf( aStream->svgFile, "\n", tag ); } else { fprintf( aStream->svgFile, "/>\n" ); } } //-------------------------------------------------------------------------- // svg_general () // // Used to print any text into the svgFile //-------------------------------------------------------------------------- void svg_general( SVG *aStream, const char *text ) { svg_indent( aStream ); fprintf( aStream->svgFile, "%s", text ); } //-------------------------------------------------------------------------- // svg_indent () // // Indents properly based on the current indent level //-------------------------------------------------------------------------- void svg_indent( SVG *aStream ) { short i; for ( i = 0; i < aStream->svgIndent; i++ ) { fprintf( aStream->svgFile, " " ); } } //-------------------------------------------------------------------------- // svg_stroke_width () // // sets the stroke width based on the current width //-------------------------------------------------------------------------- void svg_stroke_width( PLStream *pls ) { SVG *aStream; aStream = pls->dev; svg_indent( aStream ); fprintf( aStream->svgFile, "stroke-width=\"%e\"\n", pls->width ); } //-------------------------------------------------------------------------- // svg_stroke_color () // // sets the stroke color based on the current color //-------------------------------------------------------------------------- void svg_stroke_color( PLStream *pls ) { SVG *aStream; aStream = pls->dev; svg_indent( aStream ); fprintf( aStream->svgFile, "stroke=\"#" ); write_hex( aStream->svgFile, pls->curcolor.r ); write_hex( aStream->svgFile, pls->curcolor.g ); write_hex( aStream->svgFile, pls->curcolor.b ); fprintf( aStream->svgFile, "\"\n" ); svg_indent( aStream ); fprintf( aStream->svgFile, "stroke-opacity=\"%f\"\n", pls->curcolor.a ); } //-------------------------------------------------------------------------- // svg_fill_color () // // sets the fill color based on the current color //-------------------------------------------------------------------------- void svg_fill_color( PLStream *pls ) { SVG *aStream; aStream = pls->dev; svg_indent( aStream ); fprintf( aStream->svgFile, "fill=\"#" ); write_hex( aStream->svgFile, pls->curcolor.r ); write_hex( aStream->svgFile, pls->curcolor.g ); write_hex( aStream->svgFile, pls->curcolor.b ); fprintf( aStream->svgFile, "\"\n" ); svg_indent( aStream ); fprintf( aStream->svgFile, "fill-opacity=\"%f\"\n", pls->curcolor.a ); } //-------------------------------------------------------------------------- // svg_fill_background_color () // // sets the background fill color based on the current background color //-------------------------------------------------------------------------- void svg_fill_background_color( PLStream *pls ) { SVG *aStream; aStream = pls->dev; svg_indent( aStream ); fprintf( aStream->svgFile, "fill=\"#" ); write_hex( aStream->svgFile, pls->cmap0[0].r ); write_hex( aStream->svgFile, pls->cmap0[0].g ); write_hex( aStream->svgFile, pls->cmap0[0].b ); fprintf( aStream->svgFile, "\"\n" ); svg_indent( aStream ); fprintf( aStream->svgFile, "fill-opacity=\"%f\"\n", pls->cmap0[0].a ); } //-------------------------------------------------------------------------- // svg_family_check () // // support function to help supress more than one page if family file // output not specified by the user (e.g., with the -fam command-line option). //-------------------------------------------------------------------------- int svg_family_check( PLStream *pls ) { if ( pls->family || pls->page == 1 ) { return 0; } else { if ( !already_warned ) { already_warned = 1; plwarn( "All pages after the first skipped because family file output not specified.\n" ); } return 1; } } //-------------------------------------------------------------------------- // write_hex () // // writes a unsigned char as an appropriately formatted hex value //-------------------------------------------------------------------------- void write_hex( FILE *svgFile, unsigned char val ) { if ( val < 16 ) { fprintf( svgFile, "0%X", val ); } else { fprintf( svgFile, "%X", val ); } } //-------------------------------------------------------------------------- // write_unicode () // // writes a unicode character, appropriately formatted (i.e. &#xNNN) // with invalid xml characters replaced by ' '. //-------------------------------------------------------------------------- void write_unicode( FILE *svgFile, PLUNICODE ucs4_char ) { if ( ucs4_char >= ' ' || ucs4_char == '\t' || ucs4_char == '\n' || ucs4_char == '\r' ) fprintf( svgFile, "&#x%x;", ucs4_char ); else fprintf( svgFile, "&#x%x;", ' ' ); } //-------------------------------------------------------------------------- // specify_font () // // Note: // We don't actually specify a font, just the fonts properties. // The hope is that this will give the display program the freedom // to choose the font with the glyphs that it needs to display // the text. // // Known Issues: // (1) On OS-X 10.4 with Firefox and Camino the "serif" font-family // looks more like the "italic" font-style. // //-------------------------------------------------------------------------- void specify_font( FILE *svgFile, PLUNICODE ucs4_char ) { fprintf( svgFile, "" ); } else if ( ( ucs4_char & 0xF00 ) == 0x100 ) { fprintf( svgFile, "font-weight=\"bold\">" ); } } plplot-5.13.0/drivers/tk.driver_info.in000644 001752 001752 00000000033 13150160115 021562 0ustar00softwaresoftware000000 000000 tk:Tcl/TK Window:1:tk:7:tk plplot-5.13.0/drivers/psttf.cc000644 001752 001752 00000124533 13150160115 017772 0ustar00softwaresoftware000000 000000 // PLplot PostScript device driver using LASi to provide fonts // based on original ps.c PostScript driver // // Copyright (C) 1992, 2001 Geoffrey Furnish // Copyright (C) 1992, 1993, 1994, 1995, 2001 Maurice LeBrun // Copyright (C) 2000-2014 Alan W. Irwin // Copyright (C) 2001, 2002 Joao Cardoso // Copyright (C) 2001, 2003, 2004 Rafael Laboissiere // Copyright (C) 2004, 2005 Thomas J. Duck // Copyright (C) 2005, 2006 Andrew Ross // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // #include "plDevs.h" #if defined ( PLD_psttf ) //#define NEED_PLDEBUG #include "plplotP.h" #include "drivers.h" #include "ps.h" #include #include #include "plfreetype.h" #include #include #include // Define macro to truncate small values to zero - prevents // * printf printing -0.000 #define TRMFLT( a ) ( ( fabs( a ) < 5.0e-4 ) ? 0.0 : ( a ) ) using namespace LASi; using namespace std; // Device info PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_psttf = "psttf:PostScript File (monochrome):0:psttf:55:psttfm\n" "psttfc:PostScript File (color):0:psttf:56:psttfc\n"; // Prototypes for functions in this file. void plD_dispatch_init_psttfm( PLDispatchTable *pdt ); void plD_dispatch_init_psttfc( PLDispatchTable *pdt ); static char *ps_getdate( void ); static void ps_init( PLStream * ); static void fill_polygon( PLStream *pls ); static void proc_str( PLStream *, EscText * ); //static void esc_purge( char *, char * ); #define OUTBUF_LEN 128 static char outbuf[OUTBUF_LEN]; static int text = 1; static int color; static int hrshsym = 0; // Font style and weight lookup tables #define N_Pango_Lookup 5 const char * DefaultFamilyLookup[N_Pango_Lookup] = { "sans", "serif", "monospace", "sans,serif", "sans,serif" }; const char * EnvFamilyLookup[N_Pango_Lookup] = { "PLPLOT_FREETYPE_SANS_FAMILY", "PLPLOT_FREETYPE_SERIF_FAMILY", "PLPLOT_FREETYPE_MONO_FAMILY", "PLPLOT_FREETYPE_SCRIPT_FAMILY", "PLPLOT_FREETYPE_SYMBOL_FAMILY" }; #define FAMILY_LOOKUP_LEN 1024 char FamilyLookup[N_Pango_Lookup][FAMILY_LOOKUP_LEN]; const FontWeight WeightLookup[2] = { NORMAL_WEIGHT, BOLD }; const FontStyle StyleLookup[3] = { NORMAL_STYLE, ITALIC, OBLIQUE }; static DrvOpt ps_options[] = { { "text", DRV_INT, &text, "Use Postscript text (text=0|1)" }, { "color", DRV_INT, &color, "Use color (color=0|1)" }, { "hrshsym", DRV_INT, &hrshsym, "Use Hershey symbol set (hrshsym=0|1)" }, { NULL, DRV_INT, NULL, NULL } }; // text > 0 uses some postscript tricks, namely a transformation matrix // that scales, rotates (with slanting) and offsets text strings. // It has yet some bugs for 3d plots. static void psttf_dispatch_init_helper( PLDispatchTable *pdt, const char *menustr, const char *devnam, int type, int seq, plD_init_fp init ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = (char *) menustr; pdt->pl_DevName = (char *) devnam; #else (void) menustr; // Cast to void to silence compiler warnings about unused parameters (void) devnam; #endif pdt->pl_type = type; pdt->pl_seq = seq; pdt->pl_init = init; pdt->pl_line = (plD_line_fp) plD_line_psttf; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_psttf; pdt->pl_eop = (plD_eop_fp) plD_eop_psttf; pdt->pl_bop = (plD_bop_fp) plD_bop_psttf; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_psttf; pdt->pl_state = (plD_state_fp) plD_state_psttf; pdt->pl_esc = (plD_esc_fp) plD_esc_psttf; } void plD_dispatch_init_psttfm( PLDispatchTable *pdt ) { psttf_dispatch_init_helper( pdt, "PostScript File (monochrome)", "psttf", plDevType_FileOriented, 55, (plD_init_fp) plD_init_psttfm ); } void plD_dispatch_init_psttfc( PLDispatchTable *pdt ) { psttf_dispatch_init_helper( pdt, "PostScript File (color)", "psttfc", plDevType_FileOriented, 56, (plD_init_fp) plD_init_psttfc ); } //-------------------------------------------------------------------------- // plD_init_psttf() // // Initialize device. //-------------------------------------------------------------------------- void plD_init_psttfm( PLStream *pls ) { color = 0; pls->color = 0; // Not a color device plParseDrvOpts( ps_options ); if ( color ) pls->color = 1; // But user wants color ps_init( pls ); } void plD_init_psttfc( PLStream *pls ) { color = 1; pls->color = 1; // Is a color device plParseDrvOpts( ps_options ); if ( !color ) pls->color = 0; // But user does not want color ps_init( pls ); } #define MAX_NUM_TRIES 10 static void ps_init( PLStream *pls ) { int i; char *a; PSDev *dev; PostscriptDocument *doc; PLFLT pxlx, pxly; // Set default values - 7.5 x 10 [inches] (72 points = 1 inch) if ( pls->xlength <= 0 || pls->ylength <= 0 ) { pls->xlength = 540; pls->ylength = 720; } if ( pls->xdpi <= 0 ) pls->xdpi = 72.; if ( pls->ydpi <= 0 ) pls->ydpi = 72.; pxlx = YPSSIZE / LPAGE_X; pxly = XPSSIZE / LPAGE_Y; if ( text ) { pls->dev_text = 1; // want to draw text pls->dev_unicode = 1; // want unicode if ( hrshsym ) pls->dev_hrshsym = 1; // want Hershey symbols } pls->dev_fill0 = 1; // Can do solid fills // Initialize family file info plFamInit( pls ); // Prompt for a file name if not already set plOpenFile( pls ); // Create postscript document object if ( pls->psdoc != NULL ) delete (PostscriptDocument *) pls->psdoc; pls->psdoc = new PostscriptDocument(); doc = (PostscriptDocument *) ( pls->psdoc ); doc->osBody() << fixed; doc->osBody().precision( 3 ); // Allocate and initialize device-specific data if ( pls->dev != NULL ) free( (void *) pls->dev ); pls->dev = calloc( 1, (size_t) sizeof ( PSDev ) ); if ( pls->dev == NULL ) plexit( "ps_init: Out of memory." ); dev = (PSDev *) pls->dev; dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; plP_setpxl( pxlx, pxly ); dev->llx = XPSSIZE; dev->lly = YPSSIZE; dev->urx = 0; dev->ury = 0; dev->ptcnt = 0; // Rotate by 90 degrees since portrait mode addressing is used dev->xmin = 0; dev->ymin = 0; dev->xmax = PSY; dev->ymax = PSX; dev->xlen = dev->xmax - dev->xmin; dev->ylen = dev->ymax - dev->ymin; plP_setphy( dev->xmin, dev->xmax, dev->ymin, dev->ymax ); // If portrait mode is specified, then set up an additional rotation // transformation with aspect ratio allowed to adjust via freeaspect. // Default orientation is landscape (ORIENTATION == 3 or 90 deg rotation // counter-clockwise from portrait). (Legacy PLplot used seascape // which was equivalent to ORIENTATION == 1 or 90 deg clockwise rotation // from portrait.) if ( pls->portrait ) { plsdiori( (PLFLT) ( 4 - ORIENTATION ) ); pls->freeaspect = 1; } // File table for font families using either environment variables // or defaults. for ( i = 0; i < N_Pango_Lookup; i++ ) { if ( ( a = getenv( EnvFamilyLookup[i] ) ) != NULL ) { strncpy( FamilyLookup[i], a, FAMILY_LOOKUP_LEN - 1 ); FamilyLookup[i][FAMILY_LOOKUP_LEN - 1] = '\0'; } else { strncpy( FamilyLookup[i], DefaultFamilyLookup[i], FAMILY_LOOKUP_LEN ); FamilyLookup[i][FAMILY_LOOKUP_LEN - 1] = '\0'; } } } //-------------------------------------------------------------------------- // writeHeader() // // Write plplot postscript commands into the header //-------------------------------------------------------------------------- void writeHeader( PLStream *pls ) { PostscriptDocument *doc = (PostscriptDocument *) ( pls->psdoc ); doc->osHeader() << "%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; doc->osHeader() << "%%Title: PLplot Graph\n"; doc->osHeader() << "%%Creator: PLplot Version " << PLPLOT_VERSION << "\n"; doc->osHeader() << "%%CreationDate: " << ps_getdate() << "\n"; doc->osHeader() << "%%Pages: (atend)\n"; doc->osHeader() << "%%EndComments\n\n"; // Definitions // Save VM state doc->osHeader() << "/PSSave save def\n"; // Define a dictionary and start using it doc->osHeader() << "/PSDict 200 dict def\n"; doc->osHeader() << "PSDict begin\n"; doc->osHeader() << "/@restore /restore load def\n"; doc->osHeader() << "/restore\n"; doc->osHeader() << " {vmstatus pop\n"; doc->osHeader() << " dup @VMused lt {pop @VMused} if\n"; doc->osHeader() << " exch pop exch @restore /@VMused exch def\n"; doc->osHeader() << " } def\n"; doc->osHeader() << "/@pri\n"; doc->osHeader() << " {\n"; doc->osHeader() << " ( ) print\n"; doc->osHeader() << " ( ) cvs print\n"; doc->osHeader() << " } def\n"; // n @copies - doc->osHeader() << "/@copies\n"; doc->osHeader() << " {\n"; doc->osHeader() << " /#copies exch def\n"; doc->osHeader() << " } def\n"; // - @start - -- start everything doc->osHeader() << "/@start\n"; doc->osHeader() << " {\n"; doc->osHeader() << " vmstatus pop /@VMused exch def pop\n"; doc->osHeader() << " } def\n"; // - @end - -- finished doc->osHeader() << "/@end\n"; doc->osHeader() << " {flush\n"; doc->osHeader() << " end\n"; doc->osHeader() << " PSSave restore\n"; doc->osHeader() << " } def\n"; // bop - -- begin a new page // Only fill background if we are using color and if the bg isn't white doc->osHeader() << "/bop\n"; doc->osHeader() << " {\n"; doc->osHeader() << " /SaveImage save def\n"; doc->osHeader() << " } def\n"; // - eop - -- end a page doc->osHeader() << "/eop\n"; doc->osHeader() << " {\n"; doc->osHeader() << " showpage\n"; doc->osHeader() << " SaveImage restore\n"; doc->osHeader() << " } def\n"; // Set line parameters doc->osHeader() << "/@line\n"; doc->osHeader() << " {0 setlinecap\n"; doc->osHeader() << " 0 setlinejoin\n"; doc->osHeader() << " 1 setmiterlimit\n"; doc->osHeader() << " } def\n"; // d @hsize - horizontal clipping dimension doc->osHeader() << "/@hsize {/hs exch def} def\n"; doc->osHeader() << "/@vsize {/vs exch def} def\n"; // d @hoffset - shift for the plots doc->osHeader() << "/@hoffset {/ho exch def} def\n"; doc->osHeader() << "/@voffset {/vo exch def} def\n"; // Set line width doc->osHeader() << "/lw " << (int) ( ( pls->width < MIN_WIDTH ) ? DEF_WIDTH : ( pls->width > MAX_WIDTH ) ? MAX_WIDTH : pls->width ) << " def\n"; // Setup user specified offsets, scales, sizes for clipping doc->osHeader() << "/@SetPlot\n"; doc->osHeader() << " {\n"; doc->osHeader() << " ho vo translate\n"; doc->osHeader() << " XScale YScale scale\n"; doc->osHeader() << " lw setlinewidth\n"; doc->osHeader() << " } def\n"; // Setup x & y scales doc->osHeader() << "/XScale\n"; doc->osHeader() << " {hs " << YPSSIZE << " div} def\n"; doc->osHeader() << "/YScale\n"; doc->osHeader() << " {vs " << XPSSIZE << " div} def\n"; // Macro definitions of common instructions, to keep output small doc->osHeader() << "/M {moveto} def\n"; doc->osHeader() << "/D {lineto} def\n"; doc->osHeader() << "/A {0.5 0 360 arc} def\n"; doc->osHeader() << "/S {stroke} def\n"; doc->osHeader() << "/Z {stroke newpath} def\n"; if ( pls->dev_eofill ) doc->osHeader() << "/F {closepath gsave eofill grestore stroke} def\n"; else doc->osHeader() << "/F {closepath gsave fill grestore stroke} def\n"; doc->osHeader() << "/N {newpath} def\n"; doc->osHeader() << "/C {setrgbcolor} def\n"; doc->osHeader() << "/G {setgray} def\n"; doc->osHeader() << "/W {setlinewidth} def\n"; doc->osHeader() << "/R {rotate} def\n"; doc->osHeader() << "/B {Z " << 0 << " " << 0 << " M " << 0 << " " << PSY << " D " << PSX << " " << PSY << " D " << PSX << " " << 0 << " D " << 0 << " " << 0 << " closepath} def\n"; doc->osHeader() << "/CL {newpath M D D D closepath clip} def\n"; // End of dictionary definition doc->osHeader() << "end\n\n"; // Set up the plots doc->osHeader() << "PSDict begin\n"; doc->osHeader() << "@start\n"; doc->osHeader() << COPIES << " @copies\n"; doc->osHeader() << "@line\n"; doc->osHeader() << YSIZE << " @hsize\n"; doc->osHeader() << XSIZE << " @vsize\n"; doc->osHeader() << YOFFSET << " @hoffset\n"; doc->osHeader() << XOFFSET << " @voffset\n"; doc->osHeader() << "@SetPlot\n" << endl; } //-------------------------------------------------------------------------- // plD_line_psttf() // // Draw a line in the current color from (x1,y1) to (x2,y2). //-------------------------------------------------------------------------- void plD_line_psttf( PLStream *pls, short x1a, short y1a, short x2a, short y2a ) { PSDev *dev = (PSDev *) pls->dev; PostscriptDocument *doc = (PostscriptDocument *) pls->psdoc; PLINT x1 = x1a, y1 = y1a, x2 = x2a, y2 = y2a; // Rotate by 90 degrees plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &x1, &y1 ); plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &x2, &y2 ); if ( x1 == dev->xold && y1 == dev->yold && dev->ptcnt < 40 ) { if ( pls->linepos + 12 > LINELENGTH ) { doc->osBody() << '\n'; pls->linepos = 0; } else doc->osBody() << ' '; snprintf( outbuf, OUTBUF_LEN, "%d %d D", x2, y2 ); dev->ptcnt++; pls->linepos += 12; } else { doc->osBody() << " Z\n"; pls->linepos = 0; if ( x1 == x2 && y1 == y2 ) // must be a single dot, draw a circle snprintf( outbuf, OUTBUF_LEN, "%d %d A", x1, y1 ); else snprintf( outbuf, OUTBUF_LEN, "%d %d M %d %d D", x1, y1, x2, y2 ); dev->llx = MIN( dev->llx, x1 ); dev->lly = MIN( dev->lly, y1 ); dev->urx = MAX( dev->urx, x1 ); dev->ury = MAX( dev->ury, y1 ); dev->ptcnt = 1; pls->linepos += 24; } dev->llx = MIN( dev->llx, x2 ); dev->lly = MIN( dev->lly, y2 ); dev->urx = MAX( dev->urx, x2 ); dev->ury = MAX( dev->ury, y2 ); doc->osBody() << outbuf; pls->bytecnt += 1 + strlen( outbuf ); dev->xold = x2; dev->yold = y2; } //-------------------------------------------------------------------------- // plD_polyline_psttf() // // Draw a polyline in the current color. //-------------------------------------------------------------------------- void plD_polyline_psttf( PLStream *pls, short *xa, short *ya, PLINT npts ) { PLINT i; for ( i = 0; i < npts - 1; i++ ) plD_line_psttf( pls, xa[i], ya[i], xa[i + 1], ya[i + 1] ); } //-------------------------------------------------------------------------- // plD_eop_psttf() // // End of page. //-------------------------------------------------------------------------- void plD_eop_psttf( PLStream *pls ) { PostscriptDocument *doc = (PostscriptDocument *) pls->psdoc; doc->osBody() << " S\neop\n"; } //-------------------------------------------------------------------------- // plD_bop_psttf() // // Set up for the next page. // Advance to next family file if necessary (file output). //-------------------------------------------------------------------------- void plD_bop_psttf( PLStream *pls ) { PSDev *dev = (PSDev *) pls->dev; PostscriptDocument *doc = (PostscriptDocument *) pls->psdoc; dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; if ( !pls->termin ) plGetFam( pls ); pls->page++; if ( pls->family ) doc->osBody() << "%%Page: " << (int) pls->page << " 1\n"; else doc->osBody() << "%%Page: " << (int) pls->page << " " << (int) pls->page << "\n"; doc->osBody() << "bop\n"; if ( pls->color ) { PLFLT r, g, b; if ( pls->cmap0[0].r != 0xFF || pls->cmap0[0].g != 0xFF || pls->cmap0[0].b != 0xFF ) { r = ( (PLFLT) pls->cmap0[0].r ) / 255.; g = ( (PLFLT) pls->cmap0[0].g ) / 255.; b = ( (PLFLT) pls->cmap0[0].b ) / 255.; doc->osBody() << "B " << r << " " << g << " " << b << " C F\n"; } } pls->linepos = 0; // This ensures the color and line width are set correctly at the beginning of // each page plD_state_psttf( pls, PLSTATE_COLOR0 ); plD_state_psttf( pls, PLSTATE_WIDTH ); } //-------------------------------------------------------------------------- // plD_tidy_psttf() // // Close graphics file or otherwise clean up. //-------------------------------------------------------------------------- void plD_tidy_psttf( PLStream *pls ) { PSDev *dev = (PSDev *) pls->dev; PostscriptDocument *doc = (PostscriptDocument *) pls->psdoc; dev->llx /= ENLARGE; dev->lly /= ENLARGE; dev->urx /= ENLARGE; dev->ury /= ENLARGE; dev->llx += YOFFSET; dev->lly += XOFFSET; dev->urx += YOFFSET; dev->ury += XOFFSET; // changed for correct Bounding boundaries Jan Thorbecke okt 1993 // occurs from the integer truncation -- postscript uses fp arithmetic dev->urx += 1; dev->ury += 1; if ( pls->family ) doc->osFooter() << "%%Pages: 1\n"; else doc->osFooter() << "%%Pages: " << (int) pls->page << "\n"; doc->osFooter() << "@end" << endl; // Now write the rest of the header writeHeader( pls ); // Write out postscript document to file and close // For C++ stream we first need to close the file using // the C FILE * handle, then reopen as a ofstream. Yuck! if ( !strcmp( pls->FileName, "-" ) ) { doc->write( cout, dev->llx, dev->lly, dev->urx, dev->ury ); } else { plCloseFile( pls ); ofstream out; out.open( pls->FileName ); doc->write( out, dev->llx, dev->lly, dev->urx, dev->ury ); out.close(); } delete doc; pls->psdoc = NULL; } //-------------------------------------------------------------------------- // plD_state_psttf() // // Handle change in PLStream state (color, pen width, fill attribute, etc). //-------------------------------------------------------------------------- void plD_state_psttf( PLStream *pls, PLINT op ) { PSDev *dev = (PSDev *) pls->dev; PostscriptDocument *doc = (PostscriptDocument *) pls->psdoc; switch ( op ) { case PLSTATE_WIDTH: { int width = (int) ( ( pls->width < MIN_WIDTH ) ? DEF_WIDTH : ( pls->width > MAX_WIDTH ) ? MAX_WIDTH : pls->width ); doc->osBody() << " S\n" << width << " W"; dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; break; } case PLSTATE_COLOR0: if ( !pls->color ) { doc->osBody() << " S\n" << ( pls->icol0 ? 0.0 : 1.0 ) << " G"; // Reinitialize current point location. if ( dev->xold != PL_UNDEFINED && dev->yold != PL_UNDEFINED ) doc->osBody() << " " << (int) dev->xold << " " << (int) dev->yold << " M \n"; break; } // else fallthrough case PLSTATE_COLOR1: if ( pls->color ) { PLFLT r = ( (PLFLT) pls->curcolor.r ) / 255.0; PLFLT g = ( (PLFLT) pls->curcolor.g ) / 255.0; PLFLT b = ( (PLFLT) pls->curcolor.b ) / 255.0; doc->osBody() << " S\n" << r << " " << g << " " << b << " C"; } else { PLFLT r = ( (PLFLT) pls->curcolor.r ) / 255.0; doc->osBody() << " S\n" << 1.0 - r << " G"; } // Reinitialize current point location. if ( dev->xold != PL_UNDEFINED && dev->yold != PL_UNDEFINED ) doc->osBody() << " " << (int) dev->xold << " " << (int) dev->yold << " M \n"; break; } } //-------------------------------------------------------------------------- // plD_esc_psttf() // // Escape function. //-------------------------------------------------------------------------- void plD_esc_psttf( PLStream *pls, PLINT op, void *ptr ) { switch ( op ) { case PLESC_FILL: fill_polygon( pls ); break; case PLESC_HAS_TEXT: proc_str( pls, (EscText *) ptr ); break; } } //-------------------------------------------------------------------------- // fill_polygon() // // Fill polygon described in points pls->dev_x[] and pls->dev_y[]. // Only solid color fill supported. //-------------------------------------------------------------------------- static void fill_polygon( PLStream *pls ) { PSDev *dev = (PSDev *) pls->dev; PostscriptDocument *doc = (PostscriptDocument *) pls->psdoc; PLINT n, ix = 0, iy = 0; PLINT x, y; doc->osBody() << " Z\n"; for ( n = 0; n < pls->dev_npts; n++ ) { x = pls->dev_x[ix++]; y = pls->dev_y[iy++]; // Rotate by 90 degrees plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &x, &y ); // First time through start with a x y moveto if ( n == 0 ) { snprintf( outbuf, OUTBUF_LEN, "N %d %d M", x, y ); dev->llx = MIN( dev->llx, x ); dev->lly = MIN( dev->lly, y ); dev->urx = MAX( dev->urx, x ); dev->ury = MAX( dev->ury, y ); doc->osBody() << outbuf; pls->bytecnt += strlen( outbuf ); continue; } if ( pls->linepos + 21 > LINELENGTH ) { doc->osBody() << '\n'; pls->linepos = 0; } else doc->osBody() << ' '; pls->bytecnt++; snprintf( outbuf, OUTBUF_LEN, "%d %d D", x, y ); dev->llx = MIN( dev->llx, x ); dev->lly = MIN( dev->lly, y ); dev->urx = MAX( dev->urx, x ); dev->ury = MAX( dev->ury, y ); doc->osBody() << outbuf; pls->bytecnt += strlen( outbuf ); pls->linepos += 21; } dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; doc->osBody() << " F "; } //-------------------------------------------------------------------------- // ps_getdate() // // Get the date and time //-------------------------------------------------------------------------- static char * ps_getdate( void ) { int len; time_t t; char *p; t = time( (time_t *) 0 ); p = ctime( &t ); len = strlen( p ); *( p + len - 1 ) = '\0'; // zap the newline character return p; } // 0.8 should mimic the offset of first superscript/subscript level // implemented in plstr (plsym.c) for Hershey fonts. However, when // comparing with -dev xwin and -dev xcairo results changing this // factor to 0.6 appears to offset the centers of the letters // appropriately while 0.8 gives much poorer agreement with the // other devices. # define RISE_FACTOR 0.6 //-------------------------------------------------------------------------- // proc_str() // // Prints postscript strings. // N.B. Now unicode only, no string access! // //-------------------------------------------------------------------------- void proc_str( PLStream *pls, EscText *args ) { PLFLT *t = args->xform, tt[4]; // Transform matrices PLFLT theta, shear, stride; // Rotation angle and shear from the matrix PLFLT ft_ht, offset; // Font height and offset PLFLT cs, sn; PSDev *dev = (PSDev *) pls->dev; PostscriptDocument *doc = (PostscriptDocument *) pls->psdoc; char *font, esc; FontStyle style; FontWeight weight; // Be generous. Used to store lots of font changes which take // 3 characters per change. #define PROC_STR_STRING_LENGTH 1000 char *strp, str[PROC_STR_STRING_LENGTH], *cur_strp, cur_str[PROC_STR_STRING_LENGTH]; float font_factor = 1.4; PLINT clxmin, clxmax, clymin, clymax; // Clip limits PLINT clipx[4], clipy[4]; // Current clip limits PLFLT scale = 1., up = 0.; // Font scaling and shifting parameters double lineSpacing, xAdvance, ymintmp, ymaxtmp, ymin, ymax, xmin, xmax; PLINT xx[4], yy[4]; // unicode only! so test for it. if ( args->unicode_array_len > 0 ) { int j, s, f; char *fonts[PROC_STR_STRING_LENGTH]; FontStyle styles[PROC_STR_STRING_LENGTH]; FontWeight weights[PROC_STR_STRING_LENGTH]; const PLUNICODE *cur_text; PLUNICODE fci; unsigned char fontfamily, fontstyle, fontweight; PLFLT old_sscale, sscale, old_soffset, soffset, dup; PLINT level = 0; // translate from unicode into type 1 font index. // // Choose the font family, style, variant, and weight using // the FCI (font characterization integer). // plgesc( &esc ); plgfci( &fci ); plP_fci2hex( fci, &fontfamily, PL_FCI_FAMILY ); plP_fci2hex( fci, &fontstyle, PL_FCI_STYLE ); plP_fci2hex( fci, &fontweight, PL_FCI_WEIGHT ); font = (char *) FamilyLookup[fontfamily]; weight = WeightLookup[fontweight]; style = StyleLookup[fontstyle]; // Need to add some error checking here if ( false ) { fprintf( stderr, "fci = 0x%x, font name pointer = NULL \n", fci ); plabort( "proc_str: FCI inconsistent with TrueTypeLookup; " "internal PLplot error" ); return; } //pldebug("proc_str", "fci = 0x%x, font name = %s\n", fci, font); cur_text = args->unicode_array; for ( f = s = j = 0; j < args->unicode_array_len; j++ ) { if ( cur_text[j] & PL_FCI_MARK ) { // process an FCI by saving it and escaping cur_str // with an escff to make it a 2-character escape // that is not used in legacy Hershey code // if ( ( f < PROC_STR_STRING_LENGTH ) && ( s + 3 < PROC_STR_STRING_LENGTH ) ) { plP_fci2hex( cur_text[j], &fontfamily, PL_FCI_FAMILY ); plP_fci2hex( cur_text[j], &fontstyle, PL_FCI_STYLE ); plP_fci2hex( cur_text[j], &fontweight, PL_FCI_WEIGHT ); fonts[f] = (char *) FamilyLookup[fontfamily]; weights[f] = WeightLookup[fontweight]; styles[f] = StyleLookup[fontstyle]; if ( fonts[f] == NULL ) { fprintf( stderr, "string-supplied FCI = 0x%x, font name pointer = NULL \n", cur_text[j] ); plabort( "proc_str: string-supplied FCI inconsistent with font lookup;" ); return; } //pldebug("proc_str", "string-supplied FCI = 0x%x, font name = %s\n", cur_text[j], fonts[f]); cur_str[s++] = esc; cur_str[s++] = 'f'; cur_str[s++] = 'f'; f++; } } else if ( s + 1 < PROC_STR_STRING_LENGTH ) { s += ucs4_to_utf8( cur_text[j], &cur_str[s] ); //pldebug("proc_str", "unicode = 0x%x, type 1 code = %d\n", // cur_text[j], cur_str[j]); } } cur_str[s] = '\0'; // finish previous polyline dev->xold = PL_UNDEFINED; dev->yold = PL_UNDEFINED; // Determine the font height ft_ht = pls->chrht * 72.0 / 25.4; // ft_ht in points, ht is in mm // The transform matrix has only rotations and shears; extract them plRotationShear( t, &theta, &shear, &stride ); cs = cos( theta ); sn = sin( theta ); tt[0] = t[0] * cs + t[2] * sn; tt[1] = t[1] * cs + t[3] * sn; tt[2] = -t[0] * sn + t[2] * cs; tt[3] = -t[1] * sn + t[3] * cs; // // Reference point conventions: // If base = 0, it is aligned with the center of the text box // If base = 1, it is aligned with the baseline of the text box // If base = 2, it is aligned with the top of the text box // // Currently plplot only uses base=0 // Postscript uses base=1 // // We must calculate the difference between the two and apply the offset. // if ( args->base == 2 ) // not supported by plplot offset = ENLARGE * ft_ht / 2.; // half font height else if ( args->base == 1 ) offset = 0.; else offset = -ENLARGE * ft_ht / 2.; // Determine the adjustment for page orientation theta -= PI / 2. * pls->diorot; args->y += (int) ( offset * cos( theta ) ); args->x -= (int) ( offset * sin( theta ) ); // ps driver is rotated by default plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &( args->x ), &( args->y ) ); // Correct for the fact ps driver uses landscape by default theta += PI / 2.; // Output // Set clipping clipx[0] = pls->clpxmi; clipx[2] = pls->clpxma; clipy[0] = pls->clpymi; clipy[2] = pls->clpyma; clipx[1] = clipx[2]; clipy[1] = clipy[0]; clipx[3] = clipx[0]; clipy[3] = clipy[2]; difilt( clipx, clipy, 4, &clxmin, &clxmax, &clymin, &clymax ); plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &clipx[0], &clipy[0] ); plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &clipx[1], &clipy[1] ); plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &clipx[2], &clipy[2] ); plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &clipx[3], &clipy[3] ); doc->osBody() << " gsave " << clipx[0] << " " << clipy[0] << " " << clipx[1] << " " << clipy[1] << " " << clipx[2] << " " << clipy[2] << " " << clipx[3] << " " << clipy[3] << " CL\n"; // move to string reference point doc->osBody() << " " << args->x << " " << args->y << " M\n"; // Save the current position and set the string rotation doc->osBody() << "gsave " << TRMFLT( theta * 180. / PI ) << " R\n"; doc->osBody() << "[" << TRMFLT( tt[0] ) << " " << TRMFLT( tt[2] ) << " " << TRMFLT( tt[1] ) << " " << TRMFLT( tt[3] ) << " 0 0] concat\n"; xmax = 0; // Dummy run through the string first to work out the // length, including any font changes cur_strp = cur_str; f = 0; do { strp = str; if ( *cur_strp == esc ) { cur_strp++; if ( *cur_strp == esc ) // { *strp++ = *cur_strp++; } else if ( *cur_strp == 'f' ) { cur_strp++; if ( *cur_strp++ != 'f' ) { // escff occurs because of logic above. But any suffix // other than "f" should never happen. plabort( "proc_str, internal PLplot logic error;" "wrong escf escape sequence" ); return; } font = fonts[f]; style = styles[f]; weight = weights[f]; f++; continue; } else switch ( *cur_strp++ ) { case 'd': //subscript case 'D': plP_script_scale( FALSE, &level, &old_sscale, &sscale, &old_soffset, &soffset ); scale = sscale; // The correction for the difference in magnitude // between the baseline and middle coordinate systems // for subscripts should be // -0.5*(base font size - superscript/subscript font size). dup = -0.5 * ( 1.0 - sscale ); up = -font_factor * ENLARGE * ft_ht * ( RISE_FACTOR * soffset + dup ); break; case 'u': //superscript case 'U': plP_script_scale( TRUE, &level, &old_sscale, &sscale, &old_soffset, &soffset ); scale = sscale; // The correction for the difference in magnitude // between the baseline and middle coordinate systems // for superscripts should be // 0.5*(base font size - superscript/subscript font size). dup = 0.5 * ( 1.0 - sscale ); up = font_factor * ENLARGE * ft_ht * ( RISE_FACTOR * soffset + dup ); break; // ignore the next sequences case '+': case '-': case 'b': case 'B': plwarn( "'+', '-', and 'b/B' text escape sequences not processed." ); break; } } // copy from current to next token, adding a postscript escape // char '\' if necessary // while ( *cur_strp && *cur_strp != esc ) { *strp++ = *cur_strp++; } *strp = '\0'; // if(fabs(up)<0.001) up = 0.; /* Watch out for small differences */ // Set the font size doc->setFont( font, style, weight ); doc->setFontSize( font_factor * ENLARGE * ft_ht * scale ); doc->get_dimensions( (const char *) str, &lineSpacing, &xAdvance, &ymintmp, &ymaxtmp ); xmax += xAdvance; } while ( *cur_strp ); // Use the length of the string to calculate offset // Also used later for bounding box xmin = -xmax * args->just; xmax = xmin; ymin = 0; ymax = 0; // Reset parameters level = 0; scale = 1.0; up = 0.0; // Move relative to position to account for justification doc->osBody() << " gsave " << TRMFLT( xmin * tt[0] ) << " " << TRMFLT( xmin * tt[2] ) << " rmoveto\n"; // Parse string for PLplot escape sequences and print everything out cur_strp = cur_str; f = 0; do { strp = str; if ( *cur_strp == esc ) { cur_strp++; if ( *cur_strp == esc ) // { *strp++ = *cur_strp++; } else if ( *cur_strp == 'f' ) { cur_strp++; if ( *cur_strp++ != 'f' ) { // escff occurs because of logic above. But any suffix // other than "f" should never happen. plabort( "proc_str, internal PLplot logic error;" "wrong escf escape sequence" ); return; } font = fonts[f]; style = styles[f]; weight = weights[f]; f++; //pldebug("proc_str", "string-specified fci = 0x%x, font name = %s\n", fci, font); continue; } else switch ( *cur_strp++ ) { case 'd': //subscript case 'D': plP_script_scale( FALSE, &level, &old_sscale, &sscale, &old_soffset, &soffset ); scale = sscale; // The correction for the difference in magnitude // between the baseline and middle coordinate systems // for subscripts should be // -0.5*(base font size - superscript/subscript font size). dup = -0.5 * ( 1.0 - sscale ); up = -font_factor * ENLARGE * ft_ht * ( RISE_FACTOR * soffset + dup ); break; case 'u': //superscript case 'U': plP_script_scale( TRUE, &level, &old_sscale, &sscale, &old_soffset, &soffset ); scale = sscale; // The correction for the difference in magnitude // between the baseline and middle coordinate systems // for superscripts should be // 0.5*(base font size - superscript/subscript font size). dup = 0.5 * ( 1.0 - sscale ); up = font_factor * ENLARGE * ft_ht * ( RISE_FACTOR * soffset + dup ); break; // ignore the next sequences case '+': case '-': case 'b': case 'B': plwarn( "'+', '-', and 'b/B' text escape sequences not processed." ); break; } } // copy from current to next token, adding a postscript escape // char '\' if necessary // while ( *cur_strp && *cur_strp != esc ) { *strp++ = *cur_strp++; } *strp = '\0'; // if(fabs(up)<0.001) up = 0.; /* Watch out for small differences */ // Set the font size doc->setFont( font, style, weight ); doc->setFontSize( font_factor * ENLARGE * ft_ht * scale ); doc->get_dimensions( (const char *) str, &lineSpacing, &xAdvance, &ymintmp, &ymaxtmp ); ymin = MIN( ymintmp + up, ymin ); ymax = MAX( ymaxtmp + up, ymax ); xmax += xAdvance; // if up/down escape sequences, save current point and adjust baseline; // take the shear into account if ( up != 0. ) doc->osBody() << "gsave " << TRMFLT( up * tt[1] ) << " " << TRMFLT( up * tt[3] ) << " rmoveto\n"; // print the string doc->osBody() << show( (const char *) str ); // back to baseline if ( up != 0. ) doc->osBody() << "grestore " << TRMFLT( xAdvance * tt[0] ) << " " << TRMFLT( xAdvance * tt[2] ) << " rmoveto\n"; } while ( *cur_strp ); doc->osBody() << "grestore\n"; doc->osBody() << "grestore\n"; doc->osBody() << "grestore\n"; // // Estimate text bounding box from LASi get_dimensions function. // xmin, xmax are text left and right extents, // ymin, ymax are top and bottom extents. // These need to be rotated / transformed to get the correct values // xx[0] = (PLINT) ( t[0] * xmin + t[1] * ymin ); yy[0] = (PLINT) ( t[2] * xmin + t[3] * ymin ); xx[1] = (PLINT) ( t[0] * xmin + t[1] * ymax ); yy[1] = (PLINT) ( t[2] * xmin + t[3] * ymax ); xx[2] = (PLINT) ( t[0] * xmax + t[1] * ymin ); yy[2] = (PLINT) ( t[2] * xmax + t[3] * ymin ); xx[3] = (PLINT) ( t[0] * xmax + t[1] * ymax ); yy[3] = (PLINT) ( t[2] * xmax + t[3] * ymax ); plRotPhy( ORIENTATION, 0, 0, 0, 0, &xx[0], &yy[0] ); plRotPhy( ORIENTATION, 0, 0, 0, 0, &xx[1], &yy[1] ); plRotPhy( ORIENTATION, 0, 0, 0, 0, &xx[2], &yy[2] ); plRotPhy( ORIENTATION, 0, 0, 0, 0, &xx[3], &yy[3] ); xmin = MIN( MIN( MIN( xx[0], xx[1] ), xx[2] ), xx[3] ) + args->x; xmax = MAX( MAX( MAX( xx[0], xx[1] ), xx[2] ), xx[3] ) + args->x; ymin = MIN( MIN( MIN( yy[0], yy[1] ), yy[2] ), yy[3] ) + args->y; ymax = MAX( MAX( MAX( yy[0], yy[1] ), yy[2] ), yy[3] ) + args->y; dev->llx = (int) ( MIN( dev->llx, xmin ) ); dev->lly = (int) ( MIN( dev->lly, ymin ) ); dev->urx = (int) ( MAX( dev->urx, xmax ) ); dev->ury = (int) ( MAX( dev->ury, ymax ) ); // doc->osBody() << "Z " << xmin << " " << ymin << " M " // << xmin << " " << ymax << " D " // << xmax << " " << ymax << " D " // << xmax << " " << ymin << " D " // << xmin << " " << ymin << " closepath\n" // << "Z " << args->x << " " << args->y << " A closepath\n"; } } //static void //esc_purge( char *dstr, char *sstr ) //{ // char esc; // // plgesc( &esc ); // // while ( *sstr ) // { // if ( *sstr != esc ) // { // *dstr++ = *sstr++; // continue; // } // // sstr++; // if ( *sstr == esc ) // { // *dstr++ = *sstr++; // continue; // } // // else // { // switch ( *sstr++ ) // { // case 'f': // sstr++; // break; // two chars sequence // // default: // break; // single char escape // } // } // } // *dstr = '\0'; //} #else int pldummy_psttf() { return 0; } #endif // defined(PLD_psttf) || .... plplot-5.13.0/drivers/cgm.driver_info.in000644 001752 001752 00000000032 13150160115 021711 0ustar00softwaresoftware000000 000000 cgm:CGM file:0:cgm:44:cgm plplot-5.13.0/drivers/aqt.driver_info.in000644 001752 001752 00000000045 13150160115 021734 0ustar00softwaresoftware000000 000000 aqt:AquaTerm (Mac OS X):1:aqt:50:aqt plplot-5.13.0/drivers/wxwidgets_dev.cpp000644 001752 001752 00000223306 13150160115 021710 0ustar00softwaresoftware000000 000000 // Copyright (C) 2015-2017 Phil Rosenberg // Copyright (C) 2017 Alan W. Irwin // Copyright (C) 2005 Werner Smekal, Sjaak Verdoold // Copyright (C) 2005 Germain Carrera Corraleche // Copyright (C) 1999 Frank Huebner // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #define DEBUG #define NEED_PLDEBUG // Set this to help when debugging wxPLViewer issues. It uses a memory // map name without random characters and does not execute the viewer, // allowing the user to execute the viewer in a debugger //#define WXPLVIEWER_DEBUG // Headers needed for Rand #ifdef WIN32 // This include must occur before any other include of stdlib.h due to // the #define _CRT_RAND_S #define _CRT_RAND_S #include #else #include #endif // PLplot headers #include "plDevs.h" #include "wxwidgets.h" // includes wx/wx.h // wxwidgets headers #include #include // std and driver headers #include #include // Needed for cerr, etc. #if defined ( WXPLVIEWER_DEBUG ) || defined ( PLPLOT_WX_DEBUG_OUTPUT ) #include #endif //-------------------------------------------------------------------------- // PlDevice::PlDevice() // // Constructor for wxPLDevice //-------------------------------------------------------------------------- PlDevice::PlDevice() { m_prevSymbol = 0; m_prevBaseFontSize = 0.; m_prevLevel = 0; m_prevFci = 0; m_prevSymbolWidth = 0; m_prevSymbolHeight = 0; } //-------------------------------------------------------------------------- // void wxPLDevice::DrawText( PLStream* pls, EscText* args ) // // This is the main function which processes the unicode text strings. // Font size, rotation and color are set, width and height of the // text string is determined and then the string is drawn to the canvas. //-------------------------------------------------------------------------- void PlDevice::drawText( PLStream* pls, EscText* args ) { // Split the text into lines separated by forced linebreak '\n' characters // inserted by the user. typedef std::pair< PLUNICODE *, PLUNICODE *> uniIterPair; PLUNICODE *textEnd = args->unicode_array + args->unicode_array_len; PLUNICODE lf = PLUNICODE( '\n' ); std::vector< uniIterPair > lines( 1, uniIterPair( args->unicode_array, textEnd ) ); for ( PLUNICODE * uni = args->unicode_array; uni != textEnd; ++uni ) { if ( *uni == lf ) { lines.back().second = uni; lines.push_back( uniIterPair( uni + 1, textEnd ) ); } } // Check that we got unicode, warning message and return if not if ( args->unicode_array_len == 0 ) { printf( "Non unicode string passed to the wxWidgets driver, ignoring\n" ); return; } // Check that unicode string isn't longer then the max we allow if ( args->unicode_array_len >= 500 ) { printf( "Sorry, the wxWidgets drivers only handles strings of length < %d\n", 500 ); return; } // Calculate the font size (in pt) // PLplot saves it in mm (bizarre units!) PLFLT baseFontSize = pls->chrht * PLPLOT_POINTS_PER_INCH / PLPLOT_MM_PER_INCH; //initialize the text state PLUNICODE currentFci; plgfci( ¤tFci ); bool currentUnderlined = false; //Get the size of each line. Even for left aligned text //we still need the text height to vertically align text std::vector lineWidths( lines.size() ); std::vector lineHeights( lines.size() ); std::vector lineDepths( lines.size() ); { // Get the text length without drawing it. Also, determine // lineWidths, lineHeights, and lineDepths arrays that are required // for the actual draw. wxCoord paraWidth = 0; wxCoord paraHeight = 0; PLUNICODE testFci = currentFci; bool testUnderlined = currentUnderlined = false; PLFLT identityMatrix[6]; plP_affine_identity( identityMatrix ); for ( size_t i = 0; i < lines.size(); ++i ) { DrawTextLine( lines[i].first, lines[i].second - lines[i].first, 0, 0, 0, 0, identityMatrix, baseFontSize, false, testUnderlined, testFci, 0, 0, 0, 0.0, lineWidths[i], lineHeights[i], lineDepths[i] ); paraWidth = MAX( paraWidth, lineWidths[i] ); paraHeight += lineHeights[i] + lineDepths[i]; } pls->string_length = paraWidth / pls->xpmm; } if ( !pls->get_string_length ) { // Draw the text string if requested by PLplot. The needed lineWidths, // lineHeights, and lineDepths arrays are determined above. wxCoord cumSumHeight = 0; // Plplot doesn't include plot orientation in args->xform, so we must // rotate the text if needed; PLFLT textTransform[6]; PLFLT diorot = pls->diorot - 4.0 * floor( pls->diorot / 4.0 ); //put diorot in range 0-4 textTransform[0] = args->xform[0]; textTransform[2] = args->xform[1]; textTransform[1] = args->xform[2]; textTransform[3] = args->xform[3]; textTransform[4] = 0.0; textTransform[5] = 0.0; PLFLT diorotTransform[6]; if ( diorot == 0.0 ) { diorotTransform[0] = 1; diorotTransform[1] = 0; diorotTransform[2] = 0; diorotTransform[3] = 1; } else if ( diorot == 1.0 ) { diorotTransform[0] = 0; diorotTransform[1] = -1; diorotTransform[2] = 1; diorotTransform[3] = 0; } else if ( diorot == 2.0 ) { diorotTransform[0] = -1; diorotTransform[1] = 0; diorotTransform[2] = 0; diorotTransform[3] = -1; } else if ( diorot == 3.0 ) { diorotTransform[0] = 0; diorotTransform[1] = 1; diorotTransform[2] = -1; diorotTransform[3] = 0; } else { PLFLT angle = diorot * M_PI / 2.0; PLFLT cosAngle = cos( angle ); PLFLT sinAngle = sin( angle ); diorotTransform[0] = cosAngle; diorotTransform[1] = -sinAngle; diorotTransform[2] = sinAngle; diorotTransform[3] = cosAngle; } diorotTransform[4] = 0; diorotTransform[5] = 0; PLFLT finalTransform[6]; memcpy( finalTransform, textTransform, sizeof ( PLFLT ) * 6 ); plP_affine_multiply( finalTransform, textTransform, diorotTransform ); std::vector lineWidths_ignored( lines.size() ); std::vector lineHeights_ignored( lines.size() ); std::vector lineDepths_ignored( lines.size() ); for ( size_t i = 0; i < lines.size(); ++i ) { DrawTextLine( lines[i].first, lines[i].second - lines[i].first, args->x, args->y, -lineWidths[i] * args->just, 0.5 * ( lineHeights[i] ) - cumSumHeight, finalTransform, baseFontSize, true, currentUnderlined, currentFci, pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a, lineWidths_ignored[i], lineHeights_ignored[i], lineDepths_ignored[i] ); // Ignore the ignored versions even though gdb tells me // (AWI) they are the same as the unignored versions // determined above for the DrawText false case (as // expected from inspection of the DrawTextLine code). cumSumHeight += lineHeights[i] + lineDepths[i]; } } } // This function will draw a line of text given by ucs4 with ucs4Len // characters. The ucs4 argument must not contain any newline characters. // basefontSize is the size of a full size character in points. Pass // in underlined flag and fci for the beginning of the line. On // return they will be filled with the values at the end of the line. // On return textWidth, textHeigth and textDepth will be filled with // the width, ascender height and descender depth of the text string. // If drawText is true the text will actually be drawn. If it is // false the size will be calculated but the text will not be // rendered. void PlDevice::DrawTextLine( PLUNICODE* ucs4, int ucs4Len, wxCoord xOrigin, wxCoord yOrigin, wxCoord x, wxCoord y, PLFLT *transform, PLFLT baseFontSize, bool drawText, bool &underlined, PLUNICODE &fci, unsigned char red, unsigned char green, unsigned char blue, PLFLT alpha, wxCoord &textWidth, wxCoord &textHeight, wxCoord &textDepth ) { PLINT level = 0; PLFLT oldScale; PLFLT Scale = 1.; PLFLT scaledFontSize = baseFontSize; PLFLT oldOffset; PLFLT Offset = 0.; PLFLT yScale; PLFLT scaledOffset = 0.; // Factor of 1.2 is an empirical correction to work around a bug // where the calculated symmetrical subscript and superscript // offset arguments of DrawTextSection are rendered in that // routine in an asymmetical way (with subscript levels having a // differently rendered offset than the corresponding superscript // level). Of course, fixing this DrawTextSection bug is far // preferable to this workaround, but I have been unable to find // that bug in DrawTextSection so I am leaving this ultimate fix // until later. PLFLT empiricalSymmetricFactor = 1.2; wxCoord sectionWidth; wxCoord sectionHeight; wxCoord sectionDepth; // check if we have the same symbol as last time - only do this for single characters // (e.g., typical plstring use). if ( !drawText && ucs4Len == 1 && ucs4[0] == m_prevSymbol && baseFontSize == m_prevBaseFontSize && level == m_prevLevel && fci == m_prevFci ) { textWidth = m_prevSymbolWidth; textHeight = m_prevSymbolHeight; textDepth = m_prevSymbolDepth; return; } wxString section; PLFLT sectionTransform[6]; memcpy( sectionTransform, transform, sizeof ( sectionTransform ) ); // Get PLplot escape character char plplotEsc; plgesc( &plplotEsc ); // Reset the size metrics textWidth = 0; textHeight = 0; textDepth = 0; int i = 0; while ( i < ucs4Len ) { if ( ucs4[i] == (PLUNICODE) plplotEsc ) { // We found an escape character. Move to the next character to see what we need to do next ++i; if ( ucs4[i] == (PLUNICODE) plplotEsc ) { // Add the actual escape character to the string section += wxUString( (wxChar32) ucs4[i] ); } else { // We have a change of state. Output the string so far DrawTextSection( section, xOrigin, yOrigin, x + textWidth, y + scaledOffset, transform, scaledFontSize, drawText, underlined, fci, red, green, blue, alpha, yScale, sectionWidth, sectionHeight, sectionDepth ); textWidth += sectionWidth; textHeight = MAX( textHeight, sectionHeight + scaledOffset ); textDepth = MAX( textDepth, sectionDepth - scaledOffset ); section = wxEmptyString; // Act on the escape character if ( ucs4[i] == (PLUNICODE) 'u' ) { // Superscript escape // y, textHeight, and textDepth are all scaled // quantities so any offset-related variable that // is linearly combined with them such as // scaledOffset must be scaled as well. Offset is // always positive so the last factor is to give // scaledOffset the correct sign depending on // level. plP_script_scale( TRUE, &level, &oldScale, &Scale, &oldOffset, &Offset ); scaledFontSize = baseFontSize * Scale; scaledOffset = yScale * Offset * baseFontSize * ( level > 0 ? 1.0 / empiricalSymmetricFactor : -1.0 * empiricalSymmetricFactor ); } else if ( ucs4[i] == (PLUNICODE) 'd' ) { // Subscript escape // y, textHeight, and textDepth are all scaled // quantities so any offset-related variable that // is linearly combined with them such as // scaledOffset must be scaled as well. Offset is // always positive so the last factor is to give // scaledOffset the correct sign depending on // level. plP_script_scale( FALSE, &level, &oldScale, &Scale, &oldOffset, &Offset ); scaledFontSize = baseFontSize * Scale; scaledOffset = yScale * Offset * baseFontSize * ( level > 0 ? 1.0 / empiricalSymmetricFactor : -1.0 * empiricalSymmetricFactor ); } else if ( ucs4[i] == (PLUNICODE) '-' ) // underline underlined = !underlined; else if ( ucs4[i] == (PLUNICODE) '+' ) // overline { // not implemented yet } } } else if ( ucs4[i] >= PL_FCI_MARK ) { // A font change // draw string so far DrawTextSection( section, xOrigin, yOrigin, x + textWidth, y + scaledOffset, transform, scaledFontSize, drawText, underlined, fci, red, green, blue, alpha, yScale, sectionWidth, sectionHeight, sectionDepth ); textWidth += sectionWidth; textHeight = MAX( textHeight, sectionHeight + scaledOffset ); textDepth = MAX( textDepth, sectionDepth - scaledOffset ); section = wxEmptyString; // Get new fci fci = ucs4[i]; } else { // Just a regular character - add it to the string section += wxUString( (wxChar32) ucs4[i] ); } ++i; } // We have reached the end of the string. Draw the last section. DrawTextSection( section, xOrigin, yOrigin, x + textWidth, y + scaledOffset, transform, scaledFontSize, drawText, underlined, fci, red, green, blue, alpha, yScale, sectionWidth, sectionHeight, sectionDepth ); textWidth += sectionWidth; textHeight = MAX( textHeight, sectionHeight + scaledOffset ); textDepth = MAX( textDepth, sectionDepth - scaledOffset ); // If this was a single character remember its size as it is // likely to be requested repeatedly (e.g., typical plstring use). if ( ucs4Len == 1 ) { m_prevSymbol = ucs4[0]; m_prevBaseFontSize = baseFontSize; m_prevLevel = level; m_prevFci = fci; m_prevSymbolWidth = textWidth; m_prevSymbolHeight = textHeight; m_prevSymbolDepth = textDepth; } } //-------------------------------------------------------------------------- // Scaler class // This class changes the logical scale of a dc on construction and resets // it to its original value on destruction. It is ideal for making temporary // changes to the scale and guaranteeing that the scale gets set back. //-------------------------------------------------------------------------- class Scaler { public: Scaler( wxDC * dc, double xScale, double yScale ) { m_dc = dc; if ( m_dc ) { dc->GetUserScale( &m_xScaleOld, &m_yScaleOld ); dc->SetUserScale( xScale, yScale ); } } ~Scaler( ) { if ( m_dc ) m_dc->SetUserScale( m_xScaleOld, m_yScaleOld ); } private: wxDC *m_dc; double m_xScaleOld; double m_yScaleOld; Scaler & operator=( const Scaler & ); Scaler ( const Scaler & ); }; //-------------------------------------------------------------------------- // OriginChanger class // This class changes the logical origin of a dc on construction and resets // it to its original value on destruction. It is ideal for making temporary // changes to the origin and guaranteeing that the scale gets set back. //-------------------------------------------------------------------------- class OriginChanger { public: OriginChanger( wxDC * dc, wxCoord xOrigin, wxCoord yOrigin ) { m_dc = dc; if ( m_dc ) { dc->GetLogicalOrigin( &m_xOriginOld, &m_yOriginOld ); dc->SetLogicalOrigin( xOrigin, yOrigin ); } } ~OriginChanger( ) { if ( m_dc ) m_dc->SetLogicalOrigin( m_xOriginOld, m_yOriginOld ); } private: wxDC *m_dc; wxCoord m_xOriginOld; wxCoord m_yOriginOld; OriginChanger & operator=( const OriginChanger & ); OriginChanger ( const OriginChanger & ); }; //-------------------------------------------------------------------------- // DrawingObjectsChanger class // This class changes the pen and brush of a dc on construction and resets // them to their original values on destruction. It is ideal for making temporary // changes to the pen and brush and guaranteeing that they get set back. //-------------------------------------------------------------------------- class DrawingObjectsChanger { public: DrawingObjectsChanger( wxDC *dc, const wxPen &pen, const wxBrush &brush ) { m_dc = dc; if ( m_dc ) { m_pen = dc->GetPen(); m_brush = dc->GetBrush(); dc->SetPen( pen ); dc->SetBrush( brush ); } } ~DrawingObjectsChanger() { if ( m_dc ) { m_dc->SetPen( m_pen ); m_dc->SetBrush( m_brush ); } } private: wxDC *m_dc; wxPen m_pen; wxBrush m_brush; DrawingObjectsChanger & operator=( const DrawingObjectsChanger & ); DrawingObjectsChanger ( const DrawingObjectsChanger & ); }; //-------------------------------------------------------------------------- //TextObjectsSaver class //This class saves the text rendering details of a dc on construction and //resets them to their original values on destruction. It can be used to //ensure the restoration of state when a scope is exited //-------------------------------------------------------------------------- class TextObjectsSaver { public: TextObjectsSaver( wxDC *dc ) { m_dc = dc; if ( m_dc ) { m_font = dc->GetFont(); m_textForeground = dc->GetTextForeground(); m_textBackground = dc->GetTextBackground(); } } ~TextObjectsSaver() { if ( m_dc ) { m_dc->SetTextForeground( m_textForeground ); m_dc->SetTextBackground( m_textBackground ); m_dc->SetFont( m_font ); } } private: wxDC *m_dc; wxFont m_font; wxColour m_textForeground; wxColour m_textBackground; TextObjectsSaver & operator=( const TextObjectsSaver & ); TextObjectsSaver ( const TextObjectsSaver & ); }; //-------------------------------------------------------------------------- // TextObjectsChanger class // This class changes the font and text colours of a dc on construction and resets // them to their original values on destruction. It is ideal for making temporary // changes to the text and guaranteeing that they get set back. //-------------------------------------------------------------------------- class TextObjectsChanger { public: TextObjectsChanger( wxDC *dc, const wxFont &font, const wxColour &textForeground, const wxColour &textBackground ) : m_saver( dc ) { if ( dc ) { dc->SetTextForeground( textForeground ); dc->SetTextBackground( textBackground ); dc->SetFont( font ); } } TextObjectsChanger( wxDC *dc, const wxFont &font ) : m_saver( dc ) { if ( dc ) dc->SetFont( font ); } TextObjectsChanger( wxDC *dc, FontGrabber &fontGrabber, PLUNICODE fci, PLFLT size, bool underlined, const wxColour &textForeground, const wxColour &textBackground ) : m_saver( dc ) { if ( dc ) { wxFont font = fontGrabber.GetFont( fci, size, underlined ).getWxFont(); dc->SetTextForeground( textForeground ); dc->SetTextBackground( textBackground ); dc->SetFont( font ); } } private: TextObjectsSaver m_saver; TextObjectsChanger & operator=( const TextObjectsChanger & ); TextObjectsChanger ( const TextObjectsChanger & ); }; //-------------------------------------------------------------------------- // Clipper class // This class changes the clipping region of a dc on construction and restores // it to its previous region on destruction. It is ideal for making temporary // changes to the clip region and guaranteeing that the scale gets set back. // // It turns out that clipping is mostly broken for wxGCDC - see // http://trac.wxwidgets.org/ticket/17013. So there are a lot of things in // this class to work around those bugs. In particular you should check // isEveryThingClipped before drawing as I'm not sure if non-overlapping //clip regions behave properly. //-------------------------------------------------------------------------- class Clipper { public: Clipper( wxDC * dc, const wxRect &rect ) { m_dc = dc; m_clipEverything = true; if ( m_dc ) { dc->GetClippingBox( m_boxOld ); wxRect newRect = rect; m_clipEverything = !( newRect.Intersects( m_boxOld ) || ( m_boxOld.width == 0 && m_boxOld.height == 0 ) ); if ( m_clipEverything ) dc->SetClippingRegion( wxRect( -1, -1, 1, 1 ) ); //not sure if this works else dc->SetClippingRegion( rect ); } } ~Clipper( ) { if ( m_dc ) { m_dc->DestroyClippingRegion(); m_dc->SetClippingRegion( wxRect( 0, 0, 0, 0 ) ); m_dc->DestroyClippingRegion(); if ( m_boxOld.width != 0 && m_boxOld.height != 0 ) m_dc->SetClippingRegion( m_boxOld ); } } bool isEverythingClipped() { return m_clipEverything; } private: wxDC *m_dc; wxRect m_boxOld; bool m_clipEverything; Clipper & operator=( const Clipper & ); Clipper ( const Clipper & ); }; //-------------------------------------------------------------------------- // class Rand // This is a simple random number generator class, created solely so that // random numbers can be generated in this file without "contaminating" the // global series of random numbers with a new seed. // It uses an algorithm that apparently used to be used in gcc rand() // provided under GNU LGPL v2.1. //-------------------------------------------------------------------------- class Rand { public: Rand() { #ifdef WIN32 rand_s( &m_seed ); #else std::fstream fin( "/dev/urandom", std::ios::in ); if ( fin.is_open() ) fin.read( (char *) ( &m_seed ), sizeof ( m_seed ) ); else { fin.clear(); fin.open( "/dev/random", std::ios::in ); if ( fin.is_open() ) fin.read( (char *) ( &m_seed ), sizeof ( m_seed ) ); else m_seed = 0; } fin.close(); #endif } Rand( unsigned int seed ) { m_seed = seed; } unsigned int operator()() { unsigned int next = m_seed; int result; next *= 1103515245; next += 12345; result = (unsigned int) ( next / max ) % 2048; next *= 1103515245; next += 12345; result <<= 10; result ^= (unsigned int) ( next / max ) % 1024; next *= 1103515245; next += 12345; result <<= 10; result ^= (unsigned int) ( next / max ) % 1024; m_seed = next; return result; } static const unsigned int max = 65536; private: unsigned int m_seed; }; void plFontToWxFontParameters( PLUNICODE fci, PLFLT scaledFontSize, wxFontFamily &family, int &style, int &weight, int &pt ) { unsigned char plFontFamily, plFontStyle, plFontWeight; plP_fci2hex( fci, &plFontFamily, PL_FCI_FAMILY ); plP_fci2hex( fci, &plFontStyle, PL_FCI_STYLE ); plP_fci2hex( fci, &plFontWeight, PL_FCI_WEIGHT ); family = fontFamilyLookup[plFontFamily]; style = fontStyleLookup[plFontStyle]; weight = fontWeightLookup[plFontWeight]; pt = ROUND( scaledFontSize ); } Font::Font ( ) { m_fci = 0; m_size = std::numeric_limits::quiet_NaN(); m_underlined = false; m_hasFont = false; } Font::Font( PLUNICODE fci, PLFLT size, bool underlined, bool createFontOnConstruction ) { m_fci = fci; m_size = size; m_underlined = underlined; m_hasFont = false; if ( createFontOnConstruction ) createFont(); } void Font::createFont() { wxFontFamily family; int style; int weight; int pt; plFontToWxFontParameters( m_fci, m_size, family, style, weight, pt ); m_font = wxFont( pt, family, style, weight, m_underlined, wxEmptyString, wxFONTENCODING_DEFAULT ); //wxWidgets has a feature where wxDEFAULT can be passed in as the size in the constructor //which gives the default size for the system. Annoyingly wxDEFAULT is 70 which can get used //as an actual size. The workaround as per http://trac.wxwidgets.org/ticket/12315 is to call //wxFont::SetPointSize after construction. if ( pt == wxDEFAULT ) m_font.SetPointSize( pt ); m_hasFont = true; } wxFont Font::getWxFont() { if ( !m_hasFont ) createFont(); return m_font; } bool operator ==( const Font &lhs, const Font &rhs ) { return lhs.getFci() == rhs.getFci() && lhs.getSize() == rhs.getSize() && lhs.getUnderlined() == rhs.getUnderlined(); } //-------------------------------------------------------------------------- // FontGrabber::FontGrabber( ) // // Default constructor //-------------------------------------------------------------------------- FontGrabber::FontGrabber( ) { m_lastWasCached = false; } //-------------------------------------------------------------------------- // Font FontGrabber::GetFont( PLUNICODE fci ) // // Get the requested font either fresh or from the cache. //-------------------------------------------------------------------------- Font FontGrabber::GetFont( PLUNICODE fci, PLFLT scaledFontSize, bool underlined ) { Font newFont( fci, scaledFontSize, underlined ); if ( m_prevFont == newFont ) { m_lastWasCached = true; return m_prevFont; } m_lastWasCached = false; return m_prevFont = newFont; } //-------------------------------------------------------------------------- // wxPLDevice::wxPLDevice( void ) // // Constructor of the standard wxWidgets device based on the wxPLDevBase // class. Only some initialisations are done. //-------------------------------------------------------------------------- wxPLDevice::wxPLDevice( PLStream *pls, char * mfo, PLINT text, PLINT hrshsym ) : m_plplotEdgeLength( PLFLT( SHRT_MAX ) ), m_interactiveTextImage( 1, 1 ) { PLPLOT_wxLogDebug( "wxPLDevice(): enter" ); m_fixedAspect = false; m_lineSpacing = 1.0; m_dc = NULL; wxGraphicsContext *gc = wxGraphicsContext::Create( m_interactiveTextImage ); PLPLOT_wxLogDebug( "wxPLDevice(): gc done" ); try { m_interactiveTextGcdc = new wxGCDC( gc ); } catch ( ... ) { delete gc; throw( "wxPLDevice::wxPLDevice: unknown failure in new wxGCDC( gc )" ); } PLPLOT_wxLogDebug( "wxPLDevice(): m_interactiveTextGcdc done" ); if ( mfo ) strcpy( m_mfo, mfo ); else //assume we will be outputting to the default //memory map until we are given a dc to draw to #ifdef WXPLVIEWER_DEBUG strcpy( m_mfo, "plplotMemoryMap" ); #else strcpy( m_mfo, "plplotMemoryMap??????????" ); #endif // be verbose and write out debug messages #ifdef _DEBUG pls->verbose = 1; pls->debug = 1; #endif pls->color = 1; // Is a color device pls->dev_flush = 1; // Handles flushes pls->dev_fill0 = 1; // Can handle solid fills pls->dev_fill1 = 0; // Can't handle pattern fills pls->dev_dash = 0; pls->dev_clear = 1; // driver supports clear pls->plbuf_write = 1; // use the plot buffer! pls->termin = ( strlen( m_mfo ) ) > 0 ? 0 : 1; // interactive device unless we are writing to memory - pretty sure this is an unused option though pls->graphx = GRAPHICS_MODE; // This indicates this is a graphics driver. PLplot will therefore call pltext() before outputting text, however we currently have not implemented catching that text. if ( text ) { pls->dev_text = 1; // want to draw text pls->dev_unicode = 1; // want unicode if ( hrshsym ) pls->dev_hrshsym = 1; } // Set up physical limits of plotting device in plplot internal units // we just tell plplot we are the maximum plint in both dimensions //which gives us the best resolution plP_setphy( (PLINT) 0, (PLINT) SHRT_MAX, (PLINT) 0, (PLINT) SHRT_MAX ); // set dpi and page size defaults if the user has not already set // these with -dpi or -geometry command line options or with // plspage. if ( pls->xdpi <= 0. || pls->ydpi <= 0. ) { // Use recommended default pixels per inch. plspage( PLPLOT_DEFAULT_PIXELS_PER_INCH, PLPLOT_DEFAULT_PIXELS_PER_INCH, 0, 0, 0, 0 ); } if ( pls->xlength == 0 || pls->ylength == 0 ) { // Use recommended default pixel width and height. plspage( 0., 0., PLPLOT_DEFAULT_WIDTH_PIXELS, PLPLOT_DEFAULT_HEIGHT_PIXELS, 0, 0 ); } m_localBufferPosition = 0; SetSize( pls, plsc->xlength, plsc->ylength ); if ( pls->dev_data ) { SetDC( pls, (wxDC *) pls->dev_data ); PLPLOT_wxLogDebug( "wxPLDevice(): SetDC done" ); } else { SetupMemoryMap(); } PLPLOT_wxLogDebug( "wxPLDevice(): leave" ); //this must be the absolute last thing that is done //so that if an exception is thrown pls->dev remains as NULL pls->dev = (void *) this; } //-------------------------------------------------------------------------- // wxPLDevice::~wxPLDevice( void ) // // The destructor frees memory allocated by the device. //-------------------------------------------------------------------------- wxPLDevice::~wxPLDevice() { if ( m_outputMemoryMap.isValid() ) { #ifdef PL_WXWIDGETS_IPC3 m_header.completeFlag = 1; TransmitBuffer( NULL, transmissionComplete ); #else MemoryMapHeader *header = (MemoryMapHeader *) ( m_outputMemoryMap.getBuffer() ); header->completeFlag = 1; #endif } delete m_interactiveTextGcdc; } //-------------------------------------------------------------------------- // void wxPLDevice::PreDestructorTidy( PLStream *pls ) // // This function performs any tidying up that requires a PLStream. should // be called before the destructor obviously //-------------------------------------------------------------------------- void wxPLDevice::PreDestructorTidy( PLStream *pls ) { if ( !m_dc && pls->nopause ) TransmitBuffer( pls, transmissionClose ); } //-------------------------------------------------------------------------- // void wxPLDevice::DrawLine( short x1a, short y1a, short x2a, short y2a ) // // Draw a line from (x1a, y1a) to (x2a, y2a). //-------------------------------------------------------------------------- void wxPLDevice::DrawLine( short x1a, short y1a, short x2a, short y2a ) { if ( !m_dc ) return; Clipper clipper( m_dc, GetClipRegion().GetBox() ); Scaler scaler( m_dc, 1.0 / m_scale, 1.0 / m_scale ); DrawingObjectsChanger drawingObjectsChanger( m_dc, m_pen, m_brush ); m_dc->DrawLine( (wxCoord) ( m_xAspect * x1a ), (wxCoord) ( m_yAspect * ( m_plplotEdgeLength - y1a ) ), (wxCoord) ( m_xAspect * x2a ), (wxCoord) ( m_yAspect * ( m_plplotEdgeLength - y2a ) ) ); } //-------------------------------------------------------------------------- // void wxPLDevice::DrawPolyline( short *xa, short *ya, PLINT npts ) // // Draw a poly line - coordinates are in the xa and ya arrays. //-------------------------------------------------------------------------- void wxPLDevice::DrawPolyline( short *xa, short *ya, PLINT npts ) { if ( !m_dc ) return; Clipper clipper( m_dc, GetClipRegion().GetBox() ); Scaler scaler( m_dc, 1.0 / m_scale, 1.0 / m_scale ); DrawingObjectsChanger drawingObjectsChanger( m_dc, m_pen, m_brush ); for ( PLINT i = 1; i < npts; i++ ) m_dc->DrawLine( m_xAspect * xa[i - 1], m_yAspect * ( m_plplotEdgeLength - ya[i - 1] ), m_xAspect * xa[i], m_yAspect * ( m_plplotEdgeLength - ya[i] ) ); } //-------------------------------------------------------------------------- // void wxPLDevice::ClearBackground( PLStream* pls, PLINT bgr, PLINT bgg, PLINT bgb, // PLINT x1, PLINT y1, PLINT x2, PLINT y2 ) // // Clear parts ((x1,y1) to (x2,y2)) of the background in color (bgr,bgg,bgb). //-------------------------------------------------------------------------- void wxPLDevice::ClearBackground( PLStream* pls, PLINT x1, PLINT y1, PLINT x2, PLINT y2 ) { if ( !m_dc ) return; x1 = x1 < 0 ? 0 : x1; x2 = x2 < 0 ? m_plplotEdgeLength : x2; y1 = y1 < 0 ? 0 : y1; y2 = y2 < 0 ? m_plplotEdgeLength : y2; PLINT x = MIN( x1, x2 ); PLINT y = MIN( y1, y2 ); PLINT width = abs( x1 - x2 ); PLINT height = abs( y1 - y2 ); if ( width > 0 && height > 0 ) { PLINT r, g, b; PLFLT a; plgcolbga( &r, &g, &b, &a ); wxColour bgColour( r, g, b, a * 255 ); DrawingObjectsChanger changer( m_dc, wxPen( bgColour, 0 ), wxBrush( bgColour ) ); m_dc->DrawRectangle( x, y, width, height ); } } //-------------------------------------------------------------------------- // void wxPLDevice::FillPolygon( PLStream *pls ) // // Draw a filled polygon. //-------------------------------------------------------------------------- void wxPLDevice::FillPolygon( PLStream *pls ) { if ( !m_dc ) return; //edge the polygon with a 0.5 pixel line to avoid seams. This is a //bit of a bodge really but this is a difficult problem wxPen edgePen( m_brush.GetColour(), m_scale, wxSOLID ); DrawingObjectsChanger changer( m_dc, edgePen, m_brush ); //DrawingObjectsChanger changer(m_dc, wxNullPen, m_brush ); Scaler scaler( m_dc, 1.0 / m_scale, 1.0 / m_scale ); wxPoint *points = new wxPoint[pls->dev_npts]; wxCoord xoffset = 0; wxCoord yoffset = 0; for ( int i = 0; i < pls->dev_npts; i++ ) { points[i].x = (int) ( m_xAspect * pls->dev_x[i] ); points[i].y = (int) ( m_yAspect * ( m_plplotEdgeLength - pls->dev_y[i] ) ); } if ( pls->dev_eofill ) { m_dc->DrawPolygon( pls->dev_npts, points, xoffset, yoffset, wxODDEVEN_RULE ); } else { m_dc->DrawPolygon( pls->dev_npts, points, xoffset, yoffset, wxWINDING_RULE ); } delete[] points; } //-------------------------------------------------------------------------- // void wxPLDevice::SetWidth( PLStream *pls ) // // Set the width of the drawing pen. //-------------------------------------------------------------------------- void wxPLDevice::SetWidth( PLStream *pls ) { PLFLT width = ( pls->width > 0.0 ? pls->width : 1.0 ) * m_scale; m_pen = wxPen( wxColour( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a * 255 ), width, wxSOLID ); } //-------------------------------------------------------------------------- // void wxPLDevice::SetColor( PLStream *pls ) // // Set color from PLStream. //-------------------------------------------------------------------------- void wxPLDevice::SetColor( PLStream *pls ) { PLFLT width = ( pls->width > 0.0 ? pls->width : 1.0 ) * m_scale; m_pen = wxPen( wxColour( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a * 255 ), width, wxSOLID ); m_brush = wxBrush( wxColour( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a * 255 ) ); } //-------------------------------------------------------------------------- // void wxPLDevice::SetDC( PLStream *pls, void* dc ) // // Adds a dc to the device. In that case, the drivers doesn't provide // a GUI. //-------------------------------------------------------------------------- void wxPLDevice::SetDC( PLStream *pls, wxDC* dc ) { if ( m_outputMemoryMap.isValid() ) throw( "wxPLDevice::SetDC The DC must be set before initialisation. The device is outputting to a separate viewer" ); m_dc = dc; // Add the dc to the device m_useDcTextTransform = false; m_gc = NULL; if ( m_dc ) { #if wxVERSION_NUMBER >= 2902 m_useDcTextTransform = m_dc->CanUseTransformMatrix(); #endif //Prior to some point in wxWidgets 3.1 development wxGCDC didn't //support transformation matrices, but the underlying //wxGraphicsContext had its own transformation matrix ability. //So check if we are using a wxGCDC using RTTI and if so we can //use this. wxGCDC *gcdc = NULL; try { //put this in a try block as I'm not sure if it will throw if //RTTI is switched off gcdc = dynamic_cast< wxGCDC* >( m_dc ); } catch ( ... ) { throw( "unknown failure in dynamic_cast< wxGCDC* >( m_dc )" ); } if ( gcdc ) m_gc = gcdc->GetGraphicsContext(); strcpy( m_mfo, "" ); SetSize( pls, m_width, m_height ); //call with our current size to set the scaling pls->has_string_length = 1; // Driver supports string length calculations, if we have a dc to draw on } else { pls->has_string_length = 0; //if we have no device to draw on we cannot check string size } } // This function will draw a section of text given by section at location // x, y relative to the origin xOrigin, yOrigin. The text must not // contain any newline characters or PLplot escapes. // transform is a transformation to be applied to the device context AFTER // the origin of the device context has been moved to xOrigin, yOrigin. The // purpose of having this origin is to allow the units used by wxWidgets to // be different to those passed in and for the scaling factors to be // different in each direction. // scaledFontSize is the font size in pt after any scaling for super/sub script. // The underlined flag, overlined flag, fci and text colours are inputs defining // the text style. // On return yScale, textWidth, textHeigth and textDepth will be // filled with the scaling value used for the Y dimension and the // (positive) width, (positive) ascender height and (positive) // descender depth of the text string. // If drawText is true the text will actually be rendered. If it is false the size // will be calculated but the text will not be rendered. //-------------------------------------------------------------------------- void wxPLDevice::DrawTextSection( wxString section, wxCoord xOrigin, wxCoord yOrigin, wxCoord x, wxCoord y, PLFLT *transform, PLFLT scaledFontSize, bool drawText, bool underlined, PLUNICODE fci, unsigned char red, unsigned char green, unsigned char blue, PLFLT alpha, PLFLT &yScale, wxCoord §ionWidth, wxCoord §ionHeight, wxCoord §ionDepth ) { //for text, work in native coordinates, because it is significantly easier when //dealing with superscripts and text chunks. //The scaler object sets the scale to the new value until it is destroyed //when this function exits. Scaler scaler( m_dc, 1.0, 1.0 ); //Also move the origin back to the top left, rather than the bottom OriginChanger originChanger( m_dc, 0, wxCoord( m_height - m_plplotEdgeLength / m_yScale ) ); //save the text state for automatic restoration on scope exit TextObjectsSaver textObjectsSaver( m_dc ); wxCoord leading; Font font = m_fontGrabber.GetFont( fci, scaledFontSize, underlined ); // Adjusted so that utf8 "heavy multiplication x", "number sign", // "four teardrop-spoked asterisk", and "8-spoked asterisk" are // aligned properly when running // // python (or python3 as appropriate) examples/python/test_circle.py -dev wxwidgets // PLFLT empiricalYOffset = -0.020 * scaledFontSize * m_yScale; wxCoord empiricalY = y + empiricalYOffset; // Return via the yScale argument the value used for scaling in the Y direction. yScale = m_yScale; if ( m_dc ) { wxFont theFont = font.getWxFont(); // gdb sessions typically show something like the following: // 4: sectionWidth = (wxCoord &) @0x7fffffffd6ec: 7 // 3: sectionHeight = (wxCoord &) @0x7fffffffd6e8: 13 // 2: sectionDepth = (wxCoord &) @0x7fffffffd6e4: 3 // 1: leading = 0 // That is, sectionWidth, sectionHeight, and sectionDepth // returned by GetTextExtent are all normally small positive integers while leading // returned by the same call is typically zero. m_dc->GetTextExtent( section, §ionWidth, §ionHeight, §ionDepth, &leading, &theFont ); } else { wxFont theFont = font.getWxFont(); // See comments above concerning interpretation of GetTextExtent results. m_interactiveTextGcdc->GetTextExtent( section, §ionWidth, §ionHeight, §ionDepth, &leading, &theFont ); } // The leading value that is returned is a vertical offset used by // some font designers, but I (AWI) can find no documentation // concerning the sign convention for leading so I simply follow // here the convention previously used by this code. // Note, that all fonts I have tried have zero leading so (a) this // adopted sign convention does not matter for them, and (b) if it // is wrong here, there is no chance to debug it by looking at the // vertical offset of rendered strings. sectionHeight -= leading; sectionDepth += leading; sectionWidth *= m_xScale; sectionHeight *= m_yScale; sectionDepth *= m_yScale; // Draw the text if requested if ( drawText && m_dc ) { //set the text colour m_dc->SetTextBackground( wxColour( red, green, blue, alpha * 255 ) ); m_dc->SetTextForeground( wxColour( red, green, blue, alpha * 255 ) ); // Set the clipping limits PLINT rcx[4], rcy[4]; difilt_clip( rcx, rcy ); wxPoint cpoints[4]; for ( int i = 0; i < 4; i++ ) { cpoints[i].x = rcx[i] / m_xScale; cpoints[i].y = m_height - rcy[i] / m_yScale; } Clipper clipper( m_dc, wxRegion( 4, cpoints ).GetBox() ); Font font = m_fontGrabber.GetFont( fci, scaledFontSize, underlined ); m_dc->SetFont( font.getWxFont() ); if ( m_useDcTextTransform ) { #if wxVERSION_NUMBER >= 2902 wxAffineMatrix2D originalDcMatrix = m_dc->GetTransformMatrix(); wxAffineMatrix2D newMatrix = originalDcMatrix; newMatrix.Translate( xOrigin / m_xScale, m_height - yOrigin / m_yScale ); wxAffineMatrix2D textMatrix; // For some reason we don't do the mirroring like in the // wxGraphicsContext when we use a wxDC. PLFLT xTransform = transform[4] / m_xScale; PLFLT yTransform = transform[5] / m_yScale; textMatrix.Set( wxMatrix2D( transform[0], transform[2], transform[1], transform[3] ), wxPoint2DDouble( xTransform, yTransform ) ); newMatrix.Concat( textMatrix ); m_dc->SetTransformMatrix( newMatrix ); m_dc->DrawText( section, x / m_xScale, -empiricalY / m_yScale ); m_dc->SetTransformMatrix( originalDcMatrix ); #endif } else if ( m_gc ) { wxGraphicsMatrix originalGcMatrix = m_gc->GetTransform(); m_gc->Translate( xOrigin / m_xScale, m_height - yOrigin / m_yScale ); //move to text starting position //Create a wxGraphicsMatrix from our plplot transformation matrix. //Note the different conventions //1) plplot transforms use notation x' = Mx, where x and x' are column vectors, // wxGraphicsContext uses xM = x' where x and x' are row vectors. This means // we must transpose the matrix. //2) plplot Affine matrices a represented by 6 values which start at the top left // and work down each column. The 3rd row is implied as 0 0 1. wxWidget matrices // are represented by 6 values which start at the top left and work along each // row. The 3rd column is implied as 0 0 1. This means we must transpose the // matrix. //3) Items 1 and 2 cancel out so we don't actually need to do anything about them. //4) The wxGraphicsContext has positive y in the downward direction, but plplot // has positive y in the upwards direction. This means we must do a reflection // in the y direction before and after applying the transform. Also we must scale // the translation parts to match the pixel scale. //The overall transform is // // |1 0 0| |transform[0] transform[2] transform[4]/m_xScale| |1 0 0| // |0 -1 0| |transform[1] transform[3] transform[5]/m_yScale| |0 -1 0| // |0 0 1| | 0 0 1 | |0 0 1| // //which results in // // | transform[0] -transform[2] 0| // |-transform[1] transform[3] 0| // | transform[4]/m_xScale -transform[5]/m_yScale 1| // PLFLT xTransform = transform[4] / m_xScale; PLFLT yTransform = transform[5] / m_yScale; wxGraphicsMatrix matrix = m_gc->CreateMatrix( transform[0], -transform[1], -transform[2], transform[3], xTransform, -yTransform ); wxGraphicsMatrix reflectMatrix = m_gc->CreateMatrix(); m_gc->ConcatTransform( matrix ); m_gc->DrawText( section, x / m_xScale, -empiricalY / m_yScale ); m_gc->SetTransform( originalGcMatrix ); } else { // If we are stuck with a wxDC that has no transformation // abilities then all we can really do is rotate the text // - this is a bit of a poor state really, but to be // honest it is better than defaulting to Hershey for all // text PLFLT xTransformed = x / m_xScale * transform[0] + empiricalY / m_yScale * transform[2] + transform[4] / m_xScale; PLFLT yTransformed = x / m_xScale * transform[1] + empiricalY / m_yScale * transform[3] + transform[4] / m_xScale; // This angle calculation comes from transforming the // point (0,0) and any other point on the empiricalY = 0 line and // getting the angle from the horizontal of that line. PLFLT angle = atan2( transform[1], transform[0] ) * 180.0 / M_PI; m_dc->DrawRotatedText( section, xOrigin / m_xScale + xTransformed, m_height - yOrigin / m_yScale - yTransformed, angle ); } } } //-------------------------------------------------------------------------- // void wxPLDevice::EndPage( PLStream* pls ) // End the page. This is the point that we write the buffer to the memory // mapped file if needed //-------------------------------------------------------------------------- void wxPLDevice::EndPage( PLStream* pls ) { if ( !m_dc ) { if ( pls->nopause ) TransmitBuffer( pls, transmissionEndOfPageNoPause ); else TransmitBuffer( pls, transmissionEndOfPage ); return; } } //-------------------------------------------------------------------------- // void wxPLDevice::BeginPage( PLStream* pls ) // Sets up for transfer in case it is needed and sets the current state //-------------------------------------------------------------------------- void wxPLDevice::BeginPage( PLStream* pls ) { if ( !m_dc ) { m_localBufferPosition = 0; TransmitBuffer( NULL, transmissionBeginPage ); } // Get the starting colour, width and font from the stream SetWidth( pls ); SetColor( pls ); //clear the page ClearBackground( pls ); } //-------------------------------------------------------------------------- // void wxPLDevice::SetSize( PLStream* pls ) // Set the size of the page, scale parameters and the dpi //-------------------------------------------------------------------------- void wxPLDevice::SetSize( PLStream* pls, int width, int height ) { //we call BeginPage, before we fiddle with fixed aspect so that the //whole background gets filled // get boundary coordinates in plplot units PLINT xmin; PLINT xmax; PLINT ymin; PLINT ymax; plP_gphy( &xmin, &xmax, &ymin, &ymax ); //split the scaling into an overall scale, the same in both dimensions //and an aspect part which differs in both directions. //We will apply the aspect ratio part, and let the DC do the overall //scaling. This gives us subpixel accuracy, but ensures line thickness //remains consistent in both directions m_xScale = width > 0 ? (PLFLT) ( xmax - xmin ) / (PLFLT) width : 0.0; m_yScale = height > 0 ? (PLFLT) ( ymax - ymin ) / (PLFLT) height : 0.0; m_scale = MIN( m_xScale, m_yScale ); if ( !m_fixedAspect ) { m_xAspect = m_scale / m_xScale; m_yAspect = m_scale / m_yScale; } else { //now sort out the fixed aspect and reset the logical scale if needed if ( PLFLT( height ) / PLFLT( width ) > m_yAspect / m_xAspect ) { m_scale = m_xScale * m_xAspect; m_yScale = m_scale / m_yAspect; } else { m_scale = m_yScale * m_yAspect; m_xScale = m_scale / m_xAspect; } } m_width = ( xmax - xmin ) / m_xScale; pls->xlength = PLINT( m_width + 0.5 ); m_height = ( ymax - ymin ) / m_yScale; pls->ylength = PLINT( m_height + 0.5 ); // Set the number of plplot pixels per mm plP_setpxl( m_plplotEdgeLength / m_width * pls->xdpi / PLPLOT_MM_PER_INCH, m_plplotEdgeLength / m_height * pls->ydpi / PLPLOT_MM_PER_INCH ); // //The line above is technically correct, however, 3d text only looks at device dimensions (32767x32767 square) //but 2d rotated text uses the mm size derived above. The only way to consistently deal with them is //by having an equal device units per mm in both directions and do a correction in DrawText(). //Usefully this also allows us to change text rotation as aspect ratios change //PLFLT size = m_xAspect > m_yAspect ? m_width : m_height; //plP_setpxl( m_plplotEdgeLength / size * pls->xdpi / PLPLOT_MM_PER_INCH, m_plplotEdgeLength / size * pls->ydpi / PLPLOT_MM_PER_INCH ); // redraw the plot if ( m_dc && pls->plbuf_buffer ) plreplot(); } void wxPLDevice::FixAspectRatio( bool fix ) { m_fixedAspect = fix; } void wxPLDevice::Flush( PLStream *pls ) { if ( !m_dc ) #ifdef PL_WXWIDGETS_IPC3 TransmitBuffer( pls, transmissionFlush ); #else TransmitBuffer( pls, transmissionComplete ); #endif } // This function transmits data to the gui program via a memory map. // This function can be called with pls set to NULL for transmission // of just a flag for e.g. page end or begin. void wxPLDevice::TransmitBuffer( PLStream* pls, unsigned char transmissionType ) { if ( !m_outputMemoryMap.isValid() ) return; #ifdef PL_WXWIDGETS_IPC3 // New much cleaner variant of this code which makes use of two // additional members of the MemoryMapHeader called transmissionType // and plbufAmountToTransmit which contain what their names imply. try { m_header.transmissionType = transmissionType; // This value may be zeroed below for those transmissionTypes which require // that no part of plbuf should be transmitted. m_header.plbufAmountToTransmit = pls ? pls->plbuf_top - m_localBufferPosition : 0; switch ( transmissionType ) { // Special valid cases. case transmissionLocate: m_header.locateModeFlag = 1; break; // N.B. These transmissionTypes require // that no part of plbuf should be transmitted. case transmissionRequestTextSize: case transmissionClose: m_header.plbufAmountToTransmit = 0; break; // Generic valid cases where nothing special has to be done case transmissionBeginPage: case transmissionEndOfPage: case transmissionEndOfPageNoPause: case transmissionComplete: case transmissionFlush: break; // Invalid cases. default: throw( "wxPLDevice::TransmitBuffer: called with invalid value of transmissionType" ); break; } #ifdef PLPLOT_WX_DEBUG_OUTPUT std::cerr << "Before transmitBytes" << std::endl; std::cerr << "transmissionType = " << static_cast( m_header.transmissionType ) << std::endl; std::cerr << "plbufAmountToTransmit = " << m_header.plbufAmountToTransmit << std::endl; std::cerr << "viewerOpenFlag = " << m_header.viewerOpenFlag << std::endl; std::cerr << "locateModeFlag = " << m_header.locateModeFlag << std::endl; std::cerr << "completeFlag = " << m_header.completeFlag << std::endl; #endif // #ifdef PLPLOT_WX_DEBUG_OUTPUT m_outputMemoryMap.transmitBytes( true, &m_header, sizeof ( MemoryMapHeader ) ); if ( m_header.plbufAmountToTransmit > 0 ) { // N.B. the above condition implies pls is non-NULL. // Transmit m_header.plbufAmountToTransmit bytes of plbuf to the reader process. m_outputMemoryMap.transmitBytes( false, (char *) pls->plbuf_buffer + m_localBufferPosition, m_header.plbufAmountToTransmit ); m_localBufferPosition += m_header.plbufAmountToTransmit; } } // End of try block catch ( const char *message ) { plwarn( message ); plwarn( "wxPLDevice::TransmitBuffer: error" ); } catch ( ... ) { plwarn( "wxPLDevice::TransmitBuffer: Unknown error" ); } #else // #ifdef PL_WXWIDGETS_IPC3 // Amount of plbuf buffer to copy. size_t amountToCopy = pls ? pls->plbuf_top - m_localBufferPosition : 0; const size_t headerSize = sizeof ( transmissionType ) + sizeof ( size_t ); bool first = true; size_t counter = 0; const size_t counterLimit = 10000; bool completed = false; while ( !completed && counter < counterLimit ) { //if we are doing multiple loops then pause briefly before we //lock to give the reading application a chance to spot the //change. if ( !first ) wxMilliSleep( 10 ); first = false; size_t copyAmount = 0; size_t freeSpace = 0; //lock the mutex so reading and writing don't overlap try { //PLNamedMutexLocker lock( &m_mutex ); MemoryMapHeader & mapHeader = *(MemoryMapHeader *) m_outputMemoryMap.getBuffer(); //check how much free space we have before the end of the buffer //or if we have looped round how much free space we have before //we reach the read point freeSpace = m_outputMemoryMap.getSize() - mapHeader.writeLocation; // if readLocation is at the beginning then don't quite fill up the buffer if ( mapHeader.readLocation == plMemoryMapReservedSpace ) --freeSpace; //if the free space left in the file is less than that needed for the header then //just tell the GUI to skip the rest of the file so it can start again at the //beginning of the file. if ( freeSpace <= headerSize ) { if ( mapHeader.readLocation > mapHeader.writeLocation ) //don't overtake the read buffer freeSpace = 0; else if ( mapHeader.readLocation == plMemoryMapReservedSpace ) // don't catch up exactly with the read buffer freeSpace = 0; else { //send a skip end of file command and move back to the beginning of the file memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation, (void *) ( &transmissionSkipFileEnd ), sizeof ( transmissionSkipFileEnd ) ); mapHeader.writeLocation = plMemoryMapReservedSpace; counter = 0; plwarn( "wxWidgets wrapping buffer" ); continue; } } //if this is a beginning of page, then send a beginning of page flag first if ( transmissionType == transmissionBeginPage ) { memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation, (void *) ( &transmissionBeginPage ), sizeof ( transmissionBeginPage ) ); mapHeader.writeLocation += sizeof ( transmissionBeginPage ); if ( mapHeader.writeLocation == m_outputMemoryMap.getSize() ) mapHeader.writeLocation = plMemoryMapReservedSpace; counter = 0; if ( amountToCopy == 0 ) completed = true; transmissionType = transmissionRegular; continue; } //if this is a end of page and we have completed //the buffer then send a end of page flag first if ( ( transmissionType == transmissionEndOfPage || transmissionType == transmissionEndOfPageNoPause ) && amountToCopy == 0 ) { memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation, (void *) ( &transmissionType ), sizeof ( transmissionType ) ); mapHeader.writeLocation += sizeof ( transmissionType ); if ( mapHeader.writeLocation == m_outputMemoryMap.getSize() ) mapHeader.writeLocation = plMemoryMapReservedSpace; counter = 0; completed = true; continue; } if ( transmissionType == transmissionLocate && amountToCopy == 0 ) { memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation, (void *) ( &transmissionLocate ), sizeof ( transmissionLocate ) ); mapHeader.writeLocation += sizeof ( transmissionLocate ); if ( mapHeader.writeLocation == m_outputMemoryMap.getSize() ) mapHeader.writeLocation = plMemoryMapReservedSpace; mapHeader.locateModeFlag = 1; counter = 0; completed = true; continue; } if ( transmissionType == transmissionRequestTextSize ) { memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation, (void *) ( &transmissionRequestTextSize ), sizeof ( transmissionRequestTextSize ) ); mapHeader.writeLocation += sizeof ( transmissionRequestTextSize ); if ( mapHeader.writeLocation == m_outputMemoryMap.getSize() ) mapHeader.writeLocation = plMemoryMapReservedSpace; counter = 0; completed = true; continue; } if ( transmissionType == transmissionClose ) { memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation, (void *) ( &transmissionType ), sizeof ( transmissionType ) ); mapHeader.writeLocation += sizeof ( transmissionType ); if ( mapHeader.writeLocation == m_outputMemoryMap.getSize() ) mapHeader.writeLocation = plMemoryMapReservedSpace; counter = 0; completed = true; continue; } //if we have looped round stay 1 character behind the read buffer - it makes it //easier to test whether the reading has caught up with the writing or vice versa if ( mapHeader.writeLocation < mapHeader.readLocation && mapHeader.readLocation > 0 ) freeSpace = mapHeader.readLocation - mapHeader.writeLocation - 1; if ( freeSpace > headerSize ) { //decide exactly how much to copy copyAmount = MIN( amountToCopy, freeSpace - headerSize ); //copy the header and the amount we can to the buffer if ( copyAmount != amountToCopy ) memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation, (char *) ( &transmissionPartial ), sizeof ( transmissionPartial ) ); else memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation, (char *) ( &transmissionComplete ), sizeof ( transmissionComplete ) ); if ( pls ) { memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation + sizeof ( transmissionComplete ), (char *) ( ©Amount ), sizeof ( copyAmount ) ); memcpy( m_outputMemoryMap.getBuffer() + mapHeader.writeLocation + headerSize, (char *) pls->plbuf_buffer + m_localBufferPosition, copyAmount ); m_localBufferPosition += copyAmount; mapHeader.writeLocation += copyAmount + headerSize; if ( mapHeader.writeLocation == m_outputMemoryMap.getSize() ) mapHeader.writeLocation = plMemoryMapReservedSpace; amountToCopy -= copyAmount; counter = 0; } if ( amountToCopy == 0 && transmissionType != transmissionEndOfPage && transmissionType != transmissionLocate && transmissionType != transmissionEndOfPageNoPause ) completed = true; } else { ++counter; } } #ifdef WIN32 catch ( DWORD ) { plwarn( "Locking mutex failed when trying to communicate with wxPLViewer." ); break; } #endif catch ( ... ) { plwarn( "Unknown error when trying to communicate with wxPLViewer." ); break; } } if ( counter == counterLimit ) { plwarn( "Communication timeout with wxPLViewer - disconnecting" ); m_outputMemoryMap.close(); } #endif // #ifdef PL_WXWIDGETS_IPC3 } void wxPLDevice::SetupMemoryMap() { PLPLOT_wxLogDebug( "SetupMemoryMap(): enter" ); if ( strlen( m_mfo ) > 0 ) { #ifdef PL_WXWIDGETS_IPC3 const size_t mapSize = sizeof ( shmbuf ); #else const size_t mapSize = 1024 * 1024; char mutexName[PLPLOT_MAX_PATH]; #endif //create a memory map to hold the data and add it to the array of maps int nTries = 0; char mapName[PLPLOT_MAX_PATH]; static Rand randomGenerator; // make this static so that rapid repeat calls don't use the same seed while ( nTries < 10 ) { PLPLOT_wxLogDebug( "SetupMemoryMap(): mapName start" ); for ( int i = 0; i < strlen( m_mfo ); ++i ) { if ( m_mfo[i] == '?' ) mapName[i] = 'A' + (char) ( randomGenerator() % 26 ); else mapName[i] = m_mfo[i]; } PLPLOT_wxLogDebug( "SetupMemoryMap(): mapName done" ); mapName[strlen( m_mfo )] = '\0'; //truncate it earlier if needed if ( strlen( m_mfo ) > PLPLOT_MAX_PATH - 4 ) mapName[PLPLOT_MAX_PATH - 4] = '\0'; pldebug( "wxPLDevice::SetupMemoryMap", "nTries = %d, mapName = %s\n", nTries, mapName ); PLPLOT_wxLogDebug( "SetupMemoryMap(): m_outputMemoryMap.create call" ); m_outputMemoryMap.create( mapName, mapSize, false, true ); PLPLOT_wxLogDebug( "SetupMemoryMap(): m_outputMemoryMap.create done" ); if ( m_outputMemoryMap.isValid() ) { #ifndef PL_WXWIDGETS_IPC3 strcpy( mutexName, mapName ); strcat( mutexName, "mut" ); pldebug( "wxPLDevice::SetupMemoryMap", "nTries = %d, mutexName = %s\n", nTries, mutexName ); m_mutex.create( mutexName ); if ( !m_mutex.isValid() ) m_outputMemoryMap.close(); #endif // #ifndef PL_WXWIDGETS_IPC3 } if ( m_outputMemoryMap.isValid() ) break; ++nTries; } //m_outputMemoryMap.create( m_mfo, pls->plbuf_top, false, true ); //check the memory map is valid if ( !m_outputMemoryMap.isValid() ) { plwarn( "Error creating memory map for wxWidget instruction transmission. The plots will not be displayed" ); return; } #ifdef PL_WXWIDGETS_IPC3 // Should only be executed once per valid Memory map before wxPLViewer is launched. m_outputMemoryMap.initializeSemaphoresToValid( mapName ); //zero out the reserved area m_header.viewerOpenFlag = 0; m_header.locateModeFlag = 0; m_header.completeFlag = 0; #else // #ifdef PL_WXWIDGETS_IPC3 MemoryMapHeader *header = (MemoryMapHeader *) ( m_outputMemoryMap.getBuffer() ); header->readLocation = plMemoryMapReservedSpace; header->writeLocation = plMemoryMapReservedSpace; header->viewerOpenFlag = 0; header->locateModeFlag = 0; header->completeFlag = 0; #endif // #ifdef PL_WXWIDGETS_IPC3 //try to find the wxPLViewer executable, in the first instance just assume it //is in the path. //wxString exeName = wxT( "/nfs/see-fs-02_users/earpros/usr/src/plplot-plplot/build/utils/wxPLViewer" ); wxString exeName = wxT( "wxPLViewer" ); if ( plInBuildTree() ) { //if we are in the build tree check for the needed exe in there wxArrayString files; wxString utilsDir = wxString( wxT( BUILD_DIR ) ) + wxString( wxT( "/utils" ) ); wxDir::GetAllFiles( utilsDir, &files, exeName, wxDIR_FILES | wxDIR_DIRS ); if ( files.size() == 0 ) wxDir::GetAllFiles( utilsDir, &files, exeName + wxT( ".exe" ), wxDIR_FILES | wxDIR_DIRS ); if ( files.size() > 0 ) exeName = files[0]; } else { //check the plplot bin install directory wxArrayString files; wxDir::GetAllFiles( wxT( BIN_DIR ), &files, exeName, wxDIR_FILES | wxDIR_DIRS ); if ( files.size() == 0 ) wxDir::GetAllFiles( wxT( BIN_DIR ), &files, exeName + wxT( ".exe" ), wxDIR_FILES | wxDIR_DIRS ); if ( files.size() > 0 ) exeName = files[0]; } //Run the wxPlViewer with command line parameters telling it the location and size of the buffer wxString command; command << wxT( "\"" ) << exeName << wxT( "\" " ) << wxString( mapName, wxConvUTF8 ) << wxT( " " ) << mapSize << wxT( " " ) << m_width << wxT( " " ) << m_height; #ifndef WXPLVIEWER_DEBUG #ifdef WIN32 if ( wxExecute( command, wxEXEC_ASYNC ) == 0 ) plwarn( "Failed to run wxPLViewer - no plots will be shown" ); #else //WIN32 //Linux doesn't like using wxExecute without a wxApp, so use system instead command << wxT( " &" ); system( command.mb_str() ); #endif //WIN32 #else // ifndef WXPLVIEWER_DEBUG wxString runMessage; runMessage << "Begin Running wxPLViewer in the debugger now to continue. Use the parameters: plplotMemoryMap " << mapSize << " " << m_width << " " << m_height; // fprintf( stdout, runMessage ); // FIXME: The above fprintf does not output runMessage (because of buffered output?) // So output instead with cerr std::cerr << runMessage << std::endl; #endif // ifndef WXPLVIEWER_DEBUG #ifdef PL_WXWIDGETS_IPC3 size_t nbytes; try { // Update the header from the read (i.e., // wxPLViewer) side. Warning, this will block indefinitely // until the read side sends the required data. So // theoretically you could wait until the next day to launch // wxPLViewer using gdb and -dev wxwidgets would happily // wake up and start communicating with it. N.B. we could // change this infinite timeout later (by changing all // sem_wait calls in PLThreeSemaphores to sem_timedwait with a // generic timeout of say 2 minutes before it throws an // exception). But regardless of the ultimate resolution of // that issue, the following will not require any // wxMilliSleep loops. m_outputMemoryMap.receiveBytes( true, &m_header, sizeof ( MemoryMapHeader ) ); #ifdef PLPLOT_WX_DEBUG_OUTPUT std::cerr << "After receiveBytes" << std::endl; std::cerr << "transmissionType = " << static_cast( m_header.transmissionType ) << std::endl; std::cerr << "plbufAmountToTransmit = " << m_header.plbufAmountToTransmit << std::endl; std::cerr << "viewerOpenFlag = " << m_header.viewerOpenFlag << std::endl; std::cerr << "locateModeFlag = " << m_header.locateModeFlag << std::endl; std::cerr << "completeFlag = " << m_header.completeFlag << std::endl; #endif // #ifdef PLPLOT_WX_DEBUG_OUTPUT } catch ( const char *message ) { plwarn( message ); plwarn( "wxPLDevice::SetupMemoryMap: error" ); } catch ( ... ) { plwarn( "wxPLDevice::SetupMemoryMap: Unknown error" ); } // This value is generated by the read side. size_t &viewerSignal = m_header.viewerOpenFlag; #else // #ifdef PL_WXWIDGETS_IPC3 #ifndef WXPLVIEWER_DEBUG size_t maxTries = 1000; #else // ifndef WXPLVIEWER_DEBUG size_t maxTries = 100000; #endif // ifndef WXPLVIEWER_DEBUG //wait until the viewer signals it has opened the map file size_t counter = 0; size_t &viewerSignal = header->viewerOpenFlag; while ( counter < maxTries && viewerSignal == 0 ) { wxMilliSleep( 10 ); ++counter; } #endif // #ifdef PL_WXWIDGETS_IPC3 if ( viewerSignal == 0 ) plwarn( "wxPLViewer failed to signal it has found the shared memory." ); } PLPLOT_wxLogDebug( "SetupMemoryMap(): leave" ); } void wxPLDevice::Locate( PLStream * pls, PLGraphicsIn * graphicsIn ) { if ( !m_dc && m_outputMemoryMap.isValid() ) { #ifdef PL_WXWIDGETS_IPC3 TransmitBuffer( pls, transmissionLocate ); m_outputMemoryMap.receiveBytes( true, &m_header, sizeof ( MemoryMapHeader ) ); *graphicsIn = m_header.graphicsIn; #else // #ifdef PL_WXWIDGETS_IPC3 MemoryMapHeader *header = (MemoryMapHeader *) ( m_outputMemoryMap.getBuffer() ); TransmitBuffer( pls, transmissionLocate ); bool gotResponse = false; while ( !gotResponse ) { wxMilliSleep( 100 ); PLNamedMutexLocker lock( &m_mutex ); gotResponse = header->locateModeFlag == 0; } PLNamedMutexLocker lock( &m_mutex ); *graphicsIn = header->graphicsIn; #endif //ifdef PL_WXWIDGETS_IPC3 } else { plwarn( "plGetCursor cannot be used when the user supplies a wxDC or until wxPLViewer is initialised" ); graphicsIn->dX = -1; graphicsIn->dY = -1; graphicsIn->pX = -1; graphicsIn->pY = -1; } } //-------------------------------------------------------------------------- // wxRegion wxPLDevice::GetClipRegion() // Gets the current clip region from plplot as a wxRegion //-------------------------------------------------------------------------- wxRegion wxPLDevice::GetClipRegion() { PLINT rcx[4], rcy[4]; difilt_clip( rcx, rcy ); wxPoint cpoints[4]; for ( int i = 0; i < 4; i++ ) { cpoints[i].x = rcx[i] / m_xScale; cpoints[i].y = m_height - rcy[i] / m_yScale; } return wxRegion( 4, cpoints ); } plplot-5.13.0/drivers/deprecated_wxwidgets_agg.cpp000644 001752 001752 00000050276 13150160115 024054 0ustar00softwaresoftware000000 000000 // Copyright (C) 2008 Werner Smekal // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // TODO: // - let the AGG library process the text. In the moment most of the relevant code // is commented out, since there are problems with the affine transformation // // wxwidgets headers #include #include #include "plDevs.h" // plplot headers #include "plplotP.h" #include "plfci-truetype.h" // std and driver headers #include "deprecated_wxwidgets.h" #include // helper functions #if !defined ( WIN32 ) || defined ( __GNUC__ ) #include #else #define F_OK 1 #include int access( char *filename, int flag ) { FILE *infile; infile = fopen( filename, "r" ); if ( infile != NULL ) { fclose( infile ); return 0; } else return 1; } #endif #define makeunixslash( b ) do { char *I; for ( I = b; *I != 0; *I++ ) if ( *I == '\\' ) *I = '/';} while ( 0 ) //-------------------------------------------------------------------------- // wxPLDevAGG::wxPLDevAGG() // // Constructor of the AGG wxWidgets device based on the wxPLDevBase // class. Initialisations of variables and objects are done. //-------------------------------------------------------------------------- wxPLDevAGG::wxPLDevAGG() : wxPLDevBase( wxBACKEND_AGG ), mRenderingBuffer(), mPixFormat( mRenderingBuffer ), mRendererBase( mPixFormat ), mRendererSolid( mRendererBase ), mPath(), mTransform(), mConvCurve( mPath ), mConvStroke( mConvCurve ), mPathTransform( mConvCurve, mTransform ), mStrokeTransform( mConvStroke, mTransform ), mFontEngine(), mFontManager( mFontEngine ), mCurves( mFontManager.path_adaptor() ), mContour( mCurves ), mBuffer( NULL ), mStrokeWidth( 1.0 ), mStrokeOpacity( 255 ), mColorRedStroke( 255 ), mColorGreenStroke( 255 ), mColorBlueStroke( 255 ), mColorRedFill( 0 ), mColorGreenFill( 0 ), mColorBlueFill( 0 ) { mCurves.approximation_scale( 2.0 ); mContour.auto_detect_orientation( false ); mConvStroke.line_join( agg::round_join ); mConvStroke.line_cap( agg::round_cap ); // determine font directory #if defined ( WIN32 ) //static char *default_font_names[]={"arial.ttf","times.ttf","timesi.ttf","arial.ttf", // "symbol.ttf"}; // char WINDIR_PATH[255]; // char *b; // b=getenv("WINDIR"); // strncpy(WINDIR_PATH,b,255); // // Work out if we have Win95+ or Win3.?... sort of. // Actually, this just tries to find the place where the fonts live by looking // for arial, which should be on all windows machines. // At present, it only looks in two places, on one drive. I might change this // soon. // //if (WINDIR_PATH==NULL) // { // if (access("c:\\windows\\fonts\\arial.ttf", F_OK)==0) { // strcpy(font_dir,"c:/windows/fonts/"); // } // else if ( access("c:\\windows\\system\\arial.ttf", F_OK)==0) { // strcpy(font_dir,"c:/windows/system/"); // } // else // plwarn("Could not find font path; I sure hope you have defined fonts manually !"); // } // else // { // strncat(WINDIR_PATH,"\\fonts\\arial.ttf",255); // if (access(WINDIR_PATH, F_OK)==0) // { // b=strrchr(WINDIR_PATH,'\\'); // b++; //b=0; // makeunixslash(WINDIR_PATH); // strcpy(font_dir,WINDIR_PATH); // } // else // plwarn("Could not find font path; I sure hope you have defined fonts manually !"); // } // // if (pls->debug) fprintf( stderr, "%s\n", font_dir ) ; #else // For Unix systems, we will set the font path up a little differently in // that the configured PL_FREETYPE_FONT_DIR has been set as the default path, // but the user can override this by setting the environmental variable // "PLPLOT_FREETYPE_FONT_DIR" to something else. // NOTE WELL - the trailing slash must be added for now ! // // const char *str; // // fontdir.Clear(); // if( (str=getenv("PLPLOT_FREETYPE_FONT_DIR"))!=NULL ) // fontdir.Append( wxString(str, wxConvFile) ); // else // fontdir.Append( wxT(PL_FREETYPE_FONT_DIR) ); // // //printf("fontdir=%s, len=%d\n", fontdir.c_str(), fontdir.Length() ); #endif } //-------------------------------------------------------------------------- // wxPLDevAGG::~wxPLDevAGG() // // Deconstructor frees allocated buffer. //-------------------------------------------------------------------------- wxPLDevAGG::~wxPLDevAGG() { if ( ownGUI ) if ( mBuffer ) delete mBuffer; } //-------------------------------------------------------------------------- // void wxPLDevAGG::drawPath( drawPathFlag flag ) // // Common function which either draws a stroke along a path or a filled // polygon surrounded by a stroke depending on flag. //-------------------------------------------------------------------------- void wxPLDevAGG::drawPath( drawPathFlag flag ) { mRasterizer.reset(); switch ( flag ) { case Stroke: if ( mStrokeOpacity && mStrokeWidth > 0.0 ) { mConvStroke.width( mStrokeWidth ); mRasterizer.add_path( mStrokeTransform ); mRendererSolid.color( agg::rgba8( mColorRedStroke, mColorGreenStroke, mColorBlueStroke, mStrokeOpacity ) ); agg::render_scanlines( mRasterizer, mScanLine, mRendererSolid ); } break; case FillAndStroke: if ( mStrokeOpacity ) { mRasterizer.add_path( mPathTransform ); mRendererSolid.color( agg::rgba8( mColorRedStroke, mColorGreenStroke, mColorBlueStroke, mStrokeOpacity ) ); agg::render_scanlines( mRasterizer, mScanLine, mRendererSolid ); } if ( mStrokeOpacity && mStrokeWidth > 0.0 ) { mConvStroke.width( mStrokeWidth ); mRasterizer.add_path( mStrokeTransform ); mRendererSolid.color( agg::rgba8( mColorRedStroke, mColorGreenStroke, mColorBlueStroke, mStrokeOpacity ) ); agg::render_scanlines( mRasterizer, mScanLine, mRendererSolid ); } break; } } //-------------------------------------------------------------------------- // void wxPLDevAGG::DrawLine( short x1a, short y1a, short x2a, short y2a ) // // Draw a line from (x1a, y1a) to (x2a, y2a). //-------------------------------------------------------------------------- void wxPLDevAGG::DrawLine( short x1a, short y1a, short x2a, short y2a ) { mPath.remove_all(); mPath.move_to( x1a, y1a ); mPath.line_to( x2a, y2a ); if ( !resizing && ownGUI ) AGGAddtoClipRegion( x1a, y1a, x2a, y2a ); drawPath( Stroke ); } //-------------------------------------------------------------------------- // void wxPLDevAGG::DrawPolyline( short *xa, short *ya, PLINT npts ) // // Draw a poly line - coordinates are in the xa and ya arrays. //-------------------------------------------------------------------------- void wxPLDevAGG::DrawPolyline( short *xa, short *ya, PLINT npts ) { mPath.remove_all(); mPath.move_to( xa[0], ya[0] ); for ( PLINT i = 1; i < npts; i++ ) { mPath.line_to( xa[i], ya[i] ); if ( !resizing && ownGUI ) AGGAddtoClipRegion( xa[i - 1], ya[i - 1], xa[i], ya[i] ); } drawPath( Stroke ); } //-------------------------------------------------------------------------- // void wxPLDevAGG::ClearBackground( PLINT bgr, PLINT bgg, PLINT bgb, // PLINT x1, PLINT y1, PLINT x2, PLINT y2 ) // // Clear parts ((x1,y1) to (x2,y2)) of the background in color (bgr,bgg,bgb). //-------------------------------------------------------------------------- void wxPLDevAGG::ClearBackground( PLINT bgr, PLINT bgg, PLINT bgb, PLINT x1, PLINT y1, PLINT x2, PLINT y2 ) { if ( x1 < 0 && y1 < 0 && x2 < 0 && y2 < 0 ) { mRendererBase.clear( agg::rgba8( bgr, bgg, bgb ) ); if ( !resizing && ownGUI ) AddtoClipRegion( 0, 0, width, height ); } else { mPath.remove_all(); mPath.move_to( x1, y1 ); mPath.line_to( x2, y1 ); mPath.line_to( x2, y2 ); mPath.line_to( x1, y2 ); mPath.close_polygon(); mRasterizer.reset(); mRasterizer.add_path( mPathTransform ); mRendererSolid.color( agg::rgba8( bgr, bgg, bgb, 255 ) ); agg::render_scanlines( mRasterizer, mScanLine, mRendererSolid ); mConvStroke.width( 1.0 ); mRasterizer.add_path( mStrokeTransform ); mRendererSolid.color( agg::rgba8( bgr, bgg, bgb, 255 ) ); agg::render_scanlines( mRasterizer, mScanLine, mRendererSolid ); if ( !resizing && ownGUI ) AGGAddtoClipRegion( x1, y1, x2, y2 ); } } //-------------------------------------------------------------------------- // void wxPLDevAGG::AGGAddtoClipRegion( short x1, short y1, // short x2, short y2 ) // // Adds the region (x1,y1)-(x2,y2) to the regions which needs to be // updated/redrawn. //-------------------------------------------------------------------------- void wxPLDevAGG::AGGAddtoClipRegion( short x1, short y1, short x2, short y2 ) { double x1d = x1, x2d = x2, y1d = y1, y2d = y2; mTransform.transform( &x1d, &y1d ); mTransform.transform( &x2d, &y2d ); AddtoClipRegion( (int) floor( x1d ), (int) floor( y1d ), (int) ceil( x2d ), (int) ceil( y2d ) ); } //-------------------------------------------------------------------------- // void wxPLDevAGG::FillPolygon( PLStream *pls ) // // Draw a filled polygon. //-------------------------------------------------------------------------- void wxPLDevAGG::FillPolygon( PLStream *pls ) { short *xa = pls->dev_x; short *ya = pls->dev_y; mPath.remove_all(); mPath.move_to( xa[0], ya[0] ); for ( PLINT i = 1; i < pls->dev_npts; i++ ) { mPath.line_to( xa[i], ya[i] ); if ( !resizing && ownGUI ) AGGAddtoClipRegion( xa[i - 1], ya[i - 1], xa[i], ya[i] ); } mPath.line_to( xa[0], ya[0] ); mPath.close_polygon(); drawPath( FillAndStroke ); } //-------------------------------------------------------------------------- // void wxPLDevAGG::BlitRectangle( wxDC* dc, int vX, int vY, // int vW, int vH ) // // Copy/Blit a rectangle ((vX,vY) to (vX+vW,vY+vH)) into given dc. //-------------------------------------------------------------------------- void wxPLDevAGG::BlitRectangle( wxDC* dc, int vX, int vY, int vW, int vH ) { if ( mBuffer ) { wxMemoryDC MemoryDC; wxBitmap bitmap( mBuffer->GetSubImage( wxRect( vX, vY, vW, vH ) ), -1 ); MemoryDC.SelectObject( bitmap ); dc->Blit( vX, vY, vW, vH, &MemoryDC, 0, 0 ); MemoryDC.SelectObject( wxNullBitmap ); } } //-------------------------------------------------------------------------- // void wxPLDevAGG::CreateCanvas( void ) // // Create canvas (bitmap and dc) if the driver provides the GUI. //-------------------------------------------------------------------------- void wxPLDevAGG::CreateCanvas() { if ( ownGUI ) { // get a new wxImage (image buffer) if ( mBuffer ) delete mBuffer; mBuffer = new wxImage( bm_width, bm_height ); mRenderingBuffer.attach( mBuffer->GetData(), bm_width, bm_height, bm_width * 3 ); } else mRenderingBuffer.attach( mBuffer->GetData(), width, height, width * 3 ); mRendererBase.reset_clipping( true ); mTransform.reset(); mTransform.premultiply( agg::trans_affine_translation( 0.0, height ) ); mTransform.premultiply( agg::trans_affine_scaling( 1.0 / scalex, -1.0 / scaley ) ); mStrokeWidth = ( scalex + scaley ) / 2.0; } //-------------------------------------------------------------------------- // void wxPLDevAGG::SetWidth( PLStream *pls ) // // Set the width of the drawing pen. //-------------------------------------------------------------------------- void wxPLDevAGG::SetWidth( PLStream *pls ) { mStrokeWidth = ( scalex + scaley ) / 2.0 * ( pls->width > 0 ? pls->width : 1 ); // TODO: why and when ist width 0??? } //-------------------------------------------------------------------------- // void wxPLDevAGG::SetColor0( PLStream *pls ) // // Set color from colormap 0. //-------------------------------------------------------------------------- void wxPLDevAGG::SetColor0( PLStream *pls ) { mColorRedStroke = pls->curcolor.r; mColorGreenStroke = pls->curcolor.g; mColorBlueStroke = pls->curcolor.b; mStrokeOpacity = (wxUint8) ( pls->curcolor.a * 255 ); } //-------------------------------------------------------------------------- // void wxPLDevAGG::SetColor1( PLStream *pls ) // // Set color from colormap 1. //-------------------------------------------------------------------------- void wxPLDevAGG::SetColor1( PLStream *pls ) { mColorRedStroke = pls->curcolor.r; mColorGreenStroke = pls->curcolor.g; mColorBlueStroke = pls->curcolor.b; mStrokeOpacity = (wxUint8) ( pls->curcolor.a * 255 ); } //-------------------------------------------------------------------------- // void wxPLDevAGG::SetExternalBuffer( void* dc ) // // Adds a dc to the device. In that case, the drivers doesn't provide // a GUI. A new buffer (image) will be created and set up. //-------------------------------------------------------------------------- void wxPLDevAGG::SetExternalBuffer( void* image ) { mBuffer = (wxImage *) image; // Add the image to the device mRenderingBuffer.attach( mBuffer->GetData(), width, height, width * 3 ); mRendererBase.reset_clipping( true ); mTransform.reset(); mTransform.premultiply( agg::trans_affine_translation( 0.0, height ) ); mTransform.premultiply( agg::trans_affine_scaling( 1.0 / scalex, -1.0 / scaley ) ); mStrokeWidth = ( scalex + scaley ) / 2.0; ready = true; ownGUI = false; } #ifdef PL_HAVE_FREETYPE //-------------------------------------------------------------------------- // void wxPLDevAGG::PutPixel( short x, short y, PLINT color ) // // Draw a pixel in color color @ (x,y). //-------------------------------------------------------------------------- void wxPLDevAGG::PutPixel( short x, short y, PLINT color ) { mBuffer->SetRGB( x, y, GetRValue( color ), GetGValue( color ), GetBValue( color ) ); AddtoClipRegion( x, y, x, y ); } //-------------------------------------------------------------------------- // void wxPLDevAGG::PutPixel( short x, short y ) // // Draw a pixel in current color @ (x,y). //-------------------------------------------------------------------------- void wxPLDevAGG::PutPixel( short x, short y ) { mBuffer->SetRGB( x, y, mColorRedStroke, mColorGreenStroke, mColorBlueStroke ); AddtoClipRegion( x, y, x, y ); } //-------------------------------------------------------------------------- // PLINT wxPLDevAGG::GetPixel( short x, short y ) // // Get color information from pixel @ (x,y). //-------------------------------------------------------------------------- PLINT wxPLDevAGG::GetPixel( short x, short y ) { return RGB( mBuffer->GetRed( x, y ), mBuffer->GetGreen( x, y ), mBuffer->GetBlue( x, y ) ); } #endif // PL_HAVE_FREETYPE void wxPLDevAGG::PSDrawTextToDC( char* utf8_string, bool drawText ) { // Log_Verbose( "%s", __FUNCTION__ ); printf( "utf8_string=%s\n", utf8_string ); double start_x = 0.0; double start_y = 0.0; //wchar_t str[512]; //size_t len=wxConvUTF8.ToWChar( str, 512, utf8_string ); size_t len = strlen( utf8_string ); char * str = utf8_string; printf( "len=%lu\n", (unsigned long) len ); const agg::glyph_cache* glyph; if ( !drawText ) { double x = 0; double y = 0; bool first = true; char * saveStr = str; while ( *str && len ) { glyph = mFontManager.glyph( *str ); if ( glyph ) { if ( !first ) mFontManager.add_kerning( &x, &y ); x += glyph->advance_x; y += glyph->advance_y; first = false; } textHeight = textHeight > ( glyph->bounds.y2 - glyph->bounds.y1 + yOffset ) ? textHeight : ( glyph->bounds.y2 - glyph->bounds.y1 + yOffset ); ++str; --len; } textWidth = x; printf( "str: %s, textWidth=%lf\n", saveStr, textWidth ); } else { for ( size_t i = 0; i < len && str[i]; i++ ) { glyph = mFontManager.glyph( str[i] ); if ( glyph ) { printf( "before: start_x=%f, start_y=%f\n", start_x, start_y ); if ( i ) mFontManager.add_kerning( &start_x, &start_y ); printf( "after: start_x=%f, start_y=%f\n", start_x, start_y ); mFontManager.init_embedded_adaptors( glyph, start_x, start_y ); mRendererSolid.color( agg::rgba8( mColorRedStroke, mColorGreenStroke, mColorBlueStroke, mStrokeOpacity ) ); agg::render_scanlines( mFontManager.gray8_adaptor(), mFontManager.gray8_scanline(), mRendererSolid ); start_x += glyph->advance_x / scalex; //start_y += glyph->advance_y/scaley; } } } memset( utf8_string, '\0', max_string_length ); } void wxPLDevAGG::PSSetFont( PLUNICODE fci ) { // convert the fci to Base14/Type1 font information wxString fontname = fontdir + wxString( plP_FCI2FontName( fci, TrueTypeLookup, N_TrueTypeLookup ), *wxConvCurrent ); if ( !mFontEngine.load_font( "/usr/share/fonts/truetype/freefont/FreeSans.ttf", 0, agg::glyph_ren_agg_gray8 ) ) plabort( "Font could not be loaded" ); //mFontEngine.load_font( "c:\\windows\\fonts\\arial.ttf", 0, agg::glyph_ren_agg_gray8 ); mFontEngine.height( fontSize * fontScale ); mFontEngine.width( fontSize * fontScale ); mFontEngine.hinting( true ); mFontEngine.flip_y( false ); mContour.width( fontSize * fontScale * 0.2 ); } void wxPLDevAGG::ProcessString( PLStream* pls, EscText* args ) { plabort( "The AGG backend can't process the text yet own its own!" ); // Check that we got unicode, warning message and return if not if ( args->unicode_array_len == 0 ) { printf( "Non unicode string passed to a wxWidgets driver, ignoring\n" ); return; } // Check that unicode string isn't longer then the max we allow if ( args->unicode_array_len >= 500 ) { printf( "Sorry, the wxWidgets drivers only handles strings of length < %d\n", 500 ); return; } // Calculate the font size (in pixels) fontSize = pls->chrht * DEVICE_PIXELS_PER_MM * 1.2 * scaley; // calculate rotation of text plRotationShear( args->xform, &rotation, &shear, &stride ); rotation -= pls->diorot * M_PI / 2.0; cos_shear = cos( shear ); sin_shear = sin( shear ); PSDrawText( args->unicode_array, args->unicode_array_len, false ); printf( "textWidth=%f, textHeight=%f\n", textWidth, textHeight ); agg::trans_affine mtx; mtx.reset(); mtx *= agg::trans_affine_translation( args->x, args->y ); //mtx *= agg::trans_affine_rotation( rotation ); //mtx *= agg::trans_affine_skewing( shear, shear ); mtx *= mTransform; mtx *= agg::trans_affine_translation( -args->just * textWidth / scalex, -0.5 * textHeight ); mtx *= agg::trans_affine_translation( -args->just * textWidth / scalex, -0.5 * textHeight ); mFontEngine.transform( mtx ); PSDrawText( args->unicode_array, args->unicode_array_len, true ); AddtoClipRegion( 0, 0, width, height ); } plplot-5.13.0/drivers/wxwidgets.driver_info.in000644 001752 001752 00000000064 13150160115 023175 0ustar00softwaresoftware000000 000000 wxwidgets:wxWidgets Driver:1:wxwidgets:51:wxwidgets plplot-5.13.0/drivers/ps.driver_info.in000644 001752 001752 00000000124 13150160115 021567 0ustar00softwaresoftware000000 000000 ps:PostScript File (monochrome):0:ps:29:psm psc:PostScript File (color):0:ps:30:psc plplot-5.13.0/drivers/qt.cpp000644 001752 001752 00000156225 13150160115 017456 0ustar00softwaresoftware000000 000000 // // // This software is provided under the LGPL in March 2009 by the // Cluster Science Centre // QSAS team, // Imperial College, London // // Copyright (C) 2009 Imperial College, London // Copyright (C) 2009 Alan W. Irwin // // This is free software; you can redistribute it and/or modify // it under the terms of the GNU General Lesser Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This software is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // To received a copy of the GNU Library General Public License // write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // History: // // // March 2009: v1.00 // Initial release. // // #include "qt.h" #include // global variables initialised in init(), used in tidy() // QApplication* app=NULL; static int argc; // argc and argv have to exist when tidy() is used, thus they are made global static char **argv; static int appCounter = 0; // to be rigorous, all uses should be placed between mutexes // Drivers declaration extern "C" { PLDLLIMPEXP_DRIVER_DATA( const char* ) plD_DEVICE_INFO_qt = #if defined ( PLD_bmpqt ) "bmpqt:Qt Windows bitmap driver:0:qt:66:bmpqt\n" #endif #if defined ( PLD_jpgqt ) "jpgqt:Qt jpg driver:0:qt:67:jpgqt\n" #endif #if defined ( PLD_pngqt ) "pngqt:Qt png driver:0:qt:68:pngqt\n" #endif #if defined ( PLD_ppmqt ) "ppmqt:Qt ppm driver:0:qt:69:ppmqt\n" #endif #if defined ( PLD_tiffqt ) "tiffqt:Qt tiff driver:0:qt:70:tiffqt\n" #endif #if defined ( PLD_svgqt ) && QT_VERSION >= 0x040300 "svgqt:Qt SVG driver:0:qt:71:svgqt\n" #endif #if defined ( PLD_qtwidget ) "qtwidget:Qt Widget:1:qt:72:qtwidget\n" #endif #if defined ( PLD_epsqt ) "epsqt:Qt EPS driver:0:qt:73:epsqt\n" #endif #if defined ( PLD_pdfqt ) "pdfqt:Qt PDF driver:0:qt:74:pdfqt\n" #endif #if defined ( PLD_extqt ) "extqt:External Qt driver:0:qt:75:extqt\n" #endif #if defined ( PLD_memqt ) "memqt:Memory Qt driver:0:qt:76:memqt\n" #endif ; } // extern "C" static DrvOpt qt_options[] = { { "text_vectorize", DRV_INT, &vectorize, "Vectorize fonts on output (0|1)" }, { "lines_antialiasing", DRV_INT, &lines_aa, "Toggles antialiasing on lines (0|1)" }, { NULL, DRV_INT, NULL, NULL } }; bool initQtApp( bool isGUI ) { QMutexLocker locker( &QtPLDriver::mutex ); bool res = false; ++appCounter; if ( qApp == NULL && appCounter == 1 ) { argc = 1; argv = new char*[2]; argv[0] = new char[10]; argv[1] = new char[1]; snprintf( argv[0], 10, "qt_driver" ); argv[1][0] = '\0'; #ifdef Q_WS_X11 // On X11 if DISPLAY is not set then cannot open GUI. This allows non-interactive devices to still work in this case. if ( getenv( "DISPLAY" ) == NULL ) isGUI = false; #endif new QApplication( argc, argv, isGUI ); res = true; } return res; } void closeQtApp() { QMutexLocker locker( &QtPLDriver::mutex ); --appCounter; if ( qApp != NULL && appCounter == 0 ) { delete qApp; delete[] argv[0]; delete[] argv[1]; delete[] argv; argv = NULL; } } //-------------------------------------------------------------------------- // qt_family_check () // // support function to help supress more than one page if family file // output not specified by the user (e.g., with the -fam command-line option). // Adapted directly from svg.c //-------------------------------------------------------------------------- static int already_warned = 0; static int qt_family_check( PLStream *pls ) { if ( pls->family || pls->page == 1 ) { return 0; } else { if ( !already_warned ) { already_warned = 1; plwarn( "All pages after the first skipped because family file output not specified.\n" ); } return 1; } } // Declaration of the driver-specific interface functions #if defined ( PLD_bmpqt ) || defined ( PLD_jpgqt ) || defined ( PLD_pngqt ) || defined ( PLD_ppmqt ) || defined ( PLD_tiffqt ) || defined ( PLD_memqt ) void plD_init_rasterqt( PLStream * ); void plD_eop_rasterqt( PLStream * ); void plD_line_rasterqt( PLStream *, short, short, short, short ); void plD_polyline_rasterqt( PLStream *, short*, short*, PLINT ); void plD_tidy_rasterqt( PLStream * ); void plD_state_rasterqt( PLStream *, PLINT ); void plD_esc_rasterqt( PLStream *, PLINT, void* ); #endif #if defined ( PLD_bmpqt ) void plD_dispatch_init_bmpqt( PLDispatchTable *pdt ); void plD_bop_bmpqt( PLStream * ); #endif #if defined ( PLD_jpgqt ) void plD_dispatch_init_jpgqt( PLDispatchTable *pdt ); void plD_bop_jpgqt( PLStream * ); #endif #if defined ( PLD_pngqt ) void plD_dispatch_init_pngqt( PLDispatchTable *pdt ); void plD_bop_pngqt( PLStream * ); #endif #if defined ( PLD_ppmqt ) void plD_dispatch_init_ppmqt( PLDispatchTable *pdt ); void plD_bop_ppmqt( PLStream * ); #endif #if defined ( PLD_tiffqt ) void plD_dispatch_init_tiffqt( PLDispatchTable *pdt ); void plD_bop_tiffqt( PLStream * ); #endif #if defined ( PLD_svgqt ) && QT_VERSION >= 0x040300 void plD_dispatch_init_svgqt( PLDispatchTable *pdt ); void plD_init_svgqt( PLStream * ); void plD_bop_svgqt( PLStream * ); void plD_eop_svgqt( PLStream * ); void plD_line_svgqt( PLStream *, short, short, short, short ); void plD_polyline_svgqt( PLStream *, short*, short*, PLINT ); void plD_tidy_svgqt( PLStream * ); void plD_state_svgqt( PLStream *, PLINT ); void plD_esc_svgqt( PLStream *, PLINT, void* ); #endif #if defined ( PLD_epsqt ) || defined ( PLD_pdfqt ) void plD_init_epspdfqt( PLStream * ); void plD_bop_epspdfqt_helper( PLStream *, int ifeps ); void plD_eop_epspdfqt( PLStream * ); void plD_line_epspdfqt( PLStream *, short, short, short, short ); void plD_polyline_epspdfqt( PLStream *, short*, short*, PLINT ); void plD_tidy_epspdfqt( PLStream * ); void plD_state_epspdfqt( PLStream *, PLINT ); void plD_esc_epspdfqt( PLStream *, PLINT, void* ); #endif #if defined ( PLD_epsqt ) void plD_dispatch_init_epsqt( PLDispatchTable *pdt ); void plD_bop_epsqt( PLStream * ); #endif #if defined ( PLD_pdfqt ) void plD_dispatch_init_pdfqt( PLDispatchTable *pdt ); void plD_bop_pdfqt( PLStream * ); #endif #if defined ( PLD_qtwidget ) void plD_dispatch_init_qtwidget( PLDispatchTable *pdt ); void plD_init_qtwidget( PLStream * ); void plD_eop_qtwidget( PLStream * ); void plD_wait_qtwidget( PLStream * ); void plD_line_qtwidget( PLStream *, short, short, short, short ); void plD_polyline_qtwidget( PLStream *, short*, short*, PLINT ); void plD_tidy_qtwidget( PLStream * ); void plD_state_qtwidget( PLStream *, PLINT ); void plD_esc_qtwidget( PLStream *, PLINT, void* ); void plD_bop_qtwidget( PLStream * ); #endif #if defined ( PLD_extqt ) void plD_dispatch_init_extqt( PLDispatchTable *pdt ); void plD_init_extqt( PLStream * ); void plD_eop_extqt( PLStream * ); void plD_line_extqt( PLStream *, short, short, short, short ); void plD_polyline_extqt( PLStream *, short*, short*, PLINT ); void plD_tidy_extqt( PLStream * ); void plD_state_extqt( PLStream *, PLINT ); void plD_esc_extqt( PLStream *, PLINT, void* ); void plD_bop_extqt( PLStream * ); #endif #if defined ( PLD_memqt ) void plD_dispatch_init_memqt( PLDispatchTable *pdt ); void plD_init_memqt( PLStream * ); void plD_bop_memqt( PLStream * ); void plD_eop_memqt( PLStream * ); #endif ////////////////// Raster driver-specific definitions: class and interface functions ///////// #if defined ( PLD_bmpqt ) || defined ( PLD_jpgqt ) || defined ( PLD_pngqt ) || defined ( PLD_ppmqt ) || defined ( PLD_tiffqt ) || defined ( PLD_memqt ) void plD_init_rasterqt( PLStream * pls ) { double dpi; vectorize = 0; lines_aa = 1; plParseDrvOpts( qt_options ); // Stream setup pls->color = 1; pls->plbuf_write = 0; pls->dev_fill0 = 1; pls->dev_fill1 = 0; pls->dev_gradient = 1; // driver renders gradient // Let the PLplot core handle dashed lines since // the driver results for this capability have a number of issues. // pls->dev_dash=1; pls->dev_dash = 0; pls->dev_flush = 1; // Driver does not have a clear capability so use (good) PLplot core // fallback for that instead. pls->dev_clear = 0; pls->termin = 0; pls->page = 0; pls->dev_text = 1; // want to draw text pls->dev_unicode = 1; // want unicode pls->has_string_length = 1; // Driver supports string length calculations // Needs to be true only because of multi-stream case bool isMaster = initQtApp( true ); if ( pls->xdpi <= 0. ) dpi = DEFAULT_DPI; else dpi = pls->xdpi; // Shamelessly copied on the Cairo stuff :) if ( pls->xlength <= 0 || pls->ylength <= 0 ) { pls->dev = new QtRasterDevice; pls->xlength = (PLINT) ( ( (QtRasterDevice *) ( pls->dev ) )->m_dWidth ); pls->ylength = (PLINT) ( ( (QtRasterDevice *) ( pls->dev ) )->m_dHeight ); } else { pls->dev = new QtRasterDevice( pls->xlength, pls->ylength ); } ( (QtRasterDevice *) pls->dev )->setPLStream( pls ); if ( isMaster ) handler.setMasterDevice( (QtRasterDevice *) ( pls->dev ) ); if ( pls->xlength > pls->ylength ) ( (QtRasterDevice *) ( pls->dev ) )->downscale = (PLFLT) pls->xlength / (PLFLT) ( PIXELS_X - 1 ); else ( (QtRasterDevice *) ( pls->dev ) )->downscale = (PLFLT) pls->ylength / (PLFLT) PIXELS_Y; plP_setphy( (PLINT) 0, (PLINT) ( pls->xlength / ( (QtRasterDevice *) ( pls->dev ) )->downscale ), (PLINT) 0, (PLINT) ( pls->ylength / ( (QtRasterDevice *) ( pls->dev ) )->downscale ) ); plP_setpxl( dpi / 25.4 / ( (QtRasterDevice *) ( pls->dev ) )->downscale, dpi / 25.4 / ( (QtRasterDevice *) ( pls->dev ) )->downscale ); ( (QtRasterDevice *) ( pls->dev ) )->setResolution( dpi ); // Initialize family file info plFamInit( pls ); plOpenFile( pls ); } void plD_eop_rasterqt( PLStream *pls ) { if ( qt_family_check( pls ) ) { return; } ( (QtRasterDevice *) pls->dev )->savePlot(); handler.DeviceChangedPage( (QtRasterDevice *) pls->dev ); } void plD_line_rasterqt( PLStream * pls, short x1a, short y1a, short x2a, short y2a ) { QtRasterDevice* widget = (QtRasterDevice *) pls->dev; if ( widget != NULL && qt_family_check( pls ) ) { return; } if ( widget == NULL ) return; widget->QtPLDriver::setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawLine( x1a, y1a, x2a, y2a ); } void plD_polyline_rasterqt( PLStream *pls, short *xa, short *ya, PLINT npts ) { QtRasterDevice * widget = (QtRasterDevice *) pls->dev; if ( widget != NULL && qt_family_check( pls ) ) { return; } if ( widget == NULL ) return; widget->QtPLDriver::setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawPolyline( xa, ya, npts ); } void plD_esc_rasterqt( PLStream * pls, PLINT op, void* ptr ) { short *xa, *ya; unsigned char *r, *g, *b; PLFLT *alpha; PLINT i; QtRasterDevice * widget = (QtRasterDevice *) pls->dev; if ( widget != NULL && qt_family_check( pls ) ) { return; } if ( widget == NULL ) return; switch ( op ) { // case PLESC_DASH: // widget->setDashed(pls->nms, pls->mark, pls->space); // widget->QtPLDriver::setColor(pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a); // widget->drawPolyline(pls->dev_x, pls->dev_y, pls->dev_npts); // widget->setSolid(); // break; case PLESC_FILL: xa = new short[pls->dev_npts]; ya = new short[pls->dev_npts]; for ( i = 0; i < pls->dev_npts; i++ ) { xa[i] = pls->dev_x[i]; ya[i] = pls->dev_y[i]; } widget->QtPLDriver::setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawPolygon( xa, ya, pls->dev_npts ); delete[] xa; delete[] ya; break; case PLESC_GRADIENT: xa = new short[pls->dev_npts]; ya = new short[pls->dev_npts]; r = new unsigned char[pls->ncol1]; g = new unsigned char[pls->ncol1]; b = new unsigned char[pls->ncol1]; alpha = new PLFLT[pls->ncol1]; for ( i = 0; i < pls->ncol1; i++ ) { r[i] = pls->cmap1[i].r; g[i] = pls->cmap1[i].g; b[i] = pls->cmap1[i].b; alpha[i] = pls->cmap1[i].a; } widget->setGradient( pls->xgradient[0], pls->xgradient[1], pls->ygradient[0], pls->ygradient[1], r, g, b, alpha, pls->ncol1 ); for ( i = 0; i < pls->dev_npts; i++ ) { xa[i] = pls->dev_x[i]; ya[i] = pls->dev_y[i]; } widget->drawPolygon( xa, ya, pls->dev_npts ); delete[] xa; delete[] ya; delete[] r; delete[] g; delete[] b; delete[] alpha; break; case PLESC_HAS_TEXT: // call the generic ProcessString function // ProcessString( pls, (EscText *)ptr ); widget->QtPLDriver::setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawText( (EscText *) ptr ); break; default: break; } } void plD_state_rasterqt( PLStream * pls, PLINT op ) { QtRasterDevice * widget = (QtRasterDevice *) pls->dev; if ( widget != NULL && qt_family_check( pls ) ) { return; } if ( widget == NULL ) return; switch ( op ) { case PLSTATE_WIDTH: widget->setWidthF( pls->width ); break; case PLSTATE_COLOR0: ( (QtPLDriver *) widget )->QtPLDriver::setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); break; case PLSTATE_COLOR1: ( (QtPLDriver *) widget )->QtPLDriver::setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); break; default: break; } } void plD_tidy_rasterqt( PLStream * pls ) { QtRasterDevice * widget = (QtRasterDevice *) pls->dev; if ( widget != NULL ) { handler.DeviceClosed( widget ); delete widget; pls->dev = NULL; } plCloseFile( pls ); closeQtApp(); } #endif #if defined ( PLD_bmpqt ) void plD_dispatch_init_bmpqt( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "Qt Windows bitmap Driver"; pdt->pl_DevName = "bmpqt"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 66; pdt->pl_init = (plD_init_fp) plD_init_rasterqt; pdt->pl_line = (plD_line_fp) plD_line_rasterqt; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_rasterqt; pdt->pl_eop = (plD_eop_fp) plD_eop_rasterqt; pdt->pl_bop = (plD_bop_fp) plD_bop_bmpqt; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_rasterqt; pdt->pl_state = (plD_state_fp) plD_state_rasterqt; pdt->pl_esc = (plD_esc_fp) plD_esc_rasterqt; } void plD_bop_bmpqt( PLStream *pls ) { // Plot familying stuff. Not really understood, just copying gd.c plGetFam( pls ); pls->famadv = 1; pls->page++; if ( qt_family_check( pls ) ) { return; } ( (QtRasterDevice *) pls->dev )->definePlotName( pls->FileName, "BMP" ); ( (QtRasterDevice *) pls->dev )->setBackgroundColor( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b, pls->cmap0[0].a ); } #endif #if defined ( PLD_jpgqt ) void plD_dispatch_init_jpgqt( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "Qt jpg Driver"; pdt->pl_DevName = "jpgqt"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 67; pdt->pl_init = (plD_init_fp) plD_init_rasterqt; pdt->pl_line = (plD_line_fp) plD_line_rasterqt; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_rasterqt; pdt->pl_eop = (plD_eop_fp) plD_eop_rasterqt; pdt->pl_bop = (plD_bop_fp) plD_bop_jpgqt; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_rasterqt; pdt->pl_state = (plD_state_fp) plD_state_rasterqt; pdt->pl_esc = (plD_esc_fp) plD_esc_rasterqt; } void plD_bop_jpgqt( PLStream *pls ) { // Plot familying stuff. Not really understood, just copying gd.c plGetFam( pls ); pls->famadv = 1; pls->page++; if ( qt_family_check( pls ) ) { return; } ( (QtRasterDevice *) pls->dev )->definePlotName( pls->FileName, "JPG" ); ( (QtRasterDevice *) pls->dev )->setBackgroundColor( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b, pls->cmap0[0].a ); } #endif #if defined ( PLD_pngqt ) void plD_dispatch_init_pngqt( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "Qt png Driver"; pdt->pl_DevName = "pngqt"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 68; pdt->pl_init = (plD_init_fp) plD_init_rasterqt; pdt->pl_line = (plD_line_fp) plD_line_rasterqt; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_rasterqt; pdt->pl_eop = (plD_eop_fp) plD_eop_rasterqt; pdt->pl_bop = (plD_bop_fp) plD_bop_pngqt; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_rasterqt; pdt->pl_state = (plD_state_fp) plD_state_rasterqt; pdt->pl_esc = (plD_esc_fp) plD_esc_rasterqt; } void plD_bop_pngqt( PLStream *pls ) { // Plot familying stuff. Not really understood, just copying gd.c plGetFam( pls ); pls->famadv = 1; pls->page++; if ( qt_family_check( pls ) ) { return; } ( (QtRasterDevice *) pls->dev )->definePlotName( pls->FileName, "PNG" ); ( (QtRasterDevice *) pls->dev )->setBackgroundColor( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b, pls->cmap0[0].a ); } #endif #if defined ( PLD_ppmqt ) void plD_dispatch_init_ppmqt( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "Qt ppm Driver"; pdt->pl_DevName = "ppmqt"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 69; pdt->pl_init = (plD_init_fp) plD_init_rasterqt; pdt->pl_line = (plD_line_fp) plD_line_rasterqt; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_rasterqt; pdt->pl_eop = (plD_eop_fp) plD_eop_rasterqt; pdt->pl_bop = (plD_bop_fp) plD_bop_ppmqt; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_rasterqt; pdt->pl_state = (plD_state_fp) plD_state_rasterqt; pdt->pl_esc = (plD_esc_fp) plD_esc_rasterqt; } void plD_bop_ppmqt( PLStream *pls ) { // Plot familying stuff. Not really understood, just copying gd.c plGetFam( pls ); pls->famadv = 1; pls->page++; if ( qt_family_check( pls ) ) { return; } ( (QtRasterDevice *) pls->dev )->definePlotName( pls->FileName, "PPM" ); ( (QtRasterDevice *) pls->dev )->setBackgroundColor( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b, pls->cmap0[0].a ); } #endif #if defined ( PLD_tiffqt ) void plD_dispatch_init_tiffqt( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "Qt tiff Driver"; pdt->pl_DevName = "tiffqt"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 70; pdt->pl_init = (plD_init_fp) plD_init_rasterqt; pdt->pl_line = (plD_line_fp) plD_line_rasterqt; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_rasterqt; pdt->pl_eop = (plD_eop_fp) plD_eop_rasterqt; pdt->pl_bop = (plD_bop_fp) plD_bop_tiffqt; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_rasterqt; pdt->pl_state = (plD_state_fp) plD_state_rasterqt; pdt->pl_esc = (plD_esc_fp) plD_esc_rasterqt; } void plD_bop_tiffqt( PLStream *pls ) { // Plot familying stuff. Not really understood, just copying gd.c plGetFam( pls ); pls->famadv = 1; pls->page++; if ( qt_family_check( pls ) ) { return; } ( (QtRasterDevice *) pls->dev )->definePlotName( pls->FileName, "TIFF" ); ( (QtRasterDevice *) pls->dev )->setBackgroundColor( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b, pls->cmap0[0].a ); } #endif #if defined ( PLD_svgqt ) && QT_VERSION >= 0x040300 void plD_dispatch_init_svgqt( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "Qt SVG Driver"; pdt->pl_DevName = "svgqt"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 71; pdt->pl_init = (plD_init_fp) plD_init_svgqt; pdt->pl_line = (plD_line_fp) plD_line_svgqt; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_svgqt; pdt->pl_eop = (plD_eop_fp) plD_eop_svgqt; pdt->pl_bop = (plD_bop_fp) plD_bop_svgqt; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_svgqt; pdt->pl_state = (plD_state_fp) plD_state_svgqt; pdt->pl_esc = (plD_esc_fp) plD_esc_svgqt; } void plD_init_svgqt( PLStream * pls ) { vectorize = 1; lines_aa = 1; plParseDrvOpts( qt_options ); // Stream setup pls->color = 1; pls->plbuf_write = 0; pls->dev_fill0 = 1; pls->dev_fill1 = 0; pls->dev_gradient = 1; // driver renders gradient // Let the PLplot core handle dashed lines since // the driver results for this capability have a number of issues. // pls->dev_dash=1; pls->dev_dash = 0; pls->dev_flush = 1; // Driver does not have a clear capability so use (good) PLplot core // fallback for that instead. pls->dev_clear = 0; pls->termin = 0; pls->page = 0; pls->dev_text = 1; // want to draw text pls->dev_unicode = 1; // want unicode pls->has_string_length = 1; // Driver supports string length calculations // Needs to be true only because of multi-stream case bool isMaster = initQtApp( true ); if ( pls->xlength <= 0 || pls->ylength <= 0 ) { pls->dev = new QtSVGDevice; pls->xlength = (int) ( ( (QtSVGDevice *) ( pls->dev ) )->m_dWidth ); pls->ylength = (int) ( ( (QtSVGDevice *) ( pls->dev ) )->m_dHeight ); } else { pls->dev = new QtSVGDevice( pls->xlength, pls->ylength ); } ( (QtSVGDevice *) pls->dev )->setPLStream( pls ); if ( isMaster ) handler.setMasterDevice( (QtSVGDevice *) ( pls->dev ) ); if ( pls->xlength > pls->ylength ) ( (QtSVGDevice *) ( pls->dev ) )->downscale = (PLFLT) pls->xlength / (PLFLT) ( PIXELS_X - 1 ); else ( (QtSVGDevice *) ( pls->dev ) )->downscale = (PLFLT) pls->ylength / (PLFLT) PIXELS_Y; plP_setphy( (PLINT) 0, (PLINT) ( pls->xlength / ( (QtSVGDevice *) ( pls->dev ) )->downscale ), (PLINT) 0, (PLINT) ( pls->ylength / ( (QtSVGDevice *) ( pls->dev ) )->downscale ) ); plP_setpxl( POINTS_PER_INCH / 25.4 / ( (QtSVGDevice *) ( pls->dev ) )->downscale, POINTS_PER_INCH / 25.4 / ( (QtSVGDevice *) ( pls->dev ) )->downscale ); // Initialize family file info plFamInit( pls ); plOpenFile( pls ); } void plD_bop_svgqt( PLStream *pls ) { // Plot familying stuff. Not really understood, just copying gd.c plGetFam( pls ); pls->famadv = 1; pls->page++; if ( qt_family_check( pls ) ) { return; } ( (QtSVGDevice *) pls->dev )->definePlotName( pls->FileName ); ( (QtSVGDevice *) pls->dev )->setBackgroundColor( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b, pls->cmap0[0].a ); } void plD_eop_svgqt( PLStream *pls ) { double downscale; QSize s; if ( qt_family_check( pls ) ) { return; } ( (QtSVGDevice *) pls->dev )->savePlot(); // Once saved, we have to create a new device with the same properties // to be able to plot another page. downscale = ( (QtSVGDevice *) pls->dev )->downscale; s = ( (QtSVGDevice *) pls->dev )->size(); bool isMaster = ( handler.isMasterDevice( (QtSVGDevice *) pls->dev ) ); delete ( (QtSVGDevice *) pls->dev ); pls->dev = new QtSVGDevice( s.width(), s.height() ); ( (QtSVGDevice *) pls->dev )->downscale = downscale; ( (QtSVGDevice *) pls->dev )->setPLStream( pls ); if ( isMaster ) handler.setMasterDevice( (QtSVGDevice *) pls->dev ); handler.DeviceChangedPage( (QtSVGDevice *) pls->dev ); } void plD_line_svgqt( PLStream * pls, short x1a, short y1a, short x2a, short y2a ) { QtSVGDevice* widget = (QtSVGDevice *) pls->dev; if ( widget != NULL && qt_family_check( pls ) ) { return; } if ( widget == NULL ) return; widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawLine( x1a, y1a, x2a, y2a ); } void plD_polyline_svgqt( PLStream *pls, short *xa, short *ya, PLINT npts ) { QtSVGDevice * widget = (QtSVGDevice *) pls->dev; if ( widget != NULL && qt_family_check( pls ) ) { return; } if ( widget == NULL ) return; widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawPolyline( xa, ya, npts ); } void plD_esc_svgqt( PLStream * pls, PLINT op, void* ptr ) { short *xa, *ya; unsigned char *r, *g, *b; PLFLT *alpha; PLINT i; QtSVGDevice * widget = (QtSVGDevice *) pls->dev; if ( widget != NULL && qt_family_check( pls ) ) { return; } if ( widget == NULL ) return; switch ( op ) { case PLESC_FILL: xa = new short[pls->dev_npts]; ya = new short[pls->dev_npts]; for ( i = 0; i < pls->dev_npts; i++ ) { xa[i] = pls->dev_x[i]; ya[i] = pls->dev_y[i]; } widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawPolygon( xa, ya, pls->dev_npts ); delete[] xa; delete[] ya; break; case PLESC_GRADIENT: xa = new short[pls->dev_npts]; ya = new short[pls->dev_npts]; r = new unsigned char[pls->ncol1]; g = new unsigned char[pls->ncol1]; b = new unsigned char[pls->ncol1]; alpha = new PLFLT[pls->ncol1]; for ( i = 0; i < pls->ncol1; i++ ) { r[i] = pls->cmap1[i].r; g[i] = pls->cmap1[i].g; b[i] = pls->cmap1[i].b; alpha[i] = pls->cmap1[i].a; } widget->setGradient( pls->xgradient[0], pls->xgradient[1], pls->ygradient[0], pls->ygradient[1], r, g, b, alpha, pls->ncol1 ); for ( i = 0; i < pls->dev_npts; i++ ) { xa[i] = pls->dev_x[i]; ya[i] = pls->dev_y[i]; } widget->drawPolygon( xa, ya, pls->dev_npts ); delete[] xa; delete[] ya; delete[] r; delete[] g; delete[] b; delete[] alpha; break; case PLESC_HAS_TEXT: // call the generic ProcessString function // ProcessString( pls, (EscText *)ptr ); widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawText( (EscText *) ptr ); break; default: break; } } void plD_state_svgqt( PLStream * pls, PLINT op ) { QtSVGDevice * widget = (QtSVGDevice *) pls->dev; if ( widget != NULL && qt_family_check( pls ) ) { return; } if ( widget == NULL ) return; switch ( op ) { case PLSTATE_WIDTH: widget->setWidthF( pls->width ); break; case PLSTATE_COLOR0: widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); break; case PLSTATE_COLOR1: widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); break; default: break; } } void plD_tidy_svgqt( PLStream * pls ) { QtSVGDevice * widget = (QtSVGDevice *) pls->dev; if ( widget != NULL ) { handler.DeviceClosed( widget ); delete widget; pls->dev = NULL; } plCloseFile( pls ); closeQtApp(); } #endif #if defined ( PLD_epsqt ) void plD_dispatch_init_epsqt( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "Qt EPS Driver"; pdt->pl_DevName = "epsqt"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 73; pdt->pl_init = (plD_init_fp) plD_init_epspdfqt; pdt->pl_line = (plD_line_fp) plD_line_epspdfqt; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_epspdfqt; pdt->pl_eop = (plD_eop_fp) plD_eop_epspdfqt; pdt->pl_bop = (plD_bop_fp) plD_bop_epsqt; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_epspdfqt; pdt->pl_state = (plD_state_fp) plD_state_epspdfqt; pdt->pl_esc = (plD_esc_fp) plD_esc_epspdfqt; } #endif #if defined ( PLD_pdfqt ) void plD_dispatch_init_pdfqt( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "Qt PDF Driver"; pdt->pl_DevName = "pdfqt"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 74; pdt->pl_init = (plD_init_fp) plD_init_epspdfqt; pdt->pl_line = (plD_line_fp) plD_line_epspdfqt; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_epspdfqt; pdt->pl_eop = (plD_eop_fp) plD_eop_epspdfqt; pdt->pl_bop = (plD_bop_fp) plD_bop_pdfqt; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_epspdfqt; pdt->pl_state = (plD_state_fp) plD_state_epspdfqt; pdt->pl_esc = (plD_esc_fp) plD_esc_epspdfqt; } #endif #if defined ( PLD_epsqt ) || defined ( PLD_pdfqt ) void plD_init_epspdfqt( PLStream * pls ) { vectorize = 0; lines_aa = 1; plParseDrvOpts( qt_options ); // Stream setup pls->color = 1; pls->plbuf_write = 0; pls->dev_fill0 = 1; pls->dev_fill1 = 0; pls->dev_gradient = 1; // driver renders gradient pls->dev_arc = 1; // driver renders arcs // Let the PLplot core handle dashed lines since // the driver results for this capability have a number of issues. // pls->dev_dash=1; pls->dev_dash = 0; pls->dev_flush = 1; // Driver does not have a clear capability so use (good) PLplot core // fallback for that instead. pls->dev_clear = 0; pls->termin = 0; pls->page = 0; pls->dev_text = 1; // want to draw text pls->dev_unicode = 1; // want unicode pls->has_string_length = 1; // Driver supports string length calculations // QPrinter devices won't create if there is no QApplication declared... // Needs to be true only because of multi-stream case bool isMaster = initQtApp( true ); if ( pls->xlength <= 0 || pls->ylength <= 0 ) { pls->dev = new QtEPSDevice; pls->xlength = (int) ( ( (QtEPSDevice *) ( pls->dev ) )->m_dWidth ); pls->ylength = (int) ( ( (QtEPSDevice *) ( pls->dev ) )->m_dHeight ); } else { pls->dev = new QtEPSDevice( pls->xlength, pls->ylength ); } ( (QtEPSDevice *) pls->dev )->setPLStream( pls ); if ( isMaster ) handler.setMasterDevice( (QtEPSDevice *) ( pls->dev ) ); if ( pls->xlength > pls->ylength ) ( (QtEPSDevice *) ( pls->dev ) )->downscale = (PLFLT) pls->xlength / (PLFLT) ( PIXELS_X - 1 ); else ( (QtEPSDevice *) ( pls->dev ) )->downscale = (PLFLT) pls->ylength / (PLFLT) PIXELS_Y; plP_setphy( (PLINT) 0, (PLINT) ( pls->xlength / ( (QtEPSDevice *) ( pls->dev ) )->downscale ), (PLINT) 0, (PLINT) ( pls->ylength / ( (QtEPSDevice *) ( pls->dev ) )->downscale ) ); plP_setpxl( POINTS_PER_INCH / 25.4 / ( (QtEPSDevice *) ( pls->dev ) )->downscale, POINTS_PER_INCH / 25.4 / ( (QtEPSDevice *) ( pls->dev ) )->downscale ); // Initialize family file info plFamInit( pls ); plOpenFile( pls ); } void plD_bop_epspdfqt_helper( PLStream *pls, int ifeps ) { // Plot familying stuff. Not really understood, just copying gd.c plGetFam( pls ); pls->famadv = 1; pls->page++; if ( qt_family_check( pls ) ) { return; } ( (QtEPSDevice *) pls->dev )->definePlotName( pls->FileName, ifeps ); ( (QtEPSDevice *) pls->dev )->setBackgroundColor( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b, pls->cmap0[0].a ); } void plD_eop_epspdfqt( PLStream *pls ) { double downscale; if ( qt_family_check( pls ) ) { return; } ( (QtEPSDevice *) pls->dev )->savePlot(); // Once saved, we have to create a new device with the same properties // to be able to plot another page. downscale = ( (QtEPSDevice *) pls->dev )->downscale; bool isMaster = handler.isMasterDevice( (QtEPSDevice *) pls->dev ); delete ( (QtEPSDevice *) pls->dev ); pls->dev = new QtEPSDevice; ( (QtEPSDevice *) pls->dev )->downscale = downscale; if ( isMaster ) handler.setMasterDevice( (QtEPSDevice *) pls->dev ); handler.DeviceChangedPage( (QtEPSDevice *) pls->dev ); } void plD_line_epspdfqt( PLStream * pls, short x1a, short y1a, short x2a, short y2a ) { QtEPSDevice* widget = (QtEPSDevice *) pls->dev; if ( widget != NULL && qt_family_check( pls ) ) { return; } if ( widget == NULL ) return; widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawLine( x1a, y1a, x2a, y2a ); } void plD_polyline_epspdfqt( PLStream *pls, short *xa, short *ya, PLINT npts ) { QtEPSDevice * widget = (QtEPSDevice *) pls->dev; if ( widget != NULL && qt_family_check( pls ) ) { return; } if ( widget == NULL ) return; widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawPolyline( xa, ya, npts ); } void plD_esc_epspdfqt( PLStream * pls, PLINT op, void* ptr ) { short *xa, *ya; unsigned char *r, *g, *b; PLFLT *alpha; PLINT i; QtEPSDevice * widget = (QtEPSDevice *) pls->dev; arc_struct *arc_info = (arc_struct *) ptr; if ( widget != NULL && qt_family_check( pls ) ) { return; } if ( widget == NULL ) return; switch ( op ) { case PLESC_FILL: xa = new short[pls->dev_npts]; ya = new short[pls->dev_npts]; for ( i = 0; i < pls->dev_npts; i++ ) { xa[i] = pls->dev_x[i]; ya[i] = pls->dev_y[i]; } widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawPolygon( xa, ya, pls->dev_npts ); delete[] xa; delete[] ya; break; case PLESC_GRADIENT: xa = new short[pls->dev_npts]; ya = new short[pls->dev_npts]; r = new unsigned char[pls->ncol1]; g = new unsigned char[pls->ncol1]; b = new unsigned char[pls->ncol1]; alpha = new PLFLT[pls->ncol1]; for ( i = 0; i < pls->ncol1; i++ ) { r[i] = pls->cmap1[i].r; g[i] = pls->cmap1[i].g; b[i] = pls->cmap1[i].b; alpha[i] = pls->cmap1[i].a; } widget->setGradient( pls->xgradient[0], pls->xgradient[1], pls->ygradient[0], pls->ygradient[1], r, g, b, alpha, pls->ncol1 ); for ( i = 0; i < pls->dev_npts; i++ ) { xa[i] = pls->dev_x[i]; ya[i] = pls->dev_y[i]; } widget->drawPolygon( xa, ya, pls->dev_npts ); delete[] xa; delete[] ya; delete[] r; delete[] g; delete[] b; delete[] alpha; break; case PLESC_HAS_TEXT: // call the generic ProcessString function // ProcessString( pls, (EscText *)ptr ); widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawText( (EscText *) ptr ); break; case PLESC_ARC: widget->drawArc( arc_info->x, arc_info->y, arc_info->a, arc_info->b, arc_info->angle1, arc_info->angle2, arc_info->rotate, arc_info->fill ); break; default: break; } } void plD_state_epspdfqt( PLStream * pls, PLINT op ) { QtEPSDevice * widget = (QtEPSDevice *) pls->dev; if ( widget != NULL && qt_family_check( pls ) ) { return; } if ( widget == NULL ) return; switch ( op ) { case PLSTATE_WIDTH: widget->setWidthF( pls->width ); break; case PLSTATE_COLOR0: widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); break; case PLSTATE_COLOR1: widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); break; default: break; } } void plD_tidy_epspdfqt( PLStream * pls ) { QtEPSDevice * widget = (QtEPSDevice *) pls->dev; if ( widget != NULL ) { handler.DeviceClosed( widget ); delete widget; pls->dev = NULL; } plCloseFile( pls ); closeQtApp(); } #endif #if defined ( PLD_epsqt ) void plD_bop_epsqt( PLStream *pls ) { plD_bop_epspdfqt_helper( pls, 1 ); } #endif #if defined ( PLD_pdfqt ) void plD_bop_pdfqt( PLStream *pls ) { plD_bop_epspdfqt_helper( pls, 0 ); } #endif #if defined ( PLD_qtwidget ) void plD_dispatch_init_qtwidget( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "Qt Widget"; pdt->pl_DevName = "qtwidget"; #endif pdt->pl_type = plDevType_Interactive; pdt->pl_seq = 72; pdt->pl_init = (plD_init_fp) plD_init_qtwidget; pdt->pl_line = (plD_line_fp) plD_line_qtwidget; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_qtwidget; pdt->pl_eop = (plD_eop_fp) plD_eop_qtwidget; pdt->pl_bop = (plD_bop_fp) plD_bop_qtwidget; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_qtwidget; pdt->pl_state = (plD_state_fp) plD_state_qtwidget; pdt->pl_esc = (plD_esc_fp) plD_esc_qtwidget; pdt->pl_wait = (plD_wait_fp) plD_wait_qtwidget; } void plD_init_qtwidget( PLStream * pls ) { vectorize = 0; lines_aa = 1; plParseDrvOpts( qt_options ); bool isMaster = initQtApp( true ); QtPLWidget* widget; if ( pls->xlength <= 0 || pls->ylength <= 0 ) { widget = new QtPLWidget; pls->dev = (void *) widget; pls->xlength = (int) widget->m_dWidth; pls->ylength = (int) widget->m_dHeight; } else { widget = new QtPLWidget( pls->xlength, pls->ylength ); pls->dev = (void *) widget; } widget->setPLStream( pls ); if ( isMaster ) handler.setMasterDevice( widget ); if ( plsc->xlength > plsc->ylength ) widget->downscale = (PLFLT) plsc->xlength / (PLFLT) ( PIXELS_X - 1 ); else widget->downscale = (PLFLT) plsc->ylength / (PLFLT) PIXELS_Y; plP_setphy( (PLINT) 0, (PLINT) ( plsc->xlength / widget->downscale ), (PLINT) 0, (PLINT) ( plsc->ylength / widget->downscale ) ); QPicture temp; QPainter tempPainter( &temp ); plP_setpxl( temp.logicalDpiX() / 25.4 / widget->downscale, temp.logicalDpiY() / 25.4 / widget->downscale ); pls->color = 1; // Is a color device pls->plbuf_write = 1; // Store commands to device in core buffer pls->dev_fill0 = 1; // Handle solid fills pls->dev_fill1 = 0; pls->dev_gradient = 1; // driver renders gradient pls->dev_arc = 1; // driver renders arcs // Let the PLplot core handle dashed lines since // the driver results for this capability have a number of issues. // pls->dev_dash=1; pls->dev_dash = 0; pls->dev_flush = 1; // Driver does not have a clear capability so use (good) PLplot core // fallback for that instead. pls->dev_clear = 0; pls->termin = 1; pls->dev_text = 1; // want to draw text pls->dev_unicode = 1; // want unicode pls->has_string_length = 1; // Driver supports string length calculations widget->setVisible( true ); widget->resize( plsc->xlength, plsc->ylength ); widget->move( plsc->xoffset, plsc->yoffset ); widget->setWindowTitle( pls->plwindow ); qApp->connect( &handler, SIGNAL( MasterChangedPage() ), widget, SLOT( nextPage() ) ); qApp->connect( &handler, SIGNAL( MasterClosed() ), widget, SLOT( close() ) ); } void plD_eop_qtwidget( PLStream *pls ) { QtPLWidget* widget = ( (QtPLWidget *) pls->dev ); widget->flush(); widget->raise(); } void plD_wait_qtwidget( PLStream *pls ) { QtPLWidget* widget = ( (QtPLWidget *) pls->dev ); int currentPage = widget->pageNumber; widget->raise(); while ( currentPage == widget->pageNumber && handler.isMasterDevice( widget ) ) { qApp->processEvents( QEventLoop::WaitForMoreEvents ); } } void plD_bop_qtwidget( PLStream *pls ) { QtPLWidget* widget = ( (QtPLWidget *) pls->dev ); widget->setBackgroundColor( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b, pls->cmap0[0].a ); } void plD_line_qtwidget( PLStream * pls, short x1a, short y1a, short x2a, short y2a ) { QtPLWidget* widget = (QtPLWidget *) pls->dev; if ( widget == NULL ) return; widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawLine( x1a, y1a, x2a, y2a ); } void plD_polyline_qtwidget( PLStream *pls, short *xa, short *ya, PLINT npts ) { QtPLWidget * widget = (QtPLWidget *) pls->dev; if ( widget == NULL ) return; widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawPolyline( xa, ya, npts ); } void plD_esc_qtwidget( PLStream * pls, PLINT op, void* ptr ) { short *xa, *ya; PLINT i; unsigned char *r, *g, *b; PLFLT *alpha; QtPLWidget * widget = (QtPLWidget *) pls->dev; arc_struct *arc_info = (arc_struct *) ptr; if ( widget == NULL ) return; switch ( op ) { case PLESC_FILL: xa = new short[pls->dev_npts]; ya = new short[pls->dev_npts]; for ( i = 0; i < pls->dev_npts; i++ ) { xa[i] = pls->dev_x[i]; ya[i] = pls->dev_y[i]; } widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawPolygon( xa, ya, pls->dev_npts ); delete[] xa; delete[] ya; break; case PLESC_GRADIENT: xa = new short[pls->dev_npts]; ya = new short[pls->dev_npts]; r = new unsigned char[pls->ncol1]; g = new unsigned char[pls->ncol1]; b = new unsigned char[pls->ncol1]; alpha = new PLFLT[pls->ncol1]; for ( i = 0; i < pls->ncol1; i++ ) { r[i] = pls->cmap1[i].r; g[i] = pls->cmap1[i].g; b[i] = pls->cmap1[i].b; alpha[i] = pls->cmap1[i].a; } widget->setGradient( pls->xgradient[0], pls->xgradient[1], pls->ygradient[0], pls->ygradient[1], r, g, b, alpha, pls->ncol1 ); for ( i = 0; i < pls->dev_npts; i++ ) { xa[i] = pls->dev_x[i]; ya[i] = pls->dev_y[i]; } widget->drawPolygon( xa, ya, pls->dev_npts ); delete[] xa; delete[] ya; delete[] r; delete[] g; delete[] b; delete[] alpha; break; case PLESC_HAS_TEXT: widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawText( (EscText *) ptr ); break; case PLESC_ARC: widget->drawArc( arc_info->x, arc_info->y, arc_info->a, arc_info->b, arc_info->angle1, arc_info->angle2, arc_info->rotate, arc_info->fill ); break; case PLESC_FLUSH: widget->flush(); break; case PLESC_GETC: widget->getCursorCmd( (PLGraphicsIn *) ptr ); break; default: break; } } void plD_state_qtwidget( PLStream * pls, PLINT op ) { QtPLWidget * widget = (QtPLWidget *) pls->dev; if ( widget == NULL ) return; switch ( op ) { case PLSTATE_WIDTH: widget->setWidthF( pls->width ); break; case PLSTATE_COLOR0: widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); break; case PLSTATE_COLOR1: widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); break; default: break; } } void plD_tidy_qtwidget( PLStream * pls ) { QtPLWidget * widget = (QtPLWidget *) pls->dev; if ( widget != NULL ) { handler.DeviceClosed( widget ); delete widget; pls->dev = NULL; } closeQtApp(); } #endif #if defined ( PLD_extqt ) void plD_dispatch_init_extqt( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "External Qt Widget"; pdt->pl_DevName = "extqt"; #endif pdt->pl_type = plDevType_Interactive; pdt->pl_seq = 75; pdt->pl_init = (plD_init_fp) plD_init_extqt; pdt->pl_line = (plD_line_fp) plD_line_extqt; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_extqt; pdt->pl_eop = (plD_eop_fp) plD_eop_extqt; pdt->pl_bop = (plD_bop_fp) plD_bop_extqt; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_extqt; pdt->pl_state = (plD_state_fp) plD_state_extqt; pdt->pl_esc = (plD_esc_fp) plD_esc_extqt; } void plD_init_extqt( PLStream * pls ) { vectorize = 0; lines_aa = 1; if ( pls->dev == NULL /* || pls->xlength <= 0 || pls->ylength <= 0*/ ) { printf( "Error: use plsetqtdev to set up the Qt device before calling plinit()\n" ); return; } QtExtWidget* widget = (QtExtWidget *) ( pls->dev ); if ( widget->m_dWidth > widget->m_dHeight ) widget->downscale = (PLFLT) widget->m_dWidth / (PLFLT) ( PIXELS_X - 1 ); else widget->downscale = (PLFLT) widget->m_dHeight / (PLFLT) PIXELS_Y; plP_setphy( (PLINT) 0, (PLINT) ( widget->m_dWidth / widget->downscale ), (PLINT) 0, (PLINT) ( widget->m_dHeight / widget->downscale ) ); QPicture temp; QPainter tempPainter( &temp ); plP_setpxl( temp.logicalDpiX() / 25.4 / widget->downscale, temp.logicalDpiY() / 25.4 / widget->downscale ); pls->color = 1; // Is a color device pls->plbuf_write = 0; pls->dev_fill0 = 1; // Handle solid fills pls->dev_fill1 = 0; pls->dev_gradient = 1; // driver renders gradient pls->dev_arc = 1; // driver renders arcs // Let the PLplot core handle dashed lines since // the driver results for this capability have a number of issues. // pls->dev_dash=1; pls->dev_dash = 0; pls->dev_flush = 1; // Driver does not have a clear capability so use (good) PLplot core // fallback for that instead. pls->dev_clear = 0; pls->termin = 0; pls->dev_text = 1; // want to draw text pls->dev_unicode = 1; // want unicode pls->has_string_length = 1; // Driver supports string length calculations } // // These functions are separated out (instead of using dynamic_cast) // for the benefit of the PyQt4 bindings. C++ QtExtWidgets created // by PyQt4 are not properly type resolved. // void plD_line_extqt( PLStream * pls, short x1a, short y1a, short x2a, short y2a ) { QtExtWidget * widget = NULL; widget = (QtExtWidget *) pls->dev; widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawLine( x1a, y1a, x2a, y2a ); } void plD_polyline_extqt( PLStream *pls, short *xa, short *ya, PLINT npts ) { QtExtWidget * widget = NULL; widget = (QtExtWidget *) pls->dev; widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawPolyline( xa, ya, npts ); } void plD_esc_extqt( PLStream * pls, PLINT op, void* ptr ) { short *xa, *ya; PLINT i; unsigned char *r, *g, *b; PLFLT *alpha; QtExtWidget * widget = NULL; arc_struct *arc_info = (arc_struct *) ptr; widget = (QtExtWidget *) pls->dev; switch ( op ) { case PLESC_FILL: xa = new short[pls->dev_npts]; ya = new short[pls->dev_npts]; for ( i = 0; i < pls->dev_npts; i++ ) { xa[i] = pls->dev_x[i]; ya[i] = pls->dev_y[i]; } widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawPolygon( xa, ya, pls->dev_npts ); delete[] xa; delete[] ya; break; case PLESC_GRADIENT: xa = new short[pls->dev_npts]; ya = new short[pls->dev_npts]; r = new unsigned char[pls->ncol1]; g = new unsigned char[pls->ncol1]; b = new unsigned char[pls->ncol1]; alpha = new PLFLT[pls->ncol1]; for ( i = 0; i < pls->ncol1; i++ ) { r[i] = pls->cmap1[i].r; g[i] = pls->cmap1[i].g; b[i] = pls->cmap1[i].b; alpha[i] = pls->cmap1[i].a; } widget->setGradient( pls->xgradient[0], pls->xgradient[1], pls->ygradient[0], pls->ygradient[1], r, g, b, alpha, pls->ncol1 ); for ( i = 0; i < pls->dev_npts; i++ ) { xa[i] = pls->dev_x[i]; ya[i] = pls->dev_y[i]; } widget->drawPolygon( xa, ya, pls->dev_npts ); delete[] xa; delete[] ya; delete[] r; delete[] g; delete[] b; delete[] alpha; break; case PLESC_HAS_TEXT: // call the generic ProcessString function // ProcessString( pls, (EscText *)ptr ); widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); widget->drawText( (EscText *) ptr ); break; case PLESC_ARC: widget->drawArc( arc_info->x, arc_info->y, arc_info->a, arc_info->b, arc_info->angle1, arc_info->angle2, arc_info->rotate, arc_info->fill ); break; default: break; } } void plD_state_extqt( PLStream * pls, PLINT op ) { QtExtWidget * widget = NULL; widget = (QtExtWidget *) pls->dev; switch ( op ) { case PLSTATE_WIDTH: widget->setWidthF( pls->width ); break; case PLSTATE_COLOR0: widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); break; case PLSTATE_COLOR1: widget->setColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a ); break; default: break; } } void plD_tidy_extqt( PLStream * pls ) { QtExtWidget * widget = NULL; widget = (QtExtWidget *) pls->dev; if ( widget != NULL ) { handler.DeviceClosed( widget ); delete widget; pls->dev = NULL; } closeQtApp(); } void plD_eop_extqt( PLStream * /* pls */ ) { } void plD_bop_extqt( PLStream *pls ) { QtExtWidget * widget = NULL; widget = (QtExtWidget *) pls->dev; widget->setBackgroundColor( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b, pls->cmap0[0].a ); } #endif #if defined ( PLD_memqt ) void plD_dispatch_init_memqt( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "Qt Memory Driver"; pdt->pl_DevName = "memqt"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 76; pdt->pl_init = (plD_init_fp) plD_init_memqt; pdt->pl_line = (plD_line_fp) plD_line_rasterqt; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_rasterqt; pdt->pl_eop = (plD_eop_fp) plD_eop_memqt; pdt->pl_bop = (plD_bop_fp) plD_bop_memqt; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_rasterqt; pdt->pl_state = (plD_state_fp) plD_state_rasterqt; pdt->pl_esc = (plD_esc_fp) plD_esc_rasterqt; } void plD_init_memqt( PLStream * pls ) { int i; double dpi; unsigned char *qt_mem; unsigned char *input_mem; vectorize = 0; lines_aa = 1; plParseDrvOpts( qt_options ); // Stream setup pls->color = 1; pls->plbuf_write = 0; pls->dev_fill0 = 1; pls->dev_fill1 = 0; pls->dev_gradient = 1; // driver renders gradient pls->dev_arc = 1; // driver renders arcs // Let the PLplot core handle dashed lines since // the driver results for this capability have a number of issues. // pls->dev_dash=1; pls->dev_dash = 0; pls->dev_flush = 1; // Driver does not have a clear capability so use (good) PLplot core // fallback for that instead. pls->dev_clear = 0; pls->termin = 0; pls->page = 0; pls->dev_text = 1; // want to draw text pls->dev_unicode = 1; // want unicode pls->has_string_length = 1; // Driver supports string length calculations // Needs to be true only because of multi-stream case bool isMaster = initQtApp( true ); if ( pls->xdpi <= 0. ) dpi = DEFAULT_DPI; else dpi = pls->xdpi; // Set the plot size to the memory buffer size, on the off chance // that they are different. pls->xlength = pls->phyxma; pls->ylength = pls->phyyma; // Save a pointer to the user supplied memory input_mem = (unsigned char *) pls->dev; // Create a appropriately sized raster device pls->dev = new QtRasterDevice( pls->xlength, pls->ylength ); ( (QtRasterDevice *) pls->dev )->setPLStream( pls ); ( (QtRasterDevice *) pls->dev )->memory = input_mem; if ( isMaster ) handler.setMasterDevice( (QtRasterDevice *) ( pls->dev ) ); if ( pls->xlength > pls->ylength ) ( (QtRasterDevice *) ( pls->dev ) )->downscale = (PLFLT) pls->xlength / (PLFLT) ( PIXELS_X - 1 ); else ( (QtRasterDevice *) ( pls->dev ) )->downscale = (PLFLT) pls->ylength / (PLFLT) PIXELS_Y; plP_setphy( (PLINT) 0, (PLINT) ( pls->xlength / ( (QtRasterDevice *) ( pls->dev ) )->downscale ), (PLINT) 0, (PLINT) ( pls->ylength / ( (QtRasterDevice *) ( pls->dev ) )->downscale ) ); plP_setpxl( dpi / 25.4 / ( (QtRasterDevice *) ( pls->dev ) )->downscale, dpi / 25.4 / ( (QtRasterDevice *) ( pls->dev ) )->downscale ); // Copy the user supplied memory into the QImage. // This device assumes that the format of the QImage // is RGB32 (or ARGB). qt_mem = ( (QtRasterDevice *) pls->dev )->scanLine( 0 ); for ( i = 0; i < ( pls->xlength * pls->ylength ); i++ ) { qt_mem[2] = input_mem[0]; // R qt_mem[1] = input_mem[1]; // G qt_mem[0] = input_mem[2]; // B if ( pls->dev_mem_alpha == 1 ) { qt_mem[3] = input_mem[3]; input_mem += 4; } else { input_mem += 3; } qt_mem += 4; } ( (QtRasterDevice *) ( pls->dev ) )->setResolution( dpi ); // This is set so the we'll always make it past the qt_family_check(). pls->family = true; } void plD_bop_memqt( PLStream * /* pls */ ) { // Do nothing to preserve user data } void plD_eop_memqt( PLStream *pls ) { int i; unsigned char *memory; unsigned char *qt_mem; memory = ( (QtRasterDevice *) pls->dev )->memory; qt_mem = ( (QtRasterDevice *) pls->dev )->scanLine( 0 ); for ( i = 0; i < ( pls->xlength * pls->ylength ); i++ ) { memory[0] = qt_mem[2]; // R memory[1] = qt_mem[1]; // G memory[2] = qt_mem[0]; // B if ( pls->dev_mem_alpha == 1 ) { memory[3] = qt_mem[3]; memory += 4; } else { memory += 3; } qt_mem += 4; } } #endif plplot-5.13.0/drivers/deprecated_wxwidgets_dc.cpp000644 001752 001752 00000045663 13150160115 023710 0ustar00softwaresoftware000000 000000 // Copyright (C) 2005 Werner Smekal, Sjaak Verdoold // Copyright (C) 2005 Germain Carrera Corraleche // Copyright (C) 1999 Frank Huebner // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // TODO: // - text clipping // - implement AddToClipRegion for text correctly // // wxwidgets headers #include #include "plDevs.h" // plplot headers #include "plplotP.h" // std and driver headers #include #include "deprecated_wxwidgets.h" //-------------------------------------------------------------------------- // wxPLDevDC::wxPLDevDC( void ) // // Constructor of the standard wxWidgets device based on the wxPLDevBase // class. Only some initialisations are done. //-------------------------------------------------------------------------- wxPLDevDC::wxPLDevDC( void ) : wxPLDevBase( wxBACKEND_DC ) { m_dc = NULL; m_bitmap = NULL; m_font = NULL; underlined = false; } //-------------------------------------------------------------------------- // wxPLDevDC::~wxPLDevDC( void ) // // The deconstructor frees memory allocated by the device. //-------------------------------------------------------------------------- wxPLDevDC::~wxPLDevDC() { if ( ownGUI ) { if ( m_dc ) { ( (wxMemoryDC *) m_dc )->SelectObject( wxNullBitmap ); delete m_dc; } if ( m_bitmap ) delete m_bitmap; } if ( m_font ) delete m_font; } //-------------------------------------------------------------------------- // void wxPLDevDC::DrawLine( short x1a, short y1a, short x2a, short y2a ) // // Draw a line from (x1a, y1a) to (x2a, y2a). //-------------------------------------------------------------------------- void wxPLDevDC::DrawLine( short x1a, short y1a, short x2a, short y2a ) { x1a = (short) ( x1a / scalex ); y1a = (short) ( height - y1a / scaley ); x2a = (short) ( x2a / scalex ); y2a = (short) ( height - y2a / scaley ); m_dc->DrawLine( (wxCoord) x1a, (wxCoord) y1a, (wxCoord) x2a, (wxCoord) y2a ); AddtoClipRegion( (int) x1a, (int) y1a, (int) x2a, (int) y2a ); } //-------------------------------------------------------------------------- // void wxPLDevDC::DrawPolyline( short *xa, short *ya, PLINT npts ) // // Draw a poly line - coordinates are in the xa and ya arrays. //-------------------------------------------------------------------------- void wxPLDevDC::DrawPolyline( short *xa, short *ya, PLINT npts ) { wxCoord x1a, y1a, x2a, y2a; x2a = (wxCoord) ( xa[0] / scalex ); y2a = (wxCoord) ( height - ya[0] / scaley ); for ( PLINT i = 1; i < npts; i++ ) { x1a = x2a; y1a = y2a; x2a = (wxCoord) ( xa[i] / scalex ); y2a = (wxCoord) ( height - ya[i] / scaley ); m_dc->DrawLine( x1a, y1a, x2a, y2a ); AddtoClipRegion( (int) x1a, (int) y1a, (int) x2a, (int) y2a ); } } //-------------------------------------------------------------------------- // void wxPLDevDC::ClearBackground( PLINT bgr, PLINT bgg, PLINT bgb, // PLINT x1, PLINT y1, PLINT x2, PLINT y2 ) // // Clear parts ((x1,y1) to (x2,y2)) of the background in color (bgr,bgg,bgb). //-------------------------------------------------------------------------- void wxPLDevDC::ClearBackground( PLINT bgr, PLINT bgg, PLINT bgb, PLINT x1, PLINT y1, PLINT x2, PLINT y2 ) { if ( x1 < 0 ) x1 = 0; else x1 = (PLINT) ( x1 / scalex ); if ( y1 < 0 ) y1 = 0; else y1 = (PLINT) ( height - y1 / scaley ); if ( x2 < 0 ) x2 = width; else x2 = (PLINT) ( x2 / scalex ); if ( y2 < 0 ) y2 = height; else y2 = (PLINT) ( height - y2 / scaley ); const wxPen oldPen = m_dc->GetPen(); const wxBrush oldBrush = m_dc->GetBrush(); m_dc->SetPen( *( wxThePenList->FindOrCreatePen( wxColour( bgr, bgg, bgb ), 1, wxSOLID ) ) ); m_dc->SetBrush( wxBrush( wxColour( bgr, bgg, bgb ) ) ); m_dc->DrawRectangle( x1, y1, x2 - x1, y2 - y1 ); m_dc->SetPen( oldPen ); m_dc->SetBrush( oldBrush ); AddtoClipRegion( x1, y1, x2, y2 ); } //-------------------------------------------------------------------------- // void wxPLDevDC::FillPolygon( PLStream *pls ) // // Draw a filled polygon. //-------------------------------------------------------------------------- void wxPLDevDC::FillPolygon( PLStream *pls ) { wxPoint *points = new wxPoint[pls->dev_npts]; wxCoord xoffset = 0; wxCoord yoffset = 0; for ( int i = 0; i < pls->dev_npts; i++ ) { points[i].x = (int) ( pls->dev_x[i] / scalex ); points[i].y = (int) ( height - pls->dev_y[i] / scaley ); if ( i > 0 ) AddtoClipRegion( points[i - 1].x, points[i - 1].y, points[i].x, points[i].y ); } if ( pls->dev_eofill ) { m_dc->DrawPolygon( pls->dev_npts, points, xoffset, yoffset, wxODDEVEN_RULE ); } else { m_dc->DrawPolygon( pls->dev_npts, points, xoffset, yoffset, wxWINDING_RULE ); } delete[] points; } //-------------------------------------------------------------------------- // void wxPLDevDC::BlitRectangle( wxDC* dc, int vX, int vY, // int vW, int vH ) // // Copy/Blit a rectangle ((vX,vY) to (vX+vW,vY+vH)) into given dc. //-------------------------------------------------------------------------- void wxPLDevDC::BlitRectangle( wxDC* dc, int vX, int vY, int vW, int vH ) { if ( m_dc ) dc->Blit( vX, vY, vW, vH, m_dc, vX, vY ); } //-------------------------------------------------------------------------- // void wxPLDevDC::CreateCanvas( void ) // // Create canvas (bitmap and dc) if the driver provides the GUI. //-------------------------------------------------------------------------- void wxPLDevDC::CreateCanvas() { if ( ownGUI ) { if ( !m_dc ) m_dc = new wxMemoryDC(); ( (wxMemoryDC *) m_dc )->SelectObject( wxNullBitmap ); // deselect bitmap if ( m_bitmap ) delete m_bitmap; m_bitmap = new wxBitmap( bm_width, bm_height, 32 ); ( (wxMemoryDC *) m_dc )->SelectObject( *m_bitmap ); // select new bitmap } } //-------------------------------------------------------------------------- // void wxPLDevDC::SetWidth( PLStream *pls ) // // Set the width of the drawing pen. //-------------------------------------------------------------------------- void wxPLDevDC::SetWidth( PLStream *pls ) { m_dc->SetPen( *( wxThePenList->FindOrCreatePen( wxColour( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b ), pls->width > 0 ? pls->width : 1, wxSOLID ) ) ); } //-------------------------------------------------------------------------- // void wxPLDevDC::SetColor0( PLStream *pls ) // // Set color from colormap 0. //-------------------------------------------------------------------------- void wxPLDevDC::SetColor0( PLStream *pls ) { m_dc->SetPen( *( wxThePenList->FindOrCreatePen( wxColour( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a * 255 ), pls->width > 0 ? pls->width : 1, wxSOLID ) ) ); m_dc->SetBrush( wxBrush( wxColour( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a * 255 ) ) ); } //-------------------------------------------------------------------------- // void wxPLDevDC::SetColor1( PLStream *pls ) // // Set color from colormap 1. //-------------------------------------------------------------------------- void wxPLDevDC::SetColor1( PLStream *pls ) { m_dc->SetPen( *( wxThePenList->FindOrCreatePen( wxColour( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a * 255 ), pls->width > 0 ? pls->width : 1, wxSOLID ) ) ); m_dc->SetBrush( wxBrush( wxColour( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, pls->curcolor.a * 255 ) ) ); } //-------------------------------------------------------------------------- // void wxPLDevDC::SetExternalBuffer( void* dc ) // // Adds a dc to the device. In that case, the drivers doesn't provide // a GUI. //-------------------------------------------------------------------------- void wxPLDevDC::SetExternalBuffer( void* dc ) { m_dc = (wxDC *) dc; // Add the dc to the device ready = true; ownGUI = false; } #ifdef PL_HAVE_FREETYPE //-------------------------------------------------------------------------- // void wxPLDevDC::PutPixel( short x, short y, PLINT color ) // // Draw a pixel in color color @ (x,y). //-------------------------------------------------------------------------- void wxPLDevDC::PutPixel( short x, short y, PLINT color ) { const wxPen oldpen = m_dc->GetPen(); m_dc->SetPen( *( wxThePenList->FindOrCreatePen( wxColour( GetRValue( color ), GetGValue( color ), GetBValue( color ) ), 1, wxSOLID ) ) ); m_dc->DrawPoint( x, y ); AddtoClipRegion( x, y, x, y ); m_dc->SetPen( oldpen ); } //-------------------------------------------------------------------------- // void wxPLDevDC::PutPixel( short x, short y ) // // Draw a pixel in current color @ (x,y). //-------------------------------------------------------------------------- void wxPLDevDC::PutPixel( short x, short y ) { m_dc->DrawPoint( x, y ); AddtoClipRegion( x, y, x, y ); } //-------------------------------------------------------------------------- // PLINT wxPLDevDC::GetPixel( short x, short y ) // // Get color information from pixel @ (x,y). //-------------------------------------------------------------------------- PLINT wxPLDevDC::GetPixel( short x, short y ) { #ifdef __WXGTK__ // The GetPixel method is incredible slow for wxGTK. Therefore we set the colour // always to the background color, since this is the case anyway 99% of the time. PLINT bgr, bgg, bgb; // red, green, blue (void) x; (void) y; // Cast to void to silence compiler warnings about unused parameters plgcolbg( &bgr, &bgg, &bgb ); // get background color information return RGB( bgr, bgg, bgb ); #else wxColour col; m_dc->GetPixel( x, y, &col ); return RGB( col.Red(), col.Green(), col.Blue() ); #endif } #endif // PL_HAVE_FREETYPE //-------------------------------------------------------------------------- // void wxPLDevDC::PSDrawTextToDC( char* utf8_string, bool drawText ) // // Draw utf8 text to screen if drawText==true. Otherwise determine // width and height of text. //-------------------------------------------------------------------------- void wxPLDevDC::PSDrawTextToDC( char* utf8_string, bool drawText ) { wxCoord w, h, d, l; wxString str( wxConvUTF8.cMB2WC( utf8_string ), *wxConvCurrent ); m_dc->GetTextExtent( str, &w, &h, &d, &l ); if ( drawText ) { m_dc->DrawRotatedText( str, (wxCoord) ( posX - yOffset / scaley * sin_rot ), (wxCoord) ( height - (wxCoord) ( posY + yOffset * cos_rot / scaley ) ), rotation * 180.0 / M_PI ); posX += (PLINT) ( w * cos_rot ); posY += (PLINT) ( w * sin_rot ); } textWidth += w; //keep track of the height of superscript text, the depth of subscript //text and the height of regular text if ( yOffset > 0.0001 ) { //determine the height the text would have if it were full size double currentOffset = yOffset; double currentHeight = h; while ( currentOffset > 0.0001 ) { currentOffset -= scaley * fontSize * fontScale / 2.; currentHeight *= 1.25; } textHeight = (wxCoord) textHeight > ( currentHeight ) ? textHeight : currentHeight; //work out the height including superscript superscriptHeight = superscriptHeight > ( currentHeight + yOffset / scaley ) ? superscriptHeight : static_cast( ( currentHeight + yOffset / scaley ) ); } else if ( yOffset < -0.0001 ) { //determine the height the text would have if it were full size double currentOffset = yOffset; double currentHeight = h; double currentDepth = d; while ( currentOffset < -0.0001 ) { currentOffset += scaley * fontSize * fontScale * 1.25 / 2.; currentHeight *= 1.25; currentDepth *= 1.25; } textHeight = (wxCoord) textHeight > currentHeight ? textHeight : currentHeight; //work out the additional depth for subscript note an assumption has been made //that the font size of (non-superscript and non-subscript) text is the same //along a line. Currently there is no escape to change font size mid string //so this should be fine subscriptDepth = (wxCoord) subscriptDepth > ( ( -yOffset / scaley + h + d ) - ( currentDepth + textHeight ) ) ? subscriptDepth : ( ( -yOffset / scaley + h + d ) - ( currentDepth + textHeight ) ); subscriptDepth = subscriptDepth > 0 ? subscriptDepth : 0; } else textHeight = (wxCoord) textHeight > ( h ) ? textHeight : h; memset( utf8_string, '\0', max_string_length ); } //-------------------------------------------------------------------------- // void wxPLDevDC::PSSetFont( PLUNICODE fci ) // // Set font defined by fci. //-------------------------------------------------------------------------- void wxPLDevDC::PSSetFont( PLUNICODE fci ) { unsigned char fontFamily, fontStyle, fontWeight; plP_fci2hex( fci, &fontFamily, PL_FCI_FAMILY ); plP_fci2hex( fci, &fontStyle, PL_FCI_STYLE ); plP_fci2hex( fci, &fontWeight, PL_FCI_WEIGHT ); if ( m_font ) delete m_font; m_font = wxFont::New( (int) ( fontSize * fontScale < 4 ? 4 : fontSize * fontScale ), fontFamilyLookup[fontFamily], fontStyleLookup[fontStyle] | fontWeightLookup[fontWeight] ); m_font->SetUnderlined( underlined ); m_dc->SetFont( *m_font ); } //-------------------------------------------------------------------------- // void wxPLDevDC::ProcessString( PLStream* pls, EscText* args ) // // This is the main function which processes the unicode text strings. // Font size, rotation and color are set, width and height of the // text string is determined and then the string is drawn to the canvas. //-------------------------------------------------------------------------- void wxPLDevDC::ProcessString( PLStream* pls, EscText* args ) { // Check that we got unicode, warning message and return if not if ( args->unicode_array_len == 0 ) { printf( "Non unicode string passed to the wxWidgets driver, ignoring\n" ); return; } // Check that unicode string isn't longer then the max we allow if ( args->unicode_array_len >= 500 ) { printf( "Sorry, the wxWidgets drivers only handles strings of length < %d\n", 500 ); return; } // Calculate the font size (in pixels) fontSize = pls->chrht * VIRTUAL_PIXELS_PER_MM / scaley * 1.3; // Use PLplot core routine to get the corners of the clipping rectangle PLINT rcx[4], rcy[4]; difilt_clip( rcx, rcy ); wxPoint cpoints[4]; for ( int i = 0; i < 4; i++ ) { cpoints[i].x = rcx[i] / scalex; cpoints[i].y = height - rcy[i] / scaley; } wxDCClipper clip( *m_dc, wxRegion( 4, cpoints ) ); // calculate rotation of text plRotationShear( args->xform, &rotation, &shear, &stride ); rotation -= pls->diorot * M_PI / 2.0; cos_rot = cos( rotation ); sin_rot = sin( rotation ); // Set font color m_dc->SetTextForeground( wxColour( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b ) ); m_dc->SetTextBackground( wxColour( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b ) ); PLUNICODE *lineStart = args->unicode_array; int lineLen = 0; bool lineFeed = false; bool carriageReturn = false; wxCoord paraHeight = 0; // Get the curent font fontScale = 1.0; yOffset = 0.0; plgfci( &fci ); PSSetFont( fci ); while ( lineStart != args->unicode_array + args->unicode_array_len ) { while ( lineStart + lineLen != args->unicode_array + args->unicode_array_len && *( lineStart + lineLen ) != (PLUNICODE) '\n' ) { lineLen++; } //set line feed for the beginning of this line and //carriage return for the end lineFeed = carriageReturn; carriageReturn = lineStart + lineLen != args->unicode_array + args->unicode_array_len && *( lineStart + lineLen ) == (PLUNICODE) ( '\n' ); if ( lineFeed ) paraHeight += textHeight + subscriptDepth; //remember the text parameters so they can be restored double startingFontScale = fontScale; double startingYOffset = yOffset; PLUNICODE startingFci = fci; // determine extent of text posX = args->x / scalex; posY = args->y / scaley; PSDrawText( lineStart, lineLen, false ); if ( lineFeed && superscriptHeight > textHeight ) paraHeight += superscriptHeight - textHeight; // actually draw text, resetting the font first fontScale = startingFontScale; yOffset = startingYOffset; fci = startingFci; PSSetFont( fci ); posX = (PLINT) ( args->x / scalex - ( args->just * textWidth ) * cos_rot - ( 0.5 * textHeight - paraHeight * lineSpacing ) * sin_rot ); //move to set alignment posY = (PLINT) ( args->y / scaley - ( args->just * textWidth ) * sin_rot + ( 0.5 * textHeight - paraHeight * lineSpacing ) * cos_rot ); PSDrawText( lineStart, lineLen, true ); //draw text lineStart += lineLen; if ( carriageReturn ) lineStart++; lineLen = 0; } //posX = args->x; //posY = args->y; //PSDrawText( args->unicode_array, args->unicode_array_len, false ); //posX = (PLINT) ( args->x - ( ( args->just * textWidth ) * cos_rot + ( 0.5 * textHeight ) * sin_rot ) * scalex ); //posY = (PLINT) ( args->y - ( ( args->just * textWidth ) * sin_rot - ( 0.5 * textHeight ) * cos_rot ) * scaley ); //PSDrawText( args->unicode_array, args->unicode_array_len, true ); AddtoClipRegion( 0, 0, width, height ); } plplot-5.13.0/drivers/wingdi.c000644 001752 001752 00000237576 13150160115 017764 0ustar00softwaresoftware000000 000000 // PLplot WIN32 GDI driver. // // Copyright (C) 2004 Andrew Roach // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // #include "plDevs.h" #ifdef PLD_wingdi #include #include #include // GET_X_LPARAM/GET_Y_LPARAM #include // For status bars #if !defined ( __CYGWIN__ ) #include #else #include #define _T( a ) __TEXT( a ) #endif #define NEED_PLDEBUG #define DEBUG #include "plplotP.h" #include "drivers.h" #include "plevent.h" #define ARRAY_SIZE( x ) ( ( sizeof x ) / ( sizeof *x ) ) #define INITIAL_HEAP_SIZE 16384 // Initial size of heap in bytes // Driver viewer types enum _dev_viewer { VIEWER_MINIMAL = 0, // Display just a plot area window VIEWER_FULL, // Display the full function window VIEWER_PLOT // A child only plot area }; // Enumerated type for the device states enum _dev_state { DEV_WAITING = 0, // Device is idle DEV_ACTIVE, // Device is ready for next plot DEV_SIZEMOVE, // Device might be sizing or moving the window DEV_RESIZE, // Device is resizing the window DEV_DRAWING // Device is actively drawing }; // Enumerated type used to indicate the device type // for updating page metrics enum _dev_type { DEV_WINDOW, // Setup page metrics for a window DEV_PRINTER // Setup page metrics for a printer }; // Data structure used to track the fonts that are created. // This avoids the overhead of recreating the font everytime // a string is rendered. struct _font_entry { LOGFONT info; HDC hdc; // Device context used to create font HFONT font; struct _font_entry *next; // Next entry in the font list }; // Device-specific info per stream struct wingdi_Dev { // // Members that are common to interactive GUI devices // PLFLT xdpmm; // Device x pixel per mm PLFLT ydpmm; // Device y pixel per mm PLFLT xscale; // Virtual x pixels to device pixel scaling PLFLT yscale; // Virtual y pixels to device pixel scaling PLINT width; // Window Width (which can change) PLINT height; // Window Height enum _dev_viewer viewer; enum _dev_type type; enum _dev_state state; // Current state of the device enum _dev_state prev_state; // Previous state of the device // Used to restore after redraw union { unsigned int status_bar : 1; unsigned int menu_bar : 1; } feature; // // WIN32 API variables // HDC hdc; // Plot window device context HPEN pen; // Current pen used for drawing COLORREF color; // Current color HDC hdc_bmp; // Bitmap device context HBITMAP bitmap; // Bitmap of current display HWND frame; // Handle for the main window. HWND plot; // Handle for the plot area HWND status_bar; // Handle for the status bar // // Image rasterization variables HDC save_hdc; // Save the original plot window DC HBITMAP raster_bmp; // Bitmap for the raster image RECT raster_rect; // Location of the image }; // Device info PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_wingdi = "wingdi:Windows GDI:1:wingdi:11:wingdi\n"; // Counter used to indicate the number of streams in use static int wingdi_streams = 0; // Window class for the overall frame static TCHAR * frame_window_class_name = _T( "PLplotFrame" ); static WNDCLASSEX frame_window_class; // Window class for the plot area static TCHAR * plot_area_class_name = _T( "PLplotArea" ); static WNDCLASSEX plot_area_class; // Popup menu used by a plot window static HMENU plot_popup_menu; // Private heap used to allocate memory for the driver static HANDLE wingdi_heap; // Font tracking list struct _font_entry *font_list = NULL; PLDLLIMPEXP_DRIVER void plD_dispatch_init_wingdi( PLDispatchTable *pdt ); void plD_init_wingdi( PLStream * ); //-------------------------------------------------------------------------- // Graphics primitive implementation functions //-------------------------------------------------------------------------- static void plD_line_wingdi( PLStream *, short, short, short, short ); static void plD_polyline_wingdi( PLStream *, short *, short *, PLINT ); static void plD_fill_polygon_wingdi( PLStream *pls ); static void plD_clear_wingdi( PLStream *pls, PLINT x1, PLINT y1, PLINT x2, PLINT y2 ); static void plD_eop_wingdi( PLStream * ); static void plD_bop_wingdi( PLStream * ); static void plD_tidy_wingdi( PLStream * ); static void plD_wait_wingdi( PLStream * ); static void plD_state_wingdi( PLStream *, PLINT ); static void plD_esc_wingdi( PLStream *, PLINT, void * ); enum commands { CommandPrint = 0x08A1, CommandNextPage = 0x08A2, CommandQuit = 0x08A3 }; #define PlotAreaId 0x08F0 #define StatusBarId 0x08F1 void plD_dispatch_init_wingdi( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "Win32/64 GDI device"; pdt->pl_DevName = "wingdi"; #endif pdt->pl_type = plDevType_Interactive; pdt->pl_seq = 11; pdt->pl_init = (plD_init_fp) plD_init_wingdi; pdt->pl_line = (plD_line_fp) plD_line_wingdi; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_wingdi; pdt->pl_eop = (plD_eop_fp) plD_eop_wingdi; pdt->pl_bop = (plD_bop_fp) plD_bop_wingdi; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_wingdi; pdt->pl_state = (plD_state_fp) plD_state_wingdi; pdt->pl_esc = (plD_esc_fp) plD_esc_wingdi; pdt->pl_wait = (plD_wait_fp) plD_wait_wingdi; } static HCURSOR CrossHairCursor( struct wingdi_Dev * dev ) { HCURSOR cursor; cursor = LoadCursor( NULL, IDC_CROSS ); SetClassLongPtr( dev->plot, GCL_HCURSOR, (long) cursor ); return SetCursor( cursor ); } static void NormalCursor( struct wingdi_Dev * dev ) { HCURSOR cursor; cursor = LoadCursor( NULL, IDC_ARROW ); SetClassLongPtr( dev->plot, GCL_HCURSOR, (LONG_PTR) cursor ); SetCursor( cursor ); } static void BusyCursor( struct wingdi_Dev * dev ) { HCURSOR cursor; cursor = LoadCursor( NULL, IDC_WAIT ); SetClassLongPtr( dev->plot, GCL_HCURSOR, (LONG_PTR) cursor ); SetCursor( cursor ); } static void update_status_bar( struct wingdi_Dev * dev ) { LPSTR status_text[] = { TEXT( "Waiting" ), TEXT( "Active" ), TEXT( "Size/Move" ), TEXT( "Resize" ), TEXT( "Drawing" ) }; if ( dev->status_bar == NULL ) return; SendMessage( dev->status_bar, SB_SETTEXT, (WPARAM) 0, (LPARAM) status_text[dev->state] ); } //-------------------------------------------------------------------------- // static void UpdatePageMetrics ( PLStream *pls, char flag ) // // UpdatePageMetrics is a simple function which simply gets new values // for a changed DC, be it swapping from printer to screen or vice-versa. // The flag variable is used to tell the function if it is updating // from the printer (1) or screen (0). //-------------------------------------------------------------------------- static void UpdatePageMetrics( PLStream *pls, enum _dev_type dev_type ) { struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev; dev->type = dev_type; if ( dev_type == DEV_PRINTER ) { // Get the page size from the printer PLINT vsize, hsize; PLFLT plot_aspect, print_aspect; hsize = GetDeviceCaps( dev->hdc, HORZRES ); vsize = GetDeviceCaps( dev->hdc, VERTRES ); // Determine which orientation best matches the aspect // ratio of the plot window plot_aspect = (PLFLT) dev->width / (PLFLT) dev->height; print_aspect = (PLFLT) hsize / (PLFLT) vsize; if ( plot_aspect > 1.0 ) { // Wider than tall dev->width = hsize; dev->height = (PLINT) ( (PLFLT) hsize / plot_aspect ); } else { // Taller than wide dev->width = (PLINT) ( (PLFLT) vsize * plot_aspect ); dev->height = vsize; } } else { RECT rect; GetClientRect( dev->plot, &rect ); dev->width = rect.right; dev->height = rect.bottom; pldebug( "wingdi", "Page size [%d %d] [%d %d]\n", rect.left, rect.top, rect.right, rect.bottom ); } // We need the -1 because some of the coordinates // are signed and PIXEL_X/PIXEL_Y can exceed the // maximum value of a positive signed integer, which // results in a negative value. dev->xscale = (PLFLT) ( PIXELS_X - 1 ) / dev->width; dev->yscale = (PLFLT) ( PIXELS_Y - 1 ) / dev->height; pldebug( "wingdi", "Scale = (%f %f) (FLT)\n", dev->xscale, dev->yscale ); // Need to get the DPI information from Windows // HORZRES/VERTRES = Width/Height in pixels // HORZSIZE/VERTSIZE = Width/Height in millimeters pldebug( "wingdi", "Original xdpi = %f ydpi = %f\n", pls->xdpi, pls->ydpi ); //dev->xdpmm = GetDeviceCaps( dev->hdc, HORZRES ) // / GetDeviceCaps( dev->hdc, HORZSIZE ); //pls->xdpi = dev->xdpmm * 25.4; //dev->ydpmm = GetDeviceCaps( dev->hdc, VERTRES ) // / GetDeviceCaps( dev->hdc, VERTSIZE ); //pls->ydpi = dev->ydpmm * 25.4; pls->xdpi = GetDeviceCaps( dev->hdc, LOGPIXELSX ); dev->xdpmm = pls->xdpi / 25.4; pls->ydpi = GetDeviceCaps( dev->hdc, LOGPIXELSY ); dev->ydpmm = pls->ydpi / 25.4; pldebug( "wingdi", "New xdpi = %f ydpi = %f\n", pls->xdpi, pls->ydpi ); pldebug( "wingdi", "Windows reports xdpi = %d ydpi = %d\n", GetDeviceCaps( dev->hdc, LOGPIXELSX ), GetDeviceCaps( dev->hdc, LOGPIXELSY ) ); // Set the mapping from pixels to mm plP_setpxl( dev->xscale * pls->xdpi / 25.4, dev->yscale * pls->ydpi / 25.4 ); // Set the physical limits for this device. See the // previous comment about the -1. plP_setphy( 0, PIXELS_X - 1, 0, PIXELS_Y - 1 ); } //-------------------------------------------------------------------------- // static void PrintPage ( PLStream *pls ) // // Function brings up a standard printer dialog and, after the user // has selected a printer, replots the current page to the windows // printer. //-------------------------------------------------------------------------- static void PrintPage( PLStream *pls ) { struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev; PRINTDLGEX Printer; DOCINFO docinfo; DEVMODE * hDevMode; struct wingdi_Dev *push; // A copy of the entire structure // Reset the docinfo structure to 0 and set it's fields up // This structure is used to supply a name to the print queue ZeroMemory( &docinfo, sizeof ( docinfo ) ); docinfo.cbSize = sizeof ( docinfo ); docinfo.lpszDocName = _T( "Plplot Page" ); // Set the defaults for the printer device // Allocate a moveable block of memory. Must use GlobalAlloc because // HeapAlloc is not moveable. hDevMode = GlobalAlloc( GHND, sizeof ( DEVMODE ) ); if ( hDevMode == NULL ) { plwarn( "wingdi: Failed to allocate memory for printer defaults\n" ); return; } ZeroMemory( hDevMode, sizeof ( DEVMODE ) ); hDevMode->dmSpecVersion = DM_SPECVERSION; hDevMode->dmSize = sizeof ( DEVMODE ); hDevMode->dmFields = DM_ORIENTATION; hDevMode->dmOrientation = DMORIENT_LANDSCAPE; // Reset out printer structure to zero and initialise it ZeroMemory( &Printer, sizeof ( PRINTDLGEX ) ); Printer.lStructSize = sizeof ( PRINTDLGEX ); Printer.hwndOwner = dev->plot; Printer.hDevMode = hDevMode; // Disable page ranges, default to collated output, // and return the device context (used to generate the output) Printer.Flags = PD_NOPAGENUMS | PD_NOCURRENTPAGE | PD_NOSELECTION | PD_COLLATE | PD_RETURNDC; // Currently, page ranges is disabled. This code // is left as a placeholder in case print ranges // is allowed in the future. There is no mechanism // implemented that facilitates the user interaction // on selecting pages, so it is best to turn it off // for now. Printer.nPageRanges = 0; Printer.nMaxPageRanges = 0; Printer.lpPageRanges = NULL; Printer.nMinPage = 0; Printer.nMaxPage = 0; // Other print parameter defaults Printer.nCopies = 1; //Printer.nPropertyPages = ARRAY_SIZE( hPrintPropSheetList ), //Printer.lphPropertyPages = hPrintPropSheetList; Printer.nStartPage = START_PAGE_GENERAL; // Call the printer dialog function. // If the user has clicked on "Print", then we will continue // processing and print out the page. if ( PrintDlgEx( &Printer ) == S_OK && Printer.dwResultAction == PD_RESULT_PRINT ) { // Before doing anything, we will take some backup copies // of the existing values for page size and the like, because // all we are going to do is a quick and dirty modification // of plplot's internals to match the new page size and hope // it all works out ok. After that, we will manip the values, // and when all is done, restore them. push = HeapAlloc( wingdi_heap, 0, sizeof ( struct wingdi_Dev ) ); if ( push != NULL ) { BusyCursor( dev ); // Save all the state information of this device memcpy( push, dev, sizeof ( struct wingdi_Dev ) ); // Change the device context to the printer dev->hdc = Printer.hDC; UpdatePageMetrics( pls, DEV_PRINTER ); // Now the stuff that actually does the printing !! StartDoc( dev->hdc, &docinfo ); plRemakePlot( pls ); EndDoc( dev->hdc ); // Now to undo everything back to what it was for the screen memcpy( dev, push, sizeof ( struct wingdi_Dev ) ); UpdatePageMetrics( pls, DEV_WINDOW ); HeapFree( wingdi_heap, 0, push ); NormalCursor( dev ); // Force a redraw to make sure the plot area is clean of // the leftovers from the print menu //RedrawWindow( dev->plot, // NULL, NULL, // RDW_ERASE | RDW_INVALIDATE | RDW_ERASENOW ); } else { plwarn( "wingdi: Unable to save state for print" ); } } // Cleanup after printing if ( Printer.hDC != NULL ) DeleteDC( Printer.hDC ); if ( Printer.hDevMode != NULL ) DeleteDC( Printer.hDevMode ); if ( Printer.hDevNames != NULL ) DeleteDC( Printer.hDevNames ); // Free allocated memory if ( hDevMode ) GlobalFree( hDevMode ); } static void wait_for_user_input( PLStream *pls ) { struct wingdi_Dev * dev = (struct wingdi_Dev *) pls->dev; MSG msg; pldebug( "wingdi", "Waiting for user input\n" ); // Update the state and the message in the status bar dev->state = DEV_WAITING; update_status_bar( dev ); // Process messages in the queue or until we are no longer waiting while ( GetMessage( &msg, NULL, 0, 0 ) && dev->state != DEV_ACTIVE ) { TranslateMessage( &msg ); switch ( (int) msg.message ) { case WM_CONTEXTMENU: case WM_RBUTTONDOWN: TrackPopupMenu( plot_popup_menu, TPM_CENTERALIGN | TPM_RIGHTBUTTON, LOWORD( msg.lParam ), HIWORD( msg.lParam ), 0, dev->plot, NULL ); break; case WM_CHAR: if ( ( (TCHAR) ( msg.wParam ) == 32 ) || ( (TCHAR) ( msg.wParam ) == 13 ) ) { dev->state = DEV_ACTIVE; update_status_bar( dev ); } else if ( ( (TCHAR) ( msg.wParam ) == 27 ) || ( (TCHAR) ( msg.wParam ) == 'q' ) || ( (TCHAR) ( msg.wParam ) == 'Q' ) ) { dev->state = DEV_ACTIVE; update_status_bar( dev ); PostQuitMessage( 0 ); } break; case WM_LBUTTONDBLCLK: pldebug( "wingdi", "WM_LBUTTONDBLCLK\n" ); dev->state = DEV_ACTIVE; update_status_bar( dev ); break; case WM_COMMAND: switch ( LOWORD( msg.wParam ) ) { case CommandPrint: pldebug( "wingdi", "CommandPrint\n" ); PrintPage( pls ); break; case CommandNextPage: pldebug( "wingdi", "CommandNextPage\n" ); dev->state = DEV_ACTIVE; update_status_bar( dev ); break; case CommandQuit: pldebug( "wingdi", "CommandQuit\n" ); dev->state = DEV_ACTIVE; update_status_bar( dev ); PostQuitMessage( 0 ); break; } break; default: DispatchMessage( &msg ); break; } } pldebug( "wingdi", "Done waiting\n" ); dev->state = DEV_ACTIVE; } //-------------------------------------------------------------------------- // GetCursorCmd() // // Handle events connected to selecting points (modelled after xwin) //-------------------------------------------------------------------------- static void GetCursorCmd( PLStream *pls, PLGraphicsIn *gin ) { struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev; MSG msg; HCURSOR previous; plGinInit( gin ); previous = CrossHairCursor( dev ); while ( gin->pX < 0 ) { GetMessage( &msg, NULL, 0, 0 ); TranslateMessage( &msg ); switch ( (int) msg.message ) { case WM_LBUTTONDOWN: gin->pX = msg.pt.x; gin->pY = msg.pt.y; gin->dX = (PLFLT) gin->pX / ( dev->width - 1 ); gin->dY = 1.0 - (PLFLT) gin->pY / ( dev->height - 1 ); if ( msg.wParam & MK_LBUTTON ) { pldebug( "wingdi", "Left button down\n" ); // Left button was pressed gin->button = 1; gin->state = 0; //gin->keysym = 0x20; } else if ( msg.wParam & MK_MBUTTON ) { pldebug( "wingdi", "Middle button down\n" ); // Middle button was pressed gin->button = 3; gin->state = 0; } else if ( msg.wParam & MK_RBUTTON ) { pldebug( "wingdi", "Right button down\n" ); // Right button was pressed gin->button = 2; gin->state = 0; } break; case WM_LBUTTONUP: gin->pX = msg.pt.x; gin->pY = msg.pt.y; gin->dX = (PLFLT) gin->pX / ( dev->width - 1 ); gin->dY = 1.0 - (PLFLT) gin->pY / ( dev->height - 1 ); if ( msg.wParam & MK_LBUTTON ) { pldebug( "wingdi", "Left button up\n" ); // Left button was pressed gin->button = 1; gin->state = 0x100; } else if ( msg.wParam & MK_MBUTTON ) { pldebug( "wingdi", "Middle button up\n" ); // Middle button was pressed gin->button = 3; gin->state = 0x10000; } else if ( msg.wParam & MK_RBUTTON ) { pldebug( "wingdi", "Right button up\n" ); // Right button was pressed gin->button = 2; gin->state = 0x1000; } break; case WM_CHAR: gin->pX = msg.pt.x; gin->pY = msg.pt.y; gin->dX = (PLFLT) gin->pX / ( dev->width - 1 ); gin->dY = 1.0 - (PLFLT) gin->pY / ( dev->height - 1 ); gin->button = 0; gin->state = 0; gin->keysym = msg.wParam; break; } } // Restore the previous cursor SetCursor( previous ); } //-------------------------------------------------------------------------- // static void CopySCRtoBMP(PLStream *pls) // Function copies the screen contents into a bitmap which is // later used for fast redraws of the screen (when it gets corrupted) // rather than remaking the plot from the plot buffer. //-------------------------------------------------------------------------- static void CopySCRtoBMP( PLStream *pls ) { struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev; RECT rect; HGDIOBJ previous; // Delete the existing bitmap before creating a new one if ( dev->hdc_bmp != NULL ) DeleteDC( dev->hdc_bmp ); if ( dev->bitmap != NULL ) DeleteObject( dev->bitmap ); dev->hdc_bmp = CreateCompatibleDC( dev->hdc ); GetClientRect( dev->plot, &rect ); dev->bitmap = CreateCompatibleBitmap( dev->hdc, rect.right, rect.bottom ); previous = SelectObject( dev->hdc_bmp, dev->bitmap ); BitBlt( dev->hdc_bmp, 0, 0, rect.right, rect.bottom, dev->hdc, 0, 0, SRCCOPY ); SelectObject( dev->hdc_bmp, previous ); } //-------------------------------------------------------------------------- // static void Erase ( PLStream *pls ) // // This function erases the client area of a window //-------------------------------------------------------------------------- static void Erase( PLStream *pls ) { struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev; COLORREF previous_color; RECT rect; pldebug( "wingdi", " Erasing window\n" ); // // This is a new "High Speed" way of filling in the background. // supposedly this executes faster than creating a brush and // filling a rectangle - go figure ? // if ( dev->type == DEV_WINDOW ) { // NOTE: Should GetUpdateRect be used instead? GetClientRect( dev->plot, &rect ); previous_color = SetBkColor( dev->hdc, RGB( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b ) ); ExtTextOut( dev->hdc, 0, 0, ETO_OPAQUE, &rect, _T( "" ), 0, 0 ); SetBkColor( dev->hdc, previous_color ); } else { rect.left = 0; rect.top = 0; rect.right = GetDeviceCaps( dev->hdc, HORZRES ); rect.bottom = GetDeviceCaps( dev->hdc, VERTRES ); } } //-------------------------------------------------------------------------- // static void Resize( PLStream *pls ) // // This function regenerates a plot by updating the page metrics and then // using the plot buffer to recreate the plot. This function assumes that is // being called from a WM_PAINT message, thus it does not do anything to force // a redraw. //-------------------------------------------------------------------------- static void Resize( PLStream *pls ) { struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev; RECT rect; enum _dev_state current; pldebug( "wingdi", "Resizing\n" ); // Only resize the window IF plplot is not busy if ( dev->state == DEV_WAITING || dev->state == DEV_ACTIVE || dev->state == DEV_DRAWING ) { GetClientRect( dev->plot, &rect ); pldebug( "wingdi", " Size = [%d %d] [%d %d]\n", rect.left, rect.top, rect.right, rect.bottom ); // Check to make sure it isn't just minimised (i.e. zero size) if ( ( rect.right > 0 ) && ( rect.bottom > 0 ) ) { UpdatePageMetrics( pls, DEV_WINDOW ); // Save the current state because remaking the plot // will change it when the BOP is executed. current = dev->state; plRemakePlot( pls ); dev->state = current; update_status_bar( dev ); } } else { pldebug( "wingdi", " No action taken, state = %d\n", dev->state ); } pldebug( "wingdi", "Resizing done\n" ); } //-------------------------------------------------------------------------- // This is the window function for the frame window. //-------------------------------------------------------------------------- LRESULT CALLBACK PlplotFrameProc( HWND hwnd, // handle to window UINT uMsg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam ) // second message parameter { struct wingdi_Dev *dev = NULL; // Try to get the address to the device data for this window #ifdef _WIN64 dev = (struct wingdi_Dev *) GetWindowLongPtr( hwnd, GWLP_USERDATA ); #else dev = (struct wingdi_Dev *) GetWindowLongPtr( hwnd, GWL_USERDATA ); #endif switch ( uMsg ) { case WM_CREATE: // Initialize the window. { HWND hStatus; // Create the status bar only when the frame is being created hStatus = CreateWindowEx( 0, STATUSCLASSNAME, NULL, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwnd, (HMENU) StatusBarId, GetModuleHandle( NULL ), NULL ); if ( hStatus != NULL ) { int status_widths[] = { 100, 200, 300, -1 }; SendMessage( hStatus, SB_SETPARTS, (WPARAM) ARRAY_SIZE( status_widths ), (LPARAM) status_widths ); SendMessage( hStatus, SB_SETTEXT, (WPARAM) 0, (LPARAM) (LPSTR) TEXT( "Active" ) ); } else { MessageBox( hwnd, "Could not create status bar.", "Error", MB_OK | MB_ICONERROR ); } } return 0; case WM_SIZE: { // Set the size and position of the window, including the // child controls HWND hStatus, hPlot; RECT rStatus, rFrame; int plot_height; // Get the client area of the frame GetClientRect( hwnd, &rFrame ); // Get the status bar size hStatus = GetDlgItem( hwnd, StatusBarId ); SendMessage( hStatus, WM_SIZE, 0, 0 ); GetWindowRect( hStatus, &rStatus ); // Set the size of the plot area hPlot = GetDlgItem( hwnd, PlotAreaId ); plot_height = rFrame.bottom - ( rStatus.bottom - rStatus.top ); SetWindowPos( hPlot, // Handle to the plot area window NULL, 0, 0, rFrame.right, plot_height, SWP_NOZORDER | SWP_SHOWWINDOW ); } return 0; case WM_DESTROY: // Clean up window-specific data objects. return 0; // // Process other messages. // default: return DefWindowProc( hwnd, uMsg, wParam, lParam ); } return 0; } //-------------------------------------------------------------------------- // This is the window function for the plot area. Whenever a message is // dispatched using DispatchMessage (or sent with SendMessage) this function // gets called with the contents of the message. //-------------------------------------------------------------------------- LRESULT CALLBACK PlplotPlotAreaProc( HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) { PLStream *pls = NULL; struct wingdi_Dev *dev = NULL; // During WM_CREATE message, the PLStream pointer is not set, thus // we need to handle this message without attempting to getting the user data if ( nMsg == WM_CREATE ) { // Signal that the message was handled return ( 0 ); } // Try to get the address to pls for this window #ifdef _WIN64 pls = (PLStream *) GetWindowLongPtr( hwnd, GWLP_USERDATA ); #else pls = (PLStream *) GetWindowLongPtr( hwnd, GWL_USERDATA ); #endif // If we did not get a valid pointer, pass the message to the // default message handler if ( pls == NULL ) { return DefWindowProc( hwnd, nMsg, wParam, lParam ); } dev = (struct wingdi_Dev *) pls->dev; // // Process the windows messages // // Everything except WM_CREATE is done here and it is generally hoped that // pls and dev are defined already by this stage. // That will be true MOST of the time. Some times WM_PAINT will be called // before we get to initialise the user data area of the window with the // pointer to the windows plplot stream // switch ( nMsg ) { case WM_DESTROY: pldebug( "wingdi", "WM_DESTROY\n" ); PostQuitMessage( 0 ); return ( 0 ); break; case WM_PAINT: // A WM_PAINT message gets sent on an expose, resize, and move // events. On expose and move events, a blit of a bitmap will // be performed. On a resize, the plot needs to be regenerated. // Because a WM_PAINT is sent after the triggering event // (e.g. a move), the triggering event is responsible for ensuring // a valid bitmap exists. Thus, this message handler only needs // to blit the bitmap to the window. pldebug( "wingdi", "WM_PAINT state = %d\n", dev->state ); // Check to see if there is an area that needs to be redrawn. // Per the MSDN document, calling GetUpdateRect with a NULL RECT // value will determine if there is an update region. BeginPaint() // will provide the update region in rcPaint. if ( GetUpdateRect( dev->plot, NULL, FALSE ) ) { // Yes there is an update region, start the redraw PAINTSTRUCT ps; HGDIOBJ previous; BusyCursor( dev ); BeginPaint( dev->plot, &ps ); pldebug( "wingdi", " Need to redraw area (%d,%d) (%d,%d)\n", ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom ); pldebug( "wingdi", " Erase status = %d\n", ps.fErase ); pldebug( "wingdi", " Device state = %d\n", dev->state ); // If we have a valid bitmap and are not currently drawing // a new plot, then we can blit the bitmap to handle the // redraw. On a resize, this will result in the bitmap being // clipped during the resize. A StretchBlt could be used to // rescale the bitmap; however, not all devices support // stretch blits. if ( dev->bitmap != NULL && dev->state != DEV_DRAWING ) { // A bitmap exists, thus only a blit is required pldebug( "wingdi", " Blit image\n" ); previous = SelectObject( dev->hdc_bmp, dev->bitmap ); BitBlt( dev->hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom, dev->hdc_bmp, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY ); SelectObject( dev->hdc_bmp, previous ); } else { pldebug( "wingdi", " No paint action bitmap = %lx state = \n", dev->bitmap, dev->state ); } EndPaint( dev->plot, &ps ); NormalCursor( dev ); } else { pldebug( "wingdi", " No update area to paint\n" ); } pldebug( "wingdi", "WM_PAINT exit\n" ); // Signal that the message was processed return ( 0 ); break; case WM_SIZE: pldebug( "wingdi", "WM_SIZE wParam = %d\n", wParam ); // The size might have changed. We only care about // Maximized events, a drag resize, or a restore. if ( wParam == SIZE_MAXIMIZED ) { // The window was maximized, which is a resize pldebug( "wingdi", " Window maximized\n" ); Resize( pls ); } else if ( dev->state == DEV_SIZEMOVE ) { // This is a drag resize. Must check before the SIZE_RESTORED // because a drag resize is also a SIZE_RESTORED. pldebug( "wingdi", " Window size/moved\n" ); // Change the state to indicate that the window has changed // size and not just moved. dev->state = DEV_RESIZE; pldebug( "wingdi", " New state %d\n", dev->state ); update_status_bar( dev ); } else if ( wParam == SIZE_RESTORED ) { // This could be a restore from a maximized or minimized state. // Unless code is added to detect the difference (i.e. look for // an earlier SIZE_MINIMIZED), just treat it as a resize pldebug( "wingdi", " Window restored\n" ); Resize( pls ); } else { pldebug( "wingdi", " Unknowing sizing action\n" ); } pldebug( "wingdi", "WM_SIZE exit\n" ); // Indicate that this message was processed return ( 0 ); break; case WM_ENTERSIZEMOVE: pldebug( "wingdi", "WM_ENTERSIZEMOVE\n" ); pldebug( "wingdi", " Save state %d\n", dev->state ); dev->prev_state = dev->state; // Indicate that we might be sizing or moving the window dev->state = DEV_SIZEMOVE; update_status_bar( dev ); return ( 0 ); break; case WM_EXITSIZEMOVE: pldebug( "wingdi", "WM_EXITSIZEMOVE\n" ); // If the window has been resized, regenerate the plot // for the new window dimensions if ( dev->state == DEV_RESIZE || dev->state == DEV_SIZEMOVE ) { pldebug( "wingdi", " Restore state %d\n", dev->prev_state ); // Restore the previous state before handling the resize // The resize routine needs the original state to preserve // the state when the buffer is replayed. dev->state = dev->prev_state; update_status_bar( dev ); Resize( pls ); } return ( 0 ); break; case WM_ERASEBKGND: // Determine if the window needs to be erased based // on the current state. // DEV_WAITING = No erase necessary, the next WM_PAINT // will repaint the affected area pldebug( "wingdi", "WM_ERASEBKGND state = %d\n", dev->state ); if ( dev->state != DEV_WAITING ) { Erase( pls ); // Indicate that the client area was erased return ( 1 ); } // Indicate no action was taken pldebug( "wingdi", " No erase action taken\n" ); return ( 0 ); break; case WM_MOUSEMOVE: { char mesg[80]; int xw, yw; double x, y; xw = GET_X_LPARAM( lParam ); yw = GET_Y_LPARAM( lParam ); x = (double) xw * dev->xscale; y = (double) ( dev->height - yw ) * dev->yscale; snprintf( mesg, sizeof ( mesg ), "%5.1lf", x ); if ( dev->status_bar != NULL ) SendMessage( dev->status_bar, SB_SETTEXT, (WPARAM) 1, (LPARAM) mesg ); snprintf( mesg, sizeof ( mesg ), "%5.1lf", y ); if ( dev->status_bar != NULL ) SendMessage( dev->status_bar, SB_SETTEXT, (WPARAM) 2, (LPARAM) mesg ); } // Indicate that we did not process this message return ( 1 ); break; case WM_COMMAND: pldebug( "wingdi", "WM_COMMAND\n" ); return ( 0 ); break; } // If we don't handle a message completely we hand it to the system // provided default window function. return DefWindowProc( hwnd, nMsg, wParam, lParam ); } //-------------------------------------------------------------------------- // wingdi_module_initialize() // // Handles the initialization of window classes and module global // variables. //-------------------------------------------------------------------------- static void wingdi_module_initialize( void ) { INITCOMMONCONTROLSEX init_controls; // Return if the module has been initialized // Use a postfix increment so that wingdi_streams is incremented // every time this function is called. That ensures a valid count // for the number of streams that are created. This allows the // module cleanup to free resources when the last stream is closed. if ( wingdi_streams++ > 0 ) return; pldebug( "wingdi", "module init\n" ); // Initialize common controls init_controls.dwSize = sizeof ( INITCOMMONCONTROLSEX ); init_controls.dwICC = ICC_BAR_CLASSES; if ( !InitCommonControlsEx( &init_controls ) ) { plwarn( "wingdi: Failed to initialize common Window controls\n" ); } // // Initialize the frame window class // // Initialize the entire structure to zero. memset( &frame_window_class, 0, sizeof ( WNDCLASSEX ) ); // Set the name of the plot window class frame_window_class.lpszClassName = frame_window_class_name; // Set the size of the window class structure, to include // any extra data that might be passed frame_window_class.cbSize = sizeof ( WNDCLASSEX ); // All windows of this class redraw when resized. frame_window_class.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC; // Set the callback function. frame_window_class.lpfnWndProc = PlplotFrameProc; // This class is used with the current program instance. frame_window_class.hInstance = GetModuleHandle( NULL ); // Use standard application icon and arrow cursor provided by the OS frame_window_class.hIcon = LoadIcon( NULL, IDI_APPLICATION ); frame_window_class.hIconSm = LoadIcon( NULL, IDI_APPLICATION ); frame_window_class.hCursor = LoadCursor( NULL, IDC_ARROW ); // Generic solid background for the frame window frame_window_class.hbrBackground = (HBRUSH) COLOR_WINDOW; // Do not allocate extra space for the callback frame_window_class.cbWndExtra = sizeof ( struct wingdi_Dev * ); // Now register the window class for use. RegisterClassEx( &frame_window_class ); // // Initialize the plot area class // // Initialize the entire structure to zero. memset( &plot_area_class, 0, sizeof ( WNDCLASSEX ) ); // Set the name of the plot window class plot_area_class.lpszClassName = plot_area_class_name; // Set the size of the window class structure, to include // any extra data that might be passed plot_area_class.cbSize = sizeof ( WNDCLASSEX ); // All windows of this class redraw when resized. plot_area_class.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC; // Set the callback function. plot_area_class.lpfnWndProc = PlplotPlotAreaProc; // This class is used with the current program instance. plot_area_class.hInstance = GetModuleHandle( NULL ); // Use standard application icon and arrow cursor provided by the OS plot_area_class.hIcon = LoadIcon( NULL, IDI_APPLICATION ); plot_area_class.hIconSm = LoadIcon( NULL, IDI_APPLICATION ); plot_area_class.hCursor = LoadCursor( NULL, IDC_ARROW ); // Handle the erase background in the callback function plot_area_class.hbrBackground = NULL; // Allocate extra space for a window instance to store the // pointer to the plot stream (PLStream) plot_area_class.cbWndExtra = sizeof ( PLStream * ); // Now register the window class for use. RegisterClassEx( &plot_area_class ); // // Create the popup menu used by the plot window // plot_popup_menu = CreatePopupMenu(); AppendMenu( plot_popup_menu, MF_STRING, CommandPrint, _T( "Print" ) ); AppendMenu( plot_popup_menu, MF_STRING, CommandNextPage, _T( "Next Page" ) ); AppendMenu( plot_popup_menu, MF_STRING, CommandQuit, _T( "Quit" ) ); // // Create a private heap to use for memory allocation. // This will keep memory separate from the overall program // and does not have the overhead of GlobalAlloc(). Requires // a minimum of Windows XP or Windows Server 2003 // wingdi_heap = HeapCreate( 0, INITIAL_HEAP_SIZE, 0 ); if ( wingdi_heap == NULL ) { //plexit("wingdi: Unable to allocate heap of size %d bytes", // INITIAL_HEAP_SIZE); plexit( "wingdi: Unable to allocate heap" ); } } static void wingdi_module_cleanup( void ) { struct _font_entry *ptr; wingdi_streams--; if ( wingdi_streams > 0 ) return; DeleteMenu( plot_popup_menu, CommandPrint, 0 ); DeleteMenu( plot_popup_menu, CommandNextPage, 0 ); DeleteMenu( plot_popup_menu, CommandQuit, 0 ); DestroyMenu( plot_popup_menu ); if ( !UnregisterClass( plot_area_class_name, plot_area_class.hInstance ) ) { plexit( "wingdi: Failed to unregister window class" ); } if ( !UnregisterClass( frame_window_class_name, frame_window_class.hInstance ) ) { plexit( "wingdi: Failed to unregister window class" ); } while ( font_list != NULL ) { ptr = font_list; DeleteObject( ptr->font ); font_list = ptr->next; HeapFree( wingdi_heap, 0, ptr ); } if ( HeapDestroy( wingdi_heap ) == 0 ) { plexit( "wingdi: Failed to destroy heap" ); } } //-------------------------------------------------------------------------- // plD_init_wingdi() // // Initialize device (terminal). //-------------------------------------------------------------------------- void plD_init_wingdi( PLStream *pls ) { struct wingdi_Dev *dev; int status_bar = 0; // Default to no status bar int full_viewer = 0; // Default to the minimal viewer DrvOpt wingdi_options[] = { { "full", DRV_INT, &full_viewer, "Enable full function viewer (0|1)" }, { "statusbar", DRV_INT, &status_bar, "Enable status bar (0|1)" }, { NULL, DRV_INT, NULL, NULL } }; TCHAR *program; #ifdef UNICODE int programlength; #endif pls->debug = 1; pldebug( "wingdi", "Device Init\n" ); // Initialize the module wingdi_module_initialize(); // Allocate and initialize device-specific data if ( pls->dev != NULL ) free( (void *) pls->dev ); pls->dev = calloc( 1, (size_t) sizeof ( struct wingdi_Dev ) ); if ( pls->dev == NULL ) plexit( "plD_init_wingdi_Dev: Out of memory." ); // Shortcut to the device specific data structure dev = (struct wingdi_Dev *) pls->dev; pls->icol0 = 1; // Set a fall back pen color in case user doesn't pls->termin = 1; // interactive device pls->graphx = GRAPHICS_MODE; // No text mode for this driver pls->dev_fill0 = 1; // driver can do solid area fills pls->dev_xor = 1; // driver supports xor mode pls->dev_clear = 1; // driver supports clear pls->dev_text = 1; // driver supports text pls->dev_gradient = 0; // driver not support gradient fills pls->dev_dash = 0; // driver can not do dashed lines (yet) pls->plbuf_write = 1; // driver uses the buffer for redraws if ( !pls->colorset ) pls->color = 1; // Check for and set up driver options plParseDrvOpts( wingdi_options ); // Set the appropriate viewer type if ( full_viewer ) dev->viewer = VIEWER_FULL; else dev->viewer = VIEWER_MINIMAL; // Determine which features should be enabled if ( status_bar ) dev->feature.status_bar = 1; else dev->feature.status_bar = 0; // Set up the initial device parameters. This will be updated // after the plot window is initialized. if ( pls->xlength <= 0 || pls->ylength <= 0 ) { // use default width, height of 800x600 if not specified by // -geometry option or plspage plspage( 0., 0., 800, 600, 0, 0 ); } dev->width = pls->xlength - 1; // should I use -1 or not??? dev->height = pls->ylength - 1; #ifdef UNICODE //convert the program name to wide char programlength = strlen( pls->program ) + 1; program = malloc( programlength * sizeof ( TCHAR ) ); MultiByteToWideChar( CP_UTF8, 0, pls->program, programlength, program, programlength ); #else program = pls->program; #endif if ( dev->viewer == VIEWER_FULL ) { // Create our main window using the plot window class dev->frame = CreateWindowEx( WS_EX_WINDOWEDGE + WS_EX_LEFT, frame_window_class_name, // Class name program, // Caption WS_OVERLAPPEDWINDOW // Window style | WS_CLIPCHILDREN, // Exclude child area from parent pls->xoffset, // Initial x (use default) pls->yoffset, // Initial y (use default) pls->xlength, // Initial x size (use default) pls->ylength, // Initial y size (use default) NULL, // No parent window NULL, // No menu frame_window_class.hInstance, // This program instance NULL // Creation parameters ); // Create the plot area dev->plot = CreateWindowEx( 0, plot_area_class_name, // Class name NULL, // Caption WS_CHILD | WS_VISIBLE, // Style 0, 0, 0, 0, // Position information dev->frame, // Parent window (HMENU) PlotAreaId, // No menu plot_area_class.hInstance, // This program instance NULL // Creation parameters ); } else { // Create the plot area dev->plot = CreateWindowEx( WS_EX_WINDOWEDGE + WS_EX_LEFT, plot_area_class_name, // Class name NULL, // Caption WS_OVERLAPPEDWINDOW // Style | WS_VISIBLE, pls->xoffset, // Initial x (use default) pls->yoffset, // Initial y (use default) pls->xlength, // Initial x size (use default) pls->ylength, // Initial y size (use default) NULL, // Parent window NULL, // No menu plot_area_class.hInstance, // This program instance NULL // Creation parameters ); } if ( dev->plot == NULL ) { plexit( "wingdi: Failed to create plot area\n" ); } #ifdef UNICODE free( program ); #endif // Attach a pointer to the stream to the window's user area // this pointer will be used by the windows call back for // process this window #ifdef _WIN64 SetWindowLongPtr( dev->plot, GWLP_USERDATA, (LONG_PTR) pls ); if ( dev->frame ) SetWindowLongPtr( dev->frame, GWLP_USERDATA, (LONG_PTR) dev ); #else SetWindowLongPtr( dev->plot, GWL_USERDATA, (LONG) pls ); if ( dev->frame ) SetWindowLongPtr( dev->frame, GWL_USERDATA, (LONG) dev ); #endif // Get the device context of the window that was created dev->hdc = GetDC( dev->plot ); // Set the initial color for the drawing pen plD_state_wingdi( pls, PLSTATE_COLOR0 ); // Display the window which we just created (using the nShow // passed by the OS, which allows for start minimized and that // sort of thing). if ( dev->viewer == VIEWER_FULL ) { ShowWindow( dev->frame, SW_SHOWDEFAULT ); SetForegroundWindow( dev->frame ); } else { ShowWindow( dev->plot, SW_SHOWDEFAULT ); SetForegroundWindow( dev->plot ); } if ( dev->feature.status_bar ) { // Get the handle for the status bar. This will // make it easier to update the contents dev->status_bar = GetDlgItem( dev->frame, StatusBarId ); } // Now we have to find out, from windows, just how big our drawing area is // when we specified the page size earlier on, that includes the borders, // title bar etc... so now that windows has done all its initialisations, // we will ask how big the drawing area is, and tell plplot UpdatePageMetrics( pls, DEV_WINDOW ); plspage( pls->xdpi, pls->ydpi, 0, 0, 0, 0 ); // Set fill rule. if ( pls->dev_eofill ) SetPolyFillMode( dev->hdc, ALTERNATE ); else SetPolyFillMode( dev->hdc, WINDING ); // Erase the plot window Erase( pls ); // Indicate that the plot window is active dev->state = DEV_ACTIVE; update_status_bar( dev ); } //-------------------------------------------------------------------------- // plD_line_wingdi() // // Draw a line in the current color from (x1,y1) to (x2,y2). //-------------------------------------------------------------------------- static void plD_line_wingdi( PLStream *pls, short x1a, short y1a, short x2a, short y2a ) { struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev; HGDIOBJ previous_obj; POINT points[2]; points[0].x = (LONG) ( x1a / dev->xscale ); points[1].x = (LONG) ( x2a / dev->xscale ); points[0].y = (LONG) ( dev->height - ( y1a / dev->yscale ) ); points[1].y = (LONG) ( dev->height - ( y2a / dev->yscale ) ); previous_obj = SelectObject( dev->hdc, dev->pen ); if ( points[0].x != points[1].x || points[0].y != points[1].y ) { Polyline( dev->hdc, points, 2 ); } else { SetPixel( dev->hdc, points[0].x, points[0].y, dev->color ); } SelectObject( dev->hdc, previous_obj ); } //-------------------------------------------------------------------------- // plD_polyline_wingdi() // // Draw a polyline in the current color. //-------------------------------------------------------------------------- static void plD_polyline_wingdi( PLStream *pls, short *xa, short *ya, PLINT npts ) { struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev; int i; POINT *points = NULL; HGDIOBJ previous_obj; if ( npts > 0 ) { points = HeapAlloc( wingdi_heap, HEAP_ZERO_MEMORY, (size_t) npts * sizeof ( POINT ) ); if ( points != NULL ) { for ( i = 0; i < npts; i++ ) { points[i].x = (LONG) ( xa[i] / dev->xscale ); points[i].y = (LONG) ( dev->height - ( ya[i] / dev->yscale ) ); } previous_obj = SelectObject( dev->hdc, dev->pen ); Polyline( dev->hdc, points, npts ); SelectObject( dev->hdc, previous_obj ); HeapFree( wingdi_heap, 0, points ); } else { plexit( "Could not allocate memory to \"plD_polyline_wingdi\"\n" ); } } } //-------------------------------------------------------------------------- // plD_fill_polygon_wingdi() // // Fill polygon described in points pls->dev_x[] and pls->dev_y[]. //-------------------------------------------------------------------------- static void plD_fill_polygon_wingdi( PLStream *pls ) { struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev; int i; POINT *points = NULL; HGDIOBJ previous_brush, previous_pen; HPEN hpen; HBRUSH fillbrush; // Do nothing if there are no points if ( pls->dev_npts == 0 ) return; points = HeapAlloc( wingdi_heap, HEAP_ZERO_MEMORY, (size_t) pls->dev_npts * sizeof ( POINT ) ); if ( points == NULL ) plexit( "Could not allocate memory to \"plD_fill_polygon_wingdi\"\n" ); for ( i = 0; i < pls->dev_npts; i++ ) { points[i].x = (PLINT) ( pls->dev_x[i] / dev->xscale ); points[i].y = (PLINT) ( dev->height - ( pls->dev_y[i] / dev->yscale ) ); } // Create a brush for the fill and a pen for the border fillbrush = CreateSolidBrush( dev->color ); hpen = CreatePen( PS_SOLID, 1, dev->color ); previous_brush = SelectObject( dev->hdc, fillbrush ); previous_pen = SelectObject( dev->hdc, hpen ); // Draw the filled polygon Polygon( dev->hdc, points, pls->dev_npts ); // Restore the previous objects and delete SelectObject( dev->hdc, previous_brush ); DeleteObject( fillbrush ); SelectObject( dev->hdc, previous_pen ); DeleteObject( hpen ); HeapFree( wingdi_heap, 0, points ); } //-------------------------------------------------------------------------- // plD_clear_wingdi() // // Clears the client area of a window. //-------------------------------------------------------------------------- static void plD_clear_wingdi( PLStream *pls, PLINT x1, PLINT y1, PLINT x2, PLINT y2 ) { struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev; COLORREF previous_color; RECT rect; if ( x1 >= 0 && y1 >= 0 && x2 >= 0 && y2 >= 0 ) { rect.left = (LONG) ( x1 / dev->xscale ); rect.top = (LONG) ( dev->height - ( y1 / dev->yscale ) ); rect.right = (LONG) ( x2 / dev->xscale ); rect.bottom = (LONG) ( dev->height - ( y2 / dev->yscale ) ); } else { // Invalid coordinates, erase the entire area GetClientRect( dev->plot, &rect ); } // This is a new "High Speed" way of filling in the background. // supposedly this executes faster than creating a brush and // filling a rectangle - go figure ? previous_color = SetBkColor( dev->hdc, RGB( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b ) ); ExtTextOut( dev->hdc, 0, 0, ETO_OPAQUE, &rect, _T( "" ), 0, 0 ); SetBkColor( dev->hdc, previous_color ); } struct _text_state { // Font information PLFLT font_height; LONG font_weight; BYTE font_charset; BYTE font_pitch; BYTE font_family; BYTE italic; // Font transformation PLFLT rotation, shear, stride; // Super/subscript state PLINT level; // super/subscript level PLFLT old_sscale, sscale; PLFLT old_soffset, soffset; }; // set_font() // // Sets the current font of the plot window device context to one that // best matches the desired attributes. The previous font is returned so // that the caller can restore the original font. This routine caches the // fonts that it creates to improve performance. static HFONT set_font( struct wingdi_Dev * dev, LONG font_height, LONG escapement, LONG weight, BYTE italic, BYTE charset, BYTE pitch, BYTE family ) { struct _font_entry *ptr = font_list; for ( ptr = font_list; ptr != NULL; ptr = ptr->next ) { if ( ptr->info.lfHeight == font_height && ptr->info.lfEscapement == escapement && ptr->info.lfWeight == weight && ptr->info.lfItalic == italic && ptr->info.lfCharSet == charset && ptr->info.lfPitchAndFamily == ( pitch | family ) && ptr->hdc == dev->hdc ) { return SelectObject( dev->hdc, ptr->font ); } } // Allocate space for this font entry ptr = HeapAlloc( wingdi_heap, 0, sizeof ( struct _font_entry ) ); // The font was not found, thus we need to create it ptr->info.lfHeight = font_height; ptr->info.lfWidth = 0; ptr->info.lfEscapement = escapement; // Escapement angle ptr->info.lfOrientation = 0; // Orientation angle ptr->info.lfWeight = weight; // Font weight (e.g. bold) ptr->info.lfItalic = italic; ptr->info.lfUnderline = 0; ptr->info.lfStrikeOut = 0; ptr->info.lfCharSet = charset; ptr->info.lfOutPrecision = OUT_OUTLINE_PRECIS; ptr->info.lfClipPrecision = CLIP_DEFAULT_PRECIS; ptr->info.lfQuality = ANTIALIASED_QUALITY; ptr->info.lfPitchAndFamily = pitch | family; ptr->font = CreateFontIndirect( &( ptr->info ) ); ptr->hdc = dev->hdc; ptr->next = NULL; if ( ptr->font == NULL ) { plwarn( "wingdi: Unable to create a font, using default\n" ); HeapFree( wingdi_heap, 0, ptr ); return NULL; } else { if ( font_list != NULL ) { ptr->next = font_list; font_list = ptr; } else { font_list = ptr; } } return SelectObject( dev->hdc, ptr->font ); } // Find the corresponding ANSI value for a given // Hershey character static const char hershey_to_ansi_lookup[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 0, 0, 0, '!', '"', '#', '$', '%', '&', '\'', // 30 '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', // 40 '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', // 50 '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', // 60 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', // 70 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', // 80 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', // 90 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', // 100 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', // 110 'x', 'y', 'z', '{', '|', '}', '~', 0, 0, 0, // 120 }; static const char hershey_to_symbol_lookup[] = { 0, 0xB7, 0x2B, 0x2A, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10 0, 0, 0, 0, 0, 0, 0, 0, 0xAC, 0xAE, // 20 0xAD, 0xAF }; static void process_text_escape( struct wingdi_Dev *dev, EscText *args, int *i, char *buffer, int *j, struct _text_state *state ) { int val; // Get the next character to determine what the escape action is switch ( args->string[++( *i )] ) { case 'u': // Superscript or end of subscript plP_script_scale( TRUE, &state->level, &state->old_sscale, &state->sscale, &state->old_soffset, &state->soffset ); set_font( dev, (LONG) ( state->font_height * state->sscale ), (LONG) ( state->rotation ), state->font_weight, state->italic, state->font_charset, state->font_pitch, state->font_family ); break; case 'd': // Subscript or end of superscript plP_script_scale( FALSE, &state->level, &state->old_sscale, &state->sscale, &state->old_soffset, &state->soffset ); set_font( dev, (LONG) ( state->font_height * state->sscale ), (LONG) ( state->rotation ), state->font_weight, state->italic, state->font_charset, state->font_pitch, state->font_family ); break; case 'f': // Font switch switch ( args->string[++( *i )] ) { case 'n': state->font_family = FF_SWISS; state->italic = 0; state->font_charset = ANSI_CHARSET; break; case 'r': state->font_family = FF_ROMAN; state->italic = 0; state->font_charset = ANSI_CHARSET; break; case 'i': state->font_family = FF_ROMAN; state->italic = 1; state->font_charset = ANSI_CHARSET; break; case 's': state->font_family = FF_SCRIPT; state->italic = 0; state->font_charset = ANSI_CHARSET; break; } set_font( dev, (LONG) ( state->font_height * state->sscale ), (LONG) ( state->rotation ), state->font_weight, state->italic, state->font_charset, state->font_pitch, state->font_family ); break; case 'g': // Greek character font state->font_family = FF_DONTCARE; state->italic = 0; state->font_charset = GREEK_CHARSET; set_font( dev, (LONG) ( state->font_height * state->sscale ), (LONG) ( state->rotation ), state->font_weight, state->italic, state->font_charset, state->font_pitch, state->font_family ); break; case '(': // Hershey character specified sscanf( args->string + *i, "%d", &val ); if ( val > 0 && val < ARRAY_SIZE( hershey_to_ansi_lookup ) && hershey_to_ansi_lookup[val] != 0 ) { buffer[( *j )++] = hershey_to_ansi_lookup[val]; } else { plwarn( "Unsupported hershey character\n" ); // Substitute a bullet so something is displayed buffer[( *j )++] = 0x95; } *i += 4; break; case '[': // Unicode character specified plwarn( "Unicode characters are not supported\n" ); *i += 4; break; default: plwarn( "Ignoring escape code %c\n" ); //plwarn( "Ignoring escape code %c\n", args->string[i-1] ); } } //-------------------------------------------------------------------------- // plD_text_wingdi() // // Renders text on the string. Support non-Unicode and Unicode strings. //-------------------------------------------------------------------------- static void plD_text_wingdi( PLStream * pls, EscText * args ) { struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev; struct _text_state state; PLFLT cos_rot, sin_rot; PLFLT cos_shear, sin_shear; int rx, ry; UINT halign, valign; HFONT font = NULL, prev_font = NULL; COLORREF prev_color; int prev_bkmode; int text_segments; char esc; // Get the escape character used to format strings plgesc( &esc ); // Determine the font characteristics state.italic = 0; state.font_weight = FW_NORMAL; state.font_charset = ANSI_CHARSET; state.font_pitch = DEFAULT_PITCH; state.font_family = FF_DONTCARE; switch ( pls->cfont ) { case 1: // normal = (medium, upright, sans serif) state.font_family = FF_SWISS; break; case 2: // roman = (medium, upright, serif) state.font_family = FF_ROMAN; break; case 3: // italic = (medium, italic, serif) state.font_family = FF_ROMAN; state.italic = 1; break; case 4: // script = (medium, upright, script) state.font_family = FF_SCRIPT; break; } // Calculate the font size from mm to device units state.font_height = 1.6 * pls->chrht * dev->ydpmm; plRotationShear( args->xform, &state.rotation, &state.shear, &state.stride ); state.rotation -= pls->diorot * PI / 2.0; cos_rot = (PLFLT) cos( state.rotation ); sin_rot = (PLFLT) sin( state.rotation ); cos_shear = (PLFLT) cos( state.shear ); sin_shear = (PLFLT) sin( state.shear ); // Convert from radians to tenths of a degree, which is what // CreateFont() uses state.rotation = state.rotation * ( 180.0 / M_PI ) * 10.0; // Is the page rotated? if ( pls->diorot != 0 ) { // The page is rotated. We need to apply the page rotation because // args x,y are not rotated. PLFLT x, y, ox, oy; PLFLT cos_theta, sin_theta; // Translate origin of the virtual coordinates to the midpoint ox = (PLFLT) args->x - PIXELS_X / 2; oy = (PLFLT) args->y - PIXELS_Y / 2; // Apply the orientation rotation cos_theta = cos( -pls->diorot * PI / 2.0 ); sin_theta = sin( -pls->diorot * PI / 2.0 ); x = cos_theta * (PLFLT) ox - sin_theta * (PLFLT) oy; y = sin_theta * (PLFLT) ox + cos_theta * (PLFLT) oy; // Untranslate origin x = x + PIXELS_X / 2; y = y + PIXELS_Y / 2; // Convert to device coordinates rx = (int) ( x / dev->xscale ); ry = (int) ( dev->height - ( y / dev->yscale ) ); } else { rx = (int) ( args->x / dev->xscale ); ry = (int) ( dev->height - ( args->y / dev->yscale ) ); } // Determine the location of the bounding rectangle relative to the // reference point (bottom, top, or center) // Windows draws the text within a rectangle, thus the rectangle // needs to be positioned relative to specified coordinate if ( args->base == 2 ) { // If base = 2, it is aligned with the top of the text box valign = TA_TOP; } else if ( args->base == 1 ) { // If base = 1, it is aligned with the baseline of the text box valign = TA_BOTTOM; } else { // If base = 0, it is aligned with the center of the text box valign = TA_TOP; // Adjust the reference point by 1/2 of the character height ry -= (int) ( 0.5 * pls->chrht * dev->ydpmm ); } // Text justification. Left (0.0), center (0.5) and right (1.0) // justification, which are the more common options, are supported. if ( args->just < 0.499 ) { // Looks left aligned halign = TA_LEFT; } else if ( args->just > 0.501 ) { // Looks right aligned halign = TA_RIGHT; } else { halign = TA_CENTER; } prev_font = set_font( dev, (LONG) state.font_height, (LONG) state.rotation, state.font_weight, state.italic, state.font_charset, state.font_pitch, state.font_family ); prev_color = SetTextColor( dev->hdc, dev->color ); prev_bkmode = SetBkMode( dev->hdc, TRANSPARENT ); if ( args->unicode_array_len == 0 ) { // Non unicode string size_t i, j, len; char * buffer; int height = 0, width = 0; // Determine the string length and allocate a buffer to // use as a working copy of the output string. The passed // string will always be larger than the formatted version len = strlen( args->string ); buffer = HeapAlloc( wingdi_heap, 0, ( len + 1 ) * sizeof ( char ) ); if ( buffer == NULL ) { plexit( "wingdi: Unable to allocate character buffer\n" ); } // First we need to determine the number of text segments in the // string. Text segments are delimited by the escape characters text_segments = 1; state.level = 0; state.sscale = 1.0; for ( i = j = 0; i < len + 1; i++ ) { if ( args->string[i] != esc && args->string[i] != '\0' ) { // Copy characters into the buffer to build the // string segment buffer[j++] = args->string[i]; } else if ( i != len && args->string[i + 1] == esc ) { // We have two escape characters in a row, which means // to ignore the escape and copy the character buffer[j++] = args->string[i]; i++; } else { if ( j > 0 ) { SIZE segment_size; // NUL terminate what we have copied so far buffer[j] = '\0'; // Determine the size of the text segment and add // it to the width of the overall string GetTextExtentPoint32( dev->hdc, buffer, j, &segment_size ); // The effect of super/subscripts on the size of the // bounding box is ignored at this time. This will result // in small positional errors. width += segment_size.cx; if ( segment_size.cy > height ) height = segment_size.cy; } j = 0; if ( i != len ) { text_segments++; process_text_escape( dev, args, &i, buffer, &j, &state ); } } } // Output the text segments if ( text_segments > 1 ) { UINT save_text_align; // We have the total width of all the text segments. Determine // the initial reference point based on the desired alignment. // When rendering text, we use left alignment and adjust the reference // point accordingly. switch ( halign ) { case TA_LEFT: break; case TA_RIGHT: rx -= width; break; case TA_CENTER: rx -= (int) ( cos_rot * 0.5 * (PLFLT) width ); ry += (int) ( sin_rot * 0.5 * (PLFLT) width ); break; } save_text_align = SetTextAlign( dev->hdc, TA_LEFT | valign ); state.level = 0; state.sscale = 1.0; for ( i = j = 0; i < len + 1; i++ ) { if ( args->string[i] != esc && args->string[i] != '\0' ) { // Copy characters into the buffer to build the // string segment buffer[j++] = args->string[i]; } else if ( i != len && args->string[i + 1] == esc ) { // We have two escape characters in a row, which means // to ignore the escape and copy the character buffer[j++] = args->string[i]; i++; } else { SIZE segment_size; int sx, sy; if ( j > 0 ) { // NUL terminate what we have copied so far buffer[j] = '\0'; GetTextExtentPoint32( dev->hdc, buffer, j, &segment_size ); // Determine the offset due to super/subscripts sx = (int) ( ( 1.0 - cos_rot ) * (PLFLT) state.soffset ); sy = (int) ( ( 1.0 - sin_rot ) * (PLFLT) state.soffset ); TextOut( dev->hdc, rx + sx, ry + sy, buffer, j ); rx += (int) ( cos_rot * (PLFLT) segment_size.cx ); ry -= (int) ( sin_rot * (PLFLT) segment_size.cx ); } j = 0; if ( i != len ) process_text_escape( dev, args, &i, buffer, &j, &state ); } } } HeapFree( wingdi_heap, 0, buffer ); } else { // Unicode string } if ( text_segments == 1 ) { // Only one text segment, thus this is a simple string // that can be written to the device in one step. We // check for this because many strings are simple and // this approach is faster UINT save_text_align; save_text_align = SetTextAlign( dev->hdc, halign | valign ); TextOut( dev->hdc, rx, ry, args->string, strlen( args->string ) ); SetTextAlign( dev->hdc, save_text_align ); } // Restore the previous text settings if ( prev_font != NULL ) SelectObject( dev->hdc, prev_font ); SetTextColor( dev->hdc, prev_color ); SetBkMode( dev->hdc, prev_bkmode ); } //-------------------------------------------------------------------------- // End of the page //-------------------------------------------------------------------------- static void plD_eop_wingdi( PLStream *pls ) { struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev; pldebug( "wingdi", "End of the page\n" ); if ( dev->type == DEV_WINDOW ) // Save a bitmap of the current screen to help with redraws CopySCRtoBMP( pls ); else EndPage( dev->hdc ); // Set the cursor to normal to indicate that the window is not busy NormalCursor( dev ); // Indicate that the driver is no longer drawing and is ready to continue dev->state = DEV_ACTIVE; } //-------------------------------------------------------------------------- // Beginning of the new page //-------------------------------------------------------------------------- static void plD_bop_wingdi( PLStream *pls ) { struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev; pldebug( "wingdi", "Start of Page\n" ); // Indicate that the device is actively drawing dev->state = DEV_DRAWING; // Update the status bar update_status_bar( dev ); // Change the cursor to indicate the program is busy BusyCursor( dev ); if ( dev->type == DEV_WINDOW ) { // Invalidate the page so that it gets erased on the WM_PAINT. It might be // better to just clear now and not invalidate. // NOTE: Is RedrawWindow needed only for the window output device type? //RedrawWindow( dev->plot, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ERASENOW ); SendMessage( dev->plot, WM_ERASEBKGND, (WPARAM) 0, (LPARAM) NULL ); } else { StartPage( dev->hdc ); } // Reset the pen color plD_state_wingdi( pls, PLSTATE_COLOR0 ); } //-------------------------------------------------------------------------- // Stream cleanup function //-------------------------------------------------------------------------- static void plD_tidy_wingdi( PLStream *pls ) { struct wingdi_Dev *dev = NULL; pldebug( "wingdi", "plD_tidy_wingdi\n" ); if ( pls->dev != NULL ) { dev = (struct wingdi_Dev *) pls->dev; if ( dev->hdc != NULL ) ReleaseDC( dev->plot, dev->hdc ); if ( dev->bitmap != NULL ) DeleteObject( dev->bitmap ); if ( dev->plot != NULL ) DestroyWindow( dev->plot ); if ( dev->status_bar != NULL ) DestroyWindow( dev->status_bar ); if ( dev->frame != NULL ) DestroyWindow( dev->frame ); free_mem( pls->dev ); wingdi_module_cleanup(); } } //-------------------------------------------------------------------------- // plD_wait_wingdi() // // Wait for user input. //-------------------------------------------------------------------------- static void plD_wait_wingdi( PLStream *pls ) { struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev; // Wait for the user to indicate the next action wait_for_user_input( pls ); } //-------------------------------------------------------------------------- // plD_state_wingdi() // // Handle change in PLStream state (color, pen width, fill attribute, etc). //-------------------------------------------------------------------------- static void plD_state_wingdi( PLStream *pls, PLINT op ) { struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev; switch ( op ) { case PLSTATE_COLOR0: case PLSTATE_COLOR1: dev->color = RGB( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b ); break; case PLSTATE_CMAP0: case PLSTATE_CMAP1: dev->color = RGB( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b ); break; } if ( dev->pen != NULL ) DeleteObject( dev->pen ); dev->pen = CreatePen( PS_SOLID, (int) pls->width, dev->color ); } //-------------------------------------------------------------------------- // plD_esc_wingdi() // // Handle PLplot escapes //-------------------------------------------------------------------------- #ifndef PLESC_TELLME // // Placeholder code from Greg Jung until it gets incorporated // somewhere else // struct passmeup { MSG *msg; HWND *hwnd; HDC *hdc; void *roomformore[6]; char *flags; }; #define PLESC_TELLME 41 #endif static void plD_esc_wingdi( PLStream *pls, PLINT op, void *ptr ) { struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev; switch ( op ) { case PLESC_GETC: GetCursorCmd( pls, (PLGraphicsIn *) ptr ); break; case PLESC_FILL: plD_fill_polygon_wingdi( pls ); break; case PLESC_CLEAR: plD_clear_wingdi( pls, pls->sppxmi, pls->sppymi, pls->sppxma, pls->sppyma ); break; case PLESC_HAS_TEXT: plD_text_wingdi( pls, (EscText *) ptr ); break; case PLESC_DOUBLEBUFFERING: // Ignore double buffering requests. It causes problems with // strip charts and most machines are fast enough that it // does not make much of a difference. break; case PLESC_XORMOD: if ( *(PLINT *) ( ptr ) == 0 ) SetROP2( dev->hdc, R2_COPYPEN ); else SetROP2( dev->hdc, R2_XORPEN ); break; case PLESC_START_RASTERIZE: // // pldebug( "wingdi", "Start Rasterize\n" ); // // The image rasterization in PLplot uses fills. It indicates // // the start and end with a PLESC_START_RASTERIZE and a // // PLESC_END_RASTERIZE escape code. Thus, when these codes are // // received, the wingdi driver will switch from rendering to the // // display and render to a bitmap instead // // // Save the original plot area DC // dev->save_hdc = dev->hdc; // // // Create a new DC and a bitmap // dev->hdc = CreateCompatibleDC( dev->save_hdc ); // GetClientRect( dev->plot, &dev->raster_rect ); // dev->raster_bmp = CreateCompatibleBitmap( dev->hdc, // dev->raster_rect.right, // dev->raster_rect.bottom ); // SelectObject( dev->hdc, dev->raster_bmp ); // break; case PLESC_END_RASTERIZE: // // pldebug( "wingdi", "Stop Rasterize\n" ); // // Display the bitmap // BitBlt( dev->save_hdc, // dev->raster_rect.left, dev->raster_rect.top, // dev->raster_rect.right, dev->raster_rect.bottom, // dev->hdc, // dev->raster_rect.left, dev->raster_rect.top, // SRCCOPY ); // // // Cleanup // if ( dev->raster_bmp != NULL ) // DeleteObject( dev->raster_bmp ); // if ( dev->hdc != NULL ) // DeleteDC( dev->hdc ); // // // Restore the original DC // dev->hdc = dev->save_hdc; // break; case PLESC_TELLME: { struct passmeup *pup = ptr; pup->hwnd = &( dev->plot ); pup->hdc = &( dev->hdc ); } break; } } #else int pldummy_wingdi() { return ( 0 ); } #endif // PLD_wingdidev plplot-5.13.0/drivers/plmeta.driver_info.in000644 001752 001752 00000000057 13150160115 022434 0ustar00softwaresoftware000000 000000 plmeta:PLplot Native Meta-File:0:plmeta:26:plm plplot-5.13.0/drivers/gd.c000644 001752 001752 00000152302 13150160115 017054 0ustar00softwaresoftware000000 000000 // PNG, GIF, and JPEG device driver based on libgd // // Copyright (C) 2004 Joao Cardoso // Copyright (C) 2002, 2003, 2004 Andrew Roach // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // GIF SUPPORT // // Following the expiration of Unisys's worldwide patents on lzw compression // GD 2.0.28+ have reinstated support for GIFs, and so support for this // format has been added to the GD family of drivers. GIF's only support // 1, 4 and 8 bit, so no truecolour. Why would you want GIFs though ? PNG is // a far superior format, not only giving you 1,4,8 and 24 bit, but also // better compression and just about all browsers now support them. // // // The GD drivers, PNG, GIF, and JPEG, support a number of different options // depending on the version of GD installed. // // If you have installed GD Ver 2.+ you gain support for truecolour (24 // bit, 16 millionish) modes as well as different line widths. These // capibilities are part of GD more so than the GD driver, so they aren't // available in any 1.? versions of the driver. // // 24 bit support is, by default, set to "auto" if you have V2.+ of GD. // What this means is the *driver* decides when to use 24 bit or 8 bit // modes for PNG files. The logic is rather simple - if you have less than // 257 colours, it is set to 8 bit mode, if more then it's in 24 bit mode. // This should work fine for most people, most of the time, in most // situations; however, it can be overridden in case it has to via the // "-drvopt" command line switch. The png driver has two related settings: // 8bit and // 24bit // // If either of these command line toggles are set, that mode becomes the // standard used regardless of the number of colours used. It can be envoked // as follows: // x08c -dev png -drvopt 8bit -fam -o 8bitpng // or // x08c -dev png -drvopt 24bit -fam -o 24bitpng // // NOTE: // The 24 bit PNG file is an RGBA file, not RGB - it includes alpha channel // (transparency). Transparency is set to opaque, but the fact it is an // RGBA and not an RGB might cause some problems with some viewers. // Sadly, I can't do anything about it... sorry. // // GIF files can only have 256 colours, so naturally truecolour mode is not // supported for this sub-driver. // // Stuff for GD V1.? as well as V2.+ // // optimise // // From version 1.17 of the GD driver, a command line option has been // added to try and optimise the PNG files. If successful, the optimise // command will create 4 bit (16 colour) PNGs instead of 8 bit (256 colour) // ones. This results in slightly smaller files with no loss in any colour // information. The function has no real memory overhead, but does have a // slight speed hit in exchange for the optimisation. For example: // x08c -dev png -drvopt 8bit,optimise -fam -o 8bitpng // forces the png driver to make 8bit pngs, and will then optimise any PNG // images with 16 or less colours into a 4 bit PNG. Note, this DOESN'T WORK // WITH 24bit PNGs yet, and will never work with JPEGs. // // // Also as of version 1.17 of the GD driver, the options for palette // modification previously set with the command line option "-hack" have // now been moved to two options settable from the -drvopt switch. // // def_black15 // // -drvopt def_black15 sets index 15, usually white, to black if index 0, // the background colour and usually black, has been set to white from the // command line option -bg // // swp_red15 // // -drvopt swp_red15 swaps index 15, usually white, with index 1, which is // usually red. This might be desirable occasionally, but it is principally // included for cases when the background has been set on the command line // to white, and the "def_black15" option has been issued to redefine index // 15 as black. By issuing a command like: // x08c -dev png -bg ffffff -drvopt def_black15,swp_red15 // the driver will set the background to white, then redefine index 15 of // cmap0, which is usually white to black, then swap index 2 (red) to 15 // (white originally, now black), so at the end of the day, the "default" // plotting colour is now black. Why do all of this ? It is a very quick // way of making a nice web-friendly png without having to redefine the // cmaps within your program. // // smoothlines // // -drvopt smoothlines=1 turns on anti-aliased line and polygong drawing if // you are using a 24bit mode. Unfortunately gd doesn't honour line // width when anti-aliasing, so by default it is off. // #include "plDevs.h" #if defined ( PLD_png ) || defined ( PLD_jpeg ) || defined ( PLD_gif ) #include "plplotP.h" #include "drivers.h" #include // Device info // // Don't knoq if all this logic is necessary, but basically we are going to // start with all three sub-drivers present, then work out way down to two // and finally one of each. // PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_gd = #if defined ( PLD_png ) "png:PNG file:0:gd:39:png\n" #endif #if defined ( PLD_jpeg ) "jpeg:JPEG file:0:gd:40:jpeg\n" #endif #if defined ( PLD_gif ) "gif:GIF file:0:gd:47:gif\n" #endif ; #if GD2_VERS >= 2 #ifdef PL_HAVE_FREETYPE #define SMOOTH_LINES_OK #endif #endif #ifdef PL_HAVE_FREETYPE // // Freetype support has been added to the GD family of drivers using the // plfreetype.c module, and implemented as a driver-specific optional extra // invoked via the -drvopt command line toggle. It uses the // "PLESC_HAS_TEXT" command for rendering within the driver. // // Freetype support is turned on/off at compile time by defining // "PL_HAVE_FREETYPE". // // To give the user some level of control over the fonts that are used, // environmental variables can be set to over-ride the definitions used by // the five default plplot fonts. // // Freetype rendering is used with the command line "-drvopt text". // Anti-aliased fonts can be used by issuing "-drvopt text,smooth" // #include "plfreetype.h" #endif // Prototypes for functions in this file. static void fill_polygon( PLStream *pls ); static void setcmap( PLStream *pls ); static void plD_init_png_Dev( PLStream *pls ); static void plD_gd_optimise( PLStream *pls ); static void plD_black15_gd( PLStream *pls ); static void plD_red15_gd( PLStream *pls ); #ifdef PLD_gif static void plD_init_gif_Dev( PLStream *pls ); #endif #ifdef PL_HAVE_FREETYPE static void plD_pixel_gd( PLStream *pls, short x, short y ); static PLINT plD_read_pixel_gd( PLStream *pls, short x, short y ); static void plD_set_pixel_gd( PLStream *pls, short x, short y, PLINT colour ); static void init_freetype_lv1( PLStream *pls ); static void init_freetype_lv2( PLStream *pls ); #endif // top level declarations static int NCOLOURS = gdMaxColors; // In an attempt to fix a problem with the hidden line removal functions // that results in hidden lines *not* being removed from "small" plot // pages (ie, like a normal video screen), a "virtual" page of much // greater size is used to trick the algorithm into working correctly. // If, in future, this gets fixed on its own, then don't define // "use_experimental_hidden_line_hack" // #define use_experimental_hidden_line_hack // I think the current version of Freetype supports up to a maximum of // 128 grey levels for text smoothing. You can get quite acceptable // results with as few as 4 grey-levels. Uusually only about 5 get used // anyway, but the question is where, in the "grey spectrum" will they be ? // Who knows ? The following define lets you set a maximum limit on the // number of grey-levels used. It is really only here for the 24bit mode // and could be set to 255, but that would slow things down and use more // memory. 64 seems to be a nice compromise, but if you want to change it, // then change it here. // #ifndef max_number_of_grey_levels_used_in_text_smoothing #define max_number_of_grey_levels_used_in_text_smoothing 64 #endif // Not present in versions before 2.0 #ifndef gdImagePalettePixel #define gdImagePalettePixel( im, x, y ) ( im )->pixels[( y )][( x )] #endif #if GD2_VERS >= 2 int plToGdAlpha( PLFLT a ) { int tmp = (int) ( ( 1.0 - a ) * gdAlphaMax ); return tmp; } #endif // Struct to hold device-specific info. typedef struct { gdImagePtr im_out; // Graphics pointer PLINT pngx; PLINT pngy; int colour; // Current Colour int totcol; // Total number of colours int ncol1; // Actual size of ncol1 we got PLFLT scale; // scaling factor to "blow up" to // the "virtual" page in removing hidden lines int optimise; // Flag used for 4bit pngs int black15; // Flag used for forcing a black colour int red15; // Flag for swapping red and 15 unsigned char TRY_BLENDED_ANTIALIASING; // Flag to try and set up BLENDED ANTIALIASING #if GD2_VERS >= 2 int truecolour; // Flag to ALWAYS force 24 bit mode int palette; // Flag to ALWAYS force 8 bit mode unsigned char smooth; // Flag to ask for line smoothing #endif } png_Dev; void plD_init_png( PLStream * ); void plD_line_png( PLStream *, short, short, short, short ); void plD_polyline_png( PLStream *, short *, short *, PLINT ); void plD_eop_png( PLStream * ); void plD_eop_jpeg( PLStream * ); void plD_bop_png( PLStream * ); void plD_tidy_png( PLStream * ); void plD_state_png( PLStream *, PLINT ); void plD_esc_png( PLStream *, PLINT, void * ); #ifdef PLD_gif void plD_init_gif( PLStream * ); void plD_eop_gif( PLStream * ); #endif #ifdef PLD_png void plD_dispatch_init_png( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "PNG file"; pdt->pl_DevName = "png"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 39; pdt->pl_init = (plD_init_fp) plD_init_png; pdt->pl_line = (plD_line_fp) plD_line_png; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_png; pdt->pl_eop = (plD_eop_fp) plD_eop_png; pdt->pl_bop = (plD_bop_fp) plD_bop_png; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_png; pdt->pl_state = (plD_state_fp) plD_state_png; pdt->pl_esc = (plD_esc_fp) plD_esc_png; } #endif #ifdef PLD_jpeg void plD_dispatch_init_jpeg( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "JPEG File"; pdt->pl_DevName = "jpeg"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 40; pdt->pl_init = (plD_init_fp) plD_init_png; pdt->pl_line = (plD_line_fp) plD_line_png; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_png; pdt->pl_eop = (plD_eop_fp) plD_eop_jpeg; pdt->pl_bop = (plD_bop_fp) plD_bop_png; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_png; pdt->pl_state = (plD_state_fp) plD_state_png; pdt->pl_esc = (plD_esc_fp) plD_esc_png; } #endif #ifdef PLD_gif void plD_dispatch_init_gif( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "GIF File"; pdt->pl_DevName = "gif"; #endif pdt->pl_type = plDevType_FileOriented; pdt->pl_seq = 47; pdt->pl_init = (plD_init_fp) plD_init_gif; pdt->pl_line = (plD_line_fp) plD_line_png; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_png; pdt->pl_eop = (plD_eop_fp) plD_eop_gif; pdt->pl_bop = (plD_bop_fp) plD_bop_png; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_png; pdt->pl_state = (plD_state_fp) plD_state_png; pdt->pl_esc = (plD_esc_fp) plD_esc_png; } #endif //-------------------------------------------------------------------------- // plD_init_png_Dev() // //-------------------------------------------------------------------------- static void plD_init_png_Dev( PLStream *pls ) { png_Dev *dev; // Stuff for the driver options, these vars are copied into the driver // structure so that everything is thread safe and reenterant. // static int optimise = 0; static int black15 = 0; static int red15 = 0; #if GD2_VERS >= 2 static int truecolour = 0; static int palette = 0; static int smooth_line = 0; #endif #ifdef PL_HAVE_FREETYPE static int freetype = 1; static int smooth_text = 1; FT_Data *FT; #endif DrvOpt gd_options[] = { { "optimise", DRV_INT, &optimise, "Optimise PNG palette when possible" }, { "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\"" }, { "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" }, #if GD2_VERS >= 2 { "8bit", DRV_INT, &palette, "Palette (8 bit) mode" }, { "24bit", DRV_INT, &truecolour, "Truecolor (24 bit) mode" }, { "smoothlines", DRV_INT, &smooth_line, "Turn line Anti Aliasing on (1) or off (0)" }, #endif #ifdef PL_HAVE_FREETYPE { "text", DRV_INT, &freetype, "Use driver text (FreeType)" }, { "smooth", DRV_INT, &smooth_text, "Turn text smoothing on (1) or off (0)" }, #endif { NULL, DRV_INT, NULL, NULL } }; // Allocate and initialize device-specific data if ( pls->dev != NULL ) free( (void *) pls->dev ); pls->dev = calloc( 1, (size_t) sizeof ( png_Dev ) ); if ( pls->dev == NULL ) plexit( "plD_init_png_Dev: Out of memory." ); dev = (png_Dev *) pls->dev; dev->colour = 1; // Set a fall back pen colour in case user doesn't // Check for and set up driver options plParseDrvOpts( gd_options ); dev->black15 = black15; dev->red15 = red15; dev->optimise = optimise; #if GD2_VERS >= 2 dev->palette = palette; dev->truecolour = truecolour; if ( ( dev->truecolour > 0 ) && ( dev->palette > 0 ) ) plwarn( "Selecting both \"truecolor\" AND \"palette\" driver options is contradictory, so\nI will just use my best judgment.\n" ); else if ( dev->truecolour > 0 ) NCOLOURS = 16777216; else if ( ( dev->truecolour == 0 ) && ( dev->palette == 0 ) && ( ( pls->ncol1 + pls->ncol0 ) > NCOLOURS ) ) { NCOLOURS = 16777216; } if ( ( dev->palette == 0 ) && ( dev->optimise == 0 ) && ( smooth_line == 1 ) ) dev->smooth = 1; // Allow smoothing of lines if we have a truecolour device #endif #ifdef PL_HAVE_FREETYPE if ( freetype ) { pls->dev_text = 1; // want to draw text pls->dev_unicode = 1; // want unicode // As long as we aren't optimising, we'll try to use better antialaising // We can also only do this if the user wants smoothing, and hasn't // selected a palette mode. // init_freetype_lv1( pls ); FT = (FT_Data *) pls->FT; FT->want_smooth_text = smooth_text > 0 ? 1 : 0; if ( ( dev->optimise == 0 ) && ( dev->palette == 0 ) && ( smooth_text != 0 ) ) { FT->BLENDED_ANTIALIASING = 1; dev->truecolour = 1; } } #endif } //-------------------------------------------------------------------------- // plD_init_png() // // Initialize device. //-------------------------------------------------------------------------- void plD_init_png( PLStream *pls ) { png_Dev *dev = NULL; pls->termin = 0; // Not an interactive device pls->icol0 = 1; pls->bytecnt = 0; pls->page = 0; pls->dev_fill0 = 1; // Can do solid fills if ( !pls->colorset ) pls->color = 1; // Is a color device // Initialize family file info plFamInit( pls ); // Prompt for a file name if not already set plOpenFile( pls ); // Allocate and initialize device-specific data plD_init_png_Dev( pls ); dev = (png_Dev *) pls->dev; // set dpi and page size defaults if the user has not already set // these with -dpi or -geometry command line options or with // plspage. if ( pls->xdpi <= 0. || pls->ydpi <= 0. ) { // Use recommended default pixels per inch. plspage( PLPLOT_DEFAULT_PIXELS_PER_INCH, PLPLOT_DEFAULT_PIXELS_PER_INCH, 0, 0, 0, 0 ); } if ( pls->xlength == 0 || pls->ylength == 0 ) { // Use recommended default pixel width and height. plspage( 0., 0., PLPLOT_DEFAULT_WIDTH_PIXELS, PLPLOT_DEFAULT_HEIGHT_PIXELS, 0, 0 ); } pls->graphx = GRAPHICS_MODE; dev->pngx = pls->xlength - 1; // should I use -1 or not??? dev->pngy = pls->ylength - 1; #ifdef use_experimental_hidden_line_hack if ( dev->pngx > dev->pngy ) // Work out the scaling factor for the { // "virtual" (oversized) page dev->scale = (PLFLT) ( PIXELS_X - 1 ) / (PLFLT) dev->pngx; } else { dev->scale = (PLFLT) PIXELS_Y / (PLFLT) dev->pngy; } #else dev->scale = 1.; #endif // Convert DPI to pixels/mm plP_setpxl( dev->scale * pls->xdpi / PLPLOT_MM_PER_INCH, dev->scale * pls->ydpi / PLPLOT_MM_PER_INCH ); plP_setphy( 0, dev->scale * dev->pngx, 0, dev->scale * dev->pngy ); #ifdef PL_HAVE_FREETYPE if ( pls->dev_text ) { init_freetype_lv2( pls ); } #endif } #ifdef PLD_gif //-------------------------------------------------------------------------- // plD_init_gif_Dev() // // We need a new initialiser for the GIF version of the GD driver because // the GIF one does not support TRUECOLOUR //-------------------------------------------------------------------------- static void plD_init_gif_Dev( PLStream *pls ) { png_Dev *dev; // Stuff for the driver options, these vars are copied into the driver // structure so that everything is thread safe and reenterant. // static int black15 = 0; static int red15 = 0; #ifdef PL_HAVE_FREETYPE static int freetype = 1; static int smooth_text = 0; FT_Data *FT; #endif 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\"" }, { "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" }, #ifdef PL_HAVE_FREETYPE { "text", DRV_INT, &freetype, "Use driver text (FreeType)" }, { "smooth", DRV_INT, &smooth_text, "Turn text smoothing on (1) or off (0)" }, #endif { NULL, DRV_INT, NULL, NULL } }; // Allocate and initialize device-specific data if ( pls->dev != NULL ) free( (void *) pls->dev ); pls->dev = calloc( 1, (size_t) sizeof ( png_Dev ) ); if ( pls->dev == NULL ) plexit( "plD_init_gif_Dev: Out of memory." ); dev = (png_Dev *) pls->dev; dev->colour = 1; // Set a fall back pen colour in case user doesn't // Check for and set up driver options plParseDrvOpts( gd_options ); dev->black15 = black15; dev->red15 = red15; dev->optimise = 0; // Optimise does not work for GIFs... should, but it doesn't dev->palette = 1; // Always use palette mode for GIF files dev->truecolour = 0; // Never have truecolour in GIFS #ifdef PL_HAVE_FREETYPE if ( freetype ) { pls->dev_text = 1; // want to draw text pls->dev_unicode = 1; // want unicode init_freetype_lv1( pls ); FT = (FT_Data *) pls->FT; FT->want_smooth_text = smooth_text > 0 ? 1 : 0; } #endif } //-------------------------------------------------------------------------- // plD_init_gif() // // Initialize device. //-------------------------------------------------------------------------- void plD_init_gif( PLStream *pls ) { png_Dev *dev = NULL; pls->termin = 0; // Not an interactive device pls->icol0 = 1; pls->bytecnt = 0; pls->page = 0; pls->dev_fill0 = 1; // Can do solid fills if ( !pls->colorset ) pls->color = 1; // Is a color device // Initialize family file info plFamInit( pls ); // Prompt for a file name if not already set plOpenFile( pls ); // Allocate and initialize device-specific data plD_init_gif_Dev( pls ); dev = (png_Dev *) pls->dev; // set dpi and page size defaults if the user has not already set // these with -dpi or -geometry command line options or with // plspage. if ( pls->xdpi <= 0. || pls->ydpi <= 0. ) { // Use recommended default pixels per inch. plspage( PLPLOT_DEFAULT_PIXELS_PER_INCH, PLPLOT_DEFAULT_PIXELS_PER_INCH, 0, 0, 0, 0 ); } if ( pls->xlength == 0 || pls->ylength == 0 ) { // Use recommended default pixel width and height. plspage( 0., 0., PLPLOT_DEFAULT_WIDTH_PIXELS, PLPLOT_DEFAULT_HEIGHT_PIXELS, 0, 0 ); } pls->graphx = GRAPHICS_MODE; dev->pngx = pls->xlength - 1; // should I use -1 or not??? dev->pngy = pls->ylength - 1; #ifdef use_experimental_hidden_line_hack if ( dev->pngx > dev->pngy ) // Work out the scaling factor for the { // "virtual" (oversized) page dev->scale = (PLFLT) ( PIXELS_X - 1 ) / (PLFLT) dev->pngx; } else { dev->scale = (PLFLT) PIXELS_Y / (PLFLT) dev->pngy; } #else dev->scale = 1.; #endif // Convert DPI to pixels/mm plP_setpxl( dev->scale * pls->xdpi / PLPLOT_MM_PER_INCH, dev->scale * pls->ydpi / PLPLOT_MM_PER_INCH ); plP_setphy( 0, dev->scale * dev->pngx, 0, dev->scale * dev->pngy ); #ifdef PL_HAVE_FREETYPE if ( pls->dev_text ) { init_freetype_lv2( pls ); } #endif } #endif //-------------------------------------------------------------------------- // plD_line_png() // // Draw a line in the current color from (x1,y1) to (x2,y2). //-------------------------------------------------------------------------- void plD_line_png( PLStream *pls, short x1a, short y1a, short x2a, short y2a ) { png_Dev *dev = (png_Dev *) pls->dev; int x1 = x1a / dev->scale, y1 = y1a / dev->scale, x2 = x2a / dev->scale, y2 = y2a / dev->scale; y1 = dev->pngy - y1; y2 = dev->pngy - y2; #ifdef SMOOTH_LINES_OK if ( dev->smooth == 1 ) { gdImageSetAntiAliased( dev->im_out, dev->colour ); gdImageLine( dev->im_out, x1, y1, x2, y2, gdAntiAliased ); } else { gdImageLine( dev->im_out, x1, y1, x2, y2, dev->colour ); } #else gdImageLine( dev->im_out, x1, y1, x2, y2, dev->colour ); #endif } //-------------------------------------------------------------------------- // plD_polyline_png() // // Draw a polyline in the current color. //-------------------------------------------------------------------------- void plD_polyline_png( PLStream *pls, short *xa, short *ya, PLINT npts ) { PLINT i; for ( i = 0; i < npts - 1; i++ ) plD_line_png( pls, xa[i], ya[i], xa[i + 1], ya[i + 1] ); } //-------------------------------------------------------------------------- // fill_polygon() // // Fill polygon described in points pls->dev_x[] and pls->dev_y[]. //-------------------------------------------------------------------------- static void fill_polygon( PLStream *pls ) { png_Dev *dev = (png_Dev *) pls->dev; int i; gdPoint *points = NULL; if ( pls->dev_npts < 1 ) return; points = malloc( (size_t) pls->dev_npts * sizeof ( gdPoint ) ); for ( i = 0; i < pls->dev_npts; i++ ) { points[i].x = pls->dev_x[i] / dev->scale; points[i].y = dev->pngy - ( pls->dev_y[i] / dev->scale ); } #ifdef SMOOTH_LINES_OK if ( dev->smooth == 1 ) { gdImageSetAntiAliased( dev->im_out, dev->colour ); gdImageFilledPolygon( dev->im_out, points, pls->dev_npts, gdAntiAliased ); } else { gdImageFilledPolygon( dev->im_out, points, pls->dev_npts, dev->colour ); } #else gdImageFilledPolygon( dev->im_out, points, pls->dev_npts, dev->colour ); #endif free( points ); } //-------------------------------------------------------------------------- // setcmap() // // Sets up color palette. //-------------------------------------------------------------------------- static void setcmap( PLStream *pls ) { int i, ncol1 = pls->ncol1; int ncol0 = pls->ncol0, total_colours; PLColor cmap1col; png_Dev *dev = (png_Dev *) pls->dev; PLFLT tmp_colour_pos; // // Yuckky fix to get rid of the previosuly allocated palette from the // GD image // if ( dev->im_out != NULL ) { for ( i = 0; i < 256; i++ ) { gdImageColorDeallocate( dev->im_out, i ); } } if ( ncol0 > NCOLOURS / 2 ) // Check for ridiculous number of colours { // in ncol0, and appropriately adjust the plwarn( "Too many colours in cmap0." ); // number, issuing a ncol0 = NCOLOURS / 2; // warning if it does pls->ncol0 = ncol0; } dev->totcol = 0; // Reset the number of colours counter to zero total_colours = ncol0 + ncol1; // Work out how many colours are wanted if ( total_colours > NCOLOURS ) // Do some rather modest error { // checking to make sure that total_colours = NCOLOURS; // we are not defining more colours ncol1 = total_colours - ncol0; // than we have room for. if ( ncol1 <= 0 ) { plexit( "Problem setting colourmap in PNG or JPEG driver." ); } } dev->ncol1 = ncol1; // The actual size of ncol1, regardless of what was asked. // This is dependent on colour slots available. // It might well be the same as ncol1. // // Initialize cmap 0 colors if ( ( ncol0 > 0 ) && ( dev->im_out != NULL ) ) // make sure the program actually asked for cmap0 first { for ( i = 0; i < ncol0; i++ ) { #if GD2_VERS >= 2 gdImageColorAllocateAlpha( dev->im_out, pls->cmap0[i].r, pls->cmap0[i].g, pls->cmap0[i].b, plToGdAlpha( pls->cmap0[i].a ) ); #else gdImageColorAllocate( dev->im_out, pls->cmap0[i].r, pls->cmap0[i].g, pls->cmap0[i].b ); #endif ++dev->totcol; // count the number of colours we use as we use them } } // Initialize any remaining slots for cmap1 if ( ( ncol1 > 0 ) && ( dev->im_out != NULL ) ) // make sure that we want to define cmap1 first { for ( i = 0; i < ncol1; i++ ) { if ( ncol1 < pls->ncol1 ) // Check the dynamic range of colours { // // Ok, now if we have less colour slots available than are being // defined by pls->ncol1, then we still want to use the full // dynamic range of cmap1 as best we can, so what we do is work // out an approximation to the index in the full dynamic range // in cases when pls->ncol1 exceeds the number of free colours. // tmp_colour_pos = i > 0 ? pls->ncol1 * ( (PLFLT) i / ncol1 ) : 0; plcol_interp( pls, &cmap1col, (int) tmp_colour_pos, pls->ncol1 ); } else { plcol_interp( pls, &cmap1col, i, ncol1 ); } #if GD2_VERS >= 2 gdImageColorAllocateAlpha( dev->im_out, cmap1col.r, cmap1col.g, cmap1col.b, plToGdAlpha( cmap1col.a ) ); #else gdImageColorAllocate( dev->im_out, cmap1col.r, cmap1col.g, cmap1col.b ); #endif ++dev->totcol; // count the number of colours we use as we go } } } //-------------------------------------------------------------------------- // plD_state_png() // // Handle change in PLStream state (color, pen width, fill attribute, etc). //-------------------------------------------------------------------------- void plD_state_png( PLStream *pls, PLINT op ) { png_Dev *dev = (png_Dev *) pls->dev; PLFLT tmp_colour_pos; #if GD2_VERS >= 2 long temp_col; #endif switch ( op ) { #if GD2_VERS >= 2 case PLSTATE_WIDTH: gdImageSetThickness( dev->im_out, pls->width ); break; #endif case PLSTATE_COLOR0: #if GD2_VERS >= 2 if ( ( pls->icol0 == PL_RGB_COLOR ) || // Should never happen since PL_RGB_COLOR is deprecated, but here for backwards compatibility ( gdImageTrueColor( dev->im_out ) ) ) // We will do this if we are in "TrueColour" mode { if ( ( dev->totcol < NCOLOURS ) || // See if there are slots left, if so we will allocate a new colour ( gdImageTrueColor( dev->im_out ) ) ) // In TrueColour mode we allocate each colour as we come to it { // 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 #if GD2_VERS >= 2 temp_col = gdImageColorAllocateAlpha( dev->im_out, pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, plToGdAlpha( pls->curcolor.a ) ); #else temp_col = gdImageColorAllocate( dev->im_out, pls->curcolor.r, pls->curcolor.g, pls->curcolor.b ); #endif if ( gdImageTrueColor( dev->im_out ) ) dev->colour = temp_col; // If it's truecolour, then we will directly set dev->colour to our "new" colour else { dev->colour = dev->totcol; // or else, we will just set it to the last colour dev->totcol++; // Bump the total colours for next time round } } } else // just a normal colour allocate, so don't worry about the above stuff, just grab the index { dev->colour = pls->icol0; } #else dev->colour = pls->icol0; if ( dev->colour == PL_RGB_COLOR ) { if ( dev->totcol < NCOLOURS ) { #if GD2_VERS >= 2 gdImageColorAllocateAlpha( dev->im_out, pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, plToGdAlpha( pls->curcolor.a ) ); #else gdImageColorAllocate( dev->im_out, pls->curcolor.r, pls->curcolor.g, pls->curcolor.b ); #endif dev->colour = dev->totcol; } } #endif break; case PLSTATE_COLOR1: #if GD2_VERS >= 2 if ( !gdImageTrueColor( dev->im_out ) ) { #endif // // Start by checking to see if we have to compensate for cases where // we don't have the full dynamic range of cmap1 at our disposal // if ( dev->ncol1 < pls->ncol1 ) { tmp_colour_pos = dev->ncol1 * ( (PLFLT) pls->icol1 / ( pls->ncol1 > 0 ? pls->ncol1 : 1 ) ); dev->colour = pls->ncol0 + (int) tmp_colour_pos; } else dev->colour = pls->ncol0 + pls->icol1; #if GD2_VERS >= 2 } else // it is a truecolour image { #if GD2_VERS >= 2 dev->colour = gdTrueColorAlpha( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b, plToGdAlpha( pls->curcolor.a ) ); #else dev->colour = gdTrueColor( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b ); #endif } #endif break; case PLSTATE_CMAP0: case PLSTATE_CMAP1: #if GD2_VERS >= 2 if ( ( dev->im_out != NULL ) && !gdImageTrueColor( dev->im_out ) ) { #endif // // Code to redefine the entire palette // if ( pls->color ) setcmap( pls ); #if GD2_VERS >= 2 } #endif break; } } //-------------------------------------------------------------------------- // plD_esc_png() // // Escape function. //-------------------------------------------------------------------------- void plD_esc_png( PLStream *pls, PLINT op, void *ptr ) { switch ( op ) { case PLESC_FILL: // fill fill_polygon( pls ); break; #ifdef PL_HAVE_FREETYPE case PLESC_HAS_TEXT: plD_render_freetype_text( pls, (EscText *) ptr ); break; #endif } } //-------------------------------------------------------------------------- // plD_bop_png() // // Set up for the next page. // Advance to next family file if necessary (file output). //-------------------------------------------------------------------------- void plD_bop_png( PLStream *pls ) { png_Dev *dev; plGetFam( pls ); // force new file if pls->family set for all subsequent calls to plGetFam // n.b. putting this after plGetFam call is important since plinit calls // bop, and you don't want the familying sequence started until after // that first call to bop. // n.b. pls->dev can change because of an indirect call to plD_init_png // from plGetFam if familying is enabled. Thus, wait to define dev until // now. dev = (png_Dev *) pls->dev; pls->famadv = 1; pls->page++; if ( dev->black15 ) plD_black15_gd( pls ); if ( dev->red15 ) plD_red15_gd( pls ); #if GD2_VERS >= 2 if ( ( ( ( ( dev->truecolour > 0 ) && ( dev->palette > 0 ) ) || // In an EXTREMELY convaluted ( ( dev->truecolour == 0 ) && ( dev->palette == 0 ) ) ) && // manner, all this is just ( ( pls->ncol1 + pls->ncol0 ) <= 256 ) ) || // asking the question, do we ( ( ( dev->palette > 0 ) && ( dev->truecolour == 0 ) ) ) ) // want truecolour or not ? { #endif dev->im_out = gdImageCreate( pls->xlength, pls->ylength ); setcmap( pls ); #if GD2_VERS >= 2 } else { dev->im_out = gdImageCreateTrueColor( pls->xlength, pls->ylength ); plP_state( PLSTATE_COLOR0 ); // // In truecolour mode, the background colour GD makes is ALWAYS black, so to // "simulate" (stimulate?) a background colour other than black, we will just // draw a dirty big rectange covering the whole image and colour it in // whatever colour cmap0[0] happens to be. // // Question to C gurus: while it is slightly illogical and ugly, would: // if ((pls->cmap0[0].r+pls->cmap0[0].g+pls->cmap0[0].b)!=0) // be more computationally efficient than: // if ((pls->cmap0[0].r!=0)||(pls->cmap0[0].g!=0)||(pls->cmap0[0].b!=0)) // ??? // if ( ( pls->cmap0[0].r != 0 ) || ( pls->cmap0[0].g != 0 ) || ( pls->cmap0[0].b != 0 ) || ( pls->cmap0[0].a != 0.0 ) ) { gdImageFilledRectangle( dev->im_out, 0, 0, pls->xlength - 1, pls->ylength - 1, gdTrueColorAlpha( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b, plToGdAlpha( pls->cmap0[0].a ) ) ); } } // This ensures the line width is set correctly at the beginning of // each page plD_state_png( pls, PLSTATE_WIDTH ); #endif } //-------------------------------------------------------------------------- // plD_tidy_png() // // Close graphics file or otherwise clean up. //-------------------------------------------------------------------------- void plD_tidy_png( PLStream *pls ) { #ifdef PL_HAVE_FREETYPE if ( pls->dev_text ) { plD_FreeType_Destroy( pls ); } #endif plCloseFile( pls ); free_mem( pls->dev ); } //-------------------------------------------------------------------------- // plD_black15_gd() // // This small function simply redefines index 15 of cmap0, which is // usually set to white, to black, but only if index 0, which is usually // black, has been redefined to white (for example, through -bg). // //-------------------------------------------------------------------------- void plD_black15_gd( PLStream *pls ) { if ( pls->ncol0 > 15 ) { if ( ( pls->cmap0[0].r > 227 ) && ( pls->cmap0[0].g > 227 ) && ( pls->cmap0[0].b > 227 ) ) { pls->cmap0[15].r = 0; pls->cmap0[15].g = 0; pls->cmap0[15].b = 0; } } } //-------------------------------------------------------------------------- // plD_red15_gd() // // // This function swaps index 1, often the default plotting colour, with // index 15, the last defined colour. // // Colour 15 is usually white, and 1 is usually red, so swapping the two // might be desirable occasionally, but it is principally here for cases // when the background has been set on the command line to white, and the // "def_black15" option has been issued to redefine index 15 as black. By // issuing a command like // // ... -bg ffffff -drvopt def_black15,swp_red15 // // the driver will set the background to white, then redefine index 15 of // cmap0, which is usually white to black, then swap index 2 (red) to 15 // (white originally, now black), so at the end of the day, the "default" // plotting colour is now black. Why do all of this ? It is a very quick // way of making a nice web-friendly png without having to redefine the // cmaps within your program. // // If you don't like it, don't use it ! // //-------------------------------------------------------------------------- void plD_red15_gd( PLStream *pls ) { char r = pls->cmap0[1].r; char g = pls->cmap0[1].g; char b = pls->cmap0[1].b; if ( pls->ncol0 > 15 ) { pls->cmap0[1].r = pls->cmap0[15].r; pls->cmap0[1].g = pls->cmap0[15].r; pls->cmap0[1].b = pls->cmap0[15].r; pls->cmap0[15].r = r; pls->cmap0[15].g = g; pls->cmap0[15].b = b; } } //-------------------------------------------------------------------------- // plD_gd_optimise() // // // This function pretty much does exactly what it says - it optimises the // PNG file. It does this by checking to see if all the allocated colours // were actually used. If they were not, then it deallocates them. This // function often results in the PNG file being saved as a 4 bit (16 // colour) PNG rather than an 8 bit (256 colour) PNG. The file size // difference is not huge, not as great as for GIFs for example (I think // most of the saving comes from removing redundant entries from the // palette entry in the header); however some modest size savings occur. // // The function isn't always successful - the optimiser will always // deallocate unused colours as it finds them, but GD will only deallocate // them "for real" until 16 colours are used up, and then stop since it // doesn't make a difference if you have 17 colours or 255 colours. The // result of this is you may end up with an image using say, 130 colours, // but you will have 240 colour entries, some of which aren't used, and // aren't blanked out. // // Another side-effect of this function is the relative position of the // colour indices MAY shift as colours are deallocated. I really don't // think this should worry anyone, but if it does, don't optimise the // image ! // //-------------------------------------------------------------------------- void plD_gd_optimise( PLStream *pls ) { png_Dev *dev = (png_Dev *) pls->dev; int i, j; char *bbuf; bbuf = calloc( 256, (size_t) 1 ); // Allocate a buffer to "check off" colours as they are used if ( bbuf == NULL ) plexit( "plD_gd_optimise: Out of memory." ); for ( i = 0; i < ( pls->xlength - 1 ); i++ ) // Walk through the image pixel by pixel { // checking to see what colour it is for ( j = 0; j < ( pls->ylength - 1 ); j++ ) // and adding it to the list of used colours { bbuf[gdImagePalettePixel( dev->im_out, i, j )] = 1; } } for ( i = 0; i < 256; i++ ) // next walk over the colours and deallocate { // unused ones if ( bbuf[i] == 0 ) gdImageColorDeallocate( dev->im_out, i ); } free( bbuf ); } #ifdef PLD_png //-------------------------------------------------------------------------- // plD_eop_png() // // End of page. //-------------------------------------------------------------------------- void plD_eop_png( PLStream *pls ) { png_Dev *dev = (png_Dev *) pls->dev; int im_size = 0; int png_compression; void *im_ptr = NULL; size_t nwrite; if ( pls->family || pls->page == 1 ) { if ( dev->optimise ) { #if GD2_VERS >= 2 if ( ( ( ( ( dev->truecolour > 0 ) && ( dev->palette > 0 ) ) || // In an EXTREMELY convaluted ( ( dev->truecolour == 0 ) && ( dev->palette == 0 ) ) ) && // manner, all this is just ( ( pls->ncol1 + pls->ncol0 ) <= 256 ) ) || // asking the question, do we ( ( ( dev->palette > 0 ) && ( dev->truecolour == 0 ) ) ) ) // want truecolour or not ? { #endif plD_gd_optimise( pls ); #if GD2_VERS >= 2 } #endif } // image is written to output file by the driver // since if the gd.dll is linked to a different c // lib a crash occurs - this fix works also in Linux // gdImagePng(dev->im_out, pls->OutFile); #if GD2_VERS >= 2 //Set the compression/quality level for PNG files. // pls->dev_compression values of 1-9 translate to the zlib compression values 1-9 // pls->dev_compression values 10 <= compression <= 99 are divided by 10 to get the zlib // compression value. Values <=0 or greater than 99 are set to 90 which // translates to a zlib compression value of 9, the highest quality // of compression or smallest file size or largest computer time required // to achieve the compression. Smaller zlib compression values correspond // to lower qualities of compression (larger file size), but lower // computer times as well. png_compression = ( ( pls->dev_compression <= 0 ) || ( pls->dev_compression > 99 ) ) ? 90 : pls->dev_compression; png_compression = ( png_compression > 9 ) ? ( png_compression / 10 ) : png_compression; im_ptr = gdImagePngPtrEx( dev->im_out, &im_size, png_compression ); #else im_ptr = gdImagePngPtr( dev->im_out, &im_size ); #endif if ( im_ptr ) { nwrite = fwrite( im_ptr, sizeof ( char ), im_size, pls->OutFile ); if ( nwrite != im_size ) plabort( "gd driver: Error writing png file" ); gdFree( im_ptr ); } gdImageDestroy( dev->im_out ); dev->im_out = NULL; } } #endif #ifdef PL_HAVE_FREETYPE //-------------------------------------------------------------------------- // void plD_pixel_gd (PLStream *pls, short x, short y) // // callback function, of type "plD_pixel_fp", which specifies how a single // pixel is set in the current colour. //-------------------------------------------------------------------------- void plD_pixel_gd( PLStream *pls, short x, short y ) { png_Dev *dev = (png_Dev *) pls->dev; gdImageSetPixel( dev->im_out, x, y, dev->colour ); } //-------------------------------------------------------------------------- // void plD_set_pixel_gd (PLStream *pls, short x, short y) // // callback function, of type "plD_pixel_fp", which specifies how a single // pixel is set directly to hardware, using the colour provided //-------------------------------------------------------------------------- void plD_set_pixel_gd( PLStream *pls, short x, short y, PLINT colour ) { png_Dev *dev = (png_Dev *) pls->dev; int R, G, B; int Colour; G = GetGValue( colour ); R = GetRValue( colour ); B = GetBValue( colour ); Colour = gdImageColorResolve( dev->im_out, R, G, B ); gdImageSetPixel( dev->im_out, x, y, Colour ); } //-------------------------------------------------------------------------- // PLINT plD_read_pixel_gd (PLStream *pls, short x, short y) // // callback function, of type "plD_read_pixel_gd", which specifies how a // single pixel's RGB is read (in the destination context), then // returns an RGB encoded int with the info for blending. //-------------------------------------------------------------------------- PLINT plD_read_pixel_gd( PLStream *pls, short x, short y ) { png_Dev *dev = (png_Dev *) pls->dev; PLINT colour; unsigned char R, G, B; colour = gdImageGetTrueColorPixel( dev->im_out, x, y ); R = gdTrueColorGetRed( colour ); G = gdTrueColorGetGreen( colour ); B = gdTrueColorGetBlue( colour ); colour = RGB( R, G, B ); return ( colour ); } //-------------------------------------------------------------------------- // void init_freetype_lv1 (PLStream *pls) // // "level 1" initialisation of the freetype library. // "Level 1" initialisation calls plD_FreeType_init(pls) which allocates // memory to the pls->FT structure, then sets up the pixel callback // function. //-------------------------------------------------------------------------- static void init_freetype_lv1( PLStream *pls ) { FT_Data *FT; plD_FreeType_init( pls ); FT = (FT_Data *) pls->FT; FT->pixel = (plD_pixel_fp) plD_pixel_gd; FT->read_pixel = (plD_read_pixel_fp) plD_read_pixel_gd; FT->set_pixel = (plD_set_pixel_fp) plD_set_pixel_gd; } //-------------------------------------------------------------------------- // void init_freetype_lv2 (PLStream *pls) // // "Level 2" initialisation of the freetype library. // "Level 2" fills in a few setting that aren't public until after the // graphics sub-syetm has been initialised. // The "level 2" initialisation fills in a few things that are defined // later in the initialisation process for the GD driver. // // FT->scale is a scaling factor to convert co-ordinates. This is used by // the GD and other drivers to scale back a larger virtual page and this // eliminate the "hidden line removal bug". Set it to 1 if your device // doesn't have scaling. // // Some coordinate systems have zero on the bottom, others have zero on // the top. Freetype does it one way, and most everything else does it the // other. To make sure everything is working ok, we have to "flip" the // coordinates, and to do this we need to know how big in the Y dimension // the page is, and whether we have to invert the page or leave it alone. // // FT->ymax specifies the size of the page FT->invert_y=1 tells us to // invert the y-coordinates, FT->invert_y=0 will not invert the // coordinates. //-------------------------------------------------------------------------- static void init_freetype_lv2( PLStream *pls ) { png_Dev *dev = (png_Dev *) pls->dev; FT_Data *FT = (FT_Data *) pls->FT; FT->scale = dev->scale; FT->ymax = dev->pngy; FT->invert_y = 1; FT->smooth_text = 0; if ( ( FT->want_smooth_text == 1 ) && ( FT->BLENDED_ANTIALIASING == 0 ) ) // do we want to at least *try* for smoothing ? { FT->ncol0_org = pls->ncol0; // save a copy of the original size of ncol0 FT->ncol0_xtra = NCOLOURS - ( pls->ncol1 + pls->ncol0 ); // work out how many free slots we have FT->ncol0_width = FT->ncol0_xtra / ( pls->ncol0 - 1 ); // find out how many different shades of anti-aliasing we can do if ( FT->ncol0_width > 4 ) // are there enough colour slots free for text smoothing ? { if ( FT->ncol0_width > max_number_of_grey_levels_used_in_text_smoothing ) FT->ncol0_width = max_number_of_grey_levels_used_in_text_smoothing; // set a maximum number of shades plscmap0n( FT->ncol0_org + ( FT->ncol0_width * pls->ncol0 ) ); // redefine the size of cmap0 // the level manipulations are to turn off the plP_state(PLSTATE_CMAP0) // call in plscmap0 which (a) leads to segfaults since the GD image is // not defined at this point and (b) would be inefficient in any case since // setcmap is always called later (see plD_bop_png) to update the driver // color palette to be consistent with cmap0. { PLINT level_save; level_save = pls->level; pls->level = 0; pl_set_extended_cmap0( pls, FT->ncol0_width, FT->ncol0_org ); // call the function to add the extra cmap0 entries and calculate stuff pls->level = level_save; } FT->smooth_text = 1; // Yippee ! We had success setting up the extended cmap0 } else plwarn( "Insufficient colour slots available in CMAP0 to do text smoothing." ); } 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 { FT->smooth_text = 1; } } #endif #ifdef PLD_jpeg //-------------------------------------------------------------------------- // plD_eop_jpeg() // // End of page. //-------------------------------------------------------------------------- void plD_eop_jpeg( PLStream *pls ) { png_Dev *dev = (png_Dev *) pls->dev; int im_size = 0; void *im_ptr = NULL; size_t nwrite; int jpeg_compression; if ( pls->family || pls->page == 1 ) { // Set the compression/quality level for JPEG files // The higher the value, the bigger/better the image is // if ( ( pls->dev_compression <= 0 ) || ( pls->dev_compression > 99 ) ) jpeg_compression = 90; else jpeg_compression = pls->dev_compression; // image is written to output file by the driver // since if the gd.dll is linked to a different c // lib a crash occurs - this fix works also in Linux // gdImageJpeg(dev->im_out, pls->OutFile, jpeg_compression); im_ptr = gdImageJpegPtr( dev->im_out, &im_size, jpeg_compression ); if ( im_ptr ) { nwrite = fwrite( im_ptr, sizeof ( char ), im_size, pls->OutFile ); if ( nwrite != im_size ) plabort( "gd driver: Error writing png file" ); gdFree( im_ptr ); } gdImageDestroy( dev->im_out ); dev->im_out = NULL; } } #endif #ifdef PLD_gif //-------------------------------------------------------------------------- // plD_eop_gif() // // End of page. //-------------------------------------------------------------------------- void plD_eop_gif( PLStream *pls ) { png_Dev *dev = (png_Dev *) pls->dev; int im_size = 0; void *im_ptr = NULL; size_t nwrite; if ( pls->family || pls->page == 1 ) { // image is written to output file by the driver // since if the gd.dll is linked to a different c // lib a crash occurs - this fix works also in Linux // gdImageGif(dev->im_out, pls->OutFile); im_ptr = gdImageGifPtr( dev->im_out, &im_size ); if ( im_ptr ) { nwrite = fwrite( im_ptr, sizeof ( char ), im_size, pls->OutFile ); if ( nwrite != im_size ) plabort( "gd driver: Error writing png file" ); gdFree( im_ptr ); } gdImageDestroy( dev->im_out ); dev->im_out = NULL; } } #endif //#endif #else int pldummy_png() { return 0; } #endif // PNG plplot-5.13.0/drivers/xwin.c000644 001752 001752 00000322243 13150160115 017452 0ustar00softwaresoftware000000 000000 // PLplot X-windows device driver. // // Copyright (C) 2004 Maurice LeBrun // Copyright (C) 2004 Joao Cardoso // Copyright (C) 2004 Rafael Laboissiere // Copyright (C) 2004 Andrew Ross // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // #include "plDevs.h" #define DEBUG #ifdef PLD_xwin #define NEED_PLDEBUG #include "plplotP.h" #include "plxwd.h" #include "drivers.h" #include "plevent.h" #ifdef PL_HAVE_PTHREAD #include #include int pthread_mutexattr_settype( pthread_mutexattr_t *attr, int kind ); static void events_thread( void *pls ); static pthread_mutex_t events_mutex; static int already = 0; #endif // Device info PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_xwin = "xwin:X-Window (Xlib):1:xwin:5:xw\n"; static int synchronize = 0; // change to 1 for X synchronized operation // Use "-drvopt sync" cmd line option to set. static int nobuffered = 0; // make it a buffered device by default // use "-drvopt unbuffered" to make it unbuffered static int noinitcolors = 0; // Call InitColors by default // use "-drvopt noinitcolors" to leave colors uninitialized static int defaultvisual = 1; // use "-drvopt defvis=0" to not use the default visual static int usepthreads = 1; // use "-drvopt usepth=0" to not use pthreads to redisplay // // When -drvopt defvis is defined, DefaultVisual() is used to get the visual. // Otherwise, the visual is obtained using XGetVisualInfo() to make a match. // //#define HACK_STATICCOLOR // Number of instructions to skip between querying the X server for events #define MAX_INSTR 20 // The xwin driver uses the xscale and yscale values to convert from virtual // to real pixels using the current size in pixels of the display window. // We define PHYSICAL here so that PLplot core knows about this rescaling // and mm values are converted to virtual pixels at a ratio consistent with // a constant ratio of DPMM real pixels per mm. #define PHYSICAL 1 // These need to be distinguished since the handling is slightly different. #define LOCATE_INVOKED_VIA_API 1 #define LOCATE_INVOKED_VIA_DRIVER 2 // Set constants for dealing with colormap. In brief: // // --- custom colormaps --- // ccmap When set, turns on custom color map // CCMAP_XWM_COLORS Number of low "pixel" values to copy. // // --- r/w colormaps -- // RWMAP_CMAP1_COLORS Color map 1 entries. // RWMAP_MAX_COLORS Maximum colors in RW colormaps // // --- r/o colormaps -- // ROMAP_CMAP1_COLORS Color map 1 entries. // TC_CMAP1_COLORS TrueColor visual Color map 1 entries. // // See Init_CustomCmap() and Init_DefaultCmap() for more info. // Set ccmap at your own risk -- still under development. // // plplot_ccmap is statically defined in plxwd.h. Note that // plframe.c also includes that header and uses the variable. #define CCMAP_XWM_COLORS 70 #define RWMAP_CMAP1_COLORS 50 #define RWMAP_MAX_COLORS 256 #define ROMAP_CMAP1_COLORS 50 #define TC_CMAP1_COLORS 200 // Variables to hold RGB components of given colormap. // Used in an ugly hack to get past some X11R5 and TK limitations. static int sxwm_colors_set; static XColor sxwm_colors[RWMAP_MAX_COLORS]; // Keep pointers to all the displays in use static XwDisplay *xwDisplay[PLXDISPLAYS]; // Function prototypes // Driver entry and dispatch setup void plD_dispatch_init_xw( PLDispatchTable *pdt ); void plD_init_xw( PLStream * ); void plD_line_xw( PLStream *, short, short, short, short ); void plD_polyline_xw( PLStream *, short *, short *, PLINT ); void plD_eop_xw( PLStream * ); void plD_bop_xw( PLStream * ); void plD_tidy_xw( PLStream * ); void plD_wait_xw( PLStream * ); void plD_state_xw( PLStream *, PLINT ); void plD_esc_xw( PLStream *, PLINT, void * ); // Initialization static void OpenXwin( PLStream *pls ); static void Init( PLStream *pls ); static void InitMain( PLStream *pls ); static void InitColors( PLStream *pls ); static void AllocCustomMap( PLStream *pls ); static void AllocCmap0( PLStream *pls ); static void AllocCmap1( PLStream *pls ); static void MapMain( PLStream *pls ); static void CreatePixmap( PLStream *pls ); static void GetVisual( PLStream *pls ); static void AllocBGFG( PLStream *pls ); static int AreWeGrayscale( Display *display ); // Routines to poll the X-server static void WaitForPage( PLStream *pls ); static void CheckForEvents( PLStream *pls ); static void HandleEvents( PLStream *pls ); // Event handlers static void MasterEH( PLStream *pls, XEvent *event ); static void ClientEH( PLStream *pls, XEvent *event ); static void ExposeEH( PLStream *pls, XEvent *event ); static void ResizeEH( PLStream *pls, XEvent *event ); static void MotionEH( PLStream *pls, XEvent *event ); static void EnterEH( PLStream *pls, XEvent *event ); static void LeaveEH( PLStream *pls, XEvent *event ); static void KeyEH( PLStream *pls, XEvent *event ); static void ButtonEH( PLStream *pls, XEvent *event ); static void LookupXKeyEvent( PLStream *pls, XEvent *event ); static void LookupXButtonEvent( PLStream *pls, XEvent *event ); static void ProcessKey( PLStream *pls ); static void LocateKey( PLStream *pls ); static void ProcessButton( PLStream *pls ); static void LocateButton( PLStream *pls ); static void Locate( PLStream *pls ); // Routines for manipulating graphic crosshairs static void CreateXhairs( PLStream *pls ); static void DestroyXhairs( PLStream *pls ); static void DrawXhairs( PLStream *pls, int x0, int y0 ); static void UpdateXhairs( PLStream *pls ); // Escape function commands static void ExposeCmd( PLStream *pls, PLDisplay *ptr ); static void RedrawCmd( PLStream *pls ); static void ResizeCmd( PLStream *pls, PLDisplay *ptr ); static void ConfigBufferingCmd( PLStream *pls, PLBufferingCB *ptr ); static void GetCursorCmd( PLStream *pls, PLGraphicsIn *ptr ); static void FillPolygonCmd( PLStream *pls ); static void XorMod( PLStream *pls, PLINT *mod ); static void DrawImage( PLStream *pls ); // Miscellaneous static void StoreCmap0( PLStream *pls ); static void StoreCmap1( PLStream *pls ); static void imageops( PLStream *pls, PLINT *ptr ); static void SetBGFG( PLStream *pls ); #ifdef DUMMY static void SaveColormap( Display *display, Colormap colormap ); #endif static void PLColor_to_XColor( PLColor *plcolor, XColor *xcolor ); static void PLColor_from_XColor( PLColor *plcolor, XColor *xcolor ); static DrvOpt xwin_options[] = { { "sync", DRV_INT, &synchronize, "Synchronized X server operation (0|1)" }, { "nobuffered", DRV_INT, &nobuffered, "Sets unbuffered operation (0|1)" }, { "noinitcolors", DRV_INT, &noinitcolors, "Sets cmap0 allocation (0|1)" }, { "defvis", DRV_INT, &defaultvisual, "Use the Default Visual (0|1)" }, { "usepth", DRV_INT, &usepthreads, "Use pthreads (0|1)" }, { NULL, DRV_INT, NULL, NULL } }; void plD_dispatch_init_xw( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "X-Window (Xlib)"; pdt->pl_DevName = "xwin"; #endif pdt->pl_type = plDevType_Interactive; pdt->pl_seq = 5; pdt->pl_init = (plD_init_fp) plD_init_xw; pdt->pl_line = (plD_line_fp) plD_line_xw; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_xw; pdt->pl_eop = (plD_eop_fp) plD_eop_xw; pdt->pl_bop = (plD_bop_fp) plD_bop_xw; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_xw; pdt->pl_state = (plD_state_fp) plD_state_xw; pdt->pl_esc = (plD_esc_fp) plD_esc_xw; pdt->pl_wait = (plD_wait_fp) plD_wait_xw; } //-------------------------------------------------------------------------- // plD_init_xw() // // Initialize device. // X-dependent stuff done in OpenXwin() and Init(). //-------------------------------------------------------------------------- void plD_init_xw( PLStream *pls ) { XwDev *dev; PLFLT pxlx, pxly; int xmin = 0; int xmax = PIXELS_X - 1; int ymin = 0; int ymax = PIXELS_Y - 1; dbug_enter( "plD_init_xw" ); pls->termin = 1; // Is an interactive terminal pls->dev_flush = 1; // Handle our own flushes pls->dev_fill0 = 1; // Handle solid fills pls->plbuf_write = 1; // Activate plot buffer pls->dev_fastimg = 1; // is a fast image device pls->dev_xor = 1; // device support xor mode #ifndef PL_HAVE_PTHREAD usepthreads = 0; #endif plParseDrvOpts( xwin_options ); #ifndef PL_HAVE_PTHREAD if ( usepthreads ) plwarn( "You said you want pthreads, but they are not available." ); #endif if ( nobuffered ) pls->plbuf_write = 0; // deactivate plot buffer // The real meat of the initialization done here if ( pls->dev == NULL ) OpenXwin( pls ); dev = (XwDev *) pls->dev; Init( pls ); // Get ready for plotting dev->xlen = (short) ( xmax - xmin ); dev->ylen = (short) ( ymax - ymin ); dev->xscale_init = (double) dev->init_width / (double) dev->xlen; dev->yscale_init = (double) dev->init_height / (double) dev->ylen; dev->xscale = dev->xscale_init; dev->yscale = dev->yscale_init; #if PHYSICAL pxlx = DPMM / dev->xscale; pxly = DPMM / dev->yscale; #else pxlx = (double) PIXELS_X / LPAGE_X; pxly = (double) PIXELS_Y / LPAGE_Y; #endif plP_setpxl( pxlx, pxly ); plP_setphy( xmin, xmax, ymin, ymax ); #ifdef PL_HAVE_PTHREAD if ( usepthreads ) { pthread_mutexattr_t mutexatt; pthread_attr_t pthattr; if ( !already ) { pthread_mutexattr_init( &mutexatt ); if ( pthread_mutexattr_settype( &mutexatt, PLPLOT_MUTEX_RECURSIVE ) ) plexit( "xwin: pthread_mutexattr_settype() failed!\n" ); pthread_mutex_init( &events_mutex, &mutexatt ); already = 1; } else { pthread_mutex_lock( &events_mutex ); already++; pthread_mutex_unlock( &events_mutex ); } pthread_attr_init( &pthattr ); pthread_attr_setdetachstate( &pthattr, PTHREAD_CREATE_JOINABLE ); if ( pthread_create( &( dev->updater ), &pthattr, ( void *( * )( void * ) ) & events_thread, (void *) pls ) ) { pthread_mutex_lock( &events_mutex ); already--; pthread_mutex_unlock( &events_mutex ); if ( already == 0 ) { pthread_mutex_destroy( &events_mutex ); plexit( "xwin: pthread_create() failed!\n" ); } else plwarn( "xwin: couldn't create thread for this plot window!\n" ); } } #endif } //-------------------------------------------------------------------------- // plD_line_xw() // // Draw a line in the current color from (x1,y1) to (x2,y2). //-------------------------------------------------------------------------- void plD_line_xw( PLStream *pls, short x1a, short y1a, short x2a, short y2a ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; int x1 = x1a, y1 = y1a, x2 = x2a, y2 = y2a; dbug_enter( "plD_line_xw" ); #ifdef PL_HAVE_PTHREAD if ( usepthreads ) pthread_mutex_lock( &events_mutex ); #endif CheckForEvents( pls ); y1 = dev->ylen - y1; y2 = dev->ylen - y2; x1 = (int) ( x1 * dev->xscale ); x2 = (int) ( x2 * dev->xscale ); y1 = (int) ( y1 * dev->yscale ); y2 = (int) ( y2 * dev->yscale ); if ( dev->write_to_window ) XDrawLine( xwd->display, dev->window, dev->gc, x1, y1, x2, y2 ); if ( dev->write_to_pixmap ) XDrawLine( xwd->display, dev->pixmap, dev->gc, x1, y1, x2, y2 ); #ifdef PL_HAVE_PTHREAD if ( usepthreads ) pthread_mutex_unlock( &events_mutex ); #endif } //-------------------------------------------------------------------------- // XorMod() // // Enter xor mode ( mod != 0) or leave it ( mode = 0) //-------------------------------------------------------------------------- static void XorMod( PLStream *pls, PLINT *mod ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; if ( *mod == 0 ) XSetFunction( xwd->display, dev->gc, GXcopy ); else XSetFunction( xwd->display, dev->gc, GXxor ); } //-------------------------------------------------------------------------- // plD_polyline_xw() // // Draw a polyline in the current color from (x1,y1) to (x2,y2). //-------------------------------------------------------------------------- void plD_polyline_xw( PLStream *pls, short *xa, short *ya, PLINT npts ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; PLINT i; XPoint _pts[PL_MAXPOLY]; XPoint *pts; if ( npts > PL_MAXPOLY ) { pts = (XPoint *) malloc( sizeof ( XPoint ) * (size_t) npts ); } else { pts = _pts; } dbug_enter( "plD_polyline_xw" ); #ifdef PL_HAVE_PTHREAD if ( usepthreads ) pthread_mutex_lock( &events_mutex ); #endif CheckForEvents( pls ); for ( i = 0; i < npts; i++ ) { pts[i].x = (short) ( dev->xscale * xa[i] ); pts[i].y = (short) ( dev->yscale * ( dev->ylen - ya[i] ) ); } if ( dev->write_to_window ) XDrawLines( xwd->display, dev->window, dev->gc, pts, npts, CoordModeOrigin ); if ( dev->write_to_pixmap ) XDrawLines( xwd->display, dev->pixmap, dev->gc, pts, npts, CoordModeOrigin ); #ifdef PL_HAVE_PTHREAD if ( usepthreads ) pthread_mutex_unlock( &events_mutex ); #endif if ( npts > PL_MAXPOLY ) { free( pts ); } } //-------------------------------------------------------------------------- // plD_eop_xw() // // End of page. User must hit return (or third mouse button) to continue. //-------------------------------------------------------------------------- void plD_eop_xw( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; dbug_enter( "plD_eop_xw" ); #ifdef PL_HAVE_PTHREAD if ( usepthreads ) pthread_mutex_lock( &events_mutex ); #endif XFlush( xwd->display ); if ( pls->db ) ExposeCmd( pls, NULL ); #ifdef PL_HAVE_PTHREAD if ( usepthreads ) pthread_mutex_unlock( &events_mutex ); #endif } //-------------------------------------------------------------------------- // plD_bop_xw() // // Set up for the next page. //-------------------------------------------------------------------------- void plD_bop_xw( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; dbug_enter( "plD_bop_xw" ); #ifdef PL_HAVE_PTHREAD if ( usepthreads ) pthread_mutex_lock( &events_mutex ); #endif dev->bgcolor = xwd->cmap0[0]; if ( dev->write_to_window ) { XSetWindowBackground( xwd->display, dev->window, dev->bgcolor.pixel ); XSetBackground( xwd->display, dev->gc, dev->bgcolor.pixel ); XClearWindow( xwd->display, dev->window ); } if ( dev->write_to_pixmap ) { XSetForeground( xwd->display, dev->gc, dev->bgcolor.pixel ); XFillRectangle( xwd->display, dev->pixmap, dev->gc, 0, 0, dev->width, dev->height ); XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel ); } XSync( xwd->display, 0 ); pls->page++; #ifdef PL_HAVE_PTHREAD if ( usepthreads ) pthread_mutex_unlock( &events_mutex ); #endif } //-------------------------------------------------------------------------- // plD_tidy_xw() // // Close graphics file //-------------------------------------------------------------------------- void plD_tidy_xw( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; dbug_enter( "plD_tidy_xw" ); #ifdef PL_HAVE_PTHREAD if ( usepthreads ) { pthread_mutex_lock( &events_mutex ); if ( pthread_cancel( dev->updater ) == 0 ) pthread_join( dev->updater, NULL ); pthread_mutex_unlock( &events_mutex ); if ( --already == 0 ) pthread_mutex_destroy( &events_mutex ); } #endif if ( dev->is_main ) { XDestroyWindow( xwd->display, dev->window ); if ( dev->write_to_pixmap ) XFreePixmap( xwd->display, dev->pixmap ); XFlush( xwd->display ); } xwd->nstreams--; if ( xwd->nstreams == 0 ) { int ixwd = xwd->ixwd; XFreeGC( xwd->display, dev->gc ); XFreeGC( xwd->display, xwd->gcXor ); XCloseDisplay( xwd->display ); free_mem( xwd->cmap0 ); free_mem( xwd->cmap1 ); free_mem( xwDisplay[ixwd] ); } // ANR: if we set this here the tmp file will not be closed // See also comment in tkwin.c //pls->plbuf_write = 0; } //-------------------------------------------------------------------------- // plD_wait_xw() // // Wait for user input //-------------------------------------------------------------------------- void plD_wait_xw( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; dbug_enter( "plD_eop_xw" ); #ifdef PL_HAVE_PTHREAD if ( usepthreads ) pthread_mutex_lock( &events_mutex ); #endif if ( dev->is_main ) WaitForPage( pls ); #ifdef PL_HAVE_PTHREAD if ( usepthreads ) pthread_mutex_unlock( &events_mutex ); #endif } //-------------------------------------------------------------------------- // plD_state_xw() // // Handle change in PLStream state (color, pen width, fill attribute, etc). //-------------------------------------------------------------------------- void plD_state_xw( PLStream *pls, PLINT op ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; dbug_enter( "plD_state_xw" ); #ifdef PL_HAVE_PTHREAD if ( usepthreads ) pthread_mutex_lock( &events_mutex ); #endif CheckForEvents( pls ); switch ( op ) { case PLSTATE_WIDTH: XSetLineAttributes( xwd->display, dev->gc, (unsigned int) pls->width, LineSolid, CapRound, JoinMiter ); break; case PLSTATE_COLOR0: { int icol0 = pls->icol0; if ( xwd->color ) { if ( icol0 == PL_RGB_COLOR ) { PLColor_to_XColor( &pls->curcolor, &dev->curcolor ); if ( !XAllocColor( xwd->display, xwd->map, &dev->curcolor ) ) { fprintf( stderr, "Warning: could not allocate color\n" ); dev->curcolor.pixel = xwd->fgcolor.pixel; } } else { dev->curcolor = xwd->cmap0[icol0]; } XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel ); } else { dev->curcolor = xwd->fgcolor; XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel ); } break; } case PLSTATE_COLOR1: { int icol1; if ( xwd->ncol1 == 0 ) AllocCmap1( pls ); if ( xwd->ncol1 < 2 ) break; icol1 = ( pls->icol1 * ( xwd->ncol1 - 1 ) ) / ( pls->ncol1 - 1 ); if ( xwd->color ) dev->curcolor = xwd->cmap1[icol1]; else dev->curcolor = xwd->fgcolor; XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel ); break; } case PLSTATE_CMAP0: SetBGFG( pls ); // If ncol0 has changed, need to reallocate if ( pls->ncol0 != xwd->ncol0 ) AllocCmap0( pls ); StoreCmap0( pls ); break; case PLSTATE_CMAP1: StoreCmap1( pls ); break; } #ifdef PL_HAVE_PTHREAD if ( usepthreads ) pthread_mutex_unlock( &events_mutex ); #endif } //-------------------------------------------------------------------------- // plD_esc_xw() // // Escape function. // // Functions: // // PLESC_EH Handle pending events // PLESC_EXPOSE Force an expose // PLESC_FILL Fill polygon // PLESC_FLUSH Flush X event buffer // PLESC_GETC Get coordinates upon mouse click // PLESC_REDRAW Force a redraw // PLESC_RESIZE Force a resize // PLESC_XORMOD set/reset xor mode // PLESC_IMAGE draw the image in a fast way // PLESC_IMAGEOPS: image related operations: // ZEROW2D disable writing to display // ZEROW2B disable writing to buffer // ONEW2D enable writing to display // ONEW2B enable writing to buffer // PLESC_PL2DEVCOL convert PLColor to device color (XColor) // PLESC_DEV2PLCOL convert device color (XColor) to PLColor // PLESC_SETBGFG set BG, FG colors // PLESC_DEVINIT alternate device initialization // // Note the [GET|SET]DEVCOL functions go through the intermediary stream // variable tmpcolor to keep the syntax simple. //-------------------------------------------------------------------------- void plD_esc_xw( PLStream *pls, PLINT op, void *ptr ) { dbug_enter( "plD_esc_xw" ); #ifdef PL_HAVE_PTHREAD if ( usepthreads ) pthread_mutex_lock( &events_mutex ); #endif switch ( op ) { case PLESC_EH: HandleEvents( pls ); break; case PLESC_EXPOSE: ExposeCmd( pls, (PLDisplay *) ptr ); break; case PLESC_FILL: FillPolygonCmd( pls ); break; case PLESC_FLUSH: { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; HandleEvents( pls ); XFlush( xwd->display ); break; } case PLESC_GETC: GetCursorCmd( pls, (PLGraphicsIn *) ptr ); break; case PLESC_REDRAW: RedrawCmd( pls ); break; case PLESC_RESIZE: ResizeCmd( pls, (PLDisplay *) ptr ); break; case PLESC_XORMOD: XorMod( pls, (PLINT *) ptr ); break; case PLESC_DOUBLEBUFFERING: ConfigBufferingCmd( pls, (PLBufferingCB *) ptr ); break; case PLESC_IMAGE: DrawImage( pls ); break; case PLESC_IMAGEOPS: imageops( pls, (PLINT *) ptr ); break; case PLESC_PL2DEVCOL: PLColor_to_XColor( &pls->tmpcolor, (XColor *) ptr ); break; case PLESC_DEV2PLCOL: PLColor_from_XColor( &pls->tmpcolor, (XColor *) ptr ); break; case PLESC_SETBGFG: SetBGFG( pls ); break; case PLESC_DEVINIT: OpenXwin( pls ); break; } #ifdef PL_HAVE_PTHREAD if ( usepthreads ) pthread_mutex_unlock( &events_mutex ); #endif } //-------------------------------------------------------------------------- // GetCursorCmd() // // Waits for a graphics input event and returns coordinates. //-------------------------------------------------------------------------- static void GetCursorCmd( PLStream *pls, PLGraphicsIn *ptr ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; XEvent event; PLGraphicsIn *gin = &( dev->gin ); // Initialize plGinInit( gin ); dev->locate_mode = LOCATE_INVOKED_VIA_API; CreateXhairs( pls ); // Run event loop until a point is selected while ( gin->pX < 0 && dev->locate_mode ) { // XWindowEvent( xwd->display, dev->window, dev->event_mask, &event ); XNextEvent( xwd->display, &event ); MasterEH( pls, &event ); } *ptr = *gin; if ( dev->locate_mode ) { dev->locate_mode = 0; DestroyXhairs( pls ); } } //-------------------------------------------------------------------------- // FillPolygonCmd() // // Fill polygon described in points pls->dev_x[] and pls->dev_y[]. // Only solid color fill supported. //-------------------------------------------------------------------------- static void FillPolygonCmd( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; XPoint _pts[PL_MAXPOLY]; XPoint *pts; int i; if ( pls->dev_npts > PL_MAXPOLY ) { pts = (XPoint *) malloc( sizeof ( XPoint ) * (size_t) ( pls->dev_npts ) ); } else { pts = _pts; } CheckForEvents( pls ); for ( i = 0; i < pls->dev_npts; i++ ) { pts[i].x = (short) ( dev->xscale * pls->dev_x[i] ); pts[i].y = (short) ( dev->yscale * ( dev->ylen - pls->dev_y[i] ) ); } // Fill polygons if ( dev->write_to_window ) XFillPolygon( xwd->display, dev->window, dev->gc, pts, pls->dev_npts, Complex, CoordModeOrigin ); if ( dev->write_to_pixmap ) XFillPolygon( xwd->display, dev->pixmap, dev->gc, pts, pls->dev_npts, Complex, CoordModeOrigin ); // If in debug mode, draw outline of boxes being filled #ifdef DEBUG if ( pls->debug ) { XSetForeground( xwd->display, dev->gc, xwd->fgcolor.pixel ); if ( dev->write_to_window ) XDrawLines( xwd->display, dev->window, dev->gc, pts, pls->dev_npts, CoordModeOrigin ); if ( dev->write_to_pixmap ) XDrawLines( xwd->display, dev->pixmap, dev->gc, pts, pls->dev_npts, CoordModeOrigin ); XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel ); } #endif if ( pls->dev_npts > PL_MAXPOLY ) { free( pts ); } } //-------------------------------------------------------------------------- // OpenXwin() // // Performs basic driver initialization, without actually opening or // modifying a window. May be called by the outside world before plinit // in case the caller needs early access to the driver internals (not // very common -- currently only used by the plframe widget). //-------------------------------------------------------------------------- static void OpenXwin( PLStream *pls ) { XwDev *dev; XwDisplay *xwd; int i; dbug_enter( "OpenXwin" ); // Allocate and initialize device-specific data if ( pls->dev != NULL ) plwarn( "OpenXwin: device pointer is already set" ); pls->dev = calloc( 1, (size_t) sizeof ( XwDev ) ); if ( pls->dev == NULL ) plexit( "plD_init_xw: Out of memory." ); dev = (XwDev *) pls->dev; // Variables used in querying the X server for events dev->instr = 0; dev->max_instr = MAX_INSTR; // See if display matches any already in use, and if so use that dev->xwd = NULL; for ( i = 0; i < PLXDISPLAYS; i++ ) { if ( xwDisplay[i] == NULL ) { continue; } else if ( pls->FileName == NULL && xwDisplay[i]->displayName == NULL ) { dev->xwd = xwDisplay[i]; break; } else if ( pls->FileName == NULL || xwDisplay[i]->displayName == NULL ) { continue; } else if ( strcmp( xwDisplay[i]->displayName, pls->FileName ) == 0 ) { dev->xwd = xwDisplay[i]; break; } } // If no display matched, create a new one if ( dev->xwd == NULL ) { dev->xwd = (XwDisplay *) calloc( 1, (size_t) sizeof ( XwDisplay ) ); if ( dev->xwd == NULL ) plexit( "Init: Out of memory." ); for ( i = 0; i < PLXDISPLAYS; i++ ) { if ( xwDisplay[i] == NULL ) break; } if ( i == PLXDISPLAYS ) plexit( "Init: Out of xwDisplay's." ); xwDisplay[i] = xwd = (XwDisplay *) dev->xwd; xwd->nstreams = 1; // Open display #ifdef PL_HAVE_PTHREAD if ( usepthreads ) if ( !XInitThreads() ) plexit( "xwin: XInitThreads() not successful." ); #endif xwd->display = XOpenDisplay( pls->FileName ); if ( xwd->display == NULL ) { plexit( "Can't open display" ); } xwd->displayName = pls->FileName; xwd->screen = DefaultScreen( xwd->display ); if ( synchronize ) XSynchronize( xwd->display, 1 ); // Get colormap and visual xwd->map = DefaultColormap( xwd->display, xwd->screen ); GetVisual( pls ); // // Figure out if we have a color display or not. // Default is color IF the user hasn't specified and IF the output device // is not grayscale. // if ( pls->colorset ) xwd->color = pls->color; else { pls->color = 1; xwd->color = !AreWeGrayscale( xwd->display ); } // Allocate space for colors // Note cmap1 allocation is deferred xwd->ncol0_alloc = pls->ncol0; xwd->cmap0 = (XColor *) calloc( (size_t) ( pls->ncol0 ), sizeof ( XColor ) ); if ( xwd->cmap0 == 0 ) plexit( "couldn't allocate space for cmap0 colors" ); // Allocate & set background and foreground colors AllocBGFG( pls ); SetBGFG( pls ); } // Display matched, so use existing display data else { xwd = (XwDisplay *) dev->xwd; xwd->nstreams++; } xwd->ixwd = i; } //-------------------------------------------------------------------------- // Init() // // Xlib initialization routine. // // Controlling routine for X window creation and/or initialization. // The user may customize the window in the following ways: // // display: pls->OutFile (use plsfnam() or -display option) // size: pls->xlength, pls->ylength (use plspage() or -geo option) // bg color: pls->cmap0[0] (use plscolbg() or -bg option) //-------------------------------------------------------------------------- static void Init( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; Window root; int x, y; dbug_enter( "Init" ); // If not plotting into a child window, need to create main window now if ( pls->window_id == 0 ) { dev->is_main = TRUE; InitMain( pls ); } else { dev->is_main = FALSE; dev->window = (Window) pls->window_id; } // Initialize colors if ( noinitcolors == 0 ) InitColors( pls ); XSetWindowColormap( xwd->display, dev->window, xwd->map ); // Set up GC for ordinary draws if ( !dev->gc ) dev->gc = XCreateGC( xwd->display, dev->window, 0, 0 ); // Set up GC for rubber-band draws if ( !xwd->gcXor ) { XGCValues gcValues; unsigned long mask; gcValues.background = xwd->cmap0[0].pixel; gcValues.foreground = 0xFF; gcValues.function = GXxor; mask = GCForeground | GCBackground | GCFunction; xwd->gcXor = XCreateGC( xwd->display, dev->window, mask, &gcValues ); } // Get initial drawing area dimensions (void) XGetGeometry( xwd->display, dev->window, &root, &x, &y, &dev->width, &dev->height, &dev->border, &xwd->depth ); dev->init_width = (long) dev->width; dev->init_height = (long) dev->height; // Set up flags that determine what we are writing to // If nopixmap is set, ignore db if ( pls->nopixmap ) { dev->write_to_pixmap = 0; pls->db = 0; } else { dev->write_to_pixmap = 1; } dev->write_to_window = !pls->db; // Create pixmap for holding plot image (for expose events). if ( dev->write_to_pixmap ) CreatePixmap( pls ); // Set drawing color plD_state_xw( pls, PLSTATE_COLOR0 ); XSetWindowBackground( xwd->display, dev->window, xwd->cmap0[0].pixel ); XSetBackground( xwd->display, dev->gc, xwd->cmap0[0].pixel ); // Set fill rule. if ( pls->dev_eofill ) XSetFillRule( xwd->display, dev->gc, EvenOddRule ); else XSetFillRule( xwd->display, dev->gc, WindingRule ); // If main window, need to map it and wait for exposure if ( dev->is_main ) MapMain( pls ); } //-------------------------------------------------------------------------- // InitMain() // // Create main window using standard Xlib calls. //-------------------------------------------------------------------------- static void InitMain( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; Window root; XSizeHints hint; int x, y; U_INT width, height, border, depth; dbug_enter( "InitMain" ); // Get root window geometry (void) XGetGeometry( xwd->display, DefaultRootWindow( xwd->display ), &root, &x, &y, &width, &height, &border, &depth ); // Set window size // Need to be careful to set XSizeHints flags correctly hint.flags = 0; if ( pls->xlength == 0 && pls->ylength == 0 ) hint.flags |= PSize; else hint.flags |= USSize; if ( pls->xlength == 0 ) pls->xlength = (PLINT) ( width * 0.75 ); if ( pls->ylength == 0 ) pls->ylength = (PLINT) ( height * 0.75 ); if ( pls->xlength > (short) width ) pls->xlength = (PLINT) ( width - dev->border * 2 ); if ( pls->ylength > (short) height ) pls->ylength = (PLINT) ( height - dev->border * 2 ); hint.width = (int) pls->xlength; hint.height = (int) pls->ylength; dev->border = 5; // Set window position if specified by the user. // Otherwise leave up to the window manager if ( pls->xoffset != 0 || pls->yoffset != 0 ) { hint.flags |= USPosition; hint.x = (int) pls->xoffset; hint.y = (int) pls->yoffset; } else { hint.x = 0; hint.y = 0; } // Window creation dev->window = XCreateWindow( xwd->display, DefaultRootWindow( xwd->display ), hint.x, hint.y, (unsigned int) hint.width, (unsigned int) hint.height, dev->border, (int) xwd->depth, InputOutput, xwd->visual, 0, NULL ); XSetStandardProperties( xwd->display, dev->window, pls->plwindow, pls->plwindow, None, 0, 0, &hint ); } //-------------------------------------------------------------------------- // MapMain() // // Sets up event handlers, maps main window and waits for exposure. //-------------------------------------------------------------------------- static void MapMain( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; XEvent event; dbug_enter( "MapMain" ); // Input event selection dev->event_mask = ButtonPressMask | KeyPressMask | ExposureMask | ButtonMotionMask | // drag StructureNotifyMask; XSelectInput( xwd->display, dev->window, dev->event_mask ); // Window mapping XMapRaised( xwd->display, dev->window ); Atom wmDelete = XInternAtom( xwd->display, "WM_DELETE_WINDOW", False ); XSetWMProtocols( xwd->display, dev->window, &wmDelete, 1 ); // Wait for exposure // Remove extraneous expose events from the event queue for (;; ) { XWindowEvent( xwd->display, dev->window, dev->event_mask, &event ); if ( event.type == Expose ) { while ( XCheckWindowEvent( xwd->display, dev->window, ExposureMask, &event ) ) ; break; } } } //-------------------------------------------------------------------------- // WaitForPage() // // This routine waits for the user to advance the plot, while handling // all other events. //-------------------------------------------------------------------------- static void WaitForPage( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; XEvent event; dbug_enter( "WaitForPage" ); while ( !dev->exit_eventloop ) { // XWindowEvent( xwd->display, dev->window, dev->event_mask, &event ); XNextEvent( xwd->display, &event ); MasterEH( pls, &event ); } dev->exit_eventloop = FALSE; } //-------------------------------------------------------------------------- // events_thread() // // This function is being running continuously by a thread and is // responsible for dealing with expose and resize X events, in // the case that the main program is too busy to honor them. // // Dealing with other X events is possible, but not desirable, // e.g. treating the "Q" or right-mouse-button would terminate // the thread (if this is desirable, the thread should kill the // main program -- not thread aware -- and kill itself afterward). // // This works pretty well, but the main program *must* be linked // with the pthread library, although not being thread aware. // This happens automatically when linking against libplplot.so, // but when building modules for extending some language such as // Python or Octave, the language providing binary itself must be // relinked with -lpthread. // //-------------------------------------------------------------------------- #ifdef PL_HAVE_PTHREAD static void events_thread( void *pls ) { if ( usepthreads ) { PLStream *lpls = (PLStream *) pls; XwDev *dev = (XwDev *) lpls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; PLStream *oplsc; struct timespec delay; XEvent event; long event_mask; sigset_t set; // // only treats exposures and resizes, but remove usual events from queue, // as it can be disturbing to not have them acknowledged in real time, // because the program is busy, and suddenly all being acknowledged. // Also, the locator ("L" key) is sluggish if driven by the thread. // // But this approach is a problem when the thread removes events // from the queue while the program is responsible! The user hits 'L' // and nothing happens, as the thread removes it. // // Perhaps the "Q" key should have a different treatment, quiting the // program anyway? // // Changed: does not remove non treated events from the queue // event_mask = ExposureMask | StructureNotifyMask; // block all signal for this thread sigemptyset( &set ); // sigfillset(&set); can't be all signals, decide latter sigaddset( &set, SIGINT ); sigprocmask( SIG_BLOCK, &set, NULL ); pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL ); pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, NULL ); delay.tv_sec = 0; delay.tv_nsec = 10000000; // this thread runs 10 times a second. (1/10 ms) while ( 1 ) { pthread_mutex_lock( &events_mutex ); if ( dev->is_main && !lpls->plbuf_read && ++dev->instr % dev->max_instr == 0 ) { dev->instr = 0; while ( XCheckWindowEvent( xwd->display, dev->window, event_mask, &event ) ) { // As ResizeEH() ends up calling plRemakePlot(), that // indirectly uses the current stream, one needs to // temporarily set plplot current stream to this thread's // stream oplsc = plsc; plsc = lpls; switch ( event.type ) { case Expose: ExposeEH( lpls, &event ); break; case ConfigureNotify: ResizeEH( lpls, &event ); break; } plsc = oplsc; } } pthread_mutex_unlock( &events_mutex ); nanosleep( &delay, NULL ); // 10ms in linux /* pthread_yield(NULL); */ /* this puts too much load on the CPU */ } } } #endif //-------------------------------------------------------------------------- // CheckForEvents() // // A front-end to HandleEvents(), which is only called if certain conditions // are satisfied: // // - must be the creator of the main window (i.e. PLplot is handling the // X event loop by polling). // - must not be in the middle of a plot redisplay (else the order of event // handling can become circuitous). // - only query X for events and process them every dev->max_instr times // this function is called (good for performance since querying X is a // nontrivial performance hit). //-------------------------------------------------------------------------- static void CheckForEvents( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; if ( dev->is_main && !pls->plbuf_read && ++dev->instr % dev->max_instr == 0 ) { dev->instr = 0; HandleEvents( pls ); } } //-------------------------------------------------------------------------- // HandleEvents() // // Just a front-end to MasterEH(), for use when not actually waiting for an // event but only checking the event queue. //-------------------------------------------------------------------------- static void HandleEvents( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; XEvent event; while ( XCheckTypedWindowEvent( xwd->display, dev->window, ClientMessage, &event ) || XCheckWindowEvent( xwd->display, dev->window, dev->event_mask, &event ) ) MasterEH( pls, &event ); } //-------------------------------------------------------------------------- // MasterEH() // // Master X event handler routine. // Redirects control to routines to handle: // - keyboard events // - mouse events // - expose events // - resize events // // By supplying a master event handler, the user can take over all event // processing. If events other than those trapped by PLplot need handling, // just call XSelectInput with the appropriate flags. The default PLplot // event handling can be modified arbitrarily by changing the event struct. //-------------------------------------------------------------------------- static void MasterEH( PLStream *pls, XEvent *event ) { XwDev *dev = (XwDev *) pls->dev; if ( dev->MasterEH != NULL ) ( *dev->MasterEH )( pls, event ); switch ( event->type ) { case KeyPress: KeyEH( pls, event ); break; case ButtonPress: ButtonEH( pls, event ); break; case Expose: ExposeEH( pls, event ); break; case ConfigureNotify: ResizeEH( pls, event ); break; case MotionNotify: if ( event->xmotion.state ) ButtonEH( pls, event ); // drag MotionEH( pls, event ); break; case EnterNotify: EnterEH( pls, event ); break; case LeaveNotify: LeaveEH( pls, event ); break; case ClientMessage: ClientEH( pls, event ); break; } } //-------------------------------------------------------------------------- // ClientEH() // // Event handler routine for client message events (WM_DELETE_WINDOW) //-------------------------------------------------------------------------- static void ClientEH( PLStream *pls, XEvent *event ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; if ( (Atom) event->xclient.data.l[0] == XInternAtom( xwd->display, "WM_DELETE_WINDOW", False ) ) { pls->nopause = TRUE; pls->stream_closed = TRUE; dev->exit_eventloop = TRUE; // plexit( "" ); } } //-------------------------------------------------------------------------- // KeyEH() // // Event handler routine for keyboard events. //-------------------------------------------------------------------------- static void KeyEH( PLStream *pls, XEvent *event ) { XwDev *dev = (XwDev *) pls->dev; dbug_enter( "KeyEH" ); LookupXKeyEvent( pls, event ); if ( dev->locate_mode ) LocateKey( pls ); else ProcessKey( pls ); } //-------------------------------------------------------------------------- // ButtonEH() // // Event handler routine for ButtonPress events. //-------------------------------------------------------------------------- static void ButtonEH( PLStream *pls, XEvent *event ) { XwDev *dev = (XwDev *) pls->dev; dbug_enter( "ButtonEH" ); LookupXButtonEvent( pls, event ); if ( dev->locate_mode ) LocateButton( pls ); else ProcessButton( pls ); } //-------------------------------------------------------------------------- // LookupXKeyEvent() // // Fills in the PLGraphicsIn from an XKeyEvent. The PLGraphicsIn keysym is // the same as the X keysym for all cases except for control keys that have // ASCII equivalents, i.e.: // // Name X-keysym ASCII Ctrl-key // ---- -------- ----- -------- // XK_BackSpace 0xFF08 0x08 ^H // XK_Tab 0xFF09 0x09 ^I // XK_Linefeed 0xFF0A 0x0A ^J // XK_Return 0xFF0D 0x0D ^M // XK_Escape 0xFF1B 0x1B ^[ // XK_Delete 0xFFFF 0xFF (none) // // The ASCII representation of these characters is used for the PLGraphicsIn // keysym to simplify code logic. It turns out that the X keysyms are // identical to the ASCII values with the upper 8 bits set. //-------------------------------------------------------------------------- static void LookupXKeyEvent( PLStream *pls, XEvent *event ) { XwDev *dev = (XwDev *) pls->dev; PLGraphicsIn *gin = &( dev->gin ); XKeyEvent *keyEvent = (XKeyEvent *) event; KeySym keysym; int nchars, ncmax = PL_MAXKEY - 1; XComposeStatus cs; gin->pX = keyEvent->x; gin->pY = keyEvent->y; gin->dX = (PLFLT) keyEvent->x / ( dev->width - 1 ); gin->dY = 1.0 - (PLFLT) keyEvent->y / ( dev->height - 1 ); gin->state = keyEvent->state; nchars = XLookupString( keyEvent, gin->string, ncmax, &keysym, &cs ); gin->string[nchars] = '\0'; pldebug( "LookupXKeyEvent", "Keysym %x, translation: %s\n", keysym, gin->string ); switch ( keysym ) { case XK_BackSpace: case XK_Tab: case XK_Linefeed: case XK_Return: case XK_Escape: case XK_Delete: gin->keysym = 0xFF & keysym; break; default: gin->keysym = (unsigned int) keysym; } } //-------------------------------------------------------------------------- // LookupXButtonEvent() // // Fills in the PLGraphicsIn from an XButtonEvent. //-------------------------------------------------------------------------- static void LookupXButtonEvent( PLStream *pls, XEvent *event ) { XwDev *dev = (XwDev *) pls->dev; PLGraphicsIn *gin = &( dev->gin ); XButtonEvent *buttonEvent = (XButtonEvent *) event; pldebug( "LookupXButtonEvent", "Button: %d, x: %d, y: %d\n", buttonEvent->button, buttonEvent->x, buttonEvent->y ); gin->pX = buttonEvent->x; gin->pY = buttonEvent->y; gin->dX = (PLFLT) buttonEvent->x / ( dev->width - 1 ); gin->dY = 1.0 - (PLFLT) buttonEvent->y / ( dev->height - 1 ); gin->button = buttonEvent->button; gin->state = buttonEvent->state; gin->keysym = 0x20; } //-------------------------------------------------------------------------- // ProcessKey() // // Process keyboard events other than locate input. //-------------------------------------------------------------------------- static void ProcessKey( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; PLGraphicsIn *gin = &( dev->gin ); dbug_enter( "ProcessKey" ); // Call user keypress event handler. Since this is called first, the user // can disable all internal event handling by setting key.keysym to 0. // if ( pls->KeyEH != NULL ) ( *pls->KeyEH )( gin, pls->KeyEH_data, &dev->exit_eventloop ); // Handle internal events switch ( gin->keysym ) { case PLK_Return: case PLK_Linefeed: case PLK_Next: // Advance to next page (i.e. terminate event loop) on a // Check for both and for portability, also a dev->exit_eventloop = TRUE; break; case 'Q': // Terminate on a 'Q' (not 'q', since it's too easy to hit by mistake) pls->nopause = TRUE; plexit( "" ); break; case 'L': // Begin locate mode dev->locate_mode = LOCATE_INVOKED_VIA_DRIVER; CreateXhairs( pls ); break; } } //-------------------------------------------------------------------------- // ProcessButton() // // Process ButtonPress events other than locate input. // On: // Button1: nothing (except when in locate mode, see ButtonLocate) // Button2: nothing // Button3: set page advance flag //-------------------------------------------------------------------------- static void ProcessButton( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; PLGraphicsIn *gin = &( dev->gin ); dbug_enter( "ProcessButton" ); // Call user event handler. Since this is called first, the user can // disable all PLplot internal event handling by setting gin->button to 0. // if ( pls->ButtonEH != NULL ) ( *pls->ButtonEH )( gin, pls->ButtonEH_data, &dev->exit_eventloop ); // Handle internal events switch ( gin->button ) { case Button3: dev->exit_eventloop = TRUE; break; } } //-------------------------------------------------------------------------- // LocateKey() // // Front-end to locate handler for KeyPress events. // Provides for a number of special effects: // // Ends locate mode // Moves cursor one pixel in the specified direction // Accelerated cursor movement (5x for each modifier) //-------------------------------------------------------------------------- static void LocateKey( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; PLGraphicsIn *gin = &( dev->gin ); // End locate mode on if ( gin->keysym == PLK_Escape ) { dev->locate_mode = 0; DestroyXhairs( pls ); plGinInit( gin ); } // Ignore modifier keys else if ( IsModifierKey( gin->keysym ) ) { plGinInit( gin ); } // Now handle cursor keys else if ( IsCursorKey( gin->keysym ) ) { int x1, y1, dx = 0, dy = 0; int xmin = 0, xmax = (int) dev->width - 1, ymin = 0, ymax = (int) dev->height - 1; switch ( gin->keysym ) { case PLK_Left: dx = -1; break; case PLK_Right: dx = 1; break; case PLK_Up: dy = -1; break; case PLK_Down: dy = 1; break; } // Each modifier key added increases the multiplication factor by 5 // Shift if ( gin->state & 0x01 ) { dx *= 5; dy *= 5; } // Caps Lock if ( gin->state & 0x02 ) { dx *= 5; dy *= 5; } // Control if ( gin->state & 0x04 ) { dx *= 5; dy *= 5; } // Alt if ( gin->state & 0x08 ) { dx *= 5; dy *= 5; } // Bounds checking so that we don't send cursor out of window x1 = gin->pX + dx; y1 = gin->pY + dy; if ( x1 < xmin ) dx = xmin - gin->pX; if ( y1 < ymin ) dy = ymin - gin->pY; if ( x1 > xmax ) dx = xmax - gin->pX; if ( y1 > ymax ) dy = ymax - gin->pY; // Engage... XWarpPointer( xwd->display, dev->window, None, 0, 0, 0, 0, dx, dy ); plGinInit( gin ); } // Call ordinary locate handler else { Locate( pls ); } } //-------------------------------------------------------------------------- // LocateButton() // // Front-end to locate handler for ButtonPress events. // Only passes control to Locate() for Button1 presses. //-------------------------------------------------------------------------- static void LocateButton( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; PLGraphicsIn *gin = &( dev->gin ); switch ( gin->button ) { case Button1: Locate( pls ); break; } } //-------------------------------------------------------------------------- // Locate() // // Handles locate mode events. // // In locate mode: move cursor to desired location and select by pressing a // key or by clicking on the mouse (if available). Typically the world // coordinates of the selected point are reported. // // There are two ways to enter Locate mode -- via the API, or via a driver // command. The API entry point is the call plGetCursor(), which initiates // locate mode and does not return until input has been obtained. The // driver entry point is by entering a 'L' while the driver is waiting for // events. // // Locate mode input is reported in one of three ways: // 1. Through a returned PLGraphicsIn structure, when user has specified a // locate handler via (*pls->LocateEH). // 2. Through a returned PLGraphicsIn structure, when locate mode is invoked // by a plGetCursor() call. // 3. Through writes to stdout, when locate mode is invoked by a driver // command and the user has not supplied a locate handler. // // Hitting will at all times end locate mode. Other keys will // typically be interpreted as locator input. Selecting a point out of // bounds will end locate mode unless the user overrides with a supplied // Locate handler. //-------------------------------------------------------------------------- static void Locate( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; PLGraphicsIn *gin = &( dev->gin ); // Call user locate mode handler if provided if ( pls->LocateEH != NULL ) ( *pls->LocateEH )( gin, pls->LocateEH_data, &dev->locate_mode ); // Use default procedure else { // Try to locate cursor if ( plTranslateCursor( gin ) ) { // If invoked by the API, we're done // Otherwise send report to stdout if ( dev->locate_mode == LOCATE_INVOKED_VIA_DRIVER ) { pltext(); if ( gin->keysym < 0xFF && isprint( gin->keysym ) ) printf( "%f %f %c\n", gin->wX, gin->wY, gin->keysym ); else printf( "%f %f 0x%02x\n", gin->wX, gin->wY, gin->keysym ); plgra(); } } else { // Selected point is out of bounds, so end locate mode dev->locate_mode = 0; DestroyXhairs( pls ); } } } //-------------------------------------------------------------------------- // MotionEH() // // Event handler routine for MotionNotify events. // If drawing crosshairs, the first and last draws must be done "by hand". //-------------------------------------------------------------------------- static void MotionEH( PLStream *pls, XEvent *event ) { XwDev *dev = (XwDev *) pls->dev; XMotionEvent *motionEvent = (XMotionEvent *) event; if ( dev->drawing_xhairs ) { DrawXhairs( pls, motionEvent->x, motionEvent->y ); } } //-------------------------------------------------------------------------- // EnterEH() // // Event handler routine for EnterNotify events. Only called if drawing // crosshairs -- a draw must be done here to start off the new set. //-------------------------------------------------------------------------- static void EnterEH( PLStream *pls, XEvent *event ) { XwDev *dev = (XwDev *) pls->dev; XCrossingEvent *crossingEvent = (XCrossingEvent *) event; DrawXhairs( pls, crossingEvent->x, crossingEvent->y ); dev->drawing_xhairs = 1; } //-------------------------------------------------------------------------- // LeaveEH() // // Event handler routine for EnterNotify or LeaveNotify events. // If drawing crosshairs, a draw must be done here to start off the new // set or erase the last set. //-------------------------------------------------------------------------- static void LeaveEH( PLStream *pls, XEvent * PL_UNUSED( event ) ) { XwDev *dev = (XwDev *) pls->dev; UpdateXhairs( pls ); dev->drawing_xhairs = 0; } //-------------------------------------------------------------------------- // CreateXhairs() // // Creates graphic crosshairs at current pointer location. //-------------------------------------------------------------------------- static void CreateXhairs( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; Window root, child; int root_x, root_y, win_x, win_y; unsigned int mask; XEvent event; // Get a crosshair cursor and switch to it. if ( !xwd->xhair_cursor ) xwd->xhair_cursor = XCreateFontCursor( xwd->display, XC_crosshair ); XDefineCursor( xwd->display, dev->window, xwd->xhair_cursor ); // Find current pointer location and draw graphic crosshairs if pointer is // inside our window if ( XQueryPointer( xwd->display, dev->window, &root, &child, &root_x, &root_y, &win_x, &win_y, &mask ) ) { if ( win_x >= 0 && win_x < (int) dev->width && win_y >= 0 && win_y < (int) dev->height ) { DrawXhairs( pls, win_x, win_y ); dev->drawing_xhairs = 1; } } // Sync the display and then throw away all pending motion events XSync( xwd->display, 0 ); while ( XCheckWindowEvent( xwd->display, dev->window, PointerMotionMask, &event ) ) ; // Catch PointerMotion and crossing events so we can update them properly dev->event_mask |= PointerMotionMask | EnterWindowMask | LeaveWindowMask; XSelectInput( xwd->display, dev->window, dev->event_mask ); } //-------------------------------------------------------------------------- // DestroyXhairs() // // Destroys graphic crosshairs. //-------------------------------------------------------------------------- static void DestroyXhairs( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; // Switch back to boring old pointer XUndefineCursor( xwd->display, dev->window ); // Don't catch PointerMotion or crossing events any more dev->event_mask &= ~PointerMotionMask & ~EnterWindowMask & ~LeaveWindowMask; XSelectInput( xwd->display, dev->window, dev->event_mask ); // This draw removes the last set of graphic crosshairs UpdateXhairs( pls ); dev->drawing_xhairs = 0; } //-------------------------------------------------------------------------- // DrawXhairs() // // Draws graphic crosshairs at (x0, y0). The first draw erases the old set. //-------------------------------------------------------------------------- static void DrawXhairs( PLStream *pls, int x0, int y0 ) { XwDev *dev = (XwDev *) pls->dev; int xmin = 0, xmax = (int) dev->width - 1; int ymin = 0, ymax = (int) dev->height - 1; if ( dev->drawing_xhairs ) UpdateXhairs( pls ); dev->xhair_x[0].x = (short) xmin; dev->xhair_x[0].y = (short) y0; dev->xhair_x[1].x = (short) xmax; dev->xhair_x[1].y = (short) y0; dev->xhair_y[0].x = (short) x0; dev->xhair_y[0].y = (short) ymin; dev->xhair_y[1].x = (short) x0; dev->xhair_y[1].y = (short) ymax; UpdateXhairs( pls ); } //-------------------------------------------------------------------------- // UpdateXhairs() // // Updates graphic crosshairs. If already there, they are erased. //-------------------------------------------------------------------------- static void UpdateXhairs( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; XDrawLines( xwd->display, dev->window, xwd->gcXor, dev->xhair_x, 2, CoordModeOrigin ); XDrawLines( xwd->display, dev->window, xwd->gcXor, dev->xhair_y, 2, CoordModeOrigin ); } //-------------------------------------------------------------------------- // ExposeEH() // // Event handler routine for expose events. // Front end to ExposeCmd() to deal with wierdnesses of Xlib. //-------------------------------------------------------------------------- static void ExposeEH( PLStream *pls, XEvent *event ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; XExposeEvent *exposeEvent = (XExposeEvent *) event; PLDisplay pldis; int redrawn; dbug_enter( "ExposeEH" ); pldebug( "ExposeEH", "x = %d, y = %d, width = %d, height = %d, count = %d, pending = %d\n", exposeEvent->x, exposeEvent->y, exposeEvent->width, exposeEvent->height, exposeEvent->count, XPending( xwd->display ) ); // Handle expose // If we have anything overlaid (like crosshairs), we need to refresh the // entire plot in order to have a predictable outcome. In this case we // need to first clear window. Otherwise it's better to not clear it, for a // smoother redraw (unobscured regions appear to stay the same). if ( dev->drawing_xhairs ) { XClearWindow( xwd->display, dev->window ); ExposeCmd( pls, NULL ); UpdateXhairs( pls ); redrawn = 1; } else { pldis.x = (unsigned int) exposeEvent->x; pldis.y = (unsigned int) exposeEvent->y; pldis.width = (unsigned int) exposeEvent->width; pldis.height = (unsigned int) exposeEvent->height; ExposeCmd( pls, &pldis ); redrawn = !dev->write_to_pixmap; } // If entire plot was redrawn, remove extraneous events from the queue if ( redrawn ) while ( XCheckWindowEvent( xwd->display, dev->window, ExposureMask | StructureNotifyMask, event ) ) ; } //-------------------------------------------------------------------------- // ResizeEH() // // Event handler routine for resize events. // Front end to ResizeCmd() to deal with wierdnesses of Xlib. //-------------------------------------------------------------------------- static void ResizeEH( PLStream *pls, XEvent *event ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; XConfigureEvent *configEvent = (XConfigureEvent *) event; PLDisplay pldis; dbug_enter( "ResizeEH" ); pldis.width = (unsigned int) configEvent->width; pldis.height = (unsigned int) configEvent->height; // Only need to resize if size is actually changed if ( pldis.width == dev->width && pldis.height == dev->height ) return; pldebug( "ResizeEH", "x = %d, y = %d, pending = %d\n", configEvent->width, configEvent->height, XPending( xwd->display ) ); // Handle resize ResizeCmd( pls, &pldis ); if ( dev->drawing_xhairs ) { UpdateXhairs( pls ); } // Remove extraneous Expose and ConfigureNotify events from the event queue // Exposes do not need to be handled since we've redrawn the whole plot XFlush( xwd->display ); while ( XCheckWindowEvent( xwd->display, dev->window, ExposureMask | StructureNotifyMask, event ) ) ; } //-------------------------------------------------------------------------- // ExposeCmd() // // Event handler routine for expose events. // These are "pure" exposures (no resize), so don't need to clear window. //-------------------------------------------------------------------------- static void ExposeCmd( PLStream *pls, PLDisplay *pldis ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; int x, y; unsigned int width, height; dbug_enter( "ExposeCmd" ); // Return if plD_init_xw hasn't been called yet if ( dev == NULL ) { plwarn( "ExposeCmd: Illegal call -- driver uninitialized" ); return; } // Exposed area. If unspecified, the entire window is used. if ( pldis == NULL ) { x = 0; y = 0; width = dev->width; height = dev->height; } else { x = (int) pldis->x; y = (int) pldis->y; width = pldis->width; height = pldis->height; } // Usual case: refresh window from pixmap // DEBUG option: draws rectangle around refreshed region XSync( xwd->display, 0 ); if ( dev->write_to_pixmap ) { XCopyArea( xwd->display, dev->pixmap, dev->window, dev->gc, x, y, width, height, x, y ); XSync( xwd->display, 0 ); #ifdef DEBUG if ( pls->debug ) { XPoint pts[5]; int x0 = x, x1 = x + (int) width, y0 = y, y1 = y + (int) height; pts[0].x = (short) x0; pts[0].y = (short) y0; pts[1].x = (short) x1; pts[1].y = (short) y0; pts[2].x = (short) x1; pts[2].y = (short) y1; pts[3].x = (short) x0; pts[3].y = (short) y1; pts[4].x = (short) x0; pts[4].y = (short) y0; XDrawLines( xwd->display, dev->window, dev->gc, pts, 5, CoordModeOrigin ); } #endif } else { plRemakePlot( pls ); XFlush( xwd->display ); } } //-------------------------------------------------------------------------- // ResizeCmd() // // Event handler routine for resize events. //-------------------------------------------------------------------------- static void ResizeCmd( PLStream *pls, PLDisplay *pldis ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; int write_to_window = dev->write_to_window; dbug_enter( "ResizeCmd" ); // Return if plD_init_xw hasn't been called yet if ( dev == NULL ) { plwarn( "ResizeCmd: Illegal call -- driver uninitialized" ); return; } // Return if pointer to window not specified. if ( pldis == NULL ) { plwarn( "ResizeCmd: Illegal call -- window pointer uninitialized" ); return; } // Reset current window bounds dev->width = pldis->width; dev->height = pldis->height; dev->xscale = dev->width / (double) dev->init_width; dev->yscale = dev->height / (double) dev->init_height; dev->xscale = dev->xscale * dev->xscale_init; dev->yscale = dev->yscale * dev->yscale_init; #if PHYSICAL { PLFLT pxlx = DPMM / dev->xscale; PLFLT pxly = DPMM / dev->yscale; plP_setpxl( pxlx, pxly ); } #endif // Note: the following order MUST be obeyed -- if you instead redraw into // the window and then copy it to the pixmap, off-screen parts of the window // may contain garbage which is then transferred to the pixmap (and thus // will not go away after an expose). // // Resize pixmap using new dimensions if ( dev->write_to_pixmap ) { dev->write_to_window = 0; XFreePixmap( xwd->display, dev->pixmap ); CreatePixmap( pls ); } // This allows an external agent to take over the redraw if ( pls->ext_resize_draw ) return; // Initialize & redraw (to pixmap, if available). if ( dev->write_to_pixmap ) { XSetForeground( xwd->display, dev->gc, dev->bgcolor.pixel ); XFillRectangle( xwd->display, dev->pixmap, dev->gc, 0, 0, dev->width, dev->height ); XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel ); } if ( dev->write_to_window ) { XClearWindow( xwd->display, dev->window ); } plRemakePlot( pls ); XSync( xwd->display, 0 ); // If pixmap available, fake an expose if ( dev->write_to_pixmap ) { dev->write_to_window = write_to_window; XCopyArea( xwd->display, dev->pixmap, dev->window, dev->gc, 0, 0, dev->width, dev->height, 0, 0 ); XSync( xwd->display, 0 ); } } //-------------------------------------------------------------------------- // ConfigBufferingCmd() // // Allows a widget to manipulate the double buffering support in the // xwin dirver. //-------------------------------------------------------------------------- static void ConfigBufferingCmd( PLStream *pls, PLBufferingCB *ptr ) { XwDev *dev = (XwDev *) pls->dev; switch ( ptr->cmd ) { case PLESC_DOUBLEBUFFERING_ENABLE: dev->write_to_window = 0; pls->db = 1; break; case PLESC_DOUBLEBUFFERING_DISABLE: dev->write_to_window = 1; pls->db = 0; break; case PLESC_DOUBLEBUFFERING_QUERY: ptr->result = pls->db; break; default: printf( "Unrecognized buffering request ignored.\n" ); break; } } //-------------------------------------------------------------------------- // RedrawCmd() // // Handles page redraw without resize (pixmap does not get reallocated). // Calling this makes sure all necessary housekeeping gets done. //-------------------------------------------------------------------------- static void RedrawCmd( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; int write_to_window = dev->write_to_window; dbug_enter( "RedrawCmd" ); // Return if plD_init_xw hasn't been called yet if ( dev == NULL ) { plwarn( "RedrawCmd: Illegal call -- driver uninitialized" ); return; } // Initialize & redraw (to pixmap, if available). if ( dev->write_to_pixmap ) { dev->write_to_window = 0; XSetForeground( xwd->display, dev->gc, dev->bgcolor.pixel ); XFillRectangle( xwd->display, dev->pixmap, dev->gc, 0, 0, dev->width, dev->height ); XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel ); } if ( dev->write_to_window ) { XClearWindow( xwd->display, dev->window ); } plRemakePlot( pls ); XSync( xwd->display, 0 ); dev->write_to_window = write_to_window; // If pixmap available, fake an expose if ( dev->write_to_pixmap ) { XCopyArea( xwd->display, dev->pixmap, dev->window, dev->gc, 0, 0, dev->width, dev->height, 0, 0 ); XSync( xwd->display, 0 ); } } //-------------------------------------------------------------------------- // CreatePixmapErrorHandler() // // Error handler used in CreatePixmap() to catch errors in allocating // storage for pixmap. This way we can nicely substitute redraws for // pixmap copies if the server has insufficient memory. //-------------------------------------------------------------------------- static unsigned char CreatePixmapStatus; static int CreatePixmapErrorHandler( Display *display, XErrorEvent *error ) { CreatePixmapStatus = error->error_code; if ( error->error_code != BadAlloc ) { char buffer[256]; XGetErrorText( display, error->error_code, buffer, 256 ); fprintf( stderr, "Error in XCreatePixmap: %s.\n", buffer ); } return 1; } //-------------------------------------------------------------------------- // CreatePixmap() // // This routine creates a pixmap, doing error trapping in case there // isn't enough memory on the server. //-------------------------------------------------------------------------- static void CreatePixmap( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; int ( *oldErrorHandler )( Display *, XErrorEvent * ); oldErrorHandler = XSetErrorHandler( CreatePixmapErrorHandler ); CreatePixmapStatus = Success; pldebug( "CreatePixmap", "creating pixmap: width = %d, height = %d, depth = %d\n", dev->width, dev->height, xwd->depth ); dev->pixmap = XCreatePixmap( xwd->display, dev->window, dev->width, dev->height, xwd->depth ); XSync( xwd->display, 0 ); if ( CreatePixmapStatus != Success ) { dev->write_to_pixmap = 0; dev->write_to_window = 1; pls->db = 0; fprintf( stderr, "\n\ Warning: pixmap could not be allocated (insufficient memory on server).\n\ Driver will redraw the entire plot to handle expose events.\n" ); } XSetErrorHandler( oldErrorHandler ); } //-------------------------------------------------------------------------- // GetVisual() // // Get visual info. In order to safely use a visual other than that of // the parent (which hopefully is that returned by DefaultVisual), you // must first find (using XGetRGBColormaps) or create a colormap matching // this visual and then set the colormap window attribute in the // XCreateWindow attributes and valuemask arguments. I don't do this // right now, so this is turned off by default. //-------------------------------------------------------------------------- static void GetVisual( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; int visuals_matched = 0; dbug_enter( "GetVisual" ); if ( !defaultvisual ) { XVisualInfo vTemplate, *visualList; // Try for an 8 plane display, if unavailable go for the default vTemplate.screen = xwd->screen; vTemplate.depth = 8; visualList = XGetVisualInfo( xwd->display, VisualScreenMask | VisualDepthMask, &vTemplate, &visuals_matched ); #ifdef HACK_STATICCOLOR if ( visuals_matched ) { int i, found = 0; printf( "visuals_matched = %d\n", visuals_matched ); for ( i = 0; i < visuals_matched && !found; i++ ) { Visual *v = visualList[i].visual; printf( "Checking visual %d: ", i ); switch ( v->class ) { case PseudoColor: printf( "PseudoColor\n" ); break; case GrayScale: printf( "GrayScale\n" ); break; case DirectColor: printf( "DirectColor\n" ); break; case TrueColor: printf( "TrueColor\n" ); break; case StaticColor: printf( "StaticColor\n" ); break; case StaticGray: printf( "StaticGray\n" ); break; default: printf( "Unknown.\n" ); break; } if ( v->class == StaticColor ) { xwd->visual = v; xwd->depth = visualList[i].depth; found = 1; } } if ( !found ) { plexit( "Unable to get a StaticColor visual." ); } printf( "Found StaticColor visual, depth=%d\n", xwd->depth ); } #else if ( visuals_matched ) { xwd->visual = visualList->visual; // Choose first match. xwd->depth = (unsigned int) vTemplate.depth; } #endif // HACK_STATICCOLOR } if ( !visuals_matched ) { xwd->visual = DefaultVisual( xwd->display, xwd->screen ); xwd->depth = (unsigned int) DefaultDepth( xwd->display, xwd->screen ); } // Check to see if we expect to be able to allocate r/w color cells. switch ( xwd->visual->class ) { case TrueColor: case StaticColor: case StaticGray: xwd->rw_cmap = 0; break; default: xwd->rw_cmap = 1; } /*xwd->rw_cmap = 0;*/ /* debugging hack. */ // Just for kicks, see what kind of visual we got. if ( pls->verbose ) { fprintf( stderr, "XVisual class == " ); switch ( xwd->visual->class ) { case PseudoColor: fprintf( stderr, "PseudoColor\n" ); break; case GrayScale: fprintf( stderr, "GrayScale\n" ); break; case DirectColor: fprintf( stderr, "DirectColor\n" ); break; case TrueColor: fprintf( stderr, "TrueColor\n" ); break; case StaticColor: fprintf( stderr, "StaticColor\n" ); break; case StaticGray: fprintf( stderr, "StaticGray\n" ); break; default: fprintf( stderr, "Unknown.\n" ); break; } fprintf( stderr, "xwd->rw_cmap = %d\n", xwd->rw_cmap ); } } //-------------------------------------------------------------------------- // AllocBGFG() // // Allocate background & foreground colors. If possible, I choose pixel // values such that the fg pixel is the xor of the bg pixel, to make // rubber-banding easy to see. //-------------------------------------------------------------------------- static void AllocBGFG( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; int i, j, npixels; unsigned long plane_masks[1], pixels[RWMAP_MAX_COLORS]; dbug_enter( "AllocBGFG" ); // If not on a color system, just return if ( !xwd->color ) return; if ( xwd->rw_cmap && // r/w color maps XAllocColorCells( xwd->display, xwd->map, False, plane_masks, 0, pixels, 1 ) ) { // background xwd->cmap0[0].pixel = pixels[0]; } else { // r/o color maps xwd->cmap0[0].pixel = BlackPixel( xwd->display, xwd->screen ); xwd->fgcolor.pixel = WhitePixel( xwd->display, xwd->screen ); if ( xwd->rw_cmap && pls->verbose ) fprintf( stderr, "Downgrading to r/o cmap.\n" ); xwd->rw_cmap = 0; return; } // Allocate as many colors as we can npixels = RWMAP_MAX_COLORS; for (;; ) { if ( XAllocColorCells( xwd->display, xwd->map, False, plane_masks, 0, pixels, (unsigned int) npixels ) ) break; npixels--; if ( npixels == 0 ) break; } // Find the color with pixel = xor of the bg color pixel. // If a match isn't found, the last pixel allocated is used. for ( i = 0; i < npixels - 1; i++ ) { if ( pixels[i] == ( ~xwd->cmap0[0].pixel & 0xFF ) ) break; } // Use this color cell for our foreground color. Then free the rest. xwd->fgcolor.pixel = pixels[i]; for ( j = 0; j < npixels; j++ ) { if ( j != i ) XFreeColors( xwd->display, xwd->map, &pixels[j], 1, 0 ); } } //-------------------------------------------------------------------------- // SetBGFG() // // Set background & foreground colors. Foreground over background should // have high contrast. //-------------------------------------------------------------------------- static void SetBGFG( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; PLColor fgcolor; unsigned int gslevbg, gslevfg; dbug_enter( "SetBGFG" ); // // Set background color. // // Background defaults to black on color screens, white on grayscale (many // grayscale monitors have poor contrast, and black-on-white looks better). // if ( !xwd->color ) { pls->cmap0[0].r = pls->cmap0[0].g = pls->cmap0[0].b = 0xFF; } gslevbg = (unsigned int) ( ( (long) pls->cmap0[0].r + (long) pls->cmap0[0].g + (long) pls->cmap0[0].b ) / 3 ); PLColor_to_XColor( &pls->cmap0[0], &xwd->cmap0[0] ); // // Set foreground color. // // Used for grayscale output, since otherwise the plots can become nearly // unreadable (i.e. if colors get mapped onto grayscale values). In this // case it becomes the grayscale level for all draws, and is taken to be // black if the background is light, and white if the background is dark. // Note that white/black allocations never fail. // if ( gslevbg > 0x7F ) gslevfg = 0; else gslevfg = 0xFF; fgcolor.r = fgcolor.g = fgcolor.b = (unsigned char) gslevfg; PLColor_to_XColor( &fgcolor, &xwd->fgcolor ); // Now store if ( xwd->rw_cmap && xwd->color ) { XStoreColor( xwd->display, xwd->map, &xwd->fgcolor ); XStoreColor( xwd->display, xwd->map, &xwd->cmap0[0] ); } else { XAllocColor( xwd->display, xwd->map, &xwd->cmap0[0] ); XAllocColor( xwd->display, xwd->map, &xwd->fgcolor ); } } //-------------------------------------------------------------------------- // InitColors() // // Does all color initialization. //-------------------------------------------------------------------------- static void InitColors( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; dbug_enter( "InitColors" ); // Allocate and initialize color maps. // Defer cmap1 allocation until it's actually used if ( xwd->color ) { if ( plplot_ccmap ) { AllocCustomMap( pls ); } else { AllocCmap0( pls ); } } } //-------------------------------------------------------------------------- // AllocCustomMap() // // Initializes custom color map and all the cruft that goes with it. // // Assuming all color X displays do 256 colors, the breakdown is as follows: // // CCMAP_XWM_COLORS // Number of low "pixel" values to copy. These are typically allocated // first, thus are in use by the window manager. I copy them to reduce // flicker. // // // RWMAP_CMAP1_COLORS // Color map 1 entries. There should be as many as practical available // for smooth shading. On the order of 50-100 is pretty reasonable. You // don't really need all 256, especially if all you're going to do is to // print it to postscript (which doesn't have any intrinsic limitation on // the number of colors). // // It's important to leave some extra colors unallocated for Tk. In // particular the palette tools require a fair amount. I recommend leaving // at least 40 or so free. //-------------------------------------------------------------------------- static void AllocCustomMap( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; XColor xwm_colors[RWMAP_MAX_COLORS]; int i, npixels; unsigned long plane_masks[1], pixels[RWMAP_MAX_COLORS]; dbug_enter( "AllocCustomMap" ); // Determine current default colors for ( i = 0; i < RWMAP_MAX_COLORS; i++ ) { xwm_colors[i].pixel = (unsigned long int) i; } XQueryColors( xwd->display, xwd->map, xwm_colors, RWMAP_MAX_COLORS ); // Allocate cmap0 colors in the default colormap. // The custom cmap0 colors are later stored at the same pixel values. // This is a really cool trick to reduce the flicker when changing colormaps. // AllocCmap0( pls ); XAllocColor( xwd->display, xwd->map, &xwd->fgcolor ); // Create new color map xwd->map = XCreateColormap( xwd->display, DefaultRootWindow( xwd->display ), xwd->visual, AllocNone ); // Now allocate all colors so we can fill the ones we want to copy npixels = RWMAP_MAX_COLORS; for (;; ) { if ( XAllocColorCells( xwd->display, xwd->map, False, plane_masks, 0, pixels, (unsigned int) npixels ) ) break; npixels--; if ( npixels == 0 ) plexit( "couldn't allocate any colors" ); } // Fill the low colors since those are in use by the window manager for ( i = 0; i < CCMAP_XWM_COLORS; i++ ) { XStoreColor( xwd->display, xwd->map, &xwm_colors[i] ); pixels[xwm_colors[i].pixel] = 0; } // Fill the ones we will use in cmap0 for ( i = 0; i < xwd->ncol0; i++ ) { XStoreColor( xwd->display, xwd->map, &xwd->cmap0[i] ); pixels[xwd->cmap0[i].pixel] = 0; } // Finally, if the colormap was saved by an external agent, see if there are // any differences from the current default map and save those! A very cool // (or sick, depending on how you look at it) trick to get over some X and // Tk limitations. // if ( sxwm_colors_set ) { for ( i = 0; i < RWMAP_MAX_COLORS; i++ ) { if ( ( xwm_colors[i].red != sxwm_colors[i].red ) || ( xwm_colors[i].green != sxwm_colors[i].green ) || ( xwm_colors[i].blue != sxwm_colors[i].blue ) ) { if ( pixels[i] != 0 ) { XStoreColor( xwd->display, xwd->map, &xwm_colors[i] ); pixels[i] = 0; } } } } // Now free the ones we're not interested in for ( i = 0; i < npixels; i++ ) { if ( pixels[i] != 0 ) XFreeColors( xwd->display, xwd->map, &pixels[i], 1, 0 ); } // Allocate colors in cmap 1 AllocCmap1( pls ); } //-------------------------------------------------------------------------- // AllocCmap0() // // Allocate & initialize cmap0 entries. //-------------------------------------------------------------------------- static void AllocCmap0( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; int i; dbug_enter( "AllocCmap0" ); // Free all previous colors. This should work for both rw & ro colormaps for ( i = 1; i < xwd->ncol0; i++ ) { unsigned long pixel = xwd->cmap0[i].pixel; XFreeColors( xwd->display, xwd->map, &pixel, 1, 0 ); } // If the number of colors increased, need to allocate enough space for them if ( pls->ncol0 > xwd->ncol0_alloc ) { xwd->ncol0_alloc = pls->ncol0; xwd->cmap0 = (XColor *) realloc( xwd->cmap0, (size_t) pls->ncol0 * sizeof ( XColor ) ); if ( xwd->cmap0 == 0 ) plexit( "couldn't allocate space for cmap0 colors" ); } if ( xwd->rw_cmap ) { int npixels; unsigned long plane_masks[1], pixels[RWMAP_MAX_COLORS]; // Allocate and assign colors in cmap 0 npixels = pls->ncol0 - 1; for (;; ) { if ( XAllocColorCells( xwd->display, xwd->map, False, plane_masks, 0, &pixels[1], (unsigned int) npixels ) ) break; npixels--; if ( npixels == 0 ) plexit( "couldn't allocate any colors" ); } xwd->ncol0 = npixels + 1; for ( i = 1; i < xwd->ncol0; i++ ) { xwd->cmap0[i].pixel = pixels[i]; } StoreCmap0( pls ); } else { if ( pls->verbose ) fprintf( stderr, "Attempting to allocate r/o colors in cmap0.\n" ); for ( i = 1; i < pls->ncol0; i++ ) { int r; XColor c; PLColor_to_XColor( &pls->cmap0[i], &c ); r = XAllocColor( xwd->display, xwd->map, &c ); if ( pls->verbose ) fprintf( stderr, "i=%d, r=%d, pixel=%d\n", i, r, (int) c.pixel ); if ( r ) { xwd->cmap0[i] = c; xwd->cmap0[i].pixel = c.pixel; // needed for deallocation } else { XColor screen_def, exact_def; if ( pls->verbose ) fprintf( stderr, "color alloc failed, trying by name: %s.\n", pls->cmap0[i].name ); // Hmm, didn't work, try another approach. r = XAllocNamedColor( xwd->display, xwd->map, pls->cmap0[i].name, &screen_def, &exact_def ); // xwd->cmap0[i] = screen_def; if ( r ) { if ( pls->verbose ) fprintf( stderr, "yes, got a color by name.\n" ); xwd->cmap0[i] = screen_def; xwd->cmap0[i].pixel = screen_def.pixel; } else { r = XAllocNamedColor( xwd->display, xwd->map, "white", &screen_def, &exact_def ); if ( r ) { xwd->cmap0[i] = screen_def; xwd->cmap0[i].pixel = screen_def.pixel; } else printf( "Can't find white?! Giving up...\n" ); } } } xwd->ncol0 = i; if ( pls->verbose ) fprintf( stderr, "Allocated %d colors in cmap0.\n", xwd->ncol0 ); } } //-------------------------------------------------------------------------- // AllocCmap1() // // Allocate & initialize cmap1 entries. //-------------------------------------------------------------------------- static void AllocCmap1( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; int i, j, npixels; unsigned long plane_masks[1], pixels[RWMAP_MAX_COLORS]; dbug_enter( "AllocCmap1" ); if ( xwd->rw_cmap ) { if ( pls->verbose ) fprintf( stderr, "Attempting to allocate r/w colors in cmap1.\n" ); // If using the default color map, must severely limit number of colors // otherwise TK won't have enough. npixels = MAX( 2, MIN( RWMAP_CMAP1_COLORS, pls->ncol1 ) ); for (;; ) { if ( XAllocColorCells( xwd->display, xwd->map, False, plane_masks, 0, pixels, (unsigned int) npixels ) ) break; npixels--; if ( npixels == 0 ) break; } if ( npixels < 2 ) { xwd->ncol1 = -1; fprintf( stderr, "Warning: unable to allocate sufficient colors in cmap1.\n" ); return; } xwd->ncol1 = npixels; if ( pls->verbose ) fprintf( stderr, "AllocCmap1 (xwin.c): Allocated %d colors in cmap1.\n", npixels ); // Allocate space if it hasn't been done yet if ( !xwd->cmap1 ) { xwd->ncol1_alloc = xwd->ncol1; xwd->cmap1 = (XColor *) calloc( (size_t) ( xwd->ncol1 ), sizeof ( XColor ) ); if ( !xwd->cmap1 ) plexit( "couldn't allocate space for cmap1 colors" ); } // Don't assign pixels sequentially, to avoid strange problems with xor // GC's. Skipping by 2 seems to do the job best. for ( j = i = 0; i < xwd->ncol1; i++ ) { while ( pixels[j] == 0 ) j++; xwd->cmap1[i].pixel = pixels[j]; pixels[j] = 0; j += 2; if ( j >= xwd->ncol1 ) j = 0; } StoreCmap1( pls ); } else { int r, ncolors; PLColor cmap1color; XColor xcol; if ( pls->verbose ) fprintf( stderr, "Attempting to allocate r/o colors in cmap1.\n" ); switch ( xwd->visual->class ) { case TrueColor: ncolors = TC_CMAP1_COLORS; break; default: ncolors = ROMAP_CMAP1_COLORS; } // Allocate space if it hasn't been done yet if ( !xwd->cmap1 ) { xwd->ncol1_alloc = ncolors; xwd->cmap1 = (XColor *) calloc( (size_t) ncolors, sizeof ( XColor ) ); if ( !xwd->cmap1 ) plexit( "couldn't allocate space for cmap1 colors" ); } for ( i = 0; i < ncolors; i++ ) { plcol_interp( pls, &cmap1color, i, ncolors ); PLColor_to_XColor( &cmap1color, &xcol ); r = XAllocColor( xwd->display, xwd->map, &xcol ); if ( pls->verbose ) fprintf( stderr, "i=%d, r=%d, pixel=%d\n", i, r, (int) xcol.pixel ); if ( r ) xwd->cmap1[i] = xcol; else break; } if ( i < ncolors ) { xwd->ncol1 = -1; fprintf( stderr, "Warning: unable to allocate sufficient colors in cmap1\n" ); return; } else { xwd->ncol1 = ncolors; if ( pls->verbose ) fprintf( stderr, "AllocCmap1 (xwin.c): Allocated %d colors in cmap1\n", ncolors ); } } } //-------------------------------------------------------------------------- // StoreCmap0() // // Stores cmap 0 entries in X-server colormap. //-------------------------------------------------------------------------- static void StoreCmap0( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; int i; if ( !xwd->color ) return; for ( i = 1; i < xwd->ncol0; i++ ) { PLColor_to_XColor( &pls->cmap0[i], &xwd->cmap0[i] ); if ( xwd->rw_cmap ) XStoreColor( xwd->display, xwd->map, &xwd->cmap0[i] ); else XAllocColor( xwd->display, xwd->map, &xwd->cmap0[i] ); } } //-------------------------------------------------------------------------- // StoreCmap1() // // Stores cmap 1 entries in X-server colormap. //-------------------------------------------------------------------------- static void StoreCmap1( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; PLColor cmap1color; int i; if ( !xwd->color ) return; for ( i = 0; i < xwd->ncol1; i++ ) { plcol_interp( pls, &cmap1color, i, xwd->ncol1 ); PLColor_to_XColor( &cmap1color, &xwd->cmap1[i] ); if ( xwd->rw_cmap ) XStoreColor( xwd->display, xwd->map, &xwd->cmap1[i] ); else XAllocColor( xwd->display, xwd->map, &xwd->cmap1[i] ); } } //-------------------------------------------------------------------------- // PLColor_to_XColor() // // Copies the supplied PLColor to an XColor, padding with bits as necessary // (a PLColor uses 8 bits for color storage, while an XColor uses 16 bits). // The argument types follow the same order as in the function name. //-------------------------------------------------------------------------- #define ToXColor( a ) ( ( ( 0xFF & ( a ) ) << 8 ) | ( a ) ) #define ToPLColor( a ) ( ( (U_LONG) a ) >> 8 ) static void PLColor_to_XColor( PLColor *plcolor, XColor *xcolor ) { xcolor->red = (short unsigned) ToXColor( plcolor->r ); xcolor->green = (short unsigned) ToXColor( plcolor->g ); xcolor->blue = (short unsigned) ToXColor( plcolor->b ); xcolor->flags = DoRed | DoGreen | DoBlue; } //-------------------------------------------------------------------------- // PLColor_from_XColor() // // Copies the supplied XColor to a PLColor, stripping off bits as // necessary. See the previous routine for more info. //-------------------------------------------------------------------------- static void PLColor_from_XColor( PLColor *plcolor, XColor *xcolor ) { plcolor->r = (unsigned char) ToPLColor( xcolor->red ); plcolor->g = (unsigned char) ToPLColor( xcolor->green ); plcolor->b = (unsigned char) ToPLColor( xcolor->blue ); } //-------------------------------------------------------------------------- // AreWeGrayscale(Display *display) // // Determines if we're using a monochrome or grayscale device. // gmf 11-8-91; Courtesy of Paul Martz of Evans and Sutherland. // Altered Andrew Ross 26-01-2004 to fix memory leak. //-------------------------------------------------------------------------- static int AreWeGrayscale( Display *display ) { #if defined ( __cplusplus ) || defined ( c_plusplus ) #define THING c_class #else #define THING class #endif XVisualInfo *visuals; int nitems, i, igray; // get a list of info on the visuals available visuals = XGetVisualInfo( display, 0, NULL, &nitems ); igray = 1; // check the list looking for non-monochrome visual classes for ( i = 0; i < nitems; i++ ) if ( ( visuals[i].THING != GrayScale ) && ( visuals[i].THING != StaticGray ) ) { igray = 0; break; } XFree( visuals ); // if igray = 1 only StaticGray and GrayScale classes available return igray; } #ifdef DUMMY //-------------------------------------------------------------------------- // SaveColormap() **** DUMMY, NOT USED ANYMORE *** // // Saves RGB components of given colormap. // Used in an ugly hack to get past some X11R5 and TK limitations. // This isn't guaranteed to work under all circumstances, but hopefully // in the future there will be a nicer way to accomplish the same thing. // // Note: I tried using XCopyColormapAndFree to do the same thing, but under // HPUX 9.01/VUE/X11R5 at least it doesn't preserve the previous read-only // color cell allocations made by Tk. Is this a bug? Have to look at the // source to find out. //-------------------------------------------------------------------------- static void SaveColormap( Display *display, Colormap colormap ) { int i; if ( !plplot_ccmap ) return; sxwm_colors_set = 1; for ( i = 0; i < RWMAP_MAX_COLORS; i++ ) { sxwm_colors[i].pixel = i; } XQueryColors( display, colormap, sxwm_colors, RWMAP_MAX_COLORS ); // // printf("\nAt startup, default colors are: \n\n"); // for (i = 0; i < RWMAP_MAX_COLORS; i++) { // printf(" i: %d, pixel: %d, r: %d, g: %d, b: %d\n", // i, sxwm_colors[i].pixel, // sxwm_colors[i].red, sxwm_colors[i].green, sxwm_colors[i].blue); // } // } #endif //-------------------------------------------------------------------------- // GetImageErrorHandler() // // Error handler used in XGetImage() to catch errors when pixmap or window // are not completely viewable. //-------------------------------------------------------------------------- static int GetImageErrorHandler( Display *display, XErrorEvent *error ) { if ( error->error_code != BadMatch ) { char buffer[256]; XGetErrorText( display, error->error_code, buffer, 256 ); fprintf( stderr, "xwin: Error in XGetImage: %s.\n", buffer ); } return 1; } //-------------------------------------------------------------------------- // DrawImage() // // Fill polygon described in points pls->dev_x[] and pls->dev_y[]. // Only solid color fill supported. //-------------------------------------------------------------------------- static void DrawImage( PLStream *pls ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; XImage *ximg = NULL; XColor curcolor; PLINT xmin, xmax, ymin, ymax, icol1; int ( *oldErrorHandler )( Display *, XErrorEvent * ); float mlr, mtb; float blt, brt, brb, blb; float left, right; int kx, ky; int nx, ny, ix, iy; int i, corners[4], r[4] = { 0, 0, 0, 0 }; struct { float x, y; } Ppts[4]; CheckForEvents( pls ); xmin = (PLINT) ( dev->xscale * pls->imclxmin ); xmax = (PLINT) ( dev->xscale * pls->imclxmax ); ymin = (PLINT) ( dev->yscale * pls->imclymin ); ymax = (PLINT) ( dev->yscale * pls->imclymax ); nx = pls->dev_nptsX; ny = pls->dev_nptsY; // XGetImage() call fails if either the pixmap or window is not fully viewable! oldErrorHandler = XSetErrorHandler( GetImageErrorHandler ); XFlush( xwd->display ); if ( dev->write_to_pixmap ) ximg = XGetImage( xwd->display, dev->pixmap, 0, 0, dev->width, dev->height, AllPlanes, ZPixmap ); if ( dev->write_to_window ) ximg = XGetImage( xwd->display, dev->window, 0, 0, dev->width, dev->height, AllPlanes, ZPixmap ); XSetErrorHandler( oldErrorHandler ); if ( ximg == NULL ) { plabort( "Can't get image, the window must be partly off-screen, move it to fit screen" ); return; } if ( xwd->ncol1 == 0 ) AllocCmap1( pls ); if ( xwd->ncol1 < 2 ) return; // translate array for rotation switch ( (int) ( pls->diorot - 4. * floor( pls->diorot / 4. ) ) ) { case 0: r[0] = 0; r[1] = 1; r[2] = 2; r[3] = 3; break; case 1: r[0] = 1; r[1] = 2; r[2] = 3; r[3] = 0; break; case 2: r[0] = 2; r[1] = 3; r[2] = 0; r[3] = 1; break; case 3: r[0] = 3; r[1] = 0; r[2] = 1; r[3] = 2; } // after rotation and coordinate translation, each fill // lozangue will have coordinates (Ppts), slopes (m...) // and y intercepts (b...): // // Ppts[3] // ** // mlr,blt * * mtb,brt // * * //Ppts[0]< > Ppts[2] // * * // mtb,blt * * mlr,brb // ** // Ppts[1] // // slope of left/right and top/bottom edges mlr = (float) ( ( dev->yscale * ( pls->dev_iy[1] - pls->dev_iy[0] ) ) / ( dev->xscale * ( pls->dev_ix[1] - pls->dev_ix[0] ) ) ); mtb = (float) ( ( dev->yscale * ( pls->dev_iy[ny] - pls->dev_iy[0] ) ) / ( dev->xscale * ( pls->dev_ix[ny] - pls->dev_ix[0] ) ) ); for ( ix = 0; ix < nx - 1; ix++ ) { for ( iy = 0; iy < ny - 1; iy++ ) { corners[0] = ix * ny + iy; // [ix][iy] corners[1] = ( ix + 1 ) * ny + iy; // [ix+1][iy] corners[2] = ( ix + 1 ) * ny + iy + 1; // [ix+1][iy+1] corners[3] = ix * ny + iy + 1; // [ix][iy+1] for ( i = 0; i < 4; i++ ) { Ppts[i].x = (float) ( dev->xscale * ( pls->dev_ix[corners[r[i]]] ) ); Ppts[i].y = (float) ( dev->yscale * ( pls->dev_iy[corners[r[i]]] ) ); } // if any corner is inside the draw area if ( Ppts[0].x >= xmin || Ppts[2].x <= xmax || Ppts[1].y >= ymin || Ppts[3].y <= ymax ) { Ppts[0].x = MAX( Ppts[0].x, (float) xmin ); Ppts[2].x = MIN( Ppts[2].x, (float) xmax ); Ppts[1].y = MAX( Ppts[1].y, (float) ymin ); Ppts[3].y = MIN( Ppts[3].y, (float) ymax ); // the Z array has size (nx-1)*(ny-1) icol1 = pls->dev_z[ix * ( ny - 1 ) + iy]; // only plot points within zmin/zmax range if ( icol1 < pls->dev_zmin || icol1 > pls->dev_zmax ) continue; icol1 = (PLINT) ( (float) icol1 / (float) USHRT_MAX * (float) ( xwd->ncol1 - 1 ) ); if ( xwd->color ) curcolor = xwd->cmap1[icol1]; else curcolor = xwd->fgcolor; // Fill square between current and next points. // If the fill area is a single dot, accelerate the fill. if ( ( fabs( Ppts[2].x - Ppts[0].x ) == 1 ) && ( fabs( Ppts[3].y - Ppts[1].y ) == 1 ) ) { XPutPixel( ximg, (int) Ppts[0].x, (int) dev->height - 1 - (int) Ppts[0].y, (unsigned long) curcolor.pixel ); // integer rotate, accelerate } else if ( pls->diorot == floor( pls->diorot ) ) { for ( ky = (int) Ppts[1].y; ky < (int) Ppts[3].y; ky++ ) for ( kx = (int) Ppts[0].x; kx < (int) Ppts[2].x; kx++ ) XPutPixel( ximg, kx, (int) dev->height - 1 - ky, (unsigned int) curcolor.pixel ); // lozangue, scanline fill it } else { // y interception point of left/right top/bottom edges blt = Ppts[0].y - mlr * Ppts[0].x; brb = Ppts[2].y - mlr * Ppts[2].x; brt = Ppts[2].y - mtb * Ppts[2].x; blb = Ppts[0].y - mtb * Ppts[0].x; for ( ky = (int) Ppts[1].y; ky < (int) Ppts[3].y; ky++ ) { left = MAX( ( ( (float) ky - blt ) / mlr ), ( ( (float) ky - blb ) / mtb ) ); right = MIN( ( ( (float) ky - brt ) / mtb ), ( ( (float) ky - brb ) / mlr ) ); for ( kx = (int) Ppts[0].x; kx < (int) Ppts[2].x; kx++ ) { if ( kx >= rint( left ) && kx <= rint( right ) ) { XPutPixel( ximg, kx, (int) dev->height - 1 - ky, (unsigned int) curcolor.pixel ); } } } } } } } if ( dev->write_to_pixmap ) XPutImage( xwd->display, dev->pixmap, dev->gc, ximg, 0, 0, 0, 0, dev->width, dev->height ); if ( dev->write_to_window ) XPutImage( xwd->display, dev->window, dev->gc, ximg, 0, 0, 0, 0, dev->width, dev->height ); XDestroyImage( ximg ); } static void imageops( PLStream *pls, PLINT *ptr ) { XwDev *dev = (XwDev *) pls->dev; XwDisplay *xwd = (XwDisplay *) dev->xwd; // TODO: store/revert to/from previous state switch ( *ptr ) { case ZEROW2D: dev->write_to_window = 0; break; case ONEW2D: dev->write_to_window = 1; break; case ZEROW2B: dev->write_to_pixmap = 0; break; case ONEW2B: XFlush( xwd->display ); dev->write_to_pixmap = 1; break; } } #else int pldummy_xwin() { return 0; } #endif // PLD_xwin plplot-5.13.0/drivers/xwin.driver_info.in000644 001752 001752 00000000041 13150160115 022130 0ustar00softwaresoftware000000 000000 xwin:X-Window (Xlib):1:xwin:5:xw plplot-5.13.0/drivers/tkwin.driver_info.in000644 001752 001752 00000000045 13150160115 022303 0ustar00softwaresoftware000000 000000 tkwin:New tk driver:1:tkwin:45:tkwin plplot-5.13.0/drivers/ntk.c000644 001752 001752 00000044575 13150160115 017272 0ustar00softwaresoftware000000 000000 // Experimental tk driver using a plain "wish" // // Copyright (C) 2001 Joao Cardoso // Copyright (C) 2004 Rafael Laboissiere // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // #include "plDevs.h" #ifdef PLD_ntk #include "plplotP.h" #include "drivers.h" #include "plevent.h" #include // Device info PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_ntk = "ntk:New tk driver:1:ntk:43:ntk\n"; void plD_dispatch_init_ntk( PLDispatchTable *pdt ); void plD_init_ntk( PLStream * ); void plD_line_ntk( PLStream *, short, short, short, short ); void plD_polyline_ntk( PLStream *, short *, short *, PLINT ); void plD_eop_ntk( PLStream * ); void plD_bop_ntk( PLStream * ); void plD_tidy_ntk( PLStream * ); void plD_state_ntk( PLStream *, PLINT ); void plD_esc_ntk( PLStream *, PLINT, void * ); void plD_dispatch_init_ntk( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "New Tk device"; pdt->pl_DevName = "ntk"; #endif pdt->pl_type = plDevType_Interactive; pdt->pl_seq = 43; pdt->pl_init = (plD_init_fp) plD_init_ntk; pdt->pl_line = (plD_line_fp) plD_line_ntk; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_ntk; pdt->pl_eop = (plD_eop_fp) plD_eop_ntk; pdt->pl_bop = (plD_bop_fp) plD_bop_ntk; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_ntk; pdt->pl_state = (plD_state_fp) plD_state_ntk; pdt->pl_esc = (plD_esc_fp) plD_esc_ntk; } // hardwired window size #define XPIXELS 600 #define YPIXELS 400 static PLFLT scale = 10.0; // Tk canvas units are in pixels, giving corse curves, fool plplot, and scale down when sending to tk static PLFLT ppm; // device pixels per mm static Tcl_Interp *interp = NULL; // tcl interpreter static Tk_Window mainw; // tk main window static char curcolor[80]; // current color in #rrggbb notation // 12000 is large enough to satisfy example 27 needs without // erroring out in plD_polyline_ntk. Quadruple that to be conservative. #define PLPLOT_NTK_CMD_SIZE 48000 static char cmd[PLPLOT_NTK_CMD_SIZE]; // buffer to build command to interp static int ccanv = 0; // current canvas number static char base[80]; // name of frame that contains the canvas static char dash[80]; // dash string, as * // line buffering #define NPTS 1000 static short xold = -1, yold = -1; // last point of last 2 points line static short xb[NPTS], yb[NPTS]; // buffer static int curpts = 0; // current number of points buffered static int local = 1; // "local" or "remote" interpreter static char rem_interp[80]; // name of remote interp // physical devices coordinates static PLINT xmin = 0; static PLINT xmax = XPIXELS; static PLINT ymin = 0; static PLINT ymax = YPIXELS; // locator static PLGraphicsIn gin; static void tk_cmd( const char *gcmd ) { static char scmd[PLPLOT_NTK_CMD_SIZE]; if ( local ) Tcl_Eval( interp, gcmd ); else { // the -async option makes it block, some times! but is *much* faster! // and was working OK till now :( // sprintf(scmd, "send -async %s {%s}", rem_interp, cmd); // sprintf( scmd, "send %s {%s}", rem_interp, gcmd ); // mess! make it more efficient if ( Tcl_Eval( interp, scmd ) != TCL_OK ) fprintf( stderr, "%s\n", Tcl_GetStringResult( interp ) ); } } static void create_canvas( PLStream *pls ) { int columnbreak; ccanv++; columnbreak = ( ccanv % 30 == 0 ); // create new canvas sprintf( cmd, "set ccanv %d; canvas $plf.f2.c$ccanv -width $xmax -height $ymax -background #%02x%02x%02x -xscrollcommand \"$hs set\" -yscrollcommand \"$vs set\" -scrollregion \"0 0 $xmax $ymax\"", ccanv, pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b ); tk_cmd( cmd ); // add new canvas to option menu sprintf( cmd, "$plf.f1.mb.menu add command -label \"Page $ccanv\" -columnbreak %d -command {\n" "set w $plf.f2.c%d;\n" "$hs configure -command \"$w xview\";\n" "$vs configure -command \"$w yview\";\n" "set dname \"Page %d\";\n" "pack forget $ocanvas;\n" "set ocanvas $plf.f2.c%d;\n" "pack $ocanvas -fill both -expand 1;\n" "scan [$w xview] \"%%f %%f\" i j;\n" "$hs set $i $j;\n" "scan [$w yview] \"%%f %%f\" i j;\n" "$vs set $i $j;}", columnbreak, ccanv, ccanv, ccanv ); tk_cmd( cmd ); sprintf( cmd, "set item(%d) 0", ccanv ); tk_cmd( cmd ); // Shif-B1, zooms in // FIXME inform the core lib of the zoom, see plframe.c around line 2818 sprintf( cmd, "bind $plf.f2.c$ccanv {\n" "set cc %d;\n" "incr item($cc); set tt $item($cc);\n" "if {$tt == 1} {\n" "incr scroll_use;\n" "pack $hs -side bottom -fill x;\n" "pack $vs -side right -fill y;\n" "pack forget %%W; pack %%W -fill both -expand 1}\n" "set zx($cc,$tt) %%x;\n" "set zy($cc,$tt) %%y;\n" "%%W scale all %%x %%y 1.6 1.6;\n" "%%W configure -scrollregion [%%W bbox all];\n" "}", ccanv ); tk_cmd( cmd ); // Shif-B3, zooms out sprintf( cmd, "bind $plf.f2.c$ccanv {\n" "set cc %d; set tt $item($cc);\n" "if {$tt != 0} {\n" "%%W scale all $zx($cc,$tt) $zy($cc,$tt) 0.625 0.625\n" "%%W configure -scrollregion [%%W bbox all];\n" "set item($cc) [expr $tt - 1]}\n" "if { $item($cc) == 0} {\n" "set scroll_use [expr $scroll_use - 1];\n" "if {$scroll_use == 0} {\n" "pack forget $plf.f2.hscroll $plf.f2.vscroll}\n" "%%W configure -scrollregion \"0 0 $xmax $ymax\"}}", ccanv ); tk_cmd( cmd ); // Shift-B2, resets sprintf( cmd, "bind $plf.f2.c$ccanv {\n" "set cc %d; set tt $item($cc); \n" "while {$tt != 0} {\n" "%%W scale all $zx($cc,$tt) $zy($cc,$tt) 0.625 0.625\n" "set tt [expr $tt - 1]};\n" "set item($cc) 0;\n" "%%W configure -scrollregion \"0 0 $xmax $ymax\";\n" "set scroll_use [expr $scroll_use - 1];\n" "if {$scroll_use == 0} {\n" "pack forget $plf.f2.hscroll $plf.f2.vscroll}}", ccanv ); tk_cmd( cmd ); // Control-B1-Motion, pan sprintf( cmd, "bind $plf.f2.c$ccanv \"$plf.f2.c%d scan mark %%x %%y\"", ccanv ); tk_cmd( cmd ); sprintf( cmd, "bind $plf.f2.c$ccanv \"$plf.f2.c%d scan dragto %%x %%y\"", ccanv ); tk_cmd( cmd ); // Control-B2, identify and (in the far future) edit object tk_cmd( "bind $plf.f2.c$ccanv {\n" "set xx [ expr [winfo pointerx .] - [winfo rootx %W]];\n" "set yy [ expr [winfo pointery .] - [winfo rooty %W]];\n" "set near [%W find closest $xx $yy];\n" "%W move $near 20 20;\n" "after 500 \"%W move $near -20 -20\"}" ); // change view to the new canvas by invoking the menu buttom sprintf( cmd, "$plf.f1.mb.menu invoke %d", ccanv - 1 ); tk_cmd( cmd ); } //-------------------------------------------------------------------------- // plD_init_ntk() // // Initialize device (terminal). //-------------------------------------------------------------------------- void plD_init_ntk( PLStream *pls ) { pls->dev_fill0 = 1; // Handle solid fills pls->dev_fill1 = 1; // Driver handles pattern fills pls->color = 1; // Is a color device pls->dev_dash = 1; // Handle dashed lines pls->plbuf_write = 1; // Use plot buffer strcpy( curcolor, "black" ); // default color by name, not #rrggbb if ( pls->server_name != NULL ) { local = 0; strcpy( rem_interp, pls->server_name ); } if ( pls->geometry != NULL ) sscanf( pls->geometry, "%dx%d", &xmax, &ymax ); // if ( pls->plwindow != NULL ) // strcpy( base, pls->plwindow ); // else strcpy( base, ".plf" ); // default frame containing the canvas interp = Tcl_CreateInterp(); if ( Tcl_Init( interp ) != TCL_OK ) plexit( "Unable to initialize Tcl." ); if ( Tk_Init( interp ) ) plexit( "Unable to initialize Tk." ); mainw = Tk_MainWindow( interp ); Tcl_Eval( interp, "rename exec {}" ); Tcl_Eval( interp, "tk appname PLplot_ntk" ); // give interpreter a name if ( !local ) { Tcl_Eval( interp, "wm withdraw ." ); sprintf( cmd, "send %s \"set client [tk appname]; wm deiconify .\"", rem_interp ); if ( Tcl_Eval( interp, cmd ) != TCL_OK ) { fprintf( stderr, "%s\n", Tcl_GetStringResult( interp ) ); plexit( "No such tk server." ); } } sprintf( cmd, "set scroll_use 0; set plf %s; set vs $plf.f2.vscroll; set hs $plf.f2.hscroll; set xmax %d; set ymax %d; set ocanvas .;", base, xmax, ymax ); tk_cmd( cmd ); tk_cmd( "catch \"frame $plf\"; pack $plf -fill both -expand 1" ); sprintf( cmd, "frame $plf.f1;\n" "frame $plf.f2 -width %d -height %d;\n" "pack $plf.f1 -fill x;\n" "pack $plf.f2 -fill both -expand 1", xmax, ymax ); tk_cmd( cmd ); tk_cmd( "scrollbar $plf.f2.hscroll -orient horiz;\n" "scrollbar $plf.f2.vscroll" ); tk_cmd( "menubutton $plf.f1.mb -text \"Page 1\" -textvariable dname -relief raised -indicatoron 1 -menu $plf.f1.mb.menu;\n" "menu $plf.f1.mb.menu -tearoff 0;\n" "pack $plf.f1.mb -side left" ); if ( local ) tk_cmd( "button $plf.f1.quit -text Quit -command exit;\n" "pack $plf.f1.quit -side right" ); else tk_cmd( "button $plf.f1.quit -text Quit -command {send -async $client exit;\n" "destroy $plf;\n" "wm withdraw .};\n" "pack $plf.f1.quit -side right" ); // FIXME: I just discovered that Tcl_Eval is slower than Tcl_EvalObj. Fix it global-wide, `man Tcl_Eval' // Set up device parameters Tcl_Eval( interp, "tk scaling" ); // pixels per mm ppm = (PLFLT) atof( Tcl_GetStringResult( interp ) ) / ( 25.4 / 72. ); plP_setpxl( ppm, ppm ); plP_setphy( xmin, (PLINT) ( xmax * scale ), ymin, (PLINT) ( ymax * scale ) ); tk_cmd( "update" ); } static void flushbuffer( PLStream *pls ) { if ( curpts ) { plD_polyline_ntk( pls, xb, yb, curpts ); // if (curpts != 2) fprintf(stderr,"%d ", curpts); xold = yold = -1; curpts = 0; } } void plD_line_ntk( PLStream *pls, short x1a, short y1a, short x2a, short y2a ) { if ( xold == x1a && yold == y1a ) { xold = xb[curpts] = x2a; yold = yb[curpts] = y2a; curpts++; } else { flushbuffer( pls ); xb[curpts] = x1a; yb[curpts] = y1a; curpts++; xold = xb[curpts] = x2a; yold = yb[curpts] = y2a; curpts++; } if ( curpts == NPTS ) { //fprintf( stderr, "\nflush: %d ", curpts ); flushbuffer( pls ); } } void plD_polyline_ntk( PLStream * PL_UNUSED( pls ), short *xa, short *ya, PLINT npts ) { PLINT i, j; // there must exist a way to code this using the tk C API j = sprintf( cmd, "$plf.f2.c%d create line ", ccanv ); for ( i = 0; i < npts; i++ ) { // To be completely safe, assume 5 characters to the left of the // decimal point ==> 2*(5+3) characters written per sprintf // call. if ( ( j + 16 ) > PLPLOT_NTK_CMD_SIZE ) plexit( "plD_polyline_ntk: too many x, y values to hold in static cmd array" ); j += sprintf( &cmd[j], "%.1f %.1f ", xa[i] / scale, ymax - ya[i] / scale ); } j += sprintf( &cmd[j], " -fill %s", curcolor ); if ( dash[0] == '-' ) j += sprintf( &cmd[j], " %s", dash ); tk_cmd( cmd ); } // an event loop has to be designed, getcursor() and waitforpage() are just experimental static void waitforpage( PLStream * PL_UNUSED( pls ) ) { int key = 0, st = 0; // why can't I bind to the canvas? or even any frame? //tk_cmd("bind . {set keypress %N; puts \"\n%k-%A-%K-%N\"}"); tk_cmd( "bind . {set keypress %N}" ); while ( ( key & 0xff ) != PLK_Return && ( key & 0xff ) != PLK_Linefeed && key != PLK_Next && key != 'Q' ) { while ( st != 1 ) { tk_cmd( "update" ); tk_cmd( "info exists keypress" ); sscanf( Tcl_GetStringResult( interp ), "%d", &st ); } tk_cmd( "set keypress" ); sscanf( Tcl_GetStringResult( interp ), "%d", &key ); //fprintf(stderr,"\n%d\n", key);fflush(stderr); tk_cmd( "unset keypress" ); st = 0; } tk_cmd( "bind . {};" ); } void plD_eop_ntk( PLStream *pls ) { flushbuffer( pls ); tk_cmd( "update" ); } void plD_bop_ntk( PLStream *pls ) { create_canvas( pls ); } void plD_tidy_ntk( PLStream *pls ) { if ( !pls->nopause ) waitforpage( pls ); tk_cmd( "destroy $plf; wm withdraw ." ); } void plD_state_ntk( PLStream *pls, PLINT op ) { switch ( op ) { case PLSTATE_COLOR0: case PLSTATE_COLOR1: flushbuffer( pls ); sprintf( curcolor, "#%02x%02x%02x", pls->curcolor.r, pls->curcolor.g, pls->curcolor.b ); break; } } static void getcursor( PLStream * PL_UNUSED( pls ), PLGraphicsIn *ptr ) { int st = 0; plGinInit( &gin ); if ( 0 ) { while ( st != 1 ) { tk_cmd( "update" ); tk_cmd( "winfo exists $plf.f2.c$ccanv" ); sscanf( Tcl_GetStringResult( interp ), "%d", &st ); } st = 0; // this give a "Segmentation fault", even after checking for the canvas! tk_cmd( "set ocursor [lindex [$plf.f2.c$ccanv configure -cursor] 4]" ); } tk_cmd( "$plf.f2.c$ccanv configure -cursor cross;\n" "bind $plf.f2.c$ccanv