Sync with trunk (aka 'I want my virtualbox mouse integration too')
[reactos.git] / subsystems / win32 / win32k / objects / drawing.c
1 /*
2 App Software Licence
3 --------------------
4 This package includes software which is copyright (c) L. Patrick.
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
16 3. You may not sell this software package.
17 4. You may include this software in a distribution of other software,
18 and you may charge a nominal fee for the media used.
19 5. You may sell derivative programs, providing that such programs
20 simply use this software in a compiled form.
21 6. You may sell derivative programs which use a compiled, modified
22 version of this software, provided that you have attempted as
23 best as you can to propagate all modifications made to the source
24 code files of this software package back to the original author(s)
25 of this package.
26
27 THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS AS IS, AND
28 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
31 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 SUCH DAMAGE.
38 */
39 /* Copyright (c) L. Patrick
40
41 This file is part of the App cross-platform programming package.
42 You may redistribute it and/or modify it under the terms of the
43 App Software License. See the file LICENSE.TXT for details.
44
45 http://enchantia.com/software/graphapp/
46 http://www.it.usyd.edu.au/~graphapp/
47 */
48 /*
49 Modified for ReactOS
50 */
51
52 #include <w32k.h>
53
54 #define NDEBUG
55 #include <debug.h>
56
57
58 #define DEGREES_TO_RADIANS(deg) ((deg)*2*M_PI/360)
59
60 typedef struct _Rect
61 {
62 int x, y; /* top-left point inside rect */
63 int width, height; /* width and height of rect */
64 } Rect, *PRect;
65
66 int FASTCALL IntFillRect(DC *dc, INT XLeft, INT YLeft, INT Width, INT Height, PBRUSH pbrush, BOOL Pen);
67 int FASTCALL app_fill_rect(DC *dc, Rect r, PBRUSH pbrush, BOOL Pen);
68
69 static
70 POINT
71 INTERNAL_CALL
72 app_new_point(int x, int y)
73 {
74 POINT p;
75 p.x = x;
76 p.y = y;
77 return p;
78 }
79 #define pt(x,y) app_new_point((x),(y))
80
81 static
82 Rect
83 INTERNAL_CALL
84 rect(int x, int y, int width, int height)
85 {
86 Rect r;
87 r.x = x;
88 r.y = y;
89 r.width = width;
90 r.height = height;
91 return r;
92 }
93
94
95 /*
96 * app_window_fill_rect:
97 *
98 * Fill a rectangle with colour, in a window.
99 *
100 * This function implements client-side clipping, so that
101 * we never rely on the GDI system to do clipping, except if
102 * the destination is a window which is partially obscured.
103 * In that situation we must rely on the GDI system because there
104 * is no way for the program to know which portions of the
105 * window are currently obscured.
106 */
107 #define app_fill_rect( dc, r, BrushObj, Pen) \
108 IntFillRect(dc, r.x, r.y, r.width, r.height, BrushObj, Pen)
109
110 /*
111 * Drawing an ellipse with a certain line thickness.
112 * Use an inner and and outer ellipse and fill the spaces between.
113 * The inner ellipse uses all UPPERCASE letters, the outer lowercase.
114 *
115 * This algorithm is based on the fill_ellipse algorithm presented
116 * above, but uses two ellipse calculations, and some fix-up code
117 * to avoid pathological cases where the inner ellipse is almost
118 * the same size as the outer (in which case the border of the
119 * elliptical curve might otherwise have appeared broken).
120 */
121 static
122 int
123 INTERNAL_CALL
124 app_draw_ellipse(DC *g, Rect r, PBRUSH pbrush)
125 {
126 /* Outer ellipse: e(x,y) = b*b*x*x + a*a*y*y - a*a*b*b */
127
128 int a = r.width / 2;
129 int b = r.height / 2;
130 int x = 0;
131 int y = b;
132 long a2 = a*a;
133 long b2 = b*b;
134 long xcrit = (3 * a2 / 4) + 1;
135 long ycrit = (3 * b2 / 4) + 1;
136 long t = b2 + a2 - 2*a2*b; /* t = e(x+1,y-1) */
137 long dxt = b2*(3+x+x);
138 long dyt = a2*(3-y-y);
139 int d2xt = b2+b2;
140 int d2yt = a2+a2;
141
142 int w = pbrush->ptPenWidth.x;
143
144 /* Inner ellipse: E(X,Y) = B*B*X*X + A*A*Y*Y - A*A*B*B */
145
146 int A = a-w > 0 ? a-w : 0;
147 int B = b-w > 0 ? b-w : 0;
148 int X = 0;
149 int Y = B;
150 long A2 = A*A;
151 long B2 = B*B;
152 long XCRIT = (3 * A2 / 4) + 1;
153 long YCRIT = (3 * B2 / 4) + 1;
154 long T = B2 + A2 - 2*A2*B; /* T = E(X+1,Y-1) */
155 long DXT = B2*(3+X+X);
156 long DYT = A2*(3-Y-Y);
157 int D2XT = B2+B2;
158 int D2YT = A2+A2;
159
160 int movedown, moveout;
161 int innerX = 0, prevx, prevy, W;
162 Rect r1, r2;
163 int result = 1;
164
165 // START_DEBUG();
166
167 if ((r.width <= 2) || (r.height <= 2))
168 return app_fill_rect(g, r, pbrush, TRUE);
169
170 r1.x = r.x + a;
171 r1.y = r.y;
172 r1.width = r.width & 1; /* i.e. if width is odd */
173 r1.height = 1;
174
175 r2 = r1;
176 r2.y = r.y + r.height - 1;
177
178 prevx = r1.x;
179 prevy = r1.y;
180
181 while (y > 0)
182 {
183 while (Y == y)
184 {
185 innerX = X;
186
187 if (T + A2*Y < XCRIT) /* E(X+1,Y-1/2) <= 0 */
188 {
189 /* move outwards to encounter edge */
190 X += 1;
191 T += DXT;
192 DXT += D2XT;
193 }
194 else if (T - B2*X >= YCRIT) /* e(x+1/2,y-1) > 0 */
195 {
196 /* drop down one line */
197 Y -= 1;
198 T += DYT;
199 DYT += D2YT;
200 }
201 else {
202 /* drop diagonally down and out */
203 X += 1;
204 Y -= 1;
205 T += DXT + DYT;
206 DXT += D2XT;
207 DYT += D2YT;
208 }
209 }
210
211 movedown = moveout = 0;
212
213 W = x - innerX;
214 if (r1.x + W < prevx)
215 W = prevx - r1.x;
216 if (W < w)
217 W = w;
218
219 if (t + a2*y < xcrit) /* e(x+1,y-1/2) <= 0 */
220 {
221 /* move outwards to encounter edge */
222 x += 1;
223 t += dxt;
224 dxt += d2xt;
225
226 moveout = 1;
227 }
228 else if (t - b2*x >= ycrit) /* e(x+1/2,y-1) > 0 */
229 {
230 /* drop down one line */
231 y -= 1;
232 t += dyt;
233 dyt += d2yt;
234
235 movedown = 1;
236 }
237 else {
238 /* drop diagonally down and out */
239 x += 1;
240 y -= 1;
241 t += dxt + dyt;
242 dxt += d2xt;
243 dyt += d2yt;
244
245 movedown = 1;
246 moveout = 1;
247 }
248
249 if (movedown) {
250 if (r1.width == 0) {
251 r1.x -= 1; r1.width += 2;
252 r2.x -= 1; r2.width += 2;
253 moveout = 0;
254 }
255
256 if (r1.x < r.x)
257 r1.x = r2.x = r.x;
258 if (r1.width > r.width)
259 r1.width = r2.width = r.width;
260 if (r1.y == r2.y-1) {
261 r1.x = r2.x = r.x;
262 r1.width = r2.width = r.width;
263 }
264
265 if ((r1.y < r.y+w) || (r1.x+W >= r1.x+r1.width-W))
266 {
267 result &= app_fill_rect(g, r1, pbrush, TRUE);
268 result &= app_fill_rect(g, r2, pbrush, TRUE);
269
270 prevx = r1.x;
271 prevy = r1.y;
272 }
273 else if (r1.y+r1.height < r2.y)
274 {
275 /* draw distinct rectangles */
276 result &= app_fill_rect(g, rect(r1.x,r1.y,
277 W,1), pbrush, TRUE);
278 result &= app_fill_rect(g, rect(
279 r1.x+r1.width-W,r1.y,W,1), pbrush, TRUE);
280 result &= app_fill_rect(g, rect(r2.x,
281 r2.y,W,1), pbrush, TRUE);
282 result &= app_fill_rect(g, rect(
283 r2.x+r2.width-W,r2.y,W,1), pbrush, TRUE);
284
285 prevx = r1.x;
286 prevy = r1.y;
287 }
288
289 /* move down */
290 r1.y += 1;
291 r2.y -= 1;
292 }
293
294 if (moveout) {
295 /* move outwards */
296 r1.x -= 1; r1.width += 2;
297 r2.x -= 1; r2.width += 2;
298 }
299 }
300 if ((x <= a) && (prevy < r2.y)) {
301 /* draw final line */
302 r1.height = r1.y+r1.height-r2.y;
303 r1.y = r2.y;
304
305 W = w;
306 if (r.x + W != prevx)
307 W = prevx - r.x;
308 if (W < w)
309 W = w;
310
311 if (W+W >= r.width) {
312 result &= app_fill_rect(g, rect(r.x, r1.y,
313 r.width, r1.height), pbrush, TRUE);
314 return result;
315 }
316
317 result &= app_fill_rect(g, rect(r.x, r1.y, W, r1.height), pbrush, TRUE);
318 result &= app_fill_rect(g, rect(r.x+r.width-W, r1.y,
319 W, r1.height), pbrush, TRUE);
320 }
321 return result;
322 }
323
324 /*
325 * Draw an arc of an ellipse from start_angle anti-clockwise to
326 * end_angle. If the angles coincide, draw nothing; if they
327 * differ by 360 degrees or more, draw a full ellipse.
328 * The shape is drawn with the current line thickness,
329 * completely within the bounding rectangle. The shape is also
330 * axis-aligned, so that the ellipse would be horizontally and
331 * vertically symmetric is it was complete.
332 *
333 * The draw_arc algorithm is based on draw_ellipse, but unlike
334 * that algorithm is not symmetric in the general case, since
335 * an angular portion is clipped from the shape.
336 * This clipping is performed by keeping track of two hypothetical
337 * lines joining the centre point to the enclosing rectangle,
338 * at the angles start_angle and end_angle, using a line-intersection
339 * algorithm. Essentially the algorithm just fills the spaces
340 * which are within the arc and also between the angles, going
341 * in an anti-clockwise direction from start_angle to end_angle.
342 * In the top half of the ellipse, this amounts to drawing
343 * to the left of the start_angle line and to the right of
344 * the end_angle line, while in the bottom half of the ellipse,
345 * it involves drawing to the right of the start_angle and to
346 * the left of the end_angle.
347 */
348
349 /*
350 * Fill a rectangle within an arc, given the centre point p0,
351 * and the two end points of the lines corresponding to the
352 * start_angle and the end_angle. This function takes care of
353 * the logic needed to swap the fill direction below
354 * the central point, and also performs the calculations
355 * needed to intersect the current Y value with each line.
356 */
357 static
358 int
359 FASTCALL
360 app_fill_arc_rect(DC *g,
361 Rect r, // top, left, width, height
362 POINT p0, // Center
363 POINT p1, // Start
364 POINT p2, // End
365 int start_angle,
366 int end_angle,
367 PBRUSH pbrush,
368 BOOL Pen)
369 {
370 int x1, x2;
371 int start_above, end_above;
372 long rise1, run1, rise2, run2;
373
374 rise1 = p1.y - p0.y;
375 run1 = p1.x - p0.x;
376 rise2 = p2.y - p0.y;
377 run2 = p2.x - p0.x;
378
379 if (r.y <= p0.y) //
380 {
381 /* in top half of arc ellipse */
382
383 if (p1.y <= r.y)
384 {
385 /* start_line is in the top half and is */
386 /* intersected by the current Y scan line */
387 if (rise1 == 0)
388 x1 = p1.x;
389 else
390 x1 = p0.x + (r.y-p0.y)*run1/rise1;
391 start_above = 1;
392 }
393 else if ((start_angle >= 0) && (start_angle <= 180))
394 {
395 /* start_line is above middle */
396 x1 = p1.x;
397 start_above = 1;
398 }
399 else
400 {
401 /* start_line is below middle */
402 x1 = r.x + r.width;
403 start_above = 0;
404 }
405 if (x1 < r.x)
406 x1 = r.x;
407 if (x1 > r.x+r.width)
408 x1 = r.x+r.width;
409
410 if (p2.y <= r.y)
411 {
412 /* end_line is in the top half and is */
413 /* intersected by the current Y scan line */
414 if (rise2 == 0)
415 x2 = p2.x;
416 else
417 x2 = p0.x + (r.y-p0.y)*run2/rise2;
418 end_above = 1;
419 }
420 else if ((end_angle >= 0) && (end_angle <= 180))
421 {
422 /* end_line is above middle */
423 x2 = p2.x;
424 end_above = 1;
425 }
426 else
427 {
428 /* end_line is below middle */
429 x2 = r.x;
430 end_above = 0;
431 }
432
433 if (x2 < r.x) x2 = r.x;
434
435 if (x2 > r.x+r.width) x2 = r.x+r.width;
436
437 if (start_above && end_above)
438 {
439 if (start_angle > end_angle)
440 {
441 /* fill outsides of wedge */
442 if (! app_fill_rect(g, rect(r.x, r.y,
443 x1-r.x, r.height), pbrush, Pen))
444 return 0;
445 return app_fill_rect(g, rect(x2, r.y,
446 r.x+r.width-x2, r.height), pbrush, Pen);
447 }
448 else
449 {
450 /* fill inside of wedge */
451 r.width = x1-x2;
452 r.x = x2;
453 return app_fill_rect(g, r, pbrush, Pen);
454 }
455 }
456 else if (start_above)
457 {
458 /* fill to the left of the start_line */
459 r.width = x1-r.x;
460 return app_fill_rect(g, r, pbrush, Pen);
461 }
462 else if (end_above)
463 {
464 /* fill right of end_line */
465 r.width = r.x+r.width-x2;
466 r.x = x2;
467 return app_fill_rect(g, r, pbrush, Pen);
468 }
469 else
470 {
471 if (start_angle > end_angle)
472 return app_fill_rect(g,r, pbrush, Pen);
473 else
474 return 1;
475 }
476 }
477 else
478 {
479 /* in lower half of arc ellipse */
480
481 if (p1.y >= r.y)
482 {
483 /* start_line is in the lower half and is */
484 /* intersected by the current Y scan line */
485 if (rise1 == 0)
486 x1 = p1.x;
487 else
488 x1 = p0.x + (r.y-p0.y)*run1/rise1;
489 start_above = 0;
490 }
491 else if ((start_angle >= 180) && (start_angle <= 360))
492 {
493 /* start_line is below middle */
494 x1 = p1.x;
495 start_above = 0;
496 }
497 else
498 {
499 /* start_line is above middle */
500 x1 = r.x;
501 start_above = 1;
502 }
503 if (x1 < r.x)
504 x1 = r.x;
505 if (x1 > r.x+r.width)
506 x1 = r.x+r.width;
507
508 if (p2.y >= r.y)
509 {
510 /* end_line is in the lower half and is */
511 /* intersected by the current Y scan line */
512 if (rise2 == 0)
513 x2 = p2.x;
514 else
515 x2 = p0.x + (r.y-p0.y)*run2/rise2;
516 end_above = 0;
517 }
518 else if ((end_angle >= 180) && (end_angle <= 360))
519 {
520 /* end_line is below middle */
521 x2 = p2.x;
522 end_above = 0;
523 }
524 else
525 {
526 /* end_line is above middle */
527 x2 = r.x + r.width;
528 end_above = 1;
529 }
530 if (x2 < r.x)
531 x2 = r.x;
532 if (x2 > r.x+r.width)
533 x2 = r.x+r.width;
534
535 if (start_above && end_above)
536 {
537 if (start_angle > end_angle)
538 return app_fill_rect(g,r, pbrush, Pen);
539 else
540 return 1;
541 }
542 else if (start_above)
543 {
544 /* fill to the left of end_line */
545 r.width = x2-r.x;
546 return app_fill_rect(g,r, pbrush, Pen);
547 }
548 else if (end_above)
549 {
550 /* fill right of start_line */
551 r.width = r.x+r.width-x1;
552 r.x = x1;
553 return app_fill_rect(g,r, pbrush, Pen);
554 }
555 else
556 {
557 if (start_angle > end_angle)
558 {
559 /* fill outsides of wedge */
560 if (! app_fill_rect(g, rect(r.x, r.y,
561 x2-r.x, r.height), pbrush, Pen))
562 return 0;
563 return app_fill_rect(g, rect(x1, r.y,
564 r.x+r.width-x1, r.height), pbrush, Pen);
565 }
566 else
567 {
568 /* fill inside of wedge */
569 r.width = x2-x1;
570 r.x = x1;
571 return app_fill_rect(g, r, pbrush, Pen);
572 }
573 }
574 }
575 }
576
577 /*
578 * To fill an axis-aligned ellipse, we use a scan-line algorithm.
579 * We walk downwards from the top Y co-ordinate, calculating
580 * the width of the ellipse using incremental integer arithmetic.
581 * To save calculation, we observe that the top and bottom halves
582 * of the ellipsoid are mirror-images, therefore we can draw the
583 * top and bottom halves by reflection. As a result, this algorithm
584 * draws rectangles inwards from the top and bottom edges of the
585 * bounding rectangle.
586 *
587 * To save rendering time, draw as few rectangles as possible.
588 * Other ellipse-drawing algorithms assume we want to draw each
589 * line, using a draw_pixel operation, or a draw_horizontal_line
590 * operation. This approach is slower than it needs to be in
591 * circumstances where a fill_rect operation is more efficient
592 * (such as in X-Windows, where there is a communication overhead
593 * to the X-Server). For this reason, the algorithm accumulates
594 * rectangles on adjacent lines which have the same width into a
595 * single larger rectangle.
596 *
597 * This algorithm forms the basis of the later, more complex,
598 * draw_ellipse algorithm, which renders the rectangular spaces
599 * between an outer and inner ellipse, and also the draw_arc and
600 * fill_arc operations which additionally clip drawing between
601 * a start_angle and an end_angle.
602 *
603 */
604 static
605 int
606 FASTCALL
607 app_fill_ellipse(DC *g, Rect r, PBRUSH pbrush)
608 {
609 /* e(x,y) = b*b*x*x + a*a*y*y - a*a*b*b */
610
611 int a = r.width / 2;
612 int b = r.height / 2;
613 int x = 0;
614 int y = b;
615 long a2 = a*a;
616 long b2 = b*b;
617 long xcrit = (3 * a2 / 4) + 1;
618 long ycrit = (3 * b2 / 4) + 1;
619 long t = b2 + a2 - 2*a2*b; /* t = e(x+1,y-1) */
620 long dxt = b2*(3+x+x);
621 long dyt = a2*(3-y-y);
622 int d2xt = b2+b2;
623 int d2yt = a2+a2;
624 Rect r1, r2;
625 int result = 1;
626
627 // START_DEBUG();
628
629 if ((r.width <= 2) || (r.height <= 2))
630 return app_fill_rect(g, r, pbrush, FALSE);
631
632 r1.x = r.x + a;
633 r1.y = r.y;
634 r1.width = r.width & 1; /* i.e. if width is odd */
635 r1.height = 1;
636
637 r2 = r1;
638 r2.y = r.y + r.height - 1;
639
640 while (y > 0)
641 {
642 if (t + a2*y < xcrit) { /* e(x+1,y-1/2) <= 0 */
643 /* move outwards to encounter edge */
644 x += 1;
645 t += dxt;
646 dxt += d2xt;
647
648 /* move outwards */
649 r1.x -= 1; r1.width += 2;
650 r2.x -= 1; r2.width += 2;
651 }
652 else if (t - b2*x >= ycrit) { /* e(x+1/2,y-1) > 0 */
653 /* drop down one line */
654 y -= 1;
655 t += dyt;
656 dyt += d2yt;
657
658 /* enlarge rectangles */
659 r1.height += 1;
660 r2.height += 1; r2.y -= 1;
661 }
662 else {
663 /* drop diagonally down and out */
664 x += 1;
665 y -= 1;
666 t += dxt + dyt;
667 dxt += d2xt;
668 dyt += d2yt;
669
670 if ((r1.width > 0) && (r1.height > 0))
671 {
672 /* draw rectangles first */
673
674 if (r1.y+r1.height < r2.y) {
675 /* distinct rectangles */
676 result &= app_fill_rect(g, r1, pbrush, FALSE);
677 result &= app_fill_rect(g, r2, pbrush, FALSE);
678 }
679
680 /* move down */
681 r1.y += r1.height; r1.height = 1;
682 r2.y -= 1; r2.height = 1;
683 }
684 else {
685 /* skipped pixels on initial diagonal */
686
687 /* enlarge, rather than moving down */
688 r1.height += 1;
689 r2.height += 1; r2.y -= 1;
690 }
691
692 /* move outwards */
693 r1.x -= 1; r1.width += 2;
694 r2.x -= 1; r2.width += 2;
695 }
696 }
697 if (r1.y < r2.y) {
698 /* overlap */
699 r1.x = r.x;
700 r1.width = r.width;
701 r1.height = r2.y+r2.height-r1.y;
702 result &= app_fill_rect(g, r1, pbrush, FALSE);
703 }
704 else if (x <= a) {
705 /* crossover, draw final line */
706 r1.x = r.x;
707 r1.width = r.width;
708 r1.height = r1.y+r1.height-r2.y;
709 r1.y = r2.y;
710 result &= app_fill_rect(g, r1, pbrush, FALSE);
711 }
712 return result;
713 }
714
715 static
716 POINT
717 FASTCALL
718 app_boundary_point(Rect r, int angle)
719 {
720 int cx, cy;
721 double tangent;
722
723 cx = r.width;
724 cx /= 2;
725 cx += r.x;
726
727 cy = r.height;
728 cy /= 2;
729 cy += r.y;
730
731 if (angle == 0)
732 return pt(r.x+r.width, cy);
733 else if (angle == 45)
734 return pt(r.x+r.width, r.y);
735 else if (angle == 90)
736 return pt(cx, r.y);
737 else if (angle == 135)
738 return pt(r.x, r.y);
739 else if (angle == 180)
740 return pt(r.x, cy);
741 else if (angle == 225)
742 return pt(r.x, r.y+r.height);
743 else if (angle == 270)
744 return pt(cx, r.y+r.height);
745 else if (angle == 315)
746 return pt(r.x+r.width, r.y+r.height);
747
748 tangent = tan(DEGREES_TO_RADIANS(angle));
749
750 if ((angle > 45) && (angle < 135))
751 return pt((int)(cx+r.height/tangent/2), r.y);
752 else if ((angle > 225) && (angle < 315))
753 return pt((int)(cx-r.height/tangent/2), r.y+r.height);
754 else if ((angle > 135) && (angle < 225))
755 return pt(r.x, (int)(cy+r.width*tangent/2));
756 else
757 return pt(r.x+r.width, (int)(cy-r.width*tangent/2));
758 }
759
760 int
761 FASTCALL
762 app_fill_arc(DC *g, Rect r, int start_angle, int end_angle, PBRUSH pbrush, BOOL Chord)
763 {
764 /* e(x,y) = b*b*x*x + a*a*y*y - a*a*b*b */
765
766 int a = r.width / 2;
767 int b = r.height / 2;
768 int x = 0;
769 int y = b;
770 long a2 = a*a;
771 long b2 = b*b;
772 long xcrit = (3 * a2 / 4) + 1;
773 long ycrit = (3 * b2 / 4) + 1;
774 long t = b2 + a2 - 2*a2*b; /* t = e(x+1,y-1) */
775 long dxt = b2*(3+x+x);
776 long dyt = a2*(3-y-y);
777 int d2xt = b2+b2;
778 int d2yt = a2+a2;
779 Rect r1, r2;
780 int movedown, moveout;
781 int result = 1;
782
783 /* line descriptions */
784 POINT p0, p1, p2;
785
786 // START_DEBUG();
787
788 /* if angles differ by 360 degrees or more, close the shape */
789 if ((start_angle + 360 <= end_angle) ||
790 (start_angle - 360 >= end_angle))
791 {
792 return app_fill_ellipse(g, r, pbrush);
793 }
794
795 /* make start_angle >= 0 and <= 360 */
796 while (start_angle < 0)
797 start_angle += 360;
798 start_angle %= 360;
799
800 /* make end_angle >= 0 and <= 360 */
801 while (end_angle < 0)
802 end_angle += 360;
803 end_angle %= 360;
804
805 /* draw nothing if the angles are equal */
806 if (start_angle == end_angle)
807 return 1;
808
809 /* find arc wedge line end points */
810 p1 = app_boundary_point(r, start_angle);
811 p2 = app_boundary_point(r, end_angle);
812 if (Chord)
813 p0 = pt((p1.x+p2.x)/2,(p1.y+p2.y)/2);
814 else
815 p0 = pt(r.x + r.width/2, r.y + r.height/2);
816
817 /* initialise rectangles to be drawn */
818 r1.x = r.x + a;
819 r1.y = r.y;
820 r1.width = r.width & 1; /* i.e. if width is odd */
821 r1.height = 1;
822
823 r2 = r1;
824 r2.y = r.y + r.height - 1;
825
826 while (y > 0)
827 {
828 moveout = movedown = 0;
829
830 if (t + a2*y < xcrit) { /* e(x+1,y-1/2) <= 0 */
831 /* move outwards to encounter edge */
832 x += 1;
833 t += dxt;
834 dxt += d2xt;
835
836 moveout = 1;
837 }
838 else if (t - b2*x >= ycrit) { /* e(x+1/2,y-1) > 0 */
839 /* drop down one line */
840 y -= 1;
841 t += dyt;
842 dyt += d2yt;
843
844 movedown = 1;
845 }
846 else {
847 /* drop diagonally down and out */
848 x += 1;
849 y -= 1;
850 t += dxt + dyt;
851 dxt += d2xt;
852 dyt += d2yt;
853
854 moveout = 1;
855 movedown = 1;
856 }
857
858 if (movedown) {
859 if (r1.width == 0) {
860 r1.x -= 1; r1.width += 2;
861 r2.x -= 1; r2.width += 2;
862 moveout = 0;
863 }
864
865 if (r1.x < r.x)
866 r1.x = r2.x = r.x;
867 if (r1.width > r.width)
868 r1.width = r2.width = r.width;
869 if (r1.y == r2.y-1) {
870 r1.x = r2.x = r.x;
871 r1.width = r2.width = r.width;
872 }
873
874 if ((r1.width > 0) && (r1.y+r1.height < r2.y)) {
875 /* distinct rectangles */
876 result &= app_fill_arc_rect(g, r1,
877 p0, p1, p2,
878 start_angle, end_angle, pbrush, FALSE);
879 result &= app_fill_arc_rect(g, r2,
880 p0, p1, p2,
881 start_angle, end_angle, pbrush, FALSE);
882 }
883
884 /* move down */
885 r1.y += 1;
886 r2.y -= 1;
887 }
888
889 if (moveout) {
890 /* move outwards */
891 r1.x -= 1; r1.width += 2;
892 r2.x -= 1; r2.width += 2;
893 }
894 }
895 if (r1.y < r2.y) {
896 /* overlap */
897 r1.x = r.x;
898 r1.width = r.width;
899 r1.height = r2.y+r2.height-r1.y;
900 while (r1.height > 0) {
901 result &= app_fill_arc_rect(g,
902 rect(r1.x, r1.y, r1.width, 1),
903 p0, p1, p2, start_angle, end_angle, pbrush, FALSE);
904 r1.y += 1;
905 r1.height -= 1;
906 }
907 }
908 else if (x <= a) {
909 /* crossover, draw final line */
910 r1.x = r.x;
911 r1.width = r.width;
912 r1.height = r1.y+r1.height-r2.y;
913 r1.y = r2.y;
914 while (r1.height > 0) {
915 result &= app_fill_arc_rect(g,
916 rect(r1.x, r1.y, r1.width, 1),
917 p0, p1, p2, start_angle, end_angle, pbrush, FALSE);
918 r1.y += 1;
919 r1.height -= 1;
920 }
921 }
922 return result;
923 }
924
925 int app_draw_arc(DC *g, Rect r, int start_angle, int end_angle, PBRUSH pbrushPen, BOOL Chord)
926 {
927 /* Outer ellipse: e(x,y) = b*b*x*x + a*a*y*y - a*a*b*b */
928
929 int a = r.width / 2;
930 int b = r.height / 2;
931 int x = 0;
932 int y = b;
933 long a2 = a*a;
934 long b2 = b*b;
935 long xcrit = (3 * a2 / 4) + 1;
936 long ycrit = (3 * b2 / 4) + 1;
937 long t = b2 + a2 - 2*a2*b; /* t = e(x+1,y-1) */
938 long dxt = b2*(3+x+x);
939 long dyt = a2*(3-y-y);
940 int d2xt = b2+b2;
941 int d2yt = a2+a2;
942
943 int w = pbrushPen->ptPenWidth.x;
944
945 /* Inner ellipse: E(X,Y) = B*B*X*X + A*A*Y*Y - A*A*B*B */
946
947 int A = a-w > 0 ? a-w : 0;
948 int B = b-w > 0 ? b-w : 0;
949 int X = 0;
950 int Y = B;
951 long A2 = A*A;
952 long B2 = B*B;
953 long XCRIT = (3 * A2 / 4) + 1;
954 long YCRIT = (3 * B2 / 4) + 1;
955 long T = B2 + A2 - 2*A2*B; /* T = E(X+1,Y-1) */
956 long DXT = B2*(3+X+X);
957 long DYT = A2*(3-Y-Y);
958 int D2XT = B2+B2;
959 int D2YT = A2+A2;
960
961 /* arc rectangle calculations */
962 int movedown, moveout;
963 int innerX = 0, prevx, prevy, W;
964 Rect r1, r2;
965 int result = 1;
966
967 /* line descriptions */
968 POINT p0, p1, p2;
969
970 // START_DEBUG();
971
972 /* if angles differ by 360 degrees or more, close the shape */
973 if ((start_angle + 360 <= end_angle) ||
974 (start_angle - 360 >= end_angle))
975 {
976 return app_draw_ellipse(g, r, pbrushPen);
977 }
978
979 /* make start_angle >= 0 and <= 360 */
980 while (start_angle < 0)
981 start_angle += 360;
982 start_angle %= 360;
983
984 /* make end_angle >= 0 and <= 360 */
985 while (end_angle < 0)
986 end_angle += 360;
987 end_angle %= 360;
988
989 /* draw nothing if the angles are equal */
990 if (start_angle == end_angle)
991 return 1;
992
993 /* find arc wedge line end points */
994 p1 = app_boundary_point(r, start_angle);
995 p2 = app_boundary_point(r, end_angle);
996 if (Chord)
997 p0 = pt((p1.x+p2.x)/2,(p1.y+p2.y)/2);
998 else
999 p0 = pt(r.x + r.width/2, r.y + r.height/2);
1000
1001 /* determine ellipse rectangles */
1002 r1.x = r.x + a;
1003 r1.y = r.y;
1004 r1.width = r.width & 1; /* i.e. if width is odd */
1005 r1.height = 1;
1006
1007 r2 = r1;
1008 r2.y = r.y + r.height - 1;
1009
1010 prevx = r1.x;
1011 prevy = r1.y;
1012
1013 while (y > 0)
1014 {
1015 while (Y == y)
1016 {
1017 innerX = X;
1018
1019 if (T + A2*Y < XCRIT) /* E(X+1,Y-1/2) <= 0 */
1020 {
1021 /* move outwards to encounter edge */
1022 X += 1;
1023 T += DXT;
1024 DXT += D2XT;
1025 }
1026 else if (T - B2*X >= YCRIT) /* e(x+1/2,y-1) > 0 */
1027 {
1028 /* drop down one line */
1029 Y -= 1;
1030 T += DYT;
1031 DYT += D2YT;
1032 }
1033 else {
1034 /* drop diagonally down and out */
1035 X += 1;
1036 Y -= 1;
1037 T += DXT + DYT;
1038 DXT += D2XT;
1039 DYT += D2YT;
1040 }
1041 }
1042
1043 movedown = moveout = 0;
1044
1045 W = x - innerX;
1046 if (r1.x + W < prevx)
1047 W = prevx - r1.x;
1048 if (W < w)
1049 W = w;
1050
1051 if (t + a2*y < xcrit) /* e(x+1,y-1/2) <= 0 */
1052 {
1053 /* move outwards to encounter edge */
1054 x += 1;
1055 t += dxt;
1056 dxt += d2xt;
1057
1058 moveout = 1;
1059 }
1060 else if (t - b2*x >= ycrit) /* e(x+1/2,y-1) > 0 */
1061 {
1062 /* drop down one line */
1063 y -= 1;
1064 t += dyt;
1065 dyt += d2yt;
1066
1067 movedown = 1;
1068 }
1069 else {
1070 /* drop diagonally down and out */
1071 x += 1;
1072 y -= 1;
1073 t += dxt + dyt;
1074 dxt += d2xt;
1075 dyt += d2yt;
1076
1077 movedown = 1;
1078 moveout = 1;
1079 }
1080
1081 if (movedown) {
1082 if (r1.width == 0) {
1083 r1.x -= 1; r1.width += 2;
1084 r2.x -= 1; r2.width += 2;
1085 moveout = 0;
1086 }
1087
1088 if (r1.x < r.x)
1089 r1.x = r2.x = r.x;
1090 if (r1.width > r.width)
1091 r1.width = r2.width = r.width;
1092 if (r1.y == r2.y-1) {
1093 r1.x = r2.x = r.x;
1094 r1.width = r2.width = r.width;
1095 }
1096
1097 if ((r1.y < r.y+w) || (r1.x+W >= r1.x+r1.width-W))
1098 {
1099 result &= app_fill_arc_rect(g, r1,
1100 p0, p1, p2,
1101 start_angle, end_angle, pbrushPen, TRUE);
1102 result &= app_fill_arc_rect(g, r2,
1103 p0, p1, p2,
1104 start_angle, end_angle, pbrushPen, TRUE);
1105
1106 prevx = r1.x;
1107 prevy = r1.y;
1108 }
1109 else if (r1.y+r1.height < r2.y)
1110 {
1111 /* draw distinct rectangles */
1112 result &= app_fill_arc_rect(g, rect(
1113 r1.x,r1.y,W,1),
1114 p0, p1, p2,
1115 start_angle, end_angle, pbrushPen, TRUE);
1116 result &= app_fill_arc_rect(g, rect(
1117 r1.x+r1.width-W,r1.y,W,1),
1118 p0, p1, p2,
1119 start_angle, end_angle, pbrushPen, TRUE);
1120 result &= app_fill_arc_rect(g, rect(
1121 r2.x,r2.y,W,1),
1122 p0, p1, p2,
1123 start_angle, end_angle, pbrushPen, TRUE);
1124 result &= app_fill_arc_rect(g, rect(
1125 r2.x+r2.width-W,r2.y,W,1),
1126 p0, p1, p2,
1127 start_angle, end_angle, pbrushPen, TRUE);
1128
1129 prevx = r1.x;
1130 prevy = r1.y;
1131 }
1132
1133 /* move down */
1134 r1.y += 1;
1135 r2.y -= 1;
1136 }
1137
1138 if (moveout) {
1139 /* move outwards */
1140 r1.x -= 1; r1.width += 2;
1141 r2.x -= 1; r2.width += 2;
1142 }
1143 }
1144 if ((x <= a) && (prevy < r2.y)) {
1145 /* draw final lines */
1146 r1.height = r1.y+r1.height-r2.y;
1147 r1.y = r2.y;
1148
1149 W = w;
1150 if (r.x + W != prevx)
1151 W = prevx - r.x;
1152 if (W < w)
1153 W = w;
1154
1155 if (W+W >= r.width) {
1156 while (r1.height > 0) {
1157 result &= app_fill_arc_rect(g, rect(r.x,
1158 r1.y, r.width, 1), p0, p1, p2,
1159 start_angle, end_angle, pbrushPen, TRUE);
1160 r1.y += 1;
1161 r1.height -= 1;
1162 }
1163 return result;
1164 }
1165
1166 while (r1.height > 0) {
1167 result &= app_fill_arc_rect(g, rect(r.x, r1.y,
1168 W, 1), p0, p1, p2,
1169 start_angle, end_angle, pbrushPen, TRUE);
1170 result &= app_fill_arc_rect(g, rect(r.x+r.width-W,
1171 r1.y, W, 1), p0, p1, p2,
1172 start_angle, end_angle, pbrushPen, TRUE);
1173 r1.y += 1;
1174 r1.height -= 1;
1175 }
1176 }
1177
1178 return result;
1179 }
1180
1181 /* ReactOS Interface *********************************************************/
1182
1183 int
1184 FASTCALL
1185 IntFillRect( DC *dc,
1186 INT XLeft,
1187 INT YLeft,
1188 INT Width,
1189 INT Height,
1190 PBRUSH pbrush,
1191 BOOL Pen)
1192 {
1193 DWORD ROP = PATCOPY;
1194 RECTL DestRect;
1195 SURFACE *psurf;
1196 POINTL BrushOrigin;
1197 BOOL Ret = TRUE;
1198 PDC_ATTR pdcattr;
1199
1200 ASSERT(pbrush);
1201
1202 psurf = dc->dclevel.pSurface;
1203 if (psurf == NULL)
1204 {
1205 SetLastWin32Error(ERROR_INVALID_HANDLE);
1206 return 0;
1207 }
1208
1209 if (!(pbrush->flAttrs & GDIBRUSH_IS_NULL))
1210 {
1211 pdcattr = dc->pdcattr;
1212
1213 /* fix negative spaces */
1214 if (Width < 0)
1215 {
1216 XLeft += Width;
1217 Width = 0 - Width;
1218 }
1219 if (Height < 0)
1220 {
1221 YLeft += Height;
1222 Height = 0 - Height;
1223 }
1224
1225 DestRect.left = XLeft;
1226 DestRect.right = XLeft + Width;
1227
1228 DestRect.top = YLeft;
1229 DestRect.bottom = YLeft + Height;
1230
1231 BrushOrigin.x = pbrush->ptOrigin.x;
1232 BrushOrigin.y = pbrush->ptOrigin.y;
1233
1234 if (pdcattr->jROP2 == R2_XORPEN)
1235 ROP = PATINVERT;
1236 else
1237 ROP = PATCOPY;
1238
1239 Ret = IntEngBitBlt(
1240 &psurf->SurfObj,
1241 NULL,
1242 NULL,
1243 dc->rosdc.CombinedClip,
1244 NULL,
1245 &DestRect,
1246 NULL,
1247 NULL,
1248 Pen ? &dc->eboLine.BrushObject : &dc->eboFill.BrushObject,
1249 &BrushOrigin,
1250 ROP3_TO_ROP4(ROP));
1251 }
1252
1253 return (int)Ret;
1254 }
1255
1256 BOOL
1257 FASTCALL
1258 IntFillArc( PDC dc,
1259 INT XLeft,
1260 INT YLeft,
1261 INT Width,
1262 INT Height,
1263 double StartArc,
1264 double EndArc,
1265 ARCTYPE arctype)
1266 {
1267 PDC_ATTR pdcattr;
1268 PBRUSH pbrush;
1269 int Start = ceil(StartArc);
1270 int End = ceil(EndArc);
1271 BOOL Chord = (arctype == GdiTypeChord), ret;
1272
1273 pdcattr = dc->pdcattr;
1274
1275 pbrush = BRUSH_LockBrush(pdcattr->hbrush);
1276 if (!pbrush)
1277 {
1278 DPRINT1("FillArc Fail\n");
1279 SetLastWin32Error(ERROR_INTERNAL_ERROR);
1280 return FALSE;
1281 }
1282 // Sort out alignment here.
1283 ret = app_fill_arc(dc, rect( XLeft, YLeft, Width, Height),
1284 (dc->dclevel.flPath & DCPATH_CLOCKWISE) ? -End : -Start,
1285 (dc->dclevel.flPath & DCPATH_CLOCKWISE) ? -Start : -End,
1286 pbrush, Chord);
1287
1288 BRUSH_UnlockBrush(pbrush);
1289 return ret;
1290 }
1291
1292 BOOL
1293 FASTCALL
1294 IntDrawArc( PDC dc,
1295 INT XLeft,
1296 INT YLeft,
1297 INT Width,
1298 INT Height,
1299 double StartArc,
1300 double EndArc,
1301 ARCTYPE arctype,
1302 PBRUSH pbrush)
1303 {
1304 int Start = ceil(StartArc);
1305 int End = ceil(EndArc);
1306 BOOL Chord = (arctype == GdiTypeChord);
1307 // Sort out alignment here.
1308 return app_draw_arc(dc, rect( XLeft, YLeft, Width, Height),
1309 (dc->dclevel.flPath & DCPATH_CLOCKWISE) ? -End : -Start,
1310 (dc->dclevel.flPath & DCPATH_CLOCKWISE) ? -Start : -End,
1311 pbrush, Chord);
1312 }
1313
1314 BOOL
1315 FASTCALL
1316 IntDrawEllipse( PDC dc,
1317 INT XLeft,
1318 INT YLeft,
1319 INT Width,
1320 INT Height,
1321 PBRUSH pbrush)
1322 {
1323 return (BOOL)app_draw_ellipse(dc, rect( XLeft, YLeft, Width, Height), pbrush);
1324 }
1325
1326 BOOL
1327 FASTCALL
1328 IntFillEllipse( PDC dc,
1329 INT XLeft,
1330 INT YLeft,
1331 INT Width,
1332 INT Height,
1333 PBRUSH pbrush)
1334 {
1335 return (BOOL)app_fill_ellipse(dc, rect( XLeft, YLeft, Width, Height), pbrush);
1336 }
1337
1338 BOOL
1339 FASTCALL
1340 IntFillRoundRect( PDC dc,
1341 INT Left,
1342 INT Top,
1343 INT Right,
1344 INT Bottom,
1345 INT Wellipse,
1346 INT Hellipse,
1347 PBRUSH pbrush)
1348 {
1349 Rect r;
1350 int rx, ry; /* radius in x and y directions */
1351
1352 // x y Width Height
1353 r = rect( Left, Top, abs(Right-Left), abs(Bottom-Top));
1354 rx = Wellipse/2;
1355 ry = Hellipse/2;
1356
1357 if (Wellipse > r.width)
1358 {
1359 if (Hellipse > r.height) // > W > H
1360 app_fill_ellipse(dc, r, pbrush);
1361 else // > W < H
1362 {
1363 app_fill_arc(dc, rect( r.x, r.y, r.width - 1, Hellipse),
1364 0, 180, pbrush,FALSE);
1365 app_fill_arc(dc, rect(r.x, Bottom - Hellipse - 1, r.width - 1, Hellipse),
1366 180, 360, pbrush, FALSE);
1367 }
1368 }
1369 else if(Hellipse > r.height) // < W > H
1370 {
1371 app_fill_arc(dc, rect(r.x, r.y, Wellipse, r.height - 1),
1372 90, 270, pbrush, FALSE);
1373 app_fill_arc(dc, rect(Right - Wellipse - 1, r.y, Wellipse, r.height - 1),
1374 270, 90, pbrush,FALSE);
1375 }
1376 else // < W < H
1377 {
1378 app_fill_arc(dc, rect(r.x, r.y, rx+rx, ry+ry),
1379 90, 180, pbrush, FALSE);
1380
1381 app_fill_arc(dc, rect(r.x, r.y+r.height-ry-ry, rx+rx, ry+ry),
1382 180, 270, pbrush, FALSE);
1383
1384 app_fill_arc(dc, rect(r.x+r.width-rx-rx, r.y+r.height-ry-ry, rx+rx, ry+ry),
1385 270, 360, pbrush,FALSE);
1386
1387 app_fill_arc(dc, rect(r.x+r.width-rx-rx, r.y, rx+rx, ry+ry),
1388 0, 90, pbrush,FALSE);
1389 }
1390 if (Wellipse < r.width)
1391 {
1392 app_fill_rect(dc, rect(r.x+rx, r.y, r.width-rx-rx, ry+1), pbrush, FALSE);
1393 app_fill_rect(dc, rect(r.x+rx, r.y+r.height-ry+1, r.width-rx-rx, ry-1), pbrush, FALSE);
1394 }
1395 if (Hellipse < r.height)
1396 {
1397 app_fill_rect(dc, rect(r.x, r.y+ry+1, r.width, r.height-ry-ry), pbrush, FALSE);
1398 }
1399
1400 return TRUE;
1401 }
1402
1403
1404 BOOL
1405 FASTCALL
1406 IntDrawRoundRect( PDC dc,
1407 INT Left,
1408 INT Top,
1409 INT Right,
1410 INT Bottom,
1411 INT Wellipse,
1412 INT Hellipse,
1413 PBRUSH pbrushPen)
1414 {
1415 Rect r;
1416 int rx, ry; /* radius in x and y directions */
1417 int w = pbrushPen->ptPenWidth.x;
1418
1419 r = rect( Left, Top, abs(Right-Left), abs(Bottom-Top));
1420 rx = Wellipse/2;
1421 ry = Hellipse/2;
1422
1423 if (Wellipse > r.width)
1424 {
1425 if (Hellipse > r.height) // > W > H
1426 app_draw_ellipse(dc, r, pbrushPen);
1427 else // > W < H
1428 {
1429 app_draw_arc(dc, rect( r.x, r.y, r.width - 1, Hellipse - 1),
1430 0, 180, pbrushPen, FALSE);
1431 app_draw_arc(dc, rect(r.x, Bottom - Hellipse, r.width - 1, Hellipse - 1),
1432 180, 360, pbrushPen, FALSE);
1433 }
1434 }
1435 else if(Hellipse > r.height) // < W > H
1436 {
1437 app_draw_arc(dc, rect(r.x, r.y, Wellipse - 1, r.height - 1),
1438 90, 270, pbrushPen, FALSE);
1439 app_draw_arc(dc, rect(Right - Wellipse, r.y, Wellipse - 1, r.height - 1),
1440 270, 90, pbrushPen, FALSE);
1441 }
1442 else // < W < H
1443 {
1444 app_draw_arc(dc, rect(r.x, r.y, rx+rx, ry+ry),
1445 90, 180, pbrushPen, FALSE);
1446
1447 app_draw_arc(dc, rect(r.x,r.y+r.height-ry-ry,rx+rx,ry+ry),
1448 180, 270, pbrushPen, FALSE);
1449
1450 app_draw_arc(dc, rect(r.x+r.width-rx-rx, r.y+r.height-ry-ry, rx+rx, ry+ry),
1451 270, 360, pbrushPen, FALSE);
1452
1453 app_draw_arc(dc, rect(r.x+r.width-rx-rx,r.y,rx+rx,ry+ry),
1454 0, 90, pbrushPen, FALSE);
1455 }
1456 if ( Hellipse < r.height)
1457 {
1458 app_fill_rect(dc, rect(r.x, r.y+ry+1, w, r.height-ry-ry), pbrushPen, TRUE);
1459
1460
1461 app_fill_rect(dc, rect(r.x+r.width-w, r.y+ry+1, w, r.height-ry-ry),
1462 pbrushPen, TRUE);
1463 }
1464 if ( Wellipse < r.width)
1465 {
1466 app_fill_rect(dc, rect(r.x+rx, r.y+r.height-w, r.width-rx-rx, w),
1467 pbrushPen, TRUE);
1468
1469 app_fill_rect(dc, rect(r.x+rx, r.y, r.width-rx-rx, w), pbrushPen, TRUE);
1470 }
1471 return TRUE;
1472 }
1473