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