1 /*
2 * R : A Computer Language for Statistical Data Analysis
3 * Copyright (C) 2004-2020 The R Core Team
4 * Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka
5 * Copyright (C) 1998--2003 Guido Masarotto and Brian Ripley
6 * Copyright (C) 2004 The R Foundation
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, a copy is available at
20 * https://www.R-project.org/Licenses/
21 */
22
23 /*--- Device Driver for Windows; this file started from
24 * src/unix/X11/devX11.c
25 */
26
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30
31 #define R_USE_SIGNALS 1
32 #include <Defn.h>
33 #define R_USE_PROTOTYPES 1
34 #include <R_ext/GraphicsEngine.h>
35
36 #include <Fileio.h>
37 #include <stdio.h>
38 #include "opt.h"
39 #include "graphapp/ga.h"
40 #include "graphapp/stdimg.h"
41 #include "console.h"
42 #include "rui.h"
43 #define WIN32_LEAN_AND_MEAN 1
44 /* Mingw-w64 defines this to be 0x0502 */
45 #ifndef _WIN32_WINNT
46 # define _WIN32_WINNT 0x0500
47 #endif
48 #include <windows.h>
49 #include "devWindows.h"
50 #define DEVWINDOWS 1
51 #include "grDevices.h"
52
53 /* there are conflicts with Rmath.h */
54 #define imax2 Rf_imax2
55 #define imin2 Rf_imin2
56 int imax2(int, int);
57 int imin2(int, int);
58
59 #ifdef ENABLE_NLS
60 #define G_(String) libintl_dgettext("RGui", String)
61 #define GN_(String) gettext_noop (String)
62 #else /* not NLS */
63 #define G_(String) (String)
64 #define GN_(String) String
65 #endif
66
67 /* from extra.c */
68 extern size_t Rf_utf8towcs(wchar_t *wc, const char *s, size_t n);
69
70 static
71 Rboolean GADeviceDriver(pDevDesc dd, const char *display, double width,
72 double height, double pointsize,
73 Rboolean recording, int resize, int bg, int canvas,
74 double gamma, int xpos, int ypos, Rboolean buffered,
75 SEXP psenv, Rboolean restoreConsole,
76 const char *title, Rboolean clickToConfirm,
77 Rboolean fillOddEven, const char *family, int quality);
78
79
80 /* a colour used to represent the background on png if transparent
81 NB: used as RGB and BGR
82 */
83
84 #define PNG_TRANS 0xfdfefd
85
86 /* these really are globals: per machine, not per window */
87 static double user_xpinch = 0.0, user_ypinch = 0.0;
88
GAsetunits(double xpinch,double ypinch)89 static void GAsetunits(double xpinch, double ypinch)
90 {
91 user_xpinch = xpinch;
92 user_ypinch = ypinch;
93 }
94
GArgb(int color,double gamma)95 static rgb GArgb(int color, double gamma)
96 {
97 int r, g, b;
98 if (gamma != 1) {
99 r = (int) (255 * pow(R_RED(color) / 255.0, gamma));
100 g = (int) (255 * pow(R_GREEN(color) / 255.0, gamma));
101 b = (int) (255 * pow(R_BLUE(color) / 255.0, gamma));
102 } else {
103 r = R_RED(color);
104 g = R_GREEN(color);
105 b = R_BLUE(color);
106 }
107 return rgb(r, g, b);
108 }
109
110
111
112 /********************************************************/
113 /* This device driver has been documented so that it be */
114 /* used as a template for new drivers */
115 /********************************************************/
116
117 #define MM_PER_INCH 25.4 /* mm -> inch conversion */
118
119 #define TRACEDEVGA(a)
120 #define CLIP if (xd->clip.width>0) gsetcliprect(_d,xd->clip)
121
122 static drawing _d;
123
124 #define DRAW(a) {if(xd->kind != SCREEN) {_d=xd->gawin; CLIP; a;} else {_d=xd->bm; CLIP; a; if(!xd->buffered) {_d=xd->gawin; CLIP; a;} }}
125
126 #define SHOW if(xd->kind==SCREEN) {drawbits(xd); GALastUpdate = GetTickCount();}
127 #define SH if(xd->kind==SCREEN && xd->buffered && GA_xd) GA_Timer(xd)
128
129
130 #define SF 20 /* scrollbar resolution */
131
132 /********************************************************/
133 /* Each driver can have its own device-specic graphical */
134 /* parameters and resources. these should be wrapped */
135 /* in a structure (gadesc in devWindows.h) */
136 /* and attached to the overall device description via */
137 /* the dd->deviceSpecific pointer */
138 /* NOTE that there are generic graphical parameters */
139 /* which must be set by the device driver, but are */
140 /* common to all device types (see Graphics.h) */
141 /* so go in the GPar structure rather than this device- */
142 /* specific structure */
143 /********************************************************/
144
getregion(gadesc * xd)145 static rect getregion(gadesc *xd)
146 {
147 rect r = getrect(xd->bm);
148 r.x += max(0, xd->xshift);
149 r.y += max(0, xd->yshift);
150 r.width = min(r.width, xd->showWidth);
151 r.height = min(r.height, xd->showHeight);
152 return r;
153 }
154
155 /* Update the screen 100ms after last plotting call or 500ms after
156 last update (by default).
157
158 This runs on (asynchronous) timers for each device.
159 Macro SHOW does an immediate update, and records the update
160 in GALastUpdate.
161 SHOW is called for expose and mouse events, and newpage.
162
163 Macro SH calls GA_Timer. If it is more than 500ms since the last
164 update it does an update; otherwise it sets a timer running for
165 100ms. In either case cancels any existing timer.
166 SH is called for the graphics primitives. (This could probably be
167 replace by calling from Mode(0)).
168
169 There are two conditions:
170 (i) xd->buffered is true, which is a per-device condition.
171 (ii) GA_xd is non-null. This is used to inhibit updates during shutdown
172 of the device, and also (post 2.14.0) when the device is held.
173 */
174
175 static UINT_PTR TimerNo = 0;
176 static gadesc *GA_xd;
177 static DWORD GALastUpdate = 0;
178
drawbits(gadesc * xd)179 static void drawbits(gadesc *xd)
180 {
181 if (xd)
182 gbitblt(xd->gawin, xd->bm, pt(0,0), getrect(xd->bm));
183 }
184
185 static VOID CALLBACK
GA_timer_proc(HWND hwnd,UINT message,UINT_PTR tid,DWORD time)186 GA_timer_proc(HWND hwnd, UINT message, UINT_PTR tid, DWORD time)
187 {
188 if ((message != WM_TIMER) || tid != TimerNo || !GA_xd) return;
189 drawbits(GA_xd);
190 GALastUpdate = time;
191 }
192
GA_Timer(gadesc * xd)193 static void GA_Timer(gadesc *xd)
194 {
195 DWORD now = GetTickCount();
196 if(TimerNo != 0) KillTimer(0, TimerNo);
197 if(now > GALastUpdate + xd->timesince) {
198 drawbits(xd);
199 GALastUpdate = now;
200 } else {
201 GA_xd = xd;
202 TimerNo = SetTimer((HWND)0, (UINT_PTR)0, (UINT) xd->timeafter,
203 GA_timer_proc);
204 }
205 }
206
GA_holdflush(pDevDesc dd,int level)207 static int GA_holdflush(pDevDesc dd, int level)
208 {
209 gadesc *xd = (gadesc *) dd->deviceSpecific;
210 if(!xd->buffered) return 0;
211 int old = xd->holdlevel;
212 xd->holdlevel += level;
213 if(xd->holdlevel <= 0) xd->holdlevel = 0;
214 if(xd->holdlevel == 0) {
215 GA_xd = xd;
216 gsetcursor(xd->gawin, ArrowCursor);
217 drawbits(GA_xd);
218 GALastUpdate = GetTickCount();
219 }
220 if (old == 0 && xd->holdlevel > 0) {
221 if(TimerNo != 0) KillTimer(0, TimerNo);
222 drawbits(xd);
223 GA_xd = NULL;
224 gsetcursor(xd->gawin, WatchCursor);
225 }
226 return xd->holdlevel;
227 }
228
229
230 /********************************************************/
231 /* There are a number of actions that every device */
232 /* driver is expected to perform (even if, in some */
233 /* cases it does nothing - just so long as it doesn't */
234 /* crash !). this is how the graphics engine interacts */
235 /* with each device. Each action will be documented */
236 /* individually. */
237 /* hooks for these actions must be set up when the */
238 /* device is first created */
239 /********************************************************/
240
241 /* Device Driver Actions */
242
243 static void GA_Activate(pDevDesc dd);
244 static void GA_Circle(double x, double y, double r,
245 const pGEcontext gc,
246 pDevDesc dd);
247 static void GA_Clip(double x0, double x1, double y0, double y1,
248 pDevDesc dd);
249 static void GA_Close(pDevDesc dd);
250 static void GA_Deactivate(pDevDesc dd);
251 static void GA_eventHelper(pDevDesc dd, int code);
252 static Rboolean GA_Locator(double *x, double *y, pDevDesc dd);
253 static void GA_Line(double x1, double y1, double x2, double y2,
254 const pGEcontext gc,
255 pDevDesc dd);
256 static void GA_MetricInfo(int c,
257 const pGEcontext gc,
258 double* ascent, double* descent,
259 double* width, pDevDesc dd);
260 static void GA_Mode(int mode, pDevDesc dd);
261 static void GA_NewPage(const pGEcontext gc,
262 pDevDesc dd);
263 static void GA_Path(double *x, double *y,
264 int npoly, int *nper,
265 Rboolean winding,
266 const pGEcontext gc,
267 pDevDesc dd);
268 static void GA_Polygon(int n, double *x, double *y,
269 const pGEcontext gc,
270 pDevDesc dd);
271 static void GA_Polyline(int n, double *x, double *y,
272 const pGEcontext gc,
273 pDevDesc dd);
274 static void GA_Rect(double x0, double y0, double x1, double y1,
275 const pGEcontext gc,
276 pDevDesc dd);
277 static void GA_Size(double *left, double *right,
278 double *bottom, double *top,
279 pDevDesc dd);
280 static void GA_Resize(pDevDesc dd);
281 static void GA_Raster(unsigned int *raster, int w, int h,
282 double x, double y,
283 double width, double height,
284 double rot,
285 Rboolean interpolate,
286 const pGEcontext gc, pDevDesc dd);
287 static SEXP GA_Cap(pDevDesc dd);
288 static double GA_StrWidth(const char *str,
289 const pGEcontext gc,
290 pDevDesc dd);
291 static void GA_Text(double x, double y, const char *str,
292 double rot, double hadj,
293 const pGEcontext gc,
294 pDevDesc dd);
295 static Rboolean GA_Open(pDevDesc, gadesc*, const char*, double, double,
296 Rboolean, int, int, double, int, int, int);
297 static Rboolean GA_NewFrameConfirm(pDevDesc);
298 static SEXP GA_setPattern(SEXP pattern, pDevDesc dd);
299 static void GA_releasePattern(SEXP ref, pDevDesc dd);
300 static SEXP GA_setClipPath(SEXP path, SEXP ref, pDevDesc dd);
301 static void GA_releaseClipPath(SEXP ref, pDevDesc dd);
302 static SEXP GA_setMask(SEXP path, SEXP ref, pDevDesc dd);
303 static void GA_releaseMask(SEXP ref, pDevDesc dd);
304
305
306 /********************************************************/
307 /* end of list of required device driver actions */
308 /********************************************************/
309
310 //#include "rbitmap.h"
311 extern int
312 R_SaveAsPng(void *d, int width, int height,
313 unsigned int (*gp)(void *, int, int),
314 int bgr, FILE *fp, unsigned int transparent, int res);
315 extern int
316 R_SaveAsJpeg(void *d, int width, int height,
317 unsigned int (*gp)(void *, int, int),
318 int bgr, int quality, FILE *outfile, int res);
319 extern int
320 R_SaveAsTIFF(void *d, int width, int height,
321 unsigned int (*gp)(void *, int, int),
322 int bgr, const char *outfile, int res, int compression);
323 extern int
324 R_SaveAsBmp(void *d, int width, int height,
325 unsigned int (*gp)(void *, int, int), int bgr, FILE *fp, int res);
326 const char * R_pngVersion(void);
327 const char * R_jpegVersion(void);
328 const char * R_tiffVersion(void);
329
330 /* Support Routines */
331
332 static double pixelHeight(drawing d);
333 static double pixelWidth(drawing d);
334 static void SetColor(int, double, gadesc*);
335 static void SetFont(pGEcontext, double, gadesc*);
336 static void SaveAsPng(pDevDesc dd, const char *fn);
337 static void SaveAsJpeg(pDevDesc dd, int quality, const char *fn);
338 static void SaveAsBmp(pDevDesc dd, const char *fn);
339 static void SaveAsTiff(pDevDesc dd, const char *fn);
340 static void SaveAsBitmap(pDevDesc dd, int res);
341
PrivateCopyDevice(pDevDesc dd,pDevDesc ndd,const char * name)342 static void PrivateCopyDevice(pDevDesc dd, pDevDesc ndd, const char *name)
343 {
344 pGEDevDesc gdd;
345 int saveDev = curDevice();
346 gadesc *xd = (gadesc *) dd->deviceSpecific;
347 gsetcursor(xd->gawin, WatchCursor);
348 gsetVar(R_DeviceSymbol, mkString(name), R_BaseEnv);
349 ndd->displayListOn = FALSE;
350 gdd = GEcreateDevDesc(ndd);
351 GEaddDevice(gdd);
352 GEcopyDisplayList(ndevNumber(dd));
353 GEkillDevice(gdd);
354 selectDevice(saveDev);
355 gsetcursor(xd->gawin, ArrowCursor);
356 show(xd->gawin);
357 }
358
SaveAsWin(pDevDesc dd,const char * display,Rboolean restoreConsole)359 static void SaveAsWin(pDevDesc dd, const char *display,
360 Rboolean restoreConsole)
361 {
362 pDevDesc ndd = (pDevDesc) calloc(1, sizeof(DevDesc));
363 if (!ndd) {
364 R_ShowMessage(_("Not enough memory to copy graphics window"));
365 return;
366 }
367 if(!R_CheckDeviceAvailableBool()) {
368 free(ndd);
369 R_ShowMessage(_("No device available to copy graphics window"));
370 return;
371 }
372
373 pGEDevDesc gdd = desc2GEDesc(dd);
374 if (GADeviceDriver(ndd, display,
375 fromDeviceWidth(toDeviceWidth(1.0, GE_NDC, gdd),
376 GE_INCHES, gdd),
377 fromDeviceHeight(toDeviceHeight(-1.0, GE_NDC, gdd),
378 GE_INCHES, gdd),
379 ((gadesc*) dd->deviceSpecific)->basefontsize,
380 0, 1, White, White, 1, NA_INTEGER, NA_INTEGER, FALSE,
381 R_GlobalEnv, restoreConsole, "", FALSE,
382 ((gadesc*) dd->deviceSpecific)->fillOddEven, "",
383 DEFAULT_QUALITY))
384 PrivateCopyDevice(dd, ndd, display);
385 }
386
init_PS_PDF(void)387 static void init_PS_PDF(void)
388 {
389 SEXP call, initS, grNS=R_FindNamespace(mkString("grDevices"));
390
391 initS = findVarInFrame3(grNS, install("initPSandPDFfonts"), TRUE);
392 if(initS == R_UnboundValue)
393 error("missing initPSandPDFfonts() in grDevices namespace: this should not happen");
394 PROTECT(call = lang1(initS));
395 eval(call, R_GlobalEnv);
396 UNPROTECT(1);
397 }
398
399
SaveAsPostscript(pDevDesc dd,const char * fn)400 static void SaveAsPostscript(pDevDesc dd, const char *fn)
401 {
402 SEXP s;
403 pDevDesc ndd = (pDevDesc) calloc(1, sizeof(DevDesc));
404 pGEDevDesc gdd = desc2GEDesc(dd);
405 gadesc *xd = (gadesc *) dd->deviceSpecific;
406 char family[256], encoding[256], paper[256], bg[256], fg[256];
407 const char **afmpaths = NULL;
408
409 if (!ndd) {
410 R_ShowMessage(_("Not enough memory to copy graphics window"));
411 return;
412 }
413 if(!R_CheckDeviceAvailableBool()) {
414 free(ndd);
415 R_ShowMessage(_("No device available to copy graphics window"));
416 return;
417 }
418
419 if(strchr(fn, '%')) error(_("'%%' is not allowed in file name"));
420
421 /* need to initialize PS/PDF font database:
422 also sets .PostScript.Options */
423 init_PS_PDF();
424 /* Set default values and pad with zeroes ... */
425 strncpy(family, "Helvetica", 256);
426 strcpy(encoding, "ISOLatin1.enc");
427 strncpy(paper, "special", 256);
428 strncpy(bg, "transparent", 256);
429 strncpy(fg, "black", 256);
430 /* and then try to get it from .PostScript.Options */
431 s = findVar(install(".PostScript.Options"), xd->psenv);
432 if ((s != R_UnboundValue) && (s != R_NilValue)) {
433 SEXP names = getAttrib(s, R_NamesSymbol);
434 int i, done;
435 for (i = 0, done = 0; (done< 4) && (i < length(s)) ; i++) {
436 if(!strcmp("family", CHAR(STRING_ELT(names, i)))) {
437 strncpy(family, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)), 255);
438 done++;
439 }
440 if(!strcmp("paper", CHAR(STRING_ELT(names, i)))) {
441 strncpy(paper, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)), 255);
442 done++;
443 if(strcmp(paper, "default") == 0)
444 strncpy(paper, "special", 255);
445 }
446 if(!strcmp("bg", CHAR(STRING_ELT(names, i)))) {
447 strncpy(bg, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)), 255);
448 done++;
449 }
450 if(!strcmp("fg", CHAR(STRING_ELT(names, i)))) {
451 strncpy(fg, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)), 255);
452 done++;
453 }
454 }
455 }
456 if (PSDeviceDriver(ndd, fn, paper, family, afmpaths, encoding,
457 bg, fg,
458 fromDeviceWidth(toDeviceWidth(1.0, GE_NDC, gdd),
459 GE_INCHES, gdd),
460 fromDeviceHeight(toDeviceHeight(-1.0, GE_NDC, gdd),
461 GE_INCHES, gdd),
462 (double)0, ((gadesc*) dd->deviceSpecific)->basefontsize,
463 0, 1, 0, "", "R Graphics Output", R_NilValue, "rgb",
464 TRUE, xd->fillOddEven))
465 /* horizontal=F, onefile=F, pagecentre=T, print.it=F */
466 PrivateCopyDevice(dd, ndd, "postscript");
467 }
468
469
SaveAsPDF(pDevDesc dd,const char * fn)470 static void SaveAsPDF(pDevDesc dd, const char *fn)
471 {
472 SEXP s;
473 pDevDesc ndd = (pDevDesc) calloc(1, sizeof(DevDesc));
474 pGEDevDesc gdd = desc2GEDesc(dd);
475 gadesc *xd = (gadesc *) dd->deviceSpecific;
476 char family[256], encoding[256], bg[256], fg[256];
477 const char **afmpaths = NULL;
478 Rboolean useCompression = FALSE;
479
480 if (!ndd) {
481 R_ShowMessage(_("Not enough memory to copy graphics window"));
482 return;
483 }
484 if(!R_CheckDeviceAvailableBool()) {
485 free(ndd);
486 R_ShowMessage(_("No device available to copy graphics window"));
487 return;
488 }
489
490 if(strchr(fn, '%')) error(_("'%%' is not allowed in file name"));
491
492 /* Set default values... */
493 init_PS_PDF();
494 s = findVar(install(".PDF.Options"), xd->psenv);
495 strncpy(family, "Helvetica", 256);
496 strcpy(encoding, "ISOLatin1.enc");
497 strncpy(bg, "transparent", 256);
498 strncpy(fg, "black", 256);
499 /* and then try to get it from .PDF.Options */
500 if ((s != R_UnboundValue) && (s != R_NilValue)) {
501 SEXP names = getAttrib(s, R_NamesSymbol);
502 for (int i = 0; i < length(s) ; i++) {
503 if(!strcmp("family", CHAR(STRING_ELT(names, i))))
504 strncpy(family, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)),255);
505 if(!strcmp("bg", CHAR(STRING_ELT(names, i))))
506 strncpy(bg, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)), 255);
507 if(!strcmp("fg", CHAR(STRING_ELT(names, i))))
508 strncpy(fg, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)), 255);
509 if(!strcmp("compress", CHAR(STRING_ELT(names, i))))
510 useCompression = LOGICAL(VECTOR_ELT(s, i))[0] != 0;
511 }
512 }
513 if (PDFDeviceDriver(ndd, fn, "special", family, afmpaths, encoding,
514 bg, fg,
515 fromDeviceWidth(toDeviceWidth(1.0, GE_NDC, gdd),
516 GE_INCHES, gdd),
517 fromDeviceHeight(toDeviceHeight(-1.0, GE_NDC, gdd),
518 GE_INCHES, gdd),
519 ((gadesc*) dd->deviceSpecific)->basefontsize,
520 1, 0, "R Graphics Output", R_NilValue, 1, 4,
521 "rgb", TRUE, TRUE, xd->fillOddEven, useCompression))
522 PrivateCopyDevice(dd, ndd, "PDF");
523 }
524
525
526 /* Pixel Dimensions (Inches) */
pixelWidth(drawing obj)527 static double pixelWidth(drawing obj)
528 {
529 return ((double) 1) / devicepixelsx(obj);
530 }
531
pixelHeight(drawing obj)532 static double pixelHeight(drawing obj)
533 {
534 return ((double) 1) / devicepixelsy(obj);
535 }
536
537 /* Font information array. */
538 /* Point sizes: 6-24 */
539 /* Faces: plain, bold, oblique, bold-oblique */
540 /* Symbol may be added later */
541
542 #define NFONT 19
543 #define MAXFONT 32
544 static int fontnum;
545 static int fontinitdone = 0;/* in {0,1,2} */
546 static char *fontname[MAXFONT];
547 static int fontstyle[MAXFONT];
548
RStandardFonts()549 static void RStandardFonts()
550 {
551 int i;
552
553 for (i = 0; i < 4; i++) fontname[i] = "Arial";
554 fontname[4] = "Symbol";
555 fontstyle[0] = fontstyle[4] = Plain;
556 fontstyle[1] = Bold;
557 fontstyle[2] = Italic;
558 fontstyle[3] = BoldItalic;
559 fontnum = 5;
560 fontinitdone = 2; /* =fontinit done & fontname must not be
561 free-ed */
562 }
563
564
RFontInit()565 static void RFontInit()
566 {
567 int i, notdone;
568 char *opt[2];
569 char oops[256];
570
571 snprintf(oops, 256, "%s/Rdevga", getenv("R_USER"));
572 notdone = 1;
573 fontnum = 0;
574 fontinitdone = 1;
575 if (!optopenfile(oops)) {
576 snprintf(oops, 256, "%s/etc/Rdevga", getenv("R_HOME"));
577 if (!optopenfile(oops)) {
578 RStandardFonts();
579 notdone = 0;
580 }
581 }
582 while (notdone) {
583 oops[0] = '\0';
584 notdone = optread(opt, ':');
585 if (notdone == 1)
586 snprintf(oops, 256, "[%s] Error at line %d.", optfile(), optline());
587 else if (notdone == 2) {
588 fontname[fontnum] = strdup(opt[0]);
589 if (!fontname[fontnum])
590 strcpy(oops, "Insufficient memory. ");
591 else {
592 if (!strcmpi(opt[1], "plain"))
593 fontstyle[fontnum] = Plain;
594 else if (!strcmpi(opt[1], "bold"))
595 fontstyle[fontnum] = Bold;
596 else if (!strcmpi(opt[1], "italic"))
597 fontstyle[fontnum] = Italic;
598 else if (!strcmpi(opt[1], "bold&italic"))
599 fontstyle[fontnum] = BoldItalic;
600 else
601 snprintf(oops, 256, "Unknown style at line %d. ", optline());
602 fontnum += 1;
603 }
604 }
605 if (oops[0]) {
606 optclosefile();
607 strcat(oops, optfile());
608 strcat(oops, " will be ignored.");
609 R_ShowMessage(oops);
610 for (i = 0; i < fontnum; i++) free(fontname[i]);
611 RStandardFonts();
612 notdone = 0;
613 }
614 if (fontnum == MAXFONT) {
615 optclosefile();
616 notdone = 0;
617 }
618 }
619 }
620
621 /* Return a non-relocatable copy of a string */
622
SaveFontSpec(SEXP sxp,int offset)623 static char *SaveFontSpec(SEXP sxp, int offset)
624 {
625 char *s;
626 if(!isString(sxp) || length(sxp) <= offset)
627 error(_("invalid font specification"));
628 s = R_alloc(strlen(CHAR(STRING_ELT(sxp, offset)))+1, sizeof(char));
629 strcpy(s, CHAR(STRING_ELT(sxp, offset)));
630 return s;
631 }
632
633 /*
634 * Take the fontfamily from a gcontext (which is device-independent)
635 * and convert it into a Windows-specific font description using
636 * the Windows font database (see src/library/grDevices/R/windows/windows.R)
637 *
638 * IF gcontext fontfamily is empty ("")
639 * OR IF can't find gcontext fontfamily in font database
640 * THEN return NULL
641 */
translateFontFamily(const char * family)642 static char* translateFontFamily(const char* family) {
643 SEXP graphicsNS, windowsenv, fontdb, fontnames;
644 int i, nfonts;
645 char* result = NULL;
646 PROTECT_INDEX xpi;
647
648 PROTECT(graphicsNS = R_FindNamespace(ScalarString(mkChar("grDevices"))));
649 PROTECT_WITH_INDEX(windowsenv = findVar(install(".WindowsEnv"),
650 graphicsNS), &xpi);
651 if(TYPEOF(windowsenv) == PROMSXP)
652 REPROTECT(windowsenv = eval(windowsenv, graphicsNS), xpi);
653 PROTECT(fontdb = findVar(install(".Windows.Fonts"), windowsenv));
654 PROTECT(fontnames = getAttrib(fontdb, R_NamesSymbol));
655 nfonts = LENGTH(fontdb);
656 if (strlen(family) > 0) {
657 int found = 0;
658 for (i = 0; i < nfonts && !found; i++) {
659 const char* fontFamily = CHAR(STRING_ELT(fontnames, i));
660 if (strcmp(family, fontFamily) == 0) {
661 found = 1;
662 result = SaveFontSpec(VECTOR_ELT(fontdb, i), 0);
663 }
664 }
665 if (!found)
666 warning(_("font family not found in Windows font database"));
667 }
668 UNPROTECT(4);
669 return result;
670 }
671
672 /* Set the font size and face */
673 /* If the font of this size and at that the specified */
674 /* rotation is not present it is loaded. */
675 /* 0 = plain text, 1 = bold */
676 /* 2 = oblique, 3 = bold-oblique */
677
678 #define SMALLEST 1
679
SetFont(pGEcontext gc,double rot,gadesc * xd)680 static void SetFont(pGEcontext gc, double rot, gadesc *xd)
681 {
682 int size, face = gc->fontface, usePoints;
683 char* fontfamily;
684 double fs = gc->cex * gc->ps;
685 int quality = xd->fontquality;
686
687 usePoints = xd->kind <= METAFILE;
688 if (!usePoints && xd->res_dpi > 0) fs *= xd->res_dpi/72.0;
689 size = fs + 0.5;
690
691 if (face < 1 || face > fontnum) face = 1;
692 if (size < SMALLEST) size = SMALLEST;
693 if (size != xd->fontsize || face != xd->fontface ||
694 rot != xd->fontangle || strcmp(gc->fontfamily, xd->fontfamily)) {
695 if(xd->font) del(xd->font);
696 doevent();
697 /*
698 * If specify family = "", get family from face via Rdevga
699 *
700 * If specify a family and a face in 1 to 4 then get
701 * that family (mapped through WindowsFonts()) and face.
702 *
703 * If specify face > 4 then get font from face via Rdevga
704 * (whether specifed family or not).
705 */
706 char * fm = gc->fontfamily;
707 if (!fm[0]) fm = xd->basefontfamily;
708 fontfamily = translateFontFamily(fm);
709 if (fontfamily && face <= 4) {
710 xd->font = gnewfont2(xd->gawin,
711 fontfamily, fontstyle[face - 1],
712 size, rot, usePoints, quality);
713 } else {
714 xd->font = gnewfont2(xd->gawin,
715 fontname[face - 1], fontstyle[face - 1],
716 size, rot, usePoints, quality);
717 }
718 if (xd->font) {
719 strcpy(xd->fontfamily, gc->fontfamily);
720 xd->fontface = face;
721 xd->fontsize = size;
722 xd->fontangle = rot;
723 } else {
724 /* Fallback: set Arial */
725 if (face > 4) face = 1;
726 xd->font = gnewfont2(xd->gawin,
727 "Arial", fontstyle[face - 1],
728 size, rot, usePoints, quality);
729 if (!xd->font)
730 error("unable to set or substitute a suitable font");
731 xd->fontface = face;
732 xd->fontsize = size;
733 xd->fontangle = rot;
734 strcpy(xd->fontfamily, "Arial");
735 warning("unable to set font: using Arial");
736 }
737 }
738 }
739
740
SetColor(int color,double gamma,gadesc * xd)741 static void SetColor(int color, double gamma, gadesc *xd)
742 {
743 if (color != xd->col) {
744 xd->col = color;
745 xd->fgcolor = GArgb(color, gamma);
746 }
747 }
748
749
750 /*
751 * Some Notes on Line Textures
752 *
753 * Line textures are stored as an array of 4-bit integers within
754 * a single 32-bit word. These integers contain the lengths of
755 * lines to be drawn with the pen alternately down and then up.
756 * The device should try to arrange that these values are measured
757 * in points if possible, although pixels is ok on most displays.
758 *
759 * If newlty contains a line texture description it is decoded
760 * as follows:
761 *
762 * ndash = 0;
763 * for(i=0 ; i<8 && newlty&15 ; i++) {
764 * dashlist[ndash++] = newlty&15;
765 * newlty = newlty>>4;
766 * }
767 * dashlist[0] = length of pen-down segment
768 * dashlist[1] = length of pen-up segment
769 * etc
770 *
771 * An integer containing a zero terminates the pattern. Hence
772 * ndash in this code fragment gives the length of the texture
773 * description. If a description contains an odd number of
774 * elements it is replicated to create a pattern with an
775 * even number of elements. (If this is a pain, do something
776 * different its not crucial).
777 *
778 * 27/5/98 Paul - change to allow lty and lwd to interact:
779 * the line texture is now scaled by the line width so that,
780 * for example, a wide (lwd=2) dotted line (lty=2) has bigger
781 * dots which are more widely spaced. Previously, such a line
782 * would have "dots" which were wide, but not long, nor widely
783 * spaced.
784 * In this driver, done in graphapp/gdraw.c
785 */
786
SetLineStyle(const pGEcontext gc,pDevDesc dd)787 static void SetLineStyle(const pGEcontext gc, pDevDesc dd)
788 {
789 gadesc *xd = (gadesc *) dd->deviceSpecific;
790
791 xd->lty = gc->lty;
792 if(xd->lwdscale != 1.0)
793 xd->lwd = xd->lwdscale * gc->lwd;
794 else xd->lwd = gc->lwd;
795 if(xd->lwd < 1) xd->lwd = 1;
796 switch (gc->lend) {
797 case GE_ROUND_CAP:
798 xd->lend = PS_ENDCAP_ROUND;
799 break;
800 case GE_BUTT_CAP:
801 xd->lend = PS_ENDCAP_FLAT;
802 break;
803 case GE_SQUARE_CAP:
804 xd->lend = PS_ENDCAP_SQUARE;
805 break;
806 default:
807 error(_("invalid line end"));
808 }
809 switch (gc->ljoin) {
810 case GE_ROUND_JOIN:
811 xd->ljoin = PS_JOIN_ROUND;
812 break;
813 case GE_MITRE_JOIN:
814 xd->ljoin = PS_JOIN_MITER;
815 break;
816 case GE_BEVEL_JOIN:
817 xd->ljoin = PS_JOIN_BEVEL;
818 break;
819 default:
820 error(_("invalid line join"));
821 }
822
823 xd->lmitre = gc->lmitre;
824 }
825
826 /* Callback functions */
827
828
HelpResize(window w,rect r)829 static void HelpResize(window w, rect r)
830 {
831 if (AllDevicesKilled) return;
832 {
833 pDevDesc dd = (pDevDesc) getdata(w);
834 gadesc *xd = (gadesc *) dd->deviceSpecific;
835
836 if (r.width) {
837 if ((xd->windowWidth != r.width) ||
838 ((xd->windowHeight != r.height))) {
839 xd->windowWidth = r.width;
840 xd->windowHeight = r.height;
841 xd->resize = TRUE;
842 }
843 }
844 }
845 }
846
HelpClose(window w)847 static void HelpClose(window w)
848 {
849 if (AllDevicesKilled) return;
850 {
851 pDevDesc dd = (pDevDesc) getdata(w);
852 killDevice(ndevNumber(dd));
853 }
854 }
855
HelpExpose(window w,rect r)856 static void HelpExpose(window w, rect r)
857 {
858 if (AllDevicesKilled) return;
859 {
860 pDevDesc dd = (pDevDesc) getdata(w);
861 pGEDevDesc gdd = desc2GEDesc(dd);
862 gadesc *xd = (gadesc *) dd->deviceSpecific;
863
864 if (xd->resize) {
865 GA_Resize(dd);
866 /* avoid trying to replay list if there has been no drawing */
867 if(gdd->dirty) {
868 xd->replaying = TRUE;
869 GEplayDisplayList(gdd);
870 xd->replaying = FALSE;
871 }
872 R_ProcessEvents();
873 } else
874 SHOW;
875 }
876 }
877
HelpMouseClick(window w,int button,point pt)878 static void HelpMouseClick(window w, int button, point pt)
879 {
880 if (AllDevicesKilled) return;
881 {
882 pDevDesc dd = (pDevDesc) getdata(w);
883 gadesc *xd = (gadesc *) dd->deviceSpecific;
884
885 if (!xd->locator && !xd->confirmation && !dd->gettingEvent)
886 return;
887 if (button & LeftButton) {
888 int useBeep = xd->locator &&
889 asLogical(GetOption1(install("locatorBell")));
890 if(useBeep) gabeep();
891 xd->clicked = 1;
892 xd->px = pt.x;
893 xd->py = pt.y;
894 } else
895 xd->clicked = 2;
896 if (dd->gettingEvent) {
897 doMouseEvent(dd, meMouseDown, button, pt.x, pt.y);
898 if (xd->buffered) SHOW;
899 }
900 }
901 }
902
HelpMouseMove(window w,int button,point pt)903 static void HelpMouseMove(window w, int button, point pt)
904 {
905 if (AllDevicesKilled) return;
906 {
907 pDevDesc dd = (pDevDesc) getdata(w);
908 gadesc *xd = (gadesc *) dd->deviceSpecific;
909
910 if (dd->gettingEvent) {
911 doMouseEvent(dd, meMouseMove, button, pt.x, pt.y);
912 if (xd->buffered) SHOW;
913 }
914 }
915 }
916
HelpMouseUp(window w,int button,point pt)917 static void HelpMouseUp(window w, int button, point pt)
918 {
919 if (AllDevicesKilled) return;
920 {
921 pDevDesc dd = (pDevDesc) getdata(w);
922 gadesc *xd = (gadesc *) dd->deviceSpecific;
923
924 if (dd->gettingEvent) {
925 doMouseEvent(dd, meMouseUp,button, pt.x, pt.y);
926 if (xd->buffered) SHOW;
927 }
928 }
929 }
930
menustop(control m)931 static void menustop(control m)
932 {
933 pDevDesc dd = (pDevDesc) getdata(m);
934 gadesc *xd = (gadesc *) dd->deviceSpecific;
935 if (!xd->locator)
936 return;
937 xd->clicked = 2;
938 }
939
menunextplot(control m)940 static void menunextplot(control m)
941 {
942 pDevDesc dd = (pDevDesc) getdata(m);
943 gadesc *xd = (gadesc *) dd->deviceSpecific;
944 xd->clicked = 2;
945 }
946
menufilebitmap(control m)947 static void menufilebitmap(control m)
948 {
949 pDevDesc dd = (pDevDesc) getdata(m);
950 gadesc *xd = (gadesc *) dd->deviceSpecific;
951 char *fn;
952 /* the following use a private hook to set the default extension */
953 if (m == xd->mpng) {
954 setuserfilter("Png files (*.png)\0*.png\0\0");
955 fn = askfilesave(G_("Portable network graphics file"), "|.png");
956 } else if (m == xd->mbmp) {
957 setuserfilter("Windows bitmap files (*.bmp)\0*.bmp\0\0");
958 fn = askfilesave(G_("Windows bitmap file"), "|.bmp");
959 } else if (m == xd->mtiff) {
960 setuserfilter("TIFF files (*.tiff,*.tif)\0*.tiff;*.tif\0\0");
961 fn = askfilesave(G_("TIFF file"), "|.tif");
962 } else {
963 setuserfilter("Jpeg files (*.jpeg,*.jpg)\0*.jpeg;*.jpg\0\0");
964 fn = askfilesave(G_("Jpeg file"), "|.jpg");
965 }
966 if (!fn) return;
967 gsetcursor(xd->gawin, WatchCursor);
968 show(xd->gawin);
969 if (m==xd->mpng) SaveAsPng(dd, fn);
970 else if (m==xd->mbmp) SaveAsBmp(dd, fn);
971 else if (m==xd->mtiff) SaveAsTiff(dd, fn);
972 else if (m==xd->mjpeg50) SaveAsJpeg(dd, 50, fn);
973 else if (m==xd->mjpeg75) SaveAsJpeg(dd, 75, fn);
974 else SaveAsJpeg(dd, 100, fn);
975 gsetcursor(xd->gawin, ArrowCursor);
976 show(xd->gawin);
977 }
978
979
menups(control m)980 static void menups(control m)
981 {
982 pDevDesc dd = (pDevDesc) getdata(m);
983 char *fn;
984
985 setuserfilter("Encapsulated postscript files (*.eps)\0*.eps\0Postscript files (*.ps)\0*.ps\0All files (*.*)\0*.*\0\0");
986 fn = askfilesave(G_("Postscript file"), "|.ps");
987 if (!fn) return;
988 SaveAsPostscript(dd, fn);
989 }
990
991
menupdf(control m)992 static void menupdf(control m)
993 {
994 pDevDesc dd = (pDevDesc) getdata(m);
995 char *fn;
996
997 setuserfilter("PDF files (*.pdf)\0*.pdf\0All files (*.*)\0*.*\0\0");
998 fn = askfilesave(G_("PDF file"), "|.pdf");
999 if (!fn) return;
1000 SaveAsPDF(dd, fn);
1001 }
1002
1003
menuwm(control m)1004 static void menuwm(control m)
1005 {
1006 pDevDesc dd = (pDevDesc) getdata(m);
1007 char display[550], *fn;
1008
1009 setuserfilter("Enhanced metafiles (*.emf)\0*.emf\0All files (*.*)\0*.*\0\0");
1010 fn = askfilesave(G_("Enhanced metafiles"), "|.emf");
1011 if (!fn) return;
1012 if(strlen(fn) > 512) {
1013 askok(G_("file path selected is too long: only 512 bytes are allowed"));
1014 return;
1015 }
1016 snprintf(display, 550, "win.metafile:%s", fn);
1017 SaveAsWin(dd, display, TRUE);
1018 }
1019
1020
menuclpwm(control m)1021 static void menuclpwm(control m)
1022 {
1023 pDevDesc dd = (pDevDesc) getdata(m);
1024 SaveAsWin(dd, "win.metafile", TRUE);
1025 }
1026
menuclpbm(control m)1027 static void menuclpbm(control m)
1028 {
1029 pDevDesc dd = (pDevDesc) getdata(m);
1030 gadesc *xd = (gadesc *) dd->deviceSpecific;
1031
1032 show(xd->gawin);
1033 gsetcursor(xd->gawin, WatchCursor);
1034 copytoclipboard(xd->bm);
1035 gsetcursor(xd->gawin, ArrowCursor);
1036 }
1037
menustayontop(control m)1038 static void menustayontop(control m)
1039 {
1040 pDevDesc dd = (pDevDesc) getdata(m);
1041 gadesc *xd = (gadesc *) dd->deviceSpecific;
1042
1043 BringToTop(xd->gawin, 2);
1044 }
1045
menuprint(control m)1046 static void menuprint(control m)
1047 {
1048 pDevDesc dd = (pDevDesc) getdata(m);
1049 SaveAsWin(dd, "win.print:", TRUE);
1050 }
1051
menuclose(control m)1052 static void menuclose(control m)
1053 {
1054 pDevDesc dd = (pDevDesc) getdata(m);
1055 gadesc *xd = (gadesc *) dd->deviceSpecific;
1056
1057 HelpClose(xd->gawin);
1058 }
1059
grpopupact(control m)1060 static void grpopupact(control m)
1061 {
1062 pDevDesc dd = (pDevDesc) getdata(m);
1063 gadesc *xd = (gadesc *) dd->deviceSpecific;
1064
1065 if (ismdi())
1066 disable(xd->grmenustayontop);
1067 else {
1068 if (isTopmost(xd->gawin))
1069 check(xd->grmenustayontop);
1070 else
1071 uncheck(xd->grmenustayontop);
1072 }
1073 }
1074
1075 /* plot history */
1076
1077 /* NB: this puts .SavedPlots in .GlobalEnv */
1078 #define GROWTH 4
1079 #define GETDL SEXP vDL=findVar(install(".SavedPlots"), R_GlobalEnv)
1080 #define SETDL defineVar(install(".SavedPlots"), vDL, R_GlobalEnv)
1081 /* altered in 1.4.0, as incompatible format */
1082 #define PLOTHISTORYMAGIC 31416
1083 #define pMAGIC (INTEGER(VECTOR_ELT(vDL, 0))[0])
1084 #define pNUMPLOTS (INTEGER(VECTOR_ELT(vDL, 1))[0])
1085 #define pMAXPLOTS (INTEGER(VECTOR_ELT(vDL, 2))[0])
1086 #define pCURRENTPOS (INTEGER(VECTOR_ELT(vDL, 3))[0])
1087 #define pHISTORY (VECTOR_ELT(vDL, 4))
1088 #define SET_pHISTORY(v) (SET_VECTOR_ELT(vDL, 4, v))
1089 #define pCURRENT (VECTOR_ELT(pHISTORY, pCURRENTPOS))
1090 #define pCURRENTdl (VECTOR_ELT(pCURRENT, 0))
1091 #define pCURRENTgp (INTEGER(VECTOR_ELT(pCURRENT, 1)))
1092 #define pCURRENTsnapshot (VECTOR_ELT(pCURRENT, 0))
1093 #define pCHECK if ((TYPEOF(vDL)!=VECSXP)||\
1094 (TYPEOF(VECTOR_ELT(vDL, 0))!=INTSXP) ||\
1095 (LENGTH(VECTOR_ELT(vDL, 0))!=1) ||\
1096 (pMAGIC != PLOTHISTORYMAGIC)) {\
1097 R_ShowMessage(_("plot history seems corrupted"));\
1098 return;}
1099 #define pMOVE(a) {pCURRENTPOS+=a;\
1100 if(pCURRENTPOS<0) pCURRENTPOS=0;\
1101 if(pCURRENTPOS>pNUMPLOTS-1) pCURRENTPOS=pNUMPLOTS-1;\
1102 Replay(dd,vDL);SETDL;}
1103 #define pEXIST ((vDL!=R_UnboundValue) && (vDL!=R_NilValue))
1104 #define pMUSTEXIST if(!pEXIST){R_ShowMessage(_("no plot history!"));return;}
1105
1106
1107
1108
NewPlotHistory(int n)1109 static SEXP NewPlotHistory(int n)
1110 {
1111 SEXP vDL, class;
1112 int i;
1113
1114 PROTECT(vDL = allocVector(VECSXP, 5));
1115 for (i = 0; i < 4; i++)
1116 PROTECT(SET_VECTOR_ELT(vDL, i, allocVector(INTSXP, 1)));
1117 PROTECT(SET_pHISTORY (allocVector(VECSXP, n)));
1118 pMAGIC = PLOTHISTORYMAGIC;
1119 pNUMPLOTS = 0;
1120 pMAXPLOTS = n;
1121 pCURRENTPOS = -1;
1122 for (i = 0; i < n; i++)
1123 SET_VECTOR_ELT(pHISTORY, i, R_NilValue);
1124 PROTECT(class = mkString("SavedPlots"));
1125 classgets(vDL, class);
1126 SETDL;
1127 UNPROTECT(7);
1128 return vDL;
1129 }
1130
GrowthPlotHistory(SEXP vDL)1131 static SEXP GrowthPlotHistory(SEXP vDL)
1132 {
1133 SEXP vOLD;
1134 int i, oldNPlots, oldCurrent;
1135
1136 PROTECT(vOLD = pHISTORY);
1137 oldNPlots = pNUMPLOTS;
1138 oldCurrent = pCURRENTPOS;
1139 PROTECT(vDL = NewPlotHistory(pMAXPLOTS + GROWTH));
1140 for (i = 0; i < oldNPlots; i++)
1141 SET_VECTOR_ELT(pHISTORY, i, VECTOR_ELT(vOLD, i));
1142 pNUMPLOTS = oldNPlots;
1143 pCURRENTPOS = oldCurrent;
1144 SETDL;
1145 UNPROTECT(2);
1146 return vDL;
1147 }
1148
AddtoPlotHistory(SEXP snapshot,int replace)1149 static void AddtoPlotHistory(SEXP snapshot, int replace)
1150 {
1151 int where;
1152 SEXP class;
1153
1154 GETDL;
1155 PROTECT(snapshot);
1156 /* if (dl == R_NilValue) {
1157 R_ShowMessage("Display list is void!");
1158 return;
1159 } */
1160 if (!pEXIST)
1161 vDL = NewPlotHistory(GROWTH);
1162 else if (!replace && (pNUMPLOTS == pMAXPLOTS))
1163 vDL = GrowthPlotHistory(vDL);
1164 PROTECT(vDL);
1165 pCHECK;
1166 if (replace)
1167 where = pCURRENTPOS;
1168 else
1169 where = pNUMPLOTS;
1170
1171 PROTECT(class = mkString("recordedplot"));
1172 classgets(snapshot, class);
1173 SET_VECTOR_ELT(pHISTORY, where, snapshot);
1174 pCURRENTPOS = where;
1175 if (!replace) pNUMPLOTS += 1;
1176 SETDL;
1177 UNPROTECT(3);
1178 }
1179
1180
Replay(pDevDesc dd,SEXP vDL)1181 static void Replay(pDevDesc dd, SEXP vDL)
1182 {
1183 gadesc *xd = (gadesc *) dd->deviceSpecific;
1184
1185 xd->replaying = TRUE;
1186 gsetcursor(xd->gawin, WatchCursor);
1187 GEplaySnapshot(pCURRENT, desc2GEDesc(dd));
1188 xd->replaying = FALSE;
1189 gsetcursor(xd->gawin, ArrowCursor);
1190 }
1191
menurec(control m)1192 static void menurec(control m)
1193 {
1194 pDevDesc dd = (pDevDesc) getdata(m);
1195 gadesc *xd = (gadesc *) dd->deviceSpecific;
1196
1197 if (xd->recording) {
1198 xd->recording = FALSE;
1199 uncheck(m);
1200 } else {
1201 xd->recording = TRUE;
1202 check(m);
1203 }
1204 }
1205
1206
menuadd(control m)1207 static void menuadd(control m)
1208 {
1209 pDevDesc dd = (pDevDesc) getdata(m);
1210 gadesc *xd = (gadesc *) dd->deviceSpecific;
1211
1212 AddtoPlotHistory(GEcreateSnapshot(desc2GEDesc(dd)), 0);
1213 xd->needsave = FALSE;
1214 }
1215
menureplace(control m)1216 static void menureplace(control m)
1217 {
1218 pDevDesc dd = (pDevDesc) getdata(m);
1219
1220 GETDL;
1221 pMUSTEXIST;
1222 pCHECK;
1223 if (pCURRENTPOS < 0) {
1224 R_ShowMessage(G_("No plot to replace!"));
1225 return;
1226 }
1227 AddtoPlotHistory(GEcreateSnapshot(desc2GEDesc(dd)), 1);
1228 }
1229
menunext(control m)1230 static void menunext(control m)
1231 {
1232 pDevDesc dd = (pDevDesc) getdata(m);
1233 gadesc *xd = (gadesc *) dd->deviceSpecific;
1234
1235 GETDL;
1236 if (xd->needsave) return;
1237 pMUSTEXIST;
1238 pCHECK;
1239 if (pCURRENTPOS != (pNUMPLOTS - 1)) pMOVE(1);
1240 PrintWarnings();
1241 }
1242
menuprev(control m)1243 static void menuprev(control m)
1244 {
1245 pDevDesc dd = (pDevDesc) getdata(m);
1246 gadesc *xd = (gadesc *) dd->deviceSpecific;
1247
1248 GETDL;
1249 pMUSTEXIST;
1250 pCHECK;
1251 if (pNUMPLOTS) {
1252 if (xd->recording && xd->needsave) {
1253 pGEDevDesc gdd = desc2GEDesc(dd);
1254 if (gdd->displayList != R_NilValue) {
1255 AddtoPlotHistory(GEcreateSnapshot(gdd), 0);
1256 xd->needsave = FALSE;
1257 vDL = findVar(install(".SavedPlots"), R_GlobalEnv);
1258 /* may have changed vDL pointer */
1259 }
1260 }
1261 pMOVE((xd->needsave) ? 0 : -1);
1262 }
1263 PrintWarnings();
1264 }
1265
menugrclear(control m)1266 static void menugrclear(control m)
1267 {
1268 defineVar(install(".SavedPlots"), R_NilValue, R_GlobalEnv);
1269 }
1270
menugvar(control m)1271 static void menugvar(control m)
1272 {
1273 SEXP vDL;
1274 char *v = askstring(G_("Variable name"), "");
1275 pDevDesc dd = (pDevDesc) getdata(m);
1276
1277 if (!v)
1278 return;
1279 vDL = findVar(install(v), R_GlobalEnv);
1280 pMUSTEXIST;
1281 pCHECK;
1282 if (!pNUMPLOTS) {
1283 R_ShowMessage(G_("Variable doesn't contain any plots!"));
1284 return;
1285 }
1286 pCURRENTPOS = 0;
1287 Replay(dd, vDL);
1288 SETDL;
1289 }
1290
menusvar(control m)1291 static void menusvar(control m)
1292 {
1293 char *v;
1294
1295 GETDL;
1296 pMUSTEXIST;
1297 pCHECK;
1298 v = askstring(G_("Name of variable to save to"), "");
1299 if (!v)
1300 return;
1301 defineVar(install(v), vDL, R_GlobalEnv);
1302 }
1303 /* end of plot history */
1304
menuconsole(control m)1305 static void menuconsole(control m)
1306 {
1307 show(RConsole);
1308 }
1309
menuR(control m)1310 static void menuR(control m)
1311 {
1312 pDevDesc dd = (pDevDesc) getdata(m);
1313 gadesc *xd = (gadesc *) dd->deviceSpecific;
1314 check(xd->mR);
1315 uncheck(xd->mfix);
1316 uncheck(xd->mfit);
1317 xd->resizing = 1;
1318 xd->resize = TRUE;
1319 HelpExpose(m, getrect(xd->gawin));
1320 }
1321
menufit(control m)1322 static void menufit(control m)
1323 {
1324 pDevDesc dd = (pDevDesc) getdata(m);
1325 gadesc *xd = (gadesc *) dd->deviceSpecific;
1326
1327 uncheck(xd->mR);
1328 check(xd->mfit);
1329 uncheck(xd->mfix);
1330 xd->resizing = 2;
1331 xd->resize = TRUE;
1332 HelpExpose(m, getrect(xd->gawin));
1333 }
1334
menufix(control m)1335 static void menufix(control m)
1336 {
1337 pDevDesc dd = (pDevDesc) getdata(m);
1338 gadesc *xd = (gadesc *) dd->deviceSpecific;
1339
1340 uncheck(xd->mR);
1341 uncheck(xd->mfit);
1342 check(xd->mfix);
1343 xd->resizing = 3;
1344 xd->resize = TRUE;
1345 HelpExpose(m, getrect(xd->gawin));
1346 }
1347
getKeyName(int key)1348 static R_KeyName getKeyName(int key)
1349 {
1350 if (F1 <= key && key <= F10) return knF1 + key - F1 ;
1351 else switch (key) {
1352 case LEFT: return knLEFT;
1353 case UP: return knUP;
1354 case RIGHT:return knRIGHT;
1355 case DOWN: return knDOWN;
1356 case PGUP: return knPGUP;
1357 case PGDN: return knPGDN;
1358 case END: return knEND;
1359 case HOME: return knHOME;
1360 case INS: return knINS;
1361 case DEL: return knDEL;
1362 default: return knUNKNOWN;
1363 }
1364 }
1365
CHelpKeyIn(control w,int key)1366 static void CHelpKeyIn(control w, int key)
1367 {
1368 pDevDesc dd = (pDevDesc) getdata(w);
1369 gadesc *xd = (gadesc *) dd->deviceSpecific;
1370
1371 R_KeyName keyname;
1372
1373 if (dd->gettingEvent) {
1374 keyname = getKeyName(key);
1375 if (keyname > knUNKNOWN) {
1376 doKeybd(dd, keyname, NULL);
1377 if (xd->buffered) SHOW;
1378 }
1379 } else {
1380 if (xd->replaying) return;
1381 switch (key) {
1382 case INS:
1383 menuadd(xd->madd);
1384 break;
1385 case PGUP:
1386 menuprev(xd->mprev);
1387 break;
1388 case PGDN:
1389 menunext(xd->mnext);
1390 break;
1391 case ENTER:
1392 xd->enterkey = TRUE;
1393 break;
1394 }
1395 }
1396 }
1397
1398 __declspec(dllimport) extern int UserBreak;
1399
NHelpKeyIn(control w,int key)1400 static void NHelpKeyIn(control w, int key)
1401 {
1402 char keyname[7];
1403
1404 pDevDesc dd = (pDevDesc) getdata(w);
1405 gadesc *xd = (gadesc *) dd->deviceSpecific;
1406
1407 if (dd->gettingEvent) {
1408 if (0 < key && key < 32) {
1409 strcpy(keyname, "ctrl- ");
1410 keyname[5] = (char) (key + 'A' - 1);
1411 } else {
1412 keyname[0] = (char) key;
1413 keyname[1] = '\0';
1414 }
1415 doKeybd(dd, knUNKNOWN, keyname);
1416 if (xd->buffered) SHOW;
1417 } else {
1418 if (xd->replaying) return;
1419 switch (key) {
1420 case '\n': /* ENTER has been translated to newline */
1421 xd->enterkey = TRUE;
1422 return;
1423 case ESC:
1424 UserBreak = TRUE;
1425 return;
1426 }
1427 if (ggetkeystate() != CtrlKey) return;
1428 key = 'A' + key - 1;
1429 if (key == 'C') menuclpbm(xd->mclpbm);
1430 if (desc2GEDesc(dd)->displayList == R_NilValue) return;
1431 if (key == 'W') menuclpwm(xd->mclpwm);
1432 else if (key == 'P') menuprint(xd->mprint);
1433 }
1434 }
1435
mbarf(control m)1436 static void mbarf(control m)
1437 {
1438 pDevDesc dd = (pDevDesc) getdata(m);
1439 gadesc *xd = (gadesc *) dd->deviceSpecific;
1440
1441 GETDL;
1442 if (pEXIST && !xd->replaying) {
1443 enable(xd->mnext);
1444 enable(xd->mprev);
1445 if (pCURRENTPOS >= 0 && desc2GEDesc(dd)->displayList != R_NilValue)
1446 enable(xd->mreplace);
1447 else
1448 disable(xd->mreplace);
1449 enable(xd->msvar);
1450 enable(xd->mclear);
1451 } else {
1452 disable(xd->mnext);
1453 disable(xd->mprev);
1454 disable(xd->mreplace);
1455 disable(xd->msvar);
1456 disable(xd->mclear);
1457 }
1458 if (!xd->replaying)
1459 enable(xd->mgvar);
1460 else
1461 disable(xd->mgvar);
1462 if (!xd->replaying && desc2GEDesc(dd)->displayList != R_NilValue) {
1463 enable(xd->madd);
1464 enable(xd->mprint);
1465 enable(xd->mpng);
1466 enable(xd->mbmp);
1467 enable(xd->mtiff);
1468 enable(xd->mjpeg50);
1469 enable(xd->mjpeg75);
1470 enable(xd->mjpeg100);
1471 enable(xd->mwm);
1472 enable(xd->mps);
1473 enable(xd->mpdf);
1474 enable(xd->mclpwm);
1475 enable(xd->mclpbm);
1476 } else {
1477 disable(xd->madd);
1478 disable(xd->mprint);
1479 disable(xd->msubsave);
1480 disable(xd->mpng);
1481 disable(xd->mbmp);
1482 disable(xd->mtiff);
1483 disable(xd->mjpeg50);
1484 disable(xd->mjpeg75);
1485 disable(xd->mjpeg100);
1486 disable(xd->mwm);
1487 disable(xd->mps);
1488 disable(xd->mpdf);
1489 disable(xd->mclpwm);
1490 disable(xd->mclpbm);
1491 }
1492 draw(xd->mbar);
1493 }
1494
1495
1496 /********************************************************/
1497 /* device_Open is not usually called directly by the */
1498 /* graphics engine; it is usually only called from */
1499 /* the device-driver entry point. */
1500 /* this function should set up all of the device- */
1501 /* specific resources for a new device */
1502 /* this function is given a new structure for device- */
1503 /* specific information AND it must FREE the structure */
1504 /* if anything goes seriously wrong */
1505 /* NOTE that it is perfectly acceptable for this */
1506 /* function to set generic graphics parameters too */
1507 /* (i.e., override the generic parameter settings */
1508 /* which GInit sets up) all at the author's own risk */
1509 /* of course :) */
1510 /********************************************************/
1511
1512 #define MCHECK(m) {if(!(m)) {del(xd->gawin); return 0;}}
1513
devga_sbf(control c,int pos)1514 static void devga_sbf(control c, int pos)
1515 {
1516 pDevDesc dd = (pDevDesc) getdata(c);
1517 gadesc *xd = (gadesc *) dd->deviceSpecific;
1518 if (pos < 0) {
1519 pos = -pos-1;
1520 pos = min(pos*SF, (xd->origWidth - xd->windowWidth + SF-1));
1521 xd->xshift = -pos;
1522 } else {
1523 pos = min(pos*SF, (xd->origHeight - xd->windowHeight + SF-1));
1524 xd->yshift = -pos;
1525 }
1526 xd->resize = 1;
1527 HelpExpose(c, getrect(xd->gawin));
1528 }
1529
1530
1531 static Rboolean
setupScreenDevice(pDevDesc dd,gadesc * xd,double w,double h,Rboolean recording,int resize,int xpos,int ypos)1532 setupScreenDevice(pDevDesc dd, gadesc *xd, double w, double h,
1533 Rboolean recording, int resize, int xpos, int ypos)
1534 {
1535 menu m;
1536 int iw, ih;
1537 int cw, ch;
1538 double dw, dw0, dh, d;
1539 char buf[100];
1540
1541 xd->kind = SCREEN;
1542 if (R_FINITE(user_xpinch) && user_xpinch > 0.0)
1543 dw = dw0 = (int) (w * user_xpinch);
1544 else
1545 dw = dw0 = (int) (w / pixelWidth(NULL));
1546 if (R_FINITE(user_ypinch) && user_ypinch > 0.0)
1547 dh = (int) (h * user_ypinch);
1548 else
1549 dh = (int) (h / pixelHeight(NULL));
1550
1551 if (ismdi() && !isiconic(RFrame)) {
1552 cw = RgetMDIwidth();
1553 ch = RgetMDIheight();
1554 } else {
1555 cw = devicewidth(NULL);
1556 ch = deviceheight(NULL);
1557 }
1558
1559 if (resize != 3) {
1560 if ((dw / cw) > 0.85) {
1561 d = dh / dw;
1562 dw = 0.85 * cw;
1563 dh = d * dw;
1564 }
1565 if ((dh / ch) > 0.85) {
1566 d = dw / dh;
1567 dh = 0.85 * ch;
1568 dw = d * dh;
1569 }
1570 } else {
1571 dw = min(dw, 0.85*cw);
1572 dh = min(dh, 0.85*ch);
1573 }
1574 iw = dw + 0.5;
1575 ih = dh + 0.5;
1576 if (resize == 2) xd->rescale_factor = dw/dw0;
1577 {
1578 int grx, gry;
1579 grx = (xpos == NA_INTEGER) ? Rwin_graphicsx : xpos;
1580 gry = (ypos == NA_INTEGER) ? Rwin_graphicsy : ypos;
1581 if (grx < 0) grx = cw - iw + grx;
1582 if (gry < 0) gry = ch - ih + gry;
1583 if (!(xd->gawin = newwindow("R Graphics",
1584 rect(grx, gry, iw, ih),
1585 Document | StandardWindow | Menubar |
1586 VScrollbar | HScrollbar | CanvasSize)
1587 )) {
1588 warning("unable to open window");
1589 return FALSE;
1590 }
1591 }
1592 gchangescrollbar(xd->gawin, VWINSB, 0, ih/SF-1, ih/SF, 0);
1593 gchangescrollbar(xd->gawin, HWINSB, 0, iw/SF-1, iw/SF, 0);
1594
1595 addto(xd->gawin);
1596 gsetcursor(xd->gawin, ArrowCursor);
1597 if (ismdi()) {
1598 int btsize = 24;
1599 rect r = rect(2, 2, btsize, btsize);
1600 control bt, tb;
1601
1602 MCHECK(tb = newtoolbar(btsize + 4));
1603 gsetcursor(tb, ArrowCursor);
1604 addto(tb);
1605
1606 MCHECK(bt = newtoolbutton(cam_image, r, menuclpwm));
1607 MCHECK(addtooltip(bt, G_("Copy to the clipboard as a metafile")));
1608 gsetcursor(bt, ArrowCursor);
1609 setdata(bt, (void *) dd);
1610 r.x += (btsize + 6);
1611
1612 MCHECK(bt = newtoolbutton(print_image, r, menuprint));
1613 MCHECK(addtooltip(bt, G_("Print")));
1614 gsetcursor(bt, ArrowCursor);
1615 setdata(bt, (void *) dd);
1616 r.x += (btsize + 6);
1617
1618 MCHECK(bt = newtoolbutton(console_image, r, menuconsole));
1619 MCHECK(addtooltip(bt, G_("Return focus to Console")));
1620 gsetcursor(bt, ArrowCursor);
1621 setdata(bt, (void *) dd);
1622 r.x += (btsize + 6);
1623
1624 MCHECK(xd->stoploc = newtoolbutton(stop_image, r, menustop));
1625 MCHECK(addtooltip(xd->stoploc, G_("Stop locator")));
1626 gsetcursor(bt, ArrowCursor);
1627 setdata(xd->stoploc,(void *) dd);
1628 hide(xd->stoploc);
1629 } else
1630 xd->stoploc = NULL;
1631
1632 /* First we prepare 'locator' menubar and popup */
1633 addto(xd->gawin);
1634 MCHECK(xd->mbarloc = newmenubar(NULL));
1635 MCHECK(newmenu(G_("Stop")));
1636 MCHECK(m = newmenuitem(G_("Stop locator"), 0, menustop));
1637 setdata(m, (void *) dd);
1638 MCHECK(xd->locpopup = newpopup(NULL));
1639 MCHECK(m = newmenuitem(G_("Stop"), 0, menustop));
1640 setdata(m, (void *) dd);
1641 MCHECK(newmenuitem(G_("Continue"), 0, NULL));
1642
1643 /* Next the 'Click for next plot' menubar */
1644 MCHECK(xd->mbarconfirm = newmenubar(NULL));
1645 MCHECK(newmenu(G_("Next")));
1646 MCHECK(m = newmenuitem(G_("Next plot"), 0, menunextplot));
1647 setdata(m, (void *) dd);
1648
1649 /* Normal menubar */
1650 MCHECK(xd->mbar = newmenubar(mbarf));
1651 MCHECK(m = newmenu(G_("File")));
1652 MCHECK(xd->msubsave = newsubmenu(m, G_("Save as")));
1653 MCHECK(xd->mwm = newmenuitem("Metafile...", 0, menuwm));
1654 MCHECK(xd->mps = newmenuitem("Postscript...", 0, menups));
1655 MCHECK(xd->mpdf = newmenuitem("PDF...", 0, menupdf));
1656 MCHECK(xd->mpng = newmenuitem("Png...", 0, menufilebitmap));
1657 MCHECK(xd->mbmp = newmenuitem("Bmp...", 0, menufilebitmap));
1658 MCHECK(xd->mtiff = newmenuitem("TIFF...", 0, menufilebitmap));
1659 MCHECK(newsubmenu(xd->msubsave, "Jpeg"));
1660 /* avoid gettext confusion with % */
1661 snprintf(buf, 100, G_("%s quality..."), "50%");
1662 MCHECK(xd->mjpeg50 = newmenuitem(buf, 0, menufilebitmap));
1663 snprintf(buf, 100, G_("%s quality..."), "75%");
1664 MCHECK(xd->mjpeg75 = newmenuitem(buf, 0, menufilebitmap));
1665 snprintf(buf, 100, G_("%s quality..."), "100%");
1666 MCHECK(xd->mjpeg100 = newmenuitem(buf, 0, menufilebitmap));
1667 MCHECK(newsubmenu(m, G_("Copy to the clipboard")));
1668 MCHECK(xd->mclpbm = newmenuitem(G_("as a Bitmap\tCTRL+C"), 0, menuclpbm));
1669 MCHECK(xd->mclpwm = newmenuitem(G_("as a Metafile\tCTRL+W"), 0, menuclpwm));
1670 addto(m);
1671 MCHECK(newmenuitem("-", 0, NULL));
1672 MCHECK(xd->mprint = newmenuitem(G_("Print..."), 'P', menuprint));
1673 MCHECK(newmenuitem("-", 0, NULL));
1674 MCHECK(xd->mclose = newmenuitem(G_("close Device"), 0, menuclose));
1675 MCHECK(newmenu(G_("History")));
1676 MCHECK(xd->mrec = newmenuitem(G_("Recording"), 0, menurec));
1677 if(recording) check(xd->mrec);
1678 MCHECK(newmenuitem("-", 0, NULL));
1679 MCHECK(xd->madd = newmenuitem(G_("Add\tINS"), 0, menuadd));
1680 MCHECK(xd->mreplace = newmenuitem(G_("Replace"), 0, menureplace));
1681 MCHECK(newmenuitem("-", 0, NULL));
1682 MCHECK(xd->mprev = newmenuitem(G_("Previous\tPgUp"), 0, menuprev));
1683 MCHECK(xd->mnext = newmenuitem(G_("Next\tPgDown"), 0, menunext));
1684 MCHECK(newmenuitem("-", 0, NULL));
1685 MCHECK(xd->msvar = newmenuitem(G_("Save to variable..."), 0, menusvar));
1686 MCHECK(xd->mgvar = newmenuitem(G_("Get from variable..."), 0, menugvar));
1687 MCHECK(newmenuitem("-", 0, NULL));
1688 MCHECK(xd->mclear = newmenuitem(G_("Clear history"), 0, menugrclear));
1689 MCHECK(newmenu(G_("Resize")));
1690 MCHECK(xd->mR = newmenuitem(G_("R mode"), 0, menuR));
1691 if(resize == 1) check(xd->mR);
1692 MCHECK(xd->mfit = newmenuitem(G_("Fit to window"), 0, menufit));
1693 if(resize == 2) check(xd->mfit);
1694 MCHECK(xd->mfix = newmenuitem(G_("Fixed size"), 0, menufix));
1695 if(resize == 3) check(xd->mfix);
1696 newmdimenu();
1697
1698 /* Normal popup */
1699 MCHECK(xd->grpopup = newpopup(grpopupact));
1700 setdata(xd->grpopup, (void *) dd);
1701 MCHECK(m = newmenuitem(G_("Copy as metafile"), 0, menuclpwm));
1702 setdata(m, (void *) dd);
1703 MCHECK(m = newmenuitem(G_("Copy as bitmap"), 0, menuclpbm));
1704 setdata(m, (void *) dd);
1705 MCHECK(newmenuitem("-", 0, NULL));
1706 MCHECK(m = newmenuitem(G_("Save as metafile..."), 0, menuwm));
1707 setdata(m, (void *) dd);
1708 MCHECK(m = newmenuitem(G_("Save as postscript..."), 0, menups));
1709 setdata(m, (void *) dd);
1710 MCHECK(newmenuitem("-", 0, NULL));
1711 MCHECK(xd->grmenustayontop = newmenuitem(G_("Stay on top"), 0, menustayontop));
1712 setdata(xd->grmenustayontop, (void *) dd);
1713 MCHECK(newmenuitem("-", 0, NULL));
1714 MCHECK(m = newmenuitem(G_("Print..."), 'P', menuprint));
1715 setdata(m, (void *) dd);
1716 gchangepopup(xd->gawin, xd->grpopup);
1717
1718 MCHECK(xd->bm = newbitmap(getwidth(xd->gawin), getheight(xd->gawin),
1719 getdepth(xd->gawin)));
1720 MCHECK(xd->bm2 = newbitmap(getwidth(xd->gawin), getheight(xd->gawin),
1721 getdepth(xd->gawin)));
1722 gfillrect(xd->gawin, xd->outcolor, getrect(xd->gawin));
1723 gfillrect(xd->bm, xd->outcolor, getrect(xd->bm));
1724 addto(xd->gawin);
1725 setdata(xd->mbar, (void *) dd);
1726 setdata(xd->mpng, (void *) dd);
1727 setdata(xd->mbmp, (void *) dd);
1728 setdata(xd->mtiff, (void *) dd);
1729 setdata(xd->mjpeg50, (void *) dd);
1730 setdata(xd->mjpeg75, (void *) dd);
1731 setdata(xd->mjpeg100, (void *) dd);
1732 setdata(xd->mps, (void *) dd);
1733 setdata(xd->mpdf, (void *) dd);
1734 setdata(xd->mwm, (void *) dd);
1735 setdata(xd->mclpwm, (void *) dd);
1736 setdata(xd->mclpbm, (void *) dd);
1737 setdata(xd->mprint, (void *) dd);
1738 setdata(xd->mclose, (void *) dd);
1739 setdata(xd->mrec, (void *) dd);
1740 setdata(xd->mprev, (void *) dd);
1741 setdata(xd->mnext, (void *) dd);
1742 setdata(xd->mgvar, (void *) dd);
1743 setdata(xd->madd, (void *) dd);
1744 setdata(xd->mreplace, (void *) dd);
1745 setdata(xd->mR, (void *) dd);
1746 setdata(xd->mfit, (void *) dd);
1747 setdata(xd->mfix, (void *) dd);
1748 if (ismdi() && !(RguiMDI & RW_TOOLBAR)) toolbar_hide();
1749 show(xd->gawin); /* twice, for a Windows bug */
1750 show(xd->gawin);
1751 BringToTop(xd->gawin, 0);
1752 sethit(xd->gawin, devga_sbf);
1753 setresize(xd->gawin, HelpResize);
1754 setredraw(xd->gawin, HelpExpose);
1755 setmousedown(xd->gawin, HelpMouseClick);
1756 setmousemove(xd->gawin, HelpMouseMove);
1757 setmousedrag(xd->gawin, HelpMouseMove);
1758 setmouseup(xd->gawin, HelpMouseUp);
1759 setkeydown(xd->gawin, NHelpKeyIn);
1760 setkeyaction(xd->gawin, CHelpKeyIn);
1761 setclose(xd->gawin, HelpClose);
1762 xd->recording = recording;
1763 xd->replaying = FALSE;
1764 xd->resizing = resize;
1765
1766 dd->eventHelper = GA_eventHelper;
1767
1768 dd->canGenMouseDown = TRUE;
1769 dd->canGenMouseMove = TRUE;
1770 dd->canGenMouseUp = TRUE;
1771 dd->canGenKeybd = TRUE;
1772 dd->canGenIdle = FALSE;
1773 dd->gettingEvent = FALSE;
1774
1775 GA_xd = xd;
1776 return TRUE;
1777 }
1778
GA_Open(pDevDesc dd,gadesc * xd,const char * dsp,double w,double h,Rboolean recording,int resize,int canvascolor,double gamma,int xpos,int ypos,int bg)1779 static Rboolean GA_Open(pDevDesc dd, gadesc *xd, const char *dsp,
1780 double w, double h, Rboolean recording,
1781 int resize, int canvascolor, double gamma,
1782 int xpos, int ypos, int bg)
1783 {
1784 rect rr;
1785 char buf[600]; /* allow for pageno formats */
1786
1787 if (!fontinitdone)
1788 RFontInit();
1789
1790 /* Foreground and Background Colors */
1791 xd->bg = dd->startfill = bg;
1792 xd->col = dd->startcol = R_RGB(0, 0, 0);
1793
1794 xd->fgcolor = Black;
1795 xd->bgcolor = xd->canvascolor = GArgb(canvascolor, gamma);
1796 xd->outcolor = myGetSysColor(COLOR_APPWORKSPACE);
1797 xd->rescale_factor = 1.0;
1798 xd->fast = 1; /* Use 'cosmetic pens' if available.
1799 Overridden for printers and metafiles.
1800 */
1801 xd->xshift = xd->yshift = 0;
1802 xd->npage = 0;
1803 xd->fp = NULL; /* not all devices (e.g. TIFF) use the file pointer, but SaveAsBitmap
1804 looks at it */
1805
1806 if (!dsp[0]) {
1807 if (!setupScreenDevice(dd, xd, w, h, recording, resize, xpos, ypos))
1808 return FALSE;
1809 xd->have_alpha = TRUE;
1810 } else if (!strncmp(dsp, "win.print:", 10)) {
1811 xd->kind = PRINTER;
1812 xd->fast = 0; /* use scalable line widths */
1813 xd->gawin = newprinter(MM_PER_INCH * w, MM_PER_INCH * h, &dsp[10]);
1814 if (!xd->gawin) {
1815 warning("unable to open printer");
1816 return FALSE;
1817 }
1818 } else if (!strncmp(dsp, "png:", 4) || !strncmp(dsp,"bmp:", 4)) {
1819 xd->res_dpi = (xpos == NA_INTEGER) ? 0 : xpos;
1820 xd->bg = dd->startfill = canvascolor;
1821 xd->kind = (dsp[0]=='p') ? PNG : BMP;
1822 if(strlen(dsp+4) >= 512) error(_("filename too long in %s() call"),
1823 (dsp[0]=='p') ? "png" : "bmp");
1824 strcpy(xd->filename, R_ExpandFileName(dsp+4));
1825
1826 if (w < 20 && h < 20)
1827 warning(_("'width=%d, height=%d' are unlikely values in pixels"),
1828 (int)w, (int) h);
1829 /*
1830 Observe that given actual graphapp implementation 256 is
1831 irrelevant,i.e., depth of the bitmap is that of graphic card
1832 if required depth > 1
1833 */
1834 if ((xd->gawin = newbitmap(w, h, 256)) == NULL) {
1835 warning(_("unable to allocate bitmap"));
1836 return FALSE;
1837 }
1838 xd->bm = xd->gawin;
1839 if ((xd->bm2 = newbitmap(w, h, 256)) == NULL) {
1840 warning(_("unable to allocate bitmap"));
1841 return FALSE;
1842 }
1843 snprintf(buf, 600, xd->filename, 1);
1844 if ((xd->fp = R_fopen(buf, "wb")) == NULL) {
1845 del(xd->gawin);
1846 warning(_("unable to open file '%s' for writing"), buf);
1847 return FALSE;
1848 }
1849 xd->have_alpha = TRUE;
1850 } else if (!strncmp(dsp, "jpeg:", 5)) {
1851 char *p = strchr(&dsp[5], ':');
1852 xd->res_dpi = (xpos == NA_INTEGER) ? 0 : xpos;
1853 xd->bg = dd->startfill = canvascolor;
1854 xd->kind = JPEG;
1855 if (!p) return FALSE;
1856 *p = '\0';
1857 xd->quality = atoi(&dsp[5]);
1858 *p = ':' ;
1859 if(strlen(p+1) >= 512) error(_("filename too long in jpeg() call"));
1860 strcpy(xd->filename, R_ExpandFileName(p+1));
1861 if (w < 20 && h < 20)
1862 warning(_("'width=%d, height=%d' are unlikely values in pixels"),
1863 (int)w, (int) h);
1864 if((xd->gawin = newbitmap(w, h, 256)) == NULL) {
1865 warning(_("unable to allocate bitmap"));
1866 return FALSE;
1867 }
1868 xd->bm = xd->gawin;
1869 if ((xd->bm2 = newbitmap(w, h, 256)) == NULL) {
1870 warning(_("unable to allocate bitmap"));
1871 return FALSE;
1872 }
1873 snprintf(buf, 600, xd->filename, 1);
1874 if ((xd->fp = R_fopen(buf, "wb")) == NULL) {
1875 del(xd->gawin);
1876 warning(_("unable to open file '%s' for writing"), buf);
1877 return FALSE;
1878 }
1879 xd->have_alpha = TRUE;
1880 } else if (!strncmp(dsp, "tiff:", 5)) {
1881 char *p = strchr(&dsp[5], ':');
1882 xd->res_dpi = (xpos == NA_INTEGER) ? 0 : xpos;
1883 xd->bg = dd->startfill = canvascolor;
1884 xd->kind = TIFF;
1885 if (!p) return FALSE;
1886 *p = '\0';
1887 xd->quality = atoi(&dsp[5]);
1888 *p = ':' ;
1889 if(strlen(p+1) >= 512) error(_("filename too long in tiff() call"));
1890 strcpy(xd->filename, R_ExpandFileName(p+1));
1891 if (w < 20 && h < 20)
1892 warning(_("'width=%d, height=%d' are unlikely values in pixels"),
1893 (int) w, (int) h);
1894 if((xd->gawin = newbitmap(w, h, 256)) == NULL) {
1895 warning(_("unable to allocate bitmap"));
1896 return FALSE;
1897 }
1898 xd->bm = xd->gawin;
1899 if ((xd->bm2 = newbitmap(w, h, 256)) == NULL) {
1900 warning(_("unable to allocate bitmap"));
1901 return FALSE;
1902 }
1903 xd->have_alpha = TRUE;
1904 } else {
1905 /*
1906 * win.metafile[:] in memory (for the clipboard)
1907 * win.metafile:filename
1908 * anything else return FALSE
1909 */
1910 char *s = "win.metafile";
1911 int ls = strlen(s);
1912 int ld = strlen(dsp);
1913
1914 if (ls > ld)
1915 return FALSE;
1916 if (strncmp(dsp, s, ls) || (dsp[ls] && (dsp[ls] != ':'))) {
1917 warning("invalid specification for file name in win.metafile()");
1918 return FALSE;
1919 }
1920 if(ld > ls && strlen(&dsp[ls + 1]) >= 512)
1921 error(_("filename too long in win.metafile() call"));
1922 strcpy(xd->filename, (ld > ls) ? &dsp[ls + 1] : "");
1923 snprintf(buf, 600, xd->filename, 1);
1924 xd->w = MM_PER_INCH * w;
1925 xd->h = MM_PER_INCH * h;
1926 xd->gawin = newmetafile(buf, MM_PER_INCH * w, MM_PER_INCH * h);
1927 xd->kind = METAFILE;
1928 xd->fast = 0; /* use scalable line widths */
1929 if (!xd->gawin) {
1930 if(ld > ls)
1931 warning(_("unable to open metafile '%s' for writing"), buf);
1932 else
1933 warning(_("unable to open clipboard to write metafile"));
1934 return FALSE;
1935 }
1936 }
1937
1938 if (xd->kind <= METAFILE)
1939 xd->lwdscale = devicepixelsy(xd->gawin)/96.0; /* matches ps/pdf */
1940 else if (xd->res_dpi > 0)
1941 xd->lwdscale = xd->res_dpi/96.0;
1942 else
1943 xd->lwdscale = 72.0/96.0;
1944 if(xd->lwdscale < 1.0) xd->lwdscale = 1.0; /* at least one pixel */
1945 rr = getrect(xd->gawin);
1946 xd->origWidth = xd->showWidth = xd->windowWidth = rr.width;
1947 xd->origHeight = xd->showHeight = xd->windowHeight = rr.height;
1948 xd->clip = rr;
1949 setdata(xd->gawin, (void *) dd);
1950 xd->needsave = FALSE;
1951 return TRUE;
1952 }
1953
1954 /********************************************************/
1955 /* device_StrWidth should return the width of the given */
1956 /* string in DEVICE units (GStrWidth is responsible for */
1957 /* converting from DEVICE to whatever units the user */
1958 /* asked for */
1959 /********************************************************/
1960
GA_StrWidth(const char * str,const pGEcontext gc,pDevDesc dd)1961 static double GA_StrWidth(const char *str,
1962 const pGEcontext gc,
1963 pDevDesc dd)
1964 {
1965 gadesc *xd = (gadesc *) dd->deviceSpecific;
1966
1967 SetFont(gc, 0.0, xd);
1968 return (double) gstrwidth1(xd->gawin, xd->font, str, CE_NATIVE);
1969 }
1970
GA_StrWidth_UTF8(const char * str,const pGEcontext gc,pDevDesc dd)1971 static double GA_StrWidth_UTF8(const char *str,
1972 const pGEcontext gc,
1973 pDevDesc dd)
1974 {
1975 gadesc *xd = (gadesc *) dd->deviceSpecific;
1976 double a;
1977
1978 /* This should never be called for symbol fonts */
1979 SetFont(gc, 0.0, xd);
1980 if(gc->fontface != 5)
1981 a = (double) gstrwidth1(xd->gawin, xd->font, str, CE_UTF8);
1982 else
1983 a = (double) gstrwidth1(xd->gawin, xd->font, str, CE_SYMBOL);
1984 return a;
1985 }
1986
1987 /********************************************************/
1988 /* device_MetricInfo should return height, depth, and */
1989 /* width information for the given character in DEVICE */
1990 /* units (GMetricInfo does the necessary conversions) */
1991 /* This is used for formatting mathematical expressions */
1992 /********************************************************/
1993
1994 /* Character Metric Information */
1995 /* Passing c == 0 gets font information.
1996 In a mbcslocale for a non-symbol font
1997 we pass a Unicode point, otherwise an 8-bit char, and
1998 we don't care which for a 7-bit char.
1999 */
2000
GA_MetricInfo(int c,const pGEcontext gc,double * ascent,double * descent,double * width,pDevDesc dd)2001 static void GA_MetricInfo(int c,
2002 const pGEcontext gc,
2003 double* ascent, double* descent,
2004 double* width, pDevDesc dd)
2005 {
2006 int a, d, w;
2007 gadesc *xd = (gadesc *) dd->deviceSpecific;
2008 Rboolean Unicode = mbcslocale;
2009
2010 if (c < 0) { Unicode = TRUE; c = -c; }
2011 SetFont(gc, 0.0, xd);
2012 if(Unicode && gc->fontface != 5 && c > 127)
2013 gwcharmetric(xd->gawin, xd->font, c, &a, &d, &w);
2014 else
2015 gcharmetric(xd->gawin, xd->font, c, &a, &d, &w);
2016 /* Some Windows systems report that space has height and depth,
2017 so we have a kludge. Note that 32 is space in symbol font too */
2018 if(c == 32) {
2019 *ascent = 0.0;
2020 *descent = 0.0;
2021 } else {
2022 *ascent = (double) a;
2023 *descent = (double) d;
2024 }
2025 *width = (double) w;
2026 }
2027
2028 /********************************************************/
2029 /* device_Clip is given the left, right, bottom, and */
2030 /* top of a rectangle (in DEVICE coordinates). it */
2031 /* should have the side-effect that subsequent output */
2032 /* is clipped to the given rectangle */
2033 /********************************************************/
2034
GA_Clip(double x0,double x1,double y0,double y1,pDevDesc dd)2035 static void GA_Clip(double x0, double x1, double y0, double y1, pDevDesc dd)
2036 {
2037 gadesc *xd = (gadesc *) dd->deviceSpecific;
2038 rect r;
2039
2040 r = rcanon(rpt(pt(x0, y0), pt(x1, y1)));
2041 r.width += 1;
2042 r.height += 1;
2043 xd->clip = r;
2044 }
2045
2046 /********************************************************/
2047 /* device_Resize is called whenever the device is */
2048 /* resized. the function must update the GPar */
2049 /* parameters (left, right, bottom, and top) for the */
2050 /* new device size */
2051 /* this is not usually called directly by the graphics */
2052 /* engine because the detection of device resizes */
2053 /* (e.g., a window resize) are usually detected by */
2054 /* device-specific code (see R_ProcessEvents) */
2055 /********************************************************/
2056
GA_Size(double * left,double * right,double * bottom,double * top,pDevDesc dd)2057 static void GA_Size(double *left, double *right,
2058 double *bottom, double *top,
2059 pDevDesc dd)
2060 {
2061 *left = dd->left;
2062 *top = dd->top;
2063 /* There's a mysterious -0.0001 in the setting */
2064 *right = ceil(dd->right);
2065 *bottom = ceil(dd->bottom);
2066 }
2067
GA_Resize(pDevDesc dd)2068 static void GA_Resize(pDevDesc dd)
2069 {
2070 gadesc *xd = (gadesc *) dd->deviceSpecific;
2071
2072 if (xd->resize) {
2073 int iw, ih, iw0 = dd->right - dd->left,
2074 ih0 = dd->bottom - dd->top;
2075 double fw, fh, rf, shift;
2076
2077 iw = xd->windowWidth;
2078 ih = xd->windowHeight;
2079 if(xd->resizing == 1) {
2080 /* last mode might have been 3, so remove scrollbars */
2081 gchangescrollbar(xd->gawin, VWINSB, 0, ih/SF-1, ih/SF, 0);
2082 gchangescrollbar(xd->gawin, HWINSB, 0, iw/SF-1, iw/SF, 0);
2083 dd->left = 0.0;
2084 dd->top = 0.0;
2085 dd->right = iw;
2086 dd->bottom = ih;
2087 xd->showWidth = iw;
2088 xd->showHeight = ih;
2089 } else if (xd->resizing == 2) {
2090 /* last mode might have been 3, so remove scrollbars */
2091 gchangescrollbar(xd->gawin, VWINSB, 0, ih/SF-1, ih/SF, 0);
2092 gchangescrollbar(xd->gawin, HWINSB, 0, iw/SF-1, iw/SF, 0);
2093 fw = (iw + 0.5)/(iw0 + 0.5);
2094 fh = (ih + 0.5)/(ih0 + 0.5);
2095 rf = min(fw, fh);
2096 xd->rescale_factor *= rf;
2097 {
2098 SEXP scale;
2099 PROTECT(scale = ScalarReal(rf));
2100 GEhandleEvent(GE_ScalePS, dd, scale);
2101 UNPROTECT(1);
2102 }
2103 if (fw < fh) {
2104 dd->left = 0.0;
2105 xd->showWidth = dd->right = iw;
2106 xd->showHeight = ih0*fw;
2107 shift = (ih - xd->showHeight)/2.0;
2108 dd->top = shift;
2109 dd->bottom = ih0*fw + shift;
2110 xd->xshift = 0; xd->yshift = shift;
2111 } else {
2112 dd->top = 0.0;
2113 xd->showHeight = dd->bottom = ih;
2114 xd->showWidth = iw0*fh;
2115 shift = (iw - xd->showWidth)/2.0;
2116 dd->left = shift;
2117 dd->right = iw0*fh + shift;
2118 xd->xshift = shift; xd->yshift = 0;
2119 }
2120 xd->clip = getregion(xd);
2121 } else if (xd->resizing == 3) {
2122 if(iw0 < iw) shift = (iw - iw0)/2.0;
2123 else shift = min(0, xd->xshift);
2124 dd->left = shift;
2125 dd->right = iw0 + shift;
2126 xd->xshift = shift;
2127 gchangescrollbar(xd->gawin, HWINSB, max(-shift,0)/SF,
2128 xd->origWidth/SF - 1, xd->windowWidth/SF, 0);
2129 if(ih0 < ih) shift = (ih - ih0)/2.0;
2130 else shift = min(0, xd->yshift);
2131 dd->top = shift;
2132 dd->bottom = ih0 + shift;
2133 xd->yshift = shift;
2134 gchangescrollbar(xd->gawin, VWINSB, max(-shift,0)/SF,
2135 xd->origHeight/SF - 1, xd->windowHeight/SF, 0);
2136 xd->showWidth = xd->origWidth + min(0, xd->xshift);
2137 xd->showHeight = xd->origHeight + min(0, xd->yshift);
2138 }
2139 xd->resize = FALSE;
2140 if (xd->kind == SCREEN) {
2141 del(xd->bm);
2142 xd->bm = newbitmap(iw, ih, getdepth(xd->gawin));
2143 if (!xd->bm) {
2144 R_ShowMessage(_("Insufficient memory for resize. Killing device"));
2145 killDevice(ndevNumber(dd));
2146 return; /* since the device is killed */
2147 }
2148 if(xd->have_alpha) {
2149 del(xd->bm2);
2150 xd->bm2 = newbitmap(iw, ih, getdepth(xd->gawin));
2151 if (!xd->bm2) {
2152 R_ShowMessage(_("Insufficient memory for resize. Disabling alpha blending"));
2153 xd->have_alpha = FALSE;
2154 }
2155 }
2156
2157 gfillrect(xd->gawin, xd->outcolor, getrect(xd->gawin));
2158 gfillrect(xd->bm, xd->outcolor, getrect(xd->bm));
2159 }
2160 }
2161 }
2162
2163 /********************************************************/
2164 /* device_NewPage is called whenever a new plot requires*/
2165 /* a new page. a new page might mean just clearing the */
2166 /* device (as in this case) or moving to a new page */
2167 /* (e.g., postscript) */
2168 /********************************************************/
2169
GA_NewPage(const pGEcontext gc,pDevDesc dd)2170 static void GA_NewPage(const pGEcontext gc,
2171 pDevDesc dd)
2172 {
2173 gadesc *xd = (gadesc *) dd->deviceSpecific;
2174
2175 xd->npage++;
2176 if ((xd->kind == PRINTER) && xd->needsave)
2177 nextpage(xd->gawin);
2178 if ((xd->kind == METAFILE) && xd->needsave) {
2179 char buf[600];
2180 if (strlen(xd->filename) == 0)
2181 error(_("a clipboard metafile can store only one figure."));
2182 else {
2183 del(xd->gawin);
2184 snprintf(buf, 600, xd->filename, xd->npage);
2185 xd->gawin = newmetafile(buf, xd->w, xd->h);
2186 if(!xd->gawin)
2187 error(_("metafile '%s' could not be created"), buf);
2188 }
2189 }
2190 if ((xd->kind == PNG || xd->kind == JPEG || xd->kind == BMP)
2191 && xd->needsave) {
2192 char buf[600];
2193 SaveAsBitmap(dd, xd->res_dpi);
2194 snprintf(buf, 600, xd->filename, xd->npage);
2195 if ((xd->fp = R_fopen(buf, "wb")) == NULL)
2196 error(_("unable to open file '%s' for writing"), buf);
2197 }
2198 if (xd->kind == TIFF && xd->needsave) {
2199 SaveAsBitmap(dd, xd->res_dpi);
2200 }
2201 if (xd->kind == SCREEN) {
2202 if(xd->buffered && !xd->holdlevel) SHOW;
2203 if (xd->recording && xd->needsave)
2204 AddtoPlotHistory(desc2GEDesc(dd)->savedSnapshot, 0);
2205 if (xd->replaying)
2206 xd->needsave = FALSE;
2207 else
2208 xd->needsave = TRUE;
2209 }
2210 xd->bg = gc->fill;
2211 xd->warn_trans = FALSE;
2212 {
2213 unsigned int alpha = R_ALPHA(xd->bg);
2214 if(alpha == 0) xd->bgcolor = xd->canvascolor;
2215 else {
2216 xd->bgcolor = GArgb(xd->bg, gc->gamma);
2217 if(alpha < 255)
2218 xd->bgcolor = (alpha * xd->bgcolor +
2219 (255-alpha) * xd->canvascolor)/255;
2220 }
2221 }
2222 if (xd->kind != SCREEN) {
2223 xd->needsave = TRUE;
2224 xd->clip = getrect(xd->gawin);
2225 if(R_OPAQUE(xd->bg) || xd->kind == BMP || xd->kind == JPEG
2226 || xd->kind == TIFF) {
2227 DRAW(gfillrect(_d, xd->bgcolor, xd->clip));
2228 } else if(xd->kind == PNG) {
2229 DRAW(gfillrect(_d, PNG_TRANS, xd->clip));
2230 }
2231 if(xd->kind == PNG)
2232 xd->pngtrans = ggetpixel(xd->gawin, pt(0,0)) | 0xff000000;
2233 } else {
2234 xd->clip = getregion(xd);
2235 DRAW(gfillrect(_d, xd->bgcolor, xd->clip));
2236 }
2237 SH;
2238 }
2239
deleteGraphMenus(int devnum)2240 static void deleteGraphMenus(int devnum)
2241 {
2242 char prefix[15];
2243
2244 snprintf(prefix, 15, "$Graph%i", devnum);
2245 windelmenus(prefix);
2246 }
2247
2248 /********************************************************/
2249 /* device_Close is called when the device is killed */
2250 /* this function is responsible for destroying any */
2251 /* device-specific resources that were created in */
2252 /* device_Open and for FREEing the device-specific */
2253 /* parameters structure */
2254 /********************************************************/
2255
GA_Close(pDevDesc dd)2256 static void GA_Close(pDevDesc dd)
2257 {
2258 gadesc *xd = (gadesc *) dd->deviceSpecific;
2259 SEXP vDL;
2260
2261 if (xd->cntxt)
2262 ((RCNTXT *)xd->cntxt)->cend = NULL; /* Don't try to run cleanup; it will have already happened */
2263
2264 if (dd->onExit) {
2265 dd->onExit(dd);
2266 }
2267
2268 if (xd->kind == SCREEN) {
2269 if(xd->recording) {
2270 AddtoPlotHistory(GEcreateSnapshot(desc2GEDesc(dd)), 0);
2271 /* May have changed vDL, so can't use GETDL above */
2272 vDL = findVar(install(".SavedPlots"), R_GlobalEnv);
2273 pCURRENTPOS++; /* so PgUp goes to the last saved plot
2274 when a windows() device is opened */
2275 }
2276 hide(xd->gawin);
2277
2278 del(xd->bm);
2279 /* If this is the active device and buffered, shut updates off */
2280 if (xd == GA_xd) GA_xd = NULL;
2281 deleteGraphMenus(ndevNumber(dd) + 1);
2282
2283 } else if ((xd->kind == PNG) || (xd->kind == JPEG)
2284 || (xd->kind == BMP) || (xd->kind == TIFF)) {
2285 if (xd->kind == TIFF) xd->npage++;
2286 SaveAsBitmap(dd, xd->res_dpi);
2287 }
2288 del(xd->font);
2289 if(xd->bm2) del(xd->bm2);
2290 del(xd->gawin);
2291 /*
2292 * this is needed since the GraphApp delayed clean-up
2293 * ,i.e, I want free all resources NOW
2294 */
2295 /* I think the concern is rather to run all pending events on the
2296 device (but also on the console and others) */
2297 doevent();
2298 free(xd);
2299 dd->deviceSpecific = NULL;
2300 }
2301
2302 /********************************************************/
2303 /* device_Activate is called when a device becomes the */
2304 /* active device. in this case it is used to change the*/
2305 /* title of a window to indicate the active status of */
2306 /* the device to the user. not all device types will */
2307 /* do anything */
2308 /********************************************************/
2309
GA_Activate(pDevDesc dd)2310 static void GA_Activate(pDevDesc dd)
2311 {
2312 char t[150];
2313 gadesc *xd = (gadesc *) dd->deviceSpecific;
2314
2315 if (xd->replaying || (xd->kind!=SCREEN))
2316 return;
2317 if(strlen(xd->title)) {
2318 snprintf(t, 140, xd->title, ndevNumber(dd) + 1);
2319 t[139] = '\0';
2320 } else {
2321 snprintf(t, 150, "R Graphics: Device %d", ndevNumber(dd) + 1);
2322 }
2323 strcat(t, " (ACTIVE)");
2324 settext(xd->gawin, t);
2325 if (xd != GA_xd)
2326 drawbits(GA_xd);
2327 GA_xd = xd;
2328 }
2329
2330 /********************************************************/
2331 /* device_Deactivate is called when a device becomes */
2332 /* inactive. in this case it is used to change the */
2333 /* title of a window to indicate the inactive status of */
2334 /* the device to the user. not all device types will */
2335 /* do anything */
2336 /********************************************************/
2337
GA_Deactivate(pDevDesc dd)2338 static void GA_Deactivate(pDevDesc dd)
2339 {
2340 char t[150];
2341 gadesc *xd = (gadesc *) dd->deviceSpecific;
2342
2343 if (xd->replaying || (xd->kind != SCREEN))
2344 return;
2345 if(strlen(xd->title)) {
2346 snprintf(t, 140, xd->title, ndevNumber(dd) + 1);
2347 t[139] = '\0';
2348 } else {
2349 snprintf(t, 150, "R Graphics: Device %d", ndevNumber(dd) + 1);
2350 }
2351 strcat(t, " (inactive)");
2352 settext(xd->gawin, t);
2353 }
2354
2355 #define WARN_SEMI_TRANS { \
2356 if(!xd->warn_trans) warning(_("semi-transparency is not supported on this device: reported only once per page")); \
2357 xd->warn_trans = TRUE; \
2358 }
2359
2360 #define DRAW2(col) {if(xd->kind != SCREEN) gcopyalpha(xd->gawin,xd->bm2,r,R_ALPHA(col)); else {gcopyalpha(xd->bm,xd->bm2,r,R_ALPHA(col)); if(!xd->buffered) drawbits(xd);}}
2361
2362
2363
2364 /********************************************************/
2365 /* device_Rect should have the side-effect that a */
2366 /* rectangle is drawn with the given locations for its */
2367 /* opposite corners. the border of the rectangle */
2368 /* should be in the given "fg" colour and the rectangle */
2369 /* should be filled with the given "bg" colour */
2370 /* if "fg" is NA_INTEGER then no border should be drawn */
2371 /* if "bg" is NA_INTEGER then the rectangle should not */
2372 /* be filled */
2373 /* the locations are in an arbitrary coordinate system */
2374 /* and this function is responsible for converting the */
2375 /* locations to DEVICE coordinates using GConvert */
2376 /********************************************************/
2377
GA_Rect(double x0,double y0,double x1,double y1,const pGEcontext gc,pDevDesc dd)2378 static void GA_Rect(double x0, double y0, double x1, double y1,
2379 const pGEcontext gc,
2380 pDevDesc dd)
2381 {
2382 int tmp;
2383 gadesc *xd = (gadesc *) dd->deviceSpecific;
2384 rect r, rr;
2385
2386 /* These in-place conversions are ok */
2387 TRACEDEVGA("rect");
2388
2389 if (x0 > x1) {
2390 tmp = x0;
2391 x0 = x1;
2392 x1 = tmp;
2393 }
2394 if (y0 > y1) {
2395 tmp = y0;
2396 y0 = y1;
2397 y1 = tmp;
2398 }
2399 /* zero width or height disappears, so handle that case specially in case it's just rounding */
2400 if ((int)x0 == (int)x1 && x1-x0 >= 0.5) {
2401 x1 = (int)x1;
2402 x0 = x1 - 1.0;
2403 }
2404 if ((int)y0 == (int)y1 && y1-y0 >= 0.5) {
2405 y1 = (int)y1;
2406 y0 = y1 - 1.0;
2407 }
2408 r = rect((int) x0, (int) y0, (int)x1 - (int)x0, (int)y1 - (int)y0);
2409
2410 SetColor(gc->fill, gc->gamma, xd);
2411 if (R_OPAQUE(gc->fill)) {
2412 DRAW(gfillrect(_d, xd->fgcolor, r));
2413 } else if(R_ALPHA(gc->fill) > 0) {
2414 if(xd->have_alpha) {
2415 rect cp = xd->clip;
2416 /* We are only working with the screen device here, so
2417 we can assume that x->bm is the current state.
2418 Copying from the screen window does not work. */
2419 /* Clip to the device region */
2420 rr = r;
2421 if (r.x < 0) {r.x = 0; r.width = r.width + rr.x;}
2422 if (r.y < 0) {r.y = 0; r.height = r.height + rr.y;}
2423 if (r.x + r.width > cp.x + cp.width)
2424 r.width = cp.x + cp.width - r.x;
2425 if (r.y + r.height > cp.y + cp.height)
2426 r.height = cp.y + cp.height - r.y;
2427 gsetcliprect(xd->bm, xd->clip);
2428 gcopy(xd->bm2, xd->bm, r);
2429 gfillrect(xd->bm2, xd->fgcolor, rr);
2430 DRAW2(gc->fill);
2431 r = rr;
2432 } else WARN_SEMI_TRANS;
2433 }
2434
2435 SetColor(gc->col, gc->gamma, xd);
2436 SetLineStyle(gc, dd);
2437 if (R_OPAQUE(gc->col)) {
2438 DRAW(gdrawrect(_d, xd->lwd, xd->lty, xd->fgcolor, r, 0, xd->lend,
2439 xd->ljoin, xd->lmitre));
2440 } else if(R_ALPHA(gc->col) > 0) {
2441 if(xd->have_alpha) {
2442 int adj, tol = xd->lwd; /* only half needed */
2443 rect cp = xd->clip;
2444 rr = r;
2445 r.x -= tol; r.y -= tol; r.width += 2*tol; r.height += 2*tol;
2446 if (r.x < 0) {adj = r.x; r.x = 0; r.width = r.width + adj;}
2447 if (r.y < 0) {adj = r.y; r.y = 0; r.height = r.height + adj;}
2448 if (r.x + r.width > cp.x + cp.width)
2449 r.width = cp.x + cp.width - r.x;
2450 if (r.y + r.height > cp.y + cp.height)
2451 r.height = cp.y + cp.height - r.y;
2452 gsetcliprect(xd->bm, xd->clip);
2453 gcopy(xd->bm2, xd->bm, r);
2454 gdrawrect(xd->bm2, xd->lwd, xd->lty, xd->fgcolor, rr, 0, xd->lend,
2455 xd->ljoin, xd->lmitre);
2456 DRAW2(gc->col);
2457 } else WARN_SEMI_TRANS;
2458 }
2459 SH;
2460 }
2461
2462 /********************************************************/
2463 /* device_Circle should have the side-effect that a */
2464 /* circle is drawn, centred at the given location, with */
2465 /* the given radius. the border of the circle should be*/
2466 /* drawn in the given "col", and the circle should be */
2467 /* filled with the given "border" colour. */
2468 /* if "col" is NA_INTEGER then no border should be drawn*/
2469 /* if "border" is NA_INTEGER then the circle should not */
2470 /* be filled */
2471 /* the location is in arbitrary coordinates and the */
2472 /* function is responsible for converting this to */
2473 /* DEVICE coordinates. the radius is given in DEVICE */
2474 /* coordinates */
2475 /********************************************************/
2476
GA_Circle(double x,double y,double radius,const pGEcontext gc,pDevDesc dd)2477 static void GA_Circle(double x, double y, double radius,
2478 const pGEcontext gc,
2479 pDevDesc dd)
2480 {
2481 int id, ix, iy;
2482 gadesc *xd = (gadesc *) dd->deviceSpecific;
2483 rect r, rr;
2484
2485 TRACEDEVGA("circle");
2486 id = 2*radius + 0.5;
2487 if (id < 2) id = 2; /* diameter 1 is near-invisible */
2488
2489 ix = (int) x;
2490 iy = (int) y;
2491 r = rr = rect(ix - id/2, iy - id/2, id, id);
2492
2493 SetColor(gc->fill, gc->gamma, xd);
2494 if (R_OPAQUE(gc->fill)) {
2495 DRAW(gfillellipse(_d, xd->fgcolor, rr));
2496 } else if(R_ALPHA(gc->fill) > 0) {
2497 if (xd->have_alpha) {
2498 rect cp = xd->clip;
2499 /* Clip to the device region */
2500 if (r.x < 0) {r.x = 0; r.width = r.width + rr.x;}
2501 if (r.y < 0) {r.y = 0; r.height = r.height + rr.y;}
2502 if (r.x + r.width > cp.x + cp.width)
2503 r.width = cp.x + cp.width - r.x;
2504 if (r.y + r.height > cp.y + cp.height)
2505 r.height = cp.y + cp.height - r.y;
2506 gsetcliprect(xd->bm, xd->clip);
2507 gcopy(xd->bm2, xd->bm, r);
2508 gfillellipse(xd->bm2, xd->fgcolor, rr);
2509 DRAW2(gc->fill);
2510 r = rr;
2511 } else WARN_SEMI_TRANS;
2512 }
2513
2514 SetColor(gc->col, gc->gamma, xd);
2515 SetLineStyle(gc, dd);
2516 if (R_OPAQUE(gc->col)) {
2517 DRAW(gdrawellipse(_d, xd->lwd, xd->fgcolor, rr, 0, xd->lend,
2518 xd->ljoin, xd->lmitre));
2519 } else if(R_ALPHA(gc->col) > 0) {
2520 if(xd->have_alpha) {
2521 int adj, tol = xd->lwd; /* only half needed */
2522 rect cp = xd->clip;
2523 r.x -= tol; r.y -= tol; r.width += 2*tol; r.height += 2*tol;
2524 if (r.x < 0) {adj = r.x; r.x = 0; r.width = r.width + adj;}
2525 if (r.y < 0) {adj = r.y; r.y = 0; r.height = r.height + adj;}
2526 if (r.x + r.width > cp.x + cp.width)
2527 r.width = cp.x + cp.width - r.x;
2528 if (r.y + r.height > cp.y + cp.height)
2529 r.height = cp.y + cp.height - r.y;
2530 gsetcliprect(xd->bm, xd->clip);
2531 gcopy(xd->bm2, xd->bm, r);
2532 gdrawellipse(xd->bm2, xd->lwd, xd->fgcolor, rr, 0, xd->lend,
2533 xd->ljoin, xd->lmitre);
2534 DRAW2(gc->col);
2535 } else WARN_SEMI_TRANS;
2536 }
2537 SH;
2538 }
2539
2540 /********************************************************/
2541 /* device_Line should have the side-effect that a single*/
2542 /* line is drawn (from x1,y1 to x2,y2) */
2543 /* x1, y1, x2, and y2 are in arbitrary coordinates and */
2544 /* the function is responsible for converting them to */
2545 /* DEVICE coordinates using GConvert */
2546 /********************************************************/
2547
GA_Line(double x1,double y1,double x2,double y2,const pGEcontext gc,pDevDesc dd)2548 static void GA_Line(double x1, double y1, double x2, double y2,
2549 const pGEcontext gc,
2550 pDevDesc dd)
2551 {
2552 int xx1, yy1, xx2, yy2;
2553 gadesc *xd = (gadesc *) dd->deviceSpecific;
2554
2555 /* In-place conversion ok */
2556 TRACEDEVGA("line");
2557 xx1 = (int) x1;
2558 yy1 = (int) y1;
2559 xx2 = (int) x2;
2560 yy2 = (int) y2;
2561
2562 SetColor(gc->col, gc->gamma, xd);
2563 SetLineStyle(gc, dd);
2564 if (R_OPAQUE(gc->col)) {
2565 DRAW(gdrawline(_d, xd->lwd, xd->lty, xd->fgcolor,
2566 pt(xx1, yy1), pt(xx2, yy2), 0, xd->lend,
2567 xd->ljoin, xd->lmitre));
2568 SH;
2569 } else if(R_ALPHA(gc->col) > 0) {
2570 if(xd->have_alpha) {
2571 rect r = xd->clip;
2572 gsetcliprect(xd->bm, xd->clip);
2573 gcopy(xd->bm2, xd->bm, r);
2574 gdrawline(xd->bm2, xd->lwd, xd->lty, xd->fgcolor,
2575 pt(xx1, yy1), pt(xx2, yy2), 0, xd->lend,
2576 xd->ljoin, xd->lmitre);
2577 DRAW2(gc->col);
2578 SH;
2579 } else WARN_SEMI_TRANS;
2580 }
2581 }
2582
2583 /********************************************************/
2584 /* device_Polyline should have the side-effect that a */
2585 /* series of line segments are drawn using the given x */
2586 /* and y values */
2587 /* the x and y values are in arbitrary coordinates and */
2588 /* the function is responsible for converting them to */
2589 /* DEVICE coordinates using GConvert */
2590 /********************************************************/
2591
GA_Polyline(int n,double * x,double * y,const pGEcontext gc,pDevDesc dd)2592 static void GA_Polyline(int n, double *x, double *y,
2593 const pGEcontext gc,
2594 pDevDesc dd)
2595 {
2596 const void *vmax = vmaxget();
2597 point *p = (point *) R_alloc(n, sizeof(point));
2598 double devx, devy;
2599 int i;
2600 gadesc *xd = (gadesc *) dd->deviceSpecific;
2601
2602 TRACEDEVGA("pl");
2603 for (i = 0; i < n; i++) {
2604 devx = x[i];
2605 devy = y[i];
2606 p[i].x = (int) devx;
2607 p[i].y = (int) devy;
2608 }
2609
2610 SetColor(gc->col, gc->gamma, xd);
2611 SetLineStyle(gc, dd);
2612 if (R_OPAQUE(gc->col)) {
2613 DRAW(gdrawpolyline(_d, xd->lwd, xd->lty, xd->fgcolor, p, n, 0, 0,
2614 xd->lend, xd->ljoin, xd->lmitre));
2615 } else if(R_ALPHA(gc->col) > 0) {
2616 if(xd->have_alpha) {
2617 rect r = xd->clip; /* lines can go well outside bbox of points */
2618 gsetcliprect(xd->bm, xd->clip);
2619 gcopy(xd->bm2, xd->bm, r);
2620 gdrawpolyline(xd->bm2, xd->lwd, xd->lty, xd->fgcolor, p, n, 0, 0,
2621 xd->lend, xd->ljoin, xd->lmitre);
2622 DRAW2(gc->col);
2623 } else WARN_SEMI_TRANS;
2624 }
2625 vmaxset(vmax);
2626 SH;
2627 }
2628
2629 /********************************************************/
2630 /* device_Polygon should have the side-effect that a */
2631 /* polygon is drawn using the given x and y values */
2632 /* the polygon border should be drawn in the "fg" */
2633 /* colour and filled with the "bg" colour */
2634 /* if "fg" is NA_INTEGER don't draw the border */
2635 /* if "bg" is NA_INTEGER don't fill the polygon */
2636 /* the x and y values are in arbitrary coordinates and */
2637 /* the function is responsible for converting them to */
2638 /* DEVICE coordinates using GConvert */
2639 /********************************************************/
2640
GA_Polygon(int n,double * x,double * y,const pGEcontext gc,pDevDesc dd)2641 static void GA_Polygon(int n, double *x, double *y,
2642 const pGEcontext gc,
2643 pDevDesc dd)
2644 {
2645 const void *vmax = vmaxget();
2646 point *points;
2647 rect r;
2648 double devx, devy;
2649 int i, mx0 = 0, mx1 = 0, my0 = 0, my1 = 0;
2650 gadesc *xd = (gadesc *) dd->deviceSpecific;
2651
2652 TRACEDEVGA("plg");
2653 points = (point *) R_alloc(n , sizeof(point));
2654 if (!points)
2655 return;
2656 for (i = 0; i < n; i++) {
2657 devx = x[i];
2658 devy = y[i];
2659 points[i].x = (int) (devx);
2660 points[i].y = (int) (devy);
2661 mx0 = imin2(mx0, points[i].x);
2662 mx1 = imax2(mx1, points[i].x);
2663 my0 = imin2(my0, points[i].y);
2664 my1 = imax2(my1, points[i].y);
2665 }
2666 r.x = mx0; r.width = mx1 - mx0;
2667 r.y = my0; r.height = my1 - my0;
2668
2669 if (xd->doSetPolyFill && xd->fillOddEven == FALSE) {
2670 DRAW(gsetpolyfillmode(_d, 0));
2671 xd->doSetPolyFill = FALSE; /* Only set it once */
2672 }
2673
2674 SetColor(gc->fill, gc->gamma, xd);
2675 if (R_OPAQUE(gc->fill)) {
2676 DRAW(gfillpolygon(_d, xd->fgcolor, points, n));
2677 } else if(R_ALPHA(gc->fill) > 0) {
2678 if(xd->have_alpha) {
2679 gsetcliprect(xd->bm, xd->clip);
2680 gcopy(xd->bm2, xd->bm, r);
2681 gfillpolygon(xd->bm2, xd->fgcolor, points, n);
2682 DRAW2(gc->fill);
2683 } else WARN_SEMI_TRANS;
2684 }
2685
2686 SetColor(gc->col, gc->gamma, xd);
2687 SetLineStyle(gc, dd);
2688 if (R_OPAQUE(gc->col)) {
2689 DRAW(gdrawpolygon(_d, xd->lwd, xd->lty, xd->fgcolor, points, n, 0,
2690 xd->lend, xd->ljoin, xd->lmitre));
2691 } else if(R_ALPHA(gc->col) > 0) {
2692 if(xd->have_alpha) {
2693 r = xd->clip;
2694 gsetcliprect(xd->bm, xd->clip);
2695 gcopy(xd->bm2, xd->bm, r);
2696 gdrawpolygon(xd->bm2, xd->lwd, xd->lty, xd->fgcolor, points, n, 0,
2697 xd->lend, xd->ljoin, xd->lmitre);
2698 DRAW2(gc->col);
2699 } else WARN_SEMI_TRANS;
2700 }
2701 vmaxset(vmax);
2702 SH;
2703 }
2704
GA_Path(double * x,double * y,int npoly,int * nper,Rboolean winding,const pGEcontext gc,pDevDesc dd)2705 static void GA_Path(double *x, double *y,
2706 int npoly, int *nper,
2707 Rboolean winding,
2708 const pGEcontext gc,
2709 pDevDesc dd)
2710 {
2711 const void *vmax = vmaxget();
2712 point *points;
2713 point *pointIndex;
2714 rect r;
2715 double devx, devy;
2716 int i, mx0 = 0, mx1 = 0, my0 = 0, my1 = 0;
2717 gadesc *xd = (gadesc *) dd->deviceSpecific;
2718
2719 int ntot = 0;
2720 for (i=0; i < npoly; i++) {
2721 ntot = ntot + nper[i];
2722 }
2723
2724 TRACEDEVGA("path");
2725 points = (point *) R_alloc(ntot, sizeof(point));
2726 if (!points)
2727 return;
2728 for (i = 0; i < ntot; i++) {
2729 devx = x[i];
2730 devy = y[i];
2731 points[i].x = (int) (devx);
2732 points[i].y = (int) (devy);
2733 mx0 = imin2(mx0, points[i].x);
2734 mx1 = imax2(mx1, points[i].x);
2735 my0 = imin2(my0, points[i].y);
2736 my1 = imax2(my1, points[i].y);
2737 }
2738 r.x = mx0; r.width = mx1 - mx0;
2739 r.y = my0; r.height = my1 - my0;
2740
2741 if (winding) {
2742 DRAW(gsetpolyfillmode(_d, 0));
2743 } else {
2744 DRAW(gsetpolyfillmode(_d, 1));
2745 }
2746
2747 SetColor(gc->fill, gc->gamma, xd);
2748 if (R_OPAQUE(gc->fill)) {
2749 DRAW(gfillpolypolygon(_d, xd->fgcolor, points, npoly, nper));
2750 } else if(R_ALPHA(gc->fill) > 0) {
2751 if(xd->have_alpha) {
2752 gsetcliprect(xd->bm, xd->clip);
2753 gcopy(xd->bm2, xd->bm, r);
2754 gfillpolypolygon(xd->bm2, xd->fgcolor, points, npoly, nper);
2755 DRAW2(gc->fill);
2756 } else WARN_SEMI_TRANS;
2757 }
2758
2759 SetColor(gc->col, gc->gamma, xd);
2760 SetLineStyle(gc, dd);
2761 if (R_OPAQUE(gc->col)) {
2762 pointIndex = points;
2763 for (i = 0; i < npoly; i++) {
2764 DRAW(gdrawpolygon(_d, xd->lwd, xd->lty, xd->fgcolor,
2765 pointIndex, nper[i], 0,
2766 xd->lend, xd->ljoin, xd->lmitre));
2767 pointIndex = pointIndex + nper[i];
2768 }
2769 } else if(R_ALPHA(gc->col) > 0) {
2770 if(xd->have_alpha) {
2771 r = xd->clip;
2772 gsetcliprect(xd->bm, xd->clip);
2773 gcopy(xd->bm2, xd->bm, r);
2774 pointIndex = points;
2775 for (i = 0; i < npoly; i++) {
2776 gdrawpolygon(xd->bm2, xd->lwd, xd->lty, xd->fgcolor,
2777 pointIndex, nper[i], 0,
2778 xd->lend, xd->ljoin, xd->lmitre);
2779 pointIndex = pointIndex + nper[i];
2780 }
2781 DRAW2(gc->col);
2782 } else WARN_SEMI_TRANS;
2783 }
2784 vmaxset(vmax);
2785 SH;
2786 }
2787
doRaster(unsigned int * raster,int x,int y,int w,int h,double rot,pDevDesc dd)2788 static void doRaster(unsigned int *raster, int x, int y, int w, int h,
2789 double rot, pDevDesc dd)
2790 {
2791 const void *vmax = vmaxget();
2792 gadesc *xd = (gadesc *) dd->deviceSpecific;
2793 rect dr = rect(x, y, w, h);
2794 image img;
2795 GAbyte *imageData;
2796
2797 TRACEDEVGA("raster");
2798
2799 /* Create image object */
2800 img = newimage(w, h, 32);
2801
2802 /* Set the image pixels from the raster.
2803 Windows uses 0xaarrggbb.
2804 AlphaBlend requires pre-multiplied alpha, that is it uses
2805 (src + (1-alpha)*dest) for each pixel colour.
2806 We could re-order the lines here (top to bottom) to avoid a copy
2807 in imagetobitmap.
2808 */
2809 imageData = (GAbyte *) R_alloc(4*w*h, sizeof(GAbyte));
2810 for (int i = 0; i < w*h; i++) {
2811 GAbyte alpha = R_ALPHA(raster[i]);
2812 double fac = alpha/255.0;
2813 imageData[i*4 + 3] = alpha;
2814 imageData[i*4 + 2] = 0.49 + fac * R_RED(raster[i]);
2815 imageData[i*4 + 1] = 0.49 + fac * R_GREEN(raster[i]);
2816 imageData[i*4 + 0] = 0.49 + fac * R_BLUE(raster[i]);
2817 }
2818 setpixels(img, imageData);
2819 if(xd->kind != SCREEN) {
2820 gsetcliprect(xd->gawin, xd->clip);
2821 gcopyalpha2(xd->gawin, img, dr);
2822 } else {
2823 gsetcliprect(xd->bm, xd->clip);
2824 gcopyalpha2(xd->bm, img, dr);
2825 if(!xd->buffered)
2826 drawbits(xd);
2827 }
2828
2829 /* Tidy up */
2830 delimage(img);
2831 SH;
2832 vmaxset(vmax);
2833 }
2834
flipRaster(unsigned int * rasterImage,int imageWidth,int imageHeight,int invertX,int invertY,unsigned int * flippedRaster)2835 static void flipRaster(unsigned int *rasterImage,
2836 int imageWidth, int imageHeight,
2837 int invertX, int invertY,
2838 unsigned int *flippedRaster) {
2839 int i, j;
2840 int rowInc, rowOff, colInc, colOff;
2841
2842 if (invertX) {
2843 colInc = -1;
2844 colOff = imageWidth - 1;
2845 } else {
2846 colInc = 1;
2847 colOff = 0;
2848 }
2849 if (invertY) {
2850 rowInc = -1;
2851 rowOff = imageHeight - 1;
2852 } else {
2853 rowInc = 1;
2854 rowOff = 0;
2855 }
2856
2857 for (i = 0; i < imageHeight ;i++) {
2858 for (j = 0; j < imageWidth; j++) {
2859 int row = (rowInc*i + rowOff);
2860 int col = (colInc*j + colOff);
2861 flippedRaster[i*imageWidth + j] =
2862 rasterImage[row*imageWidth + col];
2863 }
2864 }
2865 }
2866
GA_Raster(unsigned int * raster,int w,int h,double x,double y,double width,double height,double rot,Rboolean interpolate,const pGEcontext gc,pDevDesc dd)2867 static void GA_Raster(unsigned int *raster, int w, int h,
2868 double x, double y,
2869 double width, double height,
2870 double rot,
2871 Rboolean interpolate,
2872 const pGEcontext gc, pDevDesc dd)
2873 {
2874 const void *vmax = vmaxget();
2875 double angle = rot*M_PI/180;
2876 unsigned int *image = raster;
2877 int imageWidth = w, imageHeight = h;
2878 Rboolean invertX = FALSE;
2879 Rboolean invertY = TRUE;
2880
2881 /* The alphablend code cannot handle negative width or height */
2882 if (height < 0) {
2883 height = -height;
2884 invertY = FALSE;
2885 }
2886 if (width < 0) {
2887 width = -width;
2888 invertX = TRUE;
2889 }
2890
2891 if (interpolate) {
2892 int newW = (int) (width + .5), newH = (int) (height + .5);
2893 unsigned int *newRaster;
2894
2895 newRaster = (unsigned int *) R_alloc(newW * newH,
2896 sizeof(unsigned int));
2897 R_GE_rasterInterpolate(image, w, h, newRaster, newW, newH);
2898 image = newRaster;
2899 imageWidth = newW;
2900 imageHeight = newH;
2901
2902 } else {
2903 /* Even if not interpolating, have to explicitly scale here
2904 * before doing rotation, so that image to rotate
2905 * is the right size AND so that can adjust (x, y)
2906 * correctly
2907 */
2908 int newW = (int) (width + .5), newH = (int) (height + .5);
2909 unsigned int *newRaster;
2910
2911 newRaster = (unsigned int *) R_alloc(newW * newH,
2912 sizeof(unsigned int));
2913 R_GE_rasterScale(image, w, h, newRaster, newW, newH);
2914 image = newRaster;
2915 imageWidth = newW;
2916 imageHeight = newH;
2917 }
2918
2919 if (invertX) {
2920 /* convert (x, y) from bottom-left to top-left */
2921 x -= imageWidth*cos(angle);
2922 if (angle != 0) y -= imageWidth*sin(angle);
2923 }
2924 if (!invertY) {
2925 /* convert (x, y) from bottom-left to top-left */
2926 y -= imageHeight*cos(angle);
2927 if (angle != 0) x -= imageHeight*sin(angle);
2928 }
2929
2930 if (angle != 0) {
2931 int newW, newH;
2932 double xoff, yoff;
2933 unsigned int *resizedRaster, *rotatedRaster;
2934
2935 R_GE_rasterRotatedSize(imageWidth, imageHeight, angle, &newW, &newH);
2936 R_GE_rasterRotatedOffset(imageWidth, imageHeight, angle, 0,
2937 &xoff, &yoff);
2938
2939 resizedRaster = (unsigned int *) R_alloc(newW * newH,
2940 sizeof(unsigned int));
2941 R_GE_rasterResizeForRotation(image, imageWidth, imageHeight,
2942 resizedRaster, newW, newH, gc);
2943
2944 rotatedRaster = (unsigned int *) R_alloc(newW * newH,
2945 sizeof(unsigned int));
2946 R_GE_rasterRotate(resizedRaster, newW, newH, angle, rotatedRaster, gc,
2947 /* Threshold alpha to
2948 * transparent/opaque only
2949 */
2950 FALSE);
2951
2952 /*
2953 * Adjust (x, y) for resized and rotated image
2954 */
2955 x -= (newW - imageWidth)/2 + xoff;
2956 y -= (newH - imageHeight)/2 - yoff;
2957
2958 image = rotatedRaster;
2959 imageWidth = newW;
2960 imageHeight = newH;
2961 }
2962
2963 if (invertX || invertY) {
2964 unsigned int *flippedRaster;
2965
2966 flippedRaster = (unsigned int *) R_alloc(imageWidth * imageHeight,
2967 sizeof(unsigned int));
2968 flipRaster(image, imageWidth, imageHeight,
2969 invertX, invertY, flippedRaster);
2970 image = flippedRaster;
2971 }
2972
2973 doRaster(image, (int) (x + .5), (int) (y + .5),
2974 imageWidth, imageHeight, rot, dd);
2975
2976 vmaxset(vmax);
2977 }
2978
GA_Cap(pDevDesc dd)2979 static SEXP GA_Cap(pDevDesc dd)
2980 {
2981 gadesc *xd = (gadesc *) dd->deviceSpecific;
2982 SEXP dim, raster = R_NilValue;
2983 image img = NULL;
2984 GAbyte *screenData;
2985
2986 /* These in-place conversions are ok */
2987 TRACEDEVGA("cap");
2988
2989 /* Only make sense for on-screen device */
2990 if(xd->kind == SCREEN) {
2991 img = bitmaptoimage(xd->gawin);
2992 if (imagedepth(img) == 8) img = convert8to32(img);
2993
2994 if (img) {
2995 int width = imagewidth(img), height = imageheight(img),
2996 size = width*height;
2997 unsigned int *rint;
2998
2999 screenData = getpixels(img);
3000
3001 PROTECT(raster = allocVector(INTSXP, size));
3002
3003 /* Copy each byte of screen to an R matrix.
3004 * The ARGB32 needs to be converted to R's ABGR32 */
3005 rint = (unsigned int *) INTEGER(raster);
3006 for (int i = 0; i < size; i++)
3007 rint[i] = R_RGBA(screenData[i*4 + 2],
3008 screenData[i*4 + 1],
3009 screenData[i*4 + 0],
3010 255);
3011 PROTECT(dim = allocVector(INTSXP, 2));
3012 INTEGER(dim)[0] = height;
3013 INTEGER(dim)[1] = width;
3014 setAttrib(raster, R_DimSymbol, dim);
3015
3016 UNPROTECT(2);
3017 }
3018
3019 /* Tidy up */
3020 delimage(img);
3021 }
3022
3023
3024 return raster;
3025 }
3026
3027 /********************************************************/
3028 /* device_Text should have the side-effect that the */
3029 /* given text is drawn at the given location */
3030 /* the text should be rotated according to rot (degrees)*/
3031 /* the location is in an arbitrary coordinate system */
3032 /* and this function is responsible for converting the */
3033 /* location to DEVICE coordinates using GConvert */
3034 /********************************************************/
3035
GA_Text0(double x,double y,const char * str,int enc,double rot,double hadj,const pGEcontext gc,pDevDesc dd)3036 static void GA_Text0(double x, double y, const char *str, int enc,
3037 double rot, double hadj,
3038 const pGEcontext gc,
3039 pDevDesc dd)
3040 {
3041 double pixs, xl, yl, rot1;
3042 gadesc *xd = (gadesc *) dd->deviceSpecific;
3043
3044 pixs = - 1;
3045 xl = 0.0;
3046 yl = -pixs;
3047 rot1 = rot * DEG2RAD;
3048 x += -xl * cos(rot1) + yl * sin(rot1);
3049 y -= -xl * sin(rot1) - yl * cos(rot1);
3050
3051 SetFont(gc, rot, xd);
3052 SetColor(gc->col, gc->gamma, xd);
3053 if (R_OPAQUE(gc->col)) {
3054 if(gc->fontface != 5) {
3055 /* As from 2.7.0 can use Unicode always */
3056 int n = strlen(str), cnt;
3057 R_CheckStack2(sizeof(wchar_t)*(n+1));
3058 wchar_t wc[n+1];/* only need terminator to debug */
3059 cnt = (enc == CE_UTF8) ?
3060 Rf_utf8towcs(wc, str, n+1): mbstowcs(wc, str, n);
3061 /* These macros need to be wrapped in braces */
3062 DRAW(gwdrawstr1(_d, xd->font, xd->fgcolor, pt(x, y),
3063 wc, cnt, hadj));
3064 } else {
3065 DRAW(gdrawstr1(_d, xd->font, xd->fgcolor, pt(x, y), str, hadj));
3066 }
3067 } else if(R_ALPHA(gc->col) > 0) {
3068 /* it is too hard to get a correct bounding box */
3069 if(xd->have_alpha) {
3070 rect r = xd->clip;
3071 r = getregion(xd);
3072 gsetcliprect(xd->bm, xd->clip);
3073 gcopy(xd->bm2, xd->bm, r);
3074 if(gc->fontface != 5) {
3075 int n = strlen(str), cnt;
3076 R_CheckStack2(sizeof(wchar_t)*(n+1));
3077 wchar_t wc[n+1];
3078 cnt = (enc == CE_UTF8) ?
3079 Rf_utf8towcs(wc, str, n+1): mbstowcs(wc, str, n);
3080 gwdrawstr1(xd->bm2, xd->font, xd->fgcolor, pt(x, y),
3081 wc, cnt, hadj);
3082 } else
3083 gdrawstr1(xd->bm2, xd->font, xd->fgcolor, pt(x, y), str, hadj);
3084 DRAW2(gc->col);
3085 } else WARN_SEMI_TRANS;
3086 }
3087 SH;
3088 }
3089
GA_Text(double x,double y,const char * str,double rot,double hadj,const pGEcontext gc,pDevDesc dd)3090 static void GA_Text(double x, double y, const char *str,
3091 double rot, double hadj,
3092 const pGEcontext gc,
3093 pDevDesc dd)
3094 {
3095 GA_Text0(x, y, str, CE_NATIVE, rot, hadj, gc, dd);
3096 }
3097
GA_Text_UTF8(double x,double y,const char * str,double rot,double hadj,const pGEcontext gc,pDevDesc dd)3098 static void GA_Text_UTF8(double x, double y, const char *str,
3099 double rot, double hadj,
3100 const pGEcontext gc,
3101 pDevDesc dd)
3102 {
3103 GA_Text0(x, y, str, CE_UTF8, rot, hadj, gc, dd);
3104 }
3105
3106
3107 /********************************************************/
3108 /* device_Locator should return the location of the next*/
3109 /* mouse click (in DEVICE coordinates; GLocator is */
3110 /* responsible for any conversions) */
3111 /* not all devices will do anything (e.g., postscript) */
3112 /********************************************************/
3113
donelocator(void * data)3114 static void donelocator(void *data)
3115 {
3116 gadesc *xd;
3117 xd = (gadesc *)data;
3118 addto(xd->gawin);
3119 gchangemenubar(xd->mbar);
3120 if (xd->stoploc) {
3121 hide(xd->stoploc);
3122 show(xd->gawin);
3123 }
3124 gsetcursor(xd->gawin, ArrowCursor);
3125 gchangepopup(xd->gawin, xd->grpopup);
3126 addto(xd->gawin);
3127 setstatus(_("R Graphics"));
3128 xd->locator = FALSE;
3129 }
3130
3131 static void GA_onExit(pDevDesc dd);
3132
GA_Locator(double * x,double * y,pDevDesc dd)3133 static Rboolean GA_Locator(double *x, double *y, pDevDesc dd)
3134 {
3135 gadesc *xd = (gadesc *) dd->deviceSpecific;
3136 RCNTXT cntxt;
3137
3138 if (xd->kind != SCREEN)
3139 return FALSE;
3140 if (xd->holdlevel > 0)
3141 error(_("attempt to use the locator after dev.hold()"));
3142 xd->locator = TRUE;
3143 xd->clicked = 0;
3144 show(xd->gawin);
3145 addto(xd->gawin);
3146 gchangemenubar(xd->mbarloc);
3147 if (xd->stoploc) {
3148 show(xd->stoploc);
3149 show(xd->gawin);
3150 }
3151 gchangepopup(xd->gawin, xd->locpopup);
3152 gsetcursor(xd->gawin, CrossCursor);
3153 setstatus(G_("Locator is active"));
3154
3155 /* set up a context which will clean up if there's an error */
3156 begincontext(&cntxt, CTXT_CCODE, R_NilValue, R_NilValue, R_NilValue,
3157 R_NilValue, R_NilValue);
3158 cntxt.cend = &donelocator;
3159 cntxt.cenddata = xd;
3160 xd->cntxt = (void *) &cntxt;
3161
3162 /* and an exit handler in case the window gets closed */
3163 dd->onExit = GA_onExit;
3164
3165 while (!xd->clicked) {
3166 SH;
3167 R_WaitEvent();
3168 R_ProcessEvents();
3169 if (!dd->deviceSpecific) { /* closing the window on other systems calls error().
3170 But that is not safe on Windows, so we NULL the device
3171 specific field and call error() here instead. */
3172 error(_("graphics device closed during call to locator or identify"));
3173 }
3174 }
3175
3176 dd->onExit = NULL;
3177 xd->cntxt = NULL;
3178
3179 endcontext(&cntxt);
3180 donelocator((void *)xd);
3181
3182 if (xd->clicked == 1) {
3183 *x = xd->px;
3184 *y = xd->py;
3185 return TRUE;
3186 } else
3187 return FALSE;
3188 }
3189
3190 /********************************************************/
3191 /* device_Mode is called whenever the graphics engine */
3192 /* starts drawing (mode=1) or stops drawing (mode=0) */
3193 /* the device is not required to do anything */
3194 /********************************************************/
3195
3196 /* Set Graphics mode - not needed for X11 */
GA_Mode(int mode,pDevDesc dd)3197 static void GA_Mode(int mode, pDevDesc dd)
3198 {
3199 }
3200
GA_setPattern(SEXP pattern,pDevDesc dd)3201 static SEXP GA_setPattern(SEXP pattern, pDevDesc dd) {
3202 return R_NilValue;
3203 }
3204
GA_releasePattern(SEXP ref,pDevDesc dd)3205 static void GA_releasePattern(SEXP ref, pDevDesc dd) {}
3206
GA_setClipPath(SEXP path,SEXP ref,pDevDesc dd)3207 static SEXP GA_setClipPath(SEXP path, SEXP ref, pDevDesc dd) {
3208 return R_NilValue;
3209 }
3210
GA_releaseClipPath(SEXP ref,pDevDesc dd)3211 static void GA_releaseClipPath(SEXP ref, pDevDesc dd) {}
3212
GA_setMask(SEXP path,SEXP ref,pDevDesc dd)3213 static SEXP GA_setMask(SEXP path, SEXP ref, pDevDesc dd) {
3214 return R_NilValue;
3215 }
3216
GA_releaseMask(SEXP ref,pDevDesc dd)3217 static void GA_releaseMask(SEXP ref, pDevDesc dd) {}
3218
3219
3220 /********************************************************/
3221 /* the device-driver entry point is given a device */
3222 /* description structure that it must set up. this */
3223 /* involves several important jobs ... */
3224 /* (1) it must ALLOCATE a new device-specific parameters*/
3225 /* structure and FREE that structure if anything goes */
3226 /* wrong (i.e., it won't report a successful setup to */
3227 /* the graphics engine (the graphics engine is NOT */
3228 /* responsible for allocating or freeing device-specific*/
3229 /* resources or parameters) */
3230 /* (2) it must initialise the device-specific resources */
3231 /* and parameters (mostly done by calling device_Open) */
3232 /* (3) it must initialise the generic graphical */
3233 /* parameters that are not initialised by GInit (because*/
3234 /* only the device knows what values they should have) */
3235 /* see Graphics.h for the official list of these */
3236 /* (4) it may reset generic graphics parameters that */
3237 /* have already been initialised by GInit (although you */
3238 /* should know what you are doing if you do this) */
3239 /* (5) it must attach the device-specific parameters */
3240 /* structure to the device description structure */
3241 /* e.g., dd->deviceSpecific = (void *) xd; */
3242 /* (6) it must FREE the overall device description if */
3243 /* it wants to bail out to the top-level */
3244 /* the graphics engine is responsible for allocating */
3245 /* the device description and freeing it in most cases */
3246 /* but if the device driver freaks out it needs to do */
3247 /* the clean-up itself */
3248 /********************************************************/
3249
3250
3251 static
GADeviceDriver(pDevDesc dd,const char * display,double width,double height,double pointsize,Rboolean recording,int resize,int bg,int canvas,double gamma,int xpos,int ypos,Rboolean buffered,SEXP psenv,Rboolean restoreConsole,const char * title,Rboolean clickToConfirm,Rboolean fillOddEven,const char * family,int quality)3252 Rboolean GADeviceDriver(pDevDesc dd, const char *display, double width,
3253 double height, double pointsize,
3254 Rboolean recording, int resize, int bg, int canvas,
3255 double gamma, int xpos, int ypos, Rboolean buffered,
3256 SEXP psenv, Rboolean restoreConsole,
3257 const char *title, Rboolean clickToConfirm,
3258 Rboolean fillOddEven, const char *family,
3259 int quality)
3260 {
3261 /* if need to bail out with some sort of "error" then */
3262 /* must free(dd) */
3263
3264 int ps; /* This really is in (big) points */
3265 gadesc *xd;
3266 rect rr;
3267
3268 /* allocate new device description */
3269 if (!(xd = (gadesc *) malloc(sizeof(gadesc)))) {
3270 warning("allocation failed in GADeviceDriver");
3271 return FALSE;
3272 }
3273
3274 /* from here on, if need to bail out with "error", must also */
3275 /* free(xd) */
3276
3277 ps = pointsize;
3278 if (ps < 1) ps = 12;
3279 /* Ensures a font is selected at first use */
3280 xd->font = NULL;
3281 xd->fontface = -1;
3282 xd->fontsize = -1;
3283 xd->fontangle = 0.0;
3284 xd->fontfamily[0] = '\0';
3285 xd->basefontsize = ps ;
3286 dd->startfont = 1;
3287 dd->startps = ps;
3288 dd->startlty = LTY_SOLID;
3289 dd->startgamma = gamma;
3290 xd->bm = NULL;
3291 xd->bm2 = NULL;
3292 xd->have_alpha = FALSE; /* selectively overridden in GA_Open */
3293 xd->warn_trans = FALSE;
3294 strncpy(xd->title, title, 101);
3295 xd->title[100] = '\0';
3296 strncpy(xd->basefontfamily, family, 101);
3297 xd->basefontfamily[100] = '\0';
3298 xd->fontquality = quality;
3299 xd->doSetPolyFill = TRUE; /* will only set it once */
3300 xd->fillOddEven = fillOddEven;
3301
3302 /* Start the Device Driver and Hardcopy. */
3303
3304 if (!GA_Open(dd, xd, display, width, height, recording, resize, canvas,
3305 gamma, xpos, ypos, bg)) {
3306 warning("opening device failed");
3307 free(xd);
3308 return FALSE;
3309 }
3310 dd->deviceSpecific = (void *) xd;
3311 /* Set up Data Structures */
3312
3313 dd->close = GA_Close;
3314 dd->activate = GA_Activate;
3315 dd->deactivate = GA_Deactivate;
3316 dd->size = GA_Size;
3317 dd->newPage = GA_NewPage;
3318 dd->clip = GA_Clip;
3319 dd->strWidth = GA_StrWidth;
3320 dd->text = GA_Text;
3321 dd->rect = GA_Rect;
3322 dd->circle = GA_Circle;
3323 dd->line = GA_Line;
3324 dd->polyline = GA_Polyline;
3325 dd->polygon = GA_Polygon;
3326 dd->path = GA_Path;
3327 dd->raster = GA_Raster;
3328 dd->cap = GA_Cap;
3329 dd->locator = GA_Locator;
3330 dd->mode = GA_Mode;
3331 dd->metricInfo = GA_MetricInfo;
3332 dd->newFrameConfirm = clickToConfirm ? GA_NewFrameConfirm : NULL;
3333 dd->hasTextUTF8 = TRUE;
3334 dd->strWidthUTF8 = GA_StrWidth_UTF8;
3335 dd->textUTF8 = GA_Text_UTF8;
3336 dd->useRotatedTextInContour = TRUE;
3337 xd->cntxt = NULL;
3338 dd->holdflush = GA_holdflush;
3339 xd->holdlevel = 0;
3340
3341 dd->setPattern = GA_setPattern;
3342 dd->releasePattern = GA_releasePattern;
3343 dd->setClipPath = GA_setClipPath;
3344 dd->releaseClipPath = GA_releaseClipPath;
3345 dd->setMask = GA_setMask;
3346 dd->releaseMask = GA_releaseMask;
3347
3348 dd->haveRaster = 2; /* full support */
3349 dd->haveCapture = dd->haveLocator = (xd->kind == SCREEN) ? 2 : 1;
3350 dd->haveTransparency = 2;
3351 switch(xd->kind) {
3352 case SCREEN:
3353 dd->haveTransparentBg = 3;
3354 break;
3355 case PRINTER:
3356 case METAFILE:
3357 case PNG:
3358 dd->haveTransparentBg = 2;
3359 break;
3360 default: /* JPEG, BMP, TIFF */
3361 dd->haveTransparentBg = 1;
3362 break;
3363 }
3364 /* set graphics parameters that must be set by device driver */
3365 /* Window Dimensions in Pixels */
3366 rr = getrect(xd->gawin);
3367 dd->left = (xd->kind == PRINTER) ? rr.x : 0; /* left */
3368 dd->right = dd->left + rr.width - 0.0001; /* right */
3369 dd->top = (xd->kind == PRINTER) ? rr.y : 0; /* top */
3370 dd->bottom = dd->top + rr.height - 0.0001; /* bottom */
3371 dd->clipLeft = dd->left; dd->clipRight = dd->right;
3372 dd->clipBottom = dd->bottom; dd->clipTop = dd->top;
3373
3374 if (resize == 3) { /* might have got a shrunken window */
3375 int iw = width/pixelWidth(NULL) + 0.5,
3376 ih = height/pixelHeight(NULL) + 0.5;
3377 xd->origWidth = dd->right = iw;
3378 xd->origHeight = dd->bottom = ih;
3379 }
3380
3381 dd->startps = ps * xd->rescale_factor;
3382 if (xd->kind > METAFILE && xd->res_dpi > 0) ps *= xd->res_dpi/72.0;
3383
3384 if (xd->kind <= METAFILE) {
3385 /* it is 12 *point*, not 12 pixel */
3386 double ps0 = ps * xd->rescale_factor;
3387 dd->cra[0] = 0.9 * ps0 * devicepixelsx(xd->gawin) / 72.0;
3388 dd->cra[1] = 1.2 * ps0 * devicepixelsy(xd->gawin) / 72.0;
3389 } else {
3390 dd->cra[0] = 0.9 * ps;
3391 dd->cra[1] = 1.2 * ps;
3392 }
3393
3394 /* Character Addressing Offsets */
3395 /* These are used to plot a single plotting character */
3396 /* so that it is exactly over the plotting point */
3397
3398 dd->xCharOffset = 0.50;
3399 dd->yCharOffset = 0.40;
3400 dd->yLineBias = 0.2;
3401
3402 /* Inches per raster unit */
3403
3404 if (xd->kind <= METAFILE) { /* non-screen devices set NA_real_ */
3405 if (R_FINITE(user_xpinch) && user_xpinch > 0.0)
3406 dd->ipr[0] = 1.0/user_xpinch;
3407 else
3408 dd->ipr[0] = pixelWidth(xd->gawin);
3409 if (R_FINITE(user_ypinch) && user_ypinch > 0.0)
3410 dd->ipr[1] = 1.0/user_ypinch;
3411 else
3412 dd->ipr[1] = pixelHeight(xd->gawin);
3413 } else if (xd->res_dpi > 0) {
3414 dd->ipr[0] = dd->ipr[1] = 1.0/xd->res_dpi;
3415 } else {
3416 dd->ipr[0] = dd->ipr[1] = 1.0/72.0;
3417 }
3418
3419
3420 /* Device capabilities */
3421 dd->canClip= TRUE;
3422 dd->canHAdj = 1; /* 0, 0.5, 1 */
3423 dd->canChangeGamma = FALSE;
3424
3425 /* initialise device description (most of the work */
3426 /* has been done in GA_Open) */
3427
3428 xd->resize = (resize == 3) || ismdi(); // MDI windows may be zoomed automatically
3429 xd->locator = FALSE;
3430 xd->buffered = buffered;
3431 xd->psenv = psenv;
3432 {
3433 SEXP timeouts = GetOption1(install("windowsTimeouts"));
3434 if(isInteger(timeouts)){
3435 xd->timeafter = INTEGER(timeouts)[0];
3436 xd->timesince = INTEGER(timeouts)[1];
3437 } else {
3438 warning(_("option 'windowsTimeouts' should be integer"));
3439 xd->timeafter = 100;
3440 xd->timesince = 500;
3441 }
3442 }
3443 dd->displayListOn = (xd->kind == SCREEN);
3444 dd->deviceVersion = R_GE_definitions;
3445 if (RConsole && restoreConsole) show(RConsole);
3446 return TRUE;
3447 }
3448
savePlot(SEXP args)3449 SEXP savePlot(SEXP args)
3450 {
3451 SEXP filename, type;
3452 const char *fn, *tp; char display[550];
3453 int device;
3454 pDevDesc dd;
3455 Rboolean restoreConsole;
3456
3457 args = CDR(args); /* skip entry point name */
3458 device = asInteger(CAR(args));
3459 if(device < 1 || device > NumDevices())
3460 error(_("invalid device number in 'savePlot'"));
3461 dd = GEgetDevice(device - 1)->dev;
3462 if(!dd) error(_("invalid device in 'savePlot'"));
3463 filename = CADR(args);
3464 if (!isString(filename) || LENGTH(filename) != 1)
3465 error(_("invalid filename argument in 'savePlot'"));
3466 /* in 2.8.0 this will always be passed as native, but be conserative */
3467 fn = translateCharFP(STRING_ELT(filename, 0));
3468 type = CADDR(args);
3469 if (!isString(type) || LENGTH(type) != 1)
3470 error(_("invalid type argument in 'savePlot'"));
3471 tp = CHAR(STRING_ELT(type, 0));
3472 restoreConsole = asLogical(CADDDR(args));
3473
3474 if(!strcmp(tp, "png")) {
3475 SaveAsPng(dd, fn);
3476 } else if (!strcmp(tp,"bmp")) {
3477 SaveAsBmp(dd,fn);
3478 } else if (!strcmp(tp,"tiff")) {
3479 SaveAsTiff(dd,fn);
3480 } else if(!strcmp(tp, "jpeg") || !strcmp(tp,"jpg")) {
3481 /*Default quality suggested in libjpeg*/
3482 SaveAsJpeg(dd, 75, fn);
3483 } else if(!strcmp(tp, "tiff") || !strcmp(tp,"tif")) {
3484 SaveAsTiff(dd, fn);
3485 } else if (!strcmp(tp, "wmf") || !strcmp(tp, "emf")) {
3486 if(strlen(fn) > 512) {
3487 askok(G_("file path selected is too long: only 512 bytes are allowed"));
3488 return R_NilValue;
3489 }
3490 snprintf(display, 550, "win.metafile:%s", fn);
3491 SaveAsWin(dd, display, restoreConsole);
3492 } else if (!strcmp(tp, "ps") || !strcmp(tp, "eps")) {
3493 SaveAsPostscript(dd, fn);
3494 } else if (!strcmp(tp, "pdf")) {
3495 SaveAsPDF(dd, fn);
3496 } else
3497 error(_("unknown type in savePlot"));
3498 return R_NilValue;
3499 }
3500
3501
3502 static int png_rows = 0;
3503
privategetpixel2(void * d,int i,int j)3504 static unsigned int privategetpixel2(void *d,int i, int j)
3505 {
3506 rgb c;
3507 c = ((rgb *)d)[i*png_rows + j];
3508 return c | 0xff000000;
3509 }
3510
3511 /* This is the device version */
3512 /* Values of res > 0 are used to set the resolution in the file */
SaveAsBitmap(pDevDesc dd,int res)3513 static void SaveAsBitmap(pDevDesc dd, int res)
3514 {
3515 rect r, r2;
3516 gadesc *xd = (gadesc *) dd->deviceSpecific;
3517 unsigned char *data;
3518
3519 r = ggetcliprect(xd->gawin);
3520 gsetcliprect(xd->gawin, r2 = getrect(xd->gawin));
3521 if(xd->fp || xd->kind == TIFF) {
3522 getbitmapdata2(xd->gawin, &data);
3523 if(data) {
3524 png_rows = r2.width;
3525 if (xd->kind == PNG)
3526 R_SaveAsPng(data, xd->windowWidth, xd->windowHeight,
3527 privategetpixel2, 0, xd->fp,
3528 R_OPAQUE(xd->bg) ? 0 : xd->pngtrans, res) ;
3529 else if (xd->kind == JPEG)
3530 R_SaveAsJpeg(data, xd->windowWidth, xd->windowHeight,
3531 privategetpixel2, 0, xd->quality, xd->fp, res) ;
3532 else if (xd->kind == TIFF) {
3533 char buf[600];
3534 snprintf(buf, 600, xd->filename, xd->npage - 1);
3535 R_SaveAsTIFF(data, xd->windowWidth, xd->windowHeight,
3536 privategetpixel2, 0, buf, res, xd->quality) ;
3537 } else
3538 R_SaveAsBmp(data, xd->windowWidth, xd->windowHeight,
3539 privategetpixel2, 0, xd->fp, res);
3540 free(data);
3541 } else
3542 warning(_("processing of the plot ran out of memory"));
3543 if(xd->fp) fclose(xd->fp);
3544 }
3545 gsetcliprect(xd->gawin, r);
3546 xd->fp = NULL;
3547 }
3548
3549 /* These are the menu item versions */
SaveAsPng(pDevDesc dd,const char * fn)3550 static void SaveAsPng(pDevDesc dd, const char *fn)
3551 {
3552 FILE *fp;
3553 rect r, r2;
3554 unsigned char *data;
3555 gadesc *xd = (gadesc *) dd->deviceSpecific;
3556
3557 if ((fp = R_fopen(fn, "wb")) == NULL) {
3558 char msg[MAX_PATH+32];
3559
3560 strcpy(msg, "Impossible to open ");
3561 strncat(msg, fn, MAX_PATH);
3562 R_ShowMessage(msg);
3563 return;
3564 }
3565 r = ggetcliprect(xd->bm);
3566 gsetcliprect(xd->bm, r2 = getrect(xd->bm));
3567 getbitmapdata2(xd->bm, &data);
3568 if(data) {
3569 png_rows = r2.width;
3570 R_SaveAsPng(data, xd->windowWidth, xd->windowHeight,
3571 privategetpixel2, 0, fp, 0, 0) ;
3572 free(data);
3573 } else
3574 warning(_("processing of the plot ran out of memory"));
3575 gsetcliprect(xd->bm, r);
3576 fclose(fp);
3577 }
3578
SaveAsJpeg(pDevDesc dd,int quality,const char * fn)3579 static void SaveAsJpeg(pDevDesc dd, int quality, const char *fn)
3580 {
3581 FILE *fp;
3582 rect r, r2;
3583 unsigned char *data;
3584 gadesc *xd = (gadesc *) dd->deviceSpecific;
3585
3586 if ((fp = R_fopen(fn,"wb")) == NULL) {
3587 char msg[MAX_PATH+32];
3588 strcpy(msg, "Impossible to open ");
3589 strncat(msg, fn, MAX_PATH);
3590 R_ShowMessage(msg);
3591 return;
3592 }
3593 r = ggetcliprect(xd->bm);
3594 gsetcliprect(xd->bm, r2 = getrect(xd->bm));
3595 getbitmapdata2(xd->bm, &data);
3596 if(data) {
3597 png_rows = r2.width;
3598 R_SaveAsJpeg(data,xd->windowWidth, xd->windowHeight,
3599 privategetpixel2, 0, quality, fp, 0) ;
3600 free(data);
3601 } else
3602 warning(_("processing of the plot ran out of memory"));
3603 gsetcliprect(xd->bm, r);
3604 fclose(fp);
3605 }
3606
3607
SaveAsBmp(pDevDesc dd,const char * fn)3608 static void SaveAsBmp(pDevDesc dd, const char *fn)
3609 {
3610 FILE *fp;
3611 rect r, r2;
3612 unsigned char *data;
3613 gadesc *xd = (gadesc *) dd->deviceSpecific;
3614
3615 if ((fp = R_fopen(fn, "wb")) == NULL) {
3616 char msg[MAX_PATH+32];
3617
3618 strcpy(msg, _("Impossible to open "));
3619 strncat(msg, fn, MAX_PATH);
3620 R_ShowMessage(msg);
3621 return;
3622 }
3623 r = ggetcliprect(xd->bm);
3624 gsetcliprect(xd->bm, r2 = getrect(xd->bm));
3625
3626 getbitmapdata2(xd->bm, &data);
3627 if(data) {
3628 png_rows = r2.width;
3629 R_SaveAsBmp(data, xd->windowWidth, xd->windowHeight,
3630 privategetpixel2, 0, fp, 0) ;
3631 free(data);
3632 } else
3633 warning(_("processing of the plot ran out of memory"));
3634 gsetcliprect(xd->bm, r);
3635 fclose(fp);
3636 }
3637
SaveAsTiff(pDevDesc dd,const char * fn)3638 static void SaveAsTiff(pDevDesc dd, const char *fn)
3639 {
3640 rect r, r2;
3641 unsigned char *data;
3642 gadesc *xd = (gadesc *) dd->deviceSpecific;
3643
3644 r = ggetcliprect(xd->bm);
3645 gsetcliprect(xd->bm, r2 = getrect(xd->bm));
3646
3647 getbitmapdata2(xd->bm, &data);
3648 if(data) {
3649 png_rows = r2.width;
3650 R_SaveAsTIFF(data, xd->windowWidth, xd->windowHeight,
3651 privategetpixel2, 0, fn, 0, 1 /* no compression */) ;
3652 free(data);
3653 } else
3654 warning(_("processing of the plot ran out of memory"));
3655 gsetcliprect(xd->bm, r);
3656 }
3657
3658 /* This is Guido's devga device, 'ga' for GraphApp. */
3659
3660 #ifndef CLEARTYPE_QUALITY
3661 # define CLEARTYPE_QUALITY 5
3662 #endif
3663
devga(SEXP args)3664 SEXP devga(SEXP args)
3665 {
3666 pGEDevDesc gdd;
3667 const char *display, *title, *family;
3668 const void *vmax;
3669 double height, width, ps, xpinch, ypinch, gamma;
3670 int recording = 0, resize = 1, bg, canvas, xpos, ypos, buffered, quality;
3671 Rboolean restoreConsole, clickToConfirm, fillOddEven;
3672 SEXP sc, psenv;
3673
3674 vmax = vmaxget();
3675 args = CDR(args); /* skip entry point name */
3676 display = translateCharFP(STRING_ELT(CAR(args), 0));
3677 args = CDR(args);
3678 width = asReal(CAR(args));
3679 args = CDR(args);
3680 height = asReal(CAR(args));
3681 args = CDR(args);
3682 if (width <= 0 || height <= 0)
3683 error(_("invalid 'width' or 'height'"));
3684 ps = asReal(CAR(args));
3685 args = CDR(args);
3686 recording = asLogical(CAR(args));
3687 if (recording == NA_LOGICAL)
3688 error(_("invalid value of '%s'"), "record");
3689 args = CDR(args);
3690 resize = asInteger(CAR(args));
3691 if (resize == NA_INTEGER)
3692 error(_("invalid value of '%s'"), "rescale");
3693 args = CDR(args);
3694 xpinch = asReal(CAR(args));
3695 args = CDR(args);
3696 ypinch = asReal(CAR(args));
3697 args = CDR(args);
3698 sc = CAR(args);
3699 if (!isString(sc) && !isInteger(sc) && !isLogical(sc) && !isReal(sc))
3700 error(_("invalid value of '%s'"), "canvas");
3701 canvas = RGBpar(sc, 0);
3702 args = CDR(args);
3703 gamma = asReal(CAR(args));
3704 args = CDR(args);
3705 xpos = asInteger(CAR(args)); /* used for res in png/jpeg/bmp */
3706 args = CDR(args);
3707 ypos = asInteger(CAR(args));
3708 args = CDR(args);
3709 buffered = asLogical(CAR(args));
3710 if (buffered == NA_LOGICAL)
3711 error(_("invalid value of '%s'"), "buffered");
3712 args = CDR(args);
3713 psenv = CAR(args);
3714 args = CDR(args);
3715 sc = CAR(args);
3716 if (!isString(sc) && !isInteger(sc) && !isLogical(sc) && !isReal(sc))
3717 error(_("invalid value of '%s'"), "bg");
3718 bg = RGBpar(sc, 0);
3719 args = CDR(args);
3720 restoreConsole = asLogical(CAR(args));
3721 args = CDR(args);
3722 sc = CAR(args);
3723 if (!isString(sc) || LENGTH(sc) != 1)
3724 error(_("invalid value of '%s'"), "title");
3725 title = CHAR(STRING_ELT(sc, 0));
3726 args = CDR(args);
3727 clickToConfirm = asLogical(CAR(args));
3728 args = CDR(args);
3729 fillOddEven = asLogical(CAR(args));
3730 if (fillOddEven == NA_LOGICAL)
3731 error(_("invalid value of '%s'"), "fillOddEven");
3732 args = CDR(args);
3733 sc = CAR(args);
3734 if (!isString(sc) || LENGTH(sc) != 1)
3735 error(_("invalid value of '%s'"), "family");
3736 family = CHAR(STRING_ELT(sc, 0));
3737 quality = DEFAULT_QUALITY;
3738 args = CDR(args);
3739 quality = asInteger(CAR(args));
3740 // printf("fontquality=%d\n", quality);
3741 switch (quality) {
3742 case 1: quality = DEFAULT_QUALITY; break;
3743 case 2: quality = NONANTIALIASED_QUALITY; break;
3744 case 3: quality = CLEARTYPE_QUALITY; break;
3745 case 4: quality = ANTIALIASED_QUALITY; break;
3746 default: quality = DEFAULT_QUALITY;
3747 }
3748
3749 R_GE_checkVersionOrDie(R_GE_version);
3750 R_CheckDeviceAvailable();
3751 BEGIN_SUSPEND_INTERRUPTS {
3752 pDevDesc dev;
3753 char type[100], *file = NULL, fn[MAX_PATH];
3754 strcpy(type, "windows");
3755 if (display[0]) {
3756 strncpy(type, display, 100 - 1);
3757 type[100 - 1] = '\0';
3758 char *p = strchr(display, ':');
3759 if (p) {
3760 strncpy(fn, p+1, MAX_PATH - 1);
3761 fn[MAX_PATH - 1] = '\0';
3762 file = fn;
3763 }
3764 // Package tkrplot assumes the exact form here,
3765 // but remove suffix for all the others.
3766 p = strchr(type, ':');
3767 if(p && strncmp(display, "win.metafile", 12)) *p = '\0';
3768 }
3769 /* Allocate and initialize the device driver data */
3770 if (!(dev = (pDevDesc) calloc(1, sizeof(DevDesc)))) return 0;
3771 GAsetunits(xpinch, ypinch);
3772 if (!GADeviceDriver(dev, display, width, height, ps,
3773 (Rboolean)recording, resize, bg, canvas, gamma,
3774 xpos, ypos, (Rboolean)buffered, psenv,
3775 restoreConsole, title, clickToConfirm,
3776 fillOddEven, family, quality)) {
3777 free(dev);
3778 error(_("unable to start %s() device"), type);
3779 }
3780 gdd = GEcreateDevDesc(dev);
3781 GEaddDevice2f(gdd, type, file);
3782 } END_SUSPEND_INTERRUPTS;
3783 vmaxset(vmax);
3784 return R_NilValue;
3785 }
3786
GA_onExit(pDevDesc dd)3787 static void GA_onExit(pDevDesc dd)
3788 {
3789 gadesc *xd = (gadesc *) dd->deviceSpecific;
3790
3791 dd->onExit = NULL;
3792 xd->confirmation = FALSE;
3793 dd->gettingEvent = FALSE;
3794
3795 if (xd->cntxt) endcontext((RCNTXT *)xd->cntxt);
3796 if (xd->locator) donelocator((void *)xd);
3797
3798 addto(xd->gawin);
3799 gchangemenubar(xd->mbar);
3800 gchangepopup(xd->gawin, xd->grpopup);
3801 addto(xd->gawin);
3802 setstatus(_("R Graphics"));
3803 GA_Activate(dd);
3804 }
3805
GA_NewFrameConfirm(pDevDesc dev)3806 static Rboolean GA_NewFrameConfirm(pDevDesc dev)
3807 {
3808 char *msg;
3809 gadesc *xd = dev->deviceSpecific;
3810
3811 if (!xd || xd->kind != SCREEN) return FALSE;
3812
3813 msg = G_("Waiting to confirm page change...");
3814 xd->confirmation = TRUE;
3815 xd->clicked = 0;
3816 xd->enterkey = 0;
3817 show(xd->gawin);
3818 addto(xd->gawin);
3819 gchangemenubar(xd->mbarconfirm);
3820 gchangepopup(xd->gawin, NULL);
3821 setstatus(msg);
3822 R_WriteConsole(msg, strlen(msg));
3823 R_WriteConsole("\n", 1);
3824 R_FlushConsole();
3825 settext(xd->gawin, G_("Click or hit ENTER for next page"));
3826 BringToTop(xd->gawin, 0);
3827 dev->onExit = GA_onExit; /* install callback for cleanup */
3828 while (!xd->clicked && !xd->enterkey) {
3829 SH;
3830 R_WaitEvent();
3831 R_ProcessEvents(); /* May not return if user interrupts */
3832 }
3833 dev->onExit(dev);
3834
3835 return TRUE;
3836 }
3837
GA_eventHelper(pDevDesc dd,int code)3838 static void GA_eventHelper(pDevDesc dd, int code)
3839 {
3840 gadesc *xd = dd->deviceSpecific;
3841
3842 if (code == 1) {
3843 show(xd->gawin);
3844 addto(xd->gawin);
3845 gchangemenubar(xd->mbar);
3846 gchangepopup(xd->gawin, NULL);
3847 if (isEnvironment(dd->eventEnv)) {
3848 SEXP prompt = findVar(install("prompt"), dd->eventEnv);
3849 if (isString(prompt) && length(prompt) == 1) {
3850 setstatus(CHAR(asChar(prompt)));
3851 settext(xd->gawin, CHAR(asChar(prompt)));
3852 } else {
3853 setstatus("");
3854 settext(xd->gawin, "");
3855 }
3856 }
3857 dd->onExit = GA_onExit; /* install callback for cleanup */
3858 } else if (code == 0)
3859 dd->onExit(dd);
3860
3861 return;
3862 }
3863
3864
3865 #define WIN32_LEAN_AND_MEAN 1
3866 #include <windows.h>
3867 typedef int (*R_SaveAsBitmap)(/* variable set of args */);
3868 static R_SaveAsBitmap R_devCairo;
3869 static int RcairoAlreadyLoaded = 0;
3870 static HINSTANCE hRcairoDll;
3871
3872 typedef SEXP (*R_cairoVersion_t)(void);
3873 static R_cairoVersion_t R_cairoVersion = NULL;
3874 typedef SEXP (*R_pangoVersion_t)(void);
3875 static R_pangoVersion_t R_pangoVersion = NULL;
3876 typedef SEXP (*R_cairoFT_t)(void);
3877 static R_cairoFT_t R_cairoFT = NULL;
3878
Load_Rcairo_Dll()3879 static int Load_Rcairo_Dll()
3880 {
3881 if (!RcairoAlreadyLoaded) {
3882 char szFullPath[PATH_MAX];
3883 strcpy(szFullPath, R_HomeDir());
3884 strcat(szFullPath, "/library/grDevices/libs/");
3885 strcat(szFullPath, R_ARCH);
3886 strcat(szFullPath, "/winCairo.dll");
3887 if (((hRcairoDll = LoadLibrary(szFullPath)) != NULL) &&
3888 ((R_devCairo =
3889 (R_SaveAsBitmap)GetProcAddress(hRcairoDll, "in_Cairo"))
3890 != NULL)) {
3891 R_cairoVersion = (R_cairoVersion_t)
3892 GetProcAddress(hRcairoDll, "in_CairoVersion");
3893 R_pangoVersion = (R_pangoVersion_t)
3894 GetProcAddress(hRcairoDll, "in_PangoVersion");
3895 R_cairoFT = (R_cairoFT_t)
3896 GetProcAddress(hRcairoDll, "in_CairoFT");
3897 RcairoAlreadyLoaded = 1;
3898 } else {
3899 if (hRcairoDll != NULL) FreeLibrary(hRcairoDll);
3900 RcairoAlreadyLoaded = -1;
3901 char buf[1000];
3902 snprintf(buf, 1000, "Unable to load '%s'", szFullPath);
3903 R_ShowMessage(buf);
3904 }
3905 }
3906 return (RcairoAlreadyLoaded > 0);
3907 }
3908
3909 /*
3910 cairo(filename, type, width, height, pointsize, bg, res, antialias, quality)
3911 */
devCairo(SEXP args)3912 SEXP devCairo(SEXP args)
3913 {
3914 if (!Load_Rcairo_Dll())
3915 error("unable to load winCairo.dll: was it built?");
3916 else (R_devCairo)(args);
3917 return R_NilValue;
3918 }
3919
cairoVersion(void)3920 SEXP cairoVersion(void)
3921 {
3922 if (!Load_Rcairo_Dll() || R_cairoVersion == NULL) return mkString("");
3923 else return (R_cairoVersion)();
3924 }
3925
pangoVersion(void)3926 SEXP pangoVersion(void)
3927 {
3928 if (!Load_Rcairo_Dll() || R_cairoVersion == NULL) return mkString("");
3929 else return (R_pangoVersion)();
3930 }
3931
cairoFT(void)3932 SEXP cairoFT(void)
3933 {
3934 if (!Load_Rcairo_Dll() || R_cairoVersion == NULL) return mkString("");
3935 else return (R_cairoFT)();
3936 }
3937
bmVersion(void)3938 SEXP bmVersion(void)
3939 {
3940 SEXP ans = PROTECT(allocVector(STRSXP, 3)),
3941 nms = PROTECT(allocVector(STRSXP, 3));
3942 setAttrib(ans, R_NamesSymbol, nms);
3943 SET_STRING_ELT(nms, 0, mkChar("libpng"));
3944 SET_STRING_ELT(nms, 1, mkChar("jpeg"));
3945 SET_STRING_ELT(nms, 2, mkChar("libtiff"));
3946 SET_STRING_ELT(ans, 0, mkChar((R_pngVersion)()));
3947 SET_STRING_ELT(ans, 1, mkChar((R_jpegVersion)()));
3948 SET_STRING_ELT(ans, 2, mkChar((R_tiffVersion)()));
3949 UNPROTECT(2);
3950 return ans;
3951 }
3952