Rev 84680 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** GraphApp - Cross-Platform Graphics Programming Library.** File: context.c -- internal functions for manipulating DCs.* Platform: Windows Version: 2.35 Date: 1998/04/04** Version: 1.00 Changes: Original version by Lachlan Patrick.* Version: 1.05 Changes: Added drawstate information.* Version: 1.50 Changes: New colour system, dithered grey.* Version: 2.00 Changes: New object class and context sub-system.* Version: 2.01 Changes: get_context is now more bulletproof.* Version: 2.02 Changes: drawto and setwinrgb have been updated.* Version: 2.20 Changes: Added currentrgb, currentfont etc.* Version: 2.35 Changes: New reference count technique.*//* 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: allow 32 contexts not 4Remove assumption that current->dest is non-NULLCopyright (C) 2023 The R Core TeamChanges for R: avoid naming conflict with LLVM*/#include "internal.h"/** Private library variables.*/PROTECTED drawstruct app_drawstate ={NULL, /* drawing destination */Black, /* colour */GA_S, /* drawing mode */{0,0}, /* point */1, /* line width */NULL, /* font */NULL, /* cursor */};PROTECTED drawstate current = & app_drawstate;PROTECTED HDC dc; /* shared DC variable */PROTECTED HPEN the_pen = 0;PROTECTED HBRUSH the_brush = 0;PROTECTED COLORREF win_rgb = 0L;/** Private drawing context variables.*/static rgb prev_pixval = Black;static int prev_width = 1;static bitmap grey_bitmap = NULL;static rgb grey_cmap [] = {0x00000000UL,0x00FFFFFFUL,};static GAbyte grey_pixels [] = {0, 1, 0, 1, 0, 1, 0, 1,1, 0, 1, 0, 1, 0, 1, 0,0, 1, 0, 1, 0, 1, 0, 1,1, 0, 1, 0, 1, 0, 1, 0,0, 1, 0, 1, 0, 1, 0, 1,1, 0, 1, 0, 1, 0, 1, 0,0, 1, 0, 1, 0, 1, 0, 1,1, 0, 1, 0, 1, 0, 1, 0,};static imagedata grey_imagedata = {8, /* depth */8, /* width */8, /* height */2, /* cmapsize */grey_cmap,grey_pixels};static image grey_image = & grey_imagedata;/** Private context list structure*/typedef struct contextinfo contextinfo;struct contextinfo{object obj; /* back pointer to drawing object */HDC dc; /* handle to DC used when drawing */HGDIOBJ old_bitmap; /* bitmap returned by SelectObject() */};/** The constant MAX_CONTEXTS controls how large the* circular array of contexts is, and thus how many* concurrently active contexts there can be. This* number should be at least 2, since bitblt needs* two contexts for source and destination bitmaps.*/#define MAX_CONTEXTS 32static contextinfo context[MAX_CONTEXTS];static int num_contexts = 0;static int empty_slot = 0;/** Create a pen and a brush for use in drawing.*/static void create_pen_and_brush(rgb c, unsigned long winrgb,int width, int depth){the_pen = CreatePen(PS_INSIDEFRAME, width, winrgb);if ((depth == 1) && (c == Grey))the_brush = CreatePatternBrush(grey_bitmap->handle);elsethe_brush = CreateSolidBrush(winrgb);}/** Delete any created pens and brushes.* Assumes they are not selected into any valid DC.*/static void delete_pen_and_brush(void){DeleteObject(the_pen);DeleteObject(the_brush);}/** Set Windows drawing globals up.*/PROTECTEDvoid init_contexts(void){grey_bitmap = imagetobitmap(grey_image);grey_bitmap->text = new_string("grey_bitmap");current = & app_drawstate;app_drawstate.fnt = SystemFont;app_drawstate.crsr = ArrowCursor;create_pen_and_brush(Black, Black, 1, 0);}static void free_context(contextinfo *c){if (! c->dc)return; /* already been deleted */SelectObject(c->dc, GetStockObject(NULL_PEN));SelectObject(c->dc, GetStockObject(NULL_BRUSH));if (c->obj->kind & ControlObject)ReleaseDC(c->obj->handle, c->dc);else if (c->obj->kind == BitmapObject) {SelectObject(c->dc, c->old_bitmap);DeleteDC(c->dc);}if (dc == c->dc)dc = 0;c->dc = 0;c->obj = NULL;c->old_bitmap = 0;}/** Add a new DC into our list of DCs. This is kind of ugly: num_contexts just keeps growing, unless* del_all_contexts is called, when it is set to 0. The free ones will be scattered through the list.*/PROTECTEDvoid add_context(object obj, HDC dc, HGDIOBJ old){contextinfo *c;/* Search for or clear a spot for the new DC if the array is full. */if (num_contexts == MAX_CONTEXTS) {int i = empty_slot;while ( context[i].dc ) {i = (i+1) % MAX_CONTEXTS;if (i == empty_slot) {free_context(&context[i]);break;}}c = & context[i];empty_slot = (i+1) % MAX_CONTEXTS;} else {c = & context[num_contexts];num_contexts++;}/* Add this context to the list. */c->obj = obj;c->dc = dc;c->old_bitmap = old;}/** Find and return a DC for a window or a bitmap.* Also keep track of which DCs have been created.*/PROTECTEDHDC get_context(object obj){int i;HDC dc;HGDIOBJ old;/* Determine if a DC for this object already exists. */for (i = 0; i < num_contexts; i++) {if (context[i].obj == obj)return context[i].dc;}/* Use GetDC or CreateCompatibleDC to return a DC. */if (obj->kind & ControlObject) {dc = GetDC(obj->handle);add_context(obj, dc, 0);}else if (obj->kind == BitmapObject) {dc = CreateCompatibleDC(0);old = SelectObject(dc, obj->handle);add_context(obj, dc, old);}else {return NULL;/* apperror("Cannot find DC for non-drawable object."); */}/* We only select in the brush or pen we need, when we* need it. Thus at all other times, they are NULL.* This ensures we can delete objects when we want, and* also makes correct context possible. */SelectObject(dc, GetStockObject(NULL_PEN));SelectObject(dc, GetStockObject(NULL_BRUSH));return dc;}/** Remove the context from the list by blanking its fields.* It is assumed that the DC has previously been released,* for example by EndPaint() in the events.c file.*/PROTECTEDvoid remove_context(object obj){int i;contextinfo *c;for (i = 0; i < num_contexts; i++) {c = & context[i];if (c->obj == obj) {c->obj = NULL;c->dc = 0;c->old_bitmap = 0;/* If we delete the last one, reduce the count: avoida search next time. */if (i == num_contexts - 1)num_contexts--;elseempty_slot = i;}}}/** Free the DC associated with a given object.*/PROTECTEDvoid del_context(object obj){int i;contextinfo *c;for (i = 0; i < num_contexts; i++) {c = & context[i];if (c->obj == obj) {free_context(c);c->obj = NULL;c->dc = 0;c->old_bitmap = 0;/* If we delete the last one, reduce the count: avoida search next time. */if (i == num_contexts - 1)num_contexts--;elseempty_slot = i;}}}/** Get rid of all DCs.*/PROTECTEDvoid del_all_contexts(void){int i;contextinfo *c;for (i = 0; i < num_contexts; i++) {c = & context[i];free_context(c);c->obj = NULL;c->dc = 0;c->old_bitmap = 0;}num_contexts = 0;empty_slot = 0;}/** De-initialise drawing variables.*/PROTECTEDvoid finish_contexts(void){del_all_contexts();DeleteObject(the_pen);DeleteObject(the_brush);}/** Set up a pen and a brush for use in colouring things, and return* the Windows RGB value equivalent to the library rgb value.*/static unsigned long set_win_rgb(rgb c, int width){int r, g, b;long luminance;int depth;unsigned long winrgb;if (current->mode == Ones)c = White;else if (current->mode == Zeros)c = Black;r = (int) ((c >> 16) & 0x000000FFL);g = (int) ((c >> 8) & 0x000000FFL);b = (int) ((c >> 0) & 0x000000FFL);if (current->dest) depth = getdepth(current->dest);else depth = 2; /* set default minimal depth if no current window */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);}winrgb = RGB(r, g, b);/* Has a colour or width change occured? */if ((c != prev_pixval) || (width != prev_width)){prev_pixval = c;prev_width = width;/* delete any old objects */delete_pen_and_brush();/* set up new objects */create_pen_and_brush(c, winrgb, width, depth);}return winrgb;}void setcursor(cursor c){decrease_refcount(current->crsr);current->crsr = c;if (c) SetCursor((HCURSOR) c->handle);increase_refcount(c);}void setfont(font f){decrease_refcount(current->fnt);current->fnt = f;increase_refcount(f);}/** Set the way that source and destination pixels are combined* when drawing.*/void setdrawmode(int mode){current->mode = mode & 0x0F; /* must be between 0x00 and 0x0F */}/** Set the colour.*/void setrgb(rgb hue){current->hue = hue;win_rgb = set_win_rgb(hue, current->linewidth);}/** Set the line width.*/void setlinewidth(int width){if (width < 1)width = 1;current->linewidth = width;win_rgb = set_win_rgb(current->hue, current->linewidth);}/** Set which window/menubar/menu to add new objects too.*/void addto(object obj){if (! obj)return;switch (obj->kind) {case WindowObject: current_window = obj; break;case MenubarObject: current_menubar = obj; break;case MenuObject: current_menu = obj; break;}}/** Set which bitmap or window to draw to; allocate a DC too.*/void drawto(drawing d){if (! d) {current = & app_drawstate;current->dest = NULL;return;}dc = get_context(d);if (d->drawstate) {/* Change the current drawing state to this one. */current = d->drawstate;current->dest = d;win_rgb = set_win_rgb(current->hue, current->linewidth);}else {/* Otherwise just use the current drawing state. */current->dest = d;}}/** Ensure drawing variables are set up.*/void setdrawstate(drawstate s){if (! s)return;moveto(s->p);setdrawmode(s->mode);setcursor(s->crsr);setfont(s->fnt);setlinewidth(s->linewidth);setrgb(s->hue);}/** Change the current drawstate to the specified one.*/void restoredrawstate(drawstate s){if (! s)return;setdrawstate(s);discard(s);}/** Reset drawing variables to initial values.*/void resetdrawstate(void){setrgb(Black);setlinewidth(1);setcursor(ArrowCursor);moveto(pt(0,0));setfont(SystemFont);setdrawmode(GA_S);}/** Return a new copy of the current drawing state.*/drawstate copydrawstate(void){drawstate s = NULL;if (current) {s = create(drawstruct);if (s)*s = *current;}return s;}/** Return drawing state information.*/drawing currentdrawing(void) { return current->dest; }rgb currentrgb(void) { return current->hue; }int currentmode(void) { return current->mode; }point currentpoint(void) { return current->p; }int currentlinewidth(void) { return current->linewidth; }font currentfont(void) { return current->fnt; }cursor currentcursor(void) { return current->crsr; }int getkeystate(void) { return keystate; }