/* $Id: plcore.c,v 1.14 2007/10/24 13:14:43 ajb Exp $ Central dispatch facility for PLplot. Also contains the PLplot main data structures, external access routines, and initialization calls. This stuff used to be in "dispatch.h", "dispatch.c", and "base.c". Copyright (C) 2004 Joao Cardoso Copyright (C) 2004, 2005 Rafael Laboissiere Copyright (C) 2004, 2006 Andrew Ross Copyright (C) 2004 Andrew Roach Copyright (C) 2005 Alan W. Irwin Copyright (C) 2005 Thomas J. Duck This file is part of PLplot. PLplot is free software; you can redistribute it and/or modify it under the terms of the GNU General Library 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 #include "plcore.h" #ifdef ENABLE_DYNDRIVERS #include #endif /*--------------------------------------------------------------------------*\ * Driver Interface * * These routines are the low-level interface to the driver -- all calls to * driver functions must pass through here. For implementing driver- * specific functions, the escape function is provided. The command stream * gets duplicated to the plot buffer here. * * All functions that result in graphics actually being plotted (rather than * just a change of state) are filtered as necessary before being passed on. * The default settings do not require any filtering, i.e. PLplot physical * coordinates are the same as the device physical coordinates (currently * this can't be changed anyway), and a global view equal to the entire page * is used. * * The reason one wants to put view-specific filtering here is that if * enabled, the plot buffer should receive the unfiltered data stream. This * allows a specific view to be used from an interactive device (e.g. TCL/TK * driver) but be restored to the full view at any time merely by * reprocessing the contents of the plot buffer. * * The metafile, on the other hand, *should* be affected by changes in the * view, since this is a crucial editing capability. It is recommended that * the initial metafile be created without a restricted global view, and * modification of the view done on a per-plot basis as desired during * subsequent processing. * \*--------------------------------------------------------------------------*/ enum {AT_BOP, DRAWING, AT_EOP}; /* Initialize device. */ /* The plot buffer must be called last. */ /* The following array of chars is used both here and in plsym.c for * translating the Greek characters from the #g escape sequences into * the Hershey and Unicode codings */ const char plP_greek_mnemonic[] = "ABGDEZYHIKLMNCOPRSTUFXQWabgdezyhiklmncoprstufxqw"; void plP_init(void) { plsc->page_status = AT_EOP; (*plsc->dispatch_table->pl_init) ((struct PLStream_struct *) plsc); if (plsc->plbuf_write) plbuf_init(plsc); } /* End of page */ /* The plot buffer must be called first. */ /* Ignore instruction if already at eop. */ void plP_eop(void) { int skip_driver_eop = 0; if (plsc->page_status == AT_EOP) return; plsc->page_status = AT_EOP; if (plsc->plbuf_write) plbuf_eop(plsc); /* Call user eop handler if present. */ if (plsc->eop_handler != NULL) (*plsc->eop_handler) (plsc->eop_data, &skip_driver_eop); if (!skip_driver_eop) (*plsc->dispatch_table->pl_eop) ((struct PLStream_struct *) plsc); } /* Set up new page. */ /* The plot buffer must be called last. */ /* Ignore if already at bop. */ /* It's not actually necessary to be AT_EOP here, so don't check for it. */ void plP_bop(void) { int skip_driver_bop = 0; plP_subpInit(); if (plsc->page_status == AT_BOP) return; plsc->page_status = AT_BOP; plsc->nplwin = 0; /* Call user bop handler if present. */ if (plsc->bop_handler != NULL) (*plsc->bop_handler) (plsc->bop_data, &skip_driver_bop); if (!skip_driver_bop) (*plsc->dispatch_table->pl_bop) ((struct PLStream_struct *) plsc); if (plsc->plbuf_write) plbuf_bop(plsc); } /* Tidy up device (flush buffers, close file, etc). */ void plP_tidy(void) { if (plsc->tidy) { (*plsc->tidy) (plsc->tidy_data); plsc->tidy = NULL; plsc->tidy_data = NULL; } (*plsc->dispatch_table->pl_tidy) ((struct PLStream_struct *) plsc); if (plsc->plbuf_write) { plbuf_tidy(plsc); } plsc->OutFile = NULL; } /* Change state. */ void plP_state(PLINT op) { if (plsc->plbuf_write) plbuf_state(plsc, op); (*plsc->dispatch_table->pl_state) ((struct PLStream_struct *) plsc, op); } /* Escape function, for driver-specific commands. */ void plP_esc(PLINT op, void *ptr) { PLINT clpxmi, clpxma, clpymi, clpyma; EscText* args; /* The plot buffer must be called first */ if(plsc->plbuf_write) plbuf_esc(plsc, op, ptr); /* Text coordinates must pass through the driver interface filter */ if(op==PLESC_HAS_TEXT && plsc->dev_unicode) { /* Apply the driver interface filter */ if (plsc->difilt) { args = (EscText*)ptr; difilt(&(args->x),&(args->y),1,&clpxmi,&clpxma,&clpymi,&clpyma); } } (*plsc->dispatch_table->pl_esc) ((struct PLStream_struct *) plsc, op, ptr); } /* Set up plot window parameters. */ /* The plot buffer must be called first */ /* Some drivers (metafile, Tk) need access to this data */ void plP_swin(PLWindow *plwin) { PLWindow *w; PLINT clpxmi, clpxma, clpymi, clpyma; /* Provide plot buffer with unfiltered window data */ if (plsc->plbuf_write) plbuf_esc(plsc, PLESC_SWIN, (void *) plwin); w = &plsc->plwin[plsc->nplwin++ % PL_MAXWINDOWS]; w->dxmi = plwin->dxmi; w->dxma = plwin->dxma; w->dymi = plwin->dymi; w->dyma = plwin->dyma; if (plsc->difilt) { xscl[0] = plP_dcpcx(w->dxmi); xscl[1] = plP_dcpcx(w->dxma); yscl[0] = plP_dcpcy(w->dymi); yscl[1] = plP_dcpcy(w->dyma); difilt(xscl, yscl, 2, &clpxmi, &clpxma, &clpymi, &clpyma); w->dxmi = plP_pcdcx(xscl[0]); w->dxma = plP_pcdcx(xscl[1]); w->dymi = plP_pcdcy(yscl[0]); w->dyma = plP_pcdcy(yscl[1]); } w->wxmi = plwin->wxmi; w->wxma = plwin->wxma; w->wymi = plwin->wymi; w->wyma = plwin->wyma; /* If the driver wants to process swin commands, call it now */ /* It must use the filtered data, which it can get from *plsc */ if (plsc->dev_swin) { (*plsc->dispatch_table->pl_esc) ( (struct PLStream_struct *) plsc, PLESC_SWIN, NULL ); } } /*--------------------------------------------------------------------------*\ * Drawing commands. \*--------------------------------------------------------------------------*/ /* Draw line between two points */ /* The plot buffer must be called first so it gets the unfiltered data */ void plP_line(short *x, short *y) { PLINT i, npts = 2, clpxmi, clpxma, clpymi, clpyma; plsc->page_status = DRAWING; if (plsc->plbuf_write) plbuf_line(plsc, x[0], y[0], x[1], y[1]); if (plsc->difilt) { for (i = 0; i < npts; i++) { xscl[i] = x[i]; yscl[i] = y[i]; } difilt(xscl, yscl, npts, &clpxmi, &clpxma, &clpymi, &clpyma); plP_pllclp(xscl, yscl, npts, clpxmi, clpxma, clpymi, clpyma, grline); } else { grline(x, y, npts); } } /* Draw polyline */ /* The plot buffer must be called first */ void plP_polyline(short *x, short *y, PLINT npts) { PLINT i, clpxmi, clpxma, clpymi, clpyma; plsc->page_status = DRAWING; if (plsc->plbuf_write) plbuf_polyline(plsc, x, y, npts); if (plsc->difilt) { for (i = 0; i < npts; i++) { xscl[i] = x[i]; yscl[i] = y[i]; } difilt(xscl, yscl, npts, &clpxmi, &clpxma, &clpymi, &clpyma); plP_pllclp(xscl, yscl, npts, clpxmi, clpxma, clpymi, clpyma, grpolyline); } else { grpolyline(x, y, npts); } } /* Fill polygon */ /* The plot buffer must be called first */ /* Here if the desired area fill capability isn't present, we mock up */ /* something in software */ static int foo; void plP_fill(short *x, short *y, PLINT npts) { PLINT i, clpxmi, clpxma, clpymi, clpyma; plsc->page_status = DRAWING; if (plsc->plbuf_write) { plsc->dev_npts = npts; plsc->dev_x = x; plsc->dev_y = y; plbuf_esc(plsc, PLESC_FILL, NULL); } /* Account for driver ability to do fills */ if (plsc->patt == 0 && ! plsc->dev_fill0) { if ( ! foo) { plwarn("Driver does not support hardware solid fills, switching to software fill.\n"); foo = 1; } plsc->patt = 8; plpsty(plsc->patt); } if (plsc->dev_fill1) { plsc->patt = -ABS(plsc->patt); } /* Perform fill. Here we MUST NOT allow the software fill to pass through the driver interface filtering twice, else we get the infamous 2*rotation for software fills on orientation swaps. */ if (plsc->patt > 0) plfill_soft(x, y, npts); else { if (plsc->difilt) { for (i = 0; i < npts; i++) { xscl[i] = x[i]; yscl[i] = y[i]; } difilt(xscl, yscl, npts, &clpxmi, &clpxma, &clpymi, &clpyma); plP_plfclp(xscl, yscl, npts, clpxmi, clpxma, clpymi, clpyma, grfill); } else { grfill(x, y, npts); } } } /* Account for driver ability to draw text itself */ /* #define DEBUG_TEXT */ #define hex2dec( a ) isdigit(a) ? a - 48 : (toupper(a) - 65) + 10 int text2num( const char *text, char end, PLUNICODE *num); int text2fci( const char *text, unsigned char *hexdigit, unsigned char *hexpower); /*--------------------------------------------------------------------------*\ * int text2num( char *text, char end, PLUNICODE *num) * char *text - pointer to the text to be parsed * char end - end character (i.e. ')' or ']' to stop parsing * PLUNICODE *num - pointer to an PLUNICODE to store the value * * Function takes a string, which can be either hex or decimal, * and converts it into an PLUNICODE, stopping at either a null, * or the character pointed to by 'end'. It is a bit brain-dead, * and probably should make more checks, but it works. \*--------------------------------------------------------------------------*/ int text2num( const char *text, char end, PLUNICODE *num) { int base=10; unsigned short i=0; *num=0; if (text[1]=='x') { base=16; i=2; } while ((text[i]!=end)&&(text[i]!=0)) { *num*=base; *num+=hex2dec(text[i]); i++; } return(i); } /*--------------------------------------------------------------------------*\ * int text2fci( char *text, unsigned char *hexdigit, unsigned char *hexpower) * char *text - pointer to the text to be parsed * unsigned char *hexdigit - pointer to hex value that is stored. * unsigned char *hexpower - pointer to hex power (left shift) that is stored. * * Function takes a pointer to a string, which is looked up in a table * to determine the corresponding FCI (font characterization integer) * hex digit value and hex power (left shift). All matched strings * start with "<" and end with the two characters "/>". * If the lookup succeeds, hexdigit and hexpower are set to the appropriate * values in the table, and the function returns the number of characters * in text that are consumed by the matching string in the table lookup. * * If the lookup fails, hexdigit is set to 0, hexpower is set to and * impossible value, and the function returns 0. \*--------------------------------------------------------------------------*/ int text2fci( const char *text, unsigned char *hexdigit, unsigned char *hexpower) { typedef struct { const char *ptext; /* pmr: const */ unsigned char hexdigit; unsigned char hexpower; unsigned char padding[6]; /* pmr: padding to align */ } TextLookupTable; /* This defines the various font control commands and the corresponding * hexdigit and hexpower in the FCI. */ #define N_TextLookupTable 10 const TextLookupTable lookup[N_TextLookupTable] = { {"", PL_FCI_SANS, PL_FCI_FAMILY, " "}, {"", PL_FCI_SERIF, PL_FCI_FAMILY, " "}, {"", PL_FCI_MONO, PL_FCI_FAMILY, " "}, {"