The R Project SVN R

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4394 ripley 1
/*
2
 * GraphApp - Cross-Platform Graphics Programming Library.
3
 *
4
 * File: drawing.c -- all the drawing functions are here.
5
 * Platform: Windows  Version: 2.40  Date: 1998/05/05
6
 *
7
 * Version: 1.00  Changes: Original version by Lachlan Patrick.
8
 * Version: 1.60  Changes: drawarc/fillarc(r,0,360) now encloses.
9
 *    New fillellipse() function replaces Windows Ellipse().
10
 * Version: 2.00  Changes: New class system implemented.
11
 * Version: 2.02  Changes: Added support for functions like MoveToEx.
12
 * Version: 2.15  Changes: Fixed brush origins problem.
13
 * Version: 2.20  Changes: Moved some arrays from context.c to here.
14
 * Version: 2.40  Changes: Moved drawimage to this file.
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
 
29
#include "internal.h"
30
 
31
#if (WINVER < 0x030a)
32
 
33
	#define MoveToEx move_to_ex
34
	#define GetCurrentPositionEx get_current_position_ex
35
	#define GetTextExtentPoint get_text_extent_point
36
 
37
	int move_to_ex(HDC hdc, int x, int y, POINT *p);
38
	int get_current_position_ex(HDC hdc, POINT *p);
39
	int get_text_extent_point(HDC hdc, char *str, int len, SIZE *s);
40
 
41
static int move_to_ex(HDC hdc, int x, int y, POINT *p)
42
{
43
	DWORD result = MoveTo(hdc, x, y);
44
	if (p) {
45
		p->x = LOWORD(result);
46
		p->y = HIWORD(result);
47
	}
48
	return 1;
49
}
50
 
51
static int get_current_position_ex(HDC hdc, POINT *p)
52
{
53
	DWORD result = GetCurrentPosition(hdc);
54
	if (p) {
55
		p->x = LOWORD(result);
56
		p->y = HIWORD(result);
57
	}
58
	return 1;
59
}
60
 
61
static int get_text_extent_point(HDC hdc, char *str, int len, SIZE *s)
62
{
63
	DWORD result = GetTextExtent(hdc, str, len);
64
	if (s) {
65
		s->cx = LOWORD(result);
66
		s->cy = HIWORD(result);
67
	}
68
	return 1;
69
}
70
 
71
#endif  /* WINVER < 0x030a */
72
 
73
/*
74
 *  Windows transfer modes corresponding to bitblt operations.
75
 */
76
static long copy_mode[16] = {
77
	BLACKNESS,	/* Zeros */
78
	NOTSRCERASE,	/* DnorS */
79
	0x00220326L,	/* DandnotS */
80
	NOTSRCCOPY,	/* notS */
81
	SRCERASE,	/* notDandS */
82
	DSTINVERT,	/* notD */
83
	SRCINVERT,	/* DxorS */
84
	0x007700E6L,	/* DnandS */
85
	SRCAND,		/* DandS */
86
	0x00990066L,	/* DxnorS */
87
	0x00AA0029L,	/* D */			/* = no-op */
88
	MERGEPAINT,	/* DornotS */
89
	SRCCOPY,	/* S */
90
	0x00DD0228L,	/* notDorS */
91
	SRCPAINT,	/* DorS */
92
	WHITENESS	/* Ones */
93
};
94
 
95
/*
96
 *  Windows transfer modes corresponding to patblt operations.
97
 */
98
static long pat_mode[16] = {
99
	BLACKNESS,	/* Zeros */
100
	0x000500A9L,	/* DnorP */
101
	0x000A0329L,	/* DandnotP */
102
	0x000F0001L,	/* notP */
103
	0x00500325L,	/* notDandP */
104
	DSTINVERT,	/* notD */
105
	PATINVERT,	/* DxorP */
106
	0x005F00E9L,	/* DnandP */
107
	0x00A000C9L,	/* DandP */
108
	0x00A50065L,	/* DxnorP */
109
	0x00AA0029L,	/* D */			/* = no-op */
110
	0x00AF0229L,	/* DornotP */
111
	PATCOPY,	/* P */
112
	0x00F50225L,	/* notDorP */
113
	0x00FA0089L,	/* DorP */
114
	WHITENESS	/* Ones */
115
};
116
 
117
/*
118
 *  Windows transfer modes corresponding to pen drawing.
119
 */
120
static int pen_mode[16] = {
121
	R2_BLACK,	/* Zeros */
122
	R2_NOTMERGEPEN,	/* DnorS */
123
	R2_MASKNOTPEN,	/* DandnotS */
124
	R2_NOTCOPYPEN,	/* notS */
125
	R2_MASKPENNOT,	/* notDandS */
126
	R2_NOT,		/* notD */
127
	R2_XORPEN,	/* DxorS */
128
	R2_NOTMASKPEN,	/* DnandS */
129
	R2_MASKPEN,	/* DandS */
130
	R2_NOTXORPEN,	/* DxnorS */
131
	R2_NOP,		/* D */			/* = no-op */
132
	R2_MERGENOTPEN,	/* DornotS */
133
	R2_COPYPEN,	/* S */
134
	R2_MERGEPENNOT,	/* notDorS */
135
	R2_MERGEPEN,	/* DorS */
136
	R2_WHITE	/* Ones */
137
};
138
 
139
/*
140
 *  Some clipping functions.
141
 */
142
rect getcliprect(void)
143
{
144
	RECT R;
145
	rect r;
146
 
147
	GetClipBox(dc, &R);
148
	r.x = R.left;
149
	r.y = R.top;
150
	r.width = R.right - R.left;
151
	r.height = R.bottom - R.top;
152
	return r;
153
}
154
 
155
void setcliprect(rect r)
156
{
157
	HRGN rgn;
158
 
159
	rgn = CreateRectRgn(r.x, r.y, r.x+r.width, r.y+r.height);
160
	SelectClipRgn(dc, rgn);
161
	DeleteObject(rgn);
162
}
163
 
164
/*
165
 *  Ensure that drawing is possible by creating DCs and windows
166
 *  as necessary.
167
 */
168
PROTECTED
169
window simple_window(void)
170
{
171
	window w;
172
	w = newwindow("Graphics", rect(0,0,0,0), StandardWindow);
173
	show(w);
174
	return w;
175
}
176
 
177
/*
178
 *  Fix brush origins.
179
 */
180
PROTECTED
181
void fix_brush(HDC dc, drawing obj, HBRUSH brush)
182
{
183
	POINT p;
184
	HWND hwnd;
185
	drawing parent;
186
 
187
	parent = parentwindow(obj);
188
	if (! parent)
189
		return;
190
	hwnd = parent->handle;
191
	p.x = p.y = 0;
192
	ClientToScreen(hwnd, &p);
193
	if (brush)
194
		UnrealizeObject(brush);
195
#if (WINVER <= 0x030a)
196
	/* Microsoft keeps changing which functions they include in GDI.DLL */
197
	/* But this function should work on systems before Win 95 */
198
	SetBrushOrg(dc, p.x, p.y);
199
#else
200
	/* And this function should work on Win 95 and NT */
201
	SetBrushOrgEx(dc, p.x, p.y, &p);
202
#endif
203
}
204
 
205
/*
206
 *  All drawing functions call enabledrawing.
207
 */
208
static void enable_drawing(void)
209
{
210
	if (! current->dest) {
211
		if (! current_window)
212
			current_window = simple_window();
213
		show(current_window);
214
		drawto(current_window);
215
	}
216
	if (! dc)
217
		dc = get_context(current->dest);
218
 
219
	fix_brush(dc, current->dest, the_brush);
220
}
221
 
222
/*
223
 *  Drawing functions begin here.
224
 */
225
void bitblt(bitmap db, bitmap sb, point p, rect r, int mode)
226
{
227
	HDC src;
228
	HDC dst;
229
 
230
	dst = get_context((object)db);
231
	src = get_context((object)sb);
232
 
233
	BitBlt(dst, p.x, p.y, r.width, r.height, src, r.x, r.y,
234
		copy_mode[mode&0x0F]);
235
}
236
 
237
void scrollrect(point dp, rect r)
238
{
239
	rect cliprect = getrect(current->dest);
240
 
241
	enable_drawing();
242
	ScrollDC(dc, dp.x-r.x, dp.y-r.y,
243
		(RECT *)&r, (RECT *) &cliprect, 0, NULL);
244
}
245
 
246
void copyrect(bitmap sb, point p, rect r)
247
{
248
	enable_drawing();
249
	bitblt(current->dest, sb, p, r, S);
250
}
251
 
252
void texturerect(bitmap sb, rect dr)
253
{
254
	long x, y, sw, sh, sdx, sdy;
255
	long right, bottom;
256
	rect sr,r;
257
 
258
	enable_drawing();
259
	sr = getrect(sb);
260
	sw = sr.width;
261
	sh = sr.height;
262
	right = dr.x + dr.width;
263
	bottom = dr.y + dr.height;
264
 
265
	for (y = dr.y; y <= bottom; y += sh)
266
		for (x = dr.x; x <= right; x += sw) {
267
 
268
			/* reduce size of source rectangle for clipping */
269
			if (x+sw > right)	sdx = right - x;
270
			else			sdx = sw;
271
			if (y+sh > bottom)	sdy = bottom - y;
272
			else			sdy = sh;
273
 
274
			r = rect(sr.x, sr.y, sdx, sdy);
275
			copyrect(sb, pt(x,y), r);
276
		}
277
}
278
 
279
void invertrect(rect r)
280
{
281
	enable_drawing();
282
	PatBlt(dc, r.x, r.y, r.width, r.height, DSTINVERT);
283
}
284
 
285
rgb getpixel(point p)
286
{
287
	rgb c;
288
 
289
	enable_drawing();
290
	c = GetPixel(dc, p.x, p.y);
291
	c = ((c&0x000000FFL)<<16) | (c&0x0000FF00L) |
292
		((c&0x00FF0000L)>>16);
293
	return c;
294
}
295
 
296
void setpixel(point p, rgb c)
297
{
298
	rgb old = current->hue;
299
 
300
	enable_drawing();
301
	setrgb(c);
302
	SelectObject(dc, the_brush);
303
	PatBlt(dc, p.x, p.y, 1, 1, pat_mode[current->mode]);
304
	SelectObject(dc, GetStockObject(NULL_BRUSH));
305
	setrgb(old);
306
}
307
 
308
void moveto(point p)
309
{
310
	current->p = p;
311
}
312
 
313
void lineto(point p)
314
{
315
	drawline(current->p, p);
316
	moveto(p);
317
}
318
 
319
void drawpoint(point p)
320
{
321
	setpixel(p, current->hue);
322
}
323
 
324
void drawline(point p1, point p2)
325
{
326
	if ((p1.x == p2.x) && (p1.y == p2.y))
327
		return; /* same point so draw nothing */
328
 
329
	enable_drawing();
330
	SelectObject(dc, the_pen);
331
	SetROP2(dc, pen_mode[current->mode]);
332
	MoveToEx(dc, p1.x, p1.y, NULL);
333
	LineTo(dc, p2.x, p2.y);
334
	SelectObject(dc, GetStockObject(NULL_PEN));
335
}
336
 
337
void drawrect(rect r)
338
{
339
	enable_drawing();
340
	SelectObject(dc, the_pen);
341
	SetROP2(dc, pen_mode[current->mode]);
342
	Rectangle(dc, r.x, r.y, r.x+r.width, r.y+r.height);
343
	SelectObject(dc, GetStockObject(NULL_PEN));
344
}
345
 
346
void fillrect(rect r)
347
{
348
	enable_drawing();
349
	SelectObject(dc, the_brush);
350
	PatBlt(dc, r.x, r.y, r.width, r.height, pat_mode[current->mode]);
351
	SelectObject(dc, GetStockObject(NULL_BRUSH));
352
}
353
 
354
#define deg2rad(deg) ((deg)*2*Pi/360)
355
 
356
void drawarc(rect r, int start_angle, int end_angle)
357
{
358
	point p0, p1, p2;
359
	double start, end;
360
	int fudge;
361
 
362
	enable_drawing();
363
 
364
	if (start_angle == end_angle)
365
		return;
366
	if (((end_angle - start_angle) % 360) == 0) {
367
		drawarc(r, 0, 180);
368
		drawarc(r, 180, 360);
369
		return;
370
	}
371
 
372
	start = deg2rad(start_angle);
373
	end = deg2rad(end_angle);
374
	p0 = midpt(topleft(r), bottomright(r));
375
	p1.x = p0.x + (int)(cos(start) * 512); /* arbitrary hypotenuse */
376
	p1.y = p0.y - (int)(sin(start) * 512);
377
	p2.x = p0.x + (int)(cos(end) * 512);
378
	p2.y = p0.y - (int)(sin(end) * 512);
379
 
380
	SelectObject(dc, the_pen);
381
	SetROP2(dc, pen_mode[current->mode]);
382
	if (current->linewidth % 2) /* Ask Bill Gates why we need this */
383
		fudge = 0;
384
	else
385
		fudge = 1;
386
	Arc(dc, r.x, r.y, r.x+r.width+fudge, r.y+r.height+fudge,
387
		p1.x, p1.y, p2.x, p2.y);
388
	SelectObject(dc, GetStockObject(NULL_PEN));
389
}
390
 
391
void fillarc(rect r, int start_angle, int end_angle)
392
{
393
	point p0, p1, p2;
394
	double start, end;
395
 
396
	enable_drawing();
397
 
398
	if (start_angle == end_angle)
399
		return;
400
	if (((end_angle - start_angle) % 360) == 0) {
401
		fillellipse(r);
402
		return;
403
	}
404
 
405
	start = deg2rad(start_angle);
406
	end = deg2rad(end_angle);
407
	p0 = midpt(topleft(r), bottomright(r));
408
	p1.x = p0.x + (int)(cos(start) * 512); /* arbitrary hypotenuse */
409
	p1.y = p0.y - (int)(sin(start) * 512);
410
	p2.x = p0.x + (int)(cos(end) * 512);
411
	p2.y = p0.y - (int)(sin(end) * 512);
412
 
413
	SelectObject(dc, the_brush);
414
	SetROP2(dc, pen_mode[current->mode]);
415
	Pie(dc, r.x, r.y, r.x+r.width+1, r.y+r.height+1,
416
		p1.x, p1.y, p2.x, p2.y);
417
	SelectObject(dc, GetStockObject(NULL_BRUSH));
418
}
419
 
420
void drawellipse(rect r)
421
{
422
	enable_drawing();
423
	SelectObject(dc, the_pen);
424
	SetROP2(dc, pen_mode[current->mode]);
425
	Ellipse(dc, r.x, r.y, r.x+r.width, r.y+r.height);
426
	SelectObject(dc, GetStockObject(NULL_PEN));
427
}
428
 
429
/*
430
 *  The old fillellipse function.
431
 *
432
 *  This function used the inbuilt Windows Ellipse function,
433
 *  which is not symmetric and also produces strange results
434
 *  at low sizes (the 'ellipses' look egg-shaped).
435
 */
436
void oldfillellipse(rect r)
437
{
438
	enable_drawing();
439
	SelectObject(dc, the_brush);
440
	SetROP2(dc, pen_mode[current->mode]);
441
	Ellipse(dc, r.x, r.y, r.x+r.width+1, r.y+r.height+1);
442
	SelectObject(dc, GetStockObject(NULL_BRUSH));
443
}
444
 
445
/*
446
 *  Fill an ellipse using a stored rectangle algorithm.
447
 *
448
 *  This algorithm is horizontally and vertically symmetrical,
449
 *  unlike the inbuilt Windows 3.1 algorithm, and also
450
 *  produces better looking ellipses at small sizes.
451
 */
452
#define fastfillrect(x,y,w,h) PatBlt(dc,(x),(y),(w),(h),mode)
453
 
454
void fillellipse(rect r)
455
{			/* e(x,y) = b*b*x*x + a*a*y*y - a*a*b*b */
456
	register long mode = pat_mode[current->mode];
457
 
458
	int w_odd = (r.width & 0x0001);
459
	int h_odd = (r.height & 0x0001);
460
	int a = r.width >> 1;
461
	int b = r.height >> 1;
462
	point c = pt(r.x+a,r.y+b);
463
	int x = 0;
464
	int y = b;
465
	long a2 = a*a;
466
	long b2 = b*b;
467
	long xcrit = ((a2+a2+a2) >> 2) + 1;
468
	long ycrit = ((b2+b2+b2) >> 2) + 1;
469
	long t = b2 + a2 - (a2+a2)*b;	/* t = e(x+1,y-1) */
470
	long dxt = b2*(3+x+x);
471
	long dyt = a2*(3-y-y);
472
	int d2xt = b2+b2;
473
	int d2yt = a2+a2;
474
	int stored = 0;
475
	int sx = 0, sy = 0, sh = 0; /* stored values of x, y, height */
476
 
477
	if ((r.width > 31) && (r.height > 31)) {
478
		oldfillellipse(r);
479
		return;
480
	}
481
	if ((r.width < 3) || (r.height < 3)) {
482
		fillrect(r);
483
		return;
484
	}
485
 
486
	enable_drawing();
487
	SelectObject(dc, the_brush);
488
 
489
	if (w_odd == 0) {
490
		fastfillrect(c.x-1,c.y-b,2,r.height);
491
	}
492
 
493
	while (y > 0) {
494
 
495
		if (stored) {
496
			if (sx != x) { /* output stored rect */
497
				fastfillrect(c.x-sx,c.y-sy,
498
					sx+sx+w_odd,sh);
499
				fastfillrect(c.x-sx,c.y+sy+h_odd-sh,
500
					sx+sx+w_odd,sh);
501
				stored = 0;
502
			}
503
			else /* increment height of stored rect */
504
				sh++;
505
		}
506
 
507
		if (t + a2*y < xcrit) { /* e(x+1,y-1/2) <= 0 */
508
			/* move left and right to encounter edge */
509
			x += 1;
510
			t += dxt;
511
			dxt += d2xt;
512
		} else if (t - b2*x >= ycrit) { /* e(x+1/2,y-1) > 0 */
513
			/* drop down one line */
514
			if (!stored) {
515
				sx = x;
516
				sy = y;
517
				sh = 1;
518
				stored = 1;
519
			}
520
			y -= 1;
521
			t += dyt;
522
			dyt += d2yt;
523
		} else {
524
			/* drop diagonally down and out */
525
			if (!stored) {
526
				sx = x;
527
				sy = y;
528
				sh = 1;
529
				stored = 1;
530
			}
531
 
532
			x += 1;
533
			y -= 1;
534
			t += dxt + dyt;
535
			dxt += d2xt;
536
			dyt += d2yt;
537
		}
538
	}
539
	if (stored) { /* output stored rectangle */
540
		fastfillrect(c.x-sx,c.y-sy,sx+sx+w_odd,sh);
541
		fastfillrect(c.x-sx,c.y+sy+h_odd-sh,sx+sx+w_odd,sh);
542
		stored = 0;
543
	}
544
	if (x <= a){
545
		fastfillrect(c.x-a,c.y-y,a+a+w_odd,1);
546
		fastfillrect(c.x-a,c.y+y-1+h_odd,a+a+w_odd,1);
547
	}
548
 
549
	SelectObject(dc, GetStockObject(NULL_BRUSH));
550
}
551
 
552
void drawroundrect(rect r)
553
{
554
	int minimum, radius;
555
 
556
	enable_drawing();
557
	SelectObject(dc, the_pen);
558
	SetROP2(dc, pen_mode[current->mode]);
559
	minimum = min(r.width, r.height);
560
	if ((radius = minimum/2) < 16)
561
		radius = 16;
562
	RoundRect(dc, r.x, r.y, r.x+r.width, r.y+r.height,
563
		radius, radius);
564
	SelectObject(dc, GetStockObject(NULL_PEN));
565
}
566
 
567
void fillroundrect(rect r)
568
{
569
	int minimum, radius;
570
 
571
	enable_drawing();
572
	SelectObject(dc, the_brush);
573
	SetROP2(dc, pen_mode[current->mode]);
574
	minimum = min(r.width, r.height);
575
	if ((radius = minimum/2) < 16)
576
		radius = 16;
577
	RoundRect(dc, r.x, r.y, r.x+r.width+1, r.y+r.height+1,
578
		radius, radius);
579
	SelectObject(dc, GetStockObject(NULL_BRUSH));
580
}
581
 
582
void drawpolygon(point *p, int n)
583
{
584
	enable_drawing();
585
	SelectObject(dc, the_pen);
586
	SetROP2(dc, pen_mode[current->mode]);
587
	Polyline(dc, (POINT FAR *) p, n);
588
	SelectObject(dc, GetStockObject(NULL_PEN));
589
}
590
 
591
void fillpolygon(point *p, int n)
592
{
593
	enable_drawing();
594
	SelectObject(dc, the_brush);
595
	SetROP2(dc, pen_mode[current->mode]);
596
	Polygon(dc, (POINT FAR *) p, n);
597
	SelectObject(dc, GetStockObject(NULL_BRUSH));
598
}
599
 
600
/*
601
 *  String drawing functions.
602
 */
603
 
604
/*
605
 *  Drawstr returns the width of the string drawn.
606
 */
607
int drawstr(point p, char *s)
608
{
609
	POINT curr_pos;
610
	int width;
611
	HFONT old;
612
 
613
	enable_drawing();
614
	SetTextColor(dc, win_rgb); /* set colour */
615
	if (! current->fnt)
616
		current->fnt = SystemFont;
617
	old = SelectObject(dc, current->fnt->handle);
618
	MoveToEx(dc, p.x, p.y, NULL);
619
	SetBkMode(dc, TRANSPARENT);
620
	SetTextAlign(dc, TA_LEFT | TA_UPDATECP);
621
 
622
	TextOut(dc, p.x, p.y, s, strlen(s));
623
 
624
	GetCurrentPositionEx(dc, &curr_pos);
625
	width = curr_pos.x - p.x;
626
	SelectObject(dc, old);
627
	/* always leave a DC with no real font selected */
628
 
629
	return width;
630
}
631
 
632
rect strrect(font f, char *s)
633
{
634
	SIZE size;
635
	long h;
636
	HFONT old;
637
	HDC dc;
638
 
639
	if (! f)
640
		f = SystemFont;
641
 
642
	h = getheight(f);
643
 
644
	dc = GetDC(0); /* get screen dc */
645
	old = SelectObject(dc, f->handle);
646
	GetTextExtentPoint(dc, (LPSTR) s, strlen(s), &size);
647
	SelectObject(dc, old);
648
	ReleaseDC(0, dc);
649
 
650
	return rect(0,0,size.cx, h);
651
}
652
 
653
point strsize(font f, char *s)
654
{
655
	rect r = strrect(f,s);
656
	return pt(r.width, r.height);
657
}
658
 
659
int strwidth(font f, char *s)
660
{
661
	rect r = strrect(f,s);
662
	return r.width;
663
}
664
 
665
/*
666
 *  Draw an image:
667
 */
668
void drawimage(image img, rect dr, rect sr)
669
{
670
	bitmap b;
671
	image i = img;
672
 
673
	if (! img)
674
		return;
675
	enable_drawing();
676
	dr = rcanon(dr);
677
	if ((dr.width != img->width) || (dr.height != img->height))
678
		i = scaleimage(img, rect(0,0,dr.width,dr.height), sr);
679
	b = imagetobitmap(i);
680
	copyrect(b, pt(dr.x,dr.y), getrect(b));
681
	del(b);
682
	if (i != img)
683
		del(i);
684
}