ee79eb236616ce2399453cee4c326169b719f513
[reactos.git] / reactos / win32ss / gdi / ntgdi / region.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 /*
21 * GDI region objects. Shamelessly ripped out from the X11 distribution
22 * Thanks for the nice licence.
23 *
24 * Copyright 1993, 1994, 1995 Alexandre Julliard
25 * Modifications and additions: Copyright 1998 Huw Davies
26 * 1999 Alex Korobka
27 *
28 * This library is free software; you can redistribute it and/or
29 * modify it under the terms of the GNU Lesser General Public
30 * License as published by the Free Software Foundation; either
31 * version 2.1 of the License, or (at your option) any later version.
32 *
33 * This library is distributed in the hope that it will be useful,
34 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
36 * Lesser General Public License for more details.
37 *
38 * You should have received a copy of the GNU Lesser General Public
39 * License along with this library; if not, write to the Free Software
40 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
41 */
42
43 /************************************************************************
44
45 Copyright (c) 1987, 1988 X Consortium
46
47 Permission is hereby granted, free of charge, to any person obtaining a copy
48 of this software and associated documentation files (the "Software"), to deal
49 in the Software without restriction, including without limitation the rights
50 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
51 copies of the Software, and to permit persons to whom the Software is
52 furnished to do so, subject to the following conditions:
53
54 The above copyright notice and this permission notice shall be included in
55 all copies or substantial portions of the Software.
56
57 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
58 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
59 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
60 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
61 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
62 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
63
64 Except as contained in this notice, the name of the X Consortium shall not be
65 used in advertising or otherwise to promote the sale, use or other dealings
66 in this Software without prior written authorization from the X Consortium.
67
68
69 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
70
71 All Rights Reserved
72
73 Permission to use, copy, modify, and distribute this software and its
74 documentation for any purpose and without fee is hereby granted,
75 provided that the above copyright notice appear in all copies and that
76 both that copyright notice and this permission notice appear in
77 supporting documentation, and that the name of Digital not be
78 used in advertising or publicity pertaining to distribution of the
79 software without specific, written prior permission.
80
81 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
82 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
83 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
84 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
85 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
86 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
87 SOFTWARE.
88
89 ************************************************************************/
90 /*
91 * The functions in this file implement the Region abstraction, similar to one
92 * used in the X11 sample server. A Region is simply an area, as the name
93 * implies, and is implemented as a "y-x-banded" array of rectangles. To
94 * explain: Each Region is made up of a certain number of rectangles sorted
95 * by y coordinate first, and then by x coordinate.
96 *
97 * Furthermore, the rectangles are banded such that every rectangle with a
98 * given upper-left y coordinate (y1) will have the same lower-right y
99 * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
100 * will span the entire vertical distance of the band. This means that some
101 * areas that could be merged into a taller rectangle will be represented as
102 * several shorter rectangles to account for shorter rectangles to its left
103 * or right but within its "vertical scope".
104 *
105 * An added constraint on the rectangles is that they must cover as much
106 * horizontal area as possible. E.g. no two rectangles in a band are allowed
107 * to touch.
108 *
109 * Whenever possible, bands will be merged together to cover a greater vertical
110 * distance (and thus reduce the number of rectangles). Two bands can be merged
111 * only if the bottom of one touches the top of the other and they have
112 * rectangles in the same places (of the same width, of course). This maintains
113 * the y-x-banding that's so nice to have...
114 */
115
116 #include <win32k.h>
117 #include <suppress.h>
118
119 #define NDEBUG
120 #include <debug.h>
121
122 PREGION prgnDefault = NULL;
123 HRGN hrgnDefault = NULL;
124
125 // Internal Functions
126
127 #if 1
128 #define COPY_RECTS(dest, src, nRects) \
129 do { \
130 PRECTL xDest = (dest); \
131 PRECTL xSrc = (src); \
132 UINT xRects = (nRects); \
133 while (xRects-- > 0) { \
134 *(xDest++) = *(xSrc++); \
135 } \
136 } while (0)
137 #else
138 #define COPY_RECTS(dest, src, nRects) RtlCopyMemory(dest, src, (nRects) * sizeof(RECTL))
139 #endif
140
141 #define EMPTY_REGION(pReg) { \
142 (pReg)->rdh.nCount = 0; \
143 (pReg)->rdh.rcBound.left = (pReg)->rdh.rcBound.top = 0; \
144 (pReg)->rdh.rcBound.right = (pReg)->rdh.rcBound.bottom = 0; \
145 (pReg)->rdh.iType = RDH_RECTANGLES; \
146 }
147
148 #define REGION_NOT_EMPTY(pReg) pReg->rdh.nCount
149
150 #define INRECT(r, x, y) \
151 ( ( ((r).right > x)) && \
152 ( ((r).left <= x)) && \
153 ( ((r).bottom > y)) && \
154 ( ((r).top <= y)) )
155
156 /* 1 if two RECTs overlap.
157 * 0 if two RECTs do not overlap.
158 */
159 #define EXTENTCHECK(r1, r2) \
160 ((r1)->right > (r2)->left && \
161 (r1)->left < (r2)->right && \
162 (r1)->bottom > (r2)->top && \
163 (r1)->top < (r2)->bottom)
164
165 /*
166 * In scan converting polygons, we want to choose those pixels
167 * which are inside the polygon. Thus, we add .5 to the starting
168 * x coordinate for both left and right edges. Now we choose the
169 * first pixel which is inside the pgon for the left edge and the
170 * first pixel which is outside the pgon for the right edge.
171 * Draw the left pixel, but not the right.
172 *
173 * How to add .5 to the starting x coordinate:
174 * If the edge is moving to the right, then subtract dy from the
175 * error term from the general form of the algorithm.
176 * If the edge is moving to the left, then add dy to the error term.
177 *
178 * The reason for the difference between edges moving to the left
179 * and edges moving to the right is simple: If an edge is moving
180 * to the right, then we want the algorithm to flip immediately.
181 * If it is moving to the left, then we don't want it to flip until
182 * we traverse an entire pixel.
183 */
184 #define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \
185 int dx; /* Local storage */ \
186 \
187 /* \
188 * If the edge is horizontal, then it is ignored \
189 * and assumed not to be processed. Otherwise, do this stuff. \
190 */ \
191 if ((dy) != 0) { \
192 xStart = (x1); \
193 dx = (x2) - xStart; \
194 if (dx < 0) { \
195 m = dx / (dy); \
196 m1 = m - 1; \
197 incr1 = -2 * dx + 2 * (dy) * m1; \
198 incr2 = -2 * dx + 2 * (dy) * m; \
199 d = 2 * m * (dy) - 2 * dx - 2 * (dy); \
200 } else { \
201 m = dx / (dy); \
202 m1 = m + 1; \
203 incr1 = 2 * dx - 2 * (dy) * m1; \
204 incr2 = 2 * dx - 2 * (dy) * m; \
205 d = -2 * m * (dy) + 2 * dx; \
206 } \
207 } \
208 }
209
210 #define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \
211 if (m1 > 0) { \
212 if (d > 0) { \
213 minval += m1; \
214 d += incr1; \
215 } \
216 else { \
217 minval += m; \
218 d += incr2; \
219 } \
220 } else {\
221 if (d >= 0) { \
222 minval += m1; \
223 d += incr1; \
224 } \
225 else { \
226 minval += m; \
227 d += incr2; \
228 } \
229 } \
230 }
231
232 /*
233 * This structure contains all of the information needed
234 * to run the bresenham algorithm.
235 * The variables may be hardcoded into the declarations
236 * instead of using this structure to make use of
237 * register declarations.
238 */
239 typedef struct
240 {
241 INT minor_axis; /* Minor axis */
242 INT d; /* Decision variable */
243 INT m, m1; /* Slope and slope+1 */
244 INT incr1, incr2; /* Error increments */
245 } BRESINFO;
246
247
248 #define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \
249 BRESINITPGON(dmaj, min1, min2, bres.minor_axis, bres.d, \
250 bres.m, bres.m1, bres.incr1, bres.incr2)
251
252 #define BRESINCRPGONSTRUCT(bres) \
253 BRESINCRPGON(bres.d, bres.minor_axis, bres.m, bres.m1, bres.incr1, bres.incr2)
254
255
256
257 /*
258 * These are the data structures needed to scan
259 * convert regions. Two different scan conversion
260 * methods are available -- the even-odd method, and
261 * the winding number method.
262 * The even-odd rule states that a point is inside
263 * the polygon if a ray drawn from that point in any
264 * direction will pass through an odd number of
265 * path segments.
266 * By the winding number rule, a point is decided
267 * to be inside the polygon if a ray drawn from that
268 * point in any direction passes through a different
269 * number of clockwise and counter-clockwise path
270 * segments.
271 *
272 * These data structures are adapted somewhat from
273 * the algorithm in (Foley/Van Dam) for scan converting
274 * polygons.
275 * The basic algorithm is to start at the top (smallest y)
276 * of the polygon, stepping down to the bottom of
277 * the polygon by incrementing the y coordinate. We
278 * keep a list of edges which the current scanline crosses,
279 * sorted by x. This list is called the Active Edge Table (AET)
280 * As we change the y-coordinate, we update each entry in
281 * in the active edge table to reflect the edges new xcoord.
282 * This list must be sorted at each scanline in case
283 * two edges intersect.
284 * We also keep a data structure known as the Edge Table (ET),
285 * which keeps track of all the edges which the current
286 * scanline has not yet reached. The ET is basically a
287 * list of SCANLINE_LIST structures containing a list of
288 * edges which are entered at a given scanline. There is one
289 * SCANLINE_LIST per scanline at which an edge is entered.
290 * When we enter a new edge, we move it from the ET to the AET.
291 *
292 * From the AET, we can implement the even-odd rule as in
293 * (Foley/Van Dam).
294 * The winding number rule is a little trickier. We also
295 * keep the EDGE_TABLEEntries in the AET linked by the
296 * nextWETE (winding EDGE_TABLE_ENTRY) link. This allows
297 * the edges to be linked just as before for updating
298 * purposes, but only uses the edges linked by the nextWETE
299 * link as edges representing spans of the polygon to
300 * drawn (as with the even-odd rule).
301 */
302
303 /*
304 * For the winding number rule
305 */
306 #define CLOCKWISE 1
307 #define COUNTERCLOCKWISE -1
308
309 typedef struct _EDGE_TABLE_ENTRY
310 {
311 INT ymax; /* ycoord at which we exit this edge. */
312 BRESINFO bres; /* Bresenham info to run the edge */
313 struct _EDGE_TABLE_ENTRY *next; /* Next in the list */
314 struct _EDGE_TABLE_ENTRY *back; /* For insertion sort */
315 struct _EDGE_TABLE_ENTRY *nextWETE; /* For winding num rule */
316 INT ClockWise; /* Flag for winding number rule */
317 } EDGE_TABLE_ENTRY;
318
319
320 typedef struct _SCANLINE_LIST
321 {
322 INT scanline; /* The scanline represented */
323 EDGE_TABLE_ENTRY *edgelist; /* Header node */
324 struct _SCANLINE_LIST *next; /* Next in the list */
325 } SCANLINE_LIST;
326
327
328 typedef struct
329 {
330 INT ymax; /* ymax for the polygon */
331 INT ymin; /* ymin for the polygon */
332 SCANLINE_LIST scanlines; /* Header node */
333 } EDGE_TABLE;
334
335
336 /*
337 * Here is a struct to help with storage allocation
338 * so we can allocate a big chunk at a time, and then take
339 * pieces from this heap when we need to.
340 */
341 #define SLLSPERBLOCK 25
342
343 typedef struct _SCANLINE_LISTBLOCK
344 {
345 SCANLINE_LIST SLLs[SLLSPERBLOCK];
346 struct _SCANLINE_LISTBLOCK *next;
347 } SCANLINE_LISTBLOCK;
348
349
350 /*
351 * A few macros for the inner loops of the fill code where
352 * performance considerations don't allow a procedure call.
353 *
354 * Evaluate the given edge at the given scanline.
355 * If the edge has expired, then we leave it and fix up
356 * the active edge table; otherwise, we increment the
357 * x value to be ready for the next scanline.
358 * The winding number rule is in effect, so we must notify
359 * the caller when the edge has been removed so he
360 * can reorder the Winding Active Edge Table.
361 */
362 #define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \
363 if (pAET->ymax == y) { /* Leaving this edge */ \
364 pPrevAET->next = pAET->next; \
365 pAET = pPrevAET->next; \
366 fixWAET = 1; \
367 if (pAET) \
368 pAET->back = pPrevAET; \
369 } \
370 else { \
371 BRESINCRPGONSTRUCT(pAET->bres); \
372 pPrevAET = pAET; \
373 pAET = pAET->next; \
374 } \
375 }
376
377
378 /*
379 * Evaluate the given edge at the given scanline.
380 * If the edge has expired, then we leave it and fix up
381 * the active edge table; otherwise, we increment the
382 * x value to be ready for the next scanline.
383 * The even-odd rule is in effect.
384 */
385 #define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \
386 if (pAET->ymax == y) { /* Leaving this edge */ \
387 pPrevAET->next = pAET->next; \
388 pAET = pPrevAET->next; \
389 if (pAET) \
390 pAET->back = pPrevAET; \
391 } \
392 else { \
393 BRESINCRPGONSTRUCT(pAET->bres); \
394 pPrevAET = pAET; \
395 pAET = pAET->next; \
396 } \
397 }
398
399 /**************************************************************************
400 *
401 * Poly Regions
402 *
403 *************************************************************************/
404
405 #define LARGE_COORDINATE 0x7fffffff /* FIXME */
406 #define SMALL_COORDINATE 0x80000000
407
408 static
409 BOOL
410 REGION_bGrowBufferSize(
411 _Inout_ PREGION prgn,
412 _In_ UINT cRects)
413 {
414 ULONG cjNewSize;
415 PVOID pvBuffer;
416 NT_ASSERT(cRects > 0);
417
418 /* Make sure we don't overflow */
419 if (cRects > MAXULONG / sizeof(RECTL))
420 {
421 return FALSE;
422 }
423
424 /* Calculate new buffer size */
425 cjNewSize = cRects * sizeof(RECTL);
426
427 /* Avoid allocating too often, by duplicating the old buffer size
428 Note: we don't do an overflow check, since the old size will never
429 get that large before running out of memory. */
430 if (2 * prgn->rdh.nRgnSize > cjNewSize)
431 {
432 cjNewSize = 2 * prgn->rdh.nRgnSize;
433 }
434
435 /* Allocate the new buffer */
436 pvBuffer = ExAllocatePoolWithTag(PagedPool, cjNewSize, TAG_REGION);
437 if (pvBuffer == NULL)
438 {
439 return FALSE;
440 }
441
442 /* Copy the rects into the new buffer */
443 COPY_RECTS(pvBuffer, prgn->Buffer, prgn->rdh.nCount);
444
445 /* Free the old buffer */
446 if (prgn->Buffer != &prgn->rdh.rcBound)
447 {
448 ExFreePoolWithTag(prgn->Buffer, TAG_REGION);
449 }
450
451 /* Set the new buffer */
452 prgn->Buffer = pvBuffer;
453 prgn->rdh.nRgnSize = cjNewSize;
454
455 return TRUE;
456 }
457
458 static __inline
459 BOOL
460 REGION_bEnsureBufferSize(
461 _Inout_ PREGION prgn,
462 _In_ UINT cRects)
463 {
464 /* Check if the current region size is too small */
465 if (cRects > prgn->rdh.nRgnSize / sizeof(RECTL))
466 {
467 /* Allocate a new buffer */
468 return REGION_bGrowBufferSize(prgn, cRects);
469 }
470
471 return TRUE;
472 }
473
474 FORCEINLINE
475 VOID
476 REGION_vAddRect(
477 _Inout_ PREGION prgn,
478 _In_ LONG left,
479 _In_ LONG top,
480 _In_ LONG right,
481 _In_ LONG bottom)
482 {
483 PRECTL prcl;
484 NT_ASSERT((prgn->rdh.nCount + 1) * sizeof(RECT) <= prgn->rdh.nRgnSize);
485
486 prcl = &prgn->Buffer[prgn->rdh.nCount];
487 prcl->left = left;
488 prcl->top = top;
489 prcl->right = right;
490 prcl->bottom = bottom;
491 prgn->rdh.nCount++;
492 }
493
494 static __inline
495 BOOL
496 REGION_bAddRect(
497 _Inout_ PREGION prgn,
498 _In_ LONG left,
499 _In_ LONG top,
500 _In_ LONG right,
501 _In_ LONG bottom)
502 {
503 if (!REGION_bEnsureBufferSize(prgn, prgn->rdh.nCount + 1))
504 {
505 return FALSE;
506 }
507
508 REGION_vAddRect(prgn, left, top, right, bottom);
509 return TRUE;
510 }
511
512 typedef VOID (FASTCALL *overlapProcp)(PREGION, PRECT, PRECT, PRECT, PRECT, INT, INT);
513 typedef VOID (FASTCALL *nonOverlapProcp)(PREGION, PRECT, PRECT, INT, INT);
514
515 // Number of points to buffer before sending them off to scanlines() : Must be an even number
516 #define NUMPTSTOBUFFER 200
517
518 #define RGN_DEFAULT_RECTS 2
519
520 // Used to allocate buffers for points and link the buffers together
521 typedef struct _POINTBLOCK
522 {
523 POINT pts[NUMPTSTOBUFFER];
524 struct _POINTBLOCK *next;
525 } POINTBLOCK;
526
527 #ifndef NDEBUG
528 /*
529 * This function is left there for debugging purposes.
530 */
531 VOID
532 FASTCALL
533 IntDumpRegion(HRGN hRgn)
534 {
535 PREGION Data;
536
537 Data = REGION_LockRgn(hRgn);
538 if (Data == NULL)
539 {
540 DbgPrint("IntDumpRegion called with invalid region!\n");
541 return;
542 }
543
544 DbgPrint("IntDumpRegion(%x): %d,%d-%d,%d %d\n",
545 hRgn,
546 Data->rdh.rcBound.left,
547 Data->rdh.rcBound.top,
548 Data->rdh.rcBound.right,
549 Data->rdh.rcBound.bottom,
550 Data->rdh.iType);
551
552 REGION_UnlockRgn(Data);
553 }
554 #endif /* Not NDEBUG */
555
556
557 INT
558 FASTCALL
559 REGION_Complexity(PREGION prgn)
560 {
561 if (prgn == NULL)
562 return NULLREGION;
563
564 DPRINT("Region Complexity -> %lu", prgn->rdh.nCount);
565 switch (prgn->rdh.nCount)
566 {
567 case 0:
568 return NULLREGION;
569 case 1:
570 return SIMPLEREGION;
571 default:
572 return COMPLEXREGION;
573 }
574 }
575
576 static
577 BOOL
578 FASTCALL
579 REGION_CopyRegion(
580 PREGION dst,
581 PREGION src)
582 {
583 /* Only copy if source and dest are not equal */
584 if (dst != src)
585 {
586 /* Check if we need to increase our buffer */
587 if (dst->rdh.nRgnSize < src->rdh.nCount * sizeof(RECT))
588 {
589 PRECTL temp;
590
591 /* Allocate a new buffer */
592 temp = ExAllocatePoolWithTag(PagedPool,
593 src->rdh.nCount * sizeof(RECT),
594 TAG_REGION);
595 if (temp == NULL)
596 return FALSE;
597
598 /* Free the old buffer */
599 if ((dst->Buffer != NULL) && (dst->Buffer != &dst->rdh.rcBound))
600 ExFreePoolWithTag(dst->Buffer, TAG_REGION);
601
602 /* Set the new buffer and the size */
603 dst->Buffer = temp;
604 dst->rdh.nRgnSize = src->rdh.nCount * sizeof(RECT);
605 }
606
607 dst->rdh.nCount = src->rdh.nCount;
608 dst->rdh.rcBound.left = src->rdh.rcBound.left;
609 dst->rdh.rcBound.top = src->rdh.rcBound.top;
610 dst->rdh.rcBound.right = src->rdh.rcBound.right;
611 dst->rdh.rcBound.bottom = src->rdh.rcBound.bottom;
612 dst->rdh.iType = src->rdh.iType;
613 COPY_RECTS(dst->Buffer, src->Buffer, src->rdh.nCount);
614 }
615
616 return TRUE;
617 }
618
619 static
620 VOID
621 FASTCALL
622 REGION_SetExtents(
623 PREGION pReg)
624 {
625 RECTL *pRect, *pRectEnd, *pExtents;
626
627 /* Quick check for NULLREGION */
628 if (pReg->rdh.nCount == 0)
629 {
630 pReg->rdh.rcBound.left = 0;
631 pReg->rdh.rcBound.top = 0;
632 pReg->rdh.rcBound.right = 0;
633 pReg->rdh.rcBound.bottom = 0;
634 pReg->rdh.iType = RDH_RECTANGLES;
635 return;
636 }
637
638 pExtents = &pReg->rdh.rcBound;
639 pRect = pReg->Buffer;
640 pRectEnd = pReg->Buffer + pReg->rdh.nCount - 1;
641
642 /* Since pRect is the first rectangle in the region, it must have the
643 * smallest top and since pRectEnd is the last rectangle in the region,
644 * it must have the largest bottom, because of banding. Initialize left and
645 * right from pRect and pRectEnd, resp., as good things to initialize them
646 * to... */
647 pExtents->left = pRect->left;
648 pExtents->top = pRect->top;
649 pExtents->right = pRectEnd->right;
650 pExtents->bottom = pRectEnd->bottom;
651
652 while (pRect <= pRectEnd)
653 {
654 if (pRect->left < pExtents->left)
655 pExtents->left = pRect->left;
656 if (pRect->right > pExtents->right)
657 pExtents->right = pRect->right;
658 pRect++;
659 }
660
661 pReg->rdh.iType = RDH_RECTANGLES;
662 }
663
664 // FIXME: This function needs review and testing
665 /***********************************************************************
666 * REGION_CropRegion
667 */
668 INT
669 FASTCALL
670 REGION_CropRegion(
671 PREGION rgnDst,
672 PREGION rgnSrc,
673 const RECTL *rect)
674 {
675 PRECTL lpr, rpr;
676 ULONG i, j, clipa, clipb, nRgnSize;
677 INT left = MAXLONG;
678 INT right = MINLONG;
679 INT top = MAXLONG;
680 INT bottom = MINLONG;
681
682 if ((rect->left >= rect->right) ||
683 (rect->top >= rect->bottom) ||
684 (EXTENTCHECK(rect, &rgnSrc->rdh.rcBound) == 0))
685 {
686 goto empty;
687 }
688
689 /* Skip all rects that are completely above our intersect rect */
690 for (clipa = 0; clipa < rgnSrc->rdh.nCount; clipa++)
691 {
692 /* bottom is exclusive, so break when we go above it */
693 if (rgnSrc->Buffer[clipa].bottom > rect->top) break;
694 }
695
696 /* Bail out, if there is nothing left */
697 if (clipa == rgnSrc->rdh.nCount) goto empty;
698
699 /* Find the last rect that is still within the intersect rect (exclusive) */
700 for (clipb = clipa; clipb < rgnSrc->rdh.nCount; clipb++)
701 {
702 /* bottom is exclusive, so stop, when we start at that y pos */
703 if (rgnSrc->Buffer[clipb].top >= rect->bottom) break;
704 }
705
706 /* Bail out, if there is nothing left */
707 if (clipb == clipa) goto empty;
708
709 // clipa - index of the first rect in the first intersecting band
710 // clipb - index of the last rect in the last intersecting band plus 1
711
712 /* Check if the buffer in the dest region is large enough,
713 otherwise allocate a new one */
714 nRgnSize = (clipb - clipa) * sizeof(RECT);
715 if ((rgnDst != rgnSrc) && (rgnDst->rdh.nRgnSize < nRgnSize))
716 {
717 PRECTL temp;
718 temp = ExAllocatePoolWithTag(PagedPool, nRgnSize, TAG_REGION);
719 if (temp == NULL)
720 return ERROR;
721
722 /* Free the old buffer */
723 if (rgnDst->Buffer && (rgnDst->Buffer != &rgnDst->rdh.rcBound))
724 ExFreePoolWithTag(rgnDst->Buffer, TAG_REGION);
725
726 rgnDst->Buffer = temp;
727 rgnDst->rdh.nCount = 0;
728 rgnDst->rdh.nRgnSize = nRgnSize;
729 rgnDst->rdh.iType = RDH_RECTANGLES;
730 }
731
732 /* Loop all rects within the intersect rect from the y perspective */
733 for (i = clipa, j = 0; i < clipb ; i++)
734 {
735 /* i - src index, j - dst index, j is always <= i for obvious reasons */
736
737 lpr = &rgnSrc->Buffer[i];
738
739 /* Make sure the source rect is not retarded */
740 ASSERT(lpr->bottom > lpr->top);
741 ASSERT(lpr->right > lpr->left);
742
743 /* We already checked above, this should hold true */
744 ASSERT(lpr->bottom > rect->top);
745 ASSERT(lpr->top < rect->bottom);
746
747 /* Check if this rect is really inside the intersect rect */
748 if ((lpr->left < rect->right) && (lpr->right > rect->left))
749 {
750 rpr = &rgnDst->Buffer[j];
751
752 /* Crop the rect with the intersect rect */
753 rpr->top = max(lpr->top, rect->top);
754 rpr->bottom = min(lpr->bottom, rect->bottom);
755 rpr->left = max(lpr->left, rect->left);
756 rpr->right = min(lpr->right, rect->right);
757
758 /* Make sure the resulting rect is not retarded */
759 ASSERT(rpr->bottom > rpr->top);
760 ASSERT(rpr->right > rpr->left);
761
762 /* Track new bounds */
763 if (rpr->left < left) left = rpr->left;
764 if (rpr->right > right) right = rpr->right;
765 if (rpr->top < top) top = rpr->top;
766 if (rpr->bottom > bottom) bottom = rpr->bottom;
767
768 /* Next target rect */
769 j++;
770 }
771 }
772
773 if (j == 0) goto empty;
774
775 /* Update the bounds rect */
776 rgnDst->rdh.rcBound.left = left;
777 rgnDst->rdh.rcBound.right = right;
778 rgnDst->rdh.rcBound.top = top;
779 rgnDst->rdh.rcBound.bottom = bottom;
780
781 /* Set new rect count */
782 rgnDst->rdh.nCount = j;
783
784 return REGION_Complexity(rgnDst);
785
786 empty:
787 if (rgnDst->Buffer == NULL)
788 {
789 rgnDst->Buffer = &rgnDst->rdh.rcBound;
790 }
791
792 EMPTY_REGION(rgnDst);
793 return NULLREGION;
794 }
795
796
797 /*!
798 * Attempt to merge the rects in the current band with those in the
799 * previous one. Used only by REGION_RegionOp.
800 *
801 * Results:
802 * The new index for the previous band.
803 *
804 * \note Side Effects:
805 * If coalescing takes place:
806 * - rectangles in the previous band will have their bottom fields
807 * altered.
808 * - pReg->numRects will be decreased.
809 *
810 */
811 static
812 INT
813 FASTCALL
814 REGION_Coalesce(
815 PREGION pReg, /* Region to coalesce */
816 INT prevStart, /* Index of start of previous band */
817 INT curStart) /* Index of start of current band */
818 {
819 RECTL *pPrevRect; /* Current rect in previous band */
820 RECTL *pCurRect; /* Current rect in current band */
821 RECTL *pRegEnd; /* End of region */
822 INT curNumRects; /* Number of rectangles in current band */
823 INT prevNumRects; /* Number of rectangles in previous band */
824 INT bandtop; /* Top coordinate for current band */
825
826 pRegEnd = pReg->Buffer + pReg->rdh.nCount;
827 pPrevRect = pReg->Buffer + prevStart;
828 prevNumRects = curStart - prevStart;
829
830 /* Figure out how many rectangles are in the current band. Have to do
831 * this because multiple bands could have been added in REGION_RegionOp
832 * at the end when one region has been exhausted. */
833 pCurRect = pReg->Buffer + curStart;
834 bandtop = pCurRect->top;
835 for (curNumRects = 0;
836 (pCurRect != pRegEnd) && (pCurRect->top == bandtop);
837 curNumRects++)
838 {
839 pCurRect++;
840 }
841
842 if (pCurRect != pRegEnd)
843 {
844 /* If more than one band was added, we have to find the start
845 * of the last band added so the next coalescing job can start
846 * at the right place... (given when multiple bands are added,
847 * this may be pointless -- see above). */
848 pRegEnd--;
849 while ((pRegEnd-1)->top == pRegEnd->top)
850 {
851 pRegEnd--;
852 }
853
854 curStart = pRegEnd - pReg->Buffer;
855 pRegEnd = pReg->Buffer + pReg->rdh.nCount;
856 }
857
858 if ((curNumRects == prevNumRects) && (curNumRects != 0))
859 {
860 pCurRect -= curNumRects;
861
862 /* The bands may only be coalesced if the bottom of the previous
863 * matches the top scanline of the current. */
864 if (pPrevRect->bottom == pCurRect->top)
865 {
866 /* Make sure the bands have rects in the same places. This
867 * assumes that rects have been added in such a way that they
868 * cover the most area possible. I.e. two rects in a band must
869 * have some horizontal space between them. */
870 do
871 {
872 if ((pPrevRect->left != pCurRect->left) ||
873 (pPrevRect->right != pCurRect->right))
874 {
875 /* The bands don't line up so they can't be coalesced. */
876 return (curStart);
877 }
878
879 pPrevRect++;
880 pCurRect++;
881 prevNumRects -= 1;
882 }
883 while (prevNumRects != 0);
884
885 pReg->rdh.nCount -= curNumRects;
886 pCurRect -= curNumRects;
887 pPrevRect -= curNumRects;
888
889 /* The bands may be merged, so set the bottom of each rect
890 * in the previous band to that of the corresponding rect in
891 * the current band. */
892 do
893 {
894 pPrevRect->bottom = pCurRect->bottom;
895 pPrevRect++;
896 pCurRect++;
897 curNumRects -= 1;
898 }
899 while (curNumRects != 0);
900
901 /* If only one band was added to the region, we have to backup
902 * curStart to the start of the previous band.
903 *
904 * If more than one band was added to the region, copy the
905 * other bands down. The assumption here is that the other bands
906 * came from the same region as the current one and no further
907 * coalescing can be done on them since it's all been done
908 * already... curStart is already in the right place. */
909 if (pCurRect == pRegEnd)
910 {
911 curStart = prevStart;
912 }
913 else
914 {
915 do
916 {
917 *pPrevRect++ = *pCurRect++;
918 }
919 while (pCurRect != pRegEnd);
920 }
921 }
922 }
923
924 return (curStart);
925 }
926
927 /*!
928 * Apply an operation to two regions. Called by REGION_Union,
929 * REGION_Inverse, REGION_Subtract, REGION_Intersect...
930 *
931 * Results:
932 * None.
933 *
934 * Side Effects:
935 * The new region is overwritten.
936 *
937 *\note The idea behind this function is to view the two regions as sets.
938 * Together they cover a rectangle of area that this function divides
939 * into horizontal bands where points are covered only by one region
940 * or by both. For the first case, the nonOverlapFunc is called with
941 * each the band and the band's upper and lower extents. For the
942 * second, the overlapFunc is called to process the entire band. It
943 * is responsible for clipping the rectangles in the band, though
944 * this function provides the boundaries.
945 * At the end of each band, the new region is coalesced, if possible,
946 * to reduce the number of rectangles in the region.
947 *
948 */
949 static
950 VOID
951 FASTCALL
952 REGION_RegionOp(
953 PREGION newReg, /* Place to store result */
954 PREGION reg1, /* First region in operation */
955 PREGION reg2, /* 2nd region in operation */
956 overlapProcp overlapFunc, /* Function to call for over-lapping bands */
957 nonOverlapProcp nonOverlap1Func, /* Function to call for non-overlapping bands in region 1 */
958 nonOverlapProcp nonOverlap2Func) /* Function to call for non-overlapping bands in region 2 */
959 {
960 RECTL *r1; /* Pointer into first region */
961 RECTL *r2; /* Pointer into 2d region */
962 RECTL *r1End; /* End of 1st region */
963 RECTL *r2End; /* End of 2d region */
964 INT ybot; /* Bottom of intersection */
965 INT ytop; /* Top of intersection */
966 RECTL *oldRects; /* Old rects for newReg */
967 ULONG prevBand; /* Index of start of
968 * Previous band in newReg */
969 ULONG curBand; /* Index of start of current band in newReg */
970 RECTL *r1BandEnd; /* End of current band in r1 */
971 RECTL *r2BandEnd; /* End of current band in r2 */
972 ULONG top; /* Top of non-overlapping band */
973 ULONG bot; /* Bottom of non-overlapping band */
974
975 /* Initialization:
976 * set r1, r2, r1End and r2End appropriately, preserve the important
977 * parts of the destination region until the end in case it's one of
978 * the two source regions, then mark the "new" region empty, allocating
979 * another array of rectangles for it to use. */
980 r1 = reg1->Buffer;
981 r2 = reg2->Buffer;
982 r1End = r1 + reg1->rdh.nCount;
983 r2End = r2 + reg2->rdh.nCount;
984
985 /* newReg may be one of the src regions so we can't empty it. We keep a
986 * note of its rects pointer (so that we can free them later), preserve its
987 * extents and simply set numRects to zero. */
988 oldRects = newReg->Buffer;
989 newReg->rdh.nCount = 0;
990
991 /* Allocate a reasonable number of rectangles for the new region. The idea
992 * is to allocate enough so the individual functions don't need to
993 * reallocate and copy the array, which is time consuming, yet we don't
994 * have to worry about using too much memory. I hope to be able to
995 * nuke the Xrealloc() at the end of this function eventually. */
996 newReg->rdh.nRgnSize = max(reg1->rdh.nCount + 1, reg2->rdh.nCount) * 2 * sizeof(RECT);
997
998 newReg->Buffer = ExAllocatePoolWithTag(PagedPool,
999 newReg->rdh.nRgnSize,
1000 TAG_REGION);
1001 if (newReg->Buffer == NULL)
1002 {
1003 newReg->rdh.nRgnSize = 0;
1004 return;
1005 }
1006
1007 /* Initialize ybot and ytop.
1008 * In the upcoming loop, ybot and ytop serve different functions depending
1009 * on whether the band being handled is an overlapping or non-overlapping
1010 * band.
1011 * In the case of a non-overlapping band (only one of the regions
1012 * has points in the band), ybot is the bottom of the most recent
1013 * intersection and thus clips the top of the rectangles in that band.
1014 * ytop is the top of the next intersection between the two regions and
1015 * serves to clip the bottom of the rectangles in the current band.
1016 * For an overlapping band (where the two regions intersect), ytop clips
1017 * the top of the rectangles of both regions and ybot clips the bottoms. */
1018 if (reg1->rdh.rcBound.top < reg2->rdh.rcBound.top)
1019 ybot = reg1->rdh.rcBound.top;
1020 else
1021 ybot = reg2->rdh.rcBound.top;
1022
1023 /* prevBand serves to mark the start of the previous band so rectangles
1024 * can be coalesced into larger rectangles. qv. miCoalesce, above.
1025 * In the beginning, there is no previous band, so prevBand == curBand
1026 * (curBand is set later on, of course, but the first band will always
1027 * start at index 0). prevBand and curBand must be indices because of
1028 * the possible expansion, and resultant moving, of the new region's
1029 * array of rectangles. */
1030 prevBand = 0;
1031 do
1032 {
1033 curBand = newReg->rdh.nCount;
1034
1035 /* This algorithm proceeds one source-band (as opposed to a
1036 * destination band, which is determined by where the two regions
1037 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
1038 * rectangle after the last one in the current band for their
1039 * respective regions. */
1040 r1BandEnd = r1;
1041 while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top))
1042 {
1043 r1BandEnd++;
1044 }
1045
1046 r2BandEnd = r2;
1047 while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top))
1048 {
1049 r2BandEnd++;
1050 }
1051
1052 /* First handle the band that doesn't intersect, if any.
1053 *
1054 * Note that attention is restricted to one band in the
1055 * non-intersecting region at once, so if a region has n
1056 * bands between the current position and the next place it overlaps
1057 * the other, this entire loop will be passed through n times. */
1058 if (r1->top < r2->top)
1059 {
1060 top = max(r1->top,ybot);
1061 bot = min(r1->bottom,r2->top);
1062
1063 if ((top != bot) && (nonOverlap1Func != NULL))
1064 {
1065 (*nonOverlap1Func)(newReg, r1, r1BandEnd, top, bot);
1066 }
1067
1068 ytop = r2->top;
1069 }
1070 else if (r2->top < r1->top)
1071 {
1072 top = max(r2->top,ybot);
1073 bot = min(r2->bottom,r1->top);
1074
1075 if ((top != bot) && (nonOverlap2Func != NULL))
1076 {
1077 (*nonOverlap2Func)(newReg, r2, r2BandEnd, top, bot);
1078 }
1079
1080 ytop = r1->top;
1081 }
1082 else
1083 {
1084 ytop = r1->top;
1085 }
1086
1087 /* If any rectangles got added to the region, try and coalesce them
1088 * with rectangles from the previous band. Note we could just do
1089 * this test in miCoalesce, but some machines incur a not
1090 * inconsiderable cost for function calls, so... */
1091 if (newReg->rdh.nCount != curBand)
1092 {
1093 prevBand = REGION_Coalesce(newReg, prevBand, curBand);
1094 }
1095
1096 /* Now see if we've hit an intersecting band. The two bands only
1097 * intersect if ybot > ytop */
1098 ybot = min(r1->bottom, r2->bottom);
1099 curBand = newReg->rdh.nCount;
1100 if (ybot > ytop)
1101 {
1102 (*overlapFunc)(newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
1103 }
1104
1105 if (newReg->rdh.nCount != curBand)
1106 {
1107 prevBand = REGION_Coalesce(newReg, prevBand, curBand);
1108 }
1109
1110 /* If we've finished with a band (bottom == ybot) we skip forward
1111 * in the region to the next band. */
1112 if (r1->bottom == ybot)
1113 {
1114 r1 = r1BandEnd;
1115 }
1116 if (r2->bottom == ybot)
1117 {
1118 r2 = r2BandEnd;
1119 }
1120 }
1121 while ((r1 != r1End) && (r2 != r2End));
1122
1123 /* Deal with whichever region still has rectangles left. */
1124 curBand = newReg->rdh.nCount;
1125 if (r1 != r1End)
1126 {
1127 if (nonOverlap1Func != NULL)
1128 {
1129 do
1130 {
1131 r1BandEnd = r1;
1132 while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top))
1133 {
1134 r1BandEnd++;
1135 }
1136
1137 (*nonOverlap1Func)(newReg,
1138 r1,
1139 r1BandEnd,
1140 max(r1->top,ybot),
1141 r1->bottom);
1142 r1 = r1BandEnd;
1143 }
1144 while (r1 != r1End);
1145 }
1146 }
1147 else if ((r2 != r2End) && (nonOverlap2Func != NULL))
1148 {
1149 do
1150 {
1151 r2BandEnd = r2;
1152 while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top))
1153 {
1154 r2BandEnd++;
1155 }
1156
1157 (*nonOverlap2Func)(newReg,
1158 r2,
1159 r2BandEnd,
1160 max(r2->top,ybot),
1161 r2->bottom);
1162 r2 = r2BandEnd;
1163 }
1164 while (r2 != r2End);
1165 }
1166
1167 if (newReg->rdh.nCount != curBand)
1168 {
1169 (VOID)REGION_Coalesce(newReg, prevBand, curBand);
1170 }
1171
1172 /* A bit of cleanup. To keep regions from growing without bound,
1173 * we shrink the array of rectangles to match the new number of
1174 * rectangles in the region. This never goes to 0, however...
1175 *
1176 * Only do this stuff if the number of rectangles allocated is more than
1177 * twice the number of rectangles in the region (a simple optimization...). */
1178 if ((newReg->rdh.nRgnSize > (2 * newReg->rdh.nCount * sizeof(RECT))) &&
1179 (newReg->rdh.nCount > 2))
1180 {
1181 if (REGION_NOT_EMPTY(newReg))
1182 {
1183 RECTL *prev_rects = newReg->Buffer;
1184 newReg->Buffer = ExAllocatePoolWithTag(PagedPool,
1185 newReg->rdh.nCount * sizeof(RECT),
1186 TAG_REGION);
1187
1188 if (newReg->Buffer == NULL)
1189 {
1190 newReg->Buffer = prev_rects;
1191 }
1192 else
1193 {
1194 newReg->rdh.nRgnSize = newReg->rdh.nCount*sizeof(RECT);
1195 COPY_RECTS(newReg->Buffer, prev_rects, newReg->rdh.nCount);
1196 if (prev_rects != &newReg->rdh.rcBound)
1197 ExFreePoolWithTag(prev_rects, TAG_REGION);
1198 }
1199 }
1200 else
1201 {
1202 /* No point in doing the extra work involved in an Xrealloc if
1203 * the region is empty */
1204 newReg->rdh.nRgnSize = sizeof(RECT);
1205 if (newReg->Buffer != &newReg->rdh.rcBound)
1206 ExFreePoolWithTag(newReg->Buffer, TAG_REGION);
1207
1208 newReg->Buffer = ExAllocatePoolWithTag(PagedPool,
1209 sizeof(RECT),
1210 TAG_REGION);
1211 ASSERT(newReg->Buffer);
1212 }
1213 }
1214
1215 newReg->rdh.iType = RDH_RECTANGLES;
1216
1217 if (oldRects != &newReg->rdh.rcBound)
1218 ExFreePoolWithTag(oldRects, TAG_REGION);
1219 return;
1220 }
1221
1222 /***********************************************************************
1223 * Region Intersection
1224 ***********************************************************************/
1225
1226
1227 /*!
1228 * Handle an overlapping band for REGION_Intersect.
1229 *
1230 * Results:
1231 * None.
1232 *
1233 * \note Side Effects:
1234 * Rectangles may be added to the region.
1235 *
1236 */
1237 static
1238 VOID
1239 FASTCALL
1240 REGION_IntersectO(
1241 PREGION pReg,
1242 PRECTL r1,
1243 PRECTL r1End,
1244 PRECTL r2,
1245 PRECTL r2End,
1246 INT top,
1247 INT bottom)
1248 {
1249 INT left, right;
1250
1251 while ((r1 != r1End) && (r2 != r2End))
1252 {
1253 left = max(r1->left, r2->left);
1254 right = min(r1->right, r2->right);
1255
1256 /* If there's any overlap between the two rectangles, add that
1257 * overlap to the new region.
1258 * There's no need to check for subsumption because the only way
1259 * such a need could arise is if some region has two rectangles
1260 * right next to each other. Since that should never happen... */
1261 if (left < right)
1262 {
1263 if (!REGION_bAddRect(pReg, left, top, right, bottom))
1264 {
1265 return;
1266 }
1267 }
1268
1269 /* Need to advance the pointers. Shift the one that extends
1270 * to the right the least, since the other still has a chance to
1271 * overlap with that region's next rectangle, if you see what I mean. */
1272 if (r1->right < r2->right)
1273 {
1274 r1++;
1275 }
1276 else if (r2->right < r1->right)
1277 {
1278 r2++;
1279 }
1280 else
1281 {
1282 r1++;
1283 r2++;
1284 }
1285 }
1286
1287 return;
1288 }
1289
1290 /***********************************************************************
1291 * REGION_IntersectRegion
1292 */
1293 static
1294 VOID
1295 FASTCALL
1296 REGION_IntersectRegion(
1297 PREGION newReg,
1298 PREGION reg1,
1299 PREGION reg2)
1300 {
1301 /* Check for trivial reject */
1302 if ((reg1->rdh.nCount == 0) ||
1303 (reg2->rdh.nCount == 0) ||
1304 (EXTENTCHECK(&reg1->rdh.rcBound, &reg2->rdh.rcBound) == 0))
1305 {
1306 newReg->rdh.nCount = 0;
1307 }
1308 else
1309 {
1310 REGION_RegionOp(newReg,
1311 reg1,
1312 reg2,
1313 REGION_IntersectO,
1314 NULL,
1315 NULL);
1316 }
1317
1318 /* Can't alter newReg's extents before we call miRegionOp because
1319 * it might be one of the source regions and miRegionOp depends
1320 * on the extents of those regions being the same. Besides, this
1321 * way there's no checking against rectangles that will be nuked
1322 * due to coalescing, so we have to examine fewer rectangles. */
1323 REGION_SetExtents(newReg);
1324 }
1325
1326 /***********************************************************************
1327 * Region Union
1328 ***********************************************************************/
1329
1330 /*!
1331 * Handle a non-overlapping band for the union operation. Just
1332 * Adds the rectangles into the region. Doesn't have to check for
1333 * subsumption or anything.
1334 *
1335 * Results:
1336 * None.
1337 *
1338 * \note Side Effects:
1339 * pReg->numRects is incremented and the final rectangles overwritten
1340 * with the rectangles we're passed.
1341 *
1342 */
1343 static
1344 VOID
1345 FASTCALL
1346 REGION_UnionNonO(
1347 PREGION pReg,
1348 PRECTL r,
1349 PRECTL rEnd,
1350 INT top,
1351 INT bottom)
1352 {
1353 if (r != rEnd)
1354 {
1355 if (!REGION_bEnsureBufferSize(pReg, pReg->rdh.nCount + (rEnd - r)))
1356 {
1357 return;
1358 }
1359
1360 do
1361 {
1362 REGION_vAddRect(pReg, r->left, top, r->right, bottom);
1363 r++;
1364 }
1365 while (r != rEnd);
1366 }
1367
1368 return;
1369 }
1370
1371 static __inline
1372 BOOL
1373 REGION_bMergeRect(
1374 _Inout_ PREGION prgn,
1375 _In_ LONG left,
1376 _In_ LONG top,
1377 _In_ LONG right,
1378 _In_ LONG bottom)
1379 {
1380 if ((prgn->rdh.nCount != 0) &&
1381 (prgn->Buffer[prgn->rdh.nCount - 1].top == top) &&
1382 (prgn->Buffer[prgn->rdh.nCount - 1].bottom == bottom) &&
1383 (prgn->Buffer[prgn->rdh.nCount - 1].right >= left))
1384 {
1385 if (prgn->Buffer[prgn->rdh.nCount - 1].right < right)
1386 {
1387 prgn->Buffer[prgn->rdh.nCount - 1].right = right;
1388 }
1389 }
1390 else
1391 {
1392 if (!REGION_bAddRect(prgn, left, top, right, bottom))
1393 {
1394 return FALSE;
1395 }
1396 }
1397
1398 return TRUE;
1399 }
1400
1401 /*!
1402 * Handle an overlapping band for the union operation. Picks the
1403 * left-most rectangle each time and merges it into the region.
1404 *
1405 * Results:
1406 * None.
1407 *
1408 * \note Side Effects:
1409 * Rectangles are overwritten in pReg->rects and pReg->numRects will
1410 * be changed.
1411 *
1412 */
1413 static
1414 VOID
1415 FASTCALL
1416 REGION_UnionO (
1417 PREGION pReg,
1418 PRECTL r1,
1419 PRECTL r1End,
1420 PRECTL r2,
1421 PRECTL r2End,
1422 INT top,
1423 INT bottom)
1424 {
1425 while ((r1 != r1End) && (r2 != r2End))
1426 {
1427 if (r1->left < r2->left)
1428 {
1429 REGION_bMergeRect(pReg, r1->left, top, r1->right, bottom);
1430 r1++;
1431 }
1432 else
1433 {
1434 REGION_bMergeRect(pReg, r2->left, top, r2->right, bottom);
1435 r2++;
1436 }
1437 }
1438
1439 if (r1 != r1End)
1440 {
1441 do
1442 {
1443 REGION_bMergeRect(pReg, r1->left, top, r1->right, bottom);
1444 r1++;
1445 }
1446 while (r1 != r1End);
1447 }
1448 else
1449 {
1450 while (r2 != r2End)
1451 {
1452 REGION_bMergeRect(pReg, r2->left, top, r2->right, bottom);
1453 r2++;
1454 }
1455 }
1456
1457 return;
1458 }
1459
1460 /***********************************************************************
1461 * REGION_UnionRegion
1462 */
1463 static
1464 VOID
1465 FASTCALL
1466 REGION_UnionRegion(
1467 PREGION newReg,
1468 PREGION reg1,
1469 PREGION reg2)
1470 {
1471 /* Checks all the simple cases
1472 * Region 1 and 2 are the same or region 1 is empty */
1473 if ((reg1 == reg2) || (reg1->rdh.nCount == 0) ||
1474 (reg1->rdh.rcBound.right <= reg1->rdh.rcBound.left) ||
1475 (reg1->rdh.rcBound.bottom <= reg1->rdh.rcBound.top))
1476 {
1477 if (newReg != reg2)
1478 {
1479 REGION_CopyRegion(newReg, reg2);
1480 }
1481
1482 return;
1483 }
1484
1485 /* If nothing to union (region 2 empty) */
1486 if ((reg2->rdh.nCount == 0) ||
1487 (reg2->rdh.rcBound.right <= reg2->rdh.rcBound.left) ||
1488 (reg2->rdh.rcBound.bottom <= reg2->rdh.rcBound.top))
1489 {
1490 if (newReg != reg1)
1491 {
1492 REGION_CopyRegion(newReg, reg1);
1493 }
1494
1495 return;
1496 }
1497
1498 /* Region 1 completely subsumes region 2 */
1499 if ((reg1->rdh.nCount == 1) &&
1500 (reg1->rdh.rcBound.left <= reg2->rdh.rcBound.left) &&
1501 (reg1->rdh.rcBound.top <= reg2->rdh.rcBound.top) &&
1502 (reg2->rdh.rcBound.right <= reg1->rdh.rcBound.right) &&
1503 (reg2->rdh.rcBound.bottom <= reg1->rdh.rcBound.bottom))
1504 {
1505 if (newReg != reg1)
1506 {
1507 REGION_CopyRegion(newReg, reg1);
1508 }
1509
1510 return;
1511 }
1512
1513 /* Region 2 completely subsumes region 1 */
1514 if ((reg2->rdh.nCount == 1) &&
1515 (reg2->rdh.rcBound.left <= reg1->rdh.rcBound.left) &&
1516 (reg2->rdh.rcBound.top <= reg1->rdh.rcBound.top) &&
1517 (reg1->rdh.rcBound.right <= reg2->rdh.rcBound.right) &&
1518 (reg1->rdh.rcBound.bottom <= reg2->rdh.rcBound.bottom))
1519 {
1520 if (newReg != reg2)
1521 {
1522 REGION_CopyRegion(newReg, reg2);
1523 }
1524
1525 return;
1526 }
1527
1528 REGION_RegionOp(newReg,
1529 reg1,
1530 reg2,
1531 REGION_UnionO,
1532 REGION_UnionNonO,
1533 REGION_UnionNonO);
1534
1535 newReg->rdh.rcBound.left = min(reg1->rdh.rcBound.left, reg2->rdh.rcBound.left);
1536 newReg->rdh.rcBound.top = min(reg1->rdh.rcBound.top, reg2->rdh.rcBound.top);
1537 newReg->rdh.rcBound.right = max(reg1->rdh.rcBound.right, reg2->rdh.rcBound.right);
1538 newReg->rdh.rcBound.bottom = max(reg1->rdh.rcBound.bottom, reg2->rdh.rcBound.bottom);
1539 }
1540
1541 /***********************************************************************
1542 * Region Subtraction
1543 ***********************************************************************/
1544
1545 /*!
1546 * Deal with non-overlapping band for subtraction. Any parts from
1547 * region 2 we discard. Anything from region 1 we add to the region.
1548 *
1549 * Results:
1550 * None.
1551 *
1552 * \note Side Effects:
1553 * pReg may be affected.
1554 *
1555 */
1556 static
1557 VOID
1558 FASTCALL
1559 REGION_SubtractNonO1(
1560 PREGION pReg,
1561 PRECTL r,
1562 PRECTL rEnd,
1563 INT top,
1564 INT bottom)
1565 {
1566 if (r != rEnd)
1567 {
1568 if (!REGION_bEnsureBufferSize(pReg, pReg->rdh.nCount + (rEnd - r)))
1569 {
1570 return;
1571 }
1572
1573 do
1574 {
1575 REGION_vAddRect(pReg, r->left, top, r->right, bottom);
1576 r++;
1577 }
1578 while (r != rEnd);
1579 }
1580
1581 return;
1582 }
1583
1584
1585 /*!
1586 * Overlapping band subtraction. x1 is the left-most point not yet
1587 * checked.
1588 *
1589 * Results:
1590 * None.
1591 *
1592 * \note Side Effects:
1593 * pReg may have rectangles added to it.
1594 *
1595 */
1596 static
1597 VOID
1598 FASTCALL
1599 REGION_SubtractO(
1600 PREGION pReg,
1601 PRECTL r1,
1602 PRECTL r1End,
1603 PRECTL r2,
1604 PRECTL r2End,
1605 INT top,
1606 INT bottom)
1607 {
1608 INT left;
1609
1610 left = r1->left;
1611
1612 while ((r1 != r1End) && (r2 != r2End))
1613 {
1614 if (r2->right <= left)
1615 {
1616 /* Subtrahend missed the boat: go to next subtrahend. */
1617 r2++;
1618 }
1619 else if (r2->left <= left)
1620 {
1621 /* Subtrahend preceeds minuend: nuke left edge of minuend. */
1622 left = r2->right;
1623 if (left >= r1->right)
1624 {
1625 /* Minuend completely covered: advance to next minuend and
1626 * reset left fence to edge of new minuend. */
1627 r1++;
1628 if (r1 != r1End)
1629 left = r1->left;
1630 }
1631 else
1632 {
1633 /* Subtrahend now used up since it doesn't extend beyond
1634 * minuend */
1635 r2++;
1636 }
1637 }
1638 else if (r2->left < r1->right)
1639 {
1640 /* Left part of subtrahend covers part of minuend: add uncovered
1641 * part of minuend to region and skip to next subtrahend. */
1642 if (!REGION_bAddRect(pReg, left, top, r2->left, bottom))
1643 {
1644 return;
1645 }
1646
1647 left = r2->right;
1648 if (left >= r1->right)
1649 {
1650 /* Minuend used up: advance to new... */
1651 r1++;
1652 if (r1 != r1End)
1653 left = r1->left;
1654 }
1655 else
1656 {
1657 /* Subtrahend used up */
1658 r2++;
1659 }
1660 }
1661 else
1662 {
1663 /* Minuend used up: add any remaining piece before advancing. */
1664 if (r1->right > left)
1665 {
1666 if (!REGION_bAddRect(pReg, left, top, r1->right, bottom))
1667 {
1668 return;
1669 }
1670 }
1671
1672 r1++;
1673 if (r1 != r1End)
1674 left = r1->left;
1675 }
1676 }
1677
1678 /* Make sure the buffer is large enough for all remaining operations */
1679 if (r1 != r1End)
1680 {
1681 if (!REGION_bEnsureBufferSize(pReg, pReg->rdh.nCount + (r1End - r1)))
1682 {
1683 return;
1684 }
1685
1686 /* Add remaining minuend rectangles to region. */
1687 do
1688 {
1689 REGION_vAddRect(pReg, left, top, r1->right, bottom);
1690 r1++;
1691 if (r1 != r1End)
1692 {
1693 left = r1->left;
1694 }
1695 }
1696 while (r1 != r1End);
1697 }
1698
1699 return;
1700 }
1701
1702 /*!
1703 * Subtract regS from regM and leave the result in regD.
1704 * S stands for subtrahend, M for minuend and D for difference.
1705 *
1706 * Results:
1707 * TRUE.
1708 *
1709 * \note Side Effects:
1710 * regD is overwritten.
1711 *
1712 */
1713 static
1714 VOID
1715 FASTCALL
1716 REGION_SubtractRegion(
1717 PREGION regD,
1718 PREGION regM,
1719 PREGION regS)
1720 {
1721 /* Check for trivial reject */
1722 if ((regM->rdh.nCount == 0) ||
1723 (regS->rdh.nCount == 0) ||
1724 (EXTENTCHECK(&regM->rdh.rcBound, &regS->rdh.rcBound) == 0))
1725 {
1726 REGION_CopyRegion(regD, regM);
1727 return;
1728 }
1729
1730 REGION_RegionOp(regD,
1731 regM,
1732 regS,
1733 REGION_SubtractO,
1734 REGION_SubtractNonO1,
1735 NULL);
1736
1737 /* Can't alter newReg's extents before we call miRegionOp because
1738 * it might be one of the source regions and miRegionOp depends
1739 * on the extents of those regions being the unaltered. Besides, this
1740 * way there's no checking against rectangles that will be nuked
1741 * due to coalescing, so we have to examine fewer rectangles. */
1742 REGION_SetExtents(regD);
1743 }
1744
1745 /***********************************************************************
1746 * REGION_XorRegion
1747 */
1748 static
1749 VOID
1750 FASTCALL
1751 REGION_XorRegion(
1752 PREGION dr,
1753 PREGION sra,
1754 PREGION srb)
1755 {
1756 HRGN htra, htrb;
1757 PREGION tra, trb;
1758
1759 // FIXME: Don't use a handle
1760 tra = REGION_AllocRgnWithHandle(sra->rdh.nCount + 1);
1761 if (tra == NULL)
1762 {
1763 return;
1764 }
1765 htra = tra->BaseObject.hHmgr;
1766
1767 // FIXME: Don't use a handle
1768 trb = REGION_AllocRgnWithHandle(srb->rdh.nCount + 1);
1769 if (trb == NULL)
1770 {
1771 REGION_UnlockRgn(tra);
1772 GreDeleteObject(htra);
1773 return;
1774 }
1775 htrb = trb->BaseObject.hHmgr;
1776
1777 REGION_SubtractRegion(tra, sra, srb);
1778 REGION_SubtractRegion(trb, srb, sra);
1779 REGION_UnionRegion(dr, tra, trb);
1780 REGION_UnlockRgn(tra);
1781 REGION_UnlockRgn(trb);
1782
1783 GreDeleteObject(htra);
1784 GreDeleteObject(htrb);
1785 return;
1786 }
1787
1788
1789 /*!
1790 * Adds a rectangle to a REGION
1791 */
1792 VOID
1793 FASTCALL
1794 REGION_UnionRectWithRgn(
1795 PREGION rgn,
1796 const RECTL *rect)
1797 {
1798 REGION region;
1799
1800 region.Buffer = &region.rdh.rcBound;
1801 region.rdh.nCount = 1;
1802 region.rdh.nRgnSize = sizeof(RECT);
1803 region.rdh.rcBound = *rect;
1804 REGION_UnionRegion(rgn, rgn, &region);
1805 }
1806
1807 INT
1808 FASTCALL
1809 REGION_SubtractRectFromRgn(
1810 PREGION prgnDest,
1811 PREGION prgnSrc,
1812 const RECTL *prcl)
1813 {
1814 REGION rgnLocal;
1815
1816 rgnLocal.Buffer = &rgnLocal.rdh.rcBound;
1817 rgnLocal.rdh.nCount = 1;
1818 rgnLocal.rdh.nRgnSize = sizeof(RECT);
1819 rgnLocal.rdh.rcBound = *prcl;
1820 REGION_SubtractRegion(prgnDest, prgnSrc, &rgnLocal);
1821 return REGION_Complexity(prgnDest);
1822 }
1823
1824 static
1825 BOOL
1826 REGION_bMakeSimpleFrameRgn(
1827 _Inout_ PREGION prgn,
1828 _In_ PRECTL prclSrc,
1829 _In_ INT cx,
1830 _In_ INT cy)
1831 {
1832 RECTL arcl[4];
1833 UINT i;
1834
1835 NT_ASSERT((cx >= 0) && (cy >= 0));
1836 NT_ASSERT((prclSrc->bottom > prclSrc->top) &&
1837 (prclSrc->right > prclSrc->left));
1838
1839 /* Start with an empty region */
1840 EMPTY_REGION(prgn);
1841
1842 /* Check for the case where the frame covers the whole rect */
1843 if (((prclSrc->bottom - prclSrc->top) <= cy * 2) ||
1844 ((prclSrc->right - prclSrc->left) <= cx * 2))
1845 {
1846 prgn->rdh.rcBound = *prclSrc;
1847 prgn->Buffer[0] = *prclSrc;
1848 prgn->rdh.nCount = 1;
1849 return TRUE;
1850 }
1851
1852 i = 0;
1853
1854 if (cy != 0)
1855 {
1856 /* Top rectangle */
1857 arcl[i].left = prclSrc->left;
1858 arcl[i].top = prclSrc->top;
1859 arcl[i].right = prclSrc->right;
1860 arcl[i].bottom = prclSrc->top + cy;
1861 i++;
1862 }
1863
1864 if (cx != 0)
1865 {
1866 /* Left rectangle */
1867 arcl[i].left = prclSrc->left;
1868 arcl[i].top = prclSrc->top + cy;
1869 arcl[i].right = prclSrc->left + cx;
1870 arcl[i].bottom = prclSrc->bottom - cy;
1871 i++;
1872
1873 /* Right rectangle */
1874 arcl[i].left = prclSrc->right - cx;
1875 arcl[i].top = prclSrc->top + cy;
1876 arcl[i].right = prclSrc->right;
1877 arcl[i].bottom = prclSrc->bottom - cy;
1878 i++;
1879 }
1880
1881 if (cy != 0)
1882 {
1883 /* Bottom rectangle */
1884 arcl[i].left = prclSrc->left;
1885 arcl[i].top = prclSrc->bottom - cy;
1886 arcl[i].right = prclSrc->right;
1887 arcl[i].bottom = prclSrc->bottom;
1888 i++;
1889 }
1890
1891 if (i != 0)
1892 {
1893 /* The frame results in a complex region. rcBounds remains
1894 the same, though. */
1895 prgn->rdh.nCount = i;
1896 NT_ASSERT(prgn->rdh.nCount > 1);
1897 prgn->rdh.nRgnSize = prgn->rdh.nCount * sizeof(RECT);
1898 NT_ASSERT(prgn->Buffer == &prgn->rdh.rcBound);
1899 prgn->Buffer = ExAllocatePoolWithTag(PagedPool,
1900 prgn->rdh.nRgnSize,
1901 TAG_REGION);
1902 if (prgn->Buffer == NULL)
1903 {
1904 prgn->rdh.nRgnSize = 0;
1905 return FALSE;
1906 }
1907
1908 _PRAGMA_WARNING_SUPPRESS(__WARNING_MAYBE_UNINIT_VAR) // arcl is initialized
1909 COPY_RECTS(prgn->Buffer, arcl, prgn->rdh.nCount);
1910 }
1911
1912 return TRUE;
1913 }
1914
1915 static
1916 BOOL
1917 REGION_bMakeFrameRegion(
1918 _Inout_ PREGION prgnDest,
1919 _Inout_ PREGION prgnSrc,
1920 _In_ INT cx,
1921 _In_ INT cy)
1922 {
1923 /* Handle negative cx / cy */
1924 cx = abs(cx);
1925 cy = abs(cy);
1926
1927 /* Check border size (the cast is necessary to catch cx/cy == INT_MIN!) */
1928 if (((UINT)cx > MAX_COORD) || ((UINT)cy > MAX_COORD))
1929 {
1930 return FALSE;
1931 }
1932
1933 /* Fail on empty source region */
1934 if (!REGION_NOT_EMPTY(prgnSrc))
1935 {
1936 return FALSE;
1937 }
1938
1939 /* Handle trivial case */
1940 if ((cx == 0) && (cy == 0))
1941 {
1942 EMPTY_REGION(prgnDest);
1943 return TRUE;
1944 }
1945
1946 /* Handle simple source region */
1947 if (REGION_Complexity(prgnSrc) == SIMPLEREGION)
1948 {
1949 return REGION_bMakeSimpleFrameRgn(prgnDest, &prgnSrc->rdh.rcBound, cx, cy);
1950 }
1951
1952 /* Check if we can move the region to create the frame region */
1953 if ((prgnSrc->rdh.rcBound.left < (MIN_COORD + cx)) ||
1954 (prgnSrc->rdh.rcBound.top < (MIN_COORD + cy)) ||
1955 (prgnSrc->rdh.rcBound.right > (MAX_COORD - cx)) ||
1956 (prgnSrc->rdh.rcBound.bottom > (MAX_COORD - cy)))
1957 {
1958 return FALSE;
1959 }
1960
1961 /* Copy the source region */
1962 if (!REGION_CopyRegion(prgnDest, prgnSrc))
1963 {
1964 return FALSE;
1965 }
1966
1967 /* Move the source region to the bottom-right */
1968 NT_VERIFY(REGION_bOffsetRgn(prgnSrc, cx, cy));
1969
1970 /* Intersect with the source region (this crops the top-left frame) */
1971 REGION_IntersectRegion(prgnDest, prgnDest, prgnSrc);
1972
1973 /* Move the source region to the bottom-left */
1974 NT_VERIFY(REGION_bOffsetRgn(prgnSrc, -2 * cx, 0));
1975
1976 /* Intersect with the source region (this crops the top-right frame) */
1977 REGION_IntersectRegion(prgnDest, prgnDest, prgnSrc);
1978
1979 /* Move the source region to the top-left */
1980 NT_VERIFY(REGION_bOffsetRgn(prgnSrc, 0, -2 * cy));
1981
1982 /* Intersect with the source region (this crops the bottom-right frame) */
1983 REGION_IntersectRegion(prgnDest, prgnDest, prgnSrc);
1984
1985 /* Move the source region to the top-right */
1986 NT_VERIFY(REGION_bOffsetRgn(prgnSrc, 2 * cx, 0));
1987
1988 /* Intersect with the source region (this crops the bottom-left frame) */
1989 REGION_IntersectRegion(prgnDest, prgnDest, prgnSrc);
1990
1991 /* Move the source region back to the original position */
1992 NT_VERIFY(REGION_bOffsetRgn(prgnSrc, -cx, cy));
1993
1994 /* Finally subtract the cropped region from the source */
1995 REGION_SubtractRegion(prgnDest, prgnSrc, prgnDest);
1996
1997 return TRUE;
1998 }
1999
2000 HRGN
2001 FASTCALL
2002 GreCreateFrameRgn(
2003 HRGN hrgn,
2004 INT cx,
2005 INT cy)
2006 {
2007 PREGION prgnFrame, prgnSrc;
2008 HRGN hrgnFrame;
2009
2010 /* Allocate a new region */
2011 prgnFrame = REGION_AllocUserRgnWithHandle(1);
2012 if (prgnFrame == NULL)
2013 {
2014 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
2015 return NULL;
2016 }
2017
2018 /* Lock the source region */
2019 prgnSrc = REGION_LockRgn(hrgn);
2020 if (prgnSrc == NULL)
2021 {
2022 REGION_Delete(prgnFrame);
2023 return FALSE;
2024 }
2025
2026 if (REGION_bMakeFrameRegion(prgnFrame, prgnSrc, cx, cy))
2027 {
2028 hrgnFrame = prgnFrame->BaseObject.hHmgr;
2029 REGION_UnlockRgn(prgnFrame);
2030 }
2031 else
2032 {
2033 REGION_Delete(prgnFrame);
2034 hrgnFrame = NULL;
2035 }
2036
2037 REGION_UnlockRgn(prgnSrc);
2038 return hrgnFrame;
2039 }
2040
2041 BOOL
2042 FASTCALL
2043 REGION_bXformRgn(
2044 _Inout_ PREGION prgn,
2045 _In_ PMATRIX pmx)
2046 {
2047 XFORMOBJ xo;
2048 ULONG i, j, cjSize;
2049 PPOINT ppt;
2050 PULONG pcPoints;
2051 RECT rect;
2052 BOOL bResult;
2053
2054 /* Check if this is a scaling only matrix (off-diagonal elements are 0 */
2055 if (pmx->flAccel & XFORM_SCALE)
2056 {
2057 /* Check if this is a translation only matrix */
2058 if (pmx->flAccel & XFORM_UNITY)
2059 {
2060 /* Just offset the region */
2061 return REGION_bOffsetRgn(prgn, (pmx->fxDx + 8) / 16, (pmx->fxDy + 8) / 16);
2062 }
2063 else
2064 {
2065 /* Initialize the xform object */
2066 XFORMOBJ_vInit(&xo, pmx);
2067
2068 /* Scaling can move the rects out of the coordinate space, so
2069 * we first need to check whether we can apply the transformation
2070 * on the bounds rect without modifying the region */
2071 if (!XFORMOBJ_bApplyXform(&xo, XF_LTOL, 2, &prgn->rdh.rcBound, &rect))
2072 {
2073 return FALSE;
2074 }
2075
2076 /* Apply the xform to the rects in the region */
2077 if (!XFORMOBJ_bApplyXform(&xo,
2078 XF_LTOL,
2079 prgn->rdh.nCount * 2,
2080 prgn->Buffer,
2081 prgn->Buffer))
2082 {
2083 /* This can not happen, since we already checked the bounds! */
2084 NT_ASSERT(FALSE);
2085 }
2086
2087 /* Reset bounds */
2088 RECTL_vSetEmptyRect(&prgn->rdh.rcBound);
2089
2090 /* Loop all rects in the region */
2091 for (i = 0; i < prgn->rdh.nCount; i++)
2092 {
2093 /* Make sure the rect is well-ordered after the xform */
2094 RECTL_vMakeWellOrdered(&prgn->Buffer[i]);
2095
2096 /* Update bounds */
2097 RECTL_bUnionRect(&prgn->rdh.rcBound,
2098 &prgn->rdh.rcBound,
2099 &prgn->Buffer[i]);
2100 }
2101
2102 /* Loop all rects in the region */
2103 for (i = 0; i < prgn->rdh.nCount - 1; i++)
2104 {
2105 for (j = i; i < prgn->rdh.nCount; i++)
2106 {
2107 NT_ASSERT(prgn->Buffer[i].top < prgn->Buffer[i].bottom);
2108 NT_ASSERT(prgn->Buffer[j].top >= prgn->Buffer[i].top);
2109 }
2110 }
2111
2112 return TRUE;
2113 }
2114 }
2115 else
2116 {
2117 /* Allocate a buffer for the polygons */
2118 cjSize = prgn->rdh.nCount * (4 * sizeof(POINT) + sizeof(ULONG));
2119 ppt = ExAllocatePoolWithTag(PagedPool, cjSize, GDITAG_REGION);
2120 if (ppt == NULL)
2121 {
2122 return FALSE;
2123 }
2124
2125 /* Fill the buffer with the rects */
2126 pcPoints = (PULONG)&ppt[4 * prgn->rdh.nCount];
2127 for (i = 0; i < prgn->rdh.nCount; i++)
2128 {
2129 /* Make sure the rect is within the legal range */
2130 pcPoints[i] = 4;
2131 ppt[4 * i + 0].x = prgn->Buffer[i].left;
2132 ppt[4 * i + 0].y = prgn->Buffer[i].top;
2133 ppt[4 * i + 1].x = prgn->Buffer[i].right;
2134 ppt[4 * i + 1].y = prgn->Buffer[i].top;
2135 ppt[4 * i + 2].x = prgn->Buffer[i].right;
2136 ppt[4 * i + 2].y = prgn->Buffer[i].bottom;
2137 ppt[4 * i + 3].x = prgn->Buffer[i].left;
2138 ppt[4 * i + 3].y = prgn->Buffer[i].bottom;
2139 }
2140
2141 /* Initialize the xform object */
2142 XFORMOBJ_vInit(&xo, pmx);
2143
2144 /* Apply the xform to the rects in the buffer */
2145 if (!XFORMOBJ_bApplyXform(&xo,
2146 XF_LTOL,
2147 prgn->rdh.nCount * 2,
2148 ppt,
2149 ppt))
2150 {
2151 /* This means, there were coordinates that would go outside of
2152 the coordinate space after the transformation */
2153 ExFreePoolWithTag(ppt, GDITAG_REGION);
2154 return FALSE;
2155 }
2156
2157 /* Now use the polygons to create a polygon region */
2158 bResult = REGION_SetPolyPolygonRgn(prgn,
2159 ppt,
2160 pcPoints,
2161 prgn->rdh.nCount,
2162 WINDING);
2163
2164 /* Free the polygon buffer */
2165 ExFreePoolWithTag(ppt, GDITAG_REGION);
2166
2167 return bResult;
2168 }
2169
2170 }
2171
2172
2173 PREGION
2174 FASTCALL
2175 REGION_AllocRgnWithHandle(
2176 INT nReg)
2177 {
2178 //HRGN hReg;
2179 PREGION pReg;
2180
2181 pReg = (PREGION)GDIOBJ_AllocateObject(GDIObjType_RGN_TYPE,
2182 sizeof(REGION),
2183 BASEFLAG_LOOKASIDE);
2184 if (pReg == NULL)
2185 {
2186 DPRINT1("Could not allocate a palette.\n");
2187 return NULL;
2188 }
2189
2190 //hReg = pReg->BaseObject.hHmgr;
2191
2192 if ((nReg == 0) || (nReg == 1))
2193 {
2194 /* Testing shows that > 95% of all regions have only 1 rect.
2195 Including that here saves us from having to do another allocation */
2196 pReg->Buffer = &pReg->rdh.rcBound;
2197 }
2198 else
2199 {
2200 pReg->Buffer = ExAllocatePoolWithTag(PagedPool,
2201 nReg * sizeof(RECT),
2202 TAG_REGION);
2203 if (pReg->Buffer == NULL)
2204 {
2205 DPRINT1("Could not allocate region buffer\n");
2206 GDIOBJ_vDeleteObject(&pReg->BaseObject);
2207 return NULL;
2208 }
2209 }
2210
2211 EMPTY_REGION(pReg);
2212 pReg->rdh.dwSize = sizeof(RGNDATAHEADER);
2213 pReg->rdh.nCount = nReg;
2214 pReg->rdh.nRgnSize = nReg * sizeof(RECT);
2215 pReg->prgnattr = &pReg->rgnattr;
2216
2217 /* Initialize the region attribute */
2218 pReg->rgnattr.AttrFlags = 0;
2219 pReg->rgnattr.iComplexity = SIMPLEREGION;
2220 pReg->rgnattr.Rect = pReg->rdh.rcBound;
2221
2222 /* Finally insert the region into the handle table */
2223 if (!GDIOBJ_hInsertObject(&pReg->BaseObject, GDI_OBJ_HMGR_POWNED))
2224 {
2225 DPRINT1("Could not insert palette into handle table.\n");
2226 GDIOBJ_vFreeObject(&pReg->BaseObject);
2227 return NULL;
2228 }
2229
2230 return pReg;
2231 }
2232
2233 BOOL
2234 NTAPI
2235 REGION_bAllocRgnAttr(
2236 PREGION prgn)
2237 {
2238 PPROCESSINFO ppi;
2239 PRGN_ATTR prgnattr;
2240
2241 NT_ASSERT(prgn->prgnattr == &prgn->rgnattr);
2242
2243 ppi = PsGetCurrentProcessWin32Process();
2244 ASSERT(ppi);
2245
2246 prgnattr = GdiPoolAllocate(ppi->pPoolRgnAttr);
2247 if (prgnattr == NULL)
2248 {
2249 DPRINT1("Could not allocate RGN attr\n");
2250 return FALSE;
2251 }
2252
2253 /* Copy the current region attribute */
2254 *prgnattr = prgn->rgnattr;
2255
2256 /* Set the object attribute in the handle table */
2257 prgn->prgnattr = prgnattr;
2258 GDIOBJ_vSetObjectAttr(&prgn->BaseObject, prgnattr);
2259
2260 return TRUE;
2261 }
2262
2263
2264 //
2265 // Allocate User Space Region Handle.
2266 //
2267 PREGION
2268 FASTCALL
2269 REGION_AllocUserRgnWithHandle(
2270 INT nRgn)
2271 {
2272 PREGION prgn;
2273
2274 prgn = REGION_AllocRgnWithHandle(nRgn);
2275 if (prgn == NULL)
2276 {
2277 return NULL;
2278 }
2279
2280 if (!REGION_bAllocRgnAttr(prgn))
2281 {
2282 ASSERT(FALSE);
2283 }
2284
2285 return prgn;
2286 }
2287
2288 static
2289 VOID
2290 REGION_vSyncRegion(
2291 _In_ PREGION prgn)
2292 {
2293 PRGN_ATTR prgnattr;
2294
2295 NT_ASSERT(prgn != NULL);
2296 NT_ASSERT(prgn->prgnattr != NULL);
2297 NT_ASSERT((prgn->prgnattr == &prgn->rgnattr) ||
2298 (prgn->prgnattr->AttrFlags & ATTR_RGN_VALID));
2299
2300 /* Get the region attribute and check if it's dirty (modified) */
2301 prgnattr = prgn->prgnattr;
2302 if (prgnattr->AttrFlags & ATTR_RGN_DIRTY)
2303 {
2304 NT_ASSERT(GreGetObjectOwner(prgn->BaseObject.hHmgr) == GDI_OBJ_HMGR_POWNED);
2305 NT_ASSERT(prgnattr != &prgn->rgnattr);
2306
2307 if (prgnattr->iComplexity == NULLREGION)
2308 {
2309 EMPTY_REGION(prgn);
2310 }
2311 else if (prgnattr->iComplexity == SIMPLEREGION)
2312 {
2313 REGION_SetRectRgn(prgn,
2314 prgnattr->Rect.left,
2315 prgnattr->Rect.top,
2316 prgnattr->Rect.right,
2317 prgnattr->Rect.bottom);
2318 }
2319 else
2320 {
2321 /* Should not happen, region attribute is corrupted! */
2322 DPRINT1("Region attribute is corrupted, ignoring\n");
2323 NT_ASSERT(FALSE);
2324 }
2325 }
2326
2327 /* Reset the flags */
2328 prgnattr->AttrFlags &= ~(ATTR_RGN_DIRTY | ATTR_RGN_VALID);
2329 }
2330
2331 PREGION
2332 FASTCALL
2333 REGION_LockRgn(
2334 _In_ HRGN hrgn)
2335 {
2336 PREGION prgn;
2337
2338 prgn = GDIOBJ_LockObject(hrgn, GDIObjType_RGN_TYPE);
2339 if (prgn == NULL)
2340 return NULL;
2341
2342 REGION_vSyncRegion(prgn);
2343 return prgn;
2344 }
2345
2346 VOID
2347 FASTCALL
2348 REGION_UnlockRgn(
2349 _In_ PREGION prgn)
2350 {
2351 PRGN_ATTR prgnattr;
2352
2353 NT_ASSERT(prgn != NULL);
2354 NT_ASSERT(prgn->prgnattr != NULL);
2355
2356 /* Get the region attribute and check if it's user mode */
2357 prgnattr = prgn->prgnattr;
2358 if (prgnattr != &prgn->rgnattr)
2359 {
2360 NT_ASSERT(GreGetObjectOwner(prgn->BaseObject.hHmgr) == GDI_OBJ_HMGR_POWNED);
2361 prgnattr->iComplexity = REGION_Complexity(prgn);
2362 prgnattr->Rect.left = prgn->rdh.rcBound.left;
2363 prgnattr->Rect.top = prgn->rdh.rcBound.top;
2364 prgnattr->Rect.right = prgn->rdh.rcBound.right;
2365 prgnattr->Rect.bottom = prgn->rdh.rcBound.bottom;
2366 prgnattr->AttrFlags |= ATTR_RGN_VALID;
2367 }
2368
2369 GDIOBJ_vUnlockObject(&prgn->BaseObject);
2370 }
2371
2372 /*
2373 System Regions:
2374 These regions do not use attribute sections and when allocated, use gdiobj
2375 level functions.
2376 */
2377 //
2378 // System Region Functions
2379 //
2380 PREGION
2381 FASTCALL
2382 IntSysCreateRectpRgn(
2383 INT LeftRect,
2384 INT TopRect,
2385 INT RightRect,
2386 INT BottomRect)
2387 {
2388 PREGION prgn;
2389
2390 /* Allocate a region, without a handle */
2391 prgn = (PREGION)GDIOBJ_AllocateObject(GDIObjType_RGN_TYPE, sizeof(REGION), BASEFLAG_LOOKASIDE);
2392 if (prgn == NULL)
2393 {
2394 return NULL;
2395 }
2396
2397 /* Initialize it */
2398 prgn->Buffer = &prgn->rdh.rcBound;
2399 prgn->prgnattr = &prgn->rgnattr;
2400 prgn->prgnattr->AttrFlags = ATTR_RGN_VALID;
2401 REGION_SetRectRgn(prgn, LeftRect, TopRect, RightRect, BottomRect);
2402
2403 return prgn;
2404 }
2405
2406 VOID
2407 NTAPI
2408 REGION_vCleanup(PVOID ObjectBody)
2409 {
2410 PREGION pRgn = (PREGION)ObjectBody;
2411 PPROCESSINFO ppi = PsGetCurrentProcessWin32Process();
2412 ASSERT(ppi);
2413
2414 ASSERT(pRgn->prgnattr);
2415 if (pRgn->prgnattr != &pRgn->rgnattr)
2416 GdiPoolFree(ppi->pPoolRgnAttr, pRgn->prgnattr);
2417
2418 if (pRgn->Buffer && pRgn->Buffer != &pRgn->rdh.rcBound)
2419 ExFreePoolWithTag(pRgn->Buffer, TAG_REGION);
2420 }
2421
2422 VOID
2423 FASTCALL
2424 REGION_Delete(PREGION pRgn)
2425 {
2426 if (pRgn == prgnDefault)
2427 return;
2428
2429 GDIOBJ_vDeleteObject(&pRgn->BaseObject);
2430 }
2431
2432 BOOL
2433 FASTCALL
2434 IntGdiSetRegionOwner(HRGN hRgn, DWORD OwnerMask)
2435 {
2436 PREGION prgn;
2437 PRGN_ATTR prgnattr;
2438 PPROCESSINFO ppi;
2439
2440 prgn = REGION_LockRgn(hRgn);
2441 if (prgn == NULL)
2442 {
2443 return FALSE;
2444 }
2445
2446 prgnattr = prgn->prgnattr;
2447 if (prgnattr != &prgn->rgnattr)
2448 {
2449 GDIOBJ_vSetObjectAttr(&prgn->BaseObject, NULL);
2450 prgn->prgnattr = &prgn->rgnattr;
2451 ppi = PsGetCurrentProcessWin32Process();
2452 GdiPoolFree(ppi->pPoolRgnAttr, prgnattr);
2453 }
2454
2455 REGION_UnlockRgn(prgn);
2456
2457 return GreSetObjectOwner(hRgn, OwnerMask);
2458 }
2459
2460 INT
2461 FASTCALL
2462 IntGdiCombineRgn(
2463 PREGION prgnDest,
2464 PREGION prgnSrc1,
2465 PREGION prgnSrc2,
2466 INT iCombineMode)
2467 {
2468
2469 if (prgnDest == NULL)
2470 {
2471 DPRINT("IntGdiCombineRgn: hDest unavailable\n");
2472 return ERROR;
2473 }
2474
2475 if (prgnSrc1 == NULL)
2476 {
2477 DPRINT("IntGdiCombineRgn: hSrc1 unavailable\n");
2478 return ERROR;
2479 }
2480
2481 if (iCombineMode == RGN_COPY)
2482 {
2483 if (!REGION_CopyRegion(prgnDest, prgnSrc1))
2484 return ERROR;
2485
2486 return REGION_Complexity(prgnDest);
2487 }
2488
2489 if (prgnSrc2 == NULL)
2490 {
2491 DPRINT1("IntGdiCombineRgn requires hSrc2 != NULL for combine mode %d!\n", iCombineMode);
2492 ASSERT(FALSE);
2493 return ERROR;
2494 }
2495
2496 switch (iCombineMode)
2497 {
2498 case RGN_AND:
2499 REGION_IntersectRegion(prgnDest, prgnSrc1, prgnSrc2);
2500 break;
2501 case RGN_OR:
2502 REGION_UnionRegion(prgnDest, prgnSrc1, prgnSrc2);
2503 break;
2504 case RGN_XOR:
2505 REGION_XorRegion(prgnDest, prgnSrc1, prgnSrc2);
2506 break;
2507 case RGN_DIFF:
2508 REGION_SubtractRegion(prgnDest, prgnSrc1, prgnSrc2);
2509 break;
2510 }
2511
2512 return REGION_Complexity(prgnDest);
2513 }
2514
2515 INT
2516 FASTCALL
2517 REGION_GetRgnBox(
2518 PREGION Rgn,
2519 PRECTL pRect)
2520 {
2521 DWORD ret;
2522
2523 if (Rgn != NULL)
2524 {
2525 *pRect = Rgn->rdh.rcBound;
2526 ret = REGION_Complexity(Rgn);
2527
2528 return ret;
2529 }
2530 return 0; // If invalid region return zero
2531 }
2532
2533 INT
2534 APIENTRY
2535 IntGdiGetRgnBox(
2536 HRGN hRgn,
2537 PRECTL pRect)
2538 {
2539 PREGION Rgn;
2540 DWORD ret;
2541
2542 Rgn = REGION_LockRgn(hRgn);
2543 if (Rgn == NULL)
2544 {
2545 return ERROR;
2546 }
2547
2548 ret = REGION_GetRgnBox(Rgn, pRect);
2549 REGION_UnlockRgn(Rgn);
2550
2551 return ret;
2552 }
2553
2554
2555 BOOL
2556 FASTCALL
2557 REGION_PtInRegion(
2558 PREGION prgn,
2559 INT X,
2560 INT Y)
2561 {
2562 ULONG i;
2563 PRECT r;
2564
2565 if (prgn->rdh.nCount > 0 && INRECT(prgn->rdh.rcBound, X, Y))
2566 {
2567 r = prgn->Buffer;
2568 for (i = 0; i < prgn->rdh.nCount; i++)
2569 {
2570 if (INRECT(r[i], X, Y))
2571 return TRUE;
2572 }
2573 }
2574
2575 return FALSE;
2576 }
2577
2578 BOOL
2579 FASTCALL
2580 REGION_RectInRegion(
2581 PREGION Rgn,
2582 const RECTL *rect)
2583 {
2584 PRECTL pCurRect, pRectEnd;
2585 RECT rc;
2586
2587 /* Swap the coordinates to make right >= left and bottom >= top */
2588 /* (region building rectangles are normalized the same way) */
2589 if (rect->top > rect->bottom)
2590 {
2591 rc.top = rect->bottom;
2592 rc.bottom = rect->top;
2593 }
2594 else
2595 {
2596 rc.top = rect->top;
2597 rc.bottom = rect->bottom;
2598 }
2599
2600 if (rect->right < rect->left)
2601 {
2602 rc.right = rect->left;
2603 rc.left = rect->right;
2604 }
2605 else
2606 {
2607 rc.right = rect->right;
2608 rc.left = rect->left;
2609 }
2610
2611 /* This is (just) a useful optimization */
2612 if ((Rgn->rdh.nCount > 0) && EXTENTCHECK(&Rgn->rdh.rcBound, &rc))
2613 {
2614 for (pCurRect = Rgn->Buffer, pRectEnd = pCurRect +
2615 Rgn->rdh.nCount; pCurRect < pRectEnd; pCurRect++)
2616 {
2617 if (pCurRect->bottom <= rc.top)
2618 continue; /* Not far enough down yet */
2619
2620 if (pCurRect->top >= rc.bottom)
2621 break; /* Too far down */
2622
2623 if (pCurRect->right <= rc.left)
2624 continue; /* Not far enough over yet */
2625
2626 if (pCurRect->left >= rc.right)
2627 {
2628 continue;
2629 }
2630
2631 return TRUE;
2632 }
2633 }
2634
2635 return FALSE;
2636 }
2637
2638 VOID
2639 FASTCALL
2640 REGION_SetRectRgn(
2641 PREGION rgn,
2642 INT LeftRect,
2643 INT TopRect,
2644 INT RightRect,
2645 INT BottomRect)
2646 {
2647 PRECTL firstRect;
2648
2649 if (LeftRect > RightRect)
2650 {
2651 INT tmp = LeftRect;
2652 LeftRect = RightRect;
2653 RightRect = tmp;
2654 }
2655
2656 if (TopRect > BottomRect)
2657 {
2658 INT tmp = TopRect;
2659 TopRect = BottomRect;
2660 BottomRect = tmp;
2661 }
2662
2663 if ((LeftRect != RightRect) && (TopRect != BottomRect))
2664 {
2665 firstRect = rgn->Buffer;
2666 ASSERT(firstRect);
2667 firstRect->left = rgn->rdh.rcBound.left = LeftRect;
2668 firstRect->top = rgn->rdh.rcBound.top = TopRect;
2669 firstRect->right = rgn->rdh.rcBound.right = RightRect;
2670 firstRect->bottom = rgn->rdh.rcBound.bottom = BottomRect;
2671 rgn->rdh.nCount = 1;
2672 rgn->rdh.iType = RDH_RECTANGLES;
2673 }
2674 else
2675 {
2676 EMPTY_REGION(rgn);
2677 }
2678 }
2679
2680 BOOL
2681 FASTCALL
2682 REGION_bOffsetRgn(
2683 _Inout_ PREGION prgn,
2684 _In_ INT cx,
2685 _In_ INT cy)
2686 {
2687 PRECTL prcl;
2688 UINT i;
2689
2690 NT_ASSERT(prgn != NULL);
2691
2692 /* Check for trivial case */
2693 if ((cx == 0) && (cy == 0))
2694 {
2695 return TRUE;
2696 }
2697
2698 /* Check for empty regions, we ignore the offset values here */
2699 if (prgn->rdh.nCount == 0)
2700 {
2701 return TRUE;
2702 }
2703
2704 /* Make sure the offset is within the legal range */
2705 if ((cx > MAX_COORD) || (cx < MIN_COORD) ||
2706 (cy > MAX_COORD) || (cy < MIN_COORD))
2707 {
2708 return FALSE;
2709 }
2710
2711 /* Are we moving right? */
2712 if (cx > 0)
2713 {
2714 /* Check if we stay inside the bounds on the right side */
2715 if (prgn->rdh.rcBound.right > (MAX_COORD - cx))
2716 {
2717 return FALSE;
2718 }
2719 }
2720 else
2721 {
2722 /* Check if we stay inside the bounds on the left side */
2723 if (prgn->rdh.rcBound.left < (MIN_COORD - cx))
2724 {
2725 return FALSE;
2726 }
2727 }
2728
2729 /* Are we moving down? */
2730 if (cy > 0)
2731 {
2732 /* Check if we stay inside the bounds on the right side */
2733 if (prgn->rdh.rcBound.bottom > (MAX_COORD - cy))
2734 {
2735 return FALSE;
2736 }
2737 }
2738 else
2739 {
2740 /* Check if we stay inside the bounds on the left side */
2741 if (prgn->rdh.rcBound.top < (MIN_COORD - cy))
2742 {
2743 return FALSE;
2744 }
2745 }
2746
2747 /* Loop to move the rects */
2748 prcl = prgn->Buffer;
2749 for (i = 0; i < prgn->rdh.nCount; i++)
2750 {
2751 prcl[i].left += cx;
2752 prcl[i].right += cx;
2753 prcl[i].top += cy;
2754 prcl[i].bottom += cy;
2755 }
2756
2757 /* Finally update the bounds rect */
2758 if (prgn->Buffer != &prgn->rdh.rcBound)
2759 {
2760 prgn->rdh.rcBound.left += cx;
2761 prgn->rdh.rcBound.right += cx;
2762 prgn->rdh.rcBound.top += cy;
2763 prgn->rdh.rcBound.bottom += cy;
2764 }
2765
2766 return TRUE;
2767 }
2768
2769 /***********************************************************************
2770 * REGION_InsertEdgeInET
2771 *
2772 * Insert the given edge into the edge table.
2773 * First we must find the correct bucket in the
2774 * Edge table, then find the right slot in the
2775 * bucket. Finally, we can insert it.
2776 *
2777 */
2778 static
2779 VOID
2780 FASTCALL
2781 REGION_InsertEdgeInET(
2782 EDGE_TABLE *ET,
2783 EDGE_TABLE_ENTRY *ETE,
2784 INT scanline,
2785 SCANLINE_LISTBLOCK **SLLBlock,
2786 INT *iSLLBlock)
2787 {
2788 EDGE_TABLE_ENTRY *start, *prev;
2789 SCANLINE_LIST *pSLL, *pPrevSLL;
2790 SCANLINE_LISTBLOCK *tmpSLLBlock;
2791
2792 /* Find the right bucket to put the edge into */
2793 pPrevSLL = &ET->scanlines;
2794 pSLL = pPrevSLL->next;
2795 while (pSLL && (pSLL->scanline < scanline))
2796 {
2797 pPrevSLL = pSLL;
2798 pSLL = pSLL->next;
2799 }
2800
2801 /* Reassign pSLL (pointer to SCANLINE_LIST) if necessary */
2802 if ((!pSLL) || (pSLL->scanline > scanline))
2803 {
2804 if (*iSLLBlock > SLLSPERBLOCK-1)
2805 {
2806 tmpSLLBlock = ExAllocatePoolWithTag(PagedPool,
2807 sizeof(SCANLINE_LISTBLOCK),
2808 TAG_REGION);
2809 if (tmpSLLBlock == NULL)
2810 {
2811 DPRINT1("REGION_InsertEdgeInETL(): Can't alloc SLLB\n");
2812 /* FIXME: Free resources? */
2813 return;
2814 }
2815
2816 (*SLLBlock)->next = tmpSLLBlock;
2817 tmpSLLBlock->next = (SCANLINE_LISTBLOCK *)NULL;
2818 *SLLBlock = tmpSLLBlock;
2819 *iSLLBlock = 0;
2820 }
2821
2822 pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
2823
2824 pSLL->next = pPrevSLL->next;
2825 pSLL->edgelist = (EDGE_TABLE_ENTRY *)NULL;
2826 pPrevSLL->next = pSLL;
2827 }
2828
2829 pSLL->scanline = scanline;
2830
2831 /* Now insert the edge in the right bucket */
2832 prev = (EDGE_TABLE_ENTRY *)NULL;
2833 start = pSLL->edgelist;
2834 while (start && (start->bres.minor_axis < ETE->bres.minor_axis))
2835 {
2836 prev = start;
2837 start = start->next;
2838 }
2839
2840 ETE->next = start;
2841
2842 if (prev)
2843 prev->next = ETE;
2844 else
2845 pSLL->edgelist = ETE;
2846 }
2847
2848 /***********************************************************************
2849 * REGION_loadAET
2850 *
2851 * This routine moves EDGE_TABLEEntries from the
2852 * EDGE_TABLE into the Active Edge Table,
2853 * leaving them sorted by smaller x coordinate.
2854 *
2855 */
2856 static
2857 VOID
2858 FASTCALL
2859 REGION_loadAET(
2860 EDGE_TABLE_ENTRY *AET,
2861 EDGE_TABLE_ENTRY *ETEs)
2862 {
2863 EDGE_TABLE_ENTRY *pPrevAET;
2864 EDGE_TABLE_ENTRY *tmp;
2865
2866 pPrevAET = AET;
2867 AET = AET->next;
2868 while (ETEs)
2869 {
2870 while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis))
2871 {
2872 pPrevAET = AET;
2873 AET = AET->next;
2874 }
2875
2876 tmp = ETEs->next;
2877 ETEs->next = AET;
2878 if (AET)
2879 AET->back = ETEs;
2880
2881 ETEs->back = pPrevAET;
2882 pPrevAET->next = ETEs;
2883 pPrevAET = ETEs;
2884
2885 ETEs = tmp;
2886 }
2887 }
2888
2889 /***********************************************************************
2890 * REGION_computeWAET
2891 *
2892 * This routine links the AET by the
2893 * nextWETE (winding EDGE_TABLE_ENTRY) link for
2894 * use by the winding number rule. The final
2895 * Active Edge Table (AET) might look something
2896 * like:
2897 *
2898 * AET
2899 * ---------- --------- ---------
2900 * |ymax | |ymax | |ymax |
2901 * | ... | |... | |... |
2902 * |next |->|next |->|next |->...
2903 * |nextWETE| |nextWETE| |nextWETE|
2904 * --------- --------- ^--------
2905 * | | |
2906 * V-------------------> V---> ...
2907 *
2908 */
2909 static
2910 VOID
2911 FASTCALL
2912 REGION_computeWAET(
2913 EDGE_TABLE_ENTRY *AET)
2914 {
2915 register EDGE_TABLE_ENTRY *pWETE;
2916 register INT inside = 1;
2917 register INT isInside = 0;
2918
2919 AET->nextWETE = (EDGE_TABLE_ENTRY *)NULL;
2920 pWETE = AET;
2921 AET = AET->next;
2922 while (AET)
2923 {
2924 if (AET->ClockWise)
2925 isInside++;
2926 else
2927 isInside--;
2928
2929 if ((!inside && !isInside) ||
2930 ( inside && isInside))
2931 {
2932 pWETE->nextWETE = AET;
2933 pWETE = AET;
2934 inside = !inside;
2935 }
2936 AET = AET->next;
2937 }
2938
2939 pWETE->nextWETE = (EDGE_TABLE_ENTRY *)NULL;
2940 }
2941
2942 /***********************************************************************
2943 * REGION_InsertionSort
2944 *
2945 * Just a simple insertion sort using
2946 * pointers and back pointers to sort the Active
2947 * Edge Table.
2948 *
2949 */
2950 static
2951 BOOL
2952 FASTCALL
2953 REGION_InsertionSort(
2954 EDGE_TABLE_ENTRY *AET)
2955 {
2956 EDGE_TABLE_ENTRY *pETEchase;
2957 EDGE_TABLE_ENTRY *pETEinsert;
2958 EDGE_TABLE_ENTRY *pETEchaseBackTMP;
2959 BOOL changed = FALSE;
2960
2961 AET = AET->next;
2962 while (AET)
2963 {
2964 pETEinsert = AET;
2965 pETEchase = AET;
2966 while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
2967 pETEchase = pETEchase->back;
2968
2969 AET = AET->next;
2970 if (pETEchase != pETEinsert)
2971 {
2972 pETEchaseBackTMP = pETEchase->back;
2973 pETEinsert->back->next = AET;
2974 if (AET)
2975 AET->back = pETEinsert->back;
2976
2977 pETEinsert->next = pETEchase;
2978 pETEchase->back->next = pETEinsert;
2979 pETEchase->back = pETEinsert;
2980 pETEinsert->back = pETEchaseBackTMP;
2981 changed = TRUE;
2982 }
2983 }
2984
2985 return changed;
2986 }
2987
2988 /***********************************************************************
2989 * REGION_FreeStorage
2990 *
2991 * Clean up our act.
2992 */
2993 static
2994 VOID
2995 FASTCALL
2996 REGION_FreeStorage(
2997 SCANLINE_LISTBLOCK *pSLLBlock)
2998 {
2999 SCANLINE_LISTBLOCK *tmpSLLBlock;
3000
3001 while (pSLLBlock)
3002 {
3003 tmpSLLBlock = pSLLBlock->next;
3004 ExFreePoolWithTag(pSLLBlock, TAG_REGION);
3005 pSLLBlock = tmpSLLBlock;
3006 }
3007 }
3008
3009
3010 /***********************************************************************
3011 * REGION_PtsToRegion
3012 *
3013 * Create an array of rectangles from a list of points.
3014 */
3015 static
3016 INT
3017 FASTCALL
3018 REGION_PtsToRegion(
3019 INT numFullPtBlocks,
3020 INT iCurPtBlock,
3021 POINTBLOCK *FirstPtBlock,
3022 PREGION reg)
3023 {
3024 RECTL *rects;
3025 POINT *pts;
3026 POINTBLOCK *CurPtBlock;
3027 INT i;
3028 RECTL *extents, *temp;
3029 INT numRects;
3030
3031 extents = &reg->rdh.rcBound;
3032
3033 numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
3034
3035 /* Make sure, we have at least one rect */
3036 if (numRects == 0)
3037 {
3038 numRects = 1;
3039 }
3040
3041 temp = ExAllocatePoolWithTag(PagedPool, numRects * sizeof(RECT), TAG_REGION);
3042 if (temp == NULL)
3043 {
3044 return 0;
3045 }
3046
3047 if (reg->Buffer != NULL)
3048 {
3049 COPY_RECTS(temp, reg->Buffer, reg->rdh.nCount);
3050 if (reg->Buffer != &reg->rdh.rcBound)
3051 ExFreePoolWithTag(reg->Buffer, TAG_REGION);
3052 }
3053 reg->Buffer = temp;
3054
3055 reg->rdh.nCount = numRects;
3056 CurPtBlock = FirstPtBlock;
3057 rects = reg->Buffer - 1;
3058 numRects = 0;
3059 extents->left = LARGE_COORDINATE, extents->right = SMALL_COORDINATE;
3060
3061 for ( ; numFullPtBlocks >= 0; numFullPtBlocks--)
3062 {
3063 /* The loop uses 2 points per iteration */
3064 i = NUMPTSTOBUFFER >> 1;
3065 if (numFullPtBlocks == 0)
3066 i = iCurPtBlock >> 1;
3067
3068 for (pts = CurPtBlock->pts; i--; pts += 2)
3069 {
3070 if (pts->x == pts[1].x)
3071 continue;
3072
3073 if ((numRects && pts->x == rects->left) &&
3074 (pts->y == rects->bottom) &&
3075 (pts[1].x == rects->right) &&
3076 ((numRects == 1) || (rects[-1].top != rects->top)) &&
3077 (i && pts[2].y > pts[1].y))
3078 {
3079 rects->bottom = pts[1].y + 1;
3080 continue;
3081 }
3082
3083 numRects++;
3084 rects++;
3085 rects->left = pts->x;
3086 rects->top = pts->y;
3087 rects->right = pts[1].x;
3088 rects->bottom = pts[1].y + 1;
3089
3090 if (rects->left < extents->left)
3091 extents->left = rects->left;
3092 if (rects->right > extents->right)
3093 extents->right = rects->right;
3094 }
3095
3096 CurPtBlock = CurPtBlock->next;
3097 }
3098
3099 if (numRects)
3100 {
3101 extents->top = reg->Buffer->top;
3102 extents->bottom = rects->bottom;
3103 }
3104 else
3105 {
3106 extents->left = 0;
3107 extents->top = 0;
3108 extents->right = 0;
3109 extents->bottom = 0;
3110 }
3111
3112 reg->rdh.nCount = numRects;
3113
3114 return(TRUE);
3115 }
3116
3117 /***********************************************************************
3118 * REGION_CreateETandAET
3119 *
3120 * This routine creates the edge table for
3121 * scan converting polygons.
3122 * The Edge Table (ET) looks like:
3123 *
3124 * EDGE_TABLE
3125 * --------
3126 * | ymax | SCANLINE_LISTs
3127 * |scanline|-->------------>-------------->...
3128 * -------- |scanline| |scanline|
3129 * |edgelist| |edgelist|
3130 * --------- ---------
3131 * | |
3132 * | |
3133 * V V
3134 * list of ETEs list of ETEs
3135 *
3136 * where ETE is an EDGE_TABLE_ENTRY data structure,
3137 * and there is one SCANLINE_LIST per scanline at
3138 * which an edge is initially entered.
3139 *
3140 */
3141 static
3142 VOID
3143 FASTCALL
3144 REGION_CreateETandAET(
3145 const ULONG *Count,
3146 INT nbpolygons,
3147 const POINT *pts,
3148 EDGE_TABLE *ET,
3149 EDGE_TABLE_ENTRY *AET,
3150 EDGE_TABLE_ENTRY *pETEs,
3151 SCANLINE_LISTBLOCK *pSLLBlock)
3152 {
3153 const POINT *top, *bottom;
3154 const POINT *PrevPt, *CurrPt, *EndPt;
3155 INT poly, count;
3156 INT iSLLBlock = 0;
3157 INT dy;
3158
3159 /* Initialize the Active Edge Table */
3160 AET->next = (EDGE_TABLE_ENTRY *)NULL;
3161 AET->back = (EDGE_TABLE_ENTRY *)NULL;
3162 AET->nextWETE = (EDGE_TABLE_ENTRY *)NULL;
3163 AET->bres.minor_axis = SMALL_COORDINATE;
3164
3165 /* Initialize the Edge Table. */
3166 ET->scanlines.next = (SCANLINE_LIST *)NULL;
3167 ET->ymax = SMALL_COORDINATE;
3168 ET->ymin = LARGE_COORDINATE;
3169 pSLLBlock->next = (SCANLINE_LISTBLOCK *)NULL;
3170
3171 EndPt = pts - 1;
3172 for (poly = 0; poly < nbpolygons; poly++)
3173 {
3174 count = Count[poly];
3175 EndPt += count;
3176 if (count < 2)
3177 continue;
3178
3179 PrevPt = EndPt;
3180
3181 /* For each vertex in the array of points.
3182 * In this loop we are dealing with two vertices at
3183 * a time -- these make up one edge of the polygon. */
3184 while (count--)
3185 {
3186 CurrPt = pts++;
3187
3188 /* Find out which point is above and which is below. */
3189 if (PrevPt->y > CurrPt->y)
3190 {
3191 bottom = PrevPt, top = CurrPt;
3192 pETEs->ClockWise = 0;
3193 }
3194 else
3195 {
3196 bottom = CurrPt, top = PrevPt;
3197 pETEs->ClockWise = 1;
3198 }
3199
3200 /* Don't add horizontal edges to the Edge table. */
3201 if (bottom->y != top->y)
3202 {
3203 /* -1 so we don't get last scanline */
3204 pETEs->ymax = bottom->y - 1;
3205
3206 /* Initialize integer edge algorithm */
3207 dy = bottom->y - top->y;
3208 BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres);
3209
3210 REGION_InsertEdgeInET(ET,
3211 pETEs,
3212 top->y,
3213 &pSLLBlock,
3214 &iSLLBlock);
3215
3216 if (PrevPt->y > ET->ymax)
3217 ET->ymax = PrevPt->y;
3218 if (PrevPt->y < ET->ymin)
3219 ET->ymin = PrevPt->y;
3220 pETEs++;
3221 }
3222
3223 PrevPt = CurrPt;
3224 }
3225 }
3226 }
3227
3228 BOOL
3229 FASTCALL
3230 REGION_SetPolyPolygonRgn(
3231 _Inout_ PREGION prgn,
3232 _In_ const POINT *ppt,
3233 _In_ const ULONG *pcPoints,
3234 _In_ ULONG cPolygons,
3235 _In_ INT iMode)
3236 {
3237 EDGE_TABLE_ENTRY *pAET; /* Active Edge Table */
3238 INT y; /* Current scanline */
3239 INT iPts = 0; /* Number of pts in buffer */
3240 EDGE_TABLE_ENTRY *pWETE; /* Winding Edge Table Entry */
3241 SCANLINE_LIST *pSLL; /* Current SCANLINE_LIST */
3242 POINT *pts; /* Output buffer */
3243 EDGE_TABLE_ENTRY *pPrevAET; /* Pointer to previous AET */
3244 EDGE_TABLE ET; /* Header node for ET */
3245 EDGE_TABLE_ENTRY AET; /* Header node for AET */
3246 EDGE_TABLE_ENTRY *pETEs; /* EDGE_TABLEEntries pool */
3247 SCANLINE_LISTBLOCK SLLBlock; /* Header for SCANLINE_LIST */
3248 INT fixWAET = FALSE;
3249 POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
3250 POINTBLOCK *tmpPtBlock;
3251 UINT numFullPtBlocks = 0;
3252 UINT poly, total;
3253
3254 /* Check if iMode is valid */
3255 if ((iMode != ALTERNATE) && (iMode != WINDING))
3256 {
3257 DPRINT1("Invalid iMode: %lu\n", iMode);
3258 return FALSE;
3259 }
3260
3261 /* Special case a rectangle */
3262 if (((cPolygons == 1) && ((pcPoints[0] == 4) ||
3263 ((pcPoints[0] == 5) && (ppt[4].x == ppt[0].x) && (ppt[4].y == ppt[0].y)))) &&
3264 (((ppt[0].y == ppt[1].y) &&
3265 (ppt[1].x == ppt[2].x) &&
3266 (ppt[2].y == ppt[3].y) &&
3267 (ppt[3].x == ppt[0].x)) ||
3268 ((ppt[0].x == ppt[1].x) &&
3269 (ppt[1].y == ppt[2].y) &&
3270 (ppt[2].x == ppt[3].x) &&
3271 (ppt[3].y == ppt[0].y))))
3272 {
3273 REGION_SetRectRgn(prgn,
3274 min(ppt[0].x, ppt[2].x),
3275 min(ppt[0].y, ppt[2].y),
3276 max(ppt[0].x, ppt[2].x),
3277 max(ppt[0].y, ppt[2].y));
3278 return TRUE;
3279 }
3280
3281 for (poly = total = 0; poly < cPolygons; poly++)
3282 total += pcPoints[poly];
3283
3284 pETEs = ExAllocatePoolWithTag(PagedPool,
3285 sizeof(EDGE_TABLE_ENTRY) * total,
3286 TAG_REGION);
3287 if (pETEs == NULL)
3288 {
3289 DPRINT1("Failed to allocate %lu edge entries\n", total);
3290 return FALSE;
3291 }
3292
3293 pts = FirstPtBlock.pts;
3294 REGION_CreateETandAET(pcPoints, cPolygons, ppt, &ET, &AET, pETEs, &SLLBlock);
3295 pSLL = ET.scanlines.next;
3296 curPtBlock = &FirstPtBlock;
3297
3298 if (iMode != WINDING)
3299 {
3300 /* For each scanline */
3301 for (y = ET.ymin; y < ET.ymax; y++)
3302 {
3303 /* Add a new edge to the active edge table when we
3304 * get to the next edge. */
3305 if (pSLL != NULL && y == pSLL->scanline)
3306 {
3307 REGION_loadAET(&AET, pSLL->edgelist);
3308 pSLL = pSLL->next;
3309 }
3310 pPrevAET = &AET;
3311 pAET = AET.next;
3312
3313 /* For each active edge */
3314 while (pAET)
3315 {
3316 pts->x = pAET->bres.minor_axis, pts->y = y;
3317 pts++, iPts++;
3318
3319 /* Send out the buffer */
3320 if (iPts == NUMPTSTOBUFFER)
3321 {
3322 tmpPtBlock = ExAllocatePoolWithTag(PagedPool,
3323 sizeof(POINTBLOCK),
3324 TAG_REGION);
3325 if (tmpPtBlock == NULL)
3326 {
3327 DPRINT1("Can't alloc tmpPtBlock\n");
3328 ExFreePoolWithTag(pETEs, TAG_REGION);
3329 return FALSE;
3330 }
3331
3332 curPtBlock->next = tmpPtBlock;
3333 curPtBlock = tmpPtBlock;
3334 pts = curPtBlock->pts;
3335 numFullPtBlocks++;
3336 iPts = 0;
3337 }
3338
3339 EVALUATEEDGEEVENODD(pAET, pPrevAET, y);
3340 }
3341
3342 REGION_InsertionSort(&AET);
3343 }
3344 }
3345 else
3346 {
3347 /* For each scanline */
3348 for (y = ET.ymin; y < ET.ymax; y++)
3349 {
3350 /* Add a new edge to the active edge table when we
3351 * get to the next edge. */
3352 if (pSLL != NULL && y == pSLL->scanline)
3353 {
3354 REGION_loadAET(&AET, pSLL->edgelist);
3355 REGION_computeWAET(&AET);
3356 pSLL = pSLL->next;
3357 }
3358
3359 pPrevAET = &AET;
3360 pAET = AET.next;
3361 pWETE = pAET;
3362
3363 /* For each active edge */
3364 while (pAET)
3365 {
3366 /* Add to the buffer only those edges that
3367 * are in the Winding active edge table. */
3368 if (pWETE == pAET)
3369 {
3370 pts->x = pAET->bres.minor_axis;
3371 pts->y = y;
3372 pts++;
3373 iPts++;
3374
3375 /* Send out the buffer */
3376 if (iPts == NUMPTSTOBUFFER)
3377 {
3378 tmpPtBlock = ExAllocatePoolWithTag(PagedPool,
3379 sizeof(POINTBLOCK),
3380 TAG_REGION);
3381 if (tmpPtBlock == NULL)
3382 {
3383 DPRINT1("Can't alloc tPB\n");
3384 ExFreePoolWithTag(pETEs, TAG_REGION);
3385 return FALSE;
3386 }
3387 curPtBlock->next = tmpPtBlock;
3388 curPtBlock = tmpPtBlock;
3389 pts = curPtBlock->pts;
3390 numFullPtBlocks++;
3391 iPts = 0;
3392 }
3393
3394 pWETE = pWETE->nextWETE;
3395 }
3396
3397 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
3398 }
3399
3400 /* Recompute the winding active edge table if
3401 * we just resorted or have exited an edge. */
3402 if (REGION_InsertionSort(&AET) || fixWAET)
3403 {
3404 REGION_computeWAET(&AET);
3405 fixWAET = FALSE;
3406 }
3407 }
3408 }
3409
3410 REGION_FreeStorage(SLLBlock.next);
3411 REGION_PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, prgn);
3412
3413 for (curPtBlock = FirstPtBlock.next; numFullPtBlocks-- > 0;)
3414 {
3415 tmpPtBlock = curPtBlock->next;
3416 ExFreePoolWithTag(curPtBlock, TAG_REGION);
3417 curPtBlock = tmpPtBlock;
3418 }
3419
3420 ExFreePoolWithTag(pETEs, TAG_REGION);
3421 return TRUE;
3422 }
3423
3424 HRGN
3425 NTAPI
3426 GreCreatePolyPolygonRgn(
3427 _In_ const POINT *ppt,
3428 _In_ const ULONG *pcPoints,
3429 _In_ ULONG cPolygons,
3430 _In_ INT iMode)
3431 {
3432 PREGION prgn;
3433 HRGN hrgn;
3434
3435 /* Allocate a new region */
3436 prgn = REGION_AllocUserRgnWithHandle(0);
3437 if (prgn == NULL)
3438 {
3439 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
3440 return NULL;
3441 }
3442
3443 /* Call the internal function and check for success */
3444 if (REGION_SetPolyPolygonRgn(prgn, ppt, pcPoints, cPolygons, iMode))
3445 {
3446 /* Success, get the handle and unlock the region */
3447 hrgn = prgn->BaseObject.hHmgr;
3448 REGION_UnlockRgn(prgn);
3449 }
3450 else
3451 {
3452 /* Failure, delete the region */
3453 REGION_Delete(prgn);
3454 hrgn = NULL;
3455 }
3456
3457 return hrgn;
3458 }
3459
3460 BOOL
3461 FASTCALL
3462 IntRectInRegion(
3463 HRGN hRgn,
3464 LPRECTL rc)
3465 {
3466 PREGION Rgn;
3467 BOOL Ret;
3468
3469 Rgn = REGION_LockRgn(hRgn);
3470 if (Rgn == NULL)
3471 {
3472 return ERROR;
3473 }
3474
3475 Ret = REGION_RectInRegion(Rgn, rc);
3476 REGION_UnlockRgn(Rgn);
3477 return Ret;
3478 }
3479
3480
3481 //
3482 // NtGdi Exported Functions
3483 //
3484 INT
3485 APIENTRY
3486 NtGdiCombineRgn(
3487 IN HRGN hrgnDst,
3488 IN HRGN hrgnSrc1,
3489 IN HRGN hrgnSrc2,
3490 IN INT iMode)
3491 {
3492 HRGN ahrgn[3];
3493 PREGION aprgn[3];
3494 INT iResult;
3495
3496 /* Validate the combine mode */
3497 if ((iMode < RGN_AND) || (iMode > RGN_COPY))
3498 {
3499 return ERROR;
3500 }
3501
3502 /* Validate that we have the required regions */
3503 if ((hrgnDst == NULL) ||
3504 (hrgnSrc1 == NULL) ||
3505 ((iMode != RGN_COPY) && (hrgnSrc2 == NULL)))
3506 {
3507 DPRINT1("NtGdiCombineRgn invalid parameters: %p, %p, %p, %d\n",
3508 hrgnDst, hrgnSrc1, hrgnSrc2, iMode);
3509 EngSetLastError(ERROR_INVALID_HANDLE);
3510 return ERROR;
3511 }
3512
3513 /* Lock all regions */
3514 ahrgn[0] = hrgnDst;
3515 ahrgn[1] = hrgnSrc1;
3516 ahrgn[2] = iMode != RGN_COPY ? hrgnSrc2 : NULL;
3517 if (!GDIOBJ_bLockMultipleObjects(3, (HGDIOBJ*)ahrgn, (PVOID*)aprgn, GDIObjType_RGN_TYPE))
3518 {
3519 DPRINT1("NtGdiCombineRgn failed to lock regions: %p, %p, %p, %d\n",
3520 hrgnDst, hrgnSrc1, hrgnSrc2, iMode);
3521 return ERROR;
3522 }
3523
3524 /* HACK: Sync usermode attributes */
3525 REGION_vSyncRegion(aprgn[0]);
3526 if (aprgn[1] != aprgn[0])
3527 REGION_vSyncRegion(aprgn[1]);
3528 if ((aprgn[2] != NULL) && (aprgn[2] != aprgn[0]) && (aprgn[2] != aprgn[1]))
3529 REGION_vSyncRegion(aprgn[2]);
3530
3531 /* Call the internal function */
3532 iResult = IntGdiCombineRgn(aprgn[0], aprgn[1], aprgn[2], iMode);
3533
3534 /* Unlock and return */
3535 REGION_UnlockRgn(aprgn[0]);
3536 REGION_UnlockRgn(aprgn[1]);
3537 if (aprgn[2] != NULL)
3538 REGION_UnlockRgn(aprgn[2]);
3539
3540 return iResult;
3541 }
3542
3543 HRGN
3544 APIENTRY
3545 NtGdiCreateEllipticRgn(
3546 INT Left,
3547 INT Top,
3548 INT Right,
3549 INT Bottom)
3550 {
3551 return NtGdiCreateRoundRectRgn(Left,
3552 Top,
3553 Right, Bottom,
3554 Right - Left,
3555 Bottom - Top);
3556 }
3557
3558 HRGN
3559 APIENTRY
3560 NtGdiCreateRectRgn(
3561 INT LeftRect,
3562 INT TopRect,
3563 INT RightRect,
3564 INT BottomRect)
3565 {
3566 PREGION pRgn;
3567 HRGN hRgn;
3568
3569 /* Allocate region data structure with space for 1 RECTL */
3570 pRgn = REGION_AllocUserRgnWithHandle(1);
3571 if (pRgn == NULL)
3572 {
3573 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
3574 return NULL;
3575 }
3576
3577 hRgn = pRgn->BaseObject.hHmgr;
3578
3579 REGION_SetRectRgn(pRgn, LeftRect, TopRect, RightRect, BottomRect);
3580 REGION_UnlockRgn(pRgn);
3581
3582 DPRINT("Returning %p.\n", hRgn);
3583
3584 return hRgn;
3585 }
3586
3587 HRGN
3588 APIENTRY
3589 NtGdiCreateRoundRectRgn(
3590 INT left,
3591 INT top,
3592 INT right,
3593 INT bottom,
3594 INT ellipse_width,
3595 INT ellipse_height)
3596 {
3597 PREGION obj;
3598 HRGN hrgn;
3599 INT asq, bsq, d, xd, yd;
3600 RECTL rect;
3601
3602 /* Make the dimensions sensible */
3603 if (left > right)
3604 {
3605 INT tmp = left;
3606 left = right;
3607 right = tmp;
3608 }
3609
3610 if (top > bottom)
3611 {
3612 INT tmp = top;
3613 top = bottom;
3614 bottom = tmp;
3615 }
3616
3617 ellipse_width = abs(ellipse_width);
3618 ellipse_height = abs(ellipse_height);
3619
3620 /* Check parameters */
3621 if (ellipse_width > right-left)
3622 ellipse_width = right-left;
3623 if (ellipse_height > bottom-top)
3624 ellipse_height = bottom-top;
3625
3626 /* Check if we can do a normal rectangle instead */
3627 if ((ellipse_width < 2) || (ellipse_height < 2))
3628 return NtGdiCreateRectRgn(left, top, right, bottom);
3629
3630 /* Create region */
3631 d = (ellipse_height < 128) ? ((3 * ellipse_height) >> 2) : 64;
3632 obj = REGION_AllocUserRgnWithHandle(d);
3633 if (obj == NULL)
3634 return 0;
3635
3636 hrgn = obj->BaseObject.hHmgr;
3637
3638 /* Ellipse algorithm, based on an article by K. Porter
3639 in DDJ Graphics Programming Column, 8/89 */
3640 asq = ellipse_width * ellipse_width / 4; /* a^2 */
3641 bsq = ellipse_height * ellipse_height / 4; /* b^2 */
3642 d = bsq - asq * ellipse_height / 2 + asq / 4; /* b^2 - a^2b + a^2/4 */
3643 xd = 0;
3644 yd = asq * ellipse_height; /* 2a^2b */
3645
3646 rect.left = left + ellipse_width / 2;
3647 rect.right = right - ellipse_width / 2;
3648
3649 /* Loop to draw first half of quadrant */
3650 while (xd < yd)
3651 {
3652 /* If nearest pixel is toward the center */
3653 if (d > 0)
3654 {
3655 /* Move toward center */
3656 rect.top = top++;
3657 rect.bottom = rect.top + 1;
3658 REGION_UnionRectWithRgn(obj, &rect);
3659 rect.top = --bottom;
3660 rect.bottom = rect.top + 1;
3661 REGION_UnionRectWithRgn(obj, &rect);
3662 yd -= 2*asq;
3663 d -= yd;
3664 }
3665
3666 /* Next horiz point */
3667 rect.left--;
3668 rect.right++;
3669 xd += 2*bsq;
3670 d += bsq + xd;
3671 }
3672
3673 /* Loop to draw second half of quadrant */
3674 d += (3 * (asq-bsq) / 2 - (xd+yd)) / 2;
3675 while (yd >= 0)
3676 {
3677 /* next vertical point */
3678 rect.top = top++;
3679 rect.bottom = rect.top + 1;
3680 REGION_UnionRectWithRgn(obj, &rect);
3681 rect.top = --bottom;
3682 rect.bottom = rect.top + 1;
3683 REGION_UnionRectWithRgn(obj, &rect);
3684
3685 /* If nearest pixel is outside ellipse */
3686 if (d < 0)
3687 {
3688 /* Move away from center */
3689 rect.left--;
3690 rect.right++;
3691 xd += 2*bsq;
3692 d += xd;
3693 }
3694
3695 yd -= 2*asq;
3696 d += asq - yd;
3697 }
3698
3699 /* Add the inside rectangle */
3700 if (top <= bottom)
3701 {
3702 rect.top = top;
3703 rect.bottom = bottom;
3704 REGION_UnionRectWithRgn(obj, &rect);
3705 }
3706
3707 REGION_UnlockRgn(obj);
3708 return hrgn;
3709 }
3710
3711 BOOL
3712 APIENTRY
3713 NtGdiEqualRgn(
3714 HRGN hSrcRgn1,
3715 HRGN hSrcRgn2)
3716 {
3717 HRGN ahrgn[2];
3718 PREGION aprgn[2];
3719 PREGION rgn1, rgn2;
3720 PRECTL tRect1, tRect2;
3721 ULONG i;
3722 BOOL bRet = FALSE;
3723
3724 /* Check if we got 2 regions */
3725 if ((hSrcRgn1 == NULL) || (hSrcRgn2 == NULL))
3726 {
3727 return FALSE;
3728 }
3729
3730 /* Check if these are the same regions */
3731 if (hSrcRgn1 == hSrcRgn2)
3732 {
3733 /* Make sure this region is valid */
3734 if ((GDI_HANDLE_GET_TYPE(hSrcRgn1) == GDILoObjType_LO_REGION_TYPE) &&
3735 GreIsHandleValid(hSrcRgn1))
3736 {
3737 return TRUE;
3738 }
3739 return FALSE;
3740 }
3741
3742 /* Lock both regions */
3743 ahrgn[0] = hSrcRgn1;
3744 ahrgn[1] = hSrcRgn2;
3745 if (!GDIOBJ_bLockMultipleObjects(2, (HGDIOBJ*)ahrgn, (PVOID*)aprgn, GDIObjType_RGN_TYPE))
3746 {
3747 DPRINT1("NtGdiEqualRgn failed to lock regions: %p, %p\n",
3748 hSrcRgn1, hSrcRgn2);
3749 return FALSE;
3750 }
3751
3752 REGION_vSyncRegion(aprgn[0]);
3753 REGION_vSyncRegion(aprgn[1]);
3754
3755 rgn1 = aprgn[0];
3756 rgn2 = aprgn[1];
3757
3758 if (rgn1->rdh.nCount != rgn2->rdh.nCount)
3759 goto exit;
3760
3761 if (rgn1->rdh.nCount == 0)
3762 {
3763 bRet = TRUE;
3764 goto exit;
3765 }
3766
3767 if ((rgn1->rdh.rcBound.left != rgn2->rdh.rcBound.left) ||
3768 (rgn1->rdh.rcBound.right != rgn2->rdh.rcBound.right) ||
3769 (rgn1->rdh.rcBound.top != rgn2->rdh.rcBound.top) ||
3770 (rgn1->rdh.rcBound.bottom != rgn2->rdh.rcBound.bottom))
3771 goto exit;
3772
3773 tRect1 = rgn1->Buffer;
3774 tRect2 = rgn2->Buffer;
3775
3776 if ((tRect1 == NULL) || (tRect2 == NULL))
3777 goto exit;
3778
3779 for (i=0; i < rgn1->rdh.nCount; i++)
3780 {
3781 if ((tRect1[i].left != tRect2[i].left) ||
3782 (tRect1[i].right != tRect2[i].right) ||
3783 (tRect1[i].top != tRect2[i].top) ||
3784 (tRect1[i].bottom != tRect2[i].bottom))
3785 goto exit;
3786 }
3787
3788 bRet = TRUE;
3789
3790 exit:
3791 REGION_UnlockRgn(rgn1);
3792 REGION_UnlockRgn(rgn2);
3793 return bRet;
3794 }
3795
3796 HRGN
3797 APIENTRY
3798 NtGdiExtCreateRegion(
3799 OPTIONAL LPXFORM Xform,
3800 DWORD Count,
3801 LPRGNDATA RgnData)
3802 {
3803 HRGN hRgn;
3804 PREGION Region;
3805 DWORD nCount = 0;
3806 DWORD iType = 0;
3807 DWORD dwSize = 0;
3808 UINT i;
3809 RECT* rects;
3810 NTSTATUS Status = STATUS_SUCCESS;
3811 MATRIX matrix;
3812 XFORMOBJ xo;
3813
3814 DPRINT("NtGdiExtCreateRegion\n");
3815 _SEH2_TRY
3816 {
3817 ProbeForRead(RgnData, Count, 1);
3818 nCount = RgnData->rdh.nCount;
3819 iType = RgnData->rdh.iType;
3820 dwSize = RgnData->rdh.dwSize;
3821 rects = (RECT*)RgnData->Buffer;
3822 }
3823 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3824 {
3825 Status = _SEH2_GetExceptionCode();
3826 }
3827 _SEH2_END;
3828
3829 if (!NT_SUCCESS(Status))
3830 {
3831 SetLastNtError(Status);
3832 return NULL;
3833 }
3834
3835 /* Check parameters, but don't set last error here */
3836 if ((Count < sizeof(RGNDATAHEADER) + nCount * sizeof(RECT)) ||
3837 (iType != RDH_RECTANGLES) ||
3838 (dwSize != sizeof(RGNDATAHEADER)))
3839 {
3840 return NULL;
3841 }
3842
3843 Region = REGION_AllocUserRgnWithHandle(nCount);
3844
3845 if (Region == NULL)
3846 {
3847 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
3848 return FALSE;
3849 }
3850 hRgn = Region->BaseObject.hHmgr;
3851
3852 _SEH2_TRY
3853 {
3854 /* Insert the rectangles one by one */
3855 for(i=0; i<nCount; i++)
3856 {
3857 REGION_UnionRectWithRgn(Region, &rects[i]);
3858 }
3859
3860 if (Xform != NULL)
3861 {
3862 ULONG ret;
3863
3864 /* Init the XFORMOBJ from the Xform struct */
3865 Status = STATUS_INVALID_PARAMETER;
3866 XFORMOBJ_vInit(&xo, &matrix);
3867 ret = XFORMOBJ_iSetXform(&xo, (XFORML*)Xform);
3868
3869 /* Check for error */
3870 if (ret != DDI_ERROR)
3871 {
3872 /* Apply the coordinate transformation on the rects */
3873 if (XFORMOBJ_bApplyXform(&xo,
3874 XF_LTOL,
3875 Region->rdh.nCount * 2,
3876 Region->Buffer,
3877 Region->Buffer))
3878 {
3879 Status = STATUS_SUCCESS;
3880 }
3881 }
3882 }
3883 }
3884 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3885 {
3886 Status = _SEH2_GetExceptionCode();
3887 }
3888 _SEH2_END;
3889 if (!NT_SUCCESS(Status))
3890 {
3891 EngSetLastError(ERROR_INVALID_PARAMETER);
3892 REGION_UnlockRgn(Region);
3893 GreDeleteObject(hRgn);
3894 return NULL;
3895 }
3896
3897 REGION_UnlockRgn(Region);
3898
3899 return hRgn;
3900 }
3901
3902 INT
3903 APIENTRY
3904 NtGdiGetRgnBox(
3905 HRGN hRgn,
3906 PRECTL pRect)
3907 {
3908 PREGION Rgn;
3909 RECTL SafeRect;
3910 DWORD ret;
3911 NTSTATUS Status = STATUS_SUCCESS;
3912
3913 Rgn = REGION_LockRgn(hRgn);
3914 if (Rgn == NULL)
3915 {
3916 return ERROR;
3917 }
3918
3919 ret = REGION_GetRgnBox(Rgn, &SafeRect);
3920 REGION_UnlockRgn(Rgn);
3921 if (ret == ERROR)
3922 {
3923 return ret;
3924 }
3925
3926 _SEH2_TRY
3927 {
3928 ProbeForWrite(pRect, sizeof(RECT), 1);
3929 *pRect = SafeRect;
3930 }
3931 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3932 {
3933 Status = _SEH2_GetExceptionCode();
3934 }
3935 _SEH2_END;
3936 if (!NT_SUCCESS(Status))
3937 {
3938 return ERROR;
3939 }
3940
3941 return ret;
3942 }
3943
3944 INT
3945 APIENTRY
3946 NtGdiOffsetRgn(
3947 _In_ HRGN hrgn,
3948 _In_ INT cx,
3949 _In_ INT cy)
3950 {
3951 PREGION prgn;
3952 INT iResult;
3953
3954 DPRINT("NtGdiOffsetRgn: hrgn %p cx %d cy %d\n", hrgn, cx, cy);
3955
3956 /* Lock the region */
3957 prgn = REGION_LockRgn(hrgn);
3958 if (prgn == NULL)
3959 {
3960 DPRINT1("NtGdiOffsetRgn: failed to lock region %p\n", hrgn);
3961 return ERROR;
3962 }
3963
3964 /* Call the internal function */
3965 if (!REGION_bOffsetRgn(prgn, cx, cy))
3966 {
3967 iResult = ERROR;
3968 }
3969 else
3970 {
3971 iResult = REGION_Complexity(prgn);
3972 }
3973
3974 /* Unlock and return the result */
3975 REGION_UnlockRgn(prgn);
3976 return iResult;
3977 }
3978
3979 BOOL
3980 APIENTRY
3981 NtGdiPtInRegion(
3982 _In_ HRGN hrgn,
3983 _In_ INT x,
3984 _In_ INT y)
3985 {
3986 PREGION prgn;
3987 BOOL bResult;
3988
3989 /* Lock the region */
3990 prgn = REGION_LockRgn(hrgn);
3991 if (prgn == NULL)
3992 {
3993 DPRINT1("NtGdiPtInRegion: hrgn error\n");
3994 return FALSE;
3995 }
3996
3997 /* Call the internal function */
3998 bResult = REGION_PtInRegion(prgn, x, y);
3999
4000 /* Unlock and return the result */
4001 REGION_UnlockRgn(prgn);
4002 return bResult;
4003 }
4004
4005 __kernel_entry
4006 BOOL
4007 APIENTRY
4008 NtGdiRectInRegion(
4009 _In_ HRGN hrgn,
4010 _Inout_ LPRECT prclUnsafe)
4011 {
4012 RECTL rcTemp;
4013
4014 /* Probe and copy the rect */
4015 _SEH2_TRY
4016 {
4017 ProbeForRead(prclUnsafe, sizeof(RECT), 1);
4018 rcTemp = *prclUnsafe;
4019 }
4020 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4021 {
4022 DPRINT1("NtGdiRectInRegion: Exception accessing the rect\n");
4023 return FALSE;
4024 }
4025 _SEH2_END;
4026
4027 /* Call the internal function */
4028 return IntRectInRegion(hrgn, &rcTemp);
4029 }
4030
4031 BOOL
4032 APIENTRY
4033 NtGdiSetRectRgn(
4034 _In_ HRGN hrgn,
4035 _In_ INT xLeft,
4036 _In_ INT yTop,
4037 _In_ INT xRight,
4038 _In_ INT yBottom)
4039 {
4040 PREGION prgn;
4041
4042 /* Lock the region */
4043 prgn = REGION_LockRgn(hrgn);
4044 if (prgn == NULL)
4045 {
4046 return FALSE;
4047 }
4048
4049 /* Call the internal API */
4050 REGION_SetRectRgn(prgn, xLeft, yTop, xRight, yBottom);
4051
4052 /* Unlock the region and return success */
4053 REGION_UnlockRgn(prgn);
4054 return TRUE;
4055 }
4056
4057 /*!
4058 * MSDN: GetRegionData, Return Values:
4059 *
4060 * "If the function succeeds and dwCount specifies an adequate number of bytes,
4061 * the return value is always dwCount. If dwCount is too small or the function
4062 * fails, the return value is 0. If lpRgnData is NULL, the return value is the
4063 * required number of bytes.
4064 *
4065 * If the function fails, the return value is zero."
4066 */
4067 _Success_(return!=0)
4068 __kernel_entry
4069 ULONG
4070 APIENTRY
4071 NtGdiGetRegionData(
4072 _In_ HRGN hrgn,
4073 _In_ ULONG cjBuffer,
4074 _Out_writes_bytes_to_opt_(cjBuffer, return) LPRGNDATA lpRgnData)
4075 {
4076 ULONG cjRects, cjSize;
4077 PREGION prgn;
4078
4079 /* Lock the region */
4080 prgn = REGION_LockRgn(hrgn);
4081 if (prgn == NULL)
4082 {
4083 EngSetLastError(ERROR_INVALID_HANDLE);
4084 return 0;
4085 }
4086
4087 /* Calculate the region sizes */
4088 cjRects = prgn->rdh.nCount * sizeof(RECT);
4089 cjSize = cjRects + sizeof(RGNDATAHEADER);
4090
4091 /* Check if region data is requested */
4092 if (lpRgnData)
4093 {
4094 /* Check if the buffer is large enough */
4095 if (cjBuffer >= cjSize)
4096 {
4097 /* Probe the buffer and copy the data */
4098 _SEH2_TRY
4099 {
4100 ProbeForWrite(lpRgnData, cjSize, sizeof(ULONG));
4101 RtlCopyMemory(lpRgnData, &prgn->rdh, sizeof(RGNDATAHEADER));
4102 RtlCopyMemory(lpRgnData->Buffer, prgn->Buffer, cjRects);
4103 lpRgnData->rdh.iType = RDH_RECTANGLES;
4104 }
4105 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4106 {
4107 EngSetLastError(ERROR_INVALID_PARAMETER);
4108 cjSize = 0;
4109 }
4110 _SEH2_END;
4111 }
4112 else
4113 {
4114 /* Buffer is too small */
4115 EngSetLastError(ERROR_INVALID_PARAMETER);
4116 cjSize = 0;
4117 }
4118 }
4119
4120 /* Unlock the region and return the size */
4121 REGION_UnlockRgn(prgn);
4122 return cjSize;
4123 }
4124
4125 /* EOF */