Many changes! Snapshots now working properly. Programmatic environment interface...
[RExecServer.git] / RDeviceImpl.c
blob4c27a48bc230d76293483c1e0c9cab32933667a3
1 #include "RDeviceImpl.h"
2 #include <Rinternals.h>
3 #include <Rgraphics.h>
4 #include <Rdevices.h>
6 #include <R_ext/GraphicsDevice.h>
7 #include <R_ext/GraphicsEngine.h>
9 typedef struct {
10 double ps,scale,width,height,tscale;
11 int dirty,bg,gstate,antialias,redraw;
12 CGRect clipRect;
13 NewDevDesc *dev; //Circular reference.
14 void* userInfo;
15 CGContextRef (*getCGContext)(QuartzDesc_t dev,void*userInfo);
16 int (*locatePoint)(QuartzDesc_t dev,void*userInfo,double*x,double*y);
17 void (*close)(QuartzDesc_t dev,void*userInfo);
18 void (*newPage)(QuartzDesc_t dev,void*userInfo);
19 void (*activate)(QuartzDesc_t dev,void*userInfo,int devNum);
20 void (*deactivate)(QuartzDesc_t dev,void*userInfo,int devNum);
21 } QuartzDesc;
24 #pragma mark QuartzDesc manipulation
25 #pragma mark -
27 void RDevice_Update(QuartzDesc_t desc);
29 double RDevice_ScaledWidth(QuartzDesc *qd) { return qd->scale*qd->width*72.0; }
30 double RDevice_ScaledHeight(QuartzDesc *qd) { return qd->scale*qd->height*72.0; }
33 double RDevice_GetWidth(QuartzDesc_t desc) { return ((QuartzDesc*)desc)->width; }
34 void RDevice_SetWidth(QuartzDesc_t desc,double width) { ((QuartzDesc*)desc)->width = width;((QuartzDesc*)desc)->dev->right = RDevice_ScaledWidth(desc); }
36 double RDevice_GetHeight(QuartzDesc_t desc) { return ((QuartzDesc*)desc)->height; }
37 void RDevice_SetHeight(QuartzDesc_t desc,double height) { ((QuartzDesc*)desc)->height = height;((QuartzDesc*)desc)->dev->bottom = RDevice_ScaledHeight(desc); }
39 double RDevice_GetScale(QuartzDesc_t desc) { return ((QuartzDesc*)desc)->scale; }
40 void RDevice_SetScale(QuartzDesc_t desc,double scale) { ((QuartzDesc*)desc)->scale = scale;RDevice_Update(desc); }
42 double RDevice_GetTextScale(QuartzDesc_t desc) { return ((QuartzDesc*)desc)->tscale; }
43 void RDevice_SetTextScale(QuartzDesc_t desc,double scale) { ((QuartzDesc*)desc)->tscale = scale;RDevice_Update(desc); }
46 int RDevice_GetDirty(QuartzDesc_t desc) { return ((QuartzDesc*)desc)->dirty; }
47 void RDevice_SetDirty(QuartzDesc_t desc,int dirty) { ((QuartzDesc*)desc)->dirty; }
49 int RDevice_GetGDepth(QuartzDesc_t desc) { return ((QuartzDesc*)desc)->gstate; }
50 void RDevice_ResetGDepth(QuartzDesc_t desc) { ((QuartzDesc*)desc)->gstate = 0; }
52 int RDevice_GetAntialias(QuartzDesc_t desc) { return ((QuartzDesc*)desc)->antialias; }
55 void RDevice_Update(QuartzDesc_t desc) {
56 QuartzDesc *qd = (QuartzDesc*)desc;
57 NewDevDesc *dev= qd->dev;
59 dev->cra[0] = 0.9*qd->scale*qd->ps*qd->tscale;
60 dev->cra[1] = 1.2*qd->scale*qd->ps*qd->tscale;
61 dev->ipr[0] = dev->ipr[1] = 1.0/(72.0*qd->scale);
64 void RDevice_ReplayDisplayList(QuartzDesc_t desc) {
65 QuartzDesc *qd = (QuartzDesc*)desc;
66 qd->redraw = 1;
67 if(qd->dev->displayList != R_NilValue)
68 GEplayDisplayList((GEDevDesc*)GetDevice(devNumber((DevDesc*)qd->dev)));
69 qd->redraw = 0;
72 void* RDevice_GetSnapshot(QuartzDesc_t desc) {
73 QuartzDesc *qd = (QuartzDesc*)desc;
74 return qd->dev->savedSnapshot;
77 void RDevice_RestoreSnapshot(QuartzDesc_t desc,void* snap) {
78 QuartzDesc *qd = (QuartzDesc*)desc;
79 PROTECT((SEXP)snap);
80 qd->redraw = 1;
81 GEplaySnapshot((SEXP)snap,(GEDevDesc*)GetDevice(devNumber((DevDesc*)qd->dev)));
82 qd->redraw = 0;
83 UNPROTECT(1);
86 #pragma mark Function Prototypes
88 static Rboolean RQuartz_Open(NewDevDesc*,QuartzDesc*,char*,double,double,int);
89 static void RQuartz_Close(NewDevDesc*);
90 static void RQuartz_Activate(NewDevDesc*);
91 static void RQuartz_Deactivate(NewDevDesc*);
92 static void RQuartz_Size(double*,double*,double*,double*,NewDevDesc*);
93 static void RQuartz_NewPage(R_GE_gcontext*,NewDevDesc*);
94 static void RQuartz_Clip(double,double,double,double,NewDevDesc*);
95 static double RQuartz_StrWidth(char*,R_GE_gcontext*,NewDevDesc*);
96 static void RQuartz_Text(double,double,char*,double,double,R_GE_gcontext*,NewDevDesc*);
97 static void RQuartz_Rect(double,double,double,double,R_GE_gcontext*,NewDevDesc*);
98 static void RQuartz_Circle(double,double,double,R_GE_gcontext*,NewDevDesc*);
99 static void RQuartz_Line(double,double,double,double,R_GE_gcontext*,NewDevDesc*);
100 static void RQuartz_Polyline(int,double*,double*,R_GE_gcontext*,NewDevDesc*);
101 static void RQuartz_Polygon(int,double*,double*,R_GE_gcontext*,NewDevDesc*);
102 static Rboolean RQuartz_Locator(double*,double*,NewDevDesc*);
103 static void RQuartz_Mode(int mode,NewDevDesc*);
104 static void RQuartz_Hold(NewDevDesc*);
105 static void RQuartz_MetricInfo(int,R_GE_gcontext *,double*,double*,double*,NewDevDesc*);
108 void* RDevice_Create(
109 void *_dev,double scale,double ps,double width,double height,int bg,int aa,
110 CGContextRef (*getCGContext)(QuartzDesc_t dev,void*userInfo), //Get the context for this device
111 int (*locatePoint)(QuartzDesc_t dev,void*userInfo,double*x,double*y),
112 void (*close)(QuartzDesc_t dev,void*userInfo),
113 void (*newPage)(QuartzDesc_t dev,void*userInfo),
114 void (*activate)(QuartzDesc_t dev,void*userInfo,int devNum),
115 void (*deactivate)(QuartzDesc_t dev,void*userInfo,int devNum),
116 void*userInfo) {
117 NewDevDesc *dev = (NewDevDesc*)_dev;
118 dev->displayList = R_NilValue;
120 dev->startfill = R_RGB(255,255,255);
121 dev->startcol = R_RGB(0,0,0);
122 dev->startps = ps;
123 dev->startfont = 1;
124 dev->startlty = LTY_SOLID;
125 dev->startgamma= 1;
127 //Set up some happy pointers
128 dev->newDevStruct = 1;
129 dev->open = RQuartz_Open;
130 dev->close = RQuartz_Close;
131 dev->activate = RQuartz_Activate;
132 dev->deactivate = RQuartz_Deactivate;
133 dev->size = RQuartz_Size;
134 dev->newPage = RQuartz_NewPage;
135 dev->clip = RQuartz_Clip;
136 dev->strWidth = RQuartz_StrWidth;
137 dev->text = RQuartz_Text;
138 dev->rect = RQuartz_Rect;
139 dev->circle = RQuartz_Circle;
140 dev->line = RQuartz_Line;
141 dev->polyline = RQuartz_Polyline;
142 dev->polygon = RQuartz_Polygon;
143 dev->locator = RQuartz_Locator;
144 dev->mode = RQuartz_Mode;
145 dev->hold = RQuartz_Hold;
146 dev->metricInfo = RQuartz_MetricInfo;
148 dev->left = 0;
149 dev->top = 0;
152 //Magic numbers from on high.
153 dev->xCharOffset = 0.4900;
154 dev->yCharOffset = 0.3333;
155 dev->yLineBias = 0.20; //This is .2 for PS/PDF devices...
157 dev->canResizePlot = TRUE;
158 dev->canChangeFont = TRUE;
159 dev->canRotateText = TRUE;
160 dev->canResizeText = TRUE;
161 dev->canClip = TRUE;
162 dev->canHAdj = 2;
163 dev->canChangeGamma= TRUE;
164 dev->displayListOn = TRUE;
166 QuartzDesc *qd = calloc(1,sizeof(QuartzDesc));
167 qd->width = width;
168 qd->height = height;
169 qd->userInfo = userInfo;
170 qd->getCGContext = getCGContext;
171 qd->locatePoint= locatePoint;
172 qd->close = close;
173 qd->newPage = newPage;
174 qd->activate = activate;
175 qd->deactivate = deactivate;
176 qd->scale = scale;
177 qd->tscale= 1.0;
178 qd->ps = ps;
179 qd->bg = bg;
180 qd->antialias = aa;
181 qd->gstate= 0;
183 dev->deviceSpecific = qd;
184 qd->dev = dev;
187 RDevice_Update(qd);
189 dev->right = RDevice_ScaledWidth(qd)-0.0001;
190 dev->bottom= RDevice_ScaledHeight(qd)-0.0001;
191 qd->clipRect = CGRectMake(0,0,dev->right,dev->bottom);
193 qd->dirty = 0;
194 qd->redraw= 0;
195 return (QuartzDesc_t)qd;
198 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
199 extern CGFontRef CGFontCreateWithName(CFStringRef);
200 #define CGFontCreateWithFontName CGFontCreateWithName
201 #define CGFontGetGlyphBBoxes CGFontGetGlyphBoundingBoxes
202 #define CGFontGetGlyphsForUnichars CGFontGetGlyphsForUnicodes
203 #endif
204 //Always needs this one...
205 extern CGFontRef CGContextGetFont(CGContextRef);
208 #define DEVDESC NewDevDesc *dd
209 #define CTXDESC R_GE_gcontext*gc,NewDevDesc*dd
211 #define DEVSPEC QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;CGContextRef ctx = xd->getCGContext(xd,xd->userInfo)
212 #define DRAWSPEC QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;CGContextRef ctx = xd->getCGContext(xd,xd->userInfo);xd->dirty = 1;if(NULL==ctx) { printf("Invalid Context\n");return; }
213 #define XD QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific
214 #pragma mark Device Implementation
216 CFStringRef RQuartz_FindFont(int fontface,char *fontfamily) {
217 SEXP ns,env,db,names;
218 PROTECT_INDEX index;
219 CFStringRef fontName = CFSTR("");
220 PROTECT(ns = R_FindNamespace(ScalarString(mkChar("grDevices"))));
221 PROTECT_WITH_INDEX(env = findVar(install(".Quartzenv"),ns),&index);
222 if(TYPEOF(env) == PROMSXP)
223 REPROTECT(env = eval(env,ns),index);
224 PROTECT(db = findVar(install(".Quartz.Fonts"),env));
225 PROTECT(names = getAttrib(db,R_NamesSymbol));
226 if(strlen(fontfamily)>0) {
227 int i;
228 for(i=0;i<length(names);i++)
229 if(0 == strcmp(fontfamily,CHAR(STRING_ELT(names,i)))) break;
230 if(i<length(names))
231 fontName = CFStringCreateWithCString(kCFAllocatorDefault,CHAR(STRING_ELT(VECTOR_ELT(db,i),fontface)), kCFStringEncodingUTF8);
233 UNPROTECT(4);
234 return fontName;
237 CGFontRef RQuartz_Font(CTXDESC) {
238 int fontface = gc->fontface;
239 CFMutableStringRef fontName = CFStringCreateMutable(kCFAllocatorDefault,0);
240 if((gc->fontface == 5) || (strcmp(gc->fontfamily,"symbol") == 0))
241 CFStringAppend(fontName,CFSTR("Symbol"));
242 else {
243 CFStringRef font = RQuartz_FindFont(gc->fontface,gc->fontfamily);
244 if(CFStringGetLength(font)>0) {
245 fontface = 1; //This is handled by the lookup process
246 CFStringAppend(fontName,font);
248 CFRelease(font);
250 if(CFStringGetLength(fontName) == 0)
251 CFStringAppend(fontName,CFSTR("Arial"));
252 if(fontface==2 || fontface == 4) {
253 CFStringAppend(fontName,CFSTR(" Bold"));
255 if(fontface==3) {
256 CFStringAppend(fontName,CFSTR(" Italic"));
258 CGFontRef font = CGFontCreateWithFontName(fontName);
259 if(font == 0) {
260 //Fall back on ATS
261 ATSFontRef tmp = ATSFontFindFromName(fontName,kATSOptionFlagsDefault);
262 font = CGFontCreateWithPlatformFont(&tmp);
264 if(NULL == font) {
265 CFShow(fontName);
267 CFRelease(fontName);
268 return font;
271 #define RQUARTZ_FILL (1)
272 #define RQUARTZ_STROKE (1<<1)
273 #define RQUARTZ_LINE (1<<2)
274 #define RQUARTZ_FONT (1<<3)
276 void RQuartz_Set(CGContextRef ctx,R_GE_gcontext*gc,int flags) {
277 if(flags & RQUARTZ_FILL) {
278 int fill = gc->fill;
279 CGContextSetRGBFillColor(ctx,R_RED(fill)/256.0,R_GREEN(fill)/256.0,R_BLUE(fill)/256.0,R_ALPHA(fill)/256.0);
281 if(flags & RQUARTZ_STROKE) {
282 int stroke = gc->col;
283 CGContextSetRGBStrokeColor(ctx,R_RED(stroke)/256.0,R_GREEN(stroke)/256.0,R_BLUE(stroke)/256.0,R_ALPHA(stroke)/256.0);
285 if(flags & RQUARTZ_LINE) {
286 float dashlist[8];
287 int i,ndash = 0;
288 int lty = gc->lty;
289 CGContextSetLineWidth(ctx,gc->lwd);
291 float lwd = gc->lwd*0.75;
292 for(i=0;i<8 && lty;i++) {
293 dashlist[ndash++] = (lwd >= 1 ? lwd : 1)*(lty&15);
294 lty >>= 4;
296 CGContextSetLineDash(ctx,0,dashlist,ndash);
297 CGLineCap cap;
298 switch(gc->lend) {
299 case GE_ROUND_CAP:cap = kCGLineCapRound;break;
300 case GE_BUTT_CAP:cap = kCGLineCapButt;break;
301 case GE_SQUARE_CAP:cap = kCGLineCapSquare;break;
303 CGContextSetLineCap(ctx,cap);
304 CGLineJoin join;
305 switch(gc->ljoin) {
306 case GE_ROUND_JOIN:join = kCGLineJoinRound;break;
307 case GE_MITRE_JOIN:join = kCGLineJoinMiter;break;
308 case GE_BEVEL_JOIN:join = kCGLineJoinBevel;break;
310 CGContextSetLineJoin(ctx,join);
311 CGContextSetMiterLimit(ctx,gc->lmitre);
313 if(flags & RQUARTZ_FONT) {
314 CGFontRef font = RQuartz_Font(gc,NULL);
315 CGContextSetFont(ctx,font);
316 CGContextSetFontSize(ctx,gc->cex*gc->ps);
320 #define SET(X) RQuartz_Set(ctx,gc,(X))
322 static Rboolean RQuartz_Open(DEVDESC,QuartzDesc *xd,char *display,double width,double height,int bg) {
323 //We don't do anything here.
324 return TRUE;
327 static void RQuartz_Close(DEVDESC) {
329 if(NULL != xd->close) xd->close(xd,xd->userInfo);
332 static void RQuartz_Activate(DEVDESC) {
334 if(NULL != xd->activate) xd->activate(xd,xd->userInfo,devNumber((DevDesc*)xd->dev));
337 static void RQuartz_Deactivate(DEVDESC) {
339 if(NULL != xd->deactivate) xd->deactivate(xd,xd->userInfo,devNumber((DevDesc*)xd->dev));
342 static void RQuartz_Size(double*left,double*right,double*bottom,double*top,DEVDESC) {
344 *left = *top = 0;
345 *right = RDevice_ScaledWidth(xd);
346 *bottom = RDevice_ScaledHeight(xd);
349 static void RQuartz_NewPage(CTXDESC) {
350 DRAWSPEC;
351 //Should have new page event
352 if(0 == xd->redraw && NULL != xd->newPage) xd->newPage(xd,xd->userInfo);
353 SET(RQUARTZ_FILL);
354 CGRect bounds = CGRectMake(0,0,RDevice_ScaledWidth(xd),RDevice_ScaledHeight(xd));
355 if(R_ALPHA(xd->bg) == 255 && R_ALPHA(gc->fill) == 255)
356 CGContextClearRect(ctx,bounds);
357 CGContextFillRect(ctx,bounds);
360 static void RQuartz_Clip(double x0,double x1,double y0,double y1,DEVDESC) {
361 DRAWSPEC;
362 if(xd->gstate > 0) {
363 --xd->gstate;
364 CGContextRestoreGState(ctx);
366 CGContextSaveGState(ctx);
367 xd->gstate++;
368 if(x1 > x0) { double t = x1;x1 = x0;x0 = t; }
369 if(y1 > y0) { double t = y1;y1 = y0;y0 = t; }
370 xd->clipRect = CGRectMake(x0,y0,x1-x0,y1-y0);
371 CGContextClipToRect(ctx,xd->clipRect);
374 CFStringRef prepareText(CTXDESC,char *text,UniChar **buffer,int *free) {
375 CFStringRef str;
376 if(gc->fontface == 5 || strcmp(gc->fontfamily,"symbol") == 0)
377 str = CFStringCreateWithCString(NULL,text,kCFStringEncodingMacSymbol);
378 else {
379 str = CFStringCreateWithCString(NULL,text,kCFStringEncodingUTF8);
380 //Try fallback string encodings if UTF8 doesn't work.
381 if(NULL == str)
382 CFStringCreateWithCString(NULL,text,kCFStringEncodingISOLatin1);
384 *buffer = (UniChar*)CFStringGetCharactersPtr(str);
385 if (*buffer == NULL) {
386 CFIndex length = CFStringGetLength(str);
387 *buffer = malloc(length * sizeof(UniChar));
388 CFStringGetCharacters(str, CFRangeMake(0, length), *buffer);
389 *free = 1;
391 return str;
394 static double RQuartz_StrWidth(char *text,CTXDESC) {
395 DEVSPEC;
396 if(ctx==NULL) return 0.0;
397 SET(RQUARTZ_FONT);
398 CGFontRef font = CGContextGetFont(ctx);
399 float aScale = (gc->cex*gc->ps*xd->tscale)/CGFontGetUnitsPerEm(font);
400 UniChar *buffer;
401 CGGlyph *glyphs;
402 int *advances;
403 int Free = 0,len,i;
404 CFStringRef str = prepareText(gc,dd,text,&buffer,&Free);
405 len = CFStringGetLength(str);
406 glyphs = malloc(sizeof(CGGlyph)*len);
407 advances = malloc(sizeof(int)*len);
408 CGFontGetGlyphsForUnichars(font,buffer,glyphs,len);
409 CGFontGetGlyphAdvances(font,glyphs,len,advances);
410 float width = 0.0;// aScale*CGFontGetLeading(CGContextGetFont(ctx));
411 for(i=0;i<len;i++) width += aScale*advances[i];
412 free(advances);
413 free(glyphs);
414 if(Free) free(buffer);
415 CFRelease(str);
416 return width;
419 static void RQuartz_Text(double x,double y,char *text,double rot,double hadj,CTXDESC) {
420 DRAWSPEC;
421 //A stupid hack because R isn't consistent.
422 int fill = gc->fill;
423 gc->fill = gc->col;
424 SET(RQUARTZ_FILL|RQUARTZ_STROKE|RQUARTZ_FONT);
425 gc->fill = fill;
426 CGFontRef font = CGContextGetFont(ctx);
427 float aScale = (gc->cex*gc->ps*xd->tscale)/(CGFontGetUnitsPerEm(font));
428 UniChar *buffer;
429 CGGlyph *glyphs;
431 int Free = 0,len,i;
432 float width = 0.0;
433 CFStringRef str = prepareText(gc,dd,text,&buffer,&Free);
434 len = CFStringGetLength(str);
435 glyphs = malloc(sizeof(CGGlyph)*len);
436 CGFontGetGlyphsForUnichars(font,buffer,glyphs,len);
437 int *advances = malloc(sizeof(int)*len);
438 CGSize *g_adv = malloc(sizeof(CGSize)*len);
440 CGFontGetGlyphAdvances(font,glyphs,len,advances);
441 for(i=0;i<len;i++) { width += advances[i]*aScale;g_adv[i] = CGSizeMake(aScale*advances[i]*cos(-0.0174532925*rot),aScale*advances[i]*sin(-0.0174532925*rot)); }
442 free(advances);
443 CGContextSetTextMatrix(ctx,CGAffineTransformConcat(CGAffineTransformMakeScale(1.0,-1.0),CGAffineTransformMakeRotation(-0.0174532925*rot)));
444 double ax = (width*hadj)*cos(-0.0174532925*rot);
445 double ay = (width*hadj)*sin(-0.0174532925*rot);
446 // double h = CGFontGetXHeight(CGContextGetFont(ctx))*aScale;
447 CGContextSetTextPosition(ctx,x-ax,y-ay);
448 // Rprintf("%s,%.2f %.2f (%.2f,%.2f) (%d,%f)\n",text,hadj,width,ax,ay,CGFontGetUnitsPerEm(CGContextGetFont(ctx)),CGContextGetFontSize(ctx));
449 CGContextShowGlyphsWithAdvances(ctx,glyphs,g_adv,len);
450 free(glyphs);
451 free(g_adv);
452 if(Free) free(buffer);
453 CFRelease(str);
456 static void RQuartz_Rect(double x0,double y0,double x1,double y1,CTXDESC) {
457 DRAWSPEC;
458 SET(RQUARTZ_FILL|RQUARTZ_STROKE|RQUARTZ_LINE);
459 CGContextBeginPath(ctx);
460 CGContextAddRect(ctx,CGRectMake(x0,y0,x1-x0,y1-y0));
461 CGContextDrawPath(ctx,kCGPathFillStroke);
464 static void RQuartz_Circle(double x,double y,double r,CTXDESC) {
465 DRAWSPEC;
466 SET(RQUARTZ_FILL|RQUARTZ_STROKE|RQUARTZ_LINE);
467 double r2 = 2.0*r;
468 CGContextBeginPath(ctx);
469 CGContextAddEllipseInRect(ctx,CGRectMake(x-r,y-r,r2,r2));
470 CGContextDrawPath(ctx,kCGPathFillStroke);
473 static void RQuartz_Line(double x1,double y1,double x2,double y2,CTXDESC) {
474 DRAWSPEC;
475 SET(RQUARTZ_STROKE|RQUARTZ_LINE);
476 CGContextBeginPath(ctx);
477 CGContextMoveToPoint(ctx,x1,y1);
478 CGContextAddLineToPoint(ctx,x2,y2);
479 CGContextStrokePath(ctx);
482 static void RQuartz_Polyline(int n,double *x,double *y,CTXDESC) {
483 if(n<2) return;
484 int i;
485 DRAWSPEC;
486 SET(RQUARTZ_STROKE|RQUARTZ_LINE);
487 CGContextBeginPath(ctx);
488 CGContextMoveToPoint(ctx,x[0],y[0]);
489 for(i=1;i<n;i++) CGContextAddLineToPoint(ctx,x[i],y[i]);
490 CGContextStrokePath(ctx);
493 static void RQuartz_Polygon(int n,double *x,double *y,CTXDESC) {
494 if(n<2) return;
495 int i;
496 DRAWSPEC;
497 SET(RQUARTZ_FILL|RQUARTZ_STROKE|RQUARTZ_LINE);
498 CGContextBeginPath(ctx);
499 CGContextMoveToPoint(ctx,x[0],y[0]);
500 for(i=1;i<n;i++) CGContextAddLineToPoint(ctx,x[i],y[i]);
501 CGContextClosePath(ctx);
502 CGContextDrawPath(ctx,kCGPathFillStroke);
505 static void RQuartz_Mode(int mode,DEVDESC) {
506 DEVSPEC;
507 if(0 == mode) {
508 if(xd->gstate > 0) {
509 --xd->gstate;
510 CGContextRestoreGState(ctx);
512 CGContextSynchronize(ctx);
513 } else {
514 //Set the clipping rect for this operation.
515 if(xd->gstate > 0) {
516 --xd->gstate;
517 CGContextRestoreGState(ctx);
519 ++xd->gstate;
520 CGContextSaveGState(ctx);
521 CGContextClipToRect(ctx,xd->clipRect);
525 static void RQuartz_Hold(DEVDESC) {
528 static void RQuartz_MetricInfo(int c,R_GE_gcontext *gc,double *ascent,double *descent,double *width,NewDevDesc *dd) {
529 DRAWSPEC;
530 SET(RQUARTZ_FONT);
531 char text[2];
532 text[0] = c;
533 text[1] = '\0';
534 CGFontRef font = CGContextGetFont(ctx);
535 float aScale = (gc->cex*gc->ps*xd->tscale)/CGFontGetUnitsPerEm(font);
536 UniChar *buffer;
537 CGGlyph *glyphs;
538 int Free = 0,len,i;
539 *width = 0.0;
540 CFStringRef str = prepareText(gc,dd,text,&buffer,&Free);
541 len = CFStringGetLength(str);
542 glyphs = malloc(sizeof(CGGlyph)*len);
543 CGFontGetGlyphsForUnichars(font,buffer,glyphs,len);
544 int *advances = malloc(sizeof(int)*len);
545 CGRect *bboxes = malloc(sizeof(CGRect)*len);
546 CGFontGetGlyphAdvances(font,glyphs,len,advances);
547 CGFontGetGlyphBBoxes(font,glyphs,len,bboxes);
548 for(i=0;i<len;i++) *width += advances[i]*aScale;
549 *ascent = aScale*(bboxes[0].size.height + bboxes[0].origin.y);
550 *descent = -aScale*bboxes[0].origin.y;
551 free(bboxes);
552 free(advances);
553 free(glyphs);
554 if(Free) free(buffer);
555 CFRelease(str);
558 static Rboolean RQuartz_Locator(double *x,double *y,DEVDESC) {
559 DEVSPEC;
560 ctx = NULL;
561 if(NULL == xd->locatePoint)
562 return FALSE;
563 return xd->locatePoint(xd,xd->userInfo,x,y);