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