The R Project SVN R

Rev

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