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