blob: 14aa06415b8143dc73b0c3540aac7073117dfb07 [file] [log] [blame] [edit]
/*
* 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 Patrick
This file is part of GraphApp, a cross-platform C graphics library.
GraphApp is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License.
GraphApp is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY.
See the file COPYLIB.TXT for details.
*/
/* Copyright (C) 2004 The R Foundation
Changes for R: allow 32 contexts not 4
Remove assumption that current->dest is non-NULL
*/
#include "internal.h"
/*
* Private library variables.
*/
PROTECTED drawstruct app_drawstate =
{
NULL, /* drawing destination */
Black, /* colour */
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 32
static 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);
else
the_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.
*/
PROTECTED
void 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.
*/
PROTECTED
void 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.
*/
PROTECTED
HDC 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.
*/
PROTECTED
void 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: avoid
a search next time. */
if (i == num_contexts - 1)
num_contexts--;
else
empty_slot = i;
}
}
}
/*
* Free the DC associated with a given object.
*/
PROTECTED
void 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: avoid
a search next time. */
if (i == num_contexts - 1)
num_contexts--;
else
empty_slot = i;
}
}
}
/*
* Get rid of all DCs.
*/
PROTECTED
void 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.
*/
PROTECTED
void 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(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; }