Rev 55703 | Rev 82658 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** R : A Computer Language for Statistical Data Analysis* Copyright (C) 1998--2004 Guido Masarotto and Brian Ripley* Copyright (C) 2005-8 The R Core Team** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, a copy is available at* http://www.r-project.org/Licenses/*//*A version of drawing.c without current/state.More safe in case of multiple redrawing*/#include "internal.h"extern unsigned int TopmostDialogs; /* from dialogs.c */#include <winbase.h>#ifndef W64WINGDIAPI BOOL WINAPI AlphaBlend(HDC,int,int,int,int,HDC,int,int,int,int,BLENDFUNCTION);#endif#include <wchar.h>#ifdef __GNUC__# define alloca(x) __builtin_alloca((x))#else# error need appropriate declaration for alloca#endif/* from extra.c */extern size_t Rf_utf8towcs(wchar_t *wc, const char *s, size_t n);static HDC GETHDC(drawing d){if (!d) {DebugBreak();return (HDC) 0; /* We should never get here, but we do? */}if ( (d->kind == PrinterObject) || (d->kind == MetafileObject)) {HDC dc = (HDC) d->handle ;SelectObject(dc, GetStockObject(NULL_PEN));SelectObject(dc, GetStockObject(NULL_BRUSH));return dc ;}elsereturn get_context(d);}/** Some clipping functions.*/rect ggetcliprect(drawing d){RECT R;rect r;HDC dc = GETHDC(d);GetClipBox(dc, &R);r.x = R.left;r.y = R.top;r.width = R.right - R.left;r.height = R.bottom - R.top;return r;}void gsetcliprect(drawing d, rect r){HRGN rgn;HDC dc = GETHDC(d);rgn = CreateRectRgn(r.x, r.y, r.x + r.width, r.y + r.height);SelectClipRgn(dc, rgn);DeleteObject(rgn);}void gbitblt(drawing db, drawing sb, point p, rect r){HDC src;HDC dst;dst = GETHDC(db);src = GETHDC(sb);BitBlt(dst, p.x, p.y, r.width, r.height, src, r.x, r.y, SRCCOPY);}/* dp gives the amount to scroll; r the full rectangle to scroll */void gscroll(drawing d, point dp, rect r){HDC dc = GETHDC(d);RECT rr ;rr.left = r.x;rr.top = r.y;rr.right = r.x + r.width;rr.bottom = r.y + r.height;ScrollDC(dc, dp.x , dp.y , &rr, &rr, 0, NULL);}void ginvert(drawing d, rect r){HDC dc = GETHDC(d);PatBlt(dc, r.x, r.y, r.width, r.height, DSTINVERT);}rgb ggetpixel(drawing d, point p){rgb c;HDC dc = GETHDC(d);c = GetPixel(dc, p.x, p.y);c = ((c&0x000000FFL)<<16) | (c&0x0000FF00L) |((c&0x00FF0000L)>>16);return c;}static COLORREF getwinrgb(drawing d, rgb c){int r, g, b;#ifdef UNUSEDlong luminance;int depth;#endifr = (int) ((c >> 16) & 0x000000FF);g = (int) ((c >> 8) & 0x000000FF);b = (int) ((c >> 0) & 0x000000FF);#ifdef UNUSEDdepth = getdepth(d);/* note: next is unused! */if (depth <= 2) /* map to black or white, or grey if c == Grey */{luminance = (r*3 + g*5 + b) / 9;if (luminance > 0x0087) r = g = b = 0x00FF;else if (luminance <= 0x0077) r = g = b = 0x0000;else r = g = b = 0x0080;c = rgb(r, g, b);}#endifreturn RGB(r, g, b);}void gsetpixel(drawing d, point p, rgb c){HDC dc = GETHDC(d);HBRUSH br = CreateSolidBrush(getwinrgb(d, c));fix_brush(dc, d, br);SelectObject(dc, br);PatBlt(dc, p.x, p.y, 1, 1, PATCOPY);SelectObject(dc, GetStockObject(NULL_BRUSH));DeleteObject(br);}typedef struct {HDC dc;int len2; /* squared length of current dash */int curseg, on; /* current dash (0-7), on/off flag */int style, width;int curx, cury; /* start of current dash */} DashStruct;static int npieces;static void CALLBACK gLineHelper(int x, int y, LPARAM aa){DashStruct *a = (DashStruct *) aa;int distx, disty;npieces++;distx = x - (a->curx);disty = y - (a->cury);if (distx*distx + disty*disty >= (a->len2)) {if (a->on)LineTo(a->dc, x, y);elseMoveToEx(a->dc, x, y, NULL);a->curx = x;a->cury = y;a->len2 = 0;while (!a->len2) {a->curseg = (a->curseg + 4) % 32;a->len2 = (((a->style) >> (a->curseg)) & 15) * (a->width);a->len2 = (a->len2) * (a->len2);a->on = (a->on) ? 0 : 1;}}}void gdrawline(drawing d, int width, int style, rgb c, point p1, point p2,int fast, int lend, int ljoin, float lmitre){point p[2];p[0] = p1;p[1] = p2;gdrawpolyline( d, width, style, c, p, 2, 0, fast, lend, ljoin, lmitre);}void gdrawpolyline(drawing d, int width, int style, rgb c,point p[], int n, int closepath, int fast,int lend, int ljoin, float lmitre){int tmpx, tmpy, tmp;HDC dc = GETHDC(d);COLORREF winrgb = getwinrgb(d, c);LOGBRUSH lb;HPEN gpen;int i;float oldmitre;if (n < 2) return;lb.lbStyle = BS_SOLID;lb.lbColor = winrgb;lb.lbHatch = 0;SetMiterLimit(dc, lmitre, &oldmitre);if (!style) {if (fast)gpen = CreatePen(PS_INSIDEFRAME, width, winrgb);elsegpen = ExtCreatePen(PS_GEOMETRIC|PS_SOLID|lend|ljoin,width, &lb, 0, NULL);SelectObject(dc, gpen);SetROP2(dc, R2_COPYPEN);npieces = 0;BeginPath(dc);MoveToEx(dc, p[0].x, p[0].y, NULL);for (i = 1; i < n ; i++) {LineTo(dc, p[i].x, p[i].y);npieces++;if (npieces > 1000) {EndPath(dc);StrokePath(dc);npieces = 0;BeginPath(dc);}}if (closepath) LineTo(dc, p[0].x, p[0].y);EndPath(dc);StrokePath(dc);SelectObject(dc, GetStockObject(NULL_PEN));DeleteObject(gpen);}else {DashStruct a;gpen = ExtCreatePen(PS_GEOMETRIC|PS_SOLID|lend|ljoin,width, &lb, 0, NULL);SelectObject(dc, gpen);SetROP2(dc, R2_COPYPEN);a.on = 1;a.dc = dc;a.len2 = (style & 15) * width;a.len2 = (a.len2) * (a.len2);a.curseg = 0;a.style = style;a.width = width;a.curx = p[0].x;a.cury = p[0].y;MoveToEx(dc, p[0].x, p[0].y, NULL);npieces = 0;BeginPath(dc);for (i = 1; i < n; i++) {LineDDA(p[i-1].x, p[i-1].y, p[i].x, p[i].y, gLineHelper,(LPARAM) &a);if ((p[i].x != a.curx) || (p[i].y != a.cury)) {if (a.on) LineTo(dc, p[i].x, p[i].y);else MoveToEx(dc, p[i].x, p[i].y, NULL);tmpx = (a.curx-p[i].x);tmpy = (a.cury-p[i].y);tmp = tmpx*tmpx + tmpy*tmpy;a.len2 = a.len2 + tmp - 2*sqrt((double)(tmp*a.len2));a.curx = p[i].x;a.cury = p[i].y;}npieces++;if (npieces > 1000) {EndPath(dc);StrokePath(dc);npieces = 0;BeginPath(dc);}}if (closepath) {LineDDA(p[n-1].x, p[n-1].y, p[0].x, p[0].y, gLineHelper,(LPARAM) &a);if (a.on) LineTo(dc,p[0].x,p[0].y);}EndPath(dc);StrokePath(dc);SelectObject(dc, GetStockObject(NULL_PEN));DeleteObject(gpen);}}void gdrawrect(drawing d, int width, int style, rgb c, rect r, int fast,int lend, int ljoin, float lmitre){int x0 = r.x;int y0 = r.y;int x1 = r.x + r.width;int y1 = r.y + r.height;point p[4];p[0] = pt(x0,y0);p[1] = pt(x1,y0);p[2] = pt(x1,y1);p[3] = pt(x0,y1);gdrawpolyline(d, width, style, c, p, 4, 1, fast, lend, ljoin, lmitre);}void gfillrect(drawing d, rgb fill, rect r){HDC dc = GETHDC(d);HBRUSH br = CreateSolidBrush(getwinrgb(d, fill));fix_brush(dc, d, br);SelectObject(dc, br);PatBlt(dc, r.x, r.y, r.width, r.height, PATCOPY);SelectObject(dc, GetStockObject(NULL_BRUSH));DeleteObject(br);}void gcopy(drawing d, drawing d2, rect r){HDC dc = GETHDC(d), sdc = GETHDC(d2);BitBlt(dc, r.x, r.y, r.width, r.height, sdc, r.x, r.y, SRCCOPY);}void gcopyalpha(drawing d, drawing d2, rect r, int alpha){if(alpha <= 0) return;{BLENDFUNCTION bl;bl.BlendOp = AC_SRC_OVER;bl.BlendFlags = 0;bl.SourceConstantAlpha = alpha;bl.AlphaFormat = 0;AlphaBlend(GETHDC(d), r.x, r.y, r.width, r.height,GETHDC(d2), r.x, r.y, r.width, r.height, bl);}}void gcopyalpha2(drawing d, image src, rect r){BLENDFUNCTION bl;bl.BlendOp = AC_SRC_OVER;bl.BlendFlags = 0;bl.SourceConstantAlpha = 255; /* per-pixel alpha only */bl.AlphaFormat = AC_SRC_ALPHA;bitmap bm = imagetobitmap(src);AlphaBlend(GETHDC(d), r.x, r.y, r.width, r.height,GETHDC(bm), 0, 0, r.width, r.height, bl);del(bm);}void gdrawellipse(drawing d, int width, rgb border, rect r, int fast,int lend, int ljoin, float lmitre){HDC dc = GETHDC(d);LOGBRUSH lb;HPEN gpen;float oldmitre;if (fast)gpen = CreatePen(PS_INSIDEFRAME, width, getwinrgb(d, border));else {SetMiterLimit(dc, lmitre, &oldmitre);lb.lbStyle = BS_SOLID;lb.lbColor = getwinrgb(d, border);lb.lbHatch = 0;gpen = ExtCreatePen(PS_GEOMETRIC|PS_SOLID|lend|ljoin,width, &lb, 0, NULL);}SelectObject(dc, gpen);SetROP2(dc, R2_COPYPEN);Ellipse(dc, r.x, r.y, r.x+r.width, r.y+r.height);SelectObject(dc, GetStockObject(NULL_PEN));DeleteObject(gpen);}void goldfillellipse(drawing d, rgb fill, rect r){HDC dc = GETHDC(d);HBRUSH br = CreateSolidBrush(getwinrgb(d, fill));fix_brush(dc, d, br);SelectObject(dc, br);Ellipse(dc, r.x, r.y, r.x+r.width, r.y+r.height);SelectObject(dc, GetStockObject(NULL_BRUSH));DeleteObject(br);}#ifndef fastfillrect#define fastfillrect(x, y, w, h) PatBlt(dc, (x), (y), (w), (h), mode)#endifvoid gfillellipse(drawing d, rgb fill, rect r){ /* e(x,y) = b*b*x*x + a*a*y*y - a*a*b*b */register long mode = PATCOPY;int w_odd = (r.width & 0x0001);int h_odd = (r.height & 0x0001);int a = r.width >> 1;int b = r.height >> 1;point c = pt(r.x+a,r.y+b);int x = 0;int y = b;long a2 = a*a;long b2 = b*b;long xcrit = ((a2+a2+a2) >> 2) + 1;long ycrit = ((b2+b2+b2) >> 2) + 1;long t = b2 + a2 - (a2+a2)*b; /* t = e(x+1,y-1) */long dxt = b2*(3+x+x);long dyt = a2*(3-y-y);int d2xt = b2+b2;int d2yt = a2+a2;int stored = 0;int sx = 0, sy = 0, sh = 0; /* stored values of x, y, height */HDC dc;HBRUSH br;if ((r.width > 31) && (r.height > 31)) {goldfillellipse(d, fill, r);return;}if ((r.width < 3) || (r.height < 3)) {gfillrect(d, fill, r);return;}dc = GETHDC(d);br = CreateSolidBrush(getwinrgb(d, fill));fix_brush(dc, d, br);SelectObject(dc, br);if (w_odd == 0) {fastfillrect(c.x-1,c.y-b,2,r.height);}while (y > 0) {if (stored) {if (sx != x) { /* output stored rect */fastfillrect(c.x-sx,c.y-sy, sx+sx+w_odd,sh);fastfillrect(c.x-sx,c.y+sy+h_odd-sh, sx+sx+w_odd,sh);stored = 0;}else /* increment height of stored rect */sh++;}if (t + a2*y < xcrit) { /* e(x+1,y-1/2) <= 0 *//* move left and right to encounter edge */x += 1;t += dxt;dxt += d2xt;} else if (t - b2*x >= ycrit) { /* e(x+1/2,y-1) > 0 *//* drop down one line */if (!stored) {sx = x;sy = y;sh = 1;stored = 1;}y -= 1;t += dyt;dyt += d2yt;} else {/* drop diagonally down and out */if (!stored) {sx = x;sy = y;sh = 1;stored = 1;}x += 1;y -= 1;t += dxt + dyt;dxt += d2xt;dyt += d2yt;}}if (stored) { /* output stored rectangle */fastfillrect(c.x-sx, c.y-sy, sx+sx+w_odd, sh);fastfillrect(c.x-sx, c.y+sy+h_odd-sh, sx+sx+w_odd, sh);stored = 0;}if (x <= a){fastfillrect(c.x-a, c.y-y, a+a+w_odd, 1);fastfillrect(c.x-a, c.y+y-1+h_odd, a+a+w_odd, 1);}SelectObject(dc, GetStockObject(NULL_BRUSH));DeleteObject(br);}void gsetpolyfillmode(drawing d, int oddeven){HDC dc = GETHDC(d);SetPolyFillMode(dc, oddeven ? ALTERNATE : WINDING);}void gfillpolygon(drawing d, rgb fill, point *p, int n){HDC dc = GETHDC(d);HBRUSH br = CreateSolidBrush(getwinrgb(d,fill));fix_brush(dc, d, br);SelectObject(dc, br);Polygon(dc, (POINT FAR *) p, n);SelectObject(dc, GetStockObject(NULL_BRUSH));DeleteObject(br);}void gfillpolypolygon(drawing d, rgb fill, point *p, int npoly, int *nper){HDC dc = GETHDC(d);HBRUSH br = CreateSolidBrush(getwinrgb(d,fill));fix_brush(dc, d, br);SelectObject(dc, br);PolyPolygon(dc, (POINT FAR *) p, nper, npoly);SelectObject(dc, GetStockObject(NULL_BRUSH));DeleteObject(br);}/* Assumes all pixels in image are opaque*/void gdrawimage(drawing d, image img, rect dr, rect sr){HDC dc = GETHDC(d);HDC bc;bitmap b;image i = img;if (! img)return;dr = rcanon(dr);if ((dr.width != img->width) || (dr.height != img->height)) {i = scaleimage(img, rect(0, 0, dr.width, dr.height), sr);}b = imagetobitmap(i);/* The next line assumes that the context returned is a NEWcontext, but that should be ok because the object 'b'has just been created in the line above, which meansthat get_context() should create a new context. */bc = get_context(b);BitBlt(dc, dr.x, dr.y, dr.width, dr.height,bc, sr.x, sr.y, SRCCOPY);/* DO NOT rely on the del() mechanism to (eventually) clean upthe context 'bc' (via deletion_traversal() in objects.c).That leads to running out of contexts (see MAX_CONTEXTSin contexts.c). Instead, explicitly dispose of the context here */del_context(b);if (i != img)del(i);del(b);}/* Use this to draw an image containing fully transparent pixels* by using a mask based on the transparent pixels* (you need to create the mask)*/void gmaskimage(drawing d, image img, rect dr, rect sr, image mask){HDC dc = GETHDC(d);HDC bc, mbc, mbwc;bitmap b, mb, mbw;image i = img;image m = mask;if (! img || ! mask)return;dr = rcanon(dr);if ((dr.width != img->width) || (dr.height != img->height)) {i = scaleimage(img, rect(0, 0, dr.width, dr.height), sr);m = scaleimage(mask, rect(0, 0, dr.width, dr.height), sr);}b = imagetobitmap(i);mb = imagetobitmap(m);mbw = newbitmap(dr.width, dr.height, 1);bc = get_context(b);mbc = get_context(mb);mbwc = get_context(mbw);BitBlt(mbwc, sr.x, sr.y, sr.width, sr.height,mbc, sr.x, sr.y, SRCCOPY);MaskBlt(dc, dr.x, dr.y, dr.width, dr.height,bc, sr.x, sr.y,(HBITMAP) mbw->handle, 0, 0,MAKEROP4(SRCCOPY, SRCAND));del_context(b);del_context(mb);del_context(mbw);if (i != img)del(i);if (m != mask)del(m);del(b);del(mb);del(mbw);}/* For ordinary text, e.g. in console */int gdrawstr(drawing d, font f, rgb c, point p, const char *s){POINT curr_pos;int width;HFONT old;HDC dc = GETHDC(d);SetTextColor(dc, getwinrgb(d,c));old = SelectObject(dc, f->handle);MoveToEx(dc, p.x, p.y, NULL);SetBkMode(dc, TRANSPARENT);SetTextAlign(dc, TA_TOP | TA_LEFT | TA_UPDATECP);if (localeCP > 0 && (localeCP != GetACP())) {/* This allows us to change locales and output in the new locale */wchar_t *wc; int n = strlen(s), cnt;wc = alloca((n+1) * sizeof(wchar_t));cnt = mbstowcs(wc, s, n);TextOutW(dc, p.x, p.y, wc, cnt);} elseTextOut(dc, p.x, p.y, s, strlen(s));GetCurrentPositionEx(dc, &curr_pos);width = curr_pos.x - p.x;SelectObject(dc, old);return width;}int gdrawwcs(drawing d, font f, rgb c, point p, const wchar_t *s){POINT curr_pos;int width;HFONT old;HDC dc = GETHDC(d);SetTextColor(dc, getwinrgb(d,c));old = SelectObject(dc, f->handle);MoveToEx(dc, p.x, p.y, NULL);SetBkMode(dc, TRANSPARENT);SetTextAlign(dc, TA_TOP | TA_LEFT | TA_UPDATECP);TextOutW(dc, p.x, p.y, s, wcslen(s));GetCurrentPositionEx(dc, &curr_pos);width = curr_pos.x - p.x;SelectObject(dc, old);return width;}#define CE_NATIVE 0#define CE_UTF8 1/* This version aligns on baseline, and allows hadj = 0, 0.5, 1 */void gdrawstr1(drawing d, font f, rgb c, point p, const char *s, double hadj){HFONT old;HDC dc = GETHDC(d);UINT flags = TA_BASELINE | TA_UPDATECP;SetTextColor(dc, getwinrgb(d,c));old = SelectObject(dc, f->handle);MoveToEx(dc, p.x, p.y, NULL);SetBkMode(dc, TRANSPARENT);if (hadj < 0.25) flags |= TA_LEFT;else if (hadj < 0.75) flags |= TA_CENTER;else flags |= TA_RIGHT;SetTextAlign(dc, flags);TextOut(dc, p.x, p.y, s, strlen(s));SelectObject(dc, old);}/* widechar version */void gwdrawstr1(drawing d, font f, rgb c, point p,const wchar_t *wc, int cnt, double hadj){HFONT old;HDC dc = GETHDC(d);UINT flags = TA_BASELINE | TA_UPDATECP;SetTextColor(dc, getwinrgb(d,c));old = SelectObject(dc, f->handle);MoveToEx(dc, p.x, p.y, NULL);SetBkMode(dc, TRANSPARENT);if (hadj < 0.25) flags |= TA_LEFT;else if (hadj < 0.75) flags |= TA_CENTER;else flags |= TA_RIGHT;SetTextAlign(dc, flags);TextOutW(dc, p.x, p.y, wc, cnt);SelectObject(dc, old);}rect gstrrect(drawing d, font f, const char *s){SIZE size;HFONT old;HDC dc;if (! f)f = SystemFont;if (d)dc = GETHDC(d);elsedc = GetDC(0);old = SelectObject(dc, f->handle);GetTextExtentPoint32(dc, (LPSTR)s, strlen(s), &size);SelectObject(dc, old);if (!d) ReleaseDC(0,dc);return rect(0, 0, size.cx, size.cy);}point gstrsize(drawing d, font f, const char *s){rect r = gstrrect(d, f, s);return pt(r.width, r.height);}int gstrwidth(drawing d, font f, const char *s){rect r = gstrrect(d, f, s);return r.width;}static rect gwcsrect(drawing d, font f, const wchar_t *s){SIZE size;HFONT old;HDC dc;if (! f) f = SystemFont;if (d) dc = GETHDC(d); else dc = GetDC(0);old = SelectObject(dc, f->handle);GetTextExtentPoint32W(dc, (LPWSTR)s, wcslen(s), &size);SelectObject(dc, old);if (!d) ReleaseDC(0,dc);return rect(0, 0, size.cx, size.cy);}int gwcswidth(drawing d, font f, const wchar_t *s){rect r = gwcsrect(d, f ,s);return r.width;}int gstrwidth1(drawing d, font f, const char *s, int enc){rect r;if (enc == CE_UTF8) {wchar_t *wc;int n = strlen(s);wc = alloca((n+1) * sizeof(wchar_t));Rf_utf8towcs(wc, s, n+1);r = gwcsrect(d, f, wc);} elser = gstrrect(d, f, s);return r.width;}int ghasfixedwidth(font f){TEXTMETRIC tm;HFONT old;HDC dc = GetDC(0);old = SelectObject(dc, (HFONT)f->handle);GetTextMetrics(dc, &tm);SelectObject(dc, old);ReleaseDC(0,dc);return !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH);}void gcharmetric(drawing d, font f, int c, int *ascent, int *descent,int *width){int first, last, extra;TEXTMETRIC tm;HFONT old;HDC dc = GETHDC(d);old = SelectObject(dc, (HFONT)f->handle);GetTextMetrics(dc, &tm);first = tm.tmFirstChar;last = tm.tmLastChar;extra = tm.tmExternalLeading + tm.tmInternalLeading - 1;if (c < 0) { /* used for setting cra */SIZE size;char *cc = "M";GetTextExtentPoint32(dc,(LPSTR) cc, 1, &size);*descent = tm.tmDescent ;*ascent = size.cy - *descent;*width = size.cx;if (*width > size.cy) *width = size.cy;} else if (c == 0) {*descent = tm.tmDescent ;*ascent = tm.tmHeight - *descent - extra ;*width = tm.tmMaxCharWidth ;} else if ((first <= c) && (c <= last)) {SIZE size;GetTextExtentPoint32(dc, (LPSTR) &c, 1, &size);*descent = tm.tmDescent ;*ascent = size.cy - *descent - extra ;*width = size.cx;/*Under NT, ' ' gives 0 ascent and descent, which seemscorrect but this : (i) makes R engine to center in random way;(ii) doesn't correspond to what 98 and X do (' ' is therehigh as the full font)*/if ((c != ' ') && (tm.tmPitchAndFamily & TMPF_TRUETYPE)) {GLYPHMETRICS gm;MAT2 m2;m2.eM11.value = m2.eM22.value = (WORD) 1 ;m2.eM21.value = m2.eM12.value = (WORD) 0 ;m2.eM11.fract = m2.eM12.fract = m2.eM21.fract = m2.eM22.fract =(short) 0 ;if (GetGlyphOutline(dc, c, GGO_METRICS, &gm, 0, NULL, &m2)!= GDI_ERROR) {*descent = gm.gmBlackBoxY - gm.gmptGlyphOrigin.y ;*ascent = gm.gmptGlyphOrigin.y + 1;}}} else {*ascent = 0;*descent = 0;*width = 0;}SelectObject(dc, old);}void gwcharmetric(drawing d, font f, int c, int *ascent, int *descent,int *width){int first, last, extra;TEXTMETRICW tm;HFONT old;HDC dc = GETHDC(d);old = SelectObject(dc, (HFONT)f->handle);GetTextMetricsW(dc, &tm);first = tm.tmFirstChar;last = tm.tmLastChar;extra = tm.tmExternalLeading + tm.tmInternalLeading - 1;if (c < 0) { /* used for setting cra */SIZE size;char *cc = "M";GetTextExtentPoint32(dc,(LPSTR) cc, 1, &size);*descent = tm.tmDescent ;*ascent = size.cy - *descent;*width = size.cx;if (*width > size.cy) *width = size.cy;} else if (c == 0) {*descent = tm.tmDescent ;*ascent = tm.tmHeight - *descent - extra ;*width = tm.tmMaxCharWidth ;} else if ((first <= c) && (c <= last)) {SIZE size;wchar_t wc = c;GetTextExtentPoint32W(dc, &wc, 1, &size);*descent = tm.tmDescent ;*ascent = size.cy - *descent - extra ;*width = size.cx;/*Under NT, ' ' gives 0 ascent and descent, which seemscorrect but this : (i) makes R engine to center in random way;(ii) doesn't correspond to what 98 and X do (' ' is therehigh as the full font)*/if ((c!=' ') && (tm.tmPitchAndFamily & TMPF_TRUETYPE)) {GLYPHMETRICS gm;MAT2 m2;m2.eM11.value = m2.eM22.value = (WORD) 1 ;m2.eM21.value = m2.eM12.value = (WORD) 0 ;m2.eM11.fract = m2.eM12.fract =m2.eM21.fract = m2.eM22.fract = (short) 0 ;if (GetGlyphOutlineW(dc, c, GGO_METRICS, &gm, 0, NULL, &m2)!= GDI_ERROR) {*descent = gm.gmBlackBoxY - gm.gmptGlyphOrigin.y ;*ascent = gm.gmptGlyphOrigin.y + 1;}}} else { /* Unicode char out of range */*ascent = 0;*descent = 0;*width = 0;}SelectObject(dc, old);}font gnewfont2(drawing d, const char *face, int style, int size,double rot, int usePoints, int quality){font obj;HFONT hf;LOGFONT lf;if (usePoints) {if ((rot <= 45.0) || ((rot > 135) && (rot <= 225)) || (rot > 315))lf.lfHeight = -MulDiv(size, devicepixelsy(d), 72);elself.lfHeight = -MulDiv(size, devicepixelsx(d), 72);} else lf.lfHeight = -size;lf.lfWidth = 0 ;lf.lfEscapement = lf.lfOrientation = (int) 10*rot;lf.lfWeight = FW_NORMAL;lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = 0;if ((! strcmp(face, "Symbol")) || (! strcmp(face, "Wingdings")))lf.lfCharSet = SYMBOL_CHARSET;elself.lfCharSet = DEFAULT_CHARSET;lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;lf.lfQuality = quality;lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;if ((strlen(face) > 1) && (face[0] == 'T') && (face[1] == 'T')) {const char *pf;lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;for (pf = &face[2]; isspace(*pf) ; pf++);strncpy(lf.lfFaceName, pf, LF_FACESIZE-1);}else {lf.lfOutPrecision = OUT_DEFAULT_PRECIS;strncpy(lf.lfFaceName, face, LF_FACESIZE-1);}if (style & Italic)lf.lfItalic = 1;if (style & Bold)lf.lfWeight = FW_BOLD;if (style & FixedWidth)lf.lfPitchAndFamily |= FIXED_PITCH;if (style & SansSerif)lf.lfPitchAndFamily |= FF_SWISS;if ((hf = CreateFontIndirect(&lf)) == 0)return NULL;obj = new_font_object(hf);if (obj)obj->text = new_string(face);if (d && ((d->kind == PrinterObject) ||(d->kind == MetafileObject))) {TEXTMETRIC tm;HFONT old = SelectObject((HDC)d->handle, hf);GetTextMetrics((HDC)d->handle, &tm);obj->rect.width = tm.tmAveCharWidth;obj->rect.height = tm.tmHeight;obj->rect.x = tm.tmAscent - tm.tmInternalLeading;obj->rect.y = tm.tmDescent;SelectObject((HDC)d->handle, old);}return (font) obj;}font gnewfont(drawing d, const char *face, int style, int size,double rot, int usePoints){return gnewfont2(d, face, style, size, rot, usePoints,DEFAULT_QUALITY);}static int measuredev(drawing dev, int what){HDC hDC;int n;if (dev)hDC = GETHDC(dev);elsehDC = GetDC(NULL);n = GetDeviceCaps(hDC, what);if (!dev) ReleaseDC(NULL, hDC);return n;}#define MEASUREDEV(a) {return measuredev(dev,a);}int devicewidth(drawing dev) MEASUREDEV(HORZRES)int deviceheight(drawing dev) MEASUREDEV(VERTRES)int devicewidthmm(drawing dev) MEASUREDEV(HORZSIZE)int deviceheightmm(drawing dev) MEASUREDEV(VERTSIZE)int devicepixelsx(drawing dev) MEASUREDEV(LOGPIXELSX)int devicepixelsy(drawing dev) MEASUREDEV(LOGPIXELSY)int isTopmost(window c){return GetWindowLong(c->handle, GWL_EXSTYLE) & WS_EX_TOPMOST;}static void setMessageBoxTopmost(window obj){if ((obj->kind == WindowObject) && (isTopmost(obj)))TopmostDialogs |= MB_TOPMOST;}void * getHandle(window c){return (void *) c->handle;}void BringToTop(window c, int stay) /* stay=0 for regular, 1 for topmost, 2 for toggle */{SetForegroundWindow(c->handle); /* needed in Rterm */if (ismdi()) BringWindowToTop(hwndFrame);BringWindowToTop(c->handle);if (stay == 2) stay = !isTopmost(c);if (stay) SetWindowPos(c->handle, HWND_TOPMOST, 0, 0, 0, 0,SWP_NOMOVE | SWP_NOSIZE);else SetWindowPos(c->handle, HWND_NOTOPMOST, 0, 0, 0, 0,SWP_NOMOVE | SWP_NOSIZE);TopmostDialogs &= !MB_TOPMOST;apply_to_list(c->parent->child, setMessageBoxTopmost);}/* type = 1 minimize2 restore3 maximize4 hide*/void GA_msgWindow(window c, int type){int state = -1;switch(type){case 1: state = SW_MINIMIZE; break;case 2: state = SW_SHOWNOACTIVATE; break;case 3: state = SW_MAXIMIZE; break;case 4: state = SW_HIDE; break;default: break;}if (state >= 0) ShowWindow(c->handle, state);}