PLplot  5.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
plcore.c
Go to the documentation of this file.
1 // $Id: plcore.c 12951 2014-01-22 22:55:21Z airwin $
2 //
3 // Central dispatch facility for PLplot.
4 // Also contains the PLplot main data structures, external access
5 // routines, and initialization calls.
6 //
7 // This stuff used to be in "dispatch.h", "dispatch.c", and "base.c".
8 //
9 //
10 // Copyright (C) 2004 Joao Cardoso
11 // Copyright (C) 2004, 2005 Rafael Laboissiere
12 // Copyright (C) 2004, 2006 Andrew Ross
13 // Copyright (C) 2004 Andrew Roach
14 // Copyright (C) 2005-2013 Alan W. Irwin
15 // Copyright (C) 2005 Thomas J. Duck
16 //
17 // This file is part of PLplot.
18 //
19 // PLplot is free software; you can redistribute it and/or modify
20 // it under the terms of the GNU Library General Public License as published
21 // by the Free Software Foundation; either version 2 of the License, or
22 // (at your option) any later version.
23 //
24 // PLplot is distributed in the hope that it will be useful,
25 // but WITHOUT ANY WARRANTY; without even the implied warranty of
26 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 // GNU Library General Public License for more details.
28 //
29 // You should have received a copy of the GNU Library General Public License
30 // along with PLplot; if not, write to the Free Software
31 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
32 //
33 //
34 
35 #define DEBUG
36 #define NEED_PLDEBUG
37 #include "plcore.h"
38 
39 #ifdef ENABLE_DYNDRIVERS
40  #ifndef LTDL_WIN32
41  #include <ltdl.h>
42  #else
43  #include "ltdl_win32.h"
44  #endif
45 #endif
46 
47 #if HAVE_DIRENT_H
48 // The following conditional is a workaround for a bug in the MacOSX system.
49 // When the dirent.h file will be fixed upstream by Apple Inc, this should
50 // go away.
51 # ifdef NEED_SYS_TYPE_H
52 # include <sys/types.h>
53 # endif
54 # include <dirent.h>
55 # define NAMLEN( dirent ) strlen( ( dirent )->d_name )
56 #else
57 # if defined ( _MSC_VER )
58 # include "dirent_msvc.h"
59 # else
60 # define dirent direct
61 # define NAMLEN( dirent ) ( dirent )->d_namlen
62 # if HAVE_SYS_NDIR_H
63 # include <sys/ndir.h>
64 # endif
65 # if HAVE_SYS_DIR_H
66 # include <sys/dir.h>
67 # endif
68 # if HAVE_NDIR_H
69 # include <ndir.h>
70 # endif
71 # endif
72 #endif
73 
74 // AM: getcwd has a somewhat strange status on Windows, its proper
75 // name is _getcwd, this is a problem in the case of DLLs, like with
76 // the Java bindings. The functions _getcwd() and chdir() are
77 // declared in direct.h for Visual C++. Since chdir() is deprecated
78 // (but still available) in Visual C++ we redefine chdir to _chdir.
79 //
80 #if defined ( _MSC_VER )
81 # include <direct.h>
82 # define getcwd _getcwd
83 # define chdir _chdir
84 #endif
85 
86 #define BUFFER_SIZE 80
87 #define BUFFER2_SIZE 300
88 #define DRVSPEC_SIZE 400
89 
90 #include <errno.h>
91 
92 int
93 text2num( const char *text, char end, PLUNICODE *num );
94 
95 int
96 text2fci( const char *text, unsigned char *hexdigit, unsigned char *hexpower );
97 
98 //--------------------------------------------------------------------------
99 // Driver Interface
100 //
101 // These routines are the low-level interface to the driver -- all calls to
102 // driver functions must pass through here. For implementing driver-
103 // specific functions, the escape function is provided. The command stream
104 // gets duplicated to the plot buffer here.
105 //
106 // All functions that result in graphics actually being plotted (rather than
107 // just a change of state) are filtered as necessary before being passed on.
108 // The default settings do not require any filtering, i.e. PLplot physical
109 // coordinates are the same as the device physical coordinates (currently
110 // this can't be changed anyway), and a global view equal to the entire page
111 // is used.
112 //
113 // The reason one wants to put view-specific filtering here is that if
114 // enabled, the plot buffer should receive the unfiltered data stream. This
115 // allows a specific view to be used from an interactive device (e.g. TCL/TK
116 // driver) but be restored to the full view at any time merely by
117 // reprocessing the contents of the plot buffer.
118 //
119 // The metafile, on the other hand, *should* be affected by changes in the
120 // view, since this is a crucial editing capability. It is recommended that
121 // the initial metafile be created without a restricted global view, and
122 // modification of the view done on a per-plot basis as desired during
123 // subsequent processing.
124 //
125 //--------------------------------------------------------------------------
126 
127 enum { AT_BOP, DRAWING, AT_EOP };
128 
129 // Initialize device.
130 // The plot buffer must be called last.
131 
132 // The following array of chars is used both here and in plsym.c for
133 // translating the Greek characters from the #g escape sequences into
134 // the Hershey and Unicode codings
135 //
136 const char plP_greek_mnemonic[] = "ABGDEZYHIKLMNCOPRSTUFXQWabgdezyhiklmncoprstufxqw";
137 
138 void
139 plP_init( void )
140 {
141  char * save_locale;
142  plsc->page_status = AT_EOP;
143  plsc->stream_closed = FALSE;
144 
145  save_locale = plsave_set_locale();
146  ( *plsc->dispatch_table->pl_init )( (struct PLStream_struct *) plsc );
147  plrestore_locale( save_locale );
148 
149  if ( plsc->plbuf_write )
150  plbuf_init( plsc );
151 }
152 
153 // End of page
154 // The plot buffer must be called first.
155 // Ignore instruction if already at eop.
156 
157 void
158 plP_eop( void )
159 {
160  int skip_driver_eop = 0;
161 
162  if ( plsc->page_status == AT_EOP )
163  return;
164 
165  plsc->page_status = AT_EOP;
166 
167  if ( plsc->plbuf_write )
168  plbuf_eop( plsc );
169 
170 // Call user eop handler if present.
171 
172  if ( plsc->eop_handler != NULL )
173  ( *plsc->eop_handler )( plsc->eop_data, &skip_driver_eop );
174 
175  if ( !skip_driver_eop )
176  {
177  char *save_locale = plsave_set_locale();
178  if ( !plsc->stream_closed )
179  {
180  ( *plsc->dispatch_table->pl_eop )( (struct PLStream_struct *) plsc );
181  }
182  plrestore_locale( save_locale );
183  }
184 }
185 
186 // Set up new page.
187 // The plot buffer must be called last.
188 // Ignore if already at bop.
189 // It's not actually necessary to be AT_EOP here, so don't check for it.
190 
191 void
192 plP_bop( void )
193 {
194  int skip_driver_bop = 0;
195 
196  plP_subpInit();
197  if ( plsc->page_status == AT_BOP )
198  return;
199 
200  plsc->page_status = AT_BOP;
201  plsc->nplwin = 0;
202 
203 // Call user bop handler if present.
204 
205  if ( plsc->bop_handler != NULL )
206  ( *plsc->bop_handler )( plsc->bop_data, &skip_driver_bop );
207 
208  if ( !skip_driver_bop )
209  {
210  char *save_locale = plsave_set_locale();
211  if ( !plsc->stream_closed )
212  {
213  ( *plsc->dispatch_table->pl_bop )( (struct PLStream_struct *) plsc );
214  }
215  plrestore_locale( save_locale );
216  }
217 
218  if ( plsc->plbuf_write )
219  plbuf_bop( plsc );
220 }
221 
222 // Tidy up device (flush buffers, close file, etc).
223 
224 void
225 plP_tidy( void )
226 {
227  char * save_locale;
228  if ( plsc->tidy )
229  {
230  ( *plsc->tidy )( plsc->tidy_data );
231  plsc->tidy = NULL;
232  plsc->tidy_data = NULL;
233  }
234 
235  save_locale = plsave_set_locale();
236  if ( !plsc->stream_closed )
237  {
238  ( *plsc->dispatch_table->pl_tidy )( (struct PLStream_struct *) plsc );
239  }
240  plrestore_locale( save_locale );
241 
242  if ( plsc->plbuf_write )
243  {
244  plbuf_tidy( plsc );
245  }
246 
247  plsc->OutFile = NULL;
248 }
249 
250 // Change state.
251 
252 void
254 {
255  char * save_locale;
256  if ( plsc->plbuf_write )
257  plbuf_state( plsc, op );
258 
259  save_locale = plsave_set_locale();
260  if ( !plsc->stream_closed )
261  {
262  ( *plsc->dispatch_table->pl_state )( (struct PLStream_struct *) plsc, op );
263  }
264  plrestore_locale( save_locale );
265 }
266 
267 // Escape function, for driver-specific commands.
268 
269 void
270 plP_esc( PLINT op, void *ptr )
271 {
272  char * save_locale;
273  PLINT clpxmi, clpxma, clpymi, clpyma;
274  EscText* args;
275 
276  // The plot buffer must be called first
277  if ( plsc->plbuf_write )
278  plbuf_esc( plsc, op, ptr );
279 
280  // Text coordinates must pass through the driver interface filter
281  if ( ( op == PLESC_HAS_TEXT && plsc->dev_unicode ) ||
282  ( op == PLESC_END_TEXT && plsc->alt_unicode ) )
283  {
284  // Apply the driver interface filter
285  if ( plsc->difilt )
286  {
287  args = (EscText *) ptr;
288  difilt( &( args->x ), &( args->y ), 1, &clpxmi, &clpxma, &clpymi, &clpyma );
289  }
290  }
291 
292  save_locale = plsave_set_locale();
293  if ( !plsc->stream_closed )
294  {
295  ( *plsc->dispatch_table->pl_esc )( (struct PLStream_struct *) plsc, op, ptr );
296  }
297  plrestore_locale( save_locale );
298 }
299 
300 // Set up plot window parameters.
301 // The plot buffer must be called first
302 // Some drivers (metafile, Tk) need access to this data
303 
304 void
306 {
307  PLWindow *w;
308  PLINT clpxmi, clpxma, clpymi, clpyma;
309 
310 // Provide plot buffer with unfiltered window data
311 
312  if ( plsc->plbuf_write )
313  plbuf_esc( plsc, PLESC_SWIN, (void *) plwin );
314 
315  w = &plsc->plwin[plsc->nplwin++ % PL_MAXWINDOWS];
316 
317  w->dxmi = plwin->dxmi;
318  w->dxma = plwin->dxma;
319  w->dymi = plwin->dymi;
320  w->dyma = plwin->dyma;
321 
322  if ( plsc->difilt )
323  {
324  xscl[0] = plP_dcpcx( w->dxmi );
325  xscl[1] = plP_dcpcx( w->dxma );
326  yscl[0] = plP_dcpcy( w->dymi );
327  yscl[1] = plP_dcpcy( w->dyma );
328 
329  difilt( xscl, yscl, 2, &clpxmi, &clpxma, &clpymi, &clpyma );
330 
331  w->dxmi = plP_pcdcx( xscl[0] );
332  w->dxma = plP_pcdcx( xscl[1] );
333  w->dymi = plP_pcdcy( yscl[0] );
334  w->dyma = plP_pcdcy( yscl[1] );
335  }
336 
337  w->wxmi = plwin->wxmi;
338  w->wxma = plwin->wxma;
339  w->wymi = plwin->wymi;
340  w->wyma = plwin->wyma;
341 
342 // If the driver wants to process swin commands, call it now
343 // It must use the filtered data, which it can get from *plsc
344 
345  if ( plsc->dev_swin )
346  {
347  char *save_locale = plsave_set_locale();
348  if ( !plsc->stream_closed )
349  {
350  ( *plsc->dispatch_table->pl_esc )( (struct PLStream_struct *) plsc,
351  PLESC_SWIN, NULL );
352  }
353  plrestore_locale( save_locale );
354  }
355 }
356 
357 //--------------------------------------------------------------------------
358 // Drawing commands.
359 //--------------------------------------------------------------------------
360 
361 // Draw line between two points
362 // The plot buffer must be called first so it gets the unfiltered data
363 
364 void
365 plP_line( short *x, short *y )
366 {
367  PLINT i, npts = 2, clpxmi, clpxma, clpymi, clpyma;
368 
369  plsc->page_status = DRAWING;
370 
371  if ( plsc->plbuf_write )
372  plbuf_line( plsc, x[0], y[0], x[1], y[1] );
373 
374  if ( plsc->difilt )
375  {
376  for ( i = 0; i < npts; i++ )
377  {
378  xscl[i] = x[i];
379  yscl[i] = y[i];
380  }
381  difilt( xscl, yscl, npts, &clpxmi, &clpxma, &clpymi, &clpyma );
382  plP_pllclp( xscl, yscl, npts, clpxmi, clpxma, clpymi, clpyma, grline );
383  }
384  else
385  {
386  grline( x, y, npts );
387  }
388 }
389 
390 // Draw polyline
391 // The plot buffer must be called first
392 
393 void
394 plP_polyline( short *x, short *y, PLINT npts )
395 {
396  PLINT i, clpxmi, clpxma, clpymi, clpyma;
397 
398  plsc->page_status = DRAWING;
399 
400  if ( plsc->plbuf_write )
401  plbuf_polyline( plsc, x, y, npts );
402 
403  if ( plsc->difilt )
404  {
405  for ( i = 0; i < npts; i++ )
406  {
407  xscl[i] = x[i];
408  yscl[i] = y[i];
409  }
410  difilt( xscl, yscl, npts, &clpxmi, &clpxma, &clpymi, &clpyma );
411  plP_pllclp( xscl, yscl, npts, clpxmi, clpxma, clpymi, clpyma,
412  grpolyline );
413  }
414  else
415  {
416  grpolyline( x, y, npts );
417  }
418 }
419 
420 // Fill polygon
421 // The plot buffer must be called first
422 // Here if the desired area fill capability isn't present, we mock up
423 // something in software
424 
425 static int foo;
426 
427 void
428 plP_fill( short *x, short *y, PLINT npts )
429 {
430  PLINT i, clpxmi, clpxma, clpymi, clpyma;
431 
432  plsc->page_status = DRAWING;
433 
434  if ( plsc->plbuf_write )
435  {
436  plsc->dev_npts = npts;
437  plsc->dev_x = x;
438  plsc->dev_y = y;
439  plbuf_esc( plsc, PLESC_FILL, NULL );
440  }
441 
442 // Account for driver ability to do fills
443 
444  if ( plsc->patt == 0 && !plsc->dev_fill0 )
445  {
446  if ( !foo )
447  {
448  plwarn( "Driver does not support hardware solid fills, switching to software fill.\n" );
449  foo = 1;
450  }
451  plsc->patt = 8;
452  plpsty( plsc->patt );
453  }
454  if ( plsc->dev_fill1 )
455  {
456  plsc->patt = -ABS( plsc->patt );
457  }
458 
459 // Perform fill. Here we MUST NOT allow the software fill to pass through the
460 // driver interface filtering twice, else we get the infamous 2*rotation for
461 // software fills on orientation swaps.
462 //
463 
464  if ( plsc->patt > 0 )
465  plfill_soft( x, y, npts );
466 
467  else
468  {
469  if ( plsc->difilt )
470  {
471  for ( i = 0; i < npts; i++ )
472  {
473  xscl[i] = x[i];
474  yscl[i] = y[i];
475  }
476  difilt( xscl, yscl, npts, &clpxmi, &clpxma, &clpymi, &clpyma );
477  plP_plfclp( xscl, yscl, npts, clpxmi, clpxma, clpymi, clpyma,
478  grfill );
479  }
480  else
481  {
482  grfill( x, y, npts );
483  }
484  }
485 }
486 
487 // Render a gradient
488 // The plot buffer must be called first
489 // N.B. plP_gradient is never called (see plgradient) unless the
490 // device driver has set plsc->dev_gradient to true.
491 
492 void
493 plP_gradient( short *x, short *y, PLINT npts )
494 {
495  PLINT i, clpxmi, clpxma, clpymi, clpyma;
496 
497  plsc->page_status = DRAWING;
498 
499  if ( plsc->plbuf_write )
500  {
501  plsc->dev_npts = npts;
502  plsc->dev_x = x;
503  plsc->dev_y = y;
504  plbuf_esc( plsc, PLESC_GRADIENT, NULL );
505  }
506 
507  // Render gradient with driver.
508  if ( plsc->difilt )
509  {
510  for ( i = 0; i < npts; i++ )
511  {
512  xscl[i] = x[i];
513  yscl[i] = y[i];
514  }
515  difilt( xscl, yscl, npts, &clpxmi, &clpxma, &clpymi, &clpyma );
516  plP_plfclp( xscl, yscl, npts, clpxmi, clpxma, clpymi, clpyma,
517  grgradient );
518  }
519  else
520  {
521  grgradient( x, y, npts );
522  }
523 }
524 
525 // Account for driver ability to draw text itself
526 //
527 // #define DEBUG_TEXT
528 //
529 
530 //--------------------------------------------------------------------------
531 // int text2num( char *text, char end, PLUNICODE *num)
532 // char *text - pointer to the text to be parsed
533 // char end - end character (i.e. ')' or ']' to stop parsing
534 // PLUNICODE *num - pointer to an PLUNICODE to store the value
535 //
536 // Function takes a string, which can be either hex or decimal,
537 // and converts it into an PLUNICODE, stopping at either a null,
538 // or the character pointed to by 'end'. This implementation using
539 // the C library strtoul replaces the original brain-dead version
540 // and should be more robust to invalid control strings.
541 //--------------------------------------------------------------------------
542 
543 int text2num( const char *text, char end, PLUNICODE *num )
544 {
545  char *endptr;
546  char msgbuf[BUFFER_SIZE];
547 
548  *num = (PLUNICODE) strtoul( text, &endptr, 0 );
549 
550  if ( end != endptr[0] )
551  {
552  snprintf( msgbuf, BUFFER_SIZE, "text2num: invalid control string detected - %c expected", end );
553  plwarn( msgbuf );
554  }
555 
556  return (int) ( endptr - text );
557 }
558 
559 //--------------------------------------------------------------------------
560 // int text2fci( char *text, unsigned char *hexdigit, unsigned char *hexpower)
561 // char *text - pointer to the text to be parsed
562 // unsigned char *hexdigit - pointer to hex value that is stored.
563 // unsigned char *hexpower - pointer to hex power (left shift) that is stored.
564 //
565 // Function takes a pointer to a string, which is looked up in a table
566 // to determine the corresponding FCI (font characterization integer)
567 // hex digit value and hex power (left shift). All matched strings
568 // start with "<" and end with the two characters "/>".
569 // If the lookup succeeds, hexdigit and hexpower are set to the appropriate
570 // values in the table, and the function returns the number of characters
571 // in text that are consumed by the matching string in the table lookup.
572 //
573 // If the lookup fails, hexdigit is set to 0, hexpower is set to and
574 // impossible value, and the function returns 0.
575 //--------------------------------------------------------------------------
576 
577 int text2fci( const char *text, unsigned char *hexdigit, unsigned char *hexpower )
578 {
579  typedef struct
580  {
581  const char *ptext;
582  unsigned char hexdigit;
583  unsigned char hexpower;
584  }
585  TextLookupTable;
586  // This defines the various font control commands and the corresponding
587  // hexdigit and hexpower in the FCI.
588  //
589 #define N_TextLookupTable 10
590  const TextLookupTable lookup[N_TextLookupTable] = {
591  { "<sans-serif/>", PL_FCI_SANS, PL_FCI_FAMILY },
592  { "<serif/>", PL_FCI_SERIF, PL_FCI_FAMILY },
593  { "<monospace/>", PL_FCI_MONO, PL_FCI_FAMILY },
594  { "<script/>", PL_FCI_SCRIPT, PL_FCI_FAMILY },
595  { "<symbol/>", PL_FCI_SYMBOL, PL_FCI_FAMILY },
596  { "<upright/>", PL_FCI_UPRIGHT, PL_FCI_STYLE },
597  { "<italic/>", PL_FCI_ITALIC, PL_FCI_STYLE },
598  { "<oblique/>", PL_FCI_OBLIQUE, PL_FCI_STYLE },
599  { "<medium/>", PL_FCI_MEDIUM, PL_FCI_WEIGHT },
600  { "<bold/>", PL_FCI_BOLD, PL_FCI_WEIGHT }
601  };
602  int i, length;
603  for ( i = 0; i < N_TextLookupTable; i++ )
604  {
605  length = (int) strlen( lookup[i].ptext );
606  if ( !strncmp( text, lookup[i].ptext, (size_t) length ) )
607  {
608  *hexdigit = lookup[i].hexdigit;
609  *hexpower = lookup[i].hexpower;
610  return ( length );
611  }
612  }
613  *hexdigit = 0;
614  *hexpower = PL_FCI_HEXPOWER_IMPOSSIBLE;
615  return ( 0 );
616 }
617 
619 
620 void
621 plP_text( PLINT base, PLFLT just, PLFLT *xform, PLINT x, PLINT y,
622  PLINT refx, PLINT refy, const char *string )
623 {
624  if ( plsc->dev_text ) // Does the device render it's own text ?
625  {
626  EscText args;
627  short len = 0;
628  char skip;
629  int i, j;
630  PLUNICODE code;
631  char esc;
632  int idx = -1;
633 
634  args.base = base;
635  args.just = just;
636  args.xform = xform;
637  args.x = x;
638  args.y = y;
639  args.refx = refx;
640  args.refy = refy;
641  args.string = string;
642 
643  if ( plsc->dev_unicode ) // Does the device also understand unicode?
644  {
645  PLINT ig;
646  PLUNICODE fci;
647  PLUNICODE orig_fci;
648  unsigned char hexdigit, hexpower;
649 
650  // Now process the text string
651 
652  if ( string != NULL ) // If the string isn't blank, then we will
653  // continue
654  //
655 
656  {
657  len = (short) strlen( string ); // this length is only used in the loop
658  // counter, we will work out the length of
659  // the unicode string as we go
660  plgesc( &esc );
661 
662  // At this stage we will do some translations into unicode, like
663  // conversion to Greek , and will save other translations such as
664  // superscript for the driver to do later on. As we move through
665  // the string and do the translations, we will get
666  // rid of the esc character sequence, just replacing it with
667  // unicode.
668  //
669 
670  // Obtain FCI (font characterization integer) for start of
671  // string.
672  plgfci( &fci );
673  orig_fci = fci;
674 
675  // Walk through the string, and convert
676  // some stuff to unicode on the fly
677  for ( j = i = 0; i < len; i++ )
678  {
679  skip = 0;
680 
681  if ( string[i] == esc )
682  {
683  switch ( string[i + 1] )
684  {
685  case '(': // hershey code
686  i += ( 2 + text2num( &string[i + 2], ')', &code ) );
687  idx = plhershey2unicode( (int) code );
688  unicode_buffer[j++] = \
690 
691 
692  // if unicode_buffer[j-1] corresponds to the escape
693  // character must unescape it by appending one more.
694  // This will probably always be necessary since it is
695  // likely unicode_buffer will always have to contain
696  // escape characters that are interpreted by the device
697  // driver.
698  //
699  if ( unicode_buffer[j - 1] == (PLUNICODE) esc )
700  unicode_buffer[j++] = (PLUNICODE) esc;
701  j--;
702  skip = 1;
703  break;
704 
705  case '[': // unicode
706  i += ( 2 + text2num( &string[i + 2], ']', &code ) );
707  unicode_buffer[j++] = code;
708 
709 
710  // if unicode_buffer[j-1] corresponds to the escape
711  // character must unescape it by appending one more.
712  // This will probably always be necessary since it is
713  // likely unicode_buffer will always have to contain
714  // escape characters that are interpreted by the device
715  // driver.
716  //
717  if ( unicode_buffer[j - 1] == (PLUNICODE) esc )
718  unicode_buffer[j++] = (PLUNICODE) esc;
719  j--;
720  skip = 1;
721  break;
722 
723  case '<': // change font
724  if ( '0' <= string[i + 2] && string[i + 2] <= '9' )
725  {
726  i += 2 + text2num( &string[i + 2], '>', &code );
727  if ( code & PL_FCI_MARK )
728  {
729  // code is a complete FCI (font characterization
730  // integer): change FCI to this value.
731  //
732  fci = code;
733  unicode_buffer[j] = fci;
734  skip = 1;
735  }
736  else
737  {
738  // code is not complete FCI. Change
739  // FCI with hex power in rightmost hex
740  // digit and hex digit value in second rightmost
741  // hex digit.
742  //
743  hexdigit = ( code >> 4 ) & PL_FCI_HEXDIGIT_MASK;
744  hexpower = code & PL_FCI_HEXPOWER_MASK;
745  plP_hex2fci( hexdigit, hexpower, &fci );
746  unicode_buffer[j] = fci;
747  skip = 1;
748  }
749  }
750  else
751  {
752  i += text2fci( &string[i + 1], &hexdigit, &hexpower );
753  if ( hexpower < 7 )
754  {
755  plP_hex2fci( hexdigit, hexpower, &fci );
756  unicode_buffer[j] = fci;
757  skip = 1;
758  }
759  }
760  break;
761 
762  case 'f': // Deprecated Hershey-style font change
763  case 'F': // Deprecated Hershey-style font change
764  // We implement an approximate response here so that
765  // reasonable results are obtained for unicode fonts,
766  // but this method is deprecated and the #<nnn> or
767  // #<command string> methods should be used instead
768  // to change unicode fonts in mid-string.
769  //
770  fci = PL_FCI_MARK;
771  if ( string[i + 2] == 'n' )
772  {
773  // medium, upright, sans-serif
775  }
776  else if ( string[i + 2] == 'r' )
777  {
778  // medium, upright, serif
780  }
781  else if ( string[i + 2] == 'i' )
782  {
783  // medium, italic, serif
786  }
787  else if ( string[i + 2] == 's' )
788  {
789  // medium, upright, script
791  }
792  else
793  fci = PL_FCI_IMPOSSIBLE;
794 
795  if ( fci != PL_FCI_IMPOSSIBLE )
796  {
797  i += 2;
798  unicode_buffer[j] = fci;
799  skip = 1;
800  }
801  break;
802 
803  case 'g': // Greek font
804  case 'G': // Greek font
805  // Get the index in the lookup table
806  // 527 = upper case alpha displacement in Hershey Table
807  // 627 = lower case alpha displacement in Hershey Table
808  //
809 
810  ig = plP_strpos( plP_greek_mnemonic, string[i + 2] );
811  if ( ig >= 0 )
812  {
813  if ( ig >= 24 )
814  ig = ig + 100 - 24;
815  ig = ig + 527;
816  // Follow pldeco in plsym.c which for
817  // lower case epsilon, theta, and phi
818  // substitutes (684, 685, and 686) for
819  // (631, 634, and 647)
820  if ( ig == 631 )
821  ig = 684;
822  else if ( ig == 634 )
823  ig = 685;
824  else if ( ig == 647 )
825  ig = 686;
826  idx = (int) plhershey2unicode( ig );
827  unicode_buffer[j++] = \
829  i += 2;
830  skip = 1; // skip is set if we have copied something
831  // into the unicode table
832  }
833  else
834  {
835  // Use "unknown" unicode character if string[i+2]
836  // is not in the Greek array.
837  unicode_buffer[j++] = (PLUNICODE) 0x00;
838  i += 2;
839  skip = 1; // skip is set if we have copied something
840  // into the unicode table
841  }
842  j--;
843  break;
844  }
845  }
846 
847  if ( skip == 0 )
848  {
849  PLUNICODE unichar = 0;
850 #ifdef HAVE_LIBUNICODE
851  const char * ptr = unicode_get_utf8( string + i, &unichar );
852 #else
853  const char * ptr = utf8_to_ucs4( string + i, &unichar );
854 #endif
855  if ( ptr == NULL )
856  {
857  char buf[BUFFER_SIZE];
858  char tmpstring[31];
859  strncpy( tmpstring, string, 30 );
860  tmpstring[30] = '\0';
861  snprintf( buf, BUFFER_SIZE, "UTF-8 string is malformed: %s%s",
862  tmpstring, strlen( string ) > 30 ? "[...]" : "" );
863  plabort( buf );
864  return;
865  }
866  unicode_buffer [j] = unichar;
867  i += (int) ( ptr - ( string + i ) - 1 );
868 
869  // Search for escesc (an unescaped escape) in the input
870  // string and adjust unicode_buffer accordingly).
871  //
872  if ( unicode_buffer[j] == (PLUNICODE) esc && string[i + 1] == esc )
873  {
874  i++;
875  unicode_buffer[++j] = (PLUNICODE) esc;
876  }
877  }
878  j++;
879  }
880  if ( j > 0 )
881  {
882  args.unicode_array_len = (short unsigned int) j; // Much easier to set the length than
883  // work it out later :-)
884  args.unicode_array = &unicode_buffer[0]; // Get address of the
885  // unicode buffer (even
886  // though it is
887  // currently static)
888  }
889 
890 
891  // The alternate unicode text handling loop.
892 
893  if ( plsc->alt_unicode )
894  {
895  args.n_fci = orig_fci;
896  plP_esc( PLESC_BEGIN_TEXT, &args );
897 
898  for ( i = 0; i < len; i++ )
899  {
900  skip = 0;
901 
902  if ( string[i] == esc )
903  {
904  switch ( string[i + 1] )
905  {
906  case '(': // hershey code
907  i += 2 + text2num( &string[i + 2], ')', &code );
908  idx = plhershey2unicode( (int) code );
909  args.n_char = \
911  plP_esc( PLESC_TEXT_CHAR, &args );
912 
913  skip = 1;
914  break;
915 
916  case '[': // unicode
917  i += 2 + text2num( &string[i + 2], ']', &code );
918  args.n_char = code;
919  plP_esc( PLESC_TEXT_CHAR, &args );
920  skip = 1;
921  break;
922 
923  case '<': // change font
924  if ( '0' <= string[i + 2] && string[i + 2] <= '9' )
925  {
926  i += 2 + text2num( &string[i + 2], '>', &code );
927  if ( code & PL_FCI_MARK )
928  {
929  // code is a complete FCI (font characterization
930  // integer): change FCI to this value.
931  //
932  fci = code;
933  skip = 1;
934 
935  args.n_fci = fci;
937  plP_esc( PLESC_CONTROL_CHAR, &args );
938  }
939  else
940  {
941  // code is not complete FCI. Change
942  // FCI with hex power in rightmost hex
943  // digit and hex digit value in second rightmost
944  // hex digit.
945  //
946  hexdigit = ( code >> 4 ) & PL_FCI_HEXDIGIT_MASK;
947  hexpower = code & PL_FCI_HEXPOWER_MASK;
948  plP_hex2fci( hexdigit, hexpower, &fci );
949  skip = 1;
950 
951  args.n_fci = fci;
953  plP_esc( PLESC_CONTROL_CHAR, &args );
954  }
955  }
956  else
957  {
958  i += text2fci( &string[i + 1], &hexdigit, &hexpower );
959  if ( hexpower < 7 )
960  {
961  plP_hex2fci( hexdigit, hexpower, &fci );
962  skip = 1;
963 
964  args.n_fci = fci;
966  plP_esc( PLESC_CONTROL_CHAR, &args );
967  }
968  }
969  break;
970 
971  case 'f': // Deprecated Hershey-style font change
972  case 'F': // Deprecated Hershey-style font change
973  // We implement an approximate response here so that
974  // reasonable results are obtained for unicode fonts,
975  // but this method is deprecated and the #<nnn> or
976  // #<command string> methods should be used instead
977  // to change unicode fonts in mid-string.
978  //
979  fci = PL_FCI_MARK;
980  if ( string[i + 2] == 'n' )
981  {
982  // medium, upright, sans-serif
984  }
985  else if ( string[i + 2] == 'r' )
986  {
987  // medium, upright, serif
989  }
990  else if ( string[i + 2] == 'i' )
991  {
992  // medium, italic, serif
995  }
996  else if ( string[i + 2] == 's' )
997  {
998  // medium, upright, script
1000  }
1001  else
1002  fci = PL_FCI_IMPOSSIBLE;
1003 
1004  if ( fci != PL_FCI_IMPOSSIBLE )
1005  {
1006  i += 2;
1007  skip = 1;
1008 
1009  args.n_fci = fci;
1011  plP_esc( PLESC_CONTROL_CHAR, &args );
1012  }
1013  break;
1014 
1015  case 'g': // Greek font
1016  case 'G': // Greek font
1017  // Get the index in the lookup table
1018  // 527 = upper case alpha displacement in Hershey Table
1019  // 627 = lower case alpha displacement in Hershey Table
1020  //
1021  ig = plP_strpos( plP_greek_mnemonic, string[i + 2] );
1022  if ( ig >= 0 )
1023  {
1024  if ( ig >= 24 )
1025  ig = ig + 100 - 24;
1026  ig = ig + 527;
1027  // Follow pldeco in plsym.c which for
1028  // lower case epsilon, theta, and phi
1029  // substitutes (684, 685, and 686) for
1030  // (631, 634, and 647)
1031  if ( ig == 631 )
1032  ig = 684;
1033  else if ( ig == 634 )
1034  ig = 685;
1035  else if ( ig == 647 )
1036  ig = 686;
1037  idx = plhershey2unicode( ig );
1038  i += 2;
1039  skip = 1; // skip is set if we have copied something
1040  // into the unicode table
1041 
1042  args.n_char = \
1044  plP_esc( PLESC_TEXT_CHAR, &args );
1045  }
1046  else
1047  {
1048  // Use "unknown" unicode character if string[i+2]
1049  // is not in the Greek array.
1050  i += 2;
1051  skip = 1; // skip is set if we have copied something
1052  // into the unicode table
1053 
1054  args.n_char = \
1056  plP_esc( PLESC_TEXT_CHAR, &args );
1057  }
1058  break;
1059 
1060  case 'u':
1062  plP_esc( PLESC_CONTROL_CHAR, &args );
1063  i += 1;
1064  skip = 1;
1065  break;
1066 
1067  case 'd':
1069  plP_esc( PLESC_CONTROL_CHAR, &args );
1070  i += 1;
1071  skip = 1;
1072  break;
1073  case 'b':
1075  plP_esc( PLESC_CONTROL_CHAR, &args );
1076  i += 1;
1077  skip = 1;
1078  break;
1079  case '+':
1081  plP_esc( PLESC_CONTROL_CHAR, &args );
1082  i += 1;
1083  skip = 1;
1084  break;
1085  case '-':
1087  plP_esc( PLESC_CONTROL_CHAR, &args );
1088  i += 1;
1089  skip = 1;
1090  break;
1091  }
1092  }
1093 
1094  if ( skip == 0 )
1095  {
1096  PLUNICODE unichar = 0;
1097 #ifdef HAVE_LIBUNICODE
1098  const char * ptr = unicode_get_utf8( string + i, &unichar );
1099 #else
1100  const char * ptr = utf8_to_ucs4( string + i, &unichar );
1101 #endif
1102  if ( ptr == NULL )
1103  {
1104  char buf[BUFFER_SIZE];
1105  char tmpstring[31];
1106  strncpy( tmpstring, string, 30 );
1107  tmpstring[30] = '\0';
1108  snprintf( buf, BUFFER_SIZE, "UTF-8 string is malformed: %s%s",
1109  tmpstring, strlen( string ) > 30 ? "[...]" : "" );
1110  plabort( buf );
1111  return;
1112  }
1113  i += (int) ( ptr - ( string + i ) - 1 );
1114 
1115  // Search for escesc (an unescaped escape) in the input
1116  // string and adjust unicode_buffer accordingly).
1117  //
1118  if ( string[i] == esc && string[i + 1] == esc )
1119  {
1120  i++;
1121  args.n_char = (PLUNICODE) esc;
1122  }
1123  else
1124  {
1125  args.n_char = unichar;
1126  }
1127  plP_esc( PLESC_TEXT_CHAR, &args );
1128  }
1129  }
1130  plP_esc( PLESC_END_TEXT, &args );
1131  }
1132 
1133  // No text to display
1134 
1135  if ( j == 0 )
1136  return;
1137  }
1138  }
1139 
1140  if ( plsc->dev_unicode )
1141  {
1142  args.string = NULL; // We are using unicode
1143  }
1144  else
1145  {
1146  args.string = string;
1147  }
1148 
1149  plP_esc( PLESC_HAS_TEXT, &args );
1150 
1151 #ifndef DEBUG_TEXT
1152  }
1153  else
1154  {
1155 #endif
1156  plstr( base, xform, refx, refy, string );
1157  }
1158 }
1159 
1160 // convert utf8 string to ucs4 unichar
1161 static const char *
1162 utf8_to_ucs4( const char *ptr, PLUNICODE *unichar )
1163 {
1164  char tmp;
1165  int isFirst = 1;
1166  int cnt = 0;
1167 
1168  do
1169  {
1170  // Get next character in string
1171  tmp = *ptr++;
1172  if ( isFirst ) // First char in UTF8 sequence
1173  {
1174  isFirst = 0;
1175  // Determine length of sequence
1176  if ( (unsigned char) ( tmp & 0x80 ) == 0x00 ) // single char
1177  {
1178  *unichar = (unsigned int) tmp & 0x7F;
1179  cnt = 0;
1180  }
1181  else if ( (unsigned char) ( tmp & 0xE0 ) == 0xC0 ) // 2 chars
1182  {
1183  *unichar = (unsigned int) tmp & 0x1F;
1184  cnt = 1;
1185  }
1186  else if ( (unsigned char) ( tmp & 0xF0 ) == 0xE0 ) // 3 chars
1187  {
1188  *unichar = (unsigned char) tmp & 0x0F;
1189  cnt = 2;
1190  }
1191  else if ( (unsigned char) ( tmp & 0xF8 ) == 0xF0 ) // 4 chars
1192  {
1193  *unichar = (unsigned char) tmp & 0x07;
1194  cnt = 3;
1195  }
1196  else if ( (unsigned char) ( tmp & 0xFC ) == 0xF8 ) // 5 chars
1197  {
1198  *unichar = (unsigned char) tmp & 0x03;
1199  cnt = 4;
1200  }
1201  else if ( (unsigned char) ( tmp & 0xFE ) == 0xFC ) // 6 chars
1202  {
1203  *unichar = (unsigned char) tmp & 0x01;
1204  cnt = 5;
1205  }
1206  else // Malformed
1207  {
1208  ptr = NULL;
1209  cnt = 0;
1210  }
1211  }
1212  else // Subsequent char in UTF8 sequence
1213  {
1214  if ( (unsigned char) ( tmp & 0xC0 ) == 0x80 )
1215  {
1216  *unichar = ( *unichar << 6 ) | ( (unsigned int) tmp & 0x3F );
1217  cnt--;
1218  }
1219  else // Malformed
1220  {
1221  ptr = NULL;
1222  cnt = 0;
1223  }
1224  }
1225  } while ( cnt > 0 );
1226  return ptr;
1227 }
1228 
1229 // convert ucs4 unichar to utf8 string
1230 int
1231 ucs4_to_utf8( PLUNICODE unichar, char *ptr )
1232 {
1233  unsigned char *tmp;
1234  int len;
1235 
1236  tmp = (unsigned char *) ptr;
1237 
1238  if ( ( unichar & 0xffff80 ) == 0 ) // single byte
1239  {
1240  *tmp = (unsigned char) unichar;
1241  tmp++;
1242  len = 1;
1243  }
1244  else if ( ( unichar & 0xfff800 ) == 0 ) // two bytes
1245  {
1246  *tmp = (unsigned char) 0xc0 | (unsigned char) ( unichar >> 6 );
1247  tmp++;
1248  *tmp = (unsigned char) ( 0x80 | (unsigned char) ( unichar & (PLUINT) 0x3f ) );
1249  tmp++;
1250  len = 2;
1251  }
1252  else if ( ( unichar & 0xff0000 ) == 0 ) // three bytes
1253  {
1254  *tmp = (unsigned char) 0xe0 | (unsigned char) ( unichar >> 12 );
1255  tmp++;
1256  *tmp = (unsigned char) ( 0x80 | (unsigned char) ( ( unichar >> 6 ) & 0x3f ) );
1257  tmp++;
1258  *tmp = (unsigned char) ( 0x80 | ( (unsigned char) unichar & 0x3f ) );
1259  tmp++;
1260  len = 3;
1261  }
1262  else if ( ( unichar & 0xe0000 ) == 0 ) // four bytes
1263  {
1264  *tmp = (unsigned char) 0xf0 | (unsigned char) ( unichar >> 18 );
1265  tmp++;
1266  *tmp = (unsigned char) ( 0x80 | (unsigned char) ( ( unichar >> 12 ) & 0x3f ) );
1267  tmp++;
1268  *tmp = (unsigned char) ( 0x80 | (unsigned char) ( ( unichar >> 6 ) & 0x3f ) );
1269  tmp++;
1270  *tmp = (unsigned char) ( 0x80 | (unsigned char) ( unichar & 0x3f ) );
1271  tmp++;
1272  len = 4;
1273  }
1274  else // Illegal coding
1275  {
1276  len = 0;
1277  }
1278  *tmp = '\0';
1279 
1280  return len;
1281 }
1282 
1283 static void
1284 grline( short *x, short *y, PLINT PL_UNUSED( npts ) )
1285 {
1286  char *save_locale = plsave_set_locale();
1287  if ( !plsc->stream_closed )
1288  {
1289  ( *plsc->dispatch_table->pl_line )( (struct PLStream_struct *) plsc,
1290  x[0], y[0], x[1], y[1] );
1291  }
1292  plrestore_locale( save_locale );
1293 }
1294 
1295 static void
1296 grpolyline( short *x, short *y, PLINT npts )
1297 {
1298  char *save_locale = plsave_set_locale();
1299  if ( !plsc->stream_closed )
1300  {
1301  ( *plsc->dispatch_table->pl_polyline )( (struct PLStream_struct *) plsc,
1302  x, y, npts );
1303  }
1304  plrestore_locale( save_locale );
1305 }
1306 
1307 static void
1308 grfill( short *x, short *y, PLINT npts )
1309 {
1310  char * save_locale;
1311  plsc->dev_npts = npts;
1312  plsc->dev_x = x;
1313  plsc->dev_y = y;
1314 
1315  save_locale = plsave_set_locale();
1316  if ( !plsc->stream_closed )
1317  {
1318  ( *plsc->dispatch_table->pl_esc )( (struct PLStream_struct *) plsc,
1319  PLESC_FILL, NULL );
1320  }
1321  plrestore_locale( save_locale );
1322 }
1323 
1324 static void
1325 grgradient( short *x, short *y, PLINT npts )
1326 {
1327  char * save_locale;
1328  plsc->dev_npts = npts;
1329  plsc->dev_x = x;
1330  plsc->dev_y = y;
1331 
1332  save_locale = plsave_set_locale();
1333  if ( !plsc->stream_closed )
1334  {
1335  ( *plsc->dispatch_table->pl_esc )( (struct PLStream_struct *) plsc,
1336  PLESC_GRADIENT, NULL );
1337  }
1338  plrestore_locale( save_locale );
1339 }
1340 
1341 //--------------------------------------------------------------------------
1342 // void difilt
1343 //
1344 // Driver interface filter -- passes all coordinates through a variety
1345 // of filters. These include filters to change :
1346 //
1347 // - mapping of meta to physical coordinates
1348 // - plot orientation
1349 // - window into plot (zooms)
1350 // - window into device (i.e set margins)
1351 //
1352 // The filters are applied in the order specified above. Because the
1353 // orientation change comes first, subsequent window specifications affect
1354 // the new coordinates (i.e. after a 90 degree flip, what was x is now y).
1355 // This is the only way that makes sense from a graphical interface
1356 // (e.g. TCL/TK driver).
1357 //
1358 // Where appropriate, the page clip limits are modified.
1359 //--------------------------------------------------------------------------
1360 
1361 void
1362 difilt( PLINT *xsc, PLINT *ysc, PLINT npts,
1363  PLINT *clpxmi, PLINT *clpxma, PLINT *clpymi, PLINT *clpyma )
1364 {
1365  PLINT i, x, y;
1366 
1367 // Map meta coordinates to physical coordinates
1368 
1369  if ( plsc->difilt & PLDI_MAP )
1370  {
1371  for ( i = 0; i < npts; i++ )
1372  {
1373  xsc[i] = (PLINT) ( plsc->dimxax * xsc[i] + plsc->dimxb );
1374  ysc[i] = (PLINT) ( plsc->dimyay * ysc[i] + plsc->dimyb );
1375  }
1376  }
1377 
1378 // Change orientation
1379 
1380  if ( plsc->difilt & PLDI_ORI )
1381  {
1382  for ( i = 0; i < npts; i++ )
1383  {
1384  x = (PLINT) ( plsc->dioxax * xsc[i] + plsc->dioxay * ysc[i] + plsc->dioxb );
1385  y = (PLINT) ( plsc->dioyax * xsc[i] + plsc->dioyay * ysc[i] + plsc->dioyb );
1386  xsc[i] = x;
1387  ysc[i] = y;
1388  }
1389  }
1390 
1391 // Change window into plot space
1392 
1393  if ( plsc->difilt & PLDI_PLT )
1394  {
1395  for ( i = 0; i < npts; i++ )
1396  {
1397  xsc[i] = (PLINT) ( plsc->dipxax * xsc[i] + plsc->dipxb );
1398  ysc[i] = (PLINT) ( plsc->dipyay * ysc[i] + plsc->dipyb );
1399  }
1400  }
1401 
1402 // Change window into device space and set clip limits
1403 // (this is the only filter that modifies them)
1404 
1405  if ( plsc->difilt & PLDI_DEV )
1406  {
1407  for ( i = 0; i < npts; i++ )
1408  {
1409  xsc[i] = (PLINT) ( plsc->didxax * xsc[i] + plsc->didxb );
1410  ysc[i] = (PLINT) ( plsc->didyay * ysc[i] + plsc->didyb );
1411  }
1412  *clpxmi = plsc->diclpxmi;
1413  *clpxma = plsc->diclpxma;
1414  *clpymi = plsc->diclpymi;
1415  *clpyma = plsc->diclpyma;
1416  }
1417  else
1418  {
1419  *clpxmi = plsc->phyxmi;
1420  *clpxma = plsc->phyxma;
1421  *clpymi = plsc->phyymi;
1422  *clpyma = plsc->phyyma;
1423  }
1424 }
1425 
1426 
1427 // Function is unused except for commented out image code
1428 // If / when that is fixed, then reinstate this function.
1429 // Needs a prototype and the casting fixed.
1430 //
1431 // void
1432 // sdifilt( short *xscl, short *yscl, PLINT npts,
1433 // PLINT *clpxmi, PLINT *clpxma, PLINT *clpymi, PLINT *clpyma )
1434 // {
1435 // int i;
1436 // short x, y;
1437 
1438 // // Map meta coordinates to physical coordinates
1439 
1440 // if ( plsc->difilt & PLDI_MAP )
1441 // {
1442 // for ( i = 0; i < npts; i++ )
1443 // {
1444 // xscl[i] = (PLINT) ( plsc->dimxax * xscl[i] + plsc->dimxb );
1445 // yscl[i] = (PLINT) ( plsc->dimyay * yscl[i] + plsc->dimyb );
1446 // }
1447 // }
1448 
1449 // // Change orientation
1450 
1451 // if ( plsc->difilt & PLDI_ORI )
1452 // {
1453 // for ( i = 0; i < npts; i++ )
1454 // {
1455 // x = (PLINT) ( plsc->dioxax * xscl[i] + plsc->dioxay * yscl[i] + plsc->dioxb );
1456 // y = (PLINT) ( plsc->dioyax * xscl[i] + plsc->dioyay * yscl[i] + plsc->dioyb );
1457 // xscl[i] = x;
1458 // yscl[i] = y;
1459 // }
1460 // }
1461 
1462 // // Change window into plot space
1463 
1464 // if ( plsc->difilt & PLDI_PLT )
1465 // {
1466 // for ( i = 0; i < npts; i++ )
1467 // {
1468 // xscl[i] = (PLINT) ( plsc->dipxax * xscl[i] + plsc->dipxb );
1469 // yscl[i] = (PLINT) ( plsc->dipyay * yscl[i] + plsc->dipyb );
1470 // }
1471 // }
1472 
1473 // // Change window into device space and set clip limits
1474 // // (this is the only filter that modifies them)
1475 
1476 // if ( plsc->difilt & PLDI_DEV )
1477 // {
1478 // for ( i = 0; i < npts; i++ )
1479 // {
1480 // xscl[i] = (PLINT) ( plsc->didxax * xscl[i] + plsc->didxb );
1481 // yscl[i] = (PLINT) ( plsc->didyay * yscl[i] + plsc->didyb );
1482 // }
1483 // *clpxmi = (PLINT) ( plsc->diclpxmi );
1484 // *clpxma = (PLINT) ( plsc->diclpxma );
1485 // *clpymi = (PLINT) ( plsc->diclpymi );
1486 // *clpyma = (PLINT) ( plsc->diclpyma );
1487 // }
1488 // else
1489 // {
1490 // *clpxmi = plsc->phyxmi;
1491 // *clpxma = plsc->phyxma;
1492 // *clpymi = plsc->phyymi;
1493 // *clpyma = plsc->phyyma;
1494 // }
1495 // }
1496 
1497 //--------------------------------------------------------------------------
1498 // void difilt_clip
1499 //
1500 // This provides the transformed text clipping region for the benefit of
1501 // those drivers that render their own text.
1502 //--------------------------------------------------------------------------
1503 
1504 void
1505 difilt_clip( PLINT *x_coords, PLINT *y_coords )
1506 {
1507  PLINT x1c, x2c, y1c, y2c;
1508 
1509  x1c = plsc->clpxmi;
1510  y1c = plsc->clpymi;
1511  x2c = plsc->clpxma;
1512  y2c = plsc->clpyma;
1513  x_coords[0] = x1c;
1514  x_coords[1] = x1c;
1515  x_coords[2] = x2c;
1516  x_coords[3] = x2c;
1517  y_coords[0] = y1c;
1518  y_coords[1] = y2c;
1519  y_coords[2] = y2c;
1520  y_coords[3] = y1c;
1521  difilt( x_coords, y_coords, 4, &x1c, &x2c, &y1c, &y2c );
1522 }
1523 
1524 
1525 //--------------------------------------------------------------------------
1526 // void pldi_ini
1527 //
1528 // Updates driver interface, making sure everything is in order.
1529 // Even if filter is not being used, the defaults need to be set up.
1530 //--------------------------------------------------------------------------
1531 
1532 static void
1534 {
1535  plsc->dipxmin = 0.0;
1536  plsc->dipxmax = 1.0;
1537  plsc->dipymin = 0.0;
1538  plsc->dipymax = 1.0;
1539 }
1540 
1541 static void
1543 {
1544  plsc->mar = 0.0;
1545  plsc->aspect = 0.0;
1546  plsc->jx = 0.0;
1547  plsc->jy = 0.0;
1548 }
1549 
1550 static void
1552 {
1553  plsc->diorot = 0.;
1554 }
1555 
1556 static void
1557 pldi_ini( void )
1558 {
1559  if ( plsc->level >= 1 )
1560  {
1561  if ( plsc->difilt & PLDI_MAP ) // Coordinate mapping
1562  calc_dimap();
1563 
1564  if ( plsc->difilt & PLDI_ORI ) // Orientation
1565  calc_diori();
1566  else
1567  setdef_diori();
1568 
1569  if ( plsc->difilt & PLDI_PLT ) // Plot window
1570  calc_diplt();
1571  else
1572  setdef_diplt();
1573 
1574  if ( plsc->difilt & PLDI_DEV ) // Device window
1575  calc_didev();
1576  else
1577  setdef_didev();
1578  }
1579 }
1580 
1581 //--------------------------------------------------------------------------
1582 // void pldid2pc
1583 //
1584 // Converts input values from relative device coordinates to relative plot
1585 // coordinates. This function must be called when selecting a plot window
1586 // from a display driver, since the coordinates chosen by the user are
1587 // necessarily device-specific.
1588 //--------------------------------------------------------------------------
1589 
1590 void
1592 {
1593  PLFLT pxmin, pymin, pxmax, pymax;
1594  PLFLT sxmin, symin, sxmax, symax;
1595  PLFLT rxmin, rymin, rxmax, rymax;
1596 
1597  if ( plsc->difilt & PLDI_DEV )
1598  {
1599  pldebug( "pldid2pc",
1600  "Relative device coordinates (in): %f, %f, %f, %f\n",
1601  *xmin, *ymin, *xmax, *ymax );
1602 
1603  pxmin = plP_dcpcx( *xmin );
1604  pymin = plP_dcpcy( *ymin );
1605  pxmax = plP_dcpcx( *xmax );
1606  pymax = plP_dcpcy( *ymax );
1607 
1608  sxmin = ( pxmin - plsc->didxb ) / plsc->didxax;
1609  symin = ( pymin - plsc->didyb ) / plsc->didyay;
1610  sxmax = ( pxmax - plsc->didxb ) / plsc->didxax;
1611  symax = ( pymax - plsc->didyb ) / plsc->didyay;
1612 
1613  rxmin = plP_pcdcx( (PLINT) sxmin );
1614  rymin = plP_pcdcy( (PLINT) symin );
1615  rxmax = plP_pcdcx( (PLINT) sxmax );
1616  rymax = plP_pcdcy( (PLINT) symax );
1617 
1618  *xmin = ( rxmin < 0 ) ? 0 : rxmin;
1619  *xmax = ( rxmax > 1 ) ? 1 : rxmax;
1620  *ymin = ( rymin < 0 ) ? 0 : rymin;
1621  *ymax = ( rymax > 1 ) ? 1 : rymax;
1622 
1623  pldebug( "pldid2pc",
1624  "Relative plot coordinates (out): %f, %f, %f, %f\n",
1625  rxmin, rymin, rxmax, rymax );
1626  }
1627 }
1628 
1629 //--------------------------------------------------------------------------
1630 // void pldip2dc
1631 //
1632 // Converts input values from relative plot coordinates to relative
1633 // device coordinates.
1634 //--------------------------------------------------------------------------
1635 
1636 void
1638 {
1639  PLFLT pxmin, pymin, pxmax, pymax;
1640  PLFLT sxmin, symin, sxmax, symax;
1641  PLFLT rxmin, rymin, rxmax, rymax;
1642 
1643  if ( plsc->difilt & PLDI_DEV )
1644  {
1645  pldebug( "pldip2pc",
1646  "Relative plot coordinates (in): %f, %f, %f, %f\n",
1647  *xmin, *ymin, *xmax, *ymax );
1648 
1649  pxmin = plP_dcpcx( *xmin );
1650  pymin = plP_dcpcy( *ymin );
1651  pxmax = plP_dcpcx( *xmax );
1652  pymax = plP_dcpcy( *ymax );
1653 
1654  sxmin = pxmin * plsc->didxax + plsc->didxb;
1655  symin = pymin * plsc->didyay + plsc->didyb;
1656  sxmax = pxmax * plsc->didxax + plsc->didxb;
1657  symax = pymax * plsc->didyay + plsc->didyb;
1658 
1659  rxmin = plP_pcdcx( (PLINT) sxmin );
1660  rymin = plP_pcdcy( (PLINT) symin );
1661  rxmax = plP_pcdcx( (PLINT) sxmax );
1662  rymax = plP_pcdcy( (PLINT) symax );
1663 
1664  *xmin = ( rxmin < 0 ) ? 0 : rxmin;
1665  *xmax = ( rxmax > 1 ) ? 1 : rxmax;
1666  *ymin = ( rymin < 0 ) ? 0 : rymin;
1667  *ymax = ( rymax > 1 ) ? 1 : rymax;
1668 
1669  pldebug( "pldip2pc",
1670  "Relative device coordinates (out): %f, %f, %f, %f\n",
1671  rxmin, rymin, rxmax, rymax );
1672  }
1673 }
1674 
1675 //--------------------------------------------------------------------------
1676 // void plsdiplt
1677 //
1678 // Set window into plot space
1679 //--------------------------------------------------------------------------
1680 
1681 void
1683 {
1684  plsc->dipxmin = ( xmin < xmax ) ? xmin : xmax;
1685  plsc->dipxmax = ( xmin < xmax ) ? xmax : xmin;
1686  plsc->dipymin = ( ymin < ymax ) ? ymin : ymax;
1687  plsc->dipymax = ( ymin < ymax ) ? ymax : ymin;
1688 
1689  if ( xmin == 0. && xmax == 1. && ymin == 0. && ymax == 1. )
1690  {
1691  plsc->difilt &= ~PLDI_PLT;
1692  return;
1693  }
1694 
1695  plsc->difilt |= PLDI_PLT;
1696  pldi_ini();
1697 }
1698 
1699 //--------------------------------------------------------------------------
1700 // void plsdiplz
1701 //
1702 // Set window into plot space incrementally (zoom)
1703 //--------------------------------------------------------------------------
1704 
1705 void
1707 {
1708  if ( plsc->difilt & PLDI_PLT )
1709  {
1710  xmin = plsc->dipxmin + ( plsc->dipxmax - plsc->dipxmin ) * xmin;
1711  ymin = plsc->dipymin + ( plsc->dipymax - plsc->dipymin ) * ymin;
1712  xmax = plsc->dipxmin + ( plsc->dipxmax - plsc->dipxmin ) * xmax;
1713  ymax = plsc->dipymin + ( plsc->dipymax - plsc->dipymin ) * ymax;
1714  }
1715 
1716  plsdiplt( xmin, ymin, xmax, ymax );
1717 }
1718 
1719 //--------------------------------------------------------------------------
1720 // void calc_diplt
1721 //
1722 // Calculate transformation coefficients to set window into plot space.
1723 //
1724 // Note: if driver has requested to handle these commands itself, we must
1725 // send the appropriate escape command. If the driver succeeds it will
1726 // cancel the filter operation. The command is deferred until this point
1727 // to ensure that the driver has been initialized.
1728 //--------------------------------------------------------------------------
1729 
1730 static void
1731 calc_diplt( void )
1732 {
1733  PLINT pxmin, pxmax, pymin, pymax, pxlen, pylen;
1734 
1735  if ( plsc->dev_di )
1736  {
1737  char *save_locale = plsave_set_locale();
1738  if ( !plsc->stream_closed )
1739  {
1740  ( *plsc->dispatch_table->pl_esc )( (struct PLStream_struct *) plsc,
1741  PLESC_DI, NULL );
1742  }
1743  plrestore_locale( save_locale );
1744  }
1745 
1746  if ( !( plsc->difilt & PLDI_PLT ) )
1747  return;
1748 
1749  pxmin = plP_dcpcx( plsc->dipxmin );
1750  pxmax = plP_dcpcx( plsc->dipxmax );
1751  pymin = plP_dcpcy( plsc->dipymin );
1752  pymax = plP_dcpcy( plsc->dipymax );
1753 
1754  pxlen = pxmax - pxmin;
1755  pylen = pymax - pymin;
1756  pxlen = MAX( 1, pxlen );
1757  pylen = MAX( 1, pylen );
1758 
1759  plsc->dipxax = plsc->phyxlen / (double) pxlen;
1760  plsc->dipyay = plsc->phyylen / (double) pylen;
1761  plsc->dipxb = plsc->phyxmi - plsc->dipxax * pxmin;
1762  plsc->dipyb = plsc->phyymi - plsc->dipyay * pymin;
1763 }
1764 
1765 //--------------------------------------------------------------------------
1766 // void plgdiplt
1767 //
1768 // Retrieve current window into plot space
1769 //--------------------------------------------------------------------------
1770 
1771 void
1772 c_plgdiplt( PLFLT *p_xmin, PLFLT *p_ymin, PLFLT *p_xmax, PLFLT *p_ymax )
1773 {
1774  *p_xmin = plsc->dipxmin;
1775  *p_xmax = plsc->dipxmax;
1776  *p_ymin = plsc->dipymin;
1777  *p_ymax = plsc->dipymax;
1778 }
1779 
1780 //--------------------------------------------------------------------------
1781 // void plsdidev
1782 //
1783 // Set window into device space using margin, aspect ratio, and
1784 // justification. If you want to just use the previous value for any of
1785 // these, just pass in the magic value PL_NOTSET.
1786 //
1787 // It is unlikely that one should ever need to change the aspect ratio
1788 // but it's in there for completeness.
1789 //--------------------------------------------------------------------------
1790 
1791 void
1792 c_plsdidev( PLFLT mar, PLFLT aspect, PLFLT jx, PLFLT jy )
1793 {
1794  plsetvar( plsc->mar, mar );
1795  plsetvar( plsc->aspect, aspect );
1796  plsetvar( plsc->jx, jx );
1797  plsetvar( plsc->jy, jy );
1798 
1799  if ( mar == 0. && aspect == 0. && jx == 0. && jy == 0. &&
1800  !( plsc->difilt & PLDI_ORI ) )
1801  {
1802  plsc->difilt &= ~PLDI_DEV;
1803  return;
1804  }
1805 
1806  plsc->difilt |= PLDI_DEV;
1807  pldi_ini();
1808 }
1809 
1810 //--------------------------------------------------------------------------
1811 // void calc_didev
1812 //
1813 // Calculate transformation coefficients to set window into device space.
1814 // Calculates relative window bounds and calls plsdidxy to finish the job.
1815 //--------------------------------------------------------------------------
1816 
1817 static void
1818 calc_didev( void )
1819 {
1820  PLFLT lx, ly, aspect, aspdev;
1821  PLFLT xmin, xmax, xlen, ymin, ymax, ylen;
1822  PLINT pxmin, pxmax, pymin, pymax, pxlen, pylen;
1823 
1824  if ( plsc->dev_di )
1825  {
1826  char *save_locale = plsave_set_locale();
1827  if ( !plsc->stream_closed )
1828  {
1829  ( *plsc->dispatch_table->pl_esc )( (struct PLStream_struct *) plsc,
1830  PLESC_DI, NULL );
1831  }
1832  plrestore_locale( save_locale );
1833  }
1834 
1835  if ( !( plsc->difilt & PLDI_DEV ) )
1836  return;
1837 
1838 // Calculate aspect ratio of physical device
1839 
1840  lx = plsc->phyxlen / plsc->xpmm;
1841  ly = plsc->phyylen / plsc->ypmm;
1842  aspdev = lx / ly;
1843 
1844  if ( plsc->difilt & PLDI_ORI )
1845  aspect = plsc->aspori;
1846  else
1847  aspect = plsc->aspect;
1848 
1849  if ( aspect <= 0. )
1850  aspect = plsc->aspdev;
1851 
1852 // Failsafe
1853 
1854  plsc->mar = ( plsc->mar > 0.5 ) ? 0.5 : plsc->mar;
1855  plsc->mar = ( plsc->mar < 0.0 ) ? 0.0 : plsc->mar;
1856  plsc->jx = ( plsc->jx > 0.5 ) ? 0.5 : plsc->jx;
1857  plsc->jx = ( plsc->jx < -0.5 ) ? -0.5 : plsc->jx;
1858  plsc->jy = ( plsc->jy > 0.5 ) ? 0.5 : plsc->jy;
1859  plsc->jy = ( plsc->jy < -0.5 ) ? -0.5 : plsc->jy;
1860 
1861 // Relative device coordinates that neutralize aspect ratio difference
1862 
1863  xlen = ( aspect < aspdev ) ? ( aspect / aspdev ) : 1.0;
1864  ylen = ( aspect < aspdev ) ? 1.0 : ( aspdev / aspect );
1865 
1866  xlen *= ( 1.0 - 2. * plsc->mar );
1867  ylen *= ( 1.0 - 2. * plsc->mar );
1868 
1869  xmin = ( 1. - xlen ) * ( 0.5 + plsc->jx );
1870  xmax = xmin + xlen;
1871 
1872  ymin = ( 1. - ylen ) * ( 0.5 + plsc->jy );
1873  ymax = ymin + ylen;
1874 
1875 // Calculate transformation coefficients
1876 
1877  pxmin = plP_dcpcx( xmin );
1878  pxmax = plP_dcpcx( xmax );
1879  pymin = plP_dcpcy( ymin );
1880  pymax = plP_dcpcy( ymax );
1881 
1882  pxlen = pxmax - pxmin;
1883  pylen = pymax - pymin;
1884  pxlen = MAX( 1, pxlen );
1885  pylen = MAX( 1, pylen );
1886 
1887  plsc->didxax = pxlen / (double) plsc->phyxlen;
1888  plsc->didyay = pylen / (double) plsc->phyylen;
1889  plsc->didxb = pxmin - plsc->didxax * plsc->phyxmi;
1890  plsc->didyb = pymin - plsc->didyay * plsc->phyymi;
1891 
1892 // Set clip limits to conform to new page size
1893 
1894  plsc->diclpxmi = (PLINT) ( plsc->didxax * plsc->phyxmi + plsc->didxb );
1895  plsc->diclpxma = (PLINT) ( plsc->didxax * plsc->phyxma + plsc->didxb );
1896  plsc->diclpymi = (PLINT) ( plsc->didyay * plsc->phyymi + plsc->didyb );
1897  plsc->diclpyma = (PLINT) ( plsc->didyay * plsc->phyyma + plsc->didyb );
1898 }
1899 
1900 //--------------------------------------------------------------------------
1901 // void plgdidev
1902 //
1903 // Retrieve current window into device space
1904 //--------------------------------------------------------------------------
1905 
1906 void
1907 c_plgdidev( PLFLT *p_mar, PLFLT *p_aspect, PLFLT *p_jx, PLFLT *p_jy )
1908 {
1909  *p_mar = plsc->mar;
1910  *p_aspect = plsc->aspect;
1911  *p_jx = plsc->jx;
1912  *p_jy = plsc->jy;
1913 }
1914 
1915 //--------------------------------------------------------------------------
1916 // void plsdiori
1917 //
1918 // Set plot orientation, specifying rotation in units of pi/2.
1919 //--------------------------------------------------------------------------
1920 
1921 void
1923 {
1924  plsc->diorot = rot;
1925  if ( rot == 0. )
1926  {
1927  plsc->difilt &= ~PLDI_ORI;
1928  pldi_ini();
1929  return;
1930  }
1931 
1932  plsc->difilt |= PLDI_ORI;
1933  pldi_ini();
1934 }
1935 
1936 //--------------------------------------------------------------------------
1937 // void calc_diori
1938 //
1939 // Calculate transformation coefficients to arbitrarily orient plot.
1940 // Preserve aspect ratios so the output doesn't suck.
1941 //--------------------------------------------------------------------------
1942 
1943 static void
1944 calc_diori( void )
1945 {
1946  PLFLT cost, sint;
1947  PLFLT x0, y0, lx, ly, aspect;
1948  PLFLT affine_result[NAFFINE], affine_left[NAFFINE];
1949 
1950  if ( plsc->dev_di )
1951  {
1952  char *save_locale = plsave_set_locale();
1953  if ( !plsc->stream_closed )
1954  {
1955  ( *plsc->dispatch_table->pl_esc )( (struct PLStream_struct *) plsc,
1956  PLESC_DI, NULL );
1957  }
1958  plrestore_locale( save_locale );
1959  }
1960 
1961  if ( !( plsc->difilt & PLDI_ORI ) )
1962  return;
1963 
1964 // Center point of rotation
1965 
1966  x0 = ( plsc->phyxma + plsc->phyxmi ) / 2.;
1967  y0 = ( plsc->phyyma + plsc->phyymi ) / 2.;
1968 
1969 // Rotation
1970 
1971  cost = ABS( cos( plsc->diorot * PI / 2. ) );
1972  sint = ABS( sin( plsc->diorot * PI / 2. ) );
1973 
1974 // Flip aspect ratio as necessary. Grungy but I don't see a better way
1975 
1976  aspect = plsc->aspect;
1977  if ( aspect == 0. )
1978  aspect = plsc->aspdev;
1979 
1980  if ( plsc->freeaspect )
1981  plsc->aspori = aspect;
1982  else
1983  plsc->aspori = ( aspect * cost + sint ) / ( aspect * sint + cost );
1984 
1985  if ( !( plsc->difilt & PLDI_DEV ) )
1986  {
1987  plsc->difilt |= PLDI_DEV;
1988  setdef_didev();
1989  }
1990  calc_didev();
1991 
1992  // Compute scale factors for relative device coordinates. Only
1993  // the aspect ratio of lx to ly matters. Note, plsc->phyxlen and
1994  // plsc->phyylen are in PLplot core library coordinates and don't
1995  // know anything about device coordinates which are likely to have
1996  // a quite different aspect ratio. So to correct between the two
1997  // coordinate systems must divide plsc->phyxlen/plsc->phyylen by
1998  // plsc->aspori.
1999 
2000  // N.B. comment out this correction because causes other issues.
2001 
2002  //lx = plsc->phyxlen/plsc->aspori;
2003  lx = plsc->phyxlen;
2004  ly = plsc->phyylen;
2005 
2006 // Transformation coefficients
2007 
2008  //
2009  // plsc->dioxax = r11;
2010  // plsc->dioxay = r21 * ( lx / ly );
2011  // plsc->dioxb = ( 1. - r11 ) * x0 - r21 * y0 * ( lx / ly );
2012  //
2013  // plsc->dioyax = r12 * ( ly / lx );
2014  // plsc->dioyay = r22;
2015  // plsc->dioyb = ( 1. - r22 ) * y0 - r12 * x0 * ( ly / lx );
2016  //
2017 
2018  // Calculate affine transformation as product of translate to middle
2019  // of device, scale to relative device coordinates, rotate, unscale
2020  // to physical coordinates, untranslate to original zero point.
2021  plP_affine_translate( affine_result, x0, y0 );
2022  plP_affine_scale( affine_left, lx, ly );
2023  plP_affine_multiply( affine_result, affine_left, affine_result );
2024  plP_affine_rotate( affine_left, plsc->diorot * 90. );
2025  plP_affine_multiply( affine_result, affine_left, affine_result );
2026  plP_affine_scale( affine_left, 1. / lx, 1. / ly );
2027  plP_affine_multiply( affine_result, affine_left, affine_result );
2028  plP_affine_translate( affine_left, -x0, -y0 );
2029  plP_affine_multiply( affine_result, affine_left, affine_result );
2030  plsc->dioxax = affine_result[0];
2031  plsc->dioxay = affine_result[2];
2032  plsc->dioxb = affine_result[4];
2033  plsc->dioyax = affine_result[1];
2034  plsc->dioyay = affine_result[3];
2035  plsc->dioyb = affine_result[5];
2036 }
2037 
2038 //--------------------------------------------------------------------------
2039 // void plgdiori
2040 //
2041 // Get plot orientation
2042 //--------------------------------------------------------------------------
2043 
2044 void
2046 {
2047  *p_rot = plsc->diorot;
2048 }
2049 
2050 //--------------------------------------------------------------------------
2051 // void plsdimap
2052 //
2053 // Set up transformation from metafile coordinates. The size of the plot is
2054 // scaled so as to preserve aspect ratio. This isn't intended to be a
2055 // general-purpose facility just yet (not sure why the user would need it,
2056 // for one).
2057 //--------------------------------------------------------------------------
2058 
2059 void
2060 c_plsdimap( PLINT dimxmin, PLINT dimxmax, PLINT dimymin, PLINT dimymax,
2061  PLFLT dimxpmm, PLFLT dimypmm )
2062 {
2063  plsetvar( plsc->dimxmin, dimxmin );
2064  plsetvar( plsc->dimxmax, dimxmax );
2065  plsetvar( plsc->dimymin, dimymin );
2066  plsetvar( plsc->dimymax, dimymax );
2067  plsetvar( plsc->dimxpmm, dimxpmm );
2068  plsetvar( plsc->dimypmm, dimypmm );
2069 
2070  plsc->difilt |= PLDI_MAP;
2071  pldi_ini();
2072 }
2073 
2074 //--------------------------------------------------------------------------
2075 // void calc_dimap
2076 //
2077 // Set up transformation from metafile coordinates. The size of the plot is
2078 // scaled so as to preserve aspect ratio. This isn't intended to be a
2079 // general-purpose facility just yet (not sure why the user would need it,
2080 // for one).
2081 //--------------------------------------------------------------------------
2082 
2083 static void
2085 {
2086  PLFLT lx, ly;
2087  PLINT pxmin, pxmax, pymin, pymax;
2088  PLFLT dimxlen, dimylen, pxlen, pylen;
2089 
2090  if ( ( plsc->dimxmin == plsc->phyxmi ) && ( plsc->dimxmax == plsc->phyxma ) &&
2091  ( plsc->dimymin == plsc->phyymi ) && ( plsc->dimymax == plsc->phyyma ) &&
2092  ( plsc->dimxpmm == plsc->xpmm ) && ( plsc->dimypmm == plsc->ypmm ) )
2093  {
2094  plsc->difilt &= ~PLDI_MAP;
2095  return;
2096  }
2097 
2098 // Set default aspect ratio
2099 
2100  lx = ( plsc->dimxmax - plsc->dimxmin + 1 ) / plsc->dimxpmm;
2101  ly = ( plsc->dimymax - plsc->dimymin + 1 ) / plsc->dimypmm;
2102 
2103  plsc->aspdev = lx / ly;
2104 
2105 // Build transformation to correct physical coordinates
2106 
2107  dimxlen = plsc->dimxmax - plsc->dimxmin;
2108  dimylen = plsc->dimymax - plsc->dimymin;
2109 
2110  pxmin = plsc->phyxmi;
2111  pxmax = plsc->phyxma;
2112  pymin = plsc->phyymi;
2113  pymax = plsc->phyyma;
2114  pxlen = pxmax - pxmin;
2115  pylen = pymax - pymin;
2116 
2117  plsc->dimxax = pxlen / dimxlen;
2118  plsc->dimyay = pylen / dimylen;
2119  plsc->dimxb = pxmin - pxlen * plsc->dimxmin / dimxlen;
2120  plsc->dimyb = pymin - pylen * plsc->dimymin / dimylen;
2121 }
2122 
2123 //--------------------------------------------------------------------------
2124 // void plflush()
2125 //
2126 // Flushes the output stream. Use sparingly, if at all.
2127 //--------------------------------------------------------------------------
2128 
2129 void
2130 c_plflush( void )
2131 {
2132  if ( plsc->dev_flush )
2133  {
2134  char *save_locale = plsave_set_locale();
2135  if ( !plsc->stream_closed )
2136  {
2137  ( *plsc->dispatch_table->pl_esc )( (struct PLStream_struct *) plsc,
2138  PLESC_FLUSH, NULL );
2139  }
2140  plrestore_locale( save_locale );
2141  }
2142  else
2143  {
2144  if ( plsc->OutFile != NULL )
2145  fflush( plsc->OutFile );
2146  }
2147 }
2148 
2149 //--------------------------------------------------------------------------
2150 // Startup routines.
2151 //--------------------------------------------------------------------------
2152 
2153 //--------------------------------------------------------------------------
2154 // void pllib_init()
2155 //
2156 // Initialize library. Called internally by every startup routine.
2157 // Everything you want to always be initialized before plinit() is called
2158 // you should put here. E.g. dispatch table setup, rcfile read, etc.
2159 //--------------------------------------------------------------------------
2160 
2161 void
2163 {
2164  if ( lib_initialized )
2165  return;
2166  lib_initialized = 1;
2167 
2168 #ifdef ENABLE_DYNDRIVERS
2169 // Create libltdl resources
2170  lt_dlinit();
2171 #endif
2172 
2173 // Initialize the dispatch table with the info from the static drivers table
2174 // and the available dynamic drivers.
2175 
2177 }
2178 
2179 //--------------------------------------------------------------------------
2180 // void plstar(nx, ny)
2181 //
2182 // Initialize PLplot, passing in the windows/page settings.
2183 //--------------------------------------------------------------------------
2184 
2185 void
2187 {
2188  pllib_init();
2189 
2190  if ( plsc->level != 0 )
2191  plend1();
2192 
2193  plssub( nx, ny );
2194 
2195  c_plinit();
2196 }
2197 
2198 //--------------------------------------------------------------------------
2199 // void plstart(devname, nx, ny)
2200 //
2201 // Initialize PLplot, passing the device name and windows/page settings.
2202 //--------------------------------------------------------------------------
2203 
2204 void
2205 c_plstart( const char *devname, PLINT nx, PLINT ny )
2206 {
2207  pllib_init();
2208 
2209  if ( plsc->level != 0 )
2210  plend1();
2211 
2212  plssub( nx, ny );
2213  plsdev( devname );
2214 
2215  c_plinit();
2216 }
2217 
2218 //--------------------------------------------------------------------------
2219 // void plinit()
2220 //
2221 // Initializes PLplot, using preset or default options.
2222 //--------------------------------------------------------------------------
2223 
2224 void
2225 c_plinit( void )
2226 {
2227  PLFLT lx, ly, xpmm_loc, ypmm_loc, aspect_old, aspect_new;
2228  PLINT inc = 0, del = 2000;
2229 
2230  pllib_init();
2231 
2232  if ( plsc->level != 0 )
2233  plend1();
2234 
2235 // Set stream number
2236 
2237  plsc->ipls = ipls;
2238 
2239 // Set up devices
2240 
2241  pllib_devinit();
2242 
2243 // Auxiliary stream setup
2244 
2245  plstrm_init();
2246 
2247 // Set title for window to a sensible default if not defined
2248  if ( plsc->plwindow == NULL )
2249  {
2250  if ( plsc->program )
2251  {
2252  if ( ( plsc->plwindow = (char *) malloc( (size_t) ( 1 + strlen( plsc->program ) ) * sizeof ( char ) ) ) == NULL )
2253  {
2254  plexit( "plinit: Insufficient memory" );
2255  }
2256  strcpy( plsc->plwindow, plsc->program );
2257  }
2258  else
2259  {
2260  if ( ( plsc->plwindow = (char *) malloc( (size_t) 7 * sizeof ( char ) ) ) == NULL )
2261  {
2262  plexit( "plinit: Insufficient memory" );
2263  }
2264  strcpy( plsc->plwindow, "PLplot" );
2265  }
2266  }
2267 
2268 // Initialize device & first page
2269 
2270  plP_init();
2271  plP_bop();
2272  plsc->level = 1;
2273 
2274 
2275 // The driver options are freed after driver initialisation,
2276 // since it is assumed that in this function options are
2277 // processed and stored somewhere else. For further driver
2278 // initialisations (e.g. copy stream) there are no driver
2279 // options defined.
2280 
2281  plP_FreeDrvOpts();
2282 
2283 // Calculate factor such that the character aspect ratio is preserved
2284 // when the overall aspect ratio is changed, i.e., if portrait mode is
2285 // requested (only honored for subset of drivers) or if the aspect ratio
2286 // is specified in any way, or if a 90 deg rotation occurs with
2287 // -freeaspect.
2288 
2289 // Case where plsc->aspect has a value.... (e.g., -a aspect on the
2290 // command line or 2nd parameter of plsdidev specified)
2291  if ( plsc->aspect > 0. )
2292  {
2293  lx = plsc->phyxlen / plsc->xpmm;
2294  ly = plsc->phyylen / plsc->ypmm;
2295  aspect_old = lx / ly;
2296  aspect_new = plsc->aspect;
2297  plsc->caspfactor = sqrt( aspect_old / aspect_new );
2298  }
2299 // Case of 90 deg rotations with -freeaspect (this is also how portrait
2300 // mode is implemented for the drivers that honor -portrait).
2301  else if ( plsc->freeaspect && ABS( cos( plsc->diorot * PI / 2. ) ) <= 1.e-5 )
2302  {
2303  lx = plsc->phyxlen / plsc->xpmm;
2304  ly = plsc->phyylen / plsc->ypmm;
2305  aspect_old = lx / ly;
2306  aspect_new = ly / lx;
2307  plsc->caspfactor = sqrt( aspect_old / aspect_new );
2308  }
2309 
2310  else
2311  plsc->caspfactor = 1.;
2312 
2313 // Load fonts
2314 
2315  plsc->cfont = 1;
2316  plfntld( initfont );
2317 
2318 // Set up subpages
2319 
2320  plP_subpInit();
2321 
2322 // Set up number of allowed digits before switching to scientific notation
2323 // The user can always change this
2324 
2325  if ( plsc->xdigmax == 0 )
2326  plsc->xdigmax = 4;
2327 
2328  if ( plsc->ydigmax == 0 )
2329  plsc->ydigmax = 4;
2330 
2331  if ( plsc->zdigmax == 0 )
2332  plsc->zdigmax = 3;
2333 
2334  if ( plsc->timefmt == NULL )
2335  c_pltimefmt( "%c" );
2336 
2337  // Use default transformation between continuous and broken-down time
2338  // (and vice versa) if the transformation has not yet been defined
2339  // for this stream.
2340  if ( plsc->qsasconfig == NULL )
2341  c_plconfigtime( 0., 0., 0., 0x0, 0, 0, 0, 0, 0, 0, 0. );
2342 
2343 // Switch to graphics mode and set color and arrow style
2344 
2345  plgra();
2346  plcol0( 1 );
2347 
2348  pllsty( 1 );
2349  plpat( 1, &inc, &del );
2350 
2351  // Set up default arrow style;
2352  plsvect( NULL, NULL, 6, 0 );
2353 
2354 // Set clip limits.
2355 
2356  plsc->clpxmi = plsc->phyxmi;
2357  plsc->clpxma = plsc->phyxma;
2358  plsc->clpymi = plsc->phyymi;
2359  plsc->clpyma = plsc->phyyma;
2360 
2361 // Page aspect ratio.
2362 
2363  lx = plsc->phyxlen / plsc->xpmm;
2364  ly = plsc->phyylen / plsc->ypmm;
2365  plsc->aspdev = lx / ly;
2366 
2367 // Initialize driver interface
2368 
2369  pldi_ini();
2370 
2371 // Apply compensating factor to original xpmm and ypmm so that
2372 // character aspect ratio is preserved when overall aspect ratio
2373 // is changed. This must appear here in the code because previous
2374 // code in this routine and in routines that it calls must use the original
2375 // values of xpmm and ypmm before the compensating factor is applied.
2376 
2377  plP_gpixmm( &xpmm_loc, &ypmm_loc );
2378  plP_setpxl( xpmm_loc * plsc->caspfactor, ypmm_loc / plsc->caspfactor );
2379 }
2380 
2381 //--------------------------------------------------------------------------
2382 // void plend()
2383 //
2384 // End a plotting session for all open streams.
2385 //--------------------------------------------------------------------------
2386 
2387 void
2388 c_plend( void )
2389 {
2390  PLINT i;
2391 
2392  if ( lib_initialized == 0 )
2393  return;
2394 
2395  for ( i = PL_NSTREAMS - 1; i >= 0; i-- )
2396  {
2397  if ( pls[i] != NULL )
2398  {
2399  plsstrm( i );
2400  c_plend1();
2401  }
2402  }
2403  plfontrel();
2404 #ifdef ENABLE_DYNDRIVERS
2405 // Release the libltdl resources
2406  lt_dlexit();
2407 // Free up memory allocated to the dispatch tables
2408  for ( i = 0; i < npldynamicdevices; i++ )
2409  {
2410  free_mem( loadable_device_list[i].devnam );
2411  free_mem( loadable_device_list[i].description );
2412  free_mem( loadable_device_list[i].drvnam );
2413  free_mem( loadable_device_list[i].tag );
2414  }
2415  free_mem( loadable_device_list );
2416  for ( i = 0; i < nloadabledrivers; i++ )
2417  {
2418  free_mem( loadable_driver_list[i].drvnam );
2419  }
2420  free_mem( loadable_driver_list );
2421  for ( i = nplstaticdevices; i < npldrivers; i++ )
2422  {
2423  free_mem( dispatch_table[i]->pl_MenuStr );
2424  free_mem( dispatch_table[i]->pl_DevName );
2425  free_mem( dispatch_table[i] );
2426  }
2427 #endif
2428  for ( i = 0; i < nplstaticdevices; i++ )
2429  {
2430  free_mem( dispatch_table[i] );
2431  }
2433 
2434  lib_initialized = 0;
2435 }
2436 
2437 //--------------------------------------------------------------------------
2438 // void plend1()
2439 //
2440 // End a plotting session for the current stream only. After the stream is
2441 // ended the memory associated with the stream's PLStream data structure is
2442 // freed (for stream > 0), and the stream counter is set to 0 (the default).
2443 //--------------------------------------------------------------------------
2444 
2445 void
2446 c_plend1( void )
2447 {
2448  if ( plsc->level > 0 )
2449  {
2450  plP_eop();
2451  plP_tidy();
2452  plsc->level = 0;
2453  }
2454  // Move from plP_tidy because FileName may be set even if level == 0
2455  if ( plsc->FileName )
2456  free_mem( plsc->FileName );
2457 
2458 // Free all malloc'ed stream memory
2459 
2460  free_mem( plsc->cmap0 );
2461  free_mem( plsc->cmap1 );
2462  free_mem( plsc->plwindow );
2463  free_mem( plsc->geometry );
2464  free_mem( plsc->dev );
2465  free_mem( plsc->BaseName );
2466 #ifndef BUFFERED_FILE
2467  free_mem( plsc->plbuf_buffer );
2468 #endif
2469  if ( plsc->program )
2470  free_mem( plsc->program );
2471  if ( plsc->server_name )
2472  free_mem( plsc->server_name );
2473  if ( plsc->server_host )
2474  free_mem( plsc->server_host );
2475  if ( plsc->server_port )
2476  free_mem( plsc->server_port );
2477  if ( plsc->user )
2478  free_mem( plsc->user );
2479  if ( plsc->plserver )
2480  free_mem( plsc->plserver );
2481  if ( plsc->auto_path )
2482  free_mem( plsc->auto_path );
2483 
2484  if ( plsc->arrow_x )
2485  free_mem( plsc->arrow_x );
2486  if ( plsc->arrow_y )
2487  free_mem( plsc->arrow_y );
2488 
2489  if ( plsc->timefmt )
2490  free_mem( plsc->timefmt );
2491 
2492  // Close qsastime library for this stream that was opened by
2493  // plconfigtime call in plinit.
2494 
2495  closeqsas( &( plsc->qsasconfig ) );
2496 
2497 // Free malloc'ed stream if not in initial stream, else clear it out
2498 
2499  if ( ipls > 0 )
2500  {
2501  free_mem( plsc );
2502  pls[ipls] = NULL;
2503  plsstrm( 0 );
2504  }
2505  else
2506  {
2507  memset( (char *) pls[ipls], 0, sizeof ( PLStream ) );
2508  }
2509 }
2510 
2511 //--------------------------------------------------------------------------
2512 // void plsstrm
2513 //
2514 // Set stream number. If the data structure for a new stream is
2515 // unallocated, we allocate it here.
2516 //--------------------------------------------------------------------------
2517 
2518 void
2520 {
2521  if ( strm < 0 || strm >= PL_NSTREAMS )
2522  {
2523  fprintf( stderr,
2524  "plsstrm: Illegal stream number %d, must be in [0, %d]\n",
2525  (int) strm, PL_NSTREAMS );
2526  }
2527  else
2528  {
2529  ipls = strm;
2530  if ( pls[ipls] == NULL )
2531  {
2532  pls[ipls] = (PLStream *) malloc( (size_t) sizeof ( PLStream ) );
2533  if ( pls[ipls] == NULL )
2534  plexit( "plsstrm: Out of memory." );
2535 
2536  memset( (char *) pls[ipls], 0, sizeof ( PLStream ) );
2537  }
2538  plsc = pls[ipls];
2539  plsc->ipls = ipls;
2540  }
2541 }
2542 
2543 //--------------------------------------------------------------------------
2544 // void plgstrm
2545 //
2546 // Get current stream number.
2547 //--------------------------------------------------------------------------
2548 
2549 void
2550 c_plgstrm( PLINT *p_strm )
2551 {
2552  *p_strm = ipls;
2553 }
2554 
2555 //--------------------------------------------------------------------------
2556 // void plmkstrm
2557 //
2558 // Creates a new stream and makes it the default. Differs from using
2559 // plsstrm(), in that a free stream number is found, and returned.
2560 //
2561 // Unfortunately, I /have/ to start at stream 1 and work upward, since
2562 // stream 0 is preallocated. One of the BIG flaws in the PLplot API is
2563 // that no initial, library-opening call is required. So stream 0 must be
2564 // preallocated, and there is no simple way of determining whether it is
2565 // already in use or not.
2566 //--------------------------------------------------------------------------
2567 
2568 void
2569 c_plmkstrm( PLINT *p_strm )
2570 {
2571  int i;
2572 
2573  for ( i = 1; i < PL_NSTREAMS; i++ )
2574  {
2575  if ( pls[i] == NULL )
2576  break;
2577  }
2578 
2579  if ( i == PL_NSTREAMS )
2580  {
2581  fprintf( stderr, "plmkstrm: Cannot create new stream\n" );
2582  *p_strm = -1;
2583  }
2584  else
2585  {
2586  *p_strm = i;
2587  plsstrm( i );
2588  }
2589  plstrm_init();
2590 }
2591 
2592 //--------------------------------------------------------------------------
2593 // void plstrm_init
2594 //
2595 // Does required startup initialization of a stream. Should be called right
2596 // after creating one (for allocating extra memory, etc). Users shouldn't
2597 // need to call this directly.
2598 //
2599 // This function can be called multiple times for a given stream, in which
2600 // case only the first call produces any effect. For streams >= 1, which
2601 // are created dynamically, this is called by the routine that allocates
2602 // the stream. Stream 0, which is preallocated, is much harder to deal with
2603 // because any of a number of different calls may be the first call to the
2604 // library. This is handled by just calling plstrm_init() from every
2605 // function that might be called first. Sucks, but it should work.
2606 //--------------------------------------------------------------------------
2607 
2608 void
2610 {
2611  if ( !plsc->initialized )
2612  {
2613  plsc->initialized = 1;
2614 
2615  if ( plsc->cmap0 == NULL )
2616  plspal0( "" );
2617 
2618  if ( plsc->cmap1 == NULL )
2619  plspal1( "", TRUE );
2620 
2621  // Set continuous plots to use the full color map 1 range
2622  plsc->cmap1_min = 0.0;
2623  plsc->cmap1_max = 1.0;
2624  }
2625 
2626  plsc->psdoc = NULL;
2627 }
2628 
2629 //--------------------------------------------------------------------------
2630 // pl_cpcolor
2631 //
2632 // Utility to copy one PLColor to another.
2633 //--------------------------------------------------------------------------
2634 
2635 void
2637 {
2638  to->r = from->r;
2639  to->g = from->g;
2640  to->b = from->b;
2641  to->a = from->a;
2642 }
2643 
2644 //--------------------------------------------------------------------------
2645 // void plcpstrm
2646 //
2647 // Copies state parameters from the reference stream to the current stream.
2648 // Tell driver interface to map device coordinates unless flags == 1.
2649 //
2650 // This function is used for making save files of selected plots (e.g.
2651 // from the TK driver). After initializing, you can get a copy of the
2652 // current plot to the specified device by switching to this stream and
2653 // issuing a plcpstrm() and a plreplot(), with calls to plbop() and
2654 // pleop() as appropriate. The plot buffer must have previously been
2655 // enabled (done automatically by some display drivers, such as X).
2656 //--------------------------------------------------------------------------
2657 
2658 void
2659 c_plcpstrm( PLINT iplsr, PLINT flags )
2660 {
2661  int i;
2662  PLStream *plsr;
2663 
2664  plsr = pls[iplsr];
2665  if ( plsr == NULL )
2666  {
2667  fprintf( stderr, "plcpstrm: stream %d not in use\n", (int) iplsr );
2668  return;
2669  }
2670 
2671 // May be debugging
2672 
2673  plsc->debug = plsr->debug;
2674 
2675 // Plot buffer -- need to copy file pointer so that plreplot() works
2676 // This also prevents inadvertent writes into the plot buffer
2677 
2678 #ifdef BUFFERED_FILE
2679  plsc->plbufFile = plsr->plbufFile;
2680 #else
2681  plsc->plbuf_buffer_grow = plsr->plbuf_buffer_grow;
2682  plsc->plbuf_buffer_size = plsr->plbuf_buffer_size;
2683  plsc->plbuf_top = plsr->plbuf_top;
2684  plsc->plbuf_readpos = plsr->plbuf_readpos;
2685  if ( ( plsc->plbuf_buffer = malloc( plsc->plbuf_buffer_size ) ) == NULL )
2686  plexit( "plcpstrm: Error allocating plot buffer." );
2687  memcpy( plsc->plbuf_buffer, plsr->plbuf_buffer, plsr->plbuf_top );
2688 #endif
2689 
2690 // Driver interface
2691 // Transformation must be recalculated in current driver coordinates
2692 
2693  if ( plsr->difilt & PLDI_PLT )
2694  plsdiplt( plsr->dipxmin, plsr->dipymin, plsr->dipxmax, plsr->dipymax );
2695 
2696  if ( plsr->difilt & PLDI_DEV )
2697  plsdidev( plsr->mar, plsr->aspect, plsr->jx, plsr->jy );
2698 
2699  if ( plsr->difilt & PLDI_ORI )
2700  plsdiori( plsr->diorot );
2701 
2702 // Map device coordinates
2703 
2704  if ( !( flags & 0x01 ) )
2705  {
2706  pldebug( "plcpstrm", "mapping parameters: %d %d %d %d %f %f\n",
2707  plsr->phyxmi, plsr->phyxma, plsr->phyymi, plsr->phyyma,
2708  plsr->xpmm, plsr->ypmm );
2709  plsdimap( plsr->phyxmi, plsr->phyxma, plsr->phyymi, plsr->phyyma,
2710  plsr->xpmm, plsr->ypmm );
2711  }
2712 
2713 // current color
2714 
2715  pl_cpcolor( &plsc->curcolor, &plsr->curcolor );
2716 
2717 // cmap 0
2718 
2719  plsc->icol0 = plsr->icol0;
2720  plsc->ncol0 = plsr->ncol0;
2721  if ( plsc->cmap0 != NULL )
2722  free( (void *) plsc->cmap0 );
2723 
2724  if ( ( plsc->cmap0 = (PLColor *) calloc( 1, (size_t) plsc->ncol0 * sizeof ( PLColor ) ) ) == NULL )
2725  {
2726  plexit( "c_plcpstrm: Insufficient memory" );
2727  }
2728 
2729  for ( i = 0; i < plsc->ncol0; i++ )
2730  pl_cpcolor( &plsc->cmap0[i], &plsr->cmap0[i] );
2731 
2732 // cmap 1
2733 
2734  plsc->icol1 = plsr->icol1;
2735  plsc->ncol1 = plsr->ncol1;
2736  plsc->cmap1_min = plsr->cmap1_min;
2737  plsc->cmap1_max = plsr->cmap1_max;
2738  if ( plsc->cmap1 != NULL )
2739  free( (void *) plsc->cmap1 );
2740 
2741  if ( ( plsc->cmap1 = (PLColor *) calloc( 1, (size_t) plsc->ncol1 * sizeof ( PLColor ) ) ) == NULL )
2742  {
2743  plexit( "c_plcpstrm: Insufficient memory" );
2744  }
2745 
2746  for ( i = 0; i < plsc->ncol1; i++ )
2747  pl_cpcolor( &plsc->cmap1[i], &plsr->cmap1[i] );
2748 
2749 // Initialize if it hasn't been done yet.
2750 
2751  if ( plsc->level == 0 )
2752  plinit();
2753 }
2754 
2755 //--------------------------------------------------------------------------
2756 // pllib_devinit()
2757 //
2758 // Does preliminary setup of device driver.
2759 //
2760 // This function (previously plGetDev) used to be what is now shown as
2761 // plSelectDev below. However, the situation is a bit more complicated now in
2762 // the dynloadable drivers era. We now have to:
2763 //
2764 // 1) Make sure the dispatch table is initialized to the union of static
2765 // drivers and available dynamic drivers (done from pllib_init now).
2766 // 2) Allow the user to select the desired device.
2767 // 3) Initialize the dispatch table entries for the selected device, in the
2768 // case that it is a dynloadable driver that has not yet been loaded.
2769 //
2770 // Also made non-static, in order to allow some device calls to be made prior
2771 // to calling plinit(). E.g. plframe needs to tell the X driver to create its
2772 // internal data structure during widget construction time (using the escape
2773 // function), but doesn't call plinit() until the plframe is actually mapped.
2774 //--------------------------------------------------------------------------
2775 
2776 void
2778 {
2779  if ( plsc->dev_initialized )
2780  return;
2781  plsc->dev_initialized = 1;
2782 
2783  plSelectDev();
2784 
2785  plLoadDriver();
2786 
2787 // offset by one since table is zero-based, but input list is not
2788  plsc->dispatch_table = dispatch_table[plsc->device - 1];
2789 }
2790 
2792 {
2793  static int inited = 0;
2794  static int inBuildTree = 0;
2795 
2796  if ( inited == 0 )
2797  {
2798  char currdir[PLPLOT_MAX_PATH];
2799  char builddir[PLPLOT_MAX_PATH];
2800 
2801  if ( getcwd( currdir, PLPLOT_MAX_PATH ) == NULL )
2802  {
2803  pldebug( "plInBuildTree():", "Not enough buffer space" );
2804  }
2805  else
2806  {
2807  // The chdir / getcwd call is to ensure we get the physical
2808  // path without any symlinks
2809  // Ignore error in chdir - build tree may not exist
2810  if ( chdir( BUILD_DIR ) == 0 )
2811  {
2812  if ( getcwd( builddir, PLPLOT_MAX_PATH ) == NULL )
2813  {
2814  pldebug( "plInBuildTree():", "Not enough buffer space" );
2815  }
2816  else
2817  {
2818  // On Windows the first character is the drive letter - it is case-insensitive
2819  if ( strncmp( builddir + 1, currdir + 1, strlen( builddir + 1 ) ) == 0 &&
2820  tolower( builddir[0] ) == tolower( currdir[0] ) )
2821  {
2822  inBuildTree = 1;
2823  }
2824  }
2825  if ( chdir( currdir ) != 0 )
2826  pldebug( "plInBuildTree():", "Unable to chdir to current directory" );
2827  }
2828  }
2829  inited = 1;
2830  }
2831  return inBuildTree;
2832 }
2833 
2834 #ifdef ENABLE_DYNDRIVERS
2835 
2836 const char*
2837 plGetDrvDir()
2838 {
2839  const char* drvdir;
2840 
2841 // Get drivers directory in PLPLOT_DRV_DIR or DRV_DIR,
2842 // on this order
2843 //
2844 
2845  if ( plInBuildTree() == 1 )
2846  {
2847  drvdir = BUILD_DIR "/drivers";
2848  pldebug( "plGetDrvDir", "Using %s as the driver directory.\n", drvdir );
2849  }
2850  else
2851  {
2852  pldebug( "plGetDrvDir", "Trying to read env var PLPLOT_DRV_DIR\n" );
2853  drvdir = getenv( "PLPLOT_DRV_DIR" );
2854 
2855  if ( drvdir == NULL )
2856  {
2857  pldebug( "plGetDrvDir",
2858  "Will use drivers dir: " DRV_DIR "\n" );
2859  drvdir = DRV_DIR;
2860  }
2861  }
2862 
2863  return drvdir;
2864 }
2865 
2866 #endif
2867 
2868 
2869 //--------------------------------------------------------------------------
2870 // void plInitDispatchTable()
2871 //
2872 // ...
2873 //--------------------------------------------------------------------------
2874 
2875 static int plDispatchSequencer( const void *p1, const void *p2 )
2876 {
2877  const PLDispatchTable* t1 = *(const PLDispatchTable * const *) p1;
2878  const PLDispatchTable* t2 = *(const PLDispatchTable * const *) p2;
2879 
2880 // printf( "sorting: t1.name=%s t1.seq=%d t2.name=%s t2.seq=%d\n",
2881 // t1->pl_DevName, t1->pl_seq, t2->pl_DevName, t2->pl_seq );
2882 
2883  return t1->pl_seq - t2->pl_seq;
2884 }
2885 
2886 static void
2888 {
2889  int n;
2890 
2891 #ifdef ENABLE_DYNDRIVERS
2892  char buf[BUFFER2_SIZE];
2893  const char * drvdir;
2894  char *devnam, *devdesc, *devtype, *driver, *tag, *seqstr;
2895  int seq;
2896  int i, j, driver_found, done = 0;
2897  FILE *fp_drvdb = NULL;
2898  DIR * dp_drvdir = NULL;
2899  struct dirent* entry;
2900  // lt_dlhandle dlhand;
2901 
2902  // Make sure driver counts are zeroed
2903  npldynamicdevices = 0;
2904  nloadabledrivers = 0;
2905 
2906 // Open a temporary file in which all the plD_DEVICE_INFO_<driver> strings
2907 // will be stored
2908  fp_drvdb = pl_create_tempfile( NULL );
2909  if ( fp_drvdb == NULL )
2910  {
2911  plabort( "plInitDispatchTable: Could not open temporary file" );
2912  return;
2913  }
2914 
2915 // Open the drivers directory
2916  drvdir = plGetDrvDir();
2917  dp_drvdir = opendir( drvdir );
2918  if ( dp_drvdir == NULL )
2919  {
2920  fclose( fp_drvdb );
2921  plabort( "plInitDispatchTable: Could not open drivers directory" );
2922  return;
2923  }
2924 
2925 // Loop over each entry in the drivers directory
2926 
2927  pldebug( "plInitDispatchTable", "Scanning dyndrivers dir\n" );
2928  while ( ( entry = readdir( dp_drvdir ) ) != NULL )
2929  {
2930  char * name = entry->d_name;
2931  // Suffix .driver_info has a length of 12 letters.
2932  size_t len = strlen( name ) - 12;
2933 
2934  pldebug( "plInitDispatchTable",
2935  "Consider file %s\n", name );
2936 
2937 // Only consider entries that have the ".driver_info" suffix
2938  if ( ( len > 0 ) && ( strcmp( name + len, ".driver_info" ) == 0 ) )
2939  {
2940  char path[PLPLOT_MAX_PATH];
2941  FILE * fd;
2942 
2943 // Open the driver's info file
2944  snprintf( path, PLPLOT_MAX_PATH, "%s/%s", drvdir, name );
2945  fd = fopen( path, "r" );
2946  if ( fd == NULL )
2947  {
2948  closedir( dp_drvdir );
2949  fclose( fp_drvdb );
2950  snprintf( buf, BUFFER2_SIZE,
2951  "plInitDispatchTable: Could not open driver info file %s\n",
2952  name );
2953  plabort( buf );
2954  return;
2955  }
2956 
2957 // Each line in the <driver>.driver_info file corresponds to a specific device.
2958 // Write it to the drivers db file and take care of leading newline
2959 // character
2960 
2961  pldebug( "plInitDispatchTable",
2962  "Opened driver info file %s\n", name );
2963  while ( fgets( buf, BUFFER2_SIZE, fd ) != NULL )
2964  {
2965  fprintf( fp_drvdb, "%s", buf );
2966  if ( buf [strlen( buf ) - 1] != '\n' )
2967  fprintf( fp_drvdb, "\n" );
2969  }
2970  fclose( fd );
2971  }
2972  }
2973 
2974 // Make sure that the temporary file containing the drivers database
2975 // is ready to read and close the directory handle
2976  fflush( fp_drvdb );
2977  closedir( dp_drvdir );
2978 
2979 #endif
2980 
2981 // Allocate space for the dispatch table.
2982  if ( ( dispatch_table = (PLDispatchTable **)
2983  malloc( (size_t) ( nplstaticdevices + npldynamicdevices ) * sizeof ( PLDispatchTable * ) ) ) == NULL )
2984  {
2985 #ifdef ENABLE_DYNDRIVERS
2986  fclose( fp_drvdb );
2987 #endif
2988  plexit( "plInitDispatchTable: Insufficient memory" );
2989  }
2990 
2991 // Initialize the dispatch table entries for the static devices by calling
2992 // the dispatch table initialization function for each static device. This
2993 // is the same function that would be called at load time for dynamic
2994 // drivers.
2995 
2996  for ( n = 0; n < nplstaticdevices; n++ )
2997  {
2998  if ( ( dispatch_table[n] = (PLDispatchTable *) malloc( sizeof ( PLDispatchTable ) ) ) == NULL )
2999  {
3000 #ifdef ENABLE_DYNDRIVERS
3001  fclose( fp_drvdb );
3002 #endif
3003  plexit( "plInitDispatchTable: Insufficient memory" );
3004  }
3005 
3007  }
3009 
3010 #ifdef ENABLE_DYNDRIVERS
3011 
3012 // Allocate space for the device and driver specs. We may not use all of
3013 // these driver descriptors, but we obviously won't need more drivers than
3014 // devices...
3015  if ( ( ( loadable_device_list = malloc( (size_t) npldynamicdevices * sizeof ( PLLoadableDevice ) ) ) == NULL ) ||
3016  ( ( loadable_driver_list = malloc( (size_t) npldynamicdevices * sizeof ( PLLoadableDriver ) ) ) == NULL ) )
3017  {
3018  fclose( fp_drvdb );
3019  plexit( "plInitDispatchTable: Insufficient memory" );
3020  }
3021 
3022  rewind( fp_drvdb );
3023 
3024  i = 0;
3025  done = !( i < npldynamicdevices );
3026  while ( !done )
3027  {
3028  char *p = fgets( buf, BUFFER2_SIZE, fp_drvdb );
3029 
3030  if ( p == 0 )
3031  {
3032  done = 1;
3033  continue;
3034  }
3035 
3036  devnam = strtok( buf, ":" );
3037  devdesc = strtok( 0, ":" );
3038  devtype = strtok( 0, ":" );
3039  driver = strtok( 0, ":" );
3040  seqstr = strtok( 0, ":" );
3041  tag = strtok( 0, "\n" );
3042 
3043  if ( devnam == NULL || devdesc == NULL || devtype == NULL || driver == NULL ||
3044  seqstr == NULL || tag == NULL )
3045  {
3046  continue; // Ill-formatted line, most probably not a valid driver information file
3047  }
3048 
3049  seq = atoi( seqstr );
3050 
3051  n = npldrivers++;
3052 
3053  if ( ( dispatch_table[n] = malloc( sizeof ( PLDispatchTable ) ) ) == NULL )
3054  {
3055  fclose( fp_drvdb );
3056  plexit( "plInitDispatchTable: Insufficient memory" );
3057  }
3058 
3059  // Fill in the dispatch table entries.
3060  dispatch_table[n]->pl_MenuStr = plstrdup( devdesc );
3061  dispatch_table[n]->pl_DevName = plstrdup( devnam );
3062  dispatch_table[n]->pl_type = atoi( devtype );
3063  dispatch_table[n]->pl_seq = seq;
3064  dispatch_table[n]->pl_init = 0;
3065  dispatch_table[n]->pl_line = 0;
3066  dispatch_table[n]->pl_polyline = 0;
3067  dispatch_table[n]->pl_eop = 0;
3068  dispatch_table[n]->pl_bop = 0;
3069  dispatch_table[n]->pl_tidy = 0;
3070  dispatch_table[n]->pl_state = 0;
3071  dispatch_table[n]->pl_esc = 0;
3072 
3073  // Add a record to the loadable device list
3074  loadable_device_list[i].devnam = plstrdup( devnam );
3075  loadable_device_list[i].description = plstrdup( devdesc );
3076  loadable_device_list[i].drvnam = plstrdup( driver );
3077  loadable_device_list[i].tag = plstrdup( tag );
3078 
3079  // Now see if this driver has been seen before. If not, add a driver
3080  // entry for it.
3081  driver_found = 0;
3082  for ( j = 0; j < nloadabledrivers; j++ )
3083  if ( strcmp( driver, loadable_driver_list[j].drvnam ) == 0 )
3084  {
3085  driver_found = 1;
3086  break;
3087  }
3088 
3089  if ( !driver_found )
3090  {
3091  loadable_driver_list[nloadabledrivers].drvnam = plstrdup( driver );
3092  loadable_driver_list[nloadabledrivers].dlhand = 0;
3093  nloadabledrivers++;
3094  }
3095 
3096  loadable_device_list[i].drvidx = j;
3097 
3098  // Get ready for next loadable device spec
3099  i++;
3100  }
3101 
3102 // RML: close fp_drvdb
3103  fclose( fp_drvdb );
3104 
3105 #endif
3106 
3107  if ( npldrivers == 0 )
3108  {
3109  npldynamicdevices = 0;
3110  plexit( "No device drivers found - please check the environment variable PLPLOT_DRV_DIR" );
3111  }
3112 
3113 // Finally, we need to sort the list into presentation order, based on the
3114 // sequence number in the dispatch ttable entries.
3115 
3116  qsort( dispatch_table, (size_t) npldrivers, sizeof ( PLDispatchTable* ),
3118 }
3119 
3120 //--------------------------------------------------------------------------
3121 // void plSelectDev()
3122 //
3123 // If the user has not already specified the output device, or the
3124 // one specified is either: (a) not available, (b) "?", or (c) NULL, the
3125 // user is prompted for it.
3126 //
3127 // Prompting quits after 10 unsuccessful tries in case the user has
3128 // run the program in the background with insufficient input.
3129 //--------------------------------------------------------------------------
3130 
3131 static void
3133 {
3134  int dev, i, count;
3135  size_t length;
3136  char response[80];
3137  char * devname_env;
3138 
3139 // If device name is not already specified, try to get it from environment
3140 
3141  if ( plsc->DevName[0] == '\0' )
3142  {
3143  devname_env = getenv( "PLPLOT_DEV" );
3144  if ( devname_env )
3145  {
3146  strncpy( plsc->DevName, devname_env, sizeof ( plsc->DevName ) - 1 );
3147  plsc->DevName[sizeof ( plsc->DevName ) - 1] = '\0';
3148  }
3149  }
3150 
3151 // Device name already specified. See if it is valid.
3152 
3153  if ( *( plsc->DevName ) != '\0' && *( plsc->DevName ) != '?' )
3154  {
3155  length = strlen( plsc->DevName );
3156  for ( i = 0; i < npldrivers; i++ )
3157  {
3158  if ( ( *plsc->DevName == *dispatch_table[i]->pl_DevName ) &&
3159  ( strncmp( plsc->DevName,
3160  dispatch_table[i]->pl_DevName, length ) == 0 ) )
3161  break;
3162  }
3163  if ( i < npldrivers )
3164  {
3165  plsc->device = i + 1;
3166  return;
3167  }
3168  else
3169  {
3170  fprintf( stderr, "Requested device %s not available\n",
3171  plsc->DevName );
3172  }
3173  }
3174 
3175  dev = 0;
3176  count = 0;
3177 
3178  if ( npldrivers == 1 )
3179  dev = 1;
3180 
3181 // User hasn't specified it correctly yet, so we prompt
3182 
3183  while ( dev < 1 || dev > npldrivers )
3184  {
3185  fprintf( stdout, "\nPlotting Options:\n" );
3186  for ( i = 0; i < npldrivers; i++ )
3187  {
3188  fprintf( stdout, " <%2d> %-10s %s\n", i + 1,
3189  dispatch_table[i]->pl_DevName,
3190  dispatch_table[i]->pl_MenuStr );
3191  }
3192  if ( ipls == 0 )
3193  fprintf( stdout, "\nEnter device number or keyword: " );
3194  else
3195  fprintf( stdout, "\nEnter device number or keyword (stream %d): ",
3196  (int) ipls );
3197 
3198  plio_fgets( response, sizeof ( response ), stdin );
3199 
3200  // First check to see if device keyword was entered.
3201  // Final "\n" in response messes things up, so ignore it.
3202 
3203  length = strlen( response );
3204  if ( *( response - 1 + length ) == '\n' )
3205  length--;
3206 
3207  for ( i = 0; i < npldrivers; i++ )
3208  {
3209  if ( !strncmp( response, dispatch_table[i]->pl_DevName,
3210  (unsigned int) length ) )
3211  break;
3212  }
3213  if ( i < npldrivers )
3214  {
3215  dev = i + 1;
3216  }
3217  else
3218  {
3219  if ( ( dev = atoi( response ) ) < 1 )
3220  {
3221  fprintf( stdout, "\nInvalid device: %s", response );
3222  dev = 0;
3223  }
3224  }
3225  if ( count++ > 10 )
3226  plexit( "plSelectDev: Too many tries." );
3227  }
3228  plsc->device = dev;
3229  strcpy( plsc->DevName, dispatch_table[dev - 1]->pl_DevName );
3230 }
3231 
3232 //--------------------------------------------------------------------------
3233 // void plLoadDriver()
3234 //
3235 // Make sure the selected driver is loaded. Static drivers are already
3236 // loaded, but if the user selected a dynamically loadable driver, we may
3237 // have to take care of that now.
3238 //--------------------------------------------------------------------------
3239 
3240 static void
3242 {
3243 #ifdef ENABLE_DYNDRIVERS
3244  int i, drvidx;
3245  char sym[BUFFER_SIZE];
3246  char *tag;
3247 
3248  int n = plsc->device - 1;
3249  PLDispatchTable *dev = dispatch_table[n];
3250  PLLoadableDriver *driver = 0;
3251 
3252 // If the dispatch table is already filled in, then either the device was
3253 // linked in statically, or else perhaps it was already loaded. In either
3254 // case, we have nothing left to do.
3255  if ( dev->pl_init )
3256  return;
3257 
3258  pldebug( "plLoadDriver", "Device not loaded!\n" );
3259 
3260 // Now search through the list of loadable devices, looking for the record
3261 // that corresponds to the requested device.
3262  for ( i = 0; i < npldynamicdevices; i++ )
3263  if ( strcmp( dev->pl_DevName, loadable_device_list[i].devnam ) == 0 )
3264  break;
3265 
3266 // If we couldn't find such a record, then there is some sort of internal
3267 // logic flaw since plSelectDev is supposed to only select a valid device.
3268 //
3269  if ( i == npldynamicdevices )
3270  {
3271  fprintf( stderr, "No such device: %s.\n", dev->pl_DevName );
3272  plexit( "plLoadDriver detected device logic screwup" );
3273  }
3274 
3275 // Note the device tag, and the driver index. Note that a given driver could
3276 // supply multiple devices, each with a unique tag to distinguish the driver
3277 // entry points for the different supported devices.
3278  tag = loadable_device_list[i].tag;
3279  drvidx = loadable_device_list[i].drvidx;
3280 
3281  pldebug( "plLoadDriver", "tag=%s, drvidx=%d\n", tag, drvidx );
3282 
3283  driver = &loadable_driver_list[drvidx];
3284 
3285 // Load the driver if it hasn't been loaded yet.
3286  if ( !driver->dlhand )
3287  {
3288  char drvspec[ DRVSPEC_SIZE ];
3289 #if defined ( LTDL_WIN32 ) || defined ( __CYGWIN__ )
3290  snprintf( drvspec, DRVSPEC_SIZE, "%s", driver->drvnam );
3291 #else
3292  snprintf( drvspec, DRVSPEC_SIZE, "%s/%s", plGetDrvDir(), driver->drvnam );
3293 #endif // LTDL_WIN32
3294 
3295  pldebug( "plLoadDriver", "Trying to load %s on %s\n",
3296  driver->drvnam, drvspec );
3297 
3298  driver->dlhand = lt_dlopenext( drvspec );
3299 
3300  // A few of our drivers do not depend on other libraries. So
3301  // allow them to be completely removed by plend to give clean
3302  // valgrind results. However, the (large) remainder of our
3303  // drivers do depend on other libraries so mark them resident
3304  // to prevent problems with atexit handlers / library
3305  // reinitialisation such as those seen with qt and cairo
3306  // drivers.
3307  if ( !( strcmp( driver->drvnam, "mem" ) == 0 ||
3308  strcmp( driver->drvnam, "null" ) == 0 ||
3309  strcmp( driver->drvnam, "plmeta" ) == 0 ||
3310  strcmp( driver->drvnam, "ps" ) == 0 ||
3311  strcmp( driver->drvnam, "svg" ) == 0 ||
3312  strcmp( driver->drvnam, "xfig" ) == 0 ) )
3313  lt_dlmakeresident( driver->dlhand );
3314  }
3315 
3316 // If it still isn't loaded, then we're doomed.
3317  if ( !driver->dlhand )
3318  {
3319  pldebug( "plLoadDriver", "lt_dlopenext failed because of "
3320  "the following reason:\n%s\n", lt_dlerror() );
3321  fprintf( stderr, "Unable to load driver: %s.\n", driver->drvnam );
3322  plexit( "Unable to load driver" );
3323  }
3324 
3325 // Now we are ready to ask the driver's device dispatch init function to
3326 // initialize the entries in the dispatch table.
3327 
3328  snprintf( sym, BUFFER_SIZE, "plD_dispatch_init_%s", tag );
3329  {
3330  PLDispatchInit dispatch_init = (PLDispatchInit) lt_dlsym( driver->dlhand, sym );
3331  if ( !dispatch_init )
3332  {
3333  fprintf( stderr,
3334  "Unable to locate dispatch table initialization function for driver: %s.\n",
3335  driver->drvnam );
3336  return;
3337  }
3338 
3339  ( *dispatch_init )( dev );
3340  }
3341 #endif
3342 }
3343 
3344 //--------------------------------------------------------------------------
3345 // void plfontld()
3346 //
3347 // Load specified font set.
3348 //--------------------------------------------------------------------------
3349 
3350 void
3352 {
3353  if ( ifont != 0 )
3354  ifont = 1;
3355 
3356  if ( plsc->level > 0 )
3357  plfntld( ifont );
3358  else
3359  initfont = ifont;
3360 }
3361 
3362 //--------------------------------------------------------------------------
3363 // void plreplot()
3364 //
3365 // Replays contents of plot buffer to current device/file.
3366 //--------------------------------------------------------------------------
3367 
3368 void
3369 c_plreplot( void )
3370 {
3371 #ifdef BUFFERED_FILE
3372  if ( plsc->plbufFile != NULL )
3373  {
3374 #else
3375  if ( plsc->plbuf_buffer != NULL )
3376  {
3377 #endif
3378  plRemakePlot( plsc );
3379  }
3380  else
3381  {
3382  plwarn( "plreplot: plot buffer not available" );
3383  }
3384 }
3385 
3386 //--------------------------------------------------------------------------
3387 // void plgFileDevs()
3388 //
3389 // Returns a list of file-oriented device names and their menu strings,
3390 // for use in a graphical interface. The caller must allocate enough
3391 // space for (*p_menustr) and (*p_devname) to hold a pointer for each
3392 // device -- 20 or so is plenty. E.g. char *menustr[20]. The size of
3393 // these arrays should be passed in *p_ndev, which, on exit, holds the
3394 // number of devices actually present.
3395 //--------------------------------------------------------------------------
3396 
3397 void
3398 plgFileDevs( const char ***p_menustr, const char ***p_devname, int *p_ndev )
3399 {
3400  plgdevlst( *p_menustr, *p_devname, p_ndev, 0 );
3401 }
3402 
3403 //--------------------------------------------------------------------------
3404 // void plgDevs()
3405 //
3406 // Like plgFileDevs(), but returns names and menu strings for all devices.
3407 //--------------------------------------------------------------------------
3408 
3409 void
3410 plgDevs( const char ***p_menustr, const char ***p_devname, int *p_ndev )
3411 {
3412  plgdevlst( *p_menustr, *p_devname, p_ndev, -1 );
3413 }
3414 
3415 static void
3416 plgdevlst( const char **p_menustr, const char **p_devname, int *p_ndev, int type )
3417 {
3418  int i, j;
3419 
3420  pllib_init();
3421 
3422  for ( i = j = 0; i < npldrivers; i++ )
3423  {
3424  if ( type < 0 || dispatch_table[i]->pl_type == type )
3425  {
3426  p_menustr[j] = dispatch_table[i]->pl_MenuStr;
3427  p_devname[j] = dispatch_table[i]->pl_DevName;
3428  if ( ++j + 1 >= *p_ndev )
3429  {
3430  plwarn( "plgdevlst: too many devices" );
3431  break;
3432  }
3433  }
3434  }
3435  p_menustr[j] = NULL;
3436  p_devname[j] = NULL;
3437  *p_ndev = j;
3438 }
3439 
3440 //--------------------------------------------------------------------------
3441 // Various external access routines.
3442 //--------------------------------------------------------------------------
3443 
3444 // Get output device parameters.
3445 
3446 void
3447 c_plgpage( PLFLT *p_xp, PLFLT *p_yp,
3448  PLINT *p_xleng, PLINT *p_yleng, PLINT *p_xoff, PLINT *p_yoff )
3449 {
3450  *p_xp = plsc->xdpi;
3451  *p_yp = plsc->ydpi;
3452  *p_xleng = plsc->xlength;
3453  *p_yleng = plsc->ylength;
3454  *p_xoff = plsc->xoffset;
3455  *p_yoff = plsc->yoffset;
3456 }
3457 
3458 // Set output device parameters. Usually ignored by the driver.
3459 
3460 void
3461 c_plspage( PLFLT xp, PLFLT yp, PLINT xleng, PLINT yleng, PLINT xoff, PLINT yoff )
3462 {
3463  if ( plsc->level > 0 )
3464  plwarn( "calling plspage() after plinit() may give unpredictable results" );
3465 
3466  if ( xp )
3467  plsc->xdpi = xp;
3468  if ( yp )
3469  plsc->ydpi = yp;
3470 
3471  if ( xleng )
3472  plsc->xlength = xleng;
3473  if ( yleng )
3474  plsc->ylength = yleng;
3475 
3476  if ( xoff )
3477  plsc->xoffset = xoff;
3478  if ( yoff )
3479  plsc->yoffset = yoff;
3480 
3481  plsc->pageset = 1;
3482 }
3483 
3484 // Set the number of subwindows in x and y
3485 
3486 void
3488 {
3489  if ( nx > 0 )
3490  plsc->nsubx = nx;
3491  if ( ny > 0 )
3492  plsc->nsuby = ny;
3493 
3494 // Force a page advance
3495 
3496  if ( plsc->level > 0 )
3497  {
3498  plP_subpInit();
3499 //AWI plP_eop();
3500 // plP_bop();
3501  }
3502 }
3503 
3504 // Set the device (keyword) name
3505 
3506 void
3507 c_plsdev( const char *devname )
3508 {
3509  if ( plsc->level > 0 )
3510  {
3511  plwarn( "plsdev: Must be called before plinit." );
3512  return;
3513  }
3514  if ( devname != NULL )
3515  {
3516  strncpy( plsc->DevName, devname, sizeof ( plsc->DevName ) - 1 );
3517  plsc->DevName[sizeof ( plsc->DevName ) - 1] = '\0';
3518  }
3519 }
3520 
3521 // Get the current device (keyword) name
3522 // Note: you MUST have allocated space for this (80 characters is safe)
3523 
3524 void
3525 c_plgdev( char *p_dev )
3526 {
3527  strcpy( p_dev, plsc->DevName );
3528 }
3529 
3530 // Set the memory area to be plotted (with the 'mem' driver) as the 'dev'
3531 // member of the stream structure. Also set the number
3532 // of pixels in the memory passed in in 'plotmem'.
3533 // Plotmem is a block of memory maxy by maxx by 3 bytes long, say:
3534 // 480 x 640 x 3 (Y, X, RGB)
3535 //
3536 // This memory will be freed by the user!
3537 //
3538 
3539 void
3540 c_plsmem( PLINT maxx, PLINT maxy, void *plotmem )
3541 {
3542  plsc->dev = plotmem;
3543  plsc->dev_mem_alpha = 0;
3544  plP_setphy( 0, maxx, 0, maxy );
3545 }
3546 
3547 // Same as plsmem, but the buffer is (Y, X, RGBA)
3548 
3549 void
3550 c_plsmema( PLINT maxx, PLINT maxy, void *plotmem )
3551 {
3552  plsc->dev = plotmem;
3553  plsc->dev_mem_alpha = 1;
3554  plP_setphy( 0, maxx, 0, maxy );
3555 }
3556 
3557 // Get the current stream pointer
3558 
3559 void
3560 plgpls( PLStream **p_pls )
3561 {
3562  *p_pls = plsc;
3563 }
3564 
3565 // Get the (current) run level.
3566 // Valid settings are:
3567 // 0 uninitialized
3568 // 1 initialized
3569 // 2 viewport defined
3570 // 3 world coords defined
3571 //
3572 
3573 void
3574 c_plglevel( PLINT *p_level )
3575 {
3576  *p_level = plsc->level;
3577 }
3578 
3579 // Set the function pointer for the keyboard event handler
3580 
3581 void
3582 plsKeyEH( void ( *KeyEH )( PLGraphicsIn *, void *, int * ),
3583  void *KeyEH_data )
3584 {
3585  plsc->KeyEH = KeyEH;
3586  plsc->KeyEH_data = KeyEH_data;
3587 }
3588 
3589 // Set the function pointer for the (mouse) button event handler
3590 
3591 void
3592 plsButtonEH( void ( *ButtonEH )( PLGraphicsIn *, void *, int * ),
3593  void *ButtonEH_data )
3594 {
3595  plsc->ButtonEH = ButtonEH;
3596  plsc->ButtonEH_data = ButtonEH_data;
3597 }
3598 
3599 // Sets an optional user bop handler.
3600 
3601 void
3602 plsbopH( void ( *handler )( void *, int * ), void *handler_data )
3603 {
3604  plsc->bop_handler = handler;
3605  plsc->bop_data = handler_data;
3606 }
3607 
3608 // Sets an optional user eop handler.
3609 
3610 void
3611 plseopH( void ( *handler )( void *, int * ), void *handler_data )
3612 {
3613  plsc->eop_handler = handler;
3614  plsc->eop_data = handler_data;
3615 }
3616 
3617 // Set the variables to be used for storing error info
3618 
3619 void
3620 plsError( PLINT *errcode, char *errmsg )
3621 {
3622  if ( errcode != NULL )
3623  plsc->errcode = errcode;
3624 
3625  if ( errmsg != NULL )
3626  plsc->errmsg = errmsg;
3627 }
3628 
3629 // Set orientation. Must be done before calling plinit.
3630 
3631 void
3633 {
3634  plsdiori( (PLFLT) ori );
3635 }
3636 
3637 //
3638 // Set pen width. Can be done any time, but before calling plinit is best
3639 // since otherwise it may be volatile (i.e. reset on next page advance).
3640 // If width < 0 or is unchanged by the call, nothing is done.
3641 //
3642 
3643 void
3645 {
3646  if ( width != plsc->width && width >= 0. )
3647  {
3648  plsc->width = width;
3649 
3650  if ( plsc->level > 0 )
3651  {
3652  if ( !plsc->widthlock )
3654  }
3655  }
3656 }
3657 
3658 // Set the output file pointer
3659 
3660 void
3661 plgfile( FILE **p_file )
3662 {
3663  *p_file = plsc->OutFile;
3664 }
3665 
3666 // Get the output file pointer
3667 
3668 void
3669 plsfile( FILE *file )
3670 {
3671  plsc->OutFile = file;
3672 }
3673 
3674 // Get the (current) output file name. Must be preallocated to >=80 bytes
3675 // Beyond that, I truncate it. You have been warned.
3676 
3677 void
3678 c_plgfnam( char *fnam )
3679 {
3680  if ( fnam == NULL )
3681  {
3682  plabort( "filename string must be preallocated to >=80 bytes" );
3683  return;
3684  }
3685 
3686  *fnam = '\0';
3687  if ( plsc->FileName != NULL )
3688  {
3689  strncpy( fnam, plsc->FileName, 79 );
3690  fnam[79] = '\0';
3691  }
3692 }
3693 
3694 // Set the output file name.
3695 
3696 void
3697 c_plsfnam( const char *fnam )
3698 {
3699  plP_sfnam( plsc, fnam );
3700 }
3701 
3702 // Set the pause (on end-of-page) status
3703 
3704 void
3706 {
3707  plsc->nopause = !p;
3708 }
3709 
3710 // Set the floating point precision (in number of places) in numeric labels.
3711 
3712 void
3713 c_plprec( PLINT setp, PLINT prec )
3714 {
3715  plsc->setpre = setp;
3716  plsc->precis = prec;
3717 }
3718 
3719 // Get the floating point precision (in number of places) in numeric labels.
3720 
3721 void
3722 plP_gprec( PLINT *p_setp, PLINT *p_prec )
3723 {
3724  *p_setp = plsc->setpre;
3725  *p_prec = plsc->precis;
3726 }
3727 
3728 const char *
3730 {
3731  return (const char *) plsc->timefmt;
3732 }
3733 
3734 //
3735 // Set the escape character for text strings.
3736 // From C you can pass as a character, from Fortran it needs to be the decimal
3737 // ASCII value. Only selected characters are allowed to prevent the user from
3738 // shooting himself in the foot (a '\' isn't allowed since it conflicts with
3739 // C's use of backslash as a character escape).
3740 //
3741 
3742 void
3743 c_plsesc( char esc )
3744 {
3745  switch ( esc )
3746  {
3747  case '!': // ASCII 33
3748  case '#': // ASCII 35
3749  case '$': // ASCII 36
3750  case '%': // ASCII 37
3751  case '&': // ASCII 38
3752  case '*': // ASCII 42
3753  case '@': // ASCII 64
3754  case '^': // ASCII 94
3755  case '~': // ASCII 126
3756  plsc->esc = esc;
3757  break;
3758 
3759  default:
3760  plwarn( "plsesc: Invalid escape character, ignoring." );
3761  }
3762 }
3763 
3764 // Get the escape character for text strings.
3765 
3766 void
3767 plgesc( char *p_esc )
3768 {
3769  if ( plsc->esc == '\0' )
3770  plsc->esc = '#';
3771 
3772  *p_esc = plsc->esc;
3773 }
3774 
3775 // Set the FCI (font characterization integer) for unicode-enabled device
3776 // drivers.
3777 //
3778 void
3780 {
3781  // Always mark FCI as such.
3782  plsc->fci = fci | PL_FCI_MARK;
3783 }
3784 
3785 // Get the FCI (font characterization integer) for unicode-enabled device
3786 // drivers.
3787 //
3788 void
3790 {
3791  // Always mark FCI as such.
3792  *p_fci = plsc->fci | PL_FCI_MARK;
3793 }
3794 // Store hex digit value shifted to the left by hexdigit hexadecimal digits
3795 // into pre-existing FCI.
3796 //
3797 void
3798 plP_hex2fci( unsigned char hexdigit, unsigned char hexpower, PLUNICODE *pfci )
3799 {
3800  PLUNICODE mask;
3801  hexpower = hexpower & PL_FCI_HEXPOWER_MASK;
3802  mask = ~( ( (PLUNICODE) PL_FCI_HEXDIGIT_MASK ) << ( (PLUNICODE) 4 * hexpower ) );
3803  *pfci = *pfci & mask;
3804  mask = ( ( (PLUNICODE) ( hexdigit & PL_FCI_HEXDIGIT_MASK ) ) << ( 4 * hexpower ) );
3805  *pfci = *pfci | mask;
3806 }
3807 
3808 // Retrieve hex digit value from FCI that is masked out and shifted to the
3809 // right by hexpower hexadecimal digits.
3810 void
3811 plP_fci2hex( PLUNICODE fci, unsigned char *phexdigit, unsigned char hexpower )
3812 {
3813  PLUNICODE mask;
3814  hexpower = hexpower & PL_FCI_HEXPOWER_MASK;
3815  mask = ( ( (PLUNICODE) PL_FCI_HEXPOWER_MASK ) << ( (PLUNICODE) ( 4 * hexpower ) ) );
3816  *phexdigit = (unsigned char) ( ( fci & mask ) >>
3817  ( (PLUNICODE) ( 4 * hexpower ) ) );
3818 }
3819 
3820 // Get the current library version number
3821 // Note: you MUST have allocated space for this (80 characters is safe)
3822 void
3823 c_plgver( char *p_ver )
3824 {
3825  strcpy( p_ver, PLPLOT_VERSION );
3826 }
3827 
3828 // Set inferior X window
3829 
3830 void
3831 plsxwin( PLINT window_id )
3832 {
3833  plsc->window_id = window_id;
3834 }
3835 
3836 //--------------------------------------------------------------------------
3837 // These set/get information for family files, and may be called prior
3838 // to plinit to set up the necessary parameters. Arguments:
3839 //
3840 // fam familying flag (boolean)
3841 // num member number
3842 // bmax maximum member size
3843 //--------------------------------------------------------------------------
3844 
3845 // Get family file parameters
3846 
3847 void
3848 c_plgfam( PLINT *p_fam, PLINT *p_num, PLINT *p_bmax )
3849 {
3850  *p_fam = plsc->family;
3851  *p_num = plsc->member;
3852  *p_bmax = plsc->bytemax;
3853 }
3854 
3855 // Set family file parameters
3856 
3857 void
3858 c_plsfam( PLINT fam, PLINT num, PLINT bmax )
3859 {
3860  if ( plsc->level > 0 )
3861  plwarn( "plsfam: Must be called before plinit." );
3862 
3863  if ( fam >= 0 )
3864  plsc->family = fam;
3865  if ( num >= 0 )
3866  plsc->member = num;
3867  if ( bmax >= 0 )
3868  plsc->bytemax = bmax;
3869 }
3870 
3871 // Advance to the next family file on the next new page
3872 
3873 void
3874 c_plfamadv( void )
3875 {
3876  plsc->famadv = 1;
3877 }
3878 
3879 //--------------------------------------------------------------------------
3880 // Interface routines for axis labling parameters.
3881 // See pldtik.c for more info.
3882 //--------------------------------------------------------------------------
3883 
3884 // Get x axis labeling parameters
3885 
3886 void
3887 c_plgxax( PLINT *p_digmax, PLINT *p_digits )
3888 {
3889  *p_digmax = plsc->xdigmax;
3890  *p_digits = plsc->xdigits;
3891 }
3892 
3893 // Set x axis labeling parameters
3894 
3895 void
3896 c_plsxax( PLINT digmax, PLINT digits )
3897 {
3898  plsc->xdigmax = digmax;
3899  plsc->xdigits = digits;
3900 }
3901 
3902 // Get y axis labeling parameters
3903 
3904 void
3905 c_plgyax( PLINT *p_digmax, PLINT *p_digits )
3906 {
3907  *p_digmax = plsc->ydigmax;
3908  *p_digits = plsc->ydigits;
3909 }
3910 
3911 // Set y axis labeling parameters
3912 
3913 void
3914 c_plsyax( PLINT digmax, PLINT digits )
3915 {
3916  plsc->ydigmax = digmax;
3917  plsc->ydigits = digits;
3918 }
3919 
3920 // Get z axis labeling parameters
3921 
3922 void
3923 c_plgzax( PLINT *p_digmax, PLINT *p_digits )
3924 {
3925  *p_digmax = plsc->zdigmax;
3926  *p_digits = plsc->zdigits;
3927 }
3928 
3929 // Set z axis labeling parameters
3930 
3931 void
3932 c_plszax( PLINT digmax, PLINT digits )
3933 {
3934  plsc->zdigmax = digmax;
3935  plsc->zdigits = digits;
3936 }
3937 
3938 // Get character default height and current (scaled) height
3939 
3940 void
3941 c_plgchr( PLFLT *p_def, PLFLT *p_ht )
3942 {
3943  *p_def = plsc->chrdef;
3944  *p_ht = plsc->chrht;
3945 }
3946 
3947 // Get viewport boundaries in normalized device coordinates
3948 
3949 void
3950 c_plgvpd( PLFLT *p_xmin, PLFLT *p_xmax, PLFLT *p_ymin, PLFLT *p_ymax )
3951 {
3952  *p_xmin = plsc->vpdxmi;
3953  *p_xmax = plsc->vpdxma;
3954  *p_ymin = plsc->vpdymi;
3955  *p_ymax = plsc->vpdyma;
3956 }
3957 
3958 // Get viewport boundaries in world coordinates
3959 
3960 void
3961 c_plgvpw( PLFLT *p_xmin, PLFLT *p_xmax, PLFLT *p_ymin, PLFLT *p_ymax )
3962 {
3963  *p_xmin = plsc->vpwxmi;
3964  *p_xmax = plsc->vpwxma;
3965  *p_ymin = plsc->vpwymi;
3966  *p_ymax = plsc->vpwyma;
3967 }
3968 
3969 // Get the viewport boundaries in world coordinates, expanded slightly
3970 void
3971 plP_xgvpw( PLFLT *p_xmin, PLFLT *p_xmax, PLFLT *p_ymin, PLFLT *p_ymax )
3972 {
3973  PLFLT dx, dy;
3974 
3975  dx = ( plsc->vpwxma - plsc->vpwxmi ) * 1.0e-5;
3976  dy = ( plsc->vpwyma - plsc->vpwymi ) * 1.0e-5;
3977 
3978  // The plot window is made slightly larger than requested so that
3979  // the end limits will be on the graph
3980 
3981  *p_xmin = plsc->vpwxmi - dx;
3982  *p_xmax = plsc->vpwxma + dx;
3983  *p_ymin = plsc->vpwymi - dy;
3984  *p_ymax = plsc->vpwyma + dy;
3985 }
3986 
3987 //--------------------------------------------------------------------------
3988 // These should not be called by the user.
3989 //--------------------------------------------------------------------------
3990 
3991 // Get x-y domain in world coordinates for 3d plots
3992 
3993 void
3994 plP_gdom( PLFLT *p_xmin, PLFLT *p_xmax, PLFLT *p_ymin, PLFLT *p_ymax )
3995 {
3996  *p_xmin = plsc->domxmi;
3997  *p_xmax = plsc->domxma;
3998  *p_ymin = plsc->domymi;
3999  *p_ymax = plsc->domyma;
4000 }
4001 
4002 // Get vertical (z) scale parameters for 3-d plot
4003 
4004 void
4005 plP_grange( PLFLT *p_zscl, PLFLT *p_zmin, PLFLT *p_zmax )
4006 {
4007  *p_zscl = plsc->zzscl;
4008  *p_zmin = plsc->ranmi;
4009  *p_zmax = plsc->ranma;
4010 }
4011 
4012 // Get parameters used in 3d plots
4013 
4014 void
4015 plP_gw3wc( PLFLT *p_dxx, PLFLT *p_dxy, PLFLT *p_dyx, PLFLT *p_dyy, PLFLT *p_dyz )
4016 {
4017  *p_dxx = plsc->cxx;
4018  *p_dxy = plsc->cxy;
4019  *p_dyx = plsc->cyx;
4020  *p_dyy = plsc->cyy;
4021  *p_dyz = plsc->cyz;
4022 }
4023 
4024 // Get clip boundaries in physical coordinates
4025 
4026 void
4027 plP_gclp( PLINT *p_ixmin, PLINT *p_ixmax, PLINT *p_iymin, PLINT *p_iymax )
4028 {
4029  *p_ixmin = plsc->clpxmi;
4030  *p_ixmax = plsc->clpxma;
4031  *p_iymin = plsc->clpymi;
4032  *p_iymax = plsc->clpyma;
4033 }
4034 
4035 // Set clip boundaries in physical coordinates
4036 
4037 void
4038 plP_sclp( PLINT ixmin, PLINT ixmax, PLINT iymin, PLINT iymax )
4039 {
4040  plsc->clpxmi = ixmin;
4041  plsc->clpxma = ixmax;
4042  plsc->clpymi = iymin;
4043  plsc->clpyma = iymax;
4044 }
4045 
4046 // Get physical device limits in physical coordinates
4047 
4048 void
4049 plP_gphy( PLINT *p_ixmin, PLINT *p_ixmax, PLINT *p_iymin, PLINT *p_iymax )
4050 {
4051  *p_ixmin = plsc->phyxmi;
4052  *p_ixmax = plsc->phyxma;
4053  *p_iymin = plsc->phyymi;
4054  *p_iymax = plsc->phyyma;
4055 }
4056 
4057 // Get number of subpages on physical device and current subpage
4058 
4059 void
4060 plP_gsub( PLINT *p_nx, PLINT *p_ny, PLINT *p_cs )
4061 {
4062  *p_nx = plsc->nsubx;
4063  *p_ny = plsc->nsuby;
4064  *p_cs = plsc->cursub;
4065 }
4066 
4067 // Set number of subpages on physical device and current subpage
4068 
4069 void
4070 plP_ssub( PLINT nx, PLINT ny, PLINT cs )
4071 {
4072  plsc->nsubx = nx;
4073  plsc->nsuby = ny;
4074  plsc->cursub = cs;
4075 }
4076 
4077 // Get number of pixels to a millimeter
4078 
4079 void
4080 plP_gpixmm( PLFLT *p_x, PLFLT *p_y )
4081 {
4082  *p_x = plsc->xpmm;
4083  *p_y = plsc->ypmm;
4084 }
4085 
4086 // All the drivers call this to set physical pixels/mm.
4087 
4088 void
4089 plP_setpxl( PLFLT xpmm, PLFLT ypmm )
4090 {
4091  plsc->xpmm = xpmm;
4092  plsc->ypmm = ypmm;
4093  plsc->umx = (PLINT) ( 1000.0 / plsc->xpmm );
4094  plsc->umy = (PLINT) ( 1000.0 / plsc->ypmm );
4095 }
4096 
4097 // Sets up physical limits of plotting device.
4098 
4099 void
4101 {
4102  if ( xmin > xmax || ymin > ymax )
4103  plexit( "plP_setphy: device minima must not exceed maxima" );
4104 
4105  plsc->phyxmi = xmin;
4106  plsc->phyxma = xmax;
4107  plsc->phyymi = ymin;
4108  plsc->phyyma = ymax;
4109  plsc->phyxlen = xmax - xmin;
4110  plsc->phyylen = ymax - ymin;
4111 }
4112 
4113 //--------------------------------------------------------------------------
4114 // void c_plscompression()
4115 //
4116 // Set compression.
4117 // Has to be done before plinit.
4118 //--------------------------------------------------------------------------
4119 
4120 void
4121 c_plscompression( PLINT compression )
4122 {
4123  if ( plsc->level <= 0 )
4124  {
4125  plsc->dev_compression = compression;
4126  }
4127 }
4128 
4129 //--------------------------------------------------------------------------
4130 // void c_plgcompression()
4131 //
4132 // Get compression
4133 //--------------------------------------------------------------------------
4134 
4135 void
4136 c_plgcompression( PLINT *compression )
4137 {
4138  *compression = plsc->dev_compression;
4139 }
4140 
4141 
4142 //--------------------------------------------------------------------------
4143 // void plP_getinitdriverlist()
4144 //
4145 // Check to see if a driver/stream has been initialised
4146 // Returns a space separated list of matches streams/drivers
4147 // If more than one stream uses the same device, then the device name
4148 // will be returned for each stream.
4149 // Caller must allocate enough memory for "names" to hold the answer.
4150 //--------------------------------------------------------------------------
4151 
4152 void
4154 {
4155  int i;
4156 
4157  for ( i = 0; i < PL_NSTREAMS; ++i )
4158  {
4159  if ( pls[i] != NULL )
4160  {
4161  if ( i == 0 )
4162  strcpy( names, pls[i]->DevName );
4163  else
4164  {
4165  strcat( names, " " );
4166  strcat( names, pls[i]->DevName );
4167  }
4168  }
4169  else
4170  break;
4171  }
4172 }
4173 
4174 
4175 //--------------------------------------------------------------------------
4176 // PLINT plP_checkdriverinit()
4177 //
4178 // Checks from a list of given drivers which ones have been initialised
4179 // and returns the number of devices matching the list, or -1 if in error.
4180 // Effectively returns the number of streams matching the given stream.
4181 //--------------------------------------------------------------------------
4182 
4184 {
4185  char *buff;
4186  char *tok = NULL;
4187  PLINT ret = 0; // set up return code to 0, the value if no devices match
4188 
4189  buff = (char *) malloc( (size_t) PL_NSTREAMS * 8 ); // Allocate enough memory for 8
4190  // characters for each possible stream
4191 
4192  if ( buff != NULL )
4193  {
4194  memset( buff, 0, PL_NSTREAMS * 8 ); // Make sure we clear it
4195  plP_getinitdriverlist( buff ); // Get the list of initialised devices
4196 
4197  for ( tok = strtok( buff, " ," ); // Check each device against the "name"
4198  tok; tok = strtok( 0, " ," ) ) // supplied to the subroutine
4199  {
4200  if ( strstr( names, tok ) != NULL ) // Check to see if the device has been initialised
4201  {
4202  ret++; // Bump the return code if it has
4203  }
4204  }
4205  free( buff ); // Clear up that memory we allocated
4206  }
4207  else
4208  ret = -1; // Error flag
4209 
4210  return ( ret );
4211 }
4212 
4213 
4214 //--------------------------------------------------------------------------
4215 // plP_image
4216 //
4217 // Author: Alessandro Mirone, Nov 2001
4218 //
4219 // Updated by Hezekiah Carty, Mar 2008.
4220 // - Added support for pltr callback
4221 // - Commented out the "dev_fastimg" rendering path
4222 //
4223 //--------------------------------------------------------------------------
4224 
4225 void
4227  void ( *pltr )( PLFLT, PLFLT, PLFLT *, PLFLT *, PLPointer ), PLPointer pltr_data )
4228 {
4229  plsc->page_status = DRAWING;
4230 
4231  plimageslow( z, nx, ny, xmin, ymin, dx, dy, pltr, pltr_data );
4232 
4233  //
4234  // COMMENTED OUT by Hezekiah Carty, March 2008
4235  // The current dev_fastimg rendering method does not work as-is with
4236  // the plimagefr coordinate transform support.
4237  // This is hopefully temporary, until the dev_fastimg rendering
4238  // path can be updated to work with the new plimage internals.
4239  // Until then, all plimage* rendering is done by the plimageslow
4240  // rendering path.
4241  //
4242 #if 0 // BEGIN dev_fastimg COMMENT
4243  PLINT i, npts;
4244  short *xscl, *yscl;
4245  int plbuf_write;
4246 
4247  plsc->page_status = DRAWING;
4248 
4249  if ( plsc->dev_fastimg == 0 )
4250  {
4251  plimageslow( x, y, z, nx - 1, ny - 1,
4252  xmin, ymin, dx, dy, zmin, zmax );
4253  return;
4254  }
4255 
4256  if ( plsc->plbuf_write )
4257  {
4258  IMG_DT img_dt;
4259 
4260  img_dt.xmin = xmin;
4261  img_dt.ymin = ymin;
4262  img_dt.dx = dx;
4263  img_dt.dy = dy;
4264 
4265  plsc->dev_ix = x;
4266  plsc->dev_iy = y;
4267  plsc->dev_z = z;
4268  plsc->dev_nptsX = nx;
4269  plsc->dev_nptsY = ny;
4270  plsc->dev_zmin = zmin;
4271  plsc->dev_zmax = zmax;
4272 
4273  plbuf_esc( plsc, PLESC_IMAGE, &img_dt );
4274  }
4275 
4276  // avoid re-saving plot buffer while in plP_esc()
4277  plbuf_write = plsc->plbuf_write;
4278  plsc->plbuf_write = 0;
4279 
4280  npts = nx * ny;
4281  if ( plsc->difilt ) // isn't this odd? when replaying the plot buffer, e.g., when resizing the window, difilt() is caled again! the plot buffer should already contain the transformed data--it would save a lot of time! (and allow for differently oriented plots when in multiplot mode)
4282  {
4283  PLINT clpxmi, clpxma, clpymi, clpyma;
4284 
4285  if ( ( ( xscl = (short *) malloc( nx * ny * sizeof ( short ) ) ) == NULL ) ||
4286  ( ( yscl = (short *) malloc( nx * ny * sizeof ( short ) ) ) == NULL ) )
4287  {
4288  plexit( "plP_image: Insufficient memory" );
4289  }
4290 
4291  for ( i = 0; i < npts; i++ )
4292  {
4293  xscl[i] = x[i];
4294  yscl[i] = y[i];
4295  }
4296  sdifilt( xscl, yscl, npts, &clpxmi, &clpxma, &clpymi, &clpyma );
4297  plsc->imclxmin = clpxmi;
4298  plsc->imclymin = clpymi;
4299  plsc->imclxmax = clpxma;
4300  plsc->imclymax = clpyma;
4301  grimage( xscl, yscl, z, nx, ny );
4302  free( xscl );
4303  free( yscl );
4304  }
4305  else
4306  {
4307  plsc->imclxmin = plsc->phyxmi;
4308  plsc->imclymin = plsc->phyymi;
4309  plsc->imclxmax = plsc->phyxma;
4310  plsc->imclymax = plsc->phyyma;
4311  grimage( x, y, z, nx, ny );
4312  }
4313  plsc->plbuf_write = plbuf_write;
4314 #endif // END dev_fastimg COMMENT
4315 }
4316 
4317 //--------------------------------------------------------------------------
4318 // plstransform
4319 //
4320 // Set a universal coordinate transform function which will be applied to all
4321 // plotted items.
4322 //--------------------------------------------------------------------------
4323 void
4324 c_plstransform( void ( *coordinate_transform )( PLFLT, PLFLT, PLFLT*, PLFLT*, PLPointer ), PLPointer coordinate_transform_data )
4325 {
4326  plsc->coordinate_transform = coordinate_transform;
4327  plsc->coordinate_transform_data = coordinate_transform_data;
4328 }