2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
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.
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.
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.
21 * GDI region objects. Shamelessly ripped out from the X11 distribution
22 * Thanks for the nice licence.
24 * Copyright 1993, 1994, 1995 Alexandre Julliard
25 * Modifications and additions: Copyright 1998 Huw Davies
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.
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.
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
43 /************************************************************************
45 Copyright (c) 1987, 1988 X Consortium
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:
54 The above copyright notice and this permission notice shall be included in
55 all copies or substantial portions of the Software.
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.
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.
69 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
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.
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
89 ************************************************************************/
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.
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".
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
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...
117 #include <suppress.h>
122 PREGION prgnDefault
= NULL
;
123 HRGN hrgnDefault
= NULL
;
125 // Internal Functions
128 #define COPY_RECTS(dest, src, nRects) \
130 PRECTL xDest = (dest); \
131 PRECTL xSrc = (src); \
132 UINT xRects = (nRects); \
133 while (xRects-- > 0) { \
134 *(xDest++) = *(xSrc++); \
138 #define COPY_RECTS(dest, src, nRects) RtlCopyMemory(dest, src, (nRects) * sizeof(RECTL))
141 #define EMPTY_REGION(pReg) { \
142 (pReg)->rdh.nCount = 0; \
143 (pReg)->rdh.rcBound.left = (pReg)->rdh.rcBound.top = 0; \
144 (pReg)->rdh.rcBound.right = (pReg)->rdh.rcBound.bottom = 0; \
145 (pReg)->rdh.iType = RDH_RECTANGLES; \
148 #define REGION_NOT_EMPTY(pReg) pReg->rdh.nCount
150 #define INRECT(r, x, y) \
151 ( ( ((r).right > x)) && \
152 ( ((r).left <= x)) && \
153 ( ((r).bottom > y)) && \
156 /* 1 if two RECTs overlap.
157 * 0 if two RECTs do not overlap.
159 #define EXTENTCHECK(r1, r2) \
160 ((r1)->right > (r2)->left && \
161 (r1)->left < (r2)->right && \
162 (r1)->bottom > (r2)->top && \
163 (r1)->top < (r2)->bottom)
166 * In scan converting polygons, we want to choose those pixels
167 * which are inside the polygon. Thus, we add .5 to the starting
168 * x coordinate for both left and right edges. Now we choose the
169 * first pixel which is inside the pgon for the left edge and the
170 * first pixel which is outside the pgon for the right edge.
171 * Draw the left pixel, but not the right.
173 * How to add .5 to the starting x coordinate:
174 * If the edge is moving to the right, then subtract dy from the
175 * error term from the general form of the algorithm.
176 * If the edge is moving to the left, then add dy to the error term.
178 * The reason for the difference between edges moving to the left
179 * and edges moving to the right is simple: If an edge is moving
180 * to the right, then we want the algorithm to flip immediately.
181 * If it is moving to the left, then we don't want it to flip until
182 * we traverse an entire pixel.
184 #define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \
185 int dx; /* Local storage */ \
188 * If the edge is horizontal, then it is ignored \
189 * and assumed not to be processed. Otherwise, do this stuff. \
193 dx = (x2) - xStart; \
197 incr1 = -2 * dx + 2 * (dy) * m1; \
198 incr2 = -2 * dx + 2 * (dy) * m; \
199 d = 2 * m * (dy) - 2 * dx - 2 * (dy); \
203 incr1 = 2 * dx - 2 * (dy) * m1; \
204 incr2 = 2 * dx - 2 * (dy) * m; \
205 d = -2 * m * (dy) + 2 * dx; \
210 #define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \
233 * This structure contains all of the information needed
234 * to run the bresenham algorithm.
235 * The variables may be hardcoded into the declarations
236 * instead of using this structure to make use of
237 * register declarations.
241 INT minor_axis
; /* Minor axis */
242 INT d
; /* Decision variable */
243 INT m
, m1
; /* Slope and slope+1 */
244 INT incr1
, incr2
; /* Error increments */
248 #define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \
249 BRESINITPGON(dmaj, min1, min2, bres.minor_axis, bres.d, \
250 bres.m, bres.m1, bres.incr1, bres.incr2)
252 #define BRESINCRPGONSTRUCT(bres) \
253 BRESINCRPGON(bres.d, bres.minor_axis, bres.m, bres.m1, bres.incr1, bres.incr2)
258 * These are the data structures needed to scan
259 * convert regions. Two different scan conversion
260 * methods are available -- the even-odd method, and
261 * the winding number method.
262 * The even-odd rule states that a point is inside
263 * the polygon if a ray drawn from that point in any
264 * direction will pass through an odd number of
266 * By the winding number rule, a point is decided
267 * to be inside the polygon if a ray drawn from that
268 * point in any direction passes through a different
269 * number of clockwise and counter-clockwise path
272 * These data structures are adapted somewhat from
273 * the algorithm in (Foley/Van Dam) for scan converting
275 * The basic algorithm is to start at the top (smallest y)
276 * of the polygon, stepping down to the bottom of
277 * the polygon by incrementing the y coordinate. We
278 * keep a list of edges which the current scanline crosses,
279 * sorted by x. This list is called the Active Edge Table (AET)
280 * As we change the y-coordinate, we update each entry in
281 * in the active edge table to reflect the edges new xcoord.
282 * This list must be sorted at each scanline in case
283 * two edges intersect.
284 * We also keep a data structure known as the Edge Table (ET),
285 * which keeps track of all the edges which the current
286 * scanline has not yet reached. The ET is basically a
287 * list of SCANLINE_LIST structures containing a list of
288 * edges which are entered at a given scanline. There is one
289 * SCANLINE_LIST per scanline at which an edge is entered.
290 * When we enter a new edge, we move it from the ET to the AET.
292 * From the AET, we can implement the even-odd rule as in
294 * The winding number rule is a little trickier. We also
295 * keep the EDGE_TABLEEntries in the AET linked by the
296 * nextWETE (winding EDGE_TABLE_ENTRY) link. This allows
297 * the edges to be linked just as before for updating
298 * purposes, but only uses the edges linked by the nextWETE
299 * link as edges representing spans of the polygon to
300 * drawn (as with the even-odd rule).
304 * For the winding number rule
307 #define COUNTERCLOCKWISE -1
309 typedef struct _EDGE_TABLE_ENTRY
311 INT ymax
; /* ycoord at which we exit this edge. */
312 BRESINFO bres
; /* Bresenham info to run the edge */
313 struct _EDGE_TABLE_ENTRY
*next
; /* Next in the list */
314 struct _EDGE_TABLE_ENTRY
*back
; /* For insertion sort */
315 struct _EDGE_TABLE_ENTRY
*nextWETE
; /* For winding num rule */
316 INT ClockWise
; /* Flag for winding number rule */
320 typedef struct _SCANLINE_LIST
322 INT scanline
; /* The scanline represented */
323 EDGE_TABLE_ENTRY
*edgelist
; /* Header node */
324 struct _SCANLINE_LIST
*next
; /* Next in the list */
330 INT ymax
; /* ymax for the polygon */
331 INT ymin
; /* ymin for the polygon */
332 SCANLINE_LIST scanlines
; /* Header node */
337 * Here is a struct to help with storage allocation
338 * so we can allocate a big chunk at a time, and then take
339 * pieces from this heap when we need to.
341 #define SLLSPERBLOCK 25
343 typedef struct _SCANLINE_LISTBLOCK
345 SCANLINE_LIST SLLs
[SLLSPERBLOCK
];
346 struct _SCANLINE_LISTBLOCK
*next
;
347 } SCANLINE_LISTBLOCK
;
351 * A few macros for the inner loops of the fill code where
352 * performance considerations don't allow a procedure call.
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.
362 #define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \
363 if (pAET->ymax == y) { /* Leaving this edge */ \
364 pPrevAET->next = pAET->next; \
365 pAET = pPrevAET->next; \
368 pAET->back = pPrevAET; \
371 BRESINCRPGONSTRUCT(pAET->bres); \
379 * Evaluate the given edge at the given scanline.
380 * If the edge has expired, then we leave it and fix up
381 * the active edge table; otherwise, we increment the
382 * x value to be ready for the next scanline.
383 * The even-odd rule is in effect.
385 #define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \
386 if (pAET->ymax == y) { /* Leaving this edge */ \
387 pPrevAET->next = pAET->next; \
388 pAET = pPrevAET->next; \
390 pAET->back = pPrevAET; \
393 BRESINCRPGONSTRUCT(pAET->bres); \
399 /**************************************************************************
403 *************************************************************************/
405 #define LARGE_COORDINATE 0x7fffffff /* FIXME */
406 #define SMALL_COORDINATE 0x80000000
410 REGION_bGrowBufferSize(
411 _Inout_ PREGION prgn
,
416 NT_ASSERT(cRects
> 0);
418 /* Make sure we don't overflow */
419 if (cRects
> MAXULONG
/ sizeof(RECTL
))
424 /* Calculate new buffer size */
425 cjNewSize
= cRects
* sizeof(RECTL
);
427 /* Avoid allocating too often, by duplicating the old buffer size
428 Note: we don't do an overflow check, since the old size will never
429 get that large before running out of memory. */
430 if (2 * prgn
->rdh
.nRgnSize
> cjNewSize
)
432 cjNewSize
= 2 * prgn
->rdh
.nRgnSize
;
435 /* Allocate the new buffer */
436 pvBuffer
= ExAllocatePoolWithTag(PagedPool
, cjNewSize
, TAG_REGION
);
437 if (pvBuffer
== NULL
)
442 /* Copy the rects into the new buffer */
443 COPY_RECTS(pvBuffer
, prgn
->Buffer
, prgn
->rdh
.nCount
);
445 /* Free the old buffer */
446 if (prgn
->Buffer
!= &prgn
->rdh
.rcBound
)
448 ExFreePoolWithTag(prgn
->Buffer
, TAG_REGION
);
451 /* Set the new buffer */
452 prgn
->Buffer
= pvBuffer
;
453 prgn
->rdh
.nRgnSize
= cjNewSize
;
460 REGION_bEnsureBufferSize(
461 _Inout_ PREGION prgn
,
464 /* Check if the current region size is too small */
465 if (cRects
> prgn
->rdh
.nRgnSize
/ sizeof(RECTL
))
467 /* Allocate a new buffer */
468 return REGION_bGrowBufferSize(prgn
, cRects
);
477 _Inout_ PREGION prgn
,
484 NT_ASSERT((prgn
->rdh
.nCount
+ 1) * sizeof(RECT
) <= prgn
->rdh
.nRgnSize
);
486 prcl
= &prgn
->Buffer
[prgn
->rdh
.nCount
];
490 prcl
->bottom
= bottom
;
497 _Inout_ PREGION prgn
,
503 if (!REGION_bEnsureBufferSize(prgn
, prgn
->rdh
.nCount
+ 1))
508 REGION_vAddRect(prgn
, left
, top
, right
, bottom
);
512 typedef VOID (FASTCALL
*overlapProcp
)(PREGION
, PRECT
, PRECT
, PRECT
, PRECT
, INT
, INT
);
513 typedef VOID (FASTCALL
*nonOverlapProcp
)(PREGION
, PRECT
, PRECT
, INT
, INT
);
515 // Number of points to buffer before sending them off to scanlines() : Must be an even number
516 #define NUMPTSTOBUFFER 200
518 #define RGN_DEFAULT_RECTS 2
520 // Used to allocate buffers for points and link the buffers together
521 typedef struct _POINTBLOCK
523 POINT pts
[NUMPTSTOBUFFER
];
524 struct _POINTBLOCK
*next
;
529 * This function is left there for debugging purposes.
533 IntDumpRegion(HRGN hRgn
)
537 Data
= REGION_LockRgn(hRgn
);
540 DbgPrint("IntDumpRegion called with invalid region!\n");
544 DbgPrint("IntDumpRegion(%x): %d,%d-%d,%d %d\n",
546 Data
->rdh
.rcBound
.left
,
547 Data
->rdh
.rcBound
.top
,
548 Data
->rdh
.rcBound
.right
,
549 Data
->rdh
.rcBound
.bottom
,
552 REGION_UnlockRgn(Data
);
554 #endif /* Not NDEBUG */
559 REGION_Complexity(PREGION prgn
)
564 DPRINT("Region Complexity -> %lu", prgn
->rdh
.nCount
);
565 switch (prgn
->rdh
.nCount
)
572 return COMPLEXREGION
;
583 /* Only copy if source and dest are not equal */
586 /* Check if we need to increase our buffer */
587 if (dst
->rdh
.nRgnSize
< src
->rdh
.nCount
* sizeof(RECT
))
591 /* Allocate a new buffer */
592 temp
= ExAllocatePoolWithTag(PagedPool
,
593 src
->rdh
.nCount
* sizeof(RECT
),
598 /* Free the old buffer */
599 if ((dst
->Buffer
!= NULL
) && (dst
->Buffer
!= &dst
->rdh
.rcBound
))
600 ExFreePoolWithTag(dst
->Buffer
, TAG_REGION
);
602 /* Set the new buffer and the size */
604 dst
->rdh
.nRgnSize
= src
->rdh
.nCount
* sizeof(RECT
);
607 dst
->rdh
.nCount
= src
->rdh
.nCount
;
608 dst
->rdh
.rcBound
.left
= src
->rdh
.rcBound
.left
;
609 dst
->rdh
.rcBound
.top
= src
->rdh
.rcBound
.top
;
610 dst
->rdh
.rcBound
.right
= src
->rdh
.rcBound
.right
;
611 dst
->rdh
.rcBound
.bottom
= src
->rdh
.rcBound
.bottom
;
612 dst
->rdh
.iType
= src
->rdh
.iType
;
613 COPY_RECTS(dst
->Buffer
, src
->Buffer
, src
->rdh
.nCount
);
625 RECTL
*pRect
, *pRectEnd
, *pExtents
;
627 /* Quick check for NULLREGION */
628 if (pReg
->rdh
.nCount
== 0)
630 pReg
->rdh
.rcBound
.left
= 0;
631 pReg
->rdh
.rcBound
.top
= 0;
632 pReg
->rdh
.rcBound
.right
= 0;
633 pReg
->rdh
.rcBound
.bottom
= 0;
634 pReg
->rdh
.iType
= RDH_RECTANGLES
;
638 pExtents
= &pReg
->rdh
.rcBound
;
639 pRect
= pReg
->Buffer
;
640 pRectEnd
= pReg
->Buffer
+ pReg
->rdh
.nCount
- 1;
642 /* Since pRect is the first rectangle in the region, it must have the
643 * smallest top and since pRectEnd is the last rectangle in the region,
644 * it must have the largest bottom, because of banding. Initialize left and
645 * right from pRect and pRectEnd, resp., as good things to initialize them
647 pExtents
->left
= pRect
->left
;
648 pExtents
->top
= pRect
->top
;
649 pExtents
->right
= pRectEnd
->right
;
650 pExtents
->bottom
= pRectEnd
->bottom
;
652 while (pRect
<= pRectEnd
)
654 if (pRect
->left
< pExtents
->left
)
655 pExtents
->left
= pRect
->left
;
656 if (pRect
->right
> pExtents
->right
)
657 pExtents
->right
= pRect
->right
;
661 pReg
->rdh
.iType
= RDH_RECTANGLES
;
664 // FIXME: This function needs review and testing
665 /***********************************************************************
676 ULONG i
, j
, clipa
, clipb
, nRgnSize
;
680 INT bottom
= MINLONG
;
682 if ((rect
->left
>= rect
->right
) ||
683 (rect
->top
>= rect
->bottom
) ||
684 (EXTENTCHECK(rect
, &rgnSrc
->rdh
.rcBound
) == 0))
689 /* Skip all rects that are completely above our intersect rect */
690 for (clipa
= 0; clipa
< rgnSrc
->rdh
.nCount
; clipa
++)
692 /* bottom is exclusive, so break when we go above it */
693 if (rgnSrc
->Buffer
[clipa
].bottom
> rect
->top
) break;
696 /* Bail out, if there is nothing left */
697 if (clipa
== rgnSrc
->rdh
.nCount
) goto empty
;
699 /* Find the last rect that is still within the intersect rect (exclusive) */
700 for (clipb
= clipa
; clipb
< rgnSrc
->rdh
.nCount
; clipb
++)
702 /* bottom is exclusive, so stop, when we start at that y pos */
703 if (rgnSrc
->Buffer
[clipb
].top
>= rect
->bottom
) break;
706 /* Bail out, if there is nothing left */
707 if (clipb
== clipa
) goto empty
;
709 // clipa - index of the first rect in the first intersecting band
710 // clipb - index of the last rect in the last intersecting band plus 1
712 /* Check if the buffer in the dest region is large enough,
713 otherwise allocate a new one */
714 nRgnSize
= (clipb
- clipa
) * sizeof(RECT
);
715 if ((rgnDst
!= rgnSrc
) && (rgnDst
->rdh
.nRgnSize
< nRgnSize
))
718 temp
= ExAllocatePoolWithTag(PagedPool
, nRgnSize
, TAG_REGION
);
722 /* Free the old buffer */
723 if (rgnDst
->Buffer
&& (rgnDst
->Buffer
!= &rgnDst
->rdh
.rcBound
))
724 ExFreePoolWithTag(rgnDst
->Buffer
, TAG_REGION
);
726 rgnDst
->Buffer
= temp
;
727 rgnDst
->rdh
.nCount
= 0;
728 rgnDst
->rdh
.nRgnSize
= nRgnSize
;
729 rgnDst
->rdh
.iType
= RDH_RECTANGLES
;
732 /* Loop all rects within the intersect rect from the y perspective */
733 for (i
= clipa
, j
= 0; i
< clipb
; i
++)
735 /* i - src index, j - dst index, j is always <= i for obvious reasons */
737 lpr
= &rgnSrc
->Buffer
[i
];
739 /* Make sure the source rect is not retarded */
740 ASSERT(lpr
->bottom
> lpr
->top
);
741 ASSERT(lpr
->right
> lpr
->left
);
743 /* We already checked above, this should hold true */
744 ASSERT(lpr
->bottom
> rect
->top
);
745 ASSERT(lpr
->top
< rect
->bottom
);
747 /* Check if this rect is really inside the intersect rect */
748 if ((lpr
->left
< rect
->right
) && (lpr
->right
> rect
->left
))
750 rpr
= &rgnDst
->Buffer
[j
];
752 /* Crop the rect with the intersect rect */
753 rpr
->top
= max(lpr
->top
, rect
->top
);
754 rpr
->bottom
= min(lpr
->bottom
, rect
->bottom
);
755 rpr
->left
= max(lpr
->left
, rect
->left
);
756 rpr
->right
= min(lpr
->right
, rect
->right
);
758 /* Make sure the resulting rect is not retarded */
759 ASSERT(rpr
->bottom
> rpr
->top
);
760 ASSERT(rpr
->right
> rpr
->left
);
762 /* Track new bounds */
763 if (rpr
->left
< left
) left
= rpr
->left
;
764 if (rpr
->right
> right
) right
= rpr
->right
;
765 if (rpr
->top
< top
) top
= rpr
->top
;
766 if (rpr
->bottom
> bottom
) bottom
= rpr
->bottom
;
768 /* Next target rect */
773 if (j
== 0) goto empty
;
775 /* Update the bounds rect */
776 rgnDst
->rdh
.rcBound
.left
= left
;
777 rgnDst
->rdh
.rcBound
.right
= right
;
778 rgnDst
->rdh
.rcBound
.top
= top
;
779 rgnDst
->rdh
.rcBound
.bottom
= bottom
;
781 /* Set new rect count */
782 rgnDst
->rdh
.nCount
= j
;
784 return REGION_Complexity(rgnDst
);
787 if (rgnDst
->Buffer
== NULL
)
789 rgnDst
->Buffer
= &rgnDst
->rdh
.rcBound
;
792 EMPTY_REGION(rgnDst
);
798 * Attempt to merge the rects in the current band with those in the
799 * previous one. Used only by REGION_RegionOp.
802 * The new index for the previous band.
804 * \note Side Effects:
805 * If coalescing takes place:
806 * - rectangles in the previous band will have their bottom fields
808 * - pReg->numRects will be decreased.
815 PREGION pReg
, /* Region to coalesce */
816 INT prevStart
, /* Index of start of previous band */
817 INT curStart
) /* Index of start of current band */
819 RECTL
*pPrevRect
; /* Current rect in previous band */
820 RECTL
*pCurRect
; /* Current rect in current band */
821 RECTL
*pRegEnd
; /* End of region */
822 INT curNumRects
; /* Number of rectangles in current band */
823 INT prevNumRects
; /* Number of rectangles in previous band */
824 INT bandtop
; /* Top coordinate for current band */
826 pRegEnd
= pReg
->Buffer
+ pReg
->rdh
.nCount
;
827 pPrevRect
= pReg
->Buffer
+ prevStart
;
828 prevNumRects
= curStart
- prevStart
;
830 /* Figure out how many rectangles are in the current band. Have to do
831 * this because multiple bands could have been added in REGION_RegionOp
832 * at the end when one region has been exhausted. */
833 pCurRect
= pReg
->Buffer
+ curStart
;
834 bandtop
= pCurRect
->top
;
835 for (curNumRects
= 0;
836 (pCurRect
!= pRegEnd
) && (pCurRect
->top
== bandtop
);
842 if (pCurRect
!= pRegEnd
)
844 /* If more than one band was added, we have to find the start
845 * of the last band added so the next coalescing job can start
846 * at the right place... (given when multiple bands are added,
847 * this may be pointless -- see above). */
849 while ((pRegEnd
-1)->top
== pRegEnd
->top
)
854 curStart
= pRegEnd
- pReg
->Buffer
;
855 pRegEnd
= pReg
->Buffer
+ pReg
->rdh
.nCount
;
858 if ((curNumRects
== prevNumRects
) && (curNumRects
!= 0))
860 pCurRect
-= curNumRects
;
862 /* The bands may only be coalesced if the bottom of the previous
863 * matches the top scanline of the current. */
864 if (pPrevRect
->bottom
== pCurRect
->top
)
866 /* Make sure the bands have rects in the same places. This
867 * assumes that rects have been added in such a way that they
868 * cover the most area possible. I.e. two rects in a band must
869 * have some horizontal space between them. */
872 if ((pPrevRect
->left
!= pCurRect
->left
) ||
873 (pPrevRect
->right
!= pCurRect
->right
))
875 /* The bands don't line up so they can't be coalesced. */
883 while (prevNumRects
!= 0);
885 pReg
->rdh
.nCount
-= curNumRects
;
886 pCurRect
-= curNumRects
;
887 pPrevRect
-= curNumRects
;
889 /* The bands may be merged, so set the bottom of each rect
890 * in the previous band to that of the corresponding rect in
891 * the current band. */
894 pPrevRect
->bottom
= pCurRect
->bottom
;
899 while (curNumRects
!= 0);
901 /* If only one band was added to the region, we have to backup
902 * curStart to the start of the previous band.
904 * If more than one band was added to the region, copy the
905 * other bands down. The assumption here is that the other bands
906 * came from the same region as the current one and no further
907 * coalescing can be done on them since it's all been done
908 * already... curStart is already in the right place. */
909 if (pCurRect
== pRegEnd
)
911 curStart
= prevStart
;
917 *pPrevRect
++ = *pCurRect
++;
919 while (pCurRect
!= pRegEnd
);
928 * Apply an operation to two regions. Called by REGION_Union,
929 * REGION_Inverse, REGION_Subtract, REGION_Intersect...
935 * The new region is overwritten.
937 *\note The idea behind this function is to view the two regions as sets.
938 * Together they cover a rectangle of area that this function divides
939 * into horizontal bands where points are covered only by one region
940 * or by both. For the first case, the nonOverlapFunc is called with
941 * each the band and the band's upper and lower extents. For the
942 * second, the overlapFunc is called to process the entire band. It
943 * is responsible for clipping the rectangles in the band, though
944 * this function provides the boundaries.
945 * At the end of each band, the new region is coalesced, if possible,
946 * to reduce the number of rectangles in the region.
953 PREGION newReg
, /* Place to store result */
954 PREGION reg1
, /* First region in operation */
955 PREGION reg2
, /* 2nd region in operation */
956 overlapProcp overlapFunc
, /* Function to call for over-lapping bands */
957 nonOverlapProcp nonOverlap1Func
, /* Function to call for non-overlapping bands in region 1 */
958 nonOverlapProcp nonOverlap2Func
) /* Function to call for non-overlapping bands in region 2 */
960 RECTL
*r1
; /* Pointer into first region */
961 RECTL
*r2
; /* Pointer into 2d region */
962 RECTL
*r1End
; /* End of 1st region */
963 RECTL
*r2End
; /* End of 2d region */
964 INT ybot
; /* Bottom of intersection */
965 INT ytop
; /* Top of intersection */
966 RECTL
*oldRects
; /* Old rects for newReg */
967 ULONG prevBand
; /* Index of start of
968 * Previous band in newReg */
969 ULONG curBand
; /* Index of start of current band in newReg */
970 RECTL
*r1BandEnd
; /* End of current band in r1 */
971 RECTL
*r2BandEnd
; /* End of current band in r2 */
972 ULONG top
; /* Top of non-overlapping band */
973 ULONG bot
; /* Bottom of non-overlapping band */
976 * set r1, r2, r1End and r2End appropriately, preserve the important
977 * parts of the destination region until the end in case it's one of
978 * the two source regions, then mark the "new" region empty, allocating
979 * another array of rectangles for it to use. */
982 r1End
= r1
+ reg1
->rdh
.nCount
;
983 r2End
= r2
+ reg2
->rdh
.nCount
;
985 /* newReg may be one of the src regions so we can't empty it. We keep a
986 * note of its rects pointer (so that we can free them later), preserve its
987 * extents and simply set numRects to zero. */
988 oldRects
= newReg
->Buffer
;
989 newReg
->rdh
.nCount
= 0;
991 /* Allocate a reasonable number of rectangles for the new region. The idea
992 * is to allocate enough so the individual functions don't need to
993 * reallocate and copy the array, which is time consuming, yet we don't
994 * have to worry about using too much memory. I hope to be able to
995 * nuke the Xrealloc() at the end of this function eventually. */
996 newReg
->rdh
.nRgnSize
= max(reg1
->rdh
.nCount
+ 1, reg2
->rdh
.nCount
) * 2 * sizeof(RECT
);
998 newReg
->Buffer
= ExAllocatePoolWithTag(PagedPool
,
999 newReg
->rdh
.nRgnSize
,
1001 if (newReg
->Buffer
== NULL
)
1003 newReg
->rdh
.nRgnSize
= 0;
1007 /* Initialize ybot and ytop.
1008 * In the upcoming loop, ybot and ytop serve different functions depending
1009 * on whether the band being handled is an overlapping or non-overlapping
1011 * In the case of a non-overlapping band (only one of the regions
1012 * has points in the band), ybot is the bottom of the most recent
1013 * intersection and thus clips the top of the rectangles in that band.
1014 * ytop is the top of the next intersection between the two regions and
1015 * serves to clip the bottom of the rectangles in the current band.
1016 * For an overlapping band (where the two regions intersect), ytop clips
1017 * the top of the rectangles of both regions and ybot clips the bottoms. */
1018 if (reg1
->rdh
.rcBound
.top
< reg2
->rdh
.rcBound
.top
)
1019 ybot
= reg1
->rdh
.rcBound
.top
;
1021 ybot
= reg2
->rdh
.rcBound
.top
;
1023 /* prevBand serves to mark the start of the previous band so rectangles
1024 * can be coalesced into larger rectangles. qv. miCoalesce, above.
1025 * In the beginning, there is no previous band, so prevBand == curBand
1026 * (curBand is set later on, of course, but the first band will always
1027 * start at index 0). prevBand and curBand must be indices because of
1028 * the possible expansion, and resultant moving, of the new region's
1029 * array of rectangles. */
1033 curBand
= newReg
->rdh
.nCount
;
1035 /* This algorithm proceeds one source-band (as opposed to a
1036 * destination band, which is determined by where the two regions
1037 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
1038 * rectangle after the last one in the current band for their
1039 * respective regions. */
1041 while ((r1BandEnd
!= r1End
) && (r1BandEnd
->top
== r1
->top
))
1047 while ((r2BandEnd
!= r2End
) && (r2BandEnd
->top
== r2
->top
))
1052 /* First handle the band that doesn't intersect, if any.
1054 * Note that attention is restricted to one band in the
1055 * non-intersecting region at once, so if a region has n
1056 * bands between the current position and the next place it overlaps
1057 * the other, this entire loop will be passed through n times. */
1058 if (r1
->top
< r2
->top
)
1060 top
= max(r1
->top
,ybot
);
1061 bot
= min(r1
->bottom
,r2
->top
);
1063 if ((top
!= bot
) && (nonOverlap1Func
!= NULL
))
1065 (*nonOverlap1Func
)(newReg
, r1
, r1BandEnd
, top
, bot
);
1070 else if (r2
->top
< r1
->top
)
1072 top
= max(r2
->top
,ybot
);
1073 bot
= min(r2
->bottom
,r1
->top
);
1075 if ((top
!= bot
) && (nonOverlap2Func
!= NULL
))
1077 (*nonOverlap2Func
)(newReg
, r2
, r2BandEnd
, top
, bot
);
1087 /* If any rectangles got added to the region, try and coalesce them
1088 * with rectangles from the previous band. Note we could just do
1089 * this test in miCoalesce, but some machines incur a not
1090 * inconsiderable cost for function calls, so... */
1091 if (newReg
->rdh
.nCount
!= curBand
)
1093 prevBand
= REGION_Coalesce(newReg
, prevBand
, curBand
);
1096 /* Now see if we've hit an intersecting band. The two bands only
1097 * intersect if ybot > ytop */
1098 ybot
= min(r1
->bottom
, r2
->bottom
);
1099 curBand
= newReg
->rdh
.nCount
;
1102 (*overlapFunc
)(newReg
, r1
, r1BandEnd
, r2
, r2BandEnd
, ytop
, ybot
);
1105 if (newReg
->rdh
.nCount
!= curBand
)
1107 prevBand
= REGION_Coalesce(newReg
, prevBand
, curBand
);
1110 /* If we've finished with a band (bottom == ybot) we skip forward
1111 * in the region to the next band. */
1112 if (r1
->bottom
== ybot
)
1116 if (r2
->bottom
== ybot
)
1121 while ((r1
!= r1End
) && (r2
!= r2End
));
1123 /* Deal with whichever region still has rectangles left. */
1124 curBand
= newReg
->rdh
.nCount
;
1127 if (nonOverlap1Func
!= NULL
)
1132 while ((r1BandEnd
< r1End
) && (r1BandEnd
->top
== r1
->top
))
1137 (*nonOverlap1Func
)(newReg
,
1144 while (r1
!= r1End
);
1147 else if ((r2
!= r2End
) && (nonOverlap2Func
!= NULL
))
1152 while ((r2BandEnd
< r2End
) && (r2BandEnd
->top
== r2
->top
))
1157 (*nonOverlap2Func
)(newReg
,
1164 while (r2
!= r2End
);
1167 if (newReg
->rdh
.nCount
!= curBand
)
1169 (VOID
)REGION_Coalesce(newReg
, prevBand
, curBand
);
1172 /* A bit of cleanup. To keep regions from growing without bound,
1173 * we shrink the array of rectangles to match the new number of
1174 * rectangles in the region. This never goes to 0, however...
1176 * Only do this stuff if the number of rectangles allocated is more than
1177 * twice the number of rectangles in the region (a simple optimization...). */
1178 if ((newReg
->rdh
.nRgnSize
> (2 * newReg
->rdh
.nCount
* sizeof(RECT
))) &&
1179 (newReg
->rdh
.nCount
> 2))
1181 if (REGION_NOT_EMPTY(newReg
))
1183 RECTL
*prev_rects
= newReg
->Buffer
;
1184 newReg
->Buffer
= ExAllocatePoolWithTag(PagedPool
,
1185 newReg
->rdh
.nCount
* sizeof(RECT
),
1188 if (newReg
->Buffer
== NULL
)
1190 newReg
->Buffer
= prev_rects
;
1194 newReg
->rdh
.nRgnSize
= newReg
->rdh
.nCount
*sizeof(RECT
);
1195 COPY_RECTS(newReg
->Buffer
, prev_rects
, newReg
->rdh
.nCount
);
1196 if (prev_rects
!= &newReg
->rdh
.rcBound
)
1197 ExFreePoolWithTag(prev_rects
, TAG_REGION
);
1202 /* No point in doing the extra work involved in an Xrealloc if
1203 * the region is empty */
1204 newReg
->rdh
.nRgnSize
= sizeof(RECT
);
1205 if (newReg
->Buffer
!= &newReg
->rdh
.rcBound
)
1206 ExFreePoolWithTag(newReg
->Buffer
, TAG_REGION
);
1208 newReg
->Buffer
= ExAllocatePoolWithTag(PagedPool
,
1211 ASSERT(newReg
->Buffer
);
1215 newReg
->rdh
.iType
= RDH_RECTANGLES
;
1217 if (oldRects
!= &newReg
->rdh
.rcBound
)
1218 ExFreePoolWithTag(oldRects
, TAG_REGION
);
1222 /***********************************************************************
1223 * Region Intersection
1224 ***********************************************************************/
1228 * Handle an overlapping band for REGION_Intersect.
1233 * \note Side Effects:
1234 * Rectangles may be added to the region.
1251 while ((r1
!= r1End
) && (r2
!= r2End
))
1253 left
= max(r1
->left
, r2
->left
);
1254 right
= min(r1
->right
, r2
->right
);
1256 /* If there's any overlap between the two rectangles, add that
1257 * overlap to the new region.
1258 * There's no need to check for subsumption because the only way
1259 * such a need could arise is if some region has two rectangles
1260 * right next to each other. Since that should never happen... */
1263 if (!REGION_bAddRect(pReg
, left
, top
, right
, bottom
))
1269 /* Need to advance the pointers. Shift the one that extends
1270 * to the right the least, since the other still has a chance to
1271 * overlap with that region's next rectangle, if you see what I mean. */
1272 if (r1
->right
< r2
->right
)
1276 else if (r2
->right
< r1
->right
)
1290 /***********************************************************************
1291 * REGION_IntersectRegion
1296 REGION_IntersectRegion(
1301 /* Check for trivial reject */
1302 if ((reg1
->rdh
.nCount
== 0) ||
1303 (reg2
->rdh
.nCount
== 0) ||
1304 (EXTENTCHECK(®1
->rdh
.rcBound
, ®2
->rdh
.rcBound
) == 0))
1306 newReg
->rdh
.nCount
= 0;
1310 REGION_RegionOp(newReg
,
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
);
1326 /***********************************************************************
1328 ***********************************************************************/
1331 * Handle a non-overlapping band for the union operation. Just
1332 * Adds the rectangles into the region. Doesn't have to check for
1333 * subsumption or anything.
1338 * \note Side Effects:
1339 * pReg->numRects is incremented and the final rectangles overwritten
1340 * with the rectangles we're passed.
1355 if (!REGION_bEnsureBufferSize(pReg
, pReg
->rdh
.nCount
+ (rEnd
- r
)))
1362 REGION_vAddRect(pReg
, r
->left
, top
, r
->right
, bottom
);
1374 _Inout_ PREGION prgn
,
1380 if ((prgn
->rdh
.nCount
!= 0) &&
1381 (prgn
->Buffer
[prgn
->rdh
.nCount
- 1].top
== top
) &&
1382 (prgn
->Buffer
[prgn
->rdh
.nCount
- 1].bottom
== bottom
) &&
1383 (prgn
->Buffer
[prgn
->rdh
.nCount
- 1].right
>= left
))
1385 if (prgn
->Buffer
[prgn
->rdh
.nCount
- 1].right
< right
)
1387 prgn
->Buffer
[prgn
->rdh
.nCount
- 1].right
= right
;
1392 if (!REGION_bAddRect(prgn
, left
, top
, right
, bottom
))
1402 * Handle an overlapping band for the union operation. Picks the
1403 * left-most rectangle each time and merges it into the region.
1408 * \note Side Effects:
1409 * Rectangles are overwritten in pReg->rects and pReg->numRects will
1425 while ((r1
!= r1End
) && (r2
!= r2End
))
1427 if (r1
->left
< r2
->left
)
1429 REGION_bMergeRect(pReg
, r1
->left
, top
, r1
->right
, bottom
);
1434 REGION_bMergeRect(pReg
, r2
->left
, top
, r2
->right
, bottom
);
1443 REGION_bMergeRect(pReg
, r1
->left
, top
, r1
->right
, bottom
);
1446 while (r1
!= r1End
);
1452 REGION_bMergeRect(pReg
, r2
->left
, top
, r2
->right
, bottom
);
1460 /***********************************************************************
1461 * REGION_UnionRegion
1471 /* Checks all the simple cases
1472 * Region 1 and 2 are the same or region 1 is empty */
1473 if ((reg1
== reg2
) || (reg1
->rdh
.nCount
== 0) ||
1474 (reg1
->rdh
.rcBound
.right
<= reg1
->rdh
.rcBound
.left
) ||
1475 (reg1
->rdh
.rcBound
.bottom
<= reg1
->rdh
.rcBound
.top
))
1479 REGION_CopyRegion(newReg
, reg2
);
1485 /* If nothing to union (region 2 empty) */
1486 if ((reg2
->rdh
.nCount
== 0) ||
1487 (reg2
->rdh
.rcBound
.right
<= reg2
->rdh
.rcBound
.left
) ||
1488 (reg2
->rdh
.rcBound
.bottom
<= reg2
->rdh
.rcBound
.top
))
1492 REGION_CopyRegion(newReg
, reg1
);
1498 /* Region 1 completely subsumes region 2 */
1499 if ((reg1
->rdh
.nCount
== 1) &&
1500 (reg1
->rdh
.rcBound
.left
<= reg2
->rdh
.rcBound
.left
) &&
1501 (reg1
->rdh
.rcBound
.top
<= reg2
->rdh
.rcBound
.top
) &&
1502 (reg2
->rdh
.rcBound
.right
<= reg1
->rdh
.rcBound
.right
) &&
1503 (reg2
->rdh
.rcBound
.bottom
<= reg1
->rdh
.rcBound
.bottom
))
1507 REGION_CopyRegion(newReg
, reg1
);
1513 /* Region 2 completely subsumes region 1 */
1514 if ((reg2
->rdh
.nCount
== 1) &&
1515 (reg2
->rdh
.rcBound
.left
<= reg1
->rdh
.rcBound
.left
) &&
1516 (reg2
->rdh
.rcBound
.top
<= reg1
->rdh
.rcBound
.top
) &&
1517 (reg1
->rdh
.rcBound
.right
<= reg2
->rdh
.rcBound
.right
) &&
1518 (reg1
->rdh
.rcBound
.bottom
<= reg2
->rdh
.rcBound
.bottom
))
1522 REGION_CopyRegion(newReg
, reg2
);
1528 REGION_RegionOp(newReg
,
1535 newReg
->rdh
.rcBound
.left
= min(reg1
->rdh
.rcBound
.left
, reg2
->rdh
.rcBound
.left
);
1536 newReg
->rdh
.rcBound
.top
= min(reg1
->rdh
.rcBound
.top
, reg2
->rdh
.rcBound
.top
);
1537 newReg
->rdh
.rcBound
.right
= max(reg1
->rdh
.rcBound
.right
, reg2
->rdh
.rcBound
.right
);
1538 newReg
->rdh
.rcBound
.bottom
= max(reg1
->rdh
.rcBound
.bottom
, reg2
->rdh
.rcBound
.bottom
);
1541 /***********************************************************************
1542 * Region Subtraction
1543 ***********************************************************************/
1546 * Deal with non-overlapping band for subtraction. Any parts from
1547 * region 2 we discard. Anything from region 1 we add to the region.
1552 * \note Side Effects:
1553 * pReg may be affected.
1559 REGION_SubtractNonO1(
1568 if (!REGION_bEnsureBufferSize(pReg
, pReg
->rdh
.nCount
+ (rEnd
- r
)))
1575 REGION_vAddRect(pReg
, r
->left
, top
, r
->right
, bottom
);
1586 * Overlapping band subtraction. x1 is the left-most point not yet
1592 * \note Side Effects:
1593 * pReg may have rectangles added to it.
1612 while ((r1
!= r1End
) && (r2
!= r2End
))
1614 if (r2
->right
<= left
)
1616 /* Subtrahend missed the boat: go to next subtrahend. */
1619 else if (r2
->left
<= left
)
1621 /* Subtrahend preceeds minuend: nuke left edge of minuend. */
1623 if (left
>= r1
->right
)
1625 /* Minuend completely covered: advance to next minuend and
1626 * reset left fence to edge of new minuend. */
1633 /* Subtrahend now used up since it doesn't extend beyond
1638 else if (r2
->left
< r1
->right
)
1640 /* Left part of subtrahend covers part of minuend: add uncovered
1641 * part of minuend to region and skip to next subtrahend. */
1642 if (!REGION_bAddRect(pReg
, left
, top
, r2
->left
, bottom
))
1648 if (left
>= r1
->right
)
1650 /* Minuend used up: advance to new... */
1657 /* Subtrahend used up */
1663 /* Minuend used up: add any remaining piece before advancing. */
1664 if (r1
->right
> left
)
1666 if (!REGION_bAddRect(pReg
, left
, top
, r1
->right
, bottom
))
1678 /* Make sure the buffer is large enough for all remaining operations */
1681 if (!REGION_bEnsureBufferSize(pReg
, pReg
->rdh
.nCount
+ (r1End
- r1
)))
1686 /* Add remaining minuend rectangles to region. */
1689 REGION_vAddRect(pReg
, left
, top
, r1
->right
, bottom
);
1696 while (r1
!= r1End
);
1703 * Subtract regS from regM and leave the result in regD.
1704 * S stands for subtrahend, M for minuend and D for difference.
1709 * \note Side Effects:
1710 * regD is overwritten.
1716 REGION_SubtractRegion(
1721 /* Check for trivial reject */
1722 if ((regM
->rdh
.nCount
== 0) ||
1723 (regS
->rdh
.nCount
== 0) ||
1724 (EXTENTCHECK(®M
->rdh
.rcBound
, ®S
->rdh
.rcBound
) == 0))
1726 REGION_CopyRegion(regD
, regM
);
1730 REGION_RegionOp(regD
,
1734 REGION_SubtractNonO1
,
1737 /* Can't alter newReg's extents before we call miRegionOp because
1738 * it might be one of the source regions and miRegionOp depends
1739 * on the extents of those regions being the unaltered. Besides, this
1740 * way there's no checking against rectangles that will be nuked
1741 * due to coalescing, so we have to examine fewer rectangles. */
1742 REGION_SetExtents(regD
);
1745 /***********************************************************************
1759 // FIXME: Don't use a handle
1760 tra
= REGION_AllocRgnWithHandle(sra
->rdh
.nCount
+ 1);
1765 htra
= tra
->BaseObject
.hHmgr
;
1767 // FIXME: Don't use a handle
1768 trb
= REGION_AllocRgnWithHandle(srb
->rdh
.nCount
+ 1);
1771 REGION_UnlockRgn(tra
);
1772 GreDeleteObject(htra
);
1775 htrb
= trb
->BaseObject
.hHmgr
;
1777 REGION_SubtractRegion(tra
, sra
, srb
);
1778 REGION_SubtractRegion(trb
, srb
, sra
);
1779 REGION_UnionRegion(dr
, tra
, trb
);
1780 REGION_UnlockRgn(tra
);
1781 REGION_UnlockRgn(trb
);
1783 GreDeleteObject(htra
);
1784 GreDeleteObject(htrb
);
1790 * Adds a rectangle to a REGION
1794 REGION_UnionRectWithRgn(
1800 region
.Buffer
= ®ion
.rdh
.rcBound
;
1801 region
.rdh
.nCount
= 1;
1802 region
.rdh
.nRgnSize
= sizeof(RECT
);
1803 region
.rdh
.rcBound
= *rect
;
1804 REGION_UnionRegion(rgn
, rgn
, ®ion
);
1809 REGION_SubtractRectFromRgn(
1816 rgnLocal
.Buffer
= &rgnLocal
.rdh
.rcBound
;
1817 rgnLocal
.rdh
.nCount
= 1;
1818 rgnLocal
.rdh
.nRgnSize
= sizeof(RECT
);
1819 rgnLocal
.rdh
.rcBound
= *prcl
;
1820 REGION_SubtractRegion(prgnDest
, prgnSrc
, &rgnLocal
);
1821 return REGION_Complexity(prgnDest
);
1826 REGION_bMakeSimpleFrameRgn(
1827 _Inout_ PREGION prgn
,
1828 _In_ PRECTL prclSrc
,
1835 NT_ASSERT((cx
>= 0) && (cy
>= 0));
1836 NT_ASSERT((prclSrc
->bottom
> prclSrc
->top
) &&
1837 (prclSrc
->right
> prclSrc
->left
));
1839 /* Start with an empty region */
1842 /* Check for the case where the frame covers the whole rect */
1843 if (((prclSrc
->bottom
- prclSrc
->top
) <= cy
* 2) ||
1844 ((prclSrc
->right
- prclSrc
->left
) <= cx
* 2))
1846 prgn
->rdh
.rcBound
= *prclSrc
;
1847 prgn
->Buffer
[0] = *prclSrc
;
1848 prgn
->rdh
.nCount
= 1;
1857 arcl
[i
].left
= prclSrc
->left
;
1858 arcl
[i
].top
= prclSrc
->top
;
1859 arcl
[i
].right
= prclSrc
->right
;
1860 arcl
[i
].bottom
= prclSrc
->top
+ cy
;
1866 /* Left rectangle */
1867 arcl
[i
].left
= prclSrc
->left
;
1868 arcl
[i
].top
= prclSrc
->top
+ cy
;
1869 arcl
[i
].right
= prclSrc
->left
+ cx
;
1870 arcl
[i
].bottom
= prclSrc
->bottom
- cy
;
1873 /* Right rectangle */
1874 arcl
[i
].left
= prclSrc
->right
- cx
;
1875 arcl
[i
].top
= prclSrc
->top
+ cy
;
1876 arcl
[i
].right
= prclSrc
->right
;
1877 arcl
[i
].bottom
= prclSrc
->bottom
- cy
;
1883 /* Bottom rectangle */
1884 arcl
[i
].left
= prclSrc
->left
;
1885 arcl
[i
].top
= prclSrc
->bottom
- cy
;
1886 arcl
[i
].right
= prclSrc
->right
;
1887 arcl
[i
].bottom
= prclSrc
->bottom
;
1893 /* The frame results in a complex region. rcBounds remains
1894 the same, though. */
1895 prgn
->rdh
.nCount
= i
;
1896 NT_ASSERT(prgn
->rdh
.nCount
> 1);
1897 prgn
->rdh
.nRgnSize
= prgn
->rdh
.nCount
* sizeof(RECT
);
1898 NT_ASSERT(prgn
->Buffer
== &prgn
->rdh
.rcBound
);
1899 prgn
->Buffer
= ExAllocatePoolWithTag(PagedPool
,
1902 if (prgn
->Buffer
== NULL
)
1904 prgn
->rdh
.nRgnSize
= 0;
1908 _PRAGMA_WARNING_SUPPRESS(__WARNING_MAYBE_UNINIT_VAR
) // arcl is initialized
1909 COPY_RECTS(prgn
->Buffer
, arcl
, prgn
->rdh
.nCount
);
1917 REGION_bMakeFrameRegion(
1918 _Inout_ PREGION prgnDest
,
1919 _Inout_ PREGION prgnSrc
,
1923 /* Handle negative cx / cy */
1927 /* Check border size (the cast is necessary to catch cx/cy == INT_MIN!) */
1928 if (((UINT
)cx
> MAX_COORD
) || ((UINT
)cy
> MAX_COORD
))
1933 /* Fail on empty source region */
1934 if (!REGION_NOT_EMPTY(prgnSrc
))
1939 /* Handle trivial case */
1940 if ((cx
== 0) && (cy
== 0))
1942 EMPTY_REGION(prgnDest
);
1946 /* Handle simple source region */
1947 if (REGION_Complexity(prgnSrc
) == SIMPLEREGION
)
1949 return REGION_bMakeSimpleFrameRgn(prgnDest
, &prgnSrc
->rdh
.rcBound
, cx
, cy
);
1952 /* Check if we can move the region to create the frame region */
1953 if ((prgnSrc
->rdh
.rcBound
.left
< (MIN_COORD
+ cx
)) ||
1954 (prgnSrc
->rdh
.rcBound
.top
< (MIN_COORD
+ cy
)) ||
1955 (prgnSrc
->rdh
.rcBound
.right
> (MAX_COORD
- cx
)) ||
1956 (prgnSrc
->rdh
.rcBound
.bottom
> (MAX_COORD
- cy
)))
1961 /* Copy the source region */
1962 if (!REGION_CopyRegion(prgnDest
, prgnSrc
))
1967 /* Move the source region to the bottom-right */
1968 NT_VERIFY(REGION_bOffsetRgn(prgnSrc
, cx
, cy
));
1970 /* Intersect with the source region (this crops the top-left frame) */
1971 REGION_IntersectRegion(prgnDest
, prgnDest
, prgnSrc
);
1973 /* Move the source region to the bottom-left */
1974 NT_VERIFY(REGION_bOffsetRgn(prgnSrc
, -2 * cx
, 0));
1976 /* Intersect with the source region (this crops the top-right frame) */
1977 REGION_IntersectRegion(prgnDest
, prgnDest
, prgnSrc
);
1979 /* Move the source region to the top-left */
1980 NT_VERIFY(REGION_bOffsetRgn(prgnSrc
, 0, -2 * cy
));
1982 /* Intersect with the source region (this crops the bottom-right frame) */
1983 REGION_IntersectRegion(prgnDest
, prgnDest
, prgnSrc
);
1985 /* Move the source region to the top-right */
1986 NT_VERIFY(REGION_bOffsetRgn(prgnSrc
, 2 * cx
, 0));
1988 /* Intersect with the source region (this crops the bottom-left frame) */
1989 REGION_IntersectRegion(prgnDest
, prgnDest
, prgnSrc
);
1991 /* Move the source region back to the original position */
1992 NT_VERIFY(REGION_bOffsetRgn(prgnSrc
, -cx
, cy
));
1994 /* Finally subtract the cropped region from the source */
1995 REGION_SubtractRegion(prgnDest
, prgnSrc
, prgnDest
);
2007 PREGION prgnFrame
, prgnSrc
;
2010 /* Allocate a new region */
2011 prgnFrame
= REGION_AllocUserRgnWithHandle(1);
2012 if (prgnFrame
== NULL
)
2014 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2018 /* Lock the source region */
2019 prgnSrc
= REGION_LockRgn(hrgn
);
2020 if (prgnSrc
== NULL
)
2022 REGION_Delete(prgnFrame
);
2026 if (REGION_bMakeFrameRegion(prgnFrame
, prgnSrc
, cx
, cy
))
2028 hrgnFrame
= prgnFrame
->BaseObject
.hHmgr
;
2029 REGION_UnlockRgn(prgnFrame
);
2033 REGION_Delete(prgnFrame
);
2037 REGION_UnlockRgn(prgnSrc
);
2044 _Inout_ PREGION prgn
,
2054 /* Check for zero rectangles and return TRUE for translation only matrices */
2055 if (prgn
->rdh
.nCount
< 1)
2056 return (pmx
->flAccel
& XFORM_UNITY
) != 0;
2058 /* Check if this is a scaling only matrix (off-diagonal elements are 0 */
2059 if (pmx
->flAccel
& XFORM_SCALE
)
2061 /* Check if this is a translation only matrix */
2062 if (pmx
->flAccel
& XFORM_UNITY
)
2064 /* Just offset the region */
2065 return REGION_bOffsetRgn(prgn
, (pmx
->fxDx
+ 8) / 16, (pmx
->fxDy
+ 8) / 16);
2069 /* Initialize the xform object */
2070 XFORMOBJ_vInit(&xo
, pmx
);
2072 /* Scaling can move the rects out of the coordinate space, so
2073 * we first need to check whether we can apply the transformation
2074 * on the bounds rect without modifying the region */
2075 if (!XFORMOBJ_bApplyXform(&xo
, XF_LTOL
, 2, &prgn
->rdh
.rcBound
, &rect
))
2080 /* Apply the xform to the rects in the region */
2081 if (!XFORMOBJ_bApplyXform(&xo
,
2083 prgn
->rdh
.nCount
* 2,
2087 /* This can not happen, since we already checked the bounds! */
2092 RECTL_vSetEmptyRect(&prgn
->rdh
.rcBound
);
2094 /* Loop all rects in the region */
2095 for (i
= 0; i
< prgn
->rdh
.nCount
; i
++)
2097 /* Make sure the rect is well-ordered after the xform */
2098 RECTL_vMakeWellOrdered(&prgn
->Buffer
[i
]);
2101 RECTL_bUnionRect(&prgn
->rdh
.rcBound
,
2106 /* Loop all rects in the region */
2107 for (i
= 0; i
< prgn
->rdh
.nCount
- 1; i
++)
2109 for (j
= i
; i
< prgn
->rdh
.nCount
; i
++)
2111 NT_ASSERT(prgn
->Buffer
[i
].top
< prgn
->Buffer
[i
].bottom
);
2112 NT_ASSERT(prgn
->Buffer
[j
].top
>= prgn
->Buffer
[i
].top
);
2121 /* Allocate a buffer for the polygons */
2122 cjSize
= prgn
->rdh
.nCount
* (4 * sizeof(POINT
) + sizeof(ULONG
));
2123 ppt
= ExAllocatePoolWithTag(PagedPool
, cjSize
, GDITAG_REGION
);
2129 /* Fill the buffer with the rects */
2130 pcPoints
= (PULONG
)&ppt
[4 * prgn
->rdh
.nCount
];
2131 for (i
= 0; i
< prgn
->rdh
.nCount
; i
++)
2133 /* Make sure the rect is within the legal range */
2135 ppt
[4 * i
+ 0].x
= prgn
->Buffer
[i
].left
;
2136 ppt
[4 * i
+ 0].y
= prgn
->Buffer
[i
].top
;
2137 ppt
[4 * i
+ 1].x
= prgn
->Buffer
[i
].right
;
2138 ppt
[4 * i
+ 1].y
= prgn
->Buffer
[i
].top
;
2139 ppt
[4 * i
+ 2].x
= prgn
->Buffer
[i
].right
;
2140 ppt
[4 * i
+ 2].y
= prgn
->Buffer
[i
].bottom
;
2141 ppt
[4 * i
+ 3].x
= prgn
->Buffer
[i
].left
;
2142 ppt
[4 * i
+ 3].y
= prgn
->Buffer
[i
].bottom
;
2145 /* Initialize the xform object */
2146 XFORMOBJ_vInit(&xo
, pmx
);
2148 /* Apply the xform to the rects in the buffer */
2149 if (!XFORMOBJ_bApplyXform(&xo
,
2151 prgn
->rdh
.nCount
* 2,
2155 /* This means, there were coordinates that would go outside of
2156 the coordinate space after the transformation */
2157 ExFreePoolWithTag(ppt
, GDITAG_REGION
);
2161 /* Now use the polygons to create a polygon region */
2162 bResult
= REGION_SetPolyPolygonRgn(prgn
,
2168 /* Free the polygon buffer */
2169 ExFreePoolWithTag(ppt
, GDITAG_REGION
);
2179 REGION_AllocRgnWithHandle(
2185 pReg
= (PREGION
)GDIOBJ_AllocateObject(GDIObjType_RGN_TYPE
,
2187 BASEFLAG_LOOKASIDE
);
2190 DPRINT1("Could not allocate a palette.\n");
2194 //hReg = pReg->BaseObject.hHmgr;
2196 if ((nReg
== 0) || (nReg
== 1))
2198 /* Testing shows that > 95% of all regions have only 1 rect.
2199 Including that here saves us from having to do another allocation */
2200 pReg
->Buffer
= &pReg
->rdh
.rcBound
;
2204 pReg
->Buffer
= ExAllocatePoolWithTag(PagedPool
,
2205 nReg
* sizeof(RECT
),
2207 if (pReg
->Buffer
== NULL
)
2209 DPRINT1("Could not allocate region buffer\n");
2210 GDIOBJ_vDeleteObject(&pReg
->BaseObject
);
2216 pReg
->rdh
.dwSize
= sizeof(RGNDATAHEADER
);
2217 pReg
->rdh
.nCount
= nReg
;
2218 pReg
->rdh
.nRgnSize
= nReg
* sizeof(RECT
);
2219 pReg
->prgnattr
= &pReg
->rgnattr
;
2221 /* Initialize the region attribute */
2222 pReg
->rgnattr
.AttrFlags
= 0;
2223 pReg
->rgnattr
.iComplexity
= SIMPLEREGION
;
2224 pReg
->rgnattr
.Rect
= pReg
->rdh
.rcBound
;
2226 /* Finally insert the region into the handle table */
2227 if (!GDIOBJ_hInsertObject(&pReg
->BaseObject
, GDI_OBJ_HMGR_POWNED
))
2229 DPRINT1("Could not insert palette into handle table.\n");
2230 GDIOBJ_vFreeObject(&pReg
->BaseObject
);
2239 REGION_bAllocRgnAttr(
2245 NT_ASSERT(prgn
->prgnattr
== &prgn
->rgnattr
);
2247 ppi
= PsGetCurrentProcessWin32Process();
2250 prgnattr
= GdiPoolAllocate(ppi
->pPoolRgnAttr
);
2251 if (prgnattr
== NULL
)
2253 DPRINT1("Could not allocate RGN attr\n");
2257 /* Copy the current region attribute */
2258 *prgnattr
= prgn
->rgnattr
;
2260 /* Set the object attribute in the handle table */
2261 prgn
->prgnattr
= prgnattr
;
2262 GDIOBJ_vSetObjectAttr(&prgn
->BaseObject
, prgnattr
);
2269 // Allocate User Space Region Handle.
2273 REGION_AllocUserRgnWithHandle(
2278 prgn
= REGION_AllocRgnWithHandle(nRgn
);
2284 if (!REGION_bAllocRgnAttr(prgn
))
2299 NT_ASSERT(prgn
!= NULL
);
2300 NT_ASSERT(prgn
->prgnattr
!= NULL
);
2301 NT_ASSERT((prgn
->prgnattr
== &prgn
->rgnattr
) ||
2302 (prgn
->prgnattr
->AttrFlags
& ATTR_RGN_VALID
));
2304 /* Get the region attribute and check if it's dirty (modified) */
2305 prgnattr
= prgn
->prgnattr
;
2306 if (prgnattr
->AttrFlags
& ATTR_RGN_DIRTY
)
2308 NT_ASSERT(GreGetObjectOwner(prgn
->BaseObject
.hHmgr
) == GDI_OBJ_HMGR_POWNED
);
2309 NT_ASSERT(prgnattr
!= &prgn
->rgnattr
);
2311 if (prgnattr
->iComplexity
== NULLREGION
)
2315 else if (prgnattr
->iComplexity
== SIMPLEREGION
)
2317 REGION_SetRectRgn(prgn
,
2318 prgnattr
->Rect
.left
,
2320 prgnattr
->Rect
.right
,
2321 prgnattr
->Rect
.bottom
);
2325 /* Should not happen, region attribute is corrupted! */
2326 DPRINT1("Region attribute is corrupted, ignoring\n");
2331 /* Reset the flags */
2332 prgnattr
->AttrFlags
&= ~(ATTR_RGN_DIRTY
| ATTR_RGN_VALID
);
2342 prgn
= GDIOBJ_LockObject(hrgn
, GDIObjType_RGN_TYPE
);
2346 REGION_vSyncRegion(prgn
);
2357 NT_ASSERT(prgn
!= NULL
);
2358 NT_ASSERT(prgn
->prgnattr
!= NULL
);
2360 /* Get the region attribute and check if it's user mode */
2361 prgnattr
= prgn
->prgnattr
;
2362 if (prgnattr
!= &prgn
->rgnattr
)
2364 NT_ASSERT(GreGetObjectOwner(prgn
->BaseObject
.hHmgr
) == GDI_OBJ_HMGR_POWNED
);
2365 prgnattr
->iComplexity
= REGION_Complexity(prgn
);
2366 prgnattr
->Rect
.left
= prgn
->rdh
.rcBound
.left
;
2367 prgnattr
->Rect
.top
= prgn
->rdh
.rcBound
.top
;
2368 prgnattr
->Rect
.right
= prgn
->rdh
.rcBound
.right
;
2369 prgnattr
->Rect
.bottom
= prgn
->rdh
.rcBound
.bottom
;
2370 prgnattr
->AttrFlags
|= ATTR_RGN_VALID
;
2373 GDIOBJ_vUnlockObject(&prgn
->BaseObject
);
2378 These regions do not use attribute sections and when allocated, use gdiobj
2382 // System Region Functions
2386 IntSysCreateRectpRgn(
2394 /* Allocate a region, without a handle */
2395 prgn
= (PREGION
)GDIOBJ_AllocateObject(GDIObjType_RGN_TYPE
, sizeof(REGION
), BASEFLAG_LOOKASIDE
);
2402 prgn
->Buffer
= &prgn
->rdh
.rcBound
;
2403 prgn
->prgnattr
= &prgn
->rgnattr
;
2404 prgn
->prgnattr
->AttrFlags
= ATTR_RGN_VALID
;
2405 REGION_SetRectRgn(prgn
, LeftRect
, TopRect
, RightRect
, BottomRect
);
2412 REGION_vCleanup(PVOID ObjectBody
)
2414 PREGION pRgn
= (PREGION
)ObjectBody
;
2415 PPROCESSINFO ppi
= PsGetCurrentProcessWin32Process();
2418 ASSERT(pRgn
->prgnattr
);
2419 if (pRgn
->prgnattr
!= &pRgn
->rgnattr
)
2420 GdiPoolFree(ppi
->pPoolRgnAttr
, pRgn
->prgnattr
);
2422 if (pRgn
->Buffer
&& pRgn
->Buffer
!= &pRgn
->rdh
.rcBound
)
2423 ExFreePoolWithTag(pRgn
->Buffer
, TAG_REGION
);
2428 REGION_Delete(PREGION pRgn
)
2430 if (pRgn
== prgnDefault
)
2433 GDIOBJ_vDeleteObject(&pRgn
->BaseObject
);
2438 IntGdiSetRegionOwner(HRGN hRgn
, DWORD OwnerMask
)
2444 prgn
= REGION_LockRgn(hRgn
);
2450 prgnattr
= prgn
->prgnattr
;
2451 if (prgnattr
!= &prgn
->rgnattr
)
2453 GDIOBJ_vSetObjectAttr(&prgn
->BaseObject
, NULL
);
2454 prgn
->prgnattr
= &prgn
->rgnattr
;
2455 ppi
= PsGetCurrentProcessWin32Process();
2456 GdiPoolFree(ppi
->pPoolRgnAttr
, prgnattr
);
2459 REGION_UnlockRgn(prgn
);
2461 return GreSetObjectOwner(hRgn
, OwnerMask
);
2473 if (prgnDest
== NULL
)
2475 DPRINT("IntGdiCombineRgn: hDest unavailable\n");
2479 if (prgnSrc1
== NULL
)
2481 DPRINT("IntGdiCombineRgn: hSrc1 unavailable\n");
2485 if (iCombineMode
== RGN_COPY
)
2487 if (!REGION_CopyRegion(prgnDest
, prgnSrc1
))
2490 return REGION_Complexity(prgnDest
);
2493 if (prgnSrc2
== NULL
)
2495 DPRINT1("IntGdiCombineRgn requires hSrc2 != NULL for combine mode %d!\n", iCombineMode
);
2500 switch (iCombineMode
)
2503 REGION_IntersectRegion(prgnDest
, prgnSrc1
, prgnSrc2
);
2506 REGION_UnionRegion(prgnDest
, prgnSrc1
, prgnSrc2
);
2509 REGION_XorRegion(prgnDest
, prgnSrc1
, prgnSrc2
);
2512 REGION_SubtractRegion(prgnDest
, prgnSrc1
, prgnSrc2
);
2516 return REGION_Complexity(prgnDest
);
2529 *pRect
= Rgn
->rdh
.rcBound
;
2530 ret
= REGION_Complexity(Rgn
);
2534 return 0; // If invalid region return zero
2546 Rgn
= REGION_LockRgn(hRgn
);
2552 ret
= REGION_GetRgnBox(Rgn
, pRect
);
2553 REGION_UnlockRgn(Rgn
);
2569 if (prgn
->rdh
.nCount
> 0 && INRECT(prgn
->rdh
.rcBound
, X
, Y
))
2572 for (i
= 0; i
< prgn
->rdh
.nCount
; i
++)
2574 if (INRECT(r
[i
], X
, Y
))
2584 REGION_RectInRegion(
2588 PRECTL pCurRect
, pRectEnd
;
2591 /* Swap the coordinates to make right >= left and bottom >= top */
2592 /* (region building rectangles are normalized the same way) */
2593 if (rect
->top
> rect
->bottom
)
2595 rc
.top
= rect
->bottom
;
2596 rc
.bottom
= rect
->top
;
2601 rc
.bottom
= rect
->bottom
;
2604 if (rect
->right
< rect
->left
)
2606 rc
.right
= rect
->left
;
2607 rc
.left
= rect
->right
;
2611 rc
.right
= rect
->right
;
2612 rc
.left
= rect
->left
;
2615 /* This is (just) a useful optimization */
2616 if ((Rgn
->rdh
.nCount
> 0) && EXTENTCHECK(&Rgn
->rdh
.rcBound
, &rc
))
2618 for (pCurRect
= Rgn
->Buffer
, pRectEnd
= pCurRect
+
2619 Rgn
->rdh
.nCount
; pCurRect
< pRectEnd
; pCurRect
++)
2621 if (pCurRect
->bottom
<= rc
.top
)
2622 continue; /* Not far enough down yet */
2624 if (pCurRect
->top
>= rc
.bottom
)
2625 break; /* Too far down */
2627 if (pCurRect
->right
<= rc
.left
)
2628 continue; /* Not far enough over yet */
2630 if (pCurRect
->left
>= rc
.right
)
2653 if (LeftRect
> RightRect
)
2656 LeftRect
= RightRect
;
2660 if (TopRect
> BottomRect
)
2663 TopRect
= BottomRect
;
2667 if ((LeftRect
!= RightRect
) && (TopRect
!= BottomRect
))
2669 firstRect
= rgn
->Buffer
;
2671 firstRect
->left
= rgn
->rdh
.rcBound
.left
= LeftRect
;
2672 firstRect
->top
= rgn
->rdh
.rcBound
.top
= TopRect
;
2673 firstRect
->right
= rgn
->rdh
.rcBound
.right
= RightRect
;
2674 firstRect
->bottom
= rgn
->rdh
.rcBound
.bottom
= BottomRect
;
2675 rgn
->rdh
.nCount
= 1;
2676 rgn
->rdh
.iType
= RDH_RECTANGLES
;
2687 _Inout_ PREGION prgn
,
2694 NT_ASSERT(prgn
!= NULL
);
2696 /* Check for trivial case */
2697 if ((cx
== 0) && (cy
== 0))
2702 /* Check for empty regions, we ignore the offset values here */
2703 if (prgn
->rdh
.nCount
== 0)
2708 /* Make sure the offset is within the legal range */
2709 if ((cx
> MAX_COORD
) || (cx
< MIN_COORD
) ||
2710 (cy
> MAX_COORD
) || (cy
< MIN_COORD
))
2715 /* Are we moving right? */
2718 /* Check if we stay inside the bounds on the right side */
2719 if (prgn
->rdh
.rcBound
.right
> (MAX_COORD
- cx
))
2726 /* Check if we stay inside the bounds on the left side */
2727 if (prgn
->rdh
.rcBound
.left
< (MIN_COORD
- cx
))
2733 /* Are we moving down? */
2736 /* Check if we stay inside the bounds on the right side */
2737 if (prgn
->rdh
.rcBound
.bottom
> (MAX_COORD
- cy
))
2744 /* Check if we stay inside the bounds on the left side */
2745 if (prgn
->rdh
.rcBound
.top
< (MIN_COORD
- cy
))
2751 /* Loop to move the rects */
2752 prcl
= prgn
->Buffer
;
2753 for (i
= 0; i
< prgn
->rdh
.nCount
; i
++)
2756 prcl
[i
].right
+= cx
;
2758 prcl
[i
].bottom
+= cy
;
2761 /* Finally update the bounds rect */
2762 if (prgn
->Buffer
!= &prgn
->rdh
.rcBound
)
2764 prgn
->rdh
.rcBound
.left
+= cx
;
2765 prgn
->rdh
.rcBound
.right
+= cx
;
2766 prgn
->rdh
.rcBound
.top
+= cy
;
2767 prgn
->rdh
.rcBound
.bottom
+= cy
;
2773 /***********************************************************************
2774 * REGION_InsertEdgeInET
2776 * Insert the given edge into the edge table.
2777 * First we must find the correct bucket in the
2778 * Edge table, then find the right slot in the
2779 * bucket. Finally, we can insert it.
2785 REGION_InsertEdgeInET(
2787 EDGE_TABLE_ENTRY
*ETE
,
2789 SCANLINE_LISTBLOCK
**SLLBlock
,
2792 EDGE_TABLE_ENTRY
*start
, *prev
;
2793 SCANLINE_LIST
*pSLL
, *pPrevSLL
;
2794 SCANLINE_LISTBLOCK
*tmpSLLBlock
;
2796 /* Find the right bucket to put the edge into */
2797 pPrevSLL
= &ET
->scanlines
;
2798 pSLL
= pPrevSLL
->next
;
2799 while (pSLL
&& (pSLL
->scanline
< scanline
))
2805 /* Reassign pSLL (pointer to SCANLINE_LIST) if necessary */
2806 if ((!pSLL
) || (pSLL
->scanline
> scanline
))
2808 if (*iSLLBlock
> SLLSPERBLOCK
-1)
2810 tmpSLLBlock
= ExAllocatePoolWithTag(PagedPool
,
2811 sizeof(SCANLINE_LISTBLOCK
),
2813 if (tmpSLLBlock
== NULL
)
2815 DPRINT1("REGION_InsertEdgeInETL(): Can't alloc SLLB\n");
2816 /* FIXME: Free resources? */
2820 (*SLLBlock
)->next
= tmpSLLBlock
;
2821 tmpSLLBlock
->next
= (SCANLINE_LISTBLOCK
*)NULL
;
2822 *SLLBlock
= tmpSLLBlock
;
2826 pSLL
= &((*SLLBlock
)->SLLs
[(*iSLLBlock
)++]);
2828 pSLL
->next
= pPrevSLL
->next
;
2829 pSLL
->edgelist
= (EDGE_TABLE_ENTRY
*)NULL
;
2830 pPrevSLL
->next
= pSLL
;
2833 pSLL
->scanline
= scanline
;
2835 /* Now insert the edge in the right bucket */
2836 prev
= (EDGE_TABLE_ENTRY
*)NULL
;
2837 start
= pSLL
->edgelist
;
2838 while (start
&& (start
->bres
.minor_axis
< ETE
->bres
.minor_axis
))
2841 start
= start
->next
;
2849 pSLL
->edgelist
= ETE
;
2852 /***********************************************************************
2855 * This routine moves EDGE_TABLEEntries from the
2856 * EDGE_TABLE into the Active Edge Table,
2857 * leaving them sorted by smaller x coordinate.
2864 EDGE_TABLE_ENTRY
*AET
,
2865 EDGE_TABLE_ENTRY
*ETEs
)
2867 EDGE_TABLE_ENTRY
*pPrevAET
;
2868 EDGE_TABLE_ENTRY
*tmp
;
2874 while (AET
&& (AET
->bres
.minor_axis
< ETEs
->bres
.minor_axis
))
2885 ETEs
->back
= pPrevAET
;
2886 pPrevAET
->next
= ETEs
;
2893 /***********************************************************************
2894 * REGION_computeWAET
2896 * This routine links the AET by the
2897 * nextWETE (winding EDGE_TABLE_ENTRY) link for
2898 * use by the winding number rule. The final
2899 * Active Edge Table (AET) might look something
2903 * ---------- --------- ---------
2904 * |ymax | |ymax | |ymax |
2905 * | ... | |... | |... |
2906 * |next |->|next |->|next |->...
2907 * |nextWETE| |nextWETE| |nextWETE|
2908 * --------- --------- ^--------
2910 * V-------------------> V---> ...
2917 EDGE_TABLE_ENTRY
*AET
)
2919 register EDGE_TABLE_ENTRY
*pWETE
;
2920 register INT inside
= 1;
2921 register INT isInside
= 0;
2923 AET
->nextWETE
= (EDGE_TABLE_ENTRY
*)NULL
;
2933 if ((!inside
&& !isInside
) ||
2934 ( inside
&& isInside
))
2936 pWETE
->nextWETE
= AET
;
2943 pWETE
->nextWETE
= (EDGE_TABLE_ENTRY
*)NULL
;
2946 /***********************************************************************
2947 * REGION_InsertionSort
2949 * Just a simple insertion sort using
2950 * pointers and back pointers to sort the Active
2957 REGION_InsertionSort(
2958 EDGE_TABLE_ENTRY
*AET
)
2960 EDGE_TABLE_ENTRY
*pETEchase
;
2961 EDGE_TABLE_ENTRY
*pETEinsert
;
2962 EDGE_TABLE_ENTRY
*pETEchaseBackTMP
;
2963 BOOL changed
= FALSE
;
2970 while (pETEchase
->back
->bres
.minor_axis
> AET
->bres
.minor_axis
)
2971 pETEchase
= pETEchase
->back
;
2974 if (pETEchase
!= pETEinsert
)
2976 pETEchaseBackTMP
= pETEchase
->back
;
2977 pETEinsert
->back
->next
= AET
;
2979 AET
->back
= pETEinsert
->back
;
2981 pETEinsert
->next
= pETEchase
;
2982 pETEchase
->back
->next
= pETEinsert
;
2983 pETEchase
->back
= pETEinsert
;
2984 pETEinsert
->back
= pETEchaseBackTMP
;
2992 /***********************************************************************
2993 * REGION_FreeStorage
3001 SCANLINE_LISTBLOCK
*pSLLBlock
)
3003 SCANLINE_LISTBLOCK
*tmpSLLBlock
;
3007 tmpSLLBlock
= pSLLBlock
->next
;
3008 ExFreePoolWithTag(pSLLBlock
, TAG_REGION
);
3009 pSLLBlock
= tmpSLLBlock
;
3014 /***********************************************************************
3015 * REGION_PtsToRegion
3017 * Create an array of rectangles from a list of points.
3023 INT numFullPtBlocks
,
3025 POINTBLOCK
*FirstPtBlock
,
3030 POINTBLOCK
*CurPtBlock
;
3032 RECTL
*extents
, *temp
;
3035 extents
= ®
->rdh
.rcBound
;
3037 numRects
= ((numFullPtBlocks
* NUMPTSTOBUFFER
) + iCurPtBlock
) >> 1;
3039 /* Make sure, we have at least one rect */
3045 temp
= ExAllocatePoolWithTag(PagedPool
, numRects
* sizeof(RECT
), TAG_REGION
);
3051 if (reg
->Buffer
!= NULL
)
3053 COPY_RECTS(temp
, reg
->Buffer
, reg
->rdh
.nCount
);
3054 if (reg
->Buffer
!= ®
->rdh
.rcBound
)
3055 ExFreePoolWithTag(reg
->Buffer
, TAG_REGION
);
3059 reg
->rdh
.nCount
= numRects
;
3060 CurPtBlock
= FirstPtBlock
;
3061 rects
= reg
->Buffer
- 1;
3063 extents
->left
= LARGE_COORDINATE
, extents
->right
= SMALL_COORDINATE
;
3065 for ( ; numFullPtBlocks
>= 0; numFullPtBlocks
--)
3067 /* The loop uses 2 points per iteration */
3068 i
= NUMPTSTOBUFFER
>> 1;
3069 if (numFullPtBlocks
== 0)
3070 i
= iCurPtBlock
>> 1;
3072 for (pts
= CurPtBlock
->pts
; i
--; pts
+= 2)
3074 if (pts
->x
== pts
[1].x
)
3077 if ((numRects
&& pts
->x
== rects
->left
) &&
3078 (pts
->y
== rects
->bottom
) &&
3079 (pts
[1].x
== rects
->right
) &&
3080 ((numRects
== 1) || (rects
[-1].top
!= rects
->top
)) &&
3081 (i
&& pts
[2].y
> pts
[1].y
))
3083 rects
->bottom
= pts
[1].y
+ 1;
3089 rects
->left
= pts
->x
;
3090 rects
->top
= pts
->y
;
3091 rects
->right
= pts
[1].x
;
3092 rects
->bottom
= pts
[1].y
+ 1;
3094 if (rects
->left
< extents
->left
)
3095 extents
->left
= rects
->left
;
3096 if (rects
->right
> extents
->right
)
3097 extents
->right
= rects
->right
;
3100 CurPtBlock
= CurPtBlock
->next
;
3105 extents
->top
= reg
->Buffer
->top
;
3106 extents
->bottom
= rects
->bottom
;
3113 extents
->bottom
= 0;
3116 reg
->rdh
.nCount
= numRects
;
3121 /***********************************************************************
3122 * REGION_CreateETandAET
3124 * This routine creates the edge table for
3125 * scan converting polygons.
3126 * The Edge Table (ET) looks like:
3130 * | ymax | SCANLINE_LISTs
3131 * |scanline|-->------------>-------------->...
3132 * -------- |scanline| |scanline|
3133 * |edgelist| |edgelist|
3134 * --------- ---------
3138 * list of ETEs list of ETEs
3140 * where ETE is an EDGE_TABLE_ENTRY data structure,
3141 * and there is one SCANLINE_LIST per scanline at
3142 * which an edge is initially entered.
3148 REGION_CreateETandAET(
3153 EDGE_TABLE_ENTRY
*AET
,
3154 EDGE_TABLE_ENTRY
*pETEs
,
3155 SCANLINE_LISTBLOCK
*pSLLBlock
)
3157 const POINT
*top
, *bottom
;
3158 const POINT
*PrevPt
, *CurrPt
, *EndPt
;
3163 /* Initialize the Active Edge Table */
3164 AET
->next
= (EDGE_TABLE_ENTRY
*)NULL
;
3165 AET
->back
= (EDGE_TABLE_ENTRY
*)NULL
;
3166 AET
->nextWETE
= (EDGE_TABLE_ENTRY
*)NULL
;
3167 AET
->bres
.minor_axis
= SMALL_COORDINATE
;
3169 /* Initialize the Edge Table. */
3170 ET
->scanlines
.next
= (SCANLINE_LIST
*)NULL
;
3171 ET
->ymax
= SMALL_COORDINATE
;
3172 ET
->ymin
= LARGE_COORDINATE
;
3173 pSLLBlock
->next
= (SCANLINE_LISTBLOCK
*)NULL
;
3176 for (poly
= 0; poly
< nbpolygons
; poly
++)
3178 count
= Count
[poly
];
3185 /* For each vertex in the array of points.
3186 * In this loop we are dealing with two vertices at
3187 * a time -- these make up one edge of the polygon. */
3192 /* Find out which point is above and which is below. */
3193 if (PrevPt
->y
> CurrPt
->y
)
3195 bottom
= PrevPt
, top
= CurrPt
;
3196 pETEs
->ClockWise
= 0;
3200 bottom
= CurrPt
, top
= PrevPt
;
3201 pETEs
->ClockWise
= 1;
3204 /* Don't add horizontal edges to the Edge table. */
3205 if (bottom
->y
!= top
->y
)
3207 /* -1 so we don't get last scanline */
3208 pETEs
->ymax
= bottom
->y
- 1;
3210 /* Initialize integer edge algorithm */
3211 dy
= bottom
->y
- top
->y
;
3212 BRESINITPGONSTRUCT(dy
, top
->x
, bottom
->x
, pETEs
->bres
);
3214 REGION_InsertEdgeInET(ET
,
3220 if (PrevPt
->y
> ET
->ymax
)
3221 ET
->ymax
= PrevPt
->y
;
3222 if (PrevPt
->y
< ET
->ymin
)
3223 ET
->ymin
= PrevPt
->y
;
3234 REGION_SetPolyPolygonRgn(
3235 _Inout_ PREGION prgn
,
3236 _In_
const POINT
*ppt
,
3237 _In_
const ULONG
*pcPoints
,
3238 _In_ ULONG cPolygons
,
3241 EDGE_TABLE_ENTRY
*pAET
; /* Active Edge Table */
3242 INT y
; /* Current scanline */
3243 INT iPts
= 0; /* Number of pts in buffer */
3244 EDGE_TABLE_ENTRY
*pWETE
; /* Winding Edge Table Entry */
3245 SCANLINE_LIST
*pSLL
; /* Current SCANLINE_LIST */
3246 POINT
*pts
; /* Output buffer */
3247 EDGE_TABLE_ENTRY
*pPrevAET
; /* Pointer to previous AET */
3248 EDGE_TABLE ET
; /* Header node for ET */
3249 EDGE_TABLE_ENTRY AET
; /* Header node for AET */
3250 EDGE_TABLE_ENTRY
*pETEs
; /* EDGE_TABLEEntries pool */
3251 SCANLINE_LISTBLOCK SLLBlock
; /* Header for SCANLINE_LIST */
3252 INT fixWAET
= FALSE
;
3253 POINTBLOCK FirstPtBlock
, *curPtBlock
; /* PtBlock buffers */
3254 POINTBLOCK
*tmpPtBlock
;
3255 UINT numFullPtBlocks
= 0;
3258 /* Check if iMode is valid */
3259 if ((iMode
!= ALTERNATE
) && (iMode
!= WINDING
))
3261 DPRINT1("Invalid iMode: %lu\n", iMode
);
3265 /* Special case a rectangle */
3266 if (((cPolygons
== 1) && ((pcPoints
[0] == 4) ||
3267 ((pcPoints
[0] == 5) && (ppt
[4].x
== ppt
[0].x
) && (ppt
[4].y
== ppt
[0].y
)))) &&
3268 (((ppt
[0].y
== ppt
[1].y
) &&
3269 (ppt
[1].x
== ppt
[2].x
) &&
3270 (ppt
[2].y
== ppt
[3].y
) &&
3271 (ppt
[3].x
== ppt
[0].x
)) ||
3272 ((ppt
[0].x
== ppt
[1].x
) &&
3273 (ppt
[1].y
== ppt
[2].y
) &&
3274 (ppt
[2].x
== ppt
[3].x
) &&
3275 (ppt
[3].y
== ppt
[0].y
))))
3277 REGION_SetRectRgn(prgn
,
3278 min(ppt
[0].x
, ppt
[2].x
),
3279 min(ppt
[0].y
, ppt
[2].y
),
3280 max(ppt
[0].x
, ppt
[2].x
),
3281 max(ppt
[0].y
, ppt
[2].y
));
3285 for (poly
= total
= 0; poly
< cPolygons
; poly
++)
3286 total
+= pcPoints
[poly
];
3288 pETEs
= ExAllocatePoolWithTag(PagedPool
,
3289 sizeof(EDGE_TABLE_ENTRY
) * total
,
3293 DPRINT1("Failed to allocate %lu edge entries\n", total
);
3297 pts
= FirstPtBlock
.pts
;
3298 REGION_CreateETandAET(pcPoints
, cPolygons
, ppt
, &ET
, &AET
, pETEs
, &SLLBlock
);
3299 pSLL
= ET
.scanlines
.next
;
3300 curPtBlock
= &FirstPtBlock
;
3302 if (iMode
!= WINDING
)
3304 /* For each scanline */
3305 for (y
= ET
.ymin
; y
< ET
.ymax
; y
++)
3307 /* Add a new edge to the active edge table when we
3308 * get to the next edge. */
3309 if (pSLL
!= NULL
&& y
== pSLL
->scanline
)
3311 REGION_loadAET(&AET
, pSLL
->edgelist
);
3317 /* For each active edge */
3320 pts
->x
= pAET
->bres
.minor_axis
, pts
->y
= y
;
3323 /* Send out the buffer */
3324 if (iPts
== NUMPTSTOBUFFER
)
3326 tmpPtBlock
= ExAllocatePoolWithTag(PagedPool
,
3329 if (tmpPtBlock
== NULL
)
3331 DPRINT1("Can't alloc tmpPtBlock\n");
3332 ExFreePoolWithTag(pETEs
, TAG_REGION
);
3336 curPtBlock
->next
= tmpPtBlock
;
3337 curPtBlock
= tmpPtBlock
;
3338 pts
= curPtBlock
->pts
;
3343 EVALUATEEDGEEVENODD(pAET
, pPrevAET
, y
);
3346 REGION_InsertionSort(&AET
);
3351 /* For each scanline */
3352 for (y
= ET
.ymin
; y
< ET
.ymax
; y
++)
3354 /* Add a new edge to the active edge table when we
3355 * get to the next edge. */
3356 if (pSLL
!= NULL
&& y
== pSLL
->scanline
)
3358 REGION_loadAET(&AET
, pSLL
->edgelist
);
3359 REGION_computeWAET(&AET
);
3367 /* For each active edge */
3370 /* Add to the buffer only those edges that
3371 * are in the Winding active edge table. */
3374 pts
->x
= pAET
->bres
.minor_axis
;
3379 /* Send out the buffer */
3380 if (iPts
== NUMPTSTOBUFFER
)
3382 tmpPtBlock
= ExAllocatePoolWithTag(PagedPool
,
3385 if (tmpPtBlock
== NULL
)
3387 DPRINT1("Can't alloc tPB\n");
3388 ExFreePoolWithTag(pETEs
, TAG_REGION
);
3391 curPtBlock
->next
= tmpPtBlock
;
3392 curPtBlock
= tmpPtBlock
;
3393 pts
= curPtBlock
->pts
;
3398 pWETE
= pWETE
->nextWETE
;
3401 EVALUATEEDGEWINDING(pAET
, pPrevAET
, y
, fixWAET
);
3404 /* Recompute the winding active edge table if
3405 * we just resorted or have exited an edge. */
3406 if (REGION_InsertionSort(&AET
) || fixWAET
)
3408 REGION_computeWAET(&AET
);
3414 REGION_FreeStorage(SLLBlock
.next
);
3415 REGION_PtsToRegion(numFullPtBlocks
, iPts
, &FirstPtBlock
, prgn
);
3417 for (curPtBlock
= FirstPtBlock
.next
; numFullPtBlocks
-- > 0;)
3419 tmpPtBlock
= curPtBlock
->next
;
3420 ExFreePoolWithTag(curPtBlock
, TAG_REGION
);
3421 curPtBlock
= tmpPtBlock
;
3424 ExFreePoolWithTag(pETEs
, TAG_REGION
);
3430 GreCreatePolyPolygonRgn(
3431 _In_
const POINT
*ppt
,
3432 _In_
const ULONG
*pcPoints
,
3433 _In_ ULONG cPolygons
,
3439 /* Allocate a new region */
3440 prgn
= REGION_AllocUserRgnWithHandle(0);
3443 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
3447 /* Call the internal function and check for success */
3448 if (REGION_SetPolyPolygonRgn(prgn
, ppt
, pcPoints
, cPolygons
, iMode
))
3450 /* Success, get the handle and unlock the region */
3451 hrgn
= prgn
->BaseObject
.hHmgr
;
3452 REGION_UnlockRgn(prgn
);
3456 /* Failure, delete the region */
3457 REGION_Delete(prgn
);
3473 Rgn
= REGION_LockRgn(hRgn
);
3479 Ret
= REGION_RectInRegion(Rgn
, rc
);
3480 REGION_UnlockRgn(Rgn
);
3486 // NtGdi Exported Functions
3500 /* Validate the combine mode */
3501 if ((iMode
< RGN_AND
) || (iMode
> RGN_COPY
))
3506 /* Validate that we have the required regions */
3507 if ((hrgnDst
== NULL
) ||
3508 (hrgnSrc1
== NULL
) ||
3509 ((iMode
!= RGN_COPY
) && (hrgnSrc2
== NULL
)))
3511 DPRINT1("NtGdiCombineRgn invalid parameters: %p, %p, %p, %d\n",
3512 hrgnDst
, hrgnSrc1
, hrgnSrc2
, iMode
);
3513 EngSetLastError(ERROR_INVALID_HANDLE
);
3517 /* Lock all regions */
3519 ahrgn
[1] = hrgnSrc1
;
3520 ahrgn
[2] = iMode
!= RGN_COPY
? hrgnSrc2
: NULL
;
3521 if (!GDIOBJ_bLockMultipleObjects(3, (HGDIOBJ
*)ahrgn
, (PVOID
*)aprgn
, GDIObjType_RGN_TYPE
))
3523 DPRINT1("NtGdiCombineRgn failed to lock regions: %p, %p, %p, %d\n",
3524 hrgnDst
, hrgnSrc1
, hrgnSrc2
, iMode
);
3528 /* HACK: Sync usermode attributes */
3529 REGION_vSyncRegion(aprgn
[0]);
3530 if (aprgn
[1] != aprgn
[0])
3531 REGION_vSyncRegion(aprgn
[1]);
3532 if ((aprgn
[2] != NULL
) && (aprgn
[2] != aprgn
[0]) && (aprgn
[2] != aprgn
[1]))
3533 REGION_vSyncRegion(aprgn
[2]);
3535 /* Call the internal function */
3536 iResult
= IntGdiCombineRgn(aprgn
[0], aprgn
[1], aprgn
[2], iMode
);
3538 /* Unlock and return */
3539 REGION_UnlockRgn(aprgn
[0]);
3540 REGION_UnlockRgn(aprgn
[1]);
3541 if (aprgn
[2] != NULL
)
3542 REGION_UnlockRgn(aprgn
[2]);
3549 NtGdiCreateEllipticRgn(
3555 return NtGdiCreateRoundRectRgn(Left
,
3573 /* Allocate region data structure with space for 1 RECTL */
3574 pRgn
= REGION_AllocUserRgnWithHandle(1);
3577 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
3581 hRgn
= pRgn
->BaseObject
.hHmgr
;
3583 REGION_SetRectRgn(pRgn
, LeftRect
, TopRect
, RightRect
, BottomRect
);
3584 REGION_UnlockRgn(pRgn
);
3586 DPRINT("Returning %p.\n", hRgn
);
3593 NtGdiCreateRoundRectRgn(
3603 INT asq
, bsq
, d
, xd
, yd
;
3606 /* Make the dimensions sensible */
3621 ellipse_width
= abs(ellipse_width
);
3622 ellipse_height
= abs(ellipse_height
);
3624 /* Check parameters */
3625 if (ellipse_width
> right
-left
)
3626 ellipse_width
= right
-left
;
3627 if (ellipse_height
> bottom
-top
)
3628 ellipse_height
= bottom
-top
;
3630 /* Check if we can do a normal rectangle instead */
3631 if ((ellipse_width
< 2) || (ellipse_height
< 2))
3632 return NtGdiCreateRectRgn(left
, top
, right
, bottom
);
3635 d
= (ellipse_height
< 128) ? ((3 * ellipse_height
) >> 2) : 64;
3636 obj
= REGION_AllocUserRgnWithHandle(d
);
3640 hrgn
= obj
->BaseObject
.hHmgr
;
3642 /* Ellipse algorithm, based on an article by K. Porter
3643 in DDJ Graphics Programming Column, 8/89 */
3644 asq
= ellipse_width
* ellipse_width
/ 4; /* a^2 */
3645 bsq
= ellipse_height
* ellipse_height
/ 4; /* b^2 */
3646 d
= bsq
- asq
* ellipse_height
/ 2 + asq
/ 4; /* b^2 - a^2b + a^2/4 */
3648 yd
= asq
* ellipse_height
; /* 2a^2b */
3650 rect
.left
= left
+ ellipse_width
/ 2;
3651 rect
.right
= right
- ellipse_width
/ 2;
3653 /* Loop to draw first half of quadrant */
3656 /* If nearest pixel is toward the center */
3659 /* Move toward center */
3661 rect
.bottom
= rect
.top
+ 1;
3662 REGION_UnionRectWithRgn(obj
, &rect
);
3663 rect
.top
= --bottom
;
3664 rect
.bottom
= rect
.top
+ 1;
3665 REGION_UnionRectWithRgn(obj
, &rect
);
3670 /* Next horiz point */
3677 /* Loop to draw second half of quadrant */
3678 d
+= (3 * (asq
-bsq
) / 2 - (xd
+yd
)) / 2;
3681 /* next vertical point */
3683 rect
.bottom
= rect
.top
+ 1;
3684 REGION_UnionRectWithRgn(obj
, &rect
);
3685 rect
.top
= --bottom
;
3686 rect
.bottom
= rect
.top
+ 1;
3687 REGION_UnionRectWithRgn(obj
, &rect
);
3689 /* If nearest pixel is outside ellipse */
3692 /* Move away from center */
3703 /* Add the inside rectangle */
3707 rect
.bottom
= bottom
;
3708 REGION_UnionRectWithRgn(obj
, &rect
);
3711 REGION_UnlockRgn(obj
);
3724 PRECTL tRect1
, tRect2
;
3728 /* Check if we got 2 regions */
3729 if ((hSrcRgn1
== NULL
) || (hSrcRgn2
== NULL
))
3734 /* Check if these are the same regions */
3735 if (hSrcRgn1
== hSrcRgn2
)
3737 /* Make sure this region is valid */
3738 if ((GDI_HANDLE_GET_TYPE(hSrcRgn1
) == GDILoObjType_LO_REGION_TYPE
) &&
3739 GreIsHandleValid(hSrcRgn1
))
3746 /* Lock both regions */
3747 ahrgn
[0] = hSrcRgn1
;
3748 ahrgn
[1] = hSrcRgn2
;
3749 if (!GDIOBJ_bLockMultipleObjects(2, (HGDIOBJ
*)ahrgn
, (PVOID
*)aprgn
, GDIObjType_RGN_TYPE
))
3751 DPRINT1("NtGdiEqualRgn failed to lock regions: %p, %p\n",
3752 hSrcRgn1
, hSrcRgn2
);
3756 REGION_vSyncRegion(aprgn
[0]);
3757 REGION_vSyncRegion(aprgn
[1]);
3762 if (rgn1
->rdh
.nCount
!= rgn2
->rdh
.nCount
)
3765 if (rgn1
->rdh
.nCount
== 0)
3771 if ((rgn1
->rdh
.rcBound
.left
!= rgn2
->rdh
.rcBound
.left
) ||
3772 (rgn1
->rdh
.rcBound
.right
!= rgn2
->rdh
.rcBound
.right
) ||
3773 (rgn1
->rdh
.rcBound
.top
!= rgn2
->rdh
.rcBound
.top
) ||
3774 (rgn1
->rdh
.rcBound
.bottom
!= rgn2
->rdh
.rcBound
.bottom
))
3777 tRect1
= rgn1
->Buffer
;
3778 tRect2
= rgn2
->Buffer
;
3780 if ((tRect1
== NULL
) || (tRect2
== NULL
))
3783 for (i
=0; i
< rgn1
->rdh
.nCount
; i
++)
3785 if ((tRect1
[i
].left
!= tRect2
[i
].left
) ||
3786 (tRect1
[i
].right
!= tRect2
[i
].right
) ||
3787 (tRect1
[i
].top
!= tRect2
[i
].top
) ||
3788 (tRect1
[i
].bottom
!= tRect2
[i
].bottom
))
3795 REGION_UnlockRgn(rgn1
);
3796 REGION_UnlockRgn(rgn2
);
3802 NtGdiExtCreateRegion(
3803 OPTIONAL LPXFORM Xform
,
3814 NTSTATUS Status
= STATUS_SUCCESS
;
3818 DPRINT("NtGdiExtCreateRegion\n");
3821 ProbeForRead(RgnData
, Count
, 1);
3822 nCount
= RgnData
->rdh
.nCount
;
3823 iType
= RgnData
->rdh
.iType
;
3824 dwSize
= RgnData
->rdh
.dwSize
;
3825 rects
= (RECT
*)RgnData
->Buffer
;
3827 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3829 Status
= _SEH2_GetExceptionCode();
3833 if (!NT_SUCCESS(Status
))
3835 SetLastNtError(Status
);
3839 /* Check parameters, but don't set last error here */
3840 if ((Count
< sizeof(RGNDATAHEADER
) + nCount
* sizeof(RECT
)) ||
3841 (iType
!= RDH_RECTANGLES
) ||
3842 (dwSize
!= sizeof(RGNDATAHEADER
)))
3847 Region
= REGION_AllocUserRgnWithHandle(nCount
);
3851 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
3854 hRgn
= Region
->BaseObject
.hHmgr
;
3858 /* Insert the rectangles one by one */
3859 for(i
=0; i
<nCount
; i
++)
3861 REGION_UnionRectWithRgn(Region
, &rects
[i
]);
3868 /* Init the XFORMOBJ from the Xform struct */
3869 Status
= STATUS_INVALID_PARAMETER
;
3870 XFORMOBJ_vInit(&xo
, &matrix
);
3871 ret
= XFORMOBJ_iSetXform(&xo
, (XFORML
*)Xform
);
3873 /* Check for error */
3874 if (ret
!= DDI_ERROR
)
3876 /* Apply the coordinate transformation on the rects */
3877 if (XFORMOBJ_bApplyXform(&xo
,
3879 Region
->rdh
.nCount
* 2,
3883 Status
= STATUS_SUCCESS
;
3888 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3890 Status
= _SEH2_GetExceptionCode();
3893 if (!NT_SUCCESS(Status
))
3895 EngSetLastError(ERROR_INVALID_PARAMETER
);
3896 REGION_UnlockRgn(Region
);
3897 GreDeleteObject(hRgn
);
3901 REGION_UnlockRgn(Region
);
3915 NTSTATUS Status
= STATUS_SUCCESS
;
3917 Rgn
= REGION_LockRgn(hRgn
);
3923 ret
= REGION_GetRgnBox(Rgn
, &SafeRect
);
3924 REGION_UnlockRgn(Rgn
);
3932 ProbeForWrite(pRect
, sizeof(RECT
), 1);
3935 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3937 Status
= _SEH2_GetExceptionCode();
3940 if (!NT_SUCCESS(Status
))
3958 DPRINT("NtGdiOffsetRgn: hrgn %p cx %d cy %d\n", hrgn
, cx
, cy
);
3960 /* Lock the region */
3961 prgn
= REGION_LockRgn(hrgn
);
3964 DPRINT1("NtGdiOffsetRgn: failed to lock region %p\n", hrgn
);
3968 /* Call the internal function */
3969 if (!REGION_bOffsetRgn(prgn
, cx
, cy
))
3975 iResult
= REGION_Complexity(prgn
);
3978 /* Unlock and return the result */
3979 REGION_UnlockRgn(prgn
);
3993 /* Lock the region */
3994 prgn
= REGION_LockRgn(hrgn
);
3997 DPRINT1("NtGdiPtInRegion: hrgn error\n");
4001 /* Call the internal function */
4002 bResult
= REGION_PtInRegion(prgn
, x
, y
);
4004 /* Unlock and return the result */
4005 REGION_UnlockRgn(prgn
);
4014 _Inout_ LPRECT prclUnsafe
)
4018 /* Probe and copy the rect */
4021 ProbeForRead(prclUnsafe
, sizeof(RECT
), 1);
4022 rcTemp
= *prclUnsafe
;
4024 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4026 DPRINT1("NtGdiRectInRegion: Exception accessing the rect\n");
4031 /* Call the internal function */
4032 return IntRectInRegion(hrgn
, &rcTemp
);
4046 /* Lock the region */
4047 prgn
= REGION_LockRgn(hrgn
);
4053 /* Call the internal API */
4054 REGION_SetRectRgn(prgn
, xLeft
, yTop
, xRight
, yBottom
);
4056 /* Unlock the region and return success */
4057 REGION_UnlockRgn(prgn
);
4062 * MSDN: GetRegionData, Return Values:
4064 * "If the function succeeds and dwCount specifies an adequate number of bytes,
4065 * the return value is always dwCount. If dwCount is too small or the function
4066 * fails, the return value is 0. If lpRgnData is NULL, the return value is the
4067 * required number of bytes.
4069 * If the function fails, the return value is zero."
4071 _Success_(return!=0)
4077 _In_ ULONG cjBuffer
,
4078 _Out_writes_bytes_to_opt_(cjBuffer
, return) LPRGNDATA lpRgnData
)
4080 ULONG cjRects
, cjSize
;
4083 /* Lock the region */
4084 prgn
= REGION_LockRgn(hrgn
);
4087 EngSetLastError(ERROR_INVALID_HANDLE
);
4091 /* Calculate the region sizes */
4092 cjRects
= prgn
->rdh
.nCount
* sizeof(RECT
);
4093 cjSize
= cjRects
+ sizeof(RGNDATAHEADER
);
4095 /* Check if region data is requested */
4098 /* Check if the buffer is large enough */
4099 if (cjBuffer
>= cjSize
)
4101 /* Probe the buffer and copy the data */
4104 ProbeForWrite(lpRgnData
, cjSize
, sizeof(ULONG
));
4105 RtlCopyMemory(lpRgnData
, &prgn
->rdh
, sizeof(RGNDATAHEADER
));
4106 RtlCopyMemory(lpRgnData
->Buffer
, prgn
->Buffer
, cjRects
);
4107 lpRgnData
->rdh
.iType
= RDH_RECTANGLES
;
4108 lpRgnData
->rdh
.nRgnSize
= cjRects
;
4110 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4112 EngSetLastError(ERROR_INVALID_PARAMETER
);
4119 /* Buffer is too small */
4120 EngSetLastError(ERROR_INVALID_PARAMETER
);
4125 /* Unlock the region and return the size */
4126 REGION_UnlockRgn(prgn
);