xref: /reactos/win32ss/gdi/ntgdi/region.c (revision 845faec4)
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 for zero rectangles and return TRUE for translation only matrices */
2055     if (prgn->rdh.nCount < 1)
2056         return (pmx->flAccel & XFORM_UNITY) != 0;
2057 
2058     /* Check if this is a scaling only matrix (off-diagonal elements are 0 */
2059     if (pmx->flAccel & XFORM_SCALE)
2060     {
2061         /* Check if this is a translation only matrix */
2062         if (pmx->flAccel & XFORM_UNITY)
2063         {
2064             /* Just offset the region */
2065             return REGION_bOffsetRgn(prgn, (pmx->fxDx + 8) / 16, (pmx->fxDy + 8) / 16);
2066         }
2067         else
2068         {
2069             /* Initialize the xform object */
2070             XFORMOBJ_vInit(&xo, pmx);
2071 
2072             /* Scaling can move the rects out of the coordinate space, so
2073              * we first need to check whether we can apply the transformation
2074              * on the bounds rect without modifying the region */
2075             if (!XFORMOBJ_bApplyXform(&xo, XF_LTOL, 2, &prgn->rdh.rcBound, &rect))
2076             {
2077                 return FALSE;
2078             }
2079 
2080             /* Apply the xform to the rects in the region */
2081             if (!XFORMOBJ_bApplyXform(&xo,
2082                                       XF_LTOL,
2083                                       prgn->rdh.nCount * 2,
2084                                       prgn->Buffer,
2085                                       prgn->Buffer))
2086             {
2087                 /* This can not happen, since we already checked the bounds! */
2088                 NT_ASSERT(FALSE);
2089             }
2090 
2091             /* Reset bounds */
2092             RECTL_vSetEmptyRect(&prgn->rdh.rcBound);
2093 
2094             /* Loop all rects in the region */
2095             for (i = 0; i < prgn->rdh.nCount; i++)
2096             {
2097                 /* Make sure the rect is well-ordered after the xform */
2098                 RECTL_vMakeWellOrdered(&prgn->Buffer[i]);
2099 
2100                 /* Update bounds */
2101                 RECTL_bUnionRect(&prgn->rdh.rcBound,
2102                                  &prgn->rdh.rcBound,
2103                                  &prgn->Buffer[i]);
2104             }
2105 
2106             /* Loop all rects in the region */
2107             for (i = 0; i < prgn->rdh.nCount - 1; i++)
2108             {
2109                 for (j = i; i < prgn->rdh.nCount; i++)
2110                 {
2111                     NT_ASSERT(prgn->Buffer[i].top < prgn->Buffer[i].bottom);
2112                     NT_ASSERT(prgn->Buffer[j].top >= prgn->Buffer[i].top);
2113                 }
2114             }
2115 
2116             return TRUE;
2117         }
2118     }
2119     else
2120     {
2121         /* Allocate a buffer for the polygons */
2122         cjSize = prgn->rdh.nCount * (4 * sizeof(POINT) + sizeof(ULONG));
2123         ppt = ExAllocatePoolWithTag(PagedPool, cjSize, GDITAG_REGION);
2124         if (ppt == NULL)
2125         {
2126             return FALSE;
2127         }
2128 
2129         /* Fill the buffer with the rects */
2130         pcPoints = (PULONG)&ppt[4 * prgn->rdh.nCount];
2131         for (i = 0; i < prgn->rdh.nCount; i++)
2132         {
2133             /* Make sure the rect is within the legal range */
2134             pcPoints[i] = 4;
2135             ppt[4 * i + 0].x = prgn->Buffer[i].left;
2136             ppt[4 * i + 0].y = prgn->Buffer[i].top;
2137             ppt[4 * i + 1].x = prgn->Buffer[i].right;
2138             ppt[4 * i + 1].y = prgn->Buffer[i].top;
2139             ppt[4 * i + 2].x = prgn->Buffer[i].right;
2140             ppt[4 * i + 2].y = prgn->Buffer[i].bottom;
2141             ppt[4 * i + 3].x = prgn->Buffer[i].left;
2142             ppt[4 * i + 3].y = prgn->Buffer[i].bottom;
2143         }
2144 
2145         /* Initialize the xform object */
2146         XFORMOBJ_vInit(&xo, pmx);
2147 
2148         /* Apply the xform to the rects in the buffer */
2149         if (!XFORMOBJ_bApplyXform(&xo,
2150                                   XF_LTOL,
2151                                   prgn->rdh.nCount * 2,
2152                                   ppt,
2153                                   ppt))
2154         {
2155             /* This means, there were coordinates that would go outside of
2156                the coordinate space after the transformation */
2157             ExFreePoolWithTag(ppt, GDITAG_REGION);
2158             return FALSE;
2159         }
2160 
2161         /* Now use the polygons to create a polygon region */
2162         bResult = REGION_SetPolyPolygonRgn(prgn,
2163                                            ppt,
2164                                            pcPoints,
2165                                            prgn->rdh.nCount,
2166                                            WINDING);
2167 
2168         /* Free the polygon buffer */
2169         ExFreePoolWithTag(ppt, GDITAG_REGION);
2170 
2171         return bResult;
2172     }
2173 
2174 }
2175 
2176 
2177 PREGION
2178 FASTCALL
2179 REGION_AllocRgnWithHandle(
2180     INT nReg)
2181 {
2182     //HRGN hReg;
2183     PREGION pReg;
2184 
2185     pReg = (PREGION)GDIOBJ_AllocateObject(GDIObjType_RGN_TYPE,
2186                                           sizeof(REGION),
2187                                           BASEFLAG_LOOKASIDE);
2188     if (pReg == NULL)
2189     {
2190         DPRINT1("Could not allocate a palette.\n");
2191         return NULL;
2192     }
2193 
2194     //hReg = pReg->BaseObject.hHmgr;
2195 
2196     if ((nReg == 0) || (nReg == 1))
2197     {
2198         /* Testing shows that > 95% of all regions have only 1 rect.
2199            Including that here saves us from having to do another allocation */
2200         pReg->Buffer = &pReg->rdh.rcBound;
2201     }
2202     else
2203     {
2204         pReg->Buffer = ExAllocatePoolWithTag(PagedPool,
2205                                              nReg * sizeof(RECT),
2206                                              TAG_REGION);
2207         if (pReg->Buffer == NULL)
2208         {
2209             DPRINT1("Could not allocate region buffer\n");
2210             GDIOBJ_vDeleteObject(&pReg->BaseObject);
2211             return NULL;
2212         }
2213     }
2214 
2215     EMPTY_REGION(pReg);
2216     pReg->rdh.dwSize = sizeof(RGNDATAHEADER);
2217     pReg->rdh.nCount = nReg;
2218     pReg->rdh.nRgnSize = nReg * sizeof(RECT);
2219     pReg->prgnattr = &pReg->rgnattr;
2220 
2221     /* Initialize the region attribute */
2222     pReg->rgnattr.AttrFlags = 0;
2223     pReg->rgnattr.iComplexity = SIMPLEREGION;
2224     pReg->rgnattr.Rect = pReg->rdh.rcBound;
2225 
2226     /* Finally insert the region into the handle table */
2227     if (!GDIOBJ_hInsertObject(&pReg->BaseObject, GDI_OBJ_HMGR_POWNED))
2228     {
2229         DPRINT1("Could not insert palette into handle table.\n");
2230         GDIOBJ_vFreeObject(&pReg->BaseObject);
2231         return NULL;
2232     }
2233 
2234     return pReg;
2235 }
2236 
2237 BOOL
2238 NTAPI
2239 REGION_bAllocRgnAttr(
2240     PREGION prgn)
2241 {
2242     PPROCESSINFO ppi;
2243     PRGN_ATTR prgnattr;
2244 
2245     NT_ASSERT(prgn->prgnattr == &prgn->rgnattr);
2246 
2247     ppi = PsGetCurrentProcessWin32Process();
2248     ASSERT(ppi);
2249 
2250     prgnattr = GdiPoolAllocate(ppi->pPoolRgnAttr);
2251     if (prgnattr == NULL)
2252     {
2253         DPRINT1("Could not allocate RGN attr\n");
2254         return FALSE;
2255     }
2256 
2257     /* Copy the current region attribute */
2258     *prgnattr = prgn->rgnattr;
2259 
2260     /* Set the object attribute in the handle table */
2261     prgn->prgnattr = prgnattr;
2262     GDIOBJ_vSetObjectAttr(&prgn->BaseObject, prgnattr);
2263 
2264     return TRUE;
2265 }
2266 
2267 
2268 //
2269 // Allocate User Space Region Handle.
2270 //
2271 PREGION
2272 FASTCALL
2273 REGION_AllocUserRgnWithHandle(
2274     INT nRgn)
2275 {
2276     PREGION prgn;
2277 
2278     prgn = REGION_AllocRgnWithHandle(nRgn);
2279     if (prgn == NULL)
2280     {
2281         return NULL;
2282     }
2283 
2284     if (!REGION_bAllocRgnAttr(prgn))
2285     {
2286         ASSERT(FALSE);
2287     }
2288 
2289     return prgn;
2290 }
2291 
2292 static
2293 VOID
2294 REGION_vSyncRegion(
2295     _In_ PREGION prgn)
2296 {
2297     PRGN_ATTR prgnattr;
2298 
2299     NT_ASSERT(prgn != NULL);
2300     NT_ASSERT(prgn->prgnattr != NULL);
2301     NT_ASSERT((prgn->prgnattr == &prgn->rgnattr) ||
2302               (prgn->prgnattr->AttrFlags & ATTR_RGN_VALID));
2303 
2304     /* Get the region attribute and check if it's dirty (modified) */
2305     prgnattr = prgn->prgnattr;
2306     if (prgnattr->AttrFlags & ATTR_RGN_DIRTY)
2307     {
2308         NT_ASSERT(GreGetObjectOwner(prgn->BaseObject.hHmgr) == GDI_OBJ_HMGR_POWNED);
2309         NT_ASSERT(prgnattr != &prgn->rgnattr);
2310 
2311         if (prgnattr->iComplexity == NULLREGION)
2312         {
2313             EMPTY_REGION(prgn);
2314         }
2315         else if (prgnattr->iComplexity == SIMPLEREGION)
2316         {
2317             REGION_SetRectRgn(prgn,
2318                               prgnattr->Rect.left,
2319                               prgnattr->Rect.top,
2320                               prgnattr->Rect.right,
2321                               prgnattr->Rect.bottom);
2322         }
2323         else
2324         {
2325             /* Should not happen, region attribute is corrupted! */
2326             DPRINT1("Region attribute is corrupted, ignoring\n");
2327             NT_ASSERT(FALSE);
2328         }
2329     }
2330 
2331     /* Reset the flags */
2332     prgnattr->AttrFlags &= ~(ATTR_RGN_DIRTY | ATTR_RGN_VALID);
2333 }
2334 
2335 PREGION
2336 FASTCALL
2337 REGION_LockRgn(
2338     _In_ HRGN hrgn)
2339 {
2340     PREGION prgn;
2341 
2342     prgn = GDIOBJ_LockObject(hrgn, GDIObjType_RGN_TYPE);
2343     if (prgn == NULL)
2344         return NULL;
2345 
2346     REGION_vSyncRegion(prgn);
2347     return prgn;
2348 }
2349 
2350 VOID
2351 FASTCALL
2352 REGION_UnlockRgn(
2353     _In_ PREGION prgn)
2354 {
2355     PRGN_ATTR prgnattr;
2356 
2357     NT_ASSERT(prgn != NULL);
2358     NT_ASSERT(prgn->prgnattr != NULL);
2359 
2360     /* Get the region attribute and check if it's user mode */
2361     prgnattr = prgn->prgnattr;
2362     if (prgnattr != &prgn->rgnattr)
2363     {
2364         NT_ASSERT(GreGetObjectOwner(prgn->BaseObject.hHmgr) == GDI_OBJ_HMGR_POWNED);
2365         prgnattr->iComplexity = REGION_Complexity(prgn);
2366         prgnattr->Rect.left   = prgn->rdh.rcBound.left;
2367         prgnattr->Rect.top    = prgn->rdh.rcBound.top;
2368         prgnattr->Rect.right  = prgn->rdh.rcBound.right;
2369         prgnattr->Rect.bottom = prgn->rdh.rcBound.bottom;
2370         prgnattr->AttrFlags |= ATTR_RGN_VALID;
2371     }
2372 
2373     GDIOBJ_vUnlockObject(&prgn->BaseObject);
2374 }
2375 
2376 /*
2377   System Regions:
2378     These regions do not use attribute sections and when allocated, use gdiobj
2379     level functions.
2380 */
2381 //
2382 // System Region Functions
2383 //
2384 PREGION
2385 FASTCALL
2386 IntSysCreateRectpRgn(
2387     INT LeftRect,
2388     INT TopRect,
2389     INT RightRect,
2390     INT BottomRect)
2391 {
2392     PREGION prgn;
2393 
2394     /* Allocate a region, without a handle */
2395     prgn = (PREGION)GDIOBJ_AllocateObject(GDIObjType_RGN_TYPE, sizeof(REGION), BASEFLAG_LOOKASIDE);
2396     if (prgn == NULL)
2397     {
2398         return NULL;
2399     }
2400 
2401     /* Initialize it */
2402     prgn->Buffer = &prgn->rdh.rcBound;
2403     prgn->prgnattr = &prgn->rgnattr;
2404     prgn->prgnattr->AttrFlags = ATTR_RGN_VALID;
2405     REGION_SetRectRgn(prgn, LeftRect, TopRect, RightRect, BottomRect);
2406 
2407     return prgn;
2408 }
2409 
2410 VOID
2411 NTAPI
2412 REGION_vCleanup(PVOID ObjectBody)
2413 {
2414     PREGION pRgn = (PREGION)ObjectBody;
2415     PPROCESSINFO ppi = PsGetCurrentProcessWin32Process();
2416     ASSERT(ppi);
2417 
2418     ASSERT(pRgn->prgnattr);
2419     if (pRgn->prgnattr != &pRgn->rgnattr)
2420         GdiPoolFree(ppi->pPoolRgnAttr, pRgn->prgnattr);
2421 
2422     if (pRgn->Buffer && pRgn->Buffer != &pRgn->rdh.rcBound)
2423         ExFreePoolWithTag(pRgn->Buffer, TAG_REGION);
2424 }
2425 
2426 VOID
2427 FASTCALL
2428 REGION_Delete(PREGION pRgn)
2429 {
2430     if (pRgn == prgnDefault)
2431         return;
2432 
2433     GDIOBJ_vDeleteObject(&pRgn->BaseObject);
2434 }
2435 
2436 BOOL
2437 FASTCALL
2438 IntGdiSetRegionOwner(HRGN hRgn, DWORD OwnerMask)
2439 {
2440     PREGION prgn;
2441     PRGN_ATTR prgnattr;
2442     PPROCESSINFO ppi;
2443 
2444     prgn = REGION_LockRgn(hRgn);
2445     if (prgn == NULL)
2446     {
2447         return FALSE;
2448     }
2449 
2450     prgnattr = prgn->prgnattr;
2451     if (prgnattr != &prgn->rgnattr)
2452     {
2453         GDIOBJ_vSetObjectAttr(&prgn->BaseObject, NULL);
2454         prgn->prgnattr = &prgn->rgnattr;
2455         ppi = PsGetCurrentProcessWin32Process();
2456         GdiPoolFree(ppi->pPoolRgnAttr, prgnattr);
2457     }
2458 
2459     REGION_UnlockRgn(prgn);
2460 
2461     return GreSetObjectOwner(hRgn, OwnerMask);
2462 }
2463 
2464 INT
2465 FASTCALL
2466 IntGdiCombineRgn(
2467     PREGION prgnDest,
2468     PREGION prgnSrc1,
2469     PREGION prgnSrc2,
2470     INT iCombineMode)
2471 {
2472 
2473     if (prgnDest == NULL)
2474     {
2475         DPRINT("IntGdiCombineRgn: hDest unavailable\n");
2476         return ERROR;
2477     }
2478 
2479     if (prgnSrc1 == NULL)
2480     {
2481         DPRINT("IntGdiCombineRgn: hSrc1 unavailable\n");
2482         return ERROR;
2483     }
2484 
2485     if (iCombineMode == RGN_COPY)
2486     {
2487         if (!REGION_CopyRegion(prgnDest, prgnSrc1))
2488             return ERROR;
2489 
2490         return REGION_Complexity(prgnDest);
2491     }
2492 
2493     if (prgnSrc2 == NULL)
2494     {
2495         DPRINT1("IntGdiCombineRgn requires hSrc2 != NULL for combine mode %d!\n", iCombineMode);
2496         ASSERT(FALSE);
2497         return ERROR;
2498     }
2499 
2500     switch (iCombineMode)
2501     {
2502         case RGN_AND:
2503             REGION_IntersectRegion(prgnDest, prgnSrc1, prgnSrc2);
2504             break;
2505         case RGN_OR:
2506             REGION_UnionRegion(prgnDest, prgnSrc1, prgnSrc2);
2507             break;
2508         case RGN_XOR:
2509             REGION_XorRegion(prgnDest, prgnSrc1, prgnSrc2);
2510             break;
2511         case RGN_DIFF:
2512             REGION_SubtractRegion(prgnDest, prgnSrc1, prgnSrc2);
2513             break;
2514     }
2515 
2516     return REGION_Complexity(prgnDest);
2517 }
2518 
2519 INT
2520 FASTCALL
2521 REGION_GetRgnBox(
2522     PREGION Rgn,
2523     PRECTL pRect)
2524 {
2525     DWORD ret;
2526 
2527     if (Rgn != NULL)
2528     {
2529         *pRect = Rgn->rdh.rcBound;
2530         ret = REGION_Complexity(Rgn);
2531 
2532         return ret;
2533     }
2534     return 0; // If invalid region return zero
2535 }
2536 
2537 INT
2538 APIENTRY
2539 IntGdiGetRgnBox(
2540     HRGN hRgn,
2541     PRECTL pRect)
2542 {
2543     PREGION Rgn;
2544     DWORD ret;
2545 
2546     Rgn = REGION_LockRgn(hRgn);
2547     if (Rgn == NULL)
2548     {
2549         return ERROR;
2550     }
2551 
2552     ret = REGION_GetRgnBox(Rgn, pRect);
2553     REGION_UnlockRgn(Rgn);
2554 
2555     return ret;
2556 }
2557 
2558 
2559 BOOL
2560 FASTCALL
2561 REGION_PtInRegion(
2562     PREGION prgn,
2563     INT X,
2564     INT Y)
2565 {
2566     ULONG i;
2567     PRECT r;
2568 
2569     if (prgn->rdh.nCount > 0 && INRECT(prgn->rdh.rcBound, X, Y))
2570     {
2571         r =  prgn->Buffer;
2572         for (i = 0; i < prgn->rdh.nCount; i++)
2573         {
2574             if (INRECT(r[i], X, Y))
2575                 return TRUE;
2576         }
2577     }
2578 
2579     return FALSE;
2580 }
2581 
2582 BOOL
2583 FASTCALL
2584 REGION_RectInRegion(
2585     PREGION Rgn,
2586     const RECTL *rect)
2587 {
2588     PRECTL pCurRect, pRectEnd;
2589     RECT rc;
2590 
2591     /* Swap the coordinates to make right >= left and bottom >= top */
2592     /* (region building rectangles are normalized the same way) */
2593     if (rect->top > rect->bottom)
2594     {
2595         rc.top = rect->bottom;
2596         rc.bottom = rect->top;
2597     }
2598     else
2599     {
2600         rc.top = rect->top;
2601         rc.bottom = rect->bottom;
2602     }
2603 
2604     if (rect->right < rect->left)
2605     {
2606         rc.right = rect->left;
2607         rc.left = rect->right;
2608     }
2609     else
2610     {
2611         rc.right = rect->right;
2612         rc.left = rect->left;
2613     }
2614 
2615     /* This is (just) a useful optimization */
2616     if ((Rgn->rdh.nCount > 0) && EXTENTCHECK(&Rgn->rdh.rcBound, &rc))
2617     {
2618         for (pCurRect = Rgn->Buffer, pRectEnd = pCurRect +
2619                                                 Rgn->rdh.nCount; pCurRect < pRectEnd; pCurRect++)
2620         {
2621             if (pCurRect->bottom <= rc.top)
2622                 continue;             /* Not far enough down yet */
2623 
2624             if (pCurRect->top >= rc.bottom)
2625                 break;                /* Too far down */
2626 
2627             if (pCurRect->right <= rc.left)
2628                 continue;              /* Not far enough over yet */
2629 
2630             if (pCurRect->left >= rc.right)
2631             {
2632                 continue;
2633             }
2634 
2635             return TRUE;
2636         }
2637     }
2638 
2639     return FALSE;
2640 }
2641 
2642 VOID
2643 FASTCALL
2644 REGION_SetRectRgn(
2645     PREGION rgn,
2646     INT LeftRect,
2647     INT TopRect,
2648     INT RightRect,
2649     INT BottomRect)
2650 {
2651     PRECTL firstRect;
2652 
2653     if (LeftRect > RightRect)
2654     {
2655         INT tmp = LeftRect;
2656         LeftRect = RightRect;
2657         RightRect = tmp;
2658     }
2659 
2660     if (TopRect > BottomRect)
2661     {
2662         INT tmp = TopRect;
2663         TopRect = BottomRect;
2664         BottomRect = tmp;
2665     }
2666 
2667     if ((LeftRect != RightRect) && (TopRect != BottomRect))
2668     {
2669         firstRect = rgn->Buffer;
2670         ASSERT(firstRect);
2671         firstRect->left = rgn->rdh.rcBound.left = LeftRect;
2672         firstRect->top = rgn->rdh.rcBound.top = TopRect;
2673         firstRect->right = rgn->rdh.rcBound.right = RightRect;
2674         firstRect->bottom = rgn->rdh.rcBound.bottom = BottomRect;
2675         rgn->rdh.nCount = 1;
2676         rgn->rdh.iType = RDH_RECTANGLES;
2677     }
2678     else
2679     {
2680         EMPTY_REGION(rgn);
2681     }
2682 }
2683 
2684 BOOL
2685 FASTCALL
2686 REGION_bOffsetRgn(
2687     _Inout_ PREGION prgn,
2688     _In_ INT cx,
2689     _In_ INT cy)
2690 {
2691     PRECTL prcl;
2692     UINT i;
2693 
2694     NT_ASSERT(prgn != NULL);
2695 
2696     /* Check for trivial case */
2697     if ((cx == 0) && (cy == 0))
2698     {
2699         return TRUE;
2700     }
2701 
2702     /* Check for empty regions, we ignore the offset values here */
2703     if (prgn->rdh.nCount == 0)
2704     {
2705         return TRUE;
2706     }
2707 
2708     /* Make sure the offset is within the legal range */
2709     if ((cx > MAX_COORD) || (cx < MIN_COORD) ||
2710         (cy > MAX_COORD) || (cy < MIN_COORD))
2711     {
2712         return FALSE;
2713     }
2714 
2715     /* Are we moving right? */
2716     if (cx > 0)
2717     {
2718         /* Check if we stay inside the bounds on the right side */
2719         if (prgn->rdh.rcBound.right > (MAX_COORD - cx))
2720         {
2721             return FALSE;
2722         }
2723     }
2724     else
2725     {
2726         /* Check if we stay inside the bounds on the left side */
2727         if (prgn->rdh.rcBound.left < (MIN_COORD - cx))
2728         {
2729             return FALSE;
2730         }
2731     }
2732 
2733     /* Are we moving down? */
2734     if (cy > 0)
2735     {
2736         /* Check if we stay inside the bounds on the right side */
2737         if (prgn->rdh.rcBound.bottom > (MAX_COORD - cy))
2738         {
2739             return FALSE;
2740         }
2741     }
2742     else
2743     {
2744         /* Check if we stay inside the bounds on the left side */
2745         if (prgn->rdh.rcBound.top < (MIN_COORD - cy))
2746         {
2747             return FALSE;
2748         }
2749     }
2750 
2751     /* Loop to move the rects */
2752     prcl = prgn->Buffer;
2753     for (i = 0; i < prgn->rdh.nCount; i++)
2754     {
2755         prcl[i].left += cx;
2756         prcl[i].right += cx;
2757         prcl[i].top += cy;
2758         prcl[i].bottom += cy;
2759     }
2760 
2761     /* Finally update the bounds rect */
2762     if (prgn->Buffer != &prgn->rdh.rcBound)
2763     {
2764         prgn->rdh.rcBound.left += cx;
2765         prgn->rdh.rcBound.right += cx;
2766         prgn->rdh.rcBound.top += cy;
2767         prgn->rdh.rcBound.bottom += cy;
2768     }
2769 
2770     return TRUE;
2771 }
2772 
2773 /***********************************************************************
2774  *     REGION_InsertEdgeInET
2775  *
2776  *     Insert the given edge into the edge table.
2777  *     First we must find the correct bucket in the
2778  *     Edge table, then find the right slot in the
2779  *     bucket.  Finally, we can insert it.
2780  *
2781  */
2782 static
2783 VOID
2784 FASTCALL
2785 REGION_InsertEdgeInET(
2786     EDGE_TABLE *ET,
2787     EDGE_TABLE_ENTRY *ETE,
2788     INT scanline,
2789     SCANLINE_LISTBLOCK **SLLBlock,
2790     INT *iSLLBlock)
2791 {
2792     EDGE_TABLE_ENTRY *start, *prev;
2793     SCANLINE_LIST *pSLL, *pPrevSLL;
2794     SCANLINE_LISTBLOCK *tmpSLLBlock;
2795 
2796     /* Find the right bucket to put the edge into */
2797     pPrevSLL = &ET->scanlines;
2798     pSLL = pPrevSLL->next;
2799     while (pSLL && (pSLL->scanline < scanline))
2800     {
2801         pPrevSLL = pSLL;
2802         pSLL = pSLL->next;
2803     }
2804 
2805     /* Reassign pSLL (pointer to SCANLINE_LIST) if necessary */
2806     if ((!pSLL) || (pSLL->scanline > scanline))
2807     {
2808         if (*iSLLBlock > SLLSPERBLOCK-1)
2809         {
2810             tmpSLLBlock = ExAllocatePoolWithTag(PagedPool,
2811                                                 sizeof(SCANLINE_LISTBLOCK),
2812                                                 TAG_REGION);
2813             if (tmpSLLBlock == NULL)
2814             {
2815                 DPRINT1("REGION_InsertEdgeInETL(): Can't alloc SLLB\n");
2816                 /* FIXME: Free resources? */
2817                 return;
2818             }
2819 
2820             (*SLLBlock)->next = tmpSLLBlock;
2821             tmpSLLBlock->next = (SCANLINE_LISTBLOCK *)NULL;
2822             *SLLBlock = tmpSLLBlock;
2823             *iSLLBlock = 0;
2824         }
2825 
2826         pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
2827 
2828         pSLL->next = pPrevSLL->next;
2829         pSLL->edgelist = (EDGE_TABLE_ENTRY *)NULL;
2830         pPrevSLL->next = pSLL;
2831     }
2832 
2833     pSLL->scanline = scanline;
2834 
2835     /* Now insert the edge in the right bucket */
2836     prev = (EDGE_TABLE_ENTRY *)NULL;
2837     start = pSLL->edgelist;
2838     while (start && (start->bres.minor_axis < ETE->bres.minor_axis))
2839     {
2840         prev = start;
2841         start = start->next;
2842     }
2843 
2844     ETE->next = start;
2845 
2846     if (prev)
2847         prev->next = ETE;
2848     else
2849         pSLL->edgelist = ETE;
2850 }
2851 
2852 /***********************************************************************
2853  *     REGION_loadAET
2854  *
2855  *     This routine moves EDGE_TABLEEntries from the
2856  *     EDGE_TABLE into the Active Edge Table,
2857  *     leaving them sorted by smaller x coordinate.
2858  *
2859  */
2860 static
2861 VOID
2862 FASTCALL
2863 REGION_loadAET(
2864     EDGE_TABLE_ENTRY *AET,
2865     EDGE_TABLE_ENTRY *ETEs)
2866 {
2867     EDGE_TABLE_ENTRY *pPrevAET;
2868     EDGE_TABLE_ENTRY *tmp;
2869 
2870     pPrevAET = AET;
2871     AET = AET->next;
2872     while (ETEs)
2873     {
2874         while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis))
2875         {
2876             pPrevAET = AET;
2877             AET = AET->next;
2878         }
2879 
2880         tmp = ETEs->next;
2881         ETEs->next = AET;
2882         if (AET)
2883             AET->back = ETEs;
2884 
2885         ETEs->back = pPrevAET;
2886         pPrevAET->next = ETEs;
2887         pPrevAET = ETEs;
2888 
2889         ETEs = tmp;
2890     }
2891 }
2892 
2893 /***********************************************************************
2894  *     REGION_computeWAET
2895  *
2896  *     This routine links the AET by the
2897  *     nextWETE (winding EDGE_TABLE_ENTRY) link for
2898  *     use by the winding number rule.  The final
2899  *     Active Edge Table (AET) might look something
2900  *     like:
2901  *
2902  *     AET
2903  *     ----------  ---------   ---------
2904  *     |ymax    |  |ymax    |  |ymax    |
2905  *     | ...    |  |...     |  |...     |
2906  *     |next    |->|next    |->|next    |->...
2907  *     |nextWETE|  |nextWETE|  |nextWETE|
2908  *     ---------   ---------   ^--------
2909  *         |                   |       |
2910  *         V------------------->       V---> ...
2911  *
2912  */
2913 static
2914 VOID
2915 FASTCALL
2916 REGION_computeWAET(
2917     EDGE_TABLE_ENTRY *AET)
2918 {
2919     register EDGE_TABLE_ENTRY *pWETE;
2920     register INT inside = 1;
2921     register INT isInside = 0;
2922 
2923     AET->nextWETE = (EDGE_TABLE_ENTRY *)NULL;
2924     pWETE = AET;
2925     AET = AET->next;
2926     while (AET)
2927     {
2928         if (AET->ClockWise)
2929             isInside++;
2930         else
2931             isInside--;
2932 
2933         if ((!inside && !isInside) ||
2934             ( inside &&  isInside))
2935         {
2936             pWETE->nextWETE = AET;
2937             pWETE = AET;
2938             inside = !inside;
2939         }
2940         AET = AET->next;
2941     }
2942 
2943     pWETE->nextWETE = (EDGE_TABLE_ENTRY *)NULL;
2944 }
2945 
2946 /***********************************************************************
2947  *     REGION_InsertionSort
2948  *
2949  *     Just a simple insertion sort using
2950  *     pointers and back pointers to sort the Active
2951  *     Edge Table.
2952  *
2953  */
2954 static
2955 BOOL
2956 FASTCALL
2957 REGION_InsertionSort(
2958     EDGE_TABLE_ENTRY *AET)
2959 {
2960     EDGE_TABLE_ENTRY *pETEchase;
2961     EDGE_TABLE_ENTRY *pETEinsert;
2962     EDGE_TABLE_ENTRY *pETEchaseBackTMP;
2963     BOOL changed = FALSE;
2964 
2965     AET = AET->next;
2966     while (AET)
2967     {
2968         pETEinsert = AET;
2969         pETEchase = AET;
2970         while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
2971             pETEchase = pETEchase->back;
2972 
2973         AET = AET->next;
2974         if (pETEchase != pETEinsert)
2975         {
2976             pETEchaseBackTMP = pETEchase->back;
2977             pETEinsert->back->next = AET;
2978             if (AET)
2979                 AET->back = pETEinsert->back;
2980 
2981             pETEinsert->next = pETEchase;
2982             pETEchase->back->next = pETEinsert;
2983             pETEchase->back = pETEinsert;
2984             pETEinsert->back = pETEchaseBackTMP;
2985             changed = TRUE;
2986         }
2987     }
2988 
2989     return changed;
2990 }
2991 
2992 /***********************************************************************
2993  *     REGION_FreeStorage
2994  *
2995  *     Clean up our act.
2996  */
2997 static
2998 VOID
2999 FASTCALL
3000 REGION_FreeStorage(
3001     SCANLINE_LISTBLOCK *pSLLBlock)
3002 {
3003     SCANLINE_LISTBLOCK   *tmpSLLBlock;
3004 
3005     while (pSLLBlock)
3006     {
3007         tmpSLLBlock = pSLLBlock->next;
3008         ExFreePoolWithTag(pSLLBlock, TAG_REGION);
3009         pSLLBlock = tmpSLLBlock;
3010     }
3011 }
3012 
3013 
3014 /***********************************************************************
3015  *     REGION_PtsToRegion
3016  *
3017  *     Create an array of rectangles from a list of points.
3018  */
3019 static
3020 INT
3021 FASTCALL
3022 REGION_PtsToRegion(
3023     INT numFullPtBlocks,
3024     INT iCurPtBlock,
3025     POINTBLOCK *FirstPtBlock,
3026     PREGION reg)
3027 {
3028     RECTL *rects;
3029     POINT *pts;
3030     POINTBLOCK *CurPtBlock;
3031     INT i;
3032     RECTL *extents, *temp;
3033     INT numRects;
3034 
3035     extents = &reg->rdh.rcBound;
3036 
3037     numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
3038 
3039     /* Make sure, we have at least one rect */
3040     if (numRects == 0)
3041     {
3042         numRects = 1;
3043     }
3044 
3045     temp = ExAllocatePoolWithTag(PagedPool, numRects * sizeof(RECT), TAG_REGION);
3046     if (temp == NULL)
3047     {
3048         return 0;
3049     }
3050 
3051     if (reg->Buffer != NULL)
3052     {
3053         COPY_RECTS(temp, reg->Buffer, reg->rdh.nCount);
3054         if (reg->Buffer != &reg->rdh.rcBound)
3055             ExFreePoolWithTag(reg->Buffer, TAG_REGION);
3056     }
3057     reg->Buffer = temp;
3058 
3059     reg->rdh.nCount = numRects;
3060     CurPtBlock = FirstPtBlock;
3061     rects = reg->Buffer - 1;
3062     numRects = 0;
3063     extents->left = LARGE_COORDINATE,  extents->right = SMALL_COORDINATE;
3064 
3065     for ( ; numFullPtBlocks >= 0; numFullPtBlocks--)
3066     {
3067         /* The loop uses 2 points per iteration */
3068         i = NUMPTSTOBUFFER >> 1;
3069         if (numFullPtBlocks == 0)
3070             i = iCurPtBlock >> 1;
3071 
3072         for (pts = CurPtBlock->pts; i--; pts += 2)
3073         {
3074             if (pts->x == pts[1].x)
3075                 continue;
3076 
3077             if ((numRects && pts->x == rects->left) &&
3078                 (pts->y == rects->bottom) &&
3079                 (pts[1].x == rects->right) &&
3080                 ((numRects == 1) || (rects[-1].top != rects->top)) &&
3081                 (i && pts[2].y > pts[1].y))
3082             {
3083                 rects->bottom = pts[1].y + 1;
3084                 continue;
3085             }
3086 
3087             numRects++;
3088             rects++;
3089             rects->left = pts->x;
3090             rects->top = pts->y;
3091             rects->right = pts[1].x;
3092             rects->bottom = pts[1].y + 1;
3093 
3094             if (rects->left < extents->left)
3095                 extents->left = rects->left;
3096             if (rects->right > extents->right)
3097                 extents->right = rects->right;
3098         }
3099 
3100         CurPtBlock = CurPtBlock->next;
3101     }
3102 
3103     if (numRects)
3104     {
3105         extents->top = reg->Buffer->top;
3106         extents->bottom = rects->bottom;
3107     }
3108     else
3109     {
3110         extents->left = 0;
3111         extents->top = 0;
3112         extents->right = 0;
3113         extents->bottom = 0;
3114     }
3115 
3116     reg->rdh.nCount = numRects;
3117 
3118     return(TRUE);
3119 }
3120 
3121 /***********************************************************************
3122  *     REGION_CreateETandAET
3123  *
3124  *     This routine creates the edge table for
3125  *     scan converting polygons.
3126  *     The Edge Table (ET) looks like:
3127  *
3128  *    EDGE_TABLE
3129  *     --------
3130  *    |  ymax  |        SCANLINE_LISTs
3131  *    |scanline|-->------------>-------------->...
3132  *     --------   |scanline|   |scanline|
3133  *                |edgelist|   |edgelist|
3134  *                ---------    ---------
3135  *                    |             |
3136  *                    |             |
3137  *                    V             V
3138  *              list of ETEs   list of ETEs
3139  *
3140  *     where ETE is an EDGE_TABLE_ENTRY data structure,
3141  *     and there is one SCANLINE_LIST per scanline at
3142  *     which an edge is initially entered.
3143  *
3144  */
3145 static
3146 VOID
3147 FASTCALL
3148 REGION_CreateETandAET(
3149     const ULONG *Count,
3150     INT nbpolygons,
3151     const POINT *pts,
3152     EDGE_TABLE *ET,
3153     EDGE_TABLE_ENTRY *AET,
3154     EDGE_TABLE_ENTRY *pETEs,
3155     SCANLINE_LISTBLOCK *pSLLBlock)
3156 {
3157     const POINT *top, *bottom;
3158     const POINT *PrevPt, *CurrPt, *EndPt;
3159     INT poly, count;
3160     INT iSLLBlock = 0;
3161     INT dy;
3162 
3163     /* Initialize the Active Edge Table */
3164     AET->next = (EDGE_TABLE_ENTRY *)NULL;
3165     AET->back = (EDGE_TABLE_ENTRY *)NULL;
3166     AET->nextWETE = (EDGE_TABLE_ENTRY *)NULL;
3167     AET->bres.minor_axis = SMALL_COORDINATE;
3168 
3169     /* Initialize the Edge Table. */
3170     ET->scanlines.next = (SCANLINE_LIST *)NULL;
3171     ET->ymax = SMALL_COORDINATE;
3172     ET->ymin = LARGE_COORDINATE;
3173     pSLLBlock->next = (SCANLINE_LISTBLOCK *)NULL;
3174 
3175     EndPt = pts - 1;
3176     for (poly = 0; poly < nbpolygons; poly++)
3177     {
3178         count = Count[poly];
3179         EndPt += count;
3180         if (count < 2)
3181             continue;
3182 
3183         PrevPt = EndPt;
3184 
3185         /*  For each vertex in the array of points.
3186          *  In this loop we are dealing with two vertices at
3187          *  a time -- these make up one edge of the polygon. */
3188         while (count--)
3189         {
3190             CurrPt = pts++;
3191 
3192             /*  Find out which point is above and which is below. */
3193             if (PrevPt->y > CurrPt->y)
3194             {
3195                 bottom = PrevPt, top = CurrPt;
3196                 pETEs->ClockWise = 0;
3197             }
3198             else
3199             {
3200                 bottom = CurrPt, top = PrevPt;
3201                 pETEs->ClockWise = 1;
3202             }
3203 
3204             /* Don't add horizontal edges to the Edge table. */
3205             if (bottom->y != top->y)
3206             {
3207                 /* -1 so we don't get last scanline */
3208                 pETEs->ymax = bottom->y - 1;
3209 
3210                 /*  Initialize integer edge algorithm */
3211                 dy = bottom->y - top->y;
3212                 BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres);
3213 
3214                 REGION_InsertEdgeInET(ET,
3215                                       pETEs,
3216                                       top->y,
3217                                       &pSLLBlock,
3218                                       &iSLLBlock);
3219 
3220                 if (PrevPt->y > ET->ymax)
3221                     ET->ymax = PrevPt->y;
3222                 if (PrevPt->y < ET->ymin)
3223                     ET->ymin = PrevPt->y;
3224                 pETEs++;
3225             }
3226 
3227             PrevPt = CurrPt;
3228         }
3229     }
3230 }
3231 
3232 BOOL
3233 FASTCALL
3234 REGION_SetPolyPolygonRgn(
3235     _Inout_ PREGION prgn,
3236     _In_ const POINT *ppt,
3237     _In_ const ULONG *pcPoints,
3238     _In_ ULONG cPolygons,
3239     _In_ INT iMode)
3240 {
3241     EDGE_TABLE_ENTRY *pAET;               /* Active Edge Table        */
3242     INT y;                                /* Current scanline         */
3243     INT iPts = 0;                         /* Number of pts in buffer  */
3244     EDGE_TABLE_ENTRY *pWETE;              /* Winding Edge Table Entry */
3245     SCANLINE_LIST *pSLL;                  /* Current SCANLINE_LIST    */
3246     POINT *pts;                           /* Output buffer            */
3247     EDGE_TABLE_ENTRY *pPrevAET;           /* Pointer to previous AET  */
3248     EDGE_TABLE ET;                        /* Header node for ET       */
3249     EDGE_TABLE_ENTRY AET;                 /* Header node for AET      */
3250     EDGE_TABLE_ENTRY *pETEs;              /* EDGE_TABLEEntries pool   */
3251     SCANLINE_LISTBLOCK SLLBlock;          /* Header for SCANLINE_LIST */
3252     INT fixWAET = FALSE;
3253     POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers          */
3254     POINTBLOCK *tmpPtBlock;
3255     UINT numFullPtBlocks = 0;
3256     UINT poly, total;
3257 
3258     /* Check if iMode is valid */
3259     if ((iMode != ALTERNATE) && (iMode != WINDING))
3260     {
3261         DPRINT1("Invalid iMode: %lu\n", iMode);
3262         return FALSE;
3263     }
3264 
3265     /* Special case a rectangle */
3266     if (((cPolygons == 1) && ((pcPoints[0] == 4) ||
3267          ((pcPoints[0] == 5) && (ppt[4].x == ppt[0].x) && (ppt[4].y == ppt[0].y)))) &&
3268         (((ppt[0].y == ppt[1].y) &&
3269           (ppt[1].x == ppt[2].x) &&
3270           (ppt[2].y == ppt[3].y) &&
3271           (ppt[3].x == ppt[0].x)) ||
3272          ((ppt[0].x == ppt[1].x) &&
3273           (ppt[1].y == ppt[2].y) &&
3274           (ppt[2].x == ppt[3].x) &&
3275           (ppt[3].y == ppt[0].y))))
3276     {
3277         REGION_SetRectRgn(prgn,
3278                           min(ppt[0].x, ppt[2].x),
3279                           min(ppt[0].y, ppt[2].y),
3280                           max(ppt[0].x, ppt[2].x),
3281                           max(ppt[0].y, ppt[2].y));
3282         return TRUE;
3283     }
3284 
3285     for (poly = total = 0; poly < cPolygons; poly++)
3286         total += pcPoints[poly];
3287 
3288     pETEs = ExAllocatePoolWithTag(PagedPool,
3289                                   sizeof(EDGE_TABLE_ENTRY) * total,
3290                                   TAG_REGION);
3291     if (pETEs == NULL)
3292     {
3293         DPRINT1("Failed to allocate %lu edge entries\n", total);
3294         return FALSE;
3295     }
3296 
3297     pts = FirstPtBlock.pts;
3298     REGION_CreateETandAET(pcPoints, cPolygons, ppt, &ET, &AET, pETEs, &SLLBlock);
3299     pSLL = ET.scanlines.next;
3300     curPtBlock = &FirstPtBlock;
3301 
3302     if (iMode != WINDING)
3303     {
3304         /*  For each scanline */
3305         for (y = ET.ymin; y < ET.ymax; y++)
3306         {
3307             /*  Add a new edge to the active edge table when we
3308              *  get to the next edge. */
3309             if (pSLL != NULL && y == pSLL->scanline)
3310             {
3311                 REGION_loadAET(&AET, pSLL->edgelist);
3312                 pSLL = pSLL->next;
3313             }
3314             pPrevAET = &AET;
3315             pAET = AET.next;
3316 
3317             /*  For each active edge */
3318             while (pAET)
3319             {
3320                 pts->x = pAET->bres.minor_axis,  pts->y = y;
3321                 pts++, iPts++;
3322 
3323                 /* Send out the buffer */
3324                 if (iPts == NUMPTSTOBUFFER)
3325                 {
3326                     tmpPtBlock = ExAllocatePoolWithTag(PagedPool,
3327                                                        sizeof(POINTBLOCK),
3328                                                        TAG_REGION);
3329                     if (tmpPtBlock == NULL)
3330                     {
3331                         DPRINT1("Can't alloc tmpPtBlock\n");
3332                         ExFreePoolWithTag(pETEs, TAG_REGION);
3333                         return FALSE;
3334                     }
3335 
3336                     curPtBlock->next = tmpPtBlock;
3337                     curPtBlock = tmpPtBlock;
3338                     pts = curPtBlock->pts;
3339                     numFullPtBlocks++;
3340                     iPts = 0;
3341                 }
3342 
3343                 EVALUATEEDGEEVENODD(pAET, pPrevAET, y);
3344             }
3345 
3346             REGION_InsertionSort(&AET);
3347         }
3348     }
3349     else
3350     {
3351         /* For each scanline */
3352         for (y = ET.ymin; y < ET.ymax; y++)
3353         {
3354             /*  Add a new edge to the active edge table when we
3355              *  get to the next edge. */
3356             if (pSLL != NULL && y == pSLL->scanline)
3357             {
3358                 REGION_loadAET(&AET, pSLL->edgelist);
3359                 REGION_computeWAET(&AET);
3360                 pSLL = pSLL->next;
3361             }
3362 
3363             pPrevAET = &AET;
3364             pAET = AET.next;
3365             pWETE = pAET;
3366 
3367             /* For each active edge */
3368             while (pAET)
3369             {
3370                 /* Add to the buffer only those edges that
3371                  * are in the Winding active edge table. */
3372                 if (pWETE == pAET)
3373                 {
3374                     pts->x = pAET->bres.minor_axis;
3375                     pts->y = y;
3376                     pts++;
3377                     iPts++;
3378 
3379                     /* Send out the buffer */
3380                     if (iPts == NUMPTSTOBUFFER)
3381                     {
3382                         tmpPtBlock = ExAllocatePoolWithTag(PagedPool,
3383                                                            sizeof(POINTBLOCK),
3384                                                            TAG_REGION);
3385                         if (tmpPtBlock == NULL)
3386                         {
3387                             DPRINT1("Can't alloc tPB\n");
3388                             ExFreePoolWithTag(pETEs, TAG_REGION);
3389                             return FALSE;
3390                         }
3391                         curPtBlock->next = tmpPtBlock;
3392                         curPtBlock = tmpPtBlock;
3393                         pts = curPtBlock->pts;
3394                         numFullPtBlocks++;
3395                         iPts = 0;
3396                     }
3397 
3398                     pWETE = pWETE->nextWETE;
3399                 }
3400 
3401                 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
3402             }
3403 
3404             /* Recompute the winding active edge table if
3405              * we just resorted or have exited an edge. */
3406             if (REGION_InsertionSort(&AET) || fixWAET)
3407             {
3408                 REGION_computeWAET(&AET);
3409                 fixWAET = FALSE;
3410             }
3411         }
3412     }
3413 
3414     REGION_FreeStorage(SLLBlock.next);
3415     REGION_PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, prgn);
3416 
3417     for (curPtBlock = FirstPtBlock.next; numFullPtBlocks-- > 0;)
3418     {
3419         tmpPtBlock = curPtBlock->next;
3420         ExFreePoolWithTag(curPtBlock, TAG_REGION);
3421         curPtBlock = tmpPtBlock;
3422     }
3423 
3424     ExFreePoolWithTag(pETEs, TAG_REGION);
3425     return TRUE;
3426 }
3427 
3428 HRGN
3429 NTAPI
3430 GreCreatePolyPolygonRgn(
3431     _In_ const POINT *ppt,
3432     _In_ const ULONG *pcPoints,
3433     _In_ ULONG cPolygons,
3434     _In_ INT iMode)
3435 {
3436     PREGION prgn;
3437     HRGN hrgn;
3438 
3439     /* Allocate a new region */
3440     prgn = REGION_AllocUserRgnWithHandle(0);
3441     if (prgn == NULL)
3442     {
3443         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
3444         return NULL;
3445     }
3446 
3447     /* Call the internal function and check for success */
3448     if (REGION_SetPolyPolygonRgn(prgn, ppt, pcPoints, cPolygons, iMode))
3449     {
3450         /* Success, get the handle and unlock the region */
3451         hrgn = prgn->BaseObject.hHmgr;
3452         REGION_UnlockRgn(prgn);
3453     }
3454     else
3455     {
3456         /* Failure, delete the region */
3457         REGION_Delete(prgn);
3458         hrgn = NULL;
3459     }
3460 
3461     return hrgn;
3462 }
3463 
3464 BOOL
3465 FASTCALL
3466 IntRectInRegion(
3467     HRGN  hRgn,
3468     LPRECTL rc)
3469 {
3470     PREGION Rgn;
3471     BOOL Ret;
3472 
3473     Rgn = REGION_LockRgn(hRgn);
3474     if (Rgn == NULL)
3475     {
3476         return ERROR;
3477     }
3478 
3479     Ret = REGION_RectInRegion(Rgn, rc);
3480     REGION_UnlockRgn(Rgn);
3481     return Ret;
3482 }
3483 
3484 
3485 //
3486 // NtGdi Exported Functions
3487 //
3488 INT
3489 APIENTRY
3490 NtGdiCombineRgn(
3491     IN HRGN hrgnDst,
3492     IN HRGN hrgnSrc1,
3493     IN HRGN hrgnSrc2,
3494     IN INT iMode)
3495 {
3496     HRGN ahrgn[3];
3497     PREGION aprgn[3];
3498     INT iResult;
3499 
3500     /* Validate the combine mode */
3501     if ((iMode < RGN_AND) || (iMode > RGN_COPY))
3502     {
3503         return ERROR;
3504     }
3505 
3506     /* Validate that we have the required regions */
3507     if ((hrgnDst == NULL) ||
3508         (hrgnSrc1 == NULL) ||
3509         ((iMode != RGN_COPY) && (hrgnSrc2 == NULL)))
3510     {
3511         DPRINT1("NtGdiCombineRgn invalid parameters: %p, %p, %p, %d\n",
3512                 hrgnDst, hrgnSrc1, hrgnSrc2, iMode);
3513         EngSetLastError(ERROR_INVALID_HANDLE);
3514         return ERROR;
3515     }
3516 
3517     /* Lock all regions */
3518     ahrgn[0] = hrgnDst;
3519     ahrgn[1] = hrgnSrc1;
3520     ahrgn[2] = iMode != RGN_COPY ? hrgnSrc2 : NULL;
3521     if (!GDIOBJ_bLockMultipleObjects(3, (HGDIOBJ*)ahrgn, (PVOID*)aprgn, GDIObjType_RGN_TYPE))
3522     {
3523         DPRINT1("NtGdiCombineRgn failed to lock regions: %p, %p, %p, %d\n",
3524                 hrgnDst, hrgnSrc1, hrgnSrc2, iMode);
3525         return ERROR;
3526     }
3527 
3528     /* HACK: Sync usermode attributes */
3529     REGION_vSyncRegion(aprgn[0]);
3530     if (aprgn[1] != aprgn[0])
3531         REGION_vSyncRegion(aprgn[1]);
3532     if ((aprgn[2] != NULL) && (aprgn[2] != aprgn[0]) && (aprgn[2] != aprgn[1]))
3533         REGION_vSyncRegion(aprgn[2]);
3534 
3535     /* Call the internal function */
3536     iResult = IntGdiCombineRgn(aprgn[0], aprgn[1], aprgn[2], iMode);
3537 
3538     /* Unlock and return */
3539     REGION_UnlockRgn(aprgn[0]);
3540     REGION_UnlockRgn(aprgn[1]);
3541     if (aprgn[2] != NULL)
3542         REGION_UnlockRgn(aprgn[2]);
3543 
3544     return iResult;
3545 }
3546 
3547 HRGN
3548 APIENTRY
3549 NtGdiCreateEllipticRgn(
3550     INT Left,
3551     INT Top,
3552     INT Right,
3553     INT Bottom)
3554 {
3555     return NtGdiCreateRoundRectRgn(Left,
3556                                    Top,
3557                                    Right, Bottom,
3558                                    Right - Left,
3559                                    Bottom - Top);
3560 }
3561 
3562 HRGN
3563 APIENTRY
3564 NtGdiCreateRectRgn(
3565     INT LeftRect,
3566     INT TopRect,
3567     INT RightRect,
3568     INT BottomRect)
3569 {
3570     PREGION pRgn;
3571     HRGN hRgn;
3572 
3573     /* Allocate region data structure with space for 1 RECTL */
3574     pRgn = REGION_AllocUserRgnWithHandle(1);
3575     if (pRgn == NULL)
3576     {
3577         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
3578         return NULL;
3579     }
3580 
3581     hRgn = pRgn->BaseObject.hHmgr;
3582 
3583     REGION_SetRectRgn(pRgn, LeftRect, TopRect, RightRect, BottomRect);
3584     REGION_UnlockRgn(pRgn);
3585 
3586     DPRINT("Returning %p.\n", hRgn);
3587 
3588     return hRgn;
3589 }
3590 
3591 HRGN
3592 APIENTRY
3593 NtGdiCreateRoundRectRgn(
3594     INT left,
3595     INT top,
3596     INT right,
3597     INT bottom,
3598     INT ellipse_width,
3599     INT ellipse_height)
3600 {
3601     PREGION obj;
3602     HRGN hrgn;
3603     INT asq, bsq, d, xd, yd;
3604     RECTL rect;
3605 
3606     /* Make the dimensions sensible */
3607     if (left > right)
3608     {
3609         INT tmp = left;
3610         left = right;
3611         right = tmp;
3612     }
3613 
3614     if (top > bottom)
3615     {
3616         INT tmp = top;
3617         top = bottom;
3618         bottom = tmp;
3619     }
3620 
3621     ellipse_width = abs(ellipse_width);
3622     ellipse_height = abs(ellipse_height);
3623 
3624     /* Check parameters */
3625     if (ellipse_width > right-left)
3626         ellipse_width = right-left;
3627     if (ellipse_height > bottom-top)
3628         ellipse_height = bottom-top;
3629 
3630     /* Check if we can do a normal rectangle instead */
3631     if ((ellipse_width < 2) || (ellipse_height < 2))
3632         return NtGdiCreateRectRgn(left, top, right, bottom);
3633 
3634     /* Create region */
3635     d = (ellipse_height < 128) ? ((3 * ellipse_height) >> 2) : 64;
3636     obj = REGION_AllocUserRgnWithHandle(d);
3637     if (obj == NULL)
3638         return 0;
3639 
3640     hrgn = obj->BaseObject.hHmgr;
3641 
3642     /* Ellipse algorithm, based on an article by K. Porter
3643        in DDJ Graphics Programming Column, 8/89 */
3644     asq = ellipse_width * ellipse_width / 4;        /* a^2 */
3645     bsq = ellipse_height * ellipse_height / 4;      /* b^2 */
3646     d = bsq - asq * ellipse_height / 2 + asq / 4;   /* b^2 - a^2b + a^2/4 */
3647     xd = 0;
3648     yd = asq * ellipse_height;                      /* 2a^2b */
3649 
3650     rect.left   = left + ellipse_width / 2;
3651     rect.right  = right - ellipse_width / 2;
3652 
3653     /* Loop to draw first half of quadrant */
3654     while (xd < yd)
3655     {
3656         /* If nearest pixel is toward the center */
3657         if (d > 0)
3658         {
3659             /* Move toward center */
3660             rect.top = top++;
3661             rect.bottom = rect.top + 1;
3662             REGION_UnionRectWithRgn(obj, &rect);
3663             rect.top = --bottom;
3664             rect.bottom = rect.top + 1;
3665             REGION_UnionRectWithRgn(obj, &rect);
3666             yd -= 2*asq;
3667             d  -= yd;
3668         }
3669 
3670         /* Next horiz point */
3671         rect.left--;
3672         rect.right++;
3673         xd += 2*bsq;
3674         d  += bsq + xd;
3675     }
3676 
3677     /* Loop to draw second half of quadrant */
3678     d += (3 * (asq-bsq) / 2 - (xd+yd)) / 2;
3679     while (yd >= 0)
3680     {
3681         /* next vertical point */
3682         rect.top = top++;
3683         rect.bottom = rect.top + 1;
3684         REGION_UnionRectWithRgn(obj, &rect);
3685         rect.top = --bottom;
3686         rect.bottom = rect.top + 1;
3687         REGION_UnionRectWithRgn(obj, &rect);
3688 
3689         /* If nearest pixel is outside ellipse */
3690         if (d < 0)
3691         {
3692             /* Move away from center */
3693             rect.left--;
3694             rect.right++;
3695             xd += 2*bsq;
3696             d  += xd;
3697         }
3698 
3699         yd -= 2*asq;
3700         d  += asq - yd;
3701     }
3702 
3703     /* Add the inside rectangle */
3704     if (top <= bottom)
3705     {
3706         rect.top = top;
3707         rect.bottom = bottom;
3708         REGION_UnionRectWithRgn(obj, &rect);
3709     }
3710 
3711     REGION_UnlockRgn(obj);
3712     return hrgn;
3713 }
3714 
3715 BOOL
3716 APIENTRY
3717 NtGdiEqualRgn(
3718     HRGN  hSrcRgn1,
3719     HRGN  hSrcRgn2)
3720 {
3721     HRGN ahrgn[2];
3722     PREGION aprgn[2];
3723     PREGION rgn1, rgn2;
3724     PRECTL tRect1, tRect2;
3725     ULONG i;
3726     BOOL bRet = FALSE;
3727 
3728     /* Check if we got 2 regions */
3729     if ((hSrcRgn1 == NULL) || (hSrcRgn2 == NULL))
3730     {
3731         return FALSE;
3732     }
3733 
3734     /* Check if these are the same regions */
3735     if (hSrcRgn1 == hSrcRgn2)
3736     {
3737         /* Make sure this region is valid */
3738         if ((GDI_HANDLE_GET_TYPE(hSrcRgn1) == GDILoObjType_LO_REGION_TYPE) &&
3739             GreIsHandleValid(hSrcRgn1))
3740         {
3741             return TRUE;
3742         }
3743         return FALSE;
3744     }
3745 
3746     /* Lock both regions */
3747     ahrgn[0] = hSrcRgn1;
3748     ahrgn[1] = hSrcRgn2;
3749     if (!GDIOBJ_bLockMultipleObjects(2, (HGDIOBJ*)ahrgn, (PVOID*)aprgn, GDIObjType_RGN_TYPE))
3750     {
3751         DPRINT1("NtGdiEqualRgn failed to lock regions: %p, %p\n",
3752                 hSrcRgn1, hSrcRgn2);
3753         return FALSE;
3754     }
3755 
3756     REGION_vSyncRegion(aprgn[0]);
3757     REGION_vSyncRegion(aprgn[1]);
3758 
3759     rgn1 = aprgn[0];
3760     rgn2 = aprgn[1];
3761 
3762     if (rgn1->rdh.nCount != rgn2->rdh.nCount)
3763         goto exit;
3764 
3765     if (rgn1->rdh.nCount == 0)
3766     {
3767         bRet = TRUE;
3768         goto exit;
3769     }
3770 
3771     if ((rgn1->rdh.rcBound.left   != rgn2->rdh.rcBound.left)  ||
3772         (rgn1->rdh.rcBound.right  != rgn2->rdh.rcBound.right) ||
3773         (rgn1->rdh.rcBound.top    != rgn2->rdh.rcBound.top)   ||
3774         (rgn1->rdh.rcBound.bottom != rgn2->rdh.rcBound.bottom))
3775         goto exit;
3776 
3777     tRect1 = rgn1->Buffer;
3778     tRect2 = rgn2->Buffer;
3779 
3780     if ((tRect1 == NULL) || (tRect2 == NULL))
3781         goto exit;
3782 
3783     for (i=0; i < rgn1->rdh.nCount; i++)
3784     {
3785         if ((tRect1[i].left   != tRect2[i].left)  ||
3786             (tRect1[i].right  != tRect2[i].right) ||
3787             (tRect1[i].top    != tRect2[i].top)   ||
3788             (tRect1[i].bottom != tRect2[i].bottom))
3789             goto exit;
3790     }
3791 
3792     bRet = TRUE;
3793 
3794 exit:
3795     REGION_UnlockRgn(rgn1);
3796     REGION_UnlockRgn(rgn2);
3797     return bRet;
3798 }
3799 
3800 HRGN
3801 APIENTRY
3802 NtGdiExtCreateRegion(
3803     OPTIONAL LPXFORM Xform,
3804     DWORD Count,
3805     LPRGNDATA RgnData)
3806 {
3807     HRGN hRgn;
3808     PREGION Region;
3809     DWORD nCount = 0;
3810     DWORD iType = 0;
3811     DWORD dwSize = 0;
3812     UINT i;
3813     RECT* rects;
3814     NTSTATUS Status = STATUS_SUCCESS;
3815     MATRIX matrix;
3816     XFORMOBJ xo;
3817 
3818     DPRINT("NtGdiExtCreateRegion\n");
3819     _SEH2_TRY
3820     {
3821         ProbeForRead(RgnData, Count, 1);
3822         nCount = RgnData->rdh.nCount;
3823         iType = RgnData->rdh.iType;
3824         dwSize = RgnData->rdh.dwSize;
3825         rects = (RECT*)RgnData->Buffer;
3826     }
3827     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3828     {
3829         Status = _SEH2_GetExceptionCode();
3830     }
3831     _SEH2_END;
3832 
3833     if (!NT_SUCCESS(Status))
3834     {
3835         SetLastNtError(Status);
3836         return NULL;
3837     }
3838 
3839     /* Check parameters, but don't set last error here */
3840     if ((Count < sizeof(RGNDATAHEADER) + nCount * sizeof(RECT)) ||
3841         (iType != RDH_RECTANGLES) ||
3842         (dwSize != sizeof(RGNDATAHEADER)))
3843     {
3844         return NULL;
3845     }
3846 
3847     Region = REGION_AllocUserRgnWithHandle(nCount);
3848 
3849     if (Region == NULL)
3850     {
3851         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
3852         return FALSE;
3853     }
3854     hRgn = Region->BaseObject.hHmgr;
3855 
3856     _SEH2_TRY
3857     {
3858         /* Insert the rectangles one by one */
3859         for(i=0; i<nCount; i++)
3860         {
3861             REGION_UnionRectWithRgn(Region, &rects[i]);
3862         }
3863 
3864         if (Xform != NULL)
3865         {
3866             ULONG ret;
3867 
3868             /* Init the XFORMOBJ from the Xform struct */
3869             Status = STATUS_INVALID_PARAMETER;
3870             XFORMOBJ_vInit(&xo, &matrix);
3871             ret = XFORMOBJ_iSetXform(&xo, (XFORML*)Xform);
3872 
3873             /* Check for error */
3874             if (ret != DDI_ERROR)
3875             {
3876                 /* Apply the coordinate transformation on the rects */
3877                 if (XFORMOBJ_bApplyXform(&xo,
3878                                          XF_LTOL,
3879                                          Region->rdh.nCount * 2,
3880                                          Region->Buffer,
3881                                          Region->Buffer))
3882                 {
3883                     Status = STATUS_SUCCESS;
3884                 }
3885             }
3886         }
3887     }
3888     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3889     {
3890         Status = _SEH2_GetExceptionCode();
3891     }
3892     _SEH2_END;
3893     if (!NT_SUCCESS(Status))
3894     {
3895         EngSetLastError(ERROR_INVALID_PARAMETER);
3896         REGION_UnlockRgn(Region);
3897         GreDeleteObject(hRgn);
3898         return NULL;
3899     }
3900 
3901     REGION_UnlockRgn(Region);
3902 
3903     return hRgn;
3904 }
3905 
3906 INT
3907 APIENTRY
3908 NtGdiGetRgnBox(
3909     HRGN hRgn,
3910     PRECTL pRect)
3911 {
3912     PREGION Rgn;
3913     RECTL SafeRect;
3914     DWORD ret;
3915     NTSTATUS Status = STATUS_SUCCESS;
3916 
3917     Rgn = REGION_LockRgn(hRgn);
3918     if (Rgn == NULL)
3919     {
3920         return ERROR;
3921     }
3922 
3923     ret = REGION_GetRgnBox(Rgn, &SafeRect);
3924     REGION_UnlockRgn(Rgn);
3925     if (ret == ERROR)
3926     {
3927         return ret;
3928     }
3929 
3930     _SEH2_TRY
3931     {
3932         ProbeForWrite(pRect, sizeof(RECT), 1);
3933         *pRect = SafeRect;
3934     }
3935     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3936     {
3937         Status = _SEH2_GetExceptionCode();
3938     }
3939     _SEH2_END;
3940     if (!NT_SUCCESS(Status))
3941     {
3942         return ERROR;
3943     }
3944 
3945     return ret;
3946 }
3947 
3948 INT
3949 APIENTRY
3950 NtGdiOffsetRgn(
3951     _In_ HRGN hrgn,
3952     _In_ INT cx,
3953     _In_ INT cy)
3954 {
3955     PREGION prgn;
3956     INT iResult;
3957 
3958     DPRINT("NtGdiOffsetRgn: hrgn %p cx %d cy %d\n", hrgn, cx, cy);
3959 
3960     /* Lock the region */
3961     prgn = REGION_LockRgn(hrgn);
3962     if (prgn == NULL)
3963     {
3964         DPRINT1("NtGdiOffsetRgn: failed to lock region %p\n", hrgn);
3965         return ERROR;
3966     }
3967 
3968     /* Call the internal function */
3969     if (!REGION_bOffsetRgn(prgn, cx, cy))
3970     {
3971         iResult = ERROR;
3972     }
3973     else
3974     {
3975         iResult = REGION_Complexity(prgn);
3976     }
3977 
3978     /* Unlock and return the result */
3979     REGION_UnlockRgn(prgn);
3980     return iResult;
3981 }
3982 
3983 BOOL
3984 APIENTRY
3985 NtGdiPtInRegion(
3986     _In_ HRGN hrgn,
3987     _In_ INT x,
3988     _In_ INT y)
3989 {
3990     PREGION prgn;
3991     BOOL bResult;
3992 
3993     /* Lock the region */
3994     prgn = REGION_LockRgn(hrgn);
3995     if (prgn == NULL)
3996     {
3997         DPRINT1("NtGdiPtInRegion: hrgn error\n");
3998         return FALSE;
3999     }
4000 
4001     /* Call the internal function */
4002     bResult = REGION_PtInRegion(prgn, x, y);
4003 
4004     /* Unlock and return the result */
4005     REGION_UnlockRgn(prgn);
4006     return bResult;
4007 }
4008 
4009 __kernel_entry
4010 BOOL
4011 APIENTRY
4012 NtGdiRectInRegion(
4013     _In_ HRGN hrgn,
4014     _Inout_ LPRECT prclUnsafe)
4015 {
4016     RECTL rcTemp;
4017 
4018     /* Probe and copy the rect */
4019     _SEH2_TRY
4020     {
4021         ProbeForRead(prclUnsafe, sizeof(RECT), 1);
4022         rcTemp = *prclUnsafe;
4023     }
4024     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4025     {
4026         DPRINT1("NtGdiRectInRegion: Exception accessing the rect\n");
4027         return FALSE;
4028     }
4029     _SEH2_END;
4030 
4031     /* Call the internal function */
4032     return IntRectInRegion(hrgn, &rcTemp);
4033 }
4034 
4035 BOOL
4036 APIENTRY
4037 NtGdiSetRectRgn(
4038     _In_ HRGN hrgn,
4039     _In_ INT xLeft,
4040     _In_ INT yTop,
4041     _In_ INT xRight,
4042     _In_ INT yBottom)
4043 {
4044     PREGION prgn;
4045 
4046     /* Lock the region */
4047     prgn = REGION_LockRgn(hrgn);
4048     if (prgn == NULL)
4049     {
4050         return FALSE;
4051     }
4052 
4053     /* Call the internal API */
4054     REGION_SetRectRgn(prgn, xLeft, yTop, xRight, yBottom);
4055 
4056     /* Unlock the region and return success */
4057     REGION_UnlockRgn(prgn);
4058     return TRUE;
4059 }
4060 
4061 /*!
4062  * MSDN: GetRegionData, Return Values:
4063  *
4064  * "If the function succeeds and dwCount specifies an adequate number of bytes,
4065  * the return value is always dwCount. If dwCount is too small or the function
4066  * fails, the return value is 0. If lpRgnData is NULL, the return value is the
4067  * required number of bytes.
4068  *
4069  * If the function fails, the return value is zero."
4070  */
4071 _Success_(return!=0)
4072 __kernel_entry
4073 ULONG
4074 APIENTRY
4075 NtGdiGetRegionData(
4076     _In_ HRGN hrgn,
4077     _In_ ULONG cjBuffer,
4078     _Out_writes_bytes_to_opt_(cjBuffer, return) LPRGNDATA lpRgnData)
4079 {
4080     ULONG cjRects, cjSize;
4081     PREGION prgn;
4082 
4083     /* Lock the region */
4084     prgn = REGION_LockRgn(hrgn);
4085     if (prgn == NULL)
4086     {
4087         EngSetLastError(ERROR_INVALID_HANDLE);
4088         return 0;
4089     }
4090 
4091     /* Calculate the region sizes */
4092     cjRects = prgn->rdh.nCount * sizeof(RECT);
4093     cjSize = cjRects + sizeof(RGNDATAHEADER);
4094 
4095     /* Check if region data is requested */
4096     if (lpRgnData)
4097     {
4098         /* Check if the buffer is large enough */
4099         if (cjBuffer >= cjSize)
4100         {
4101             /* Probe the buffer and copy the data */
4102             _SEH2_TRY
4103             {
4104                 ProbeForWrite(lpRgnData, cjSize, sizeof(ULONG));
4105                 RtlCopyMemory(lpRgnData, &prgn->rdh, sizeof(RGNDATAHEADER));
4106                 RtlCopyMemory(lpRgnData->Buffer, prgn->Buffer, cjRects);
4107                 lpRgnData->rdh.iType = RDH_RECTANGLES;
4108                 lpRgnData->rdh.nRgnSize = cjRects;
4109             }
4110             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4111             {
4112                 EngSetLastError(ERROR_INVALID_PARAMETER);
4113                 cjSize = 0;
4114             }
4115             _SEH2_END;
4116         }
4117         else
4118         {
4119             /* Buffer is too small */
4120             EngSetLastError(ERROR_INVALID_PARAMETER);
4121             cjSize = 0;
4122         }
4123     }
4124 
4125     /* Unlock the region and return the size */
4126     REGION_UnlockRgn(prgn);
4127     return cjSize;
4128 }
4129 
4130 /* EOF */
4131