The R Project SVN R

Rev

Rev 54330 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
4394 ripley 1
/*
2
 * GraphApp - Cross-Platform Graphics Programming Library.
3
 *
4
 * File: context.c -- internal functions for manipulating DCs.
5
 * Platform: Windows  Version: 2.35  Date: 1998/04/04
6
 *
7
 * Version: 1.00  Changes: Original version by Lachlan Patrick.
8
 * Version: 1.05  Changes: Added drawstate information.
9
 * Version: 1.50  Changes: New colour system, dithered grey.
10
 * Version: 2.00  Changes: New object class and context sub-system.
11
 * Version: 2.01  Changes: get_context is now more bulletproof.
12
 * Version: 2.02  Changes: drawto and setwinrgb have been updated.
13
 * Version: 2.20  Changes: Added currentrgb, currentfont etc.
14
 * Version: 2.35  Changes: New reference count technique.
15
 */
16
 
17
/* Copyright (C) 1993-1998 Lachlan Patrick
18
 
19
   This file is part of GraphApp, a cross-platform C graphics library.
20
 
21
   GraphApp is free software; you can redistribute it and/or modify it
22
   under the terms of the GNU Library General Public License.
23
   GraphApp is distributed in the hope that it will be useful, but
24
   WITHOUT ANY WARRANTY.
25
 
26
   See the file COPYLIB.TXT for details.
27
*/
28
 
45017 ripley 29
/* Copyright (C) 2004	The R Foundation
9184 ripley 30
 
31702 murdoch 31
      Changes for R:  allow 32 contexts not 4
32
      Remove assumption that current->dest is non-NULL
33
*/
34
 
4394 ripley 35
#include "internal.h"
36
 
37
/*
38
 *  Private library variables.
39
 */
40
 
45017 ripley 41
PROTECTED drawstruct app_drawstate =
42
{
43
    NULL,	/* drawing destination */
44
    Black,	/* colour */
45
    S,	/* drawing mode */
46
    {0,0},	/* point */
47
    1,	/* line width */
48
    NULL,	/* font */
49
    NULL,	/* cursor */
50
};
4394 ripley 51
 
45017 ripley 52
PROTECTED drawstate current = & app_drawstate;
53
PROTECTED HDC       dc;	/* shared DC variable */
54
PROTECTED HPEN      the_pen   = 0;
55
PROTECTED HBRUSH    the_brush = 0;
4394 ripley 56
 
45017 ripley 57
PROTECTED COLORREF win_rgb  = 0L;
4394 ripley 58
 
59
/*
60
 *  Private drawing context variables.
61
 */
62
 
45017 ripley 63
static	rgb	prev_pixval	= Black;
64
static	int	prev_width	= 1;
4394 ripley 65
 
45017 ripley 66
static	bitmap	grey_bitmap	= NULL;
4394 ripley 67
 
45017 ripley 68
static rgb grey_cmap [] = {
69
    0x00000000UL,
70
    0x00FFFFFFUL,
71
};
72
static byte grey_pixels [] = {
73
    0, 1, 0, 1, 0, 1, 0, 1,
74
    1, 0, 1, 0, 1, 0, 1, 0,
75
    0, 1, 0, 1, 0, 1, 0, 1,
76
    1, 0, 1, 0, 1, 0, 1, 0,
77
    0, 1, 0, 1, 0, 1, 0, 1,
78
    1, 0, 1, 0, 1, 0, 1, 0,
79
    0, 1, 0, 1, 0, 1, 0, 1,
80
    1, 0, 1, 0, 1, 0, 1, 0,
81
};
82
static imagedata grey_imagedata = {
83
    8,	/* depth */
84
    8,	/* width */
85
    8,	/* height */
86
    2,	/* cmapsize */
87
    grey_cmap,
88
    grey_pixels
89
};
90
static image grey_image = & grey_imagedata;
4394 ripley 91
 
92
/*
93
 *  Private context list structure
94
 */
45017 ripley 95
typedef struct contextinfo  contextinfo;
4394 ripley 96
 
45017 ripley 97
struct contextinfo
98
{
99
    object	obj;	/* back pointer to drawing object */
100
    HDC	dc;	/* handle to DC used when drawing */
101
    HGDIOBJ old_bitmap; /* bitmap returned by SelectObject() */
102
};
4394 ripley 103
 
104
/*
105
 *  The constant MAX_CONTEXTS controls how large the
106
 *  circular array of contexts is, and thus how many
107
 *  concurrently active contexts there can be.  This
108
 *  number should be at least 2,  since bitblt needs
109
 *  two contexts for source and destination bitmaps.
110
 */
45017 ripley 111
#define MAX_CONTEXTS 32
4394 ripley 112
 
45017 ripley 113
static contextinfo context[MAX_CONTEXTS];
114
static int num_contexts = 0;
115
static int empty_slot = 0;
4394 ripley 116
 
117
/*
118
 *  Create a pen and a brush for use in drawing.
119
 */
120
static void create_pen_and_brush(rgb c, unsigned long winrgb,
121
		int width, int depth)
122
{
45017 ripley 123
    the_pen = CreatePen(PS_INSIDEFRAME, width, winrgb);
4394 ripley 124
 
45017 ripley 125
    if ((depth == 1) && (c == Grey))
126
	the_brush = CreatePatternBrush(grey_bitmap->handle);
127
    else
128
	the_brush = CreateSolidBrush(winrgb);
4394 ripley 129
}
130
 
131
/*
132
 *  Delete any created pens and brushes.
133
 *  Assumes they are not selected into any valid DC.
134
 */
135
static void delete_pen_and_brush(void)
136
{
45017 ripley 137
    DeleteObject(the_pen);
138
    DeleteObject(the_brush);
4394 ripley 139
}
140
 
141
/*
142
 *  Set Windows drawing globals up.
143
 */
144
PROTECTED
145
void init_contexts(void)
146
{
45017 ripley 147
    grey_bitmap = imagetobitmap(grey_image);
148
    grey_bitmap->text = new_string("grey_bitmap");
4394 ripley 149
 
45017 ripley 150
    current = & app_drawstate;
151
    app_drawstate.fnt = SystemFont;
152
    app_drawstate.crsr = ArrowCursor;
4394 ripley 153
 
45017 ripley 154
    create_pen_and_brush(Black, Black, 1, 0);
4394 ripley 155
}
156
 
157
static void free_context(contextinfo *c)
158
{
45017 ripley 159
    if (! c->dc)
160
	return; /* already been deleted */
4394 ripley 161
 
45017 ripley 162
    SelectObject(c->dc, GetStockObject(NULL_PEN));
163
    SelectObject(c->dc, GetStockObject(NULL_BRUSH));
4394 ripley 164
 
45017 ripley 165
    if (c->obj->kind & ControlObject)
166
	ReleaseDC(c->obj->handle, c->dc);
167
    else if (c->obj->kind == BitmapObject) {
168
	SelectObject(c->dc, c->old_bitmap);
169
	DeleteDC(c->dc);
170
    }
4394 ripley 171
 
45017 ripley 172
    if (dc == c->dc)
173
	dc = 0;
4394 ripley 174
 
45017 ripley 175
    c->dc = 0;
176
    c->obj = NULL;
177
    c->old_bitmap = 0;
4394 ripley 178
}
179
 
180
/*
56956 murdoch 181
 *  Add a new DC into our list of DCs.  This is kind of ugly:  num_contexts just keeps growing, unless
182
 *  del_all_contexts is called, when it is set to 0.  The free ones will be scattered through the list.
4394 ripley 183
 */
184
PROTECTED
185
void add_context(object obj, HDC dc, HGDIOBJ old)
186
{
45017 ripley 187
    contextinfo *c;
4394 ripley 188
 
56956 murdoch 189
    /* Search for or clear a spot for the new DC if the array is full. */
45017 ripley 190
    if (num_contexts == MAX_CONTEXTS) {
56956 murdoch 191
        int i = empty_slot;
192
        while ( context[i].dc ) {
193
             i = (i+1) % MAX_CONTEXTS;
194
             if (i == empty_slot) {
195
		 free_context(&context[i]);
196
		 break;
197
	     }
198
	}
199
        c = & context[i];
200
	empty_slot = (i+1) % MAX_CONTEXTS;
45017 ripley 201
    } else {
202
	c = & context[num_contexts];
203
	num_contexts++;
204
    }
205
    /* Add this context to the list. */
206
    c->obj = obj;
207
    c->dc = dc;
208
    c->old_bitmap = old;
4394 ripley 209
}
210
 
211
/*
212
 *  Find and return a DC for a window or a bitmap.
213
 *  Also keep track of which DCs have been created.
214
 */
215
PROTECTED
216
HDC get_context(object obj)
217
{
45017 ripley 218
    int i;
219
    HDC dc;
220
    HGDIOBJ old;
4394 ripley 221
 
45017 ripley 222
    /* Determine if a DC for this object already exists. */
223
    for (i = 0; i < num_contexts; i++) {
224
	if (context[i].obj == obj)
225
	    return context[i].dc;
226
    }
4394 ripley 227
 
45017 ripley 228
    /* Use GetDC or CreateCompatibleDC to return a DC. */
229
    if (obj->kind & ControlObject) {
230
	dc = GetDC(obj->handle);
231
	add_context(obj, dc, 0);
232
    }
233
    else if (obj->kind == BitmapObject) {
234
	dc = CreateCompatibleDC(0);
235
	old = SelectObject(dc, obj->handle);
236
	add_context(obj, dc, old);
237
    }
238
    else {
239
	return NULL;
4394 ripley 240
	/* apperror("Cannot find DC for non-drawable object."); */
45017 ripley 241
    }
4394 ripley 242
 
45017 ripley 243
    /* We only select in the brush or pen we need, when we
244
     * need it. Thus at all other times, they are NULL.
245
     * This ensures we can delete objects when we want, and
246
     * also makes correct context possible. */
247
    SelectObject(dc, GetStockObject(NULL_PEN));
248
    SelectObject(dc, GetStockObject(NULL_BRUSH));
4394 ripley 249
 
45017 ripley 250
    return dc;
4394 ripley 251
}
252
 
253
/*
254
 *  Remove the context from the list by blanking its fields.
255
 *  It is assumed that the DC has previously been released,
256
 *  for example by EndPaint() in the events.c file.
257
 */
258
PROTECTED
259
void remove_context(object obj)
260
{
45017 ripley 261
    int i;
262
    contextinfo *c;
4394 ripley 263
 
45017 ripley 264
    for (i = 0; i < num_contexts; i++) {
265
	c = & context[i];
266
	if (c->obj == obj) {
267
	    c->obj = NULL;
268
	    c->dc = 0;
269
	    c->old_bitmap = 0;
56956 murdoch 270
	    /* If we delete the last one, reduce the count:  avoid 
271
	       a search next time. */
272
	    if (i == num_contexts - 1)
273
	    	num_contexts--;
274
	    else
275
	    	empty_slot = i;
4394 ripley 276
	}
45017 ripley 277
    }
4394 ripley 278
}
279
 
280
/*
281
 *  Free the DC associated with a given object.
282
 */
283
PROTECTED
284
void del_context(object obj)
285
{
45017 ripley 286
    int i;
287
    contextinfo *c;
4394 ripley 288
 
45017 ripley 289
    for (i = 0; i < num_contexts; i++) {
290
	c = & context[i];
291
	if (c->obj == obj) {
292
	    free_context(c);
293
	    c->obj = NULL;
294
	    c->dc = 0;
295
	    c->old_bitmap = 0;
56956 murdoch 296
	    /* If we delete the last one, reduce the count:  avoid 
297
	       a search next time. */
298
	    if (i == num_contexts - 1)
299
	    	num_contexts--;
300
	    else
301
	    	empty_slot = i;
302
 
4394 ripley 303
	}
45017 ripley 304
    }
4394 ripley 305
}
306
 
307
/*
308
 *  Get rid of all DCs.
309
 */
310
PROTECTED
311
void del_all_contexts(void)
312
{
45017 ripley 313
    int i;
314
    contextinfo *c;
4394 ripley 315
 
45017 ripley 316
    for (i = 0; i < num_contexts; i++) {
317
	c = & context[i];
318
	free_context(c);
319
	c->obj = NULL;
320
	c->dc = 0;
321
	c->old_bitmap = 0;
322
    }
323
    num_contexts = 0;
324
    empty_slot = 0;
4394 ripley 325
}
326
 
327
/*
328
 *  De-initialise drawing variables.
329
 */
330
PROTECTED
331
void finish_contexts(void)
332
{
45017 ripley 333
    del_all_contexts();
334
    DeleteObject(the_pen);
335
    DeleteObject(the_brush);
4394 ripley 336
}
337
 
338
/*
339
 *  Set up a pen and a brush for use in colouring things, and return
340
 *  the Windows RGB value equivalent to the library rgb value.
341
 */
342
static unsigned long set_win_rgb(rgb c, int width)
343
{
45017 ripley 344
    int r, g, b;
345
    long luminance;
346
    int depth;
347
    unsigned long winrgb;
4394 ripley 348
 
45017 ripley 349
    if (current->mode == Ones)
350
	c = White;
351
    else if (current->mode == Zeros)
352
	c = Black;
4394 ripley 353
 
45017 ripley 354
    r = (int) ((c >> 16) & 0x000000FFL);
355
    g = (int) ((c >>  8) & 0x000000FFL);
356
    b = (int) ((c >>  0) & 0x000000FFL);
4394 ripley 357
 
45017 ripley 358
    if (current->dest) depth = getdepth(current->dest);
359
    else depth = 2;  /* set default minimal depth if no current window */
4394 ripley 360
 
45017 ripley 361
    if (depth <= 2)	/* map to black or white, or grey if c == Grey */
362
    {
363
	luminance = (r*3 + g*5 + b) / 9;
364
	if (luminance > 0x0087)		r = g = b = 0x00FF;
365
	else if (luminance <= 0x0077)	r = g = b = 0x0000;
366
	else				r = g = b = 0x0080;
367
	c = rgb(r,g,b);
368
    }
4394 ripley 369
 
45017 ripley 370
    winrgb = RGB(r, g, b);
4394 ripley 371
 
45017 ripley 372
    /* Has a colour or width change occured? */
373
    if ((c != prev_pixval) || (width != prev_width))
374
    {
375
	prev_pixval = c;
376
	prev_width = width;
4394 ripley 377
 
45017 ripley 378
	/* delete any old objects */
379
	delete_pen_and_brush();
4394 ripley 380
 
45017 ripley 381
	/* set up new objects */
382
	create_pen_and_brush(c, winrgb, width, depth);
383
    }
384
    return winrgb;
4394 ripley 385
}
386
 
387
void setcursor(cursor c)
388
{
45017 ripley 389
    decrease_refcount(current->crsr);
390
    current->crsr = c;
391
    if (c) SetCursor((HCURSOR) c->handle);
392
    increase_refcount(c);
4394 ripley 393
}
394
 
395
void setfont(font f)
396
{
45017 ripley 397
    decrease_refcount(current->fnt);
398
    current->fnt = f;
399
    increase_refcount(f);
4394 ripley 400
}
401
 
402
/*
403
 *  Set the way that source and destination pixels are combined
404
 *  when drawing.
405
 */
406
void setdrawmode(int mode)
407
{
45017 ripley 408
    current->mode = mode & 0x0F; /* must be between 0x00 and 0x0F */
4394 ripley 409
}
410
 
411
/*
412
 *  Set the colour.
413
 */
414
void setrgb(rgb hue)
415
{
45017 ripley 416
    current->hue = hue;
417
    win_rgb = set_win_rgb(hue, current->linewidth);
4394 ripley 418
}
419
 
420
/*
421
 *  Set the line width.
422
 */
423
void setlinewidth(int width)
424
{
45017 ripley 425
    if (width < 1)
426
	width = 1;
427
    current->linewidth = width;
428
    win_rgb = set_win_rgb(current->hue, current->linewidth);
4394 ripley 429
}
430
 
431
/*
432
 *  Set which window/menubar/menu to add new objects too.
433
 */
434
void addto(object obj)
435
{
45017 ripley 436
    if (! obj)
437
	return;
438
    switch (obj->kind) {
439
    case WindowObject:	current_window = obj;	break;
440
    case MenubarObject:	current_menubar = obj;	break;
441
    case MenuObject:	current_menu = obj;	break;
442
    }
4394 ripley 443
}
444
 
445
/*
446
 *  Set which bitmap or window to draw to; allocate a DC too.
447
 */
448
void drawto(drawing d)
449
{
45017 ripley 450
    if (! d) {
451
	current = & app_drawstate;
452
	current->dest = NULL;
453
	return;
454
    }
4394 ripley 455
 
45017 ripley 456
    dc = get_context(d);
4394 ripley 457
 
45017 ripley 458
    if (d->drawstate) {
459
	/* Change the current drawing state to this one. */
460
	current = d->drawstate;
461
	current->dest = d;
462
	win_rgb = set_win_rgb(current->hue, current->linewidth);
463
    }
464
    else {
465
	/* Otherwise just use the current drawing state. */
466
	current->dest = d;
467
    }
4394 ripley 468
}
469
 
470
/*
471
 *  Ensure drawing variables are set up.
472
 */
473
void setdrawstate(drawstate s)
474
{
45017 ripley 475
    if (! s)
476
	return;
477
    moveto(s->p);
478
    setdrawmode(s->mode);
479
    setcursor(s->crsr);
480
    setfont(s->fnt);
481
    setlinewidth(s->linewidth);
482
    setrgb(s->hue);
4394 ripley 483
}
484
 
485
/*
486
 *  Change the current drawstate to the specified one.
487
 */
488
void restoredrawstate(drawstate s)
489
{
45017 ripley 490
    if (! s)
491
	return;
492
    setdrawstate(s);
493
    discard(s);
4394 ripley 494
}
495
 
496
/*
497
 *  Reset drawing variables to initial values.
498
 */
499
void resetdrawstate(void)
500
{
45017 ripley 501
    setrgb(Black);
502
    setlinewidth(1);
503
    setcursor(ArrowCursor);
504
    moveto(pt(0,0));
505
    setfont(SystemFont);
506
    setdrawmode(S);
4394 ripley 507
}
508
 
509
/*
510
 *  Return a new copy of the current drawing state.
511
 */
512
drawstate copydrawstate(void)
513
{
45017 ripley 514
    drawstate s = NULL;
4394 ripley 515
 
45017 ripley 516
    if (current) {
517
	s = create(drawstruct);
518
	if (s)
519
	    *s = *current;
520
    }
521
    return s;
4394 ripley 522
}
523
 
524
/*
525
 *  Return drawing state information.
526
 */
527
 
528
drawing	currentdrawing(void)    { return current->dest; }
45017 ripley 529
rgb	currentrgb(void)        { return current->hue; }
530
int	currentmode(void)       { return current->mode; }
4394 ripley 531
point	currentpoint(void)      { return current->p; }
45017 ripley 532
int	currentlinewidth(void)  { return current->linewidth; }
4394 ripley 533
font	currentfont(void)       { return current->fnt; }
534
cursor	currentcursor(void)     { return current->crsr; }
535
 
45017 ripley 536
int	getkeystate(void)       { return keystate; }