Rev 44870 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** GraphApp - Cross-Platform Graphics Programming Library.** File: drawing.c -- all the drawing functions are here.* Platform: Windows Version: 2.40 Date: 1998/05/05** Version: 1.00 Changes: Original version by Lachlan Patrick.* Version: 1.60 Changes: drawarc/fillarc(r,0,360) now encloses.* New fillellipse() function replaces Windows Ellipse().* Version: 2.00 Changes: New class system implemented.* Version: 2.02 Changes: Added support for functions like MoveToEx.* Version: 2.15 Changes: Fixed brush origins problem.* Version: 2.20 Changes: Moved some arrays from context.c to here.* Version: 2.40 Changes: Moved drawimage to this file.*//* Copyright (C) 1993-1998 Lachlan PatrickThis file is part of GraphApp, a cross-platform C graphics library.GraphApp is free software; you can redistribute it and/or modify itunder the terms of the GNU Library General Public License.GraphApp is distributed in the hope that it will be useful, butWITHOUT ANY WARRANTY.See the file COPYLIB.TXT for details.*//* Copyright (C) 2004 The R FoundationChanges for R:Remove assumption of current->dest being non-NULL*/#include "internal.h"#if (WINVER < 0x030a)#define MoveToEx move_to_ex#define GetCurrentPositionEx get_current_position_ex#define GetTextExtentPoint get_text_extent_pointint move_to_ex(HDC hdc, int x, int y, POINT *p);int get_current_position_ex(HDC hdc, POINT *p);int get_text_extent_point(HDC hdc, const char *str, int len, SIZE *s);static int move_to_ex(HDC hdc, int x, int y, POINT *p){DWORD result = MoveTo(hdc, x, y);if (p) {p->x = LOWORD(result);p->y = HIWORD(result);}return 1;}static int get_current_position_ex(HDC hdc, POINT *p){DWORD result = GetCurrentPosition(hdc);if (p) {p->x = LOWORD(result);p->y = HIWORD(result);}return 1;}static int get_text_extent_point(HDC hdc, const char *str, int len, SIZE *s){DWORD result = GetTextExtent(hdc, str, len);if (s) {s->cx = LOWORD(result);s->cy = HIWORD(result);}return 1;}#endif /* WINVER < 0x030a *//** Windows transfer modes corresponding to bitblt operations.*/static long copy_mode[16] = {BLACKNESS, /* Zeros */NOTSRCERASE, /* DnorS */0x00220326L, /* DandnotS */NOTSRCCOPY, /* notS */SRCERASE, /* notDandS */DSTINVERT, /* notD */SRCINVERT, /* DxorS */0x007700E6L, /* DnandS */SRCAND, /* DandS */0x00990066L, /* DxnorS */0x00AA0029L, /* D */ /* = no-op */MERGEPAINT, /* DornotS */SRCCOPY, /* S */0x00DD0228L, /* notDorS */SRCPAINT, /* DorS */WHITENESS /* Ones */};/** Windows transfer modes corresponding to patblt operations.*/static long pat_mode[16] = {BLACKNESS, /* Zeros */0x000500A9L, /* DnorP */0x000A0329L, /* DandnotP */0x000F0001L, /* notP */0x00500325L, /* notDandP */DSTINVERT, /* notD */PATINVERT, /* DxorP */0x005F00E9L, /* DnandP */0x00A000C9L, /* DandP */0x00A50065L, /* DxnorP */0x00AA0029L, /* D */ /* = no-op */0x00AF0229L, /* DornotP */PATCOPY, /* P */0x00F50225L, /* notDorP */0x00FA0089L, /* DorP */WHITENESS /* Ones */};/** Windows transfer modes corresponding to pen drawing.*/static int pen_mode[16] = {R2_BLACK, /* Zeros */R2_NOTMERGEPEN, /* DnorS */R2_MASKNOTPEN, /* DandnotS */R2_NOTCOPYPEN, /* notS */R2_MASKPENNOT, /* notDandS */R2_NOT, /* notD */R2_XORPEN, /* DxorS */R2_NOTMASKPEN, /* DnandS */R2_MASKPEN, /* DandS */R2_NOTXORPEN, /* DxnorS */R2_NOP, /* D */ /* = no-op */R2_MERGENOTPEN, /* DornotS */R2_COPYPEN, /* S */R2_MERGEPENNOT, /* notDorS */R2_MERGEPEN, /* DorS */R2_WHITE /* Ones */};/** Some clipping functions.*/rect getcliprect(void){RECT R;rect r;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 setcliprect(rect r){HRGN rgn;rgn = CreateRectRgn(r.x, r.y, r.x+r.width, r.y+r.height);SelectClipRgn(dc, rgn);DeleteObject(rgn);}/** Ensure that drawing is possible by creating DCs and windows* as necessary.*/PROTECTEDwindow simple_window(void){window w;w = newwindow("Graphics", rect(0,0,0,0), StandardWindow);show(w);return w;}/** Fix brush origins.*/PROTECTEDvoid fix_brush(HDC dc, drawing obj, HBRUSH brush){POINT p;HWND hwnd;drawing parent;parent = parentwindow(obj);if (! parent)return;hwnd = parent->handle;p.x = p.y = 0;ClientToScreen(hwnd, &p);if (brush)UnrealizeObject(brush);#if (WINVER <= 0x030a)/* Microsoft keeps changing which functions they include in GDI.DLL *//* But this function should work on systems before Win 95 */SetBrushOrg(dc, p.x, p.y);#else/* And this function should work on Win 95 and NT */SetBrushOrgEx(dc, p.x, p.y, &p);#endif}/** All drawing functions call enabledrawing.*/static void enable_drawing(void){if (! current->dest) {if (! current_window)current_window = simple_window();show(current_window);drawto(current_window);}if (! dc)dc = get_context(current->dest);fix_brush(dc, current->dest, the_brush);}/** Drawing functions begin here.*/void bitblt(bitmap db, bitmap sb, point p, rect r, int mode){HDC src;HDC dst;dst = get_context((object)db);src = get_context((object)sb);BitBlt(dst, p.x, p.y, r.width, r.height, src, r.x, r.y,copy_mode[mode&0x0F]);}void scrollrect(point dp, rect r){rect cliprect;if (current->dest) cliprect = getrect(current->dest);else return;enable_drawing();ScrollDC(dc, dp.x-r.x, dp.y-r.y,rect2RECT(&r), rect2RECT(&cliprect), 0, NULL);}void copyrect(bitmap sb, point p, rect r){enable_drawing();if (current->dest) bitblt(current->dest, sb, p, r, S);}void texturerect(bitmap sb, rect dr){long x, y, sw, sh, sdx, sdy;long right, bottom;rect sr,r;enable_drawing();sr = getrect(sb);sw = sr.width;sh = sr.height;right = dr.x + dr.width;bottom = dr.y + dr.height;for (y = dr.y; y <= bottom; y += sh)for (x = dr.x; x <= right; x += sw) {/* reduce size of source rectangle for clipping */if (x+sw > right) sdx = right - x;else sdx = sw;if (y+sh > bottom) sdy = bottom - y;else sdy = sh;r = rect(sr.x, sr.y, sdx, sdy);copyrect(sb, pt(x,y), r);}}void invertrect(rect r){enable_drawing();PatBlt(dc, r.x, r.y, r.width, r.height, DSTINVERT);}rgb getpixel(point p){rgb c;enable_drawing();c = GetPixel(dc, p.x, p.y);c = ((c&0x000000FFL)<<16) | (c&0x0000FF00L) |((c&0x00FF0000L)>>16);return c;}void setpixel(point p, rgb c){rgb old = current->hue;enable_drawing();setrgb(c);SelectObject(dc, the_brush);PatBlt(dc, p.x, p.y, 1, 1, pat_mode[current->mode]);SelectObject(dc, GetStockObject(NULL_BRUSH));setrgb(old);}void moveto(point p){current->p = p;}void lineto(point p){drawline(current->p, p);moveto(p);}void drawpoint(point p){setpixel(p, current->hue);}void drawline(point p1, point p2){if ((p1.x == p2.x) && (p1.y == p2.y))return; /* same point so draw nothing */enable_drawing();SelectObject(dc, the_pen);SetROP2(dc, pen_mode[current->mode]);MoveToEx(dc, p1.x, p1.y, NULL);LineTo(dc, p2.x, p2.y);SelectObject(dc, GetStockObject(NULL_PEN));}void drawrect(rect r){enable_drawing();SelectObject(dc, the_pen);SetROP2(dc, pen_mode[current->mode]);Rectangle(dc, r.x, r.y, r.x+r.width, r.y+r.height);SelectObject(dc, GetStockObject(NULL_PEN));}void fillrect(rect r){enable_drawing();SelectObject(dc, the_brush);PatBlt(dc, r.x, r.y, r.width, r.height, pat_mode[current->mode]);SelectObject(dc, GetStockObject(NULL_BRUSH));}#define deg2rad(deg) ((deg)*2*Pi/360)void drawarc(rect r, int start_angle, int end_angle){point p0, p1, p2;double start, end;int fudge;enable_drawing();if (start_angle == end_angle)return;if (((end_angle - start_angle) % 360) == 0) {drawarc(r, 0, 180);drawarc(r, 180, 360);return;}start = deg2rad(start_angle);end = deg2rad(end_angle);p0 = midpt(topleft(r), bottomright(r));p1.x = p0.x + (int)(cos(start) * 512); /* arbitrary hypotenuse */p1.y = p0.y - (int)(sin(start) * 512);p2.x = p0.x + (int)(cos(end) * 512);p2.y = p0.y - (int)(sin(end) * 512);SelectObject(dc, the_pen);SetROP2(dc, pen_mode[current->mode]);if (current->linewidth % 2) /* Ask Bill Gates why we need this */fudge = 0;elsefudge = 1;Arc(dc, r.x, r.y, r.x+r.width+fudge, r.y+r.height+fudge,p1.x, p1.y, p2.x, p2.y);SelectObject(dc, GetStockObject(NULL_PEN));}void fillarc(rect r, int start_angle, int end_angle){point p0, p1, p2;double start, end;enable_drawing();if (start_angle == end_angle)return;if (((end_angle - start_angle) % 360) == 0) {fillellipse(r);return;}start = deg2rad(start_angle);end = deg2rad(end_angle);p0 = midpt(topleft(r), bottomright(r));p1.x = p0.x + (int)(cos(start) * 512); /* arbitrary hypotenuse */p1.y = p0.y - (int)(sin(start) * 512);p2.x = p0.x + (int)(cos(end) * 512);p2.y = p0.y - (int)(sin(end) * 512);SelectObject(dc, the_brush);SetROP2(dc, pen_mode[current->mode]);Pie(dc, r.x, r.y, r.x+r.width+1, r.y+r.height+1,p1.x, p1.y, p2.x, p2.y);SelectObject(dc, GetStockObject(NULL_BRUSH));}void drawellipse(rect r){enable_drawing();SelectObject(dc, the_pen);SetROP2(dc, pen_mode[current->mode]);Ellipse(dc, r.x, r.y, r.x+r.width, r.y+r.height);SelectObject(dc, GetStockObject(NULL_PEN));}/** The old fillellipse function.** This function used the inbuilt Windows Ellipse function,* which is not symmetric and also produces strange results* at low sizes (the 'ellipses' look egg-shaped).*/void oldfillellipse(rect r){enable_drawing();SelectObject(dc, the_brush);SetROP2(dc, pen_mode[current->mode]);Ellipse(dc, r.x, r.y, r.x+r.width+1, r.y+r.height+1);SelectObject(dc, GetStockObject(NULL_BRUSH));}/** Fill an ellipse using a stored rectangle algorithm.** This algorithm is horizontally and vertically symmetrical,* unlike the inbuilt Windows 3.1 algorithm, and also* produces better looking ellipses at small sizes.*/#define fastfillrect(x,y,w,h) PatBlt(dc,(x),(y),(w),(h),mode)void fillellipse(rect r){ /* e(x,y) = b*b*x*x + a*a*y*y - a*a*b*b */register long mode = pat_mode[current->mode];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 */if ((r.width > 31) && (r.height > 31)) {oldfillellipse(r);return;}if ((r.width < 3) || (r.height < 3)) {fillrect(r);return;}enable_drawing();SelectObject(dc, the_brush);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));}void drawroundrect(rect r){int minimum, radius;enable_drawing();SelectObject(dc, the_pen);SetROP2(dc, pen_mode[current->mode]);minimum = min(r.width, r.height);if ((radius = minimum/2) < 16)radius = 16;RoundRect(dc, r.x, r.y, r.x+r.width, r.y+r.height,radius, radius);SelectObject(dc, GetStockObject(NULL_PEN));}void fillroundrect(rect r){int minimum, radius;enable_drawing();SelectObject(dc, the_brush);SetROP2(dc, pen_mode[current->mode]);minimum = min(r.width, r.height);if ((radius = minimum/2) < 16)radius = 16;RoundRect(dc, r.x, r.y, r.x+r.width+1, r.y+r.height+1,radius, radius);SelectObject(dc, GetStockObject(NULL_BRUSH));}void drawpolygon(point *p, int n){enable_drawing();SelectObject(dc, the_pen);SetROP2(dc, pen_mode[current->mode]);Polyline(dc, (POINT FAR *) p, n);SelectObject(dc, GetStockObject(NULL_PEN));}void fillpolygon(point *p, int n){enable_drawing();SelectObject(dc, the_brush);SetROP2(dc, pen_mode[current->mode]);Polygon(dc, (POINT FAR *) p, n);SelectObject(dc, GetStockObject(NULL_BRUSH));}/** String drawing functions.*//** Drawstr returns the width of the string drawn.*/int drawstr(point p, const char *s){POINT curr_pos;int width;HFONT old;enable_drawing();SetTextColor(dc, win_rgb); /* set colour */if (! current->fnt)current->fnt = SystemFont;old = SelectObject(dc, current->fnt->handle);MoveToEx(dc, p.x, p.y, NULL);SetBkMode(dc, TRANSPARENT);SetTextAlign(dc, TA_LEFT | TA_UPDATECP);TextOut(dc, p.x, p.y, s, strlen(s));GetCurrentPositionEx(dc, &curr_pos);width = curr_pos.x - p.x;SelectObject(dc, old);/* always leave a DC with no real font selected */return width;}rect strrect(font f, const char *s){SIZE size;long h;HFONT old;HDC dc;if (! f)f = SystemFont;h = getheight(f);dc = GetDC(0); /* get screen dc */old = SelectObject(dc, f->handle);GetTextExtentPoint(dc, (LPSTR) s, strlen(s), &size);SelectObject(dc, old);ReleaseDC(0, dc);return rect(0,0,size.cx, h);}point strsize(font f, const char *s){rect r = strrect(f,s);return pt(r.width, r.height);}int strwidth(font f, const char *s){rect r = strrect(f,s);return r.width;}/** Draw an image:*/void drawimage(image img, rect dr, rect sr){bitmap b;image i = img;if (! img)return;enable_drawing();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);copyrect(b, pt(dr.x,dr.y), getrect(b));del(b);if (i != img)del(i);}