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 if this is a scaling only matrix (off-diagonal elements are 0 */
2055 if (pmx
->flAccel
& XFORM_SCALE
)
2057 /* Check if this is a translation only matrix */
2058 if (pmx
->flAccel
& XFORM_UNITY
)
2060 /* Just offset the region */
2061 return REGION_bOffsetRgn(prgn
, (pmx
->fxDx
+ 8) / 16, (pmx
->fxDy
+ 8) / 16);
2065 /* Initialize the xform object */
2066 XFORMOBJ_vInit(&xo
, pmx
);
2068 /* Scaling can move the rects out of the coordinate space, so
2069 * we first need to check whether we can apply the transformation
2070 * on the bounds rect without modifying the region */
2071 if (!XFORMOBJ_bApplyXform(&xo
, XF_LTOL
, 2, &prgn
->rdh
.rcBound
, &rect
))
2076 /* Apply the xform to the rects in the region */
2077 if (!XFORMOBJ_bApplyXform(&xo
,
2079 prgn
->rdh
.nCount
* 2,
2083 /* This can not happen, since we already checked the bounds! */
2088 RECTL_vSetEmptyRect(&prgn
->rdh
.rcBound
);
2090 /* Loop all rects in the region */
2091 for (i
= 0; i
< prgn
->rdh
.nCount
; i
++)
2093 /* Make sure the rect is well-ordered after the xform */
2094 RECTL_vMakeWellOrdered(&prgn
->Buffer
[i
]);
2097 RECTL_bUnionRect(&prgn
->rdh
.rcBound
,
2102 /* Loop all rects in the region */
2103 for (i
= 0; i
< prgn
->rdh
.nCount
- 1; i
++)
2105 for (j
= i
; i
< prgn
->rdh
.nCount
; i
++)
2107 NT_ASSERT(prgn
->Buffer
[i
].top
< prgn
->Buffer
[i
].bottom
);
2108 NT_ASSERT(prgn
->Buffer
[j
].top
>= prgn
->Buffer
[i
].top
);
2117 /* Allocate a buffer for the polygons */
2118 cjSize
= prgn
->rdh
.nCount
* (4 * sizeof(POINT
) + sizeof(ULONG
));
2119 ppt
= ExAllocatePoolWithTag(PagedPool
, cjSize
, GDITAG_REGION
);
2125 /* Fill the buffer with the rects */
2126 pcPoints
= (PULONG
)&ppt
[4 * prgn
->rdh
.nCount
];
2127 for (i
= 0; i
< prgn
->rdh
.nCount
; i
++)
2129 /* Make sure the rect is within the legal range */
2131 ppt
[4 * i
+ 0].x
= prgn
->Buffer
[i
].left
;
2132 ppt
[4 * i
+ 0].y
= prgn
->Buffer
[i
].top
;
2133 ppt
[4 * i
+ 1].x
= prgn
->Buffer
[i
].right
;
2134 ppt
[4 * i
+ 1].y
= prgn
->Buffer
[i
].top
;
2135 ppt
[4 * i
+ 2].x
= prgn
->Buffer
[i
].right
;
2136 ppt
[4 * i
+ 2].y
= prgn
->Buffer
[i
].bottom
;
2137 ppt
[4 * i
+ 3].x
= prgn
->Buffer
[i
].left
;
2138 ppt
[4 * i
+ 3].y
= prgn
->Buffer
[i
].bottom
;
2141 /* Initialize the xform object */
2142 XFORMOBJ_vInit(&xo
, pmx
);
2144 /* Apply the xform to the rects in the buffer */
2145 if (!XFORMOBJ_bApplyXform(&xo
,
2147 prgn
->rdh
.nCount
* 2,
2151 /* This means, there were coordinates that would go outside of
2152 the coordinate space after the transformation */
2153 ExFreePoolWithTag(ppt
, GDITAG_REGION
);
2157 /* Now use the polygons to create a polygon region */
2158 bResult
= REGION_SetPolyPolygonRgn(prgn
,
2164 /* Free the polygon buffer */
2165 ExFreePoolWithTag(ppt
, GDITAG_REGION
);
2175 REGION_AllocRgnWithHandle(
2181 pReg
= (PREGION
)GDIOBJ_AllocateObject(GDIObjType_RGN_TYPE
,
2183 BASEFLAG_LOOKASIDE
);
2186 DPRINT1("Could not allocate a palette.\n");
2190 //hReg = pReg->BaseObject.hHmgr;
2192 if ((nReg
== 0) || (nReg
== 1))
2194 /* Testing shows that > 95% of all regions have only 1 rect.
2195 Including that here saves us from having to do another allocation */
2196 pReg
->Buffer
= &pReg
->rdh
.rcBound
;
2200 pReg
->Buffer
= ExAllocatePoolWithTag(PagedPool
,
2201 nReg
* sizeof(RECT
),
2203 if (pReg
->Buffer
== NULL
)
2205 DPRINT1("Could not allocate region buffer\n");
2206 GDIOBJ_vDeleteObject(&pReg
->BaseObject
);
2212 pReg
->rdh
.dwSize
= sizeof(RGNDATAHEADER
);
2213 pReg
->rdh
.nCount
= nReg
;
2214 pReg
->rdh
.nRgnSize
= nReg
* sizeof(RECT
);
2215 pReg
->prgnattr
= &pReg
->rgnattr
;
2217 /* Initialize the region attribute */
2218 pReg
->rgnattr
.AttrFlags
= 0;
2219 pReg
->rgnattr
.iComplexity
= SIMPLEREGION
;
2220 pReg
->rgnattr
.Rect
= pReg
->rdh
.rcBound
;
2222 /* Finally insert the region into the handle table */
2223 if (!GDIOBJ_hInsertObject(&pReg
->BaseObject
, GDI_OBJ_HMGR_POWNED
))
2225 DPRINT1("Could not insert palette into handle table.\n");
2226 GDIOBJ_vFreeObject(&pReg
->BaseObject
);
2235 REGION_bAllocRgnAttr(
2241 NT_ASSERT(prgn
->prgnattr
== &prgn
->rgnattr
);
2243 ppi
= PsGetCurrentProcessWin32Process();
2246 prgnattr
= GdiPoolAllocate(ppi
->pPoolRgnAttr
);
2247 if (prgnattr
== NULL
)
2249 DPRINT1("Could not allocate RGN attr\n");
2253 /* Copy the current region attribute */
2254 *prgnattr
= prgn
->rgnattr
;
2256 /* Set the object attribute in the handle table */
2257 prgn
->prgnattr
= prgnattr
;
2258 GDIOBJ_vSetObjectAttr(&prgn
->BaseObject
, prgnattr
);
2265 // Allocate User Space Region Handle.
2269 REGION_AllocUserRgnWithHandle(
2274 prgn
= REGION_AllocRgnWithHandle(nRgn
);
2280 if (!REGION_bAllocRgnAttr(prgn
))
2295 NT_ASSERT(prgn
!= NULL
);
2296 NT_ASSERT(prgn
->prgnattr
!= NULL
);
2297 NT_ASSERT((prgn
->prgnattr
== &prgn
->rgnattr
) ||
2298 (prgn
->prgnattr
->AttrFlags
& ATTR_RGN_VALID
));
2300 /* Get the region attribute and check if it's dirty (modified) */
2301 prgnattr
= prgn
->prgnattr
;
2302 if (prgnattr
->AttrFlags
& ATTR_RGN_DIRTY
)
2304 NT_ASSERT(GreGetObjectOwner(prgn
->BaseObject
.hHmgr
) == GDI_OBJ_HMGR_POWNED
);
2305 NT_ASSERT(prgnattr
!= &prgn
->rgnattr
);
2307 if (prgnattr
->iComplexity
== NULLREGION
)
2311 else if (prgnattr
->iComplexity
== SIMPLEREGION
)
2313 REGION_SetRectRgn(prgn
,
2314 prgnattr
->Rect
.left
,
2316 prgnattr
->Rect
.right
,
2317 prgnattr
->Rect
.bottom
);
2321 /* Should not happen, region attribute is corrupted! */
2322 DPRINT1("Region attribute is corrupted, ignoring\n");
2327 /* Reset the flags */
2328 prgnattr
->AttrFlags
&= ~(ATTR_RGN_DIRTY
| ATTR_RGN_VALID
);
2338 prgn
= GDIOBJ_LockObject(hrgn
, GDIObjType_RGN_TYPE
);
2342 REGION_vSyncRegion(prgn
);
2353 NT_ASSERT(prgn
!= NULL
);
2354 NT_ASSERT(prgn
->prgnattr
!= NULL
);
2356 /* Get the region attribute and check if it's user mode */
2357 prgnattr
= prgn
->prgnattr
;
2358 if (prgnattr
!= &prgn
->rgnattr
)
2360 NT_ASSERT(GreGetObjectOwner(prgn
->BaseObject
.hHmgr
) == GDI_OBJ_HMGR_POWNED
);
2361 prgnattr
->iComplexity
= REGION_Complexity(prgn
);
2362 prgnattr
->Rect
.left
= prgn
->rdh
.rcBound
.left
;
2363 prgnattr
->Rect
.top
= prgn
->rdh
.rcBound
.top
;
2364 prgnattr
->Rect
.right
= prgn
->rdh
.rcBound
.right
;
2365 prgnattr
->Rect
.bottom
= prgn
->rdh
.rcBound
.bottom
;
2366 prgnattr
->AttrFlags
|= ATTR_RGN_VALID
;
2369 GDIOBJ_vUnlockObject(&prgn
->BaseObject
);
2374 These regions do not use attribute sections and when allocated, use gdiobj
2378 // System Region Functions
2382 IntSysCreateRectpRgn(
2390 /* Allocate a region, without a handle */
2391 prgn
= (PREGION
)GDIOBJ_AllocateObject(GDIObjType_RGN_TYPE
, sizeof(REGION
), BASEFLAG_LOOKASIDE
);
2398 prgn
->Buffer
= &prgn
->rdh
.rcBound
;
2399 prgn
->prgnattr
= &prgn
->rgnattr
;
2400 prgn
->prgnattr
->AttrFlags
= ATTR_RGN_VALID
;
2401 REGION_SetRectRgn(prgn
, LeftRect
, TopRect
, RightRect
, BottomRect
);
2408 REGION_vCleanup(PVOID ObjectBody
)
2410 PREGION pRgn
= (PREGION
)ObjectBody
;
2411 PPROCESSINFO ppi
= PsGetCurrentProcessWin32Process();
2414 ASSERT(pRgn
->prgnattr
);
2415 if (pRgn
->prgnattr
!= &pRgn
->rgnattr
)
2416 GdiPoolFree(ppi
->pPoolRgnAttr
, pRgn
->prgnattr
);
2418 if (pRgn
->Buffer
&& pRgn
->Buffer
!= &pRgn
->rdh
.rcBound
)
2419 ExFreePoolWithTag(pRgn
->Buffer
, TAG_REGION
);
2424 REGION_Delete(PREGION pRgn
)
2426 if (pRgn
== prgnDefault
)
2429 GDIOBJ_vDeleteObject(&pRgn
->BaseObject
);
2434 IntGdiSetRegionOwner(HRGN hRgn
, DWORD OwnerMask
)
2440 prgn
= REGION_LockRgn(hRgn
);
2446 prgnattr
= prgn
->prgnattr
;
2447 if (prgnattr
!= &prgn
->rgnattr
)
2449 GDIOBJ_vSetObjectAttr(&prgn
->BaseObject
, NULL
);
2450 prgn
->prgnattr
= &prgn
->rgnattr
;
2451 ppi
= PsGetCurrentProcessWin32Process();
2452 GdiPoolFree(ppi
->pPoolRgnAttr
, prgnattr
);
2455 REGION_UnlockRgn(prgn
);
2457 return GreSetObjectOwner(hRgn
, OwnerMask
);
2469 if (prgnDest
== NULL
)
2471 DPRINT("IntGdiCombineRgn: hDest unavailable\n");
2475 if (prgnSrc1
== NULL
)
2477 DPRINT("IntGdiCombineRgn: hSrc1 unavailable\n");
2481 if (iCombineMode
== RGN_COPY
)
2483 if (!REGION_CopyRegion(prgnDest
, prgnSrc1
))
2486 return REGION_Complexity(prgnDest
);
2489 if (prgnSrc2
== NULL
)
2491 DPRINT1("IntGdiCombineRgn requires hSrc2 != NULL for combine mode %d!\n", iCombineMode
);
2496 switch (iCombineMode
)
2499 REGION_IntersectRegion(prgnDest
, prgnSrc1
, prgnSrc2
);
2502 REGION_UnionRegion(prgnDest
, prgnSrc1
, prgnSrc2
);
2505 REGION_XorRegion(prgnDest
, prgnSrc1
, prgnSrc2
);
2508 REGION_SubtractRegion(prgnDest
, prgnSrc1
, prgnSrc2
);
2512 return REGION_Complexity(prgnDest
);
2525 *pRect
= Rgn
->rdh
.rcBound
;
2526 ret
= REGION_Complexity(Rgn
);
2530 return 0; // If invalid region return zero
2542 Rgn
= REGION_LockRgn(hRgn
);
2548 ret
= REGION_GetRgnBox(Rgn
, pRect
);
2549 REGION_UnlockRgn(Rgn
);
2565 if (prgn
->rdh
.nCount
> 0 && INRECT(prgn
->rdh
.rcBound
, X
, Y
))
2568 for (i
= 0; i
< prgn
->rdh
.nCount
; i
++)
2570 if (INRECT(r
[i
], X
, Y
))
2580 REGION_RectInRegion(
2584 PRECTL pCurRect
, pRectEnd
;
2587 /* Swap the coordinates to make right >= left and bottom >= top */
2588 /* (region building rectangles are normalized the same way) */
2589 if (rect
->top
> rect
->bottom
)
2591 rc
.top
= rect
->bottom
;
2592 rc
.bottom
= rect
->top
;
2597 rc
.bottom
= rect
->bottom
;
2600 if (rect
->right
< rect
->left
)
2602 rc
.right
= rect
->left
;
2603 rc
.left
= rect
->right
;
2607 rc
.right
= rect
->right
;
2608 rc
.left
= rect
->left
;
2611 /* This is (just) a useful optimization */
2612 if ((Rgn
->rdh
.nCount
> 0) && EXTENTCHECK(&Rgn
->rdh
.rcBound
, &rc
))
2614 for (pCurRect
= Rgn
->Buffer
, pRectEnd
= pCurRect
+
2615 Rgn
->rdh
.nCount
; pCurRect
< pRectEnd
; pCurRect
++)
2617 if (pCurRect
->bottom
<= rc
.top
)
2618 continue; /* Not far enough down yet */
2620 if (pCurRect
->top
>= rc
.bottom
)
2621 break; /* Too far down */
2623 if (pCurRect
->right
<= rc
.left
)
2624 continue; /* Not far enough over yet */
2626 if (pCurRect
->left
>= rc
.right
)
2649 if (LeftRect
> RightRect
)
2652 LeftRect
= RightRect
;
2656 if (TopRect
> BottomRect
)
2659 TopRect
= BottomRect
;
2663 if ((LeftRect
!= RightRect
) && (TopRect
!= BottomRect
))
2665 firstRect
= rgn
->Buffer
;
2667 firstRect
->left
= rgn
->rdh
.rcBound
.left
= LeftRect
;
2668 firstRect
->top
= rgn
->rdh
.rcBound
.top
= TopRect
;
2669 firstRect
->right
= rgn
->rdh
.rcBound
.right
= RightRect
;
2670 firstRect
->bottom
= rgn
->rdh
.rcBound
.bottom
= BottomRect
;
2671 rgn
->rdh
.nCount
= 1;
2672 rgn
->rdh
.iType
= RDH_RECTANGLES
;
2683 _Inout_ PREGION prgn
,
2690 NT_ASSERT(prgn
!= NULL
);
2692 /* Check for trivial case */
2693 if ((cx
== 0) && (cy
== 0))
2698 /* Check for empty regions, we ignore the offset values here */
2699 if (prgn
->rdh
.nCount
== 0)
2704 /* Make sure the offset is within the legal range */
2705 if ((cx
> MAX_COORD
) || (cx
< MIN_COORD
) ||
2706 (cy
> MAX_COORD
) || (cy
< MIN_COORD
))
2711 /* Are we moving right? */
2714 /* Check if we stay inside the bounds on the right side */
2715 if (prgn
->rdh
.rcBound
.right
> (MAX_COORD
- cx
))
2722 /* Check if we stay inside the bounds on the left side */
2723 if (prgn
->rdh
.rcBound
.left
< (MIN_COORD
- cx
))
2729 /* Are we moving down? */
2732 /* Check if we stay inside the bounds on the right side */
2733 if (prgn
->rdh
.rcBound
.bottom
> (MAX_COORD
- cy
))
2740 /* Check if we stay inside the bounds on the left side */
2741 if (prgn
->rdh
.rcBound
.top
< (MIN_COORD
- cy
))
2747 /* Loop to move the rects */
2748 prcl
= prgn
->Buffer
;
2749 for (i
= 0; i
< prgn
->rdh
.nCount
; i
++)
2752 prcl
[i
].right
+= cx
;
2754 prcl
[i
].bottom
+= cy
;
2757 /* Finally update the bounds rect */
2758 if (prgn
->Buffer
!= &prgn
->rdh
.rcBound
)
2760 prgn
->rdh
.rcBound
.left
+= cx
;
2761 prgn
->rdh
.rcBound
.right
+= cx
;
2762 prgn
->rdh
.rcBound
.top
+= cy
;
2763 prgn
->rdh
.rcBound
.bottom
+= cy
;
2769 /***********************************************************************
2770 * REGION_InsertEdgeInET
2772 * Insert the given edge into the edge table.
2773 * First we must find the correct bucket in the
2774 * Edge table, then find the right slot in the
2775 * bucket. Finally, we can insert it.
2781 REGION_InsertEdgeInET(
2783 EDGE_TABLE_ENTRY
*ETE
,
2785 SCANLINE_LISTBLOCK
**SLLBlock
,
2788 EDGE_TABLE_ENTRY
*start
, *prev
;
2789 SCANLINE_LIST
*pSLL
, *pPrevSLL
;
2790 SCANLINE_LISTBLOCK
*tmpSLLBlock
;
2792 /* Find the right bucket to put the edge into */
2793 pPrevSLL
= &ET
->scanlines
;
2794 pSLL
= pPrevSLL
->next
;
2795 while (pSLL
&& (pSLL
->scanline
< scanline
))
2801 /* Reassign pSLL (pointer to SCANLINE_LIST) if necessary */
2802 if ((!pSLL
) || (pSLL
->scanline
> scanline
))
2804 if (*iSLLBlock
> SLLSPERBLOCK
-1)
2806 tmpSLLBlock
= ExAllocatePoolWithTag(PagedPool
,
2807 sizeof(SCANLINE_LISTBLOCK
),
2809 if (tmpSLLBlock
== NULL
)
2811 DPRINT1("REGION_InsertEdgeInETL(): Can't alloc SLLB\n");
2812 /* FIXME: Free resources? */
2816 (*SLLBlock
)->next
= tmpSLLBlock
;
2817 tmpSLLBlock
->next
= (SCANLINE_LISTBLOCK
*)NULL
;
2818 *SLLBlock
= tmpSLLBlock
;
2822 pSLL
= &((*SLLBlock
)->SLLs
[(*iSLLBlock
)++]);
2824 pSLL
->next
= pPrevSLL
->next
;
2825 pSLL
->edgelist
= (EDGE_TABLE_ENTRY
*)NULL
;
2826 pPrevSLL
->next
= pSLL
;
2829 pSLL
->scanline
= scanline
;
2831 /* Now insert the edge in the right bucket */
2832 prev
= (EDGE_TABLE_ENTRY
*)NULL
;
2833 start
= pSLL
->edgelist
;
2834 while (start
&& (start
->bres
.minor_axis
< ETE
->bres
.minor_axis
))
2837 start
= start
->next
;
2845 pSLL
->edgelist
= ETE
;
2848 /***********************************************************************
2851 * This routine moves EDGE_TABLEEntries from the
2852 * EDGE_TABLE into the Active Edge Table,
2853 * leaving them sorted by smaller x coordinate.
2860 EDGE_TABLE_ENTRY
*AET
,
2861 EDGE_TABLE_ENTRY
*ETEs
)
2863 EDGE_TABLE_ENTRY
*pPrevAET
;
2864 EDGE_TABLE_ENTRY
*tmp
;
2870 while (AET
&& (AET
->bres
.minor_axis
< ETEs
->bres
.minor_axis
))
2881 ETEs
->back
= pPrevAET
;
2882 pPrevAET
->next
= ETEs
;
2889 /***********************************************************************
2890 * REGION_computeWAET
2892 * This routine links the AET by the
2893 * nextWETE (winding EDGE_TABLE_ENTRY) link for
2894 * use by the winding number rule. The final
2895 * Active Edge Table (AET) might look something
2899 * ---------- --------- ---------
2900 * |ymax | |ymax | |ymax |
2901 * | ... | |... | |... |
2902 * |next |->|next |->|next |->...
2903 * |nextWETE| |nextWETE| |nextWETE|
2904 * --------- --------- ^--------
2906 * V-------------------> V---> ...
2913 EDGE_TABLE_ENTRY
*AET
)
2915 register EDGE_TABLE_ENTRY
*pWETE
;
2916 register INT inside
= 1;
2917 register INT isInside
= 0;
2919 AET
->nextWETE
= (EDGE_TABLE_ENTRY
*)NULL
;
2929 if ((!inside
&& !isInside
) ||
2930 ( inside
&& isInside
))
2932 pWETE
->nextWETE
= AET
;
2939 pWETE
->nextWETE
= (EDGE_TABLE_ENTRY
*)NULL
;
2942 /***********************************************************************
2943 * REGION_InsertionSort
2945 * Just a simple insertion sort using
2946 * pointers and back pointers to sort the Active
2953 REGION_InsertionSort(
2954 EDGE_TABLE_ENTRY
*AET
)
2956 EDGE_TABLE_ENTRY
*pETEchase
;
2957 EDGE_TABLE_ENTRY
*pETEinsert
;
2958 EDGE_TABLE_ENTRY
*pETEchaseBackTMP
;
2959 BOOL changed
= FALSE
;
2966 while (pETEchase
->back
->bres
.minor_axis
> AET
->bres
.minor_axis
)
2967 pETEchase
= pETEchase
->back
;
2970 if (pETEchase
!= pETEinsert
)
2972 pETEchaseBackTMP
= pETEchase
->back
;
2973 pETEinsert
->back
->next
= AET
;
2975 AET
->back
= pETEinsert
->back
;
2977 pETEinsert
->next
= pETEchase
;
2978 pETEchase
->back
->next
= pETEinsert
;
2979 pETEchase
->back
= pETEinsert
;
2980 pETEinsert
->back
= pETEchaseBackTMP
;
2988 /***********************************************************************
2989 * REGION_FreeStorage
2997 SCANLINE_LISTBLOCK
*pSLLBlock
)
2999 SCANLINE_LISTBLOCK
*tmpSLLBlock
;
3003 tmpSLLBlock
= pSLLBlock
->next
;
3004 ExFreePoolWithTag(pSLLBlock
, TAG_REGION
);
3005 pSLLBlock
= tmpSLLBlock
;
3010 /***********************************************************************
3011 * REGION_PtsToRegion
3013 * Create an array of rectangles from a list of points.
3019 INT numFullPtBlocks
,
3021 POINTBLOCK
*FirstPtBlock
,
3026 POINTBLOCK
*CurPtBlock
;
3028 RECTL
*extents
, *temp
;
3031 extents
= ®
->rdh
.rcBound
;
3033 numRects
= ((numFullPtBlocks
* NUMPTSTOBUFFER
) + iCurPtBlock
) >> 1;
3035 /* Make sure, we have at least one rect */
3041 temp
= ExAllocatePoolWithTag(PagedPool
, numRects
* sizeof(RECT
), TAG_REGION
);
3047 if (reg
->Buffer
!= NULL
)
3049 COPY_RECTS(temp
, reg
->Buffer
, reg
->rdh
.nCount
);
3050 if (reg
->Buffer
!= ®
->rdh
.rcBound
)
3051 ExFreePoolWithTag(reg
->Buffer
, TAG_REGION
);
3055 reg
->rdh
.nCount
= numRects
;
3056 CurPtBlock
= FirstPtBlock
;
3057 rects
= reg
->Buffer
- 1;
3059 extents
->left
= LARGE_COORDINATE
, extents
->right
= SMALL_COORDINATE
;
3061 for ( ; numFullPtBlocks
>= 0; numFullPtBlocks
--)
3063 /* The loop uses 2 points per iteration */
3064 i
= NUMPTSTOBUFFER
>> 1;
3065 if (numFullPtBlocks
== 0)
3066 i
= iCurPtBlock
>> 1;
3068 for (pts
= CurPtBlock
->pts
; i
--; pts
+= 2)
3070 if (pts
->x
== pts
[1].x
)
3073 if ((numRects
&& pts
->x
== rects
->left
) &&
3074 (pts
->y
== rects
->bottom
) &&
3075 (pts
[1].x
== rects
->right
) &&
3076 ((numRects
== 1) || (rects
[-1].top
!= rects
->top
)) &&
3077 (i
&& pts
[2].y
> pts
[1].y
))
3079 rects
->bottom
= pts
[1].y
+ 1;
3085 rects
->left
= pts
->x
;
3086 rects
->top
= pts
->y
;
3087 rects
->right
= pts
[1].x
;
3088 rects
->bottom
= pts
[1].y
+ 1;
3090 if (rects
->left
< extents
->left
)
3091 extents
->left
= rects
->left
;
3092 if (rects
->right
> extents
->right
)
3093 extents
->right
= rects
->right
;
3096 CurPtBlock
= CurPtBlock
->next
;
3101 extents
->top
= reg
->Buffer
->top
;
3102 extents
->bottom
= rects
->bottom
;
3109 extents
->bottom
= 0;
3112 reg
->rdh
.nCount
= numRects
;
3117 /***********************************************************************
3118 * REGION_CreateETandAET
3120 * This routine creates the edge table for
3121 * scan converting polygons.
3122 * The Edge Table (ET) looks like:
3126 * | ymax | SCANLINE_LISTs
3127 * |scanline|-->------------>-------------->...
3128 * -------- |scanline| |scanline|
3129 * |edgelist| |edgelist|
3130 * --------- ---------
3134 * list of ETEs list of ETEs
3136 * where ETE is an EDGE_TABLE_ENTRY data structure,
3137 * and there is one SCANLINE_LIST per scanline at
3138 * which an edge is initially entered.
3144 REGION_CreateETandAET(
3149 EDGE_TABLE_ENTRY
*AET
,
3150 EDGE_TABLE_ENTRY
*pETEs
,
3151 SCANLINE_LISTBLOCK
*pSLLBlock
)
3153 const POINT
*top
, *bottom
;
3154 const POINT
*PrevPt
, *CurrPt
, *EndPt
;
3159 /* Initialize the Active Edge Table */
3160 AET
->next
= (EDGE_TABLE_ENTRY
*)NULL
;
3161 AET
->back
= (EDGE_TABLE_ENTRY
*)NULL
;
3162 AET
->nextWETE
= (EDGE_TABLE_ENTRY
*)NULL
;
3163 AET
->bres
.minor_axis
= SMALL_COORDINATE
;
3165 /* Initialize the Edge Table. */
3166 ET
->scanlines
.next
= (SCANLINE_LIST
*)NULL
;
3167 ET
->ymax
= SMALL_COORDINATE
;
3168 ET
->ymin
= LARGE_COORDINATE
;
3169 pSLLBlock
->next
= (SCANLINE_LISTBLOCK
*)NULL
;
3172 for (poly
= 0; poly
< nbpolygons
; poly
++)
3174 count
= Count
[poly
];
3181 /* For each vertex in the array of points.
3182 * In this loop we are dealing with two vertices at
3183 * a time -- these make up one edge of the polygon. */
3188 /* Find out which point is above and which is below. */
3189 if (PrevPt
->y
> CurrPt
->y
)
3191 bottom
= PrevPt
, top
= CurrPt
;
3192 pETEs
->ClockWise
= 0;
3196 bottom
= CurrPt
, top
= PrevPt
;
3197 pETEs
->ClockWise
= 1;
3200 /* Don't add horizontal edges to the Edge table. */
3201 if (bottom
->y
!= top
->y
)
3203 /* -1 so we don't get last scanline */
3204 pETEs
->ymax
= bottom
->y
- 1;
3206 /* Initialize integer edge algorithm */
3207 dy
= bottom
->y
- top
->y
;
3208 BRESINITPGONSTRUCT(dy
, top
->x
, bottom
->x
, pETEs
->bres
);
3210 REGION_InsertEdgeInET(ET
,
3216 if (PrevPt
->y
> ET
->ymax
)
3217 ET
->ymax
= PrevPt
->y
;
3218 if (PrevPt
->y
< ET
->ymin
)
3219 ET
->ymin
= PrevPt
->y
;
3230 REGION_SetPolyPolygonRgn(
3231 _Inout_ PREGION prgn
,
3232 _In_
const POINT
*ppt
,
3233 _In_
const ULONG
*pcPoints
,
3234 _In_ ULONG cPolygons
,
3237 EDGE_TABLE_ENTRY
*pAET
; /* Active Edge Table */
3238 INT y
; /* Current scanline */
3239 INT iPts
= 0; /* Number of pts in buffer */
3240 EDGE_TABLE_ENTRY
*pWETE
; /* Winding Edge Table Entry */
3241 SCANLINE_LIST
*pSLL
; /* Current SCANLINE_LIST */
3242 POINT
*pts
; /* Output buffer */
3243 EDGE_TABLE_ENTRY
*pPrevAET
; /* Pointer to previous AET */
3244 EDGE_TABLE ET
; /* Header node for ET */
3245 EDGE_TABLE_ENTRY AET
; /* Header node for AET */
3246 EDGE_TABLE_ENTRY
*pETEs
; /* EDGE_TABLEEntries pool */
3247 SCANLINE_LISTBLOCK SLLBlock
; /* Header for SCANLINE_LIST */
3248 INT fixWAET
= FALSE
;
3249 POINTBLOCK FirstPtBlock
, *curPtBlock
; /* PtBlock buffers */
3250 POINTBLOCK
*tmpPtBlock
;
3251 UINT numFullPtBlocks
= 0;
3254 /* Check if iMode is valid */
3255 if ((iMode
!= ALTERNATE
) && (iMode
!= WINDING
))
3257 DPRINT1("Invalid iMode: %lu\n", iMode
);
3261 /* Special case a rectangle */
3262 if (((cPolygons
== 1) && ((pcPoints
[0] == 4) ||
3263 ((pcPoints
[0] == 5) && (ppt
[4].x
== ppt
[0].x
) && (ppt
[4].y
== ppt
[0].y
)))) &&
3264 (((ppt
[0].y
== ppt
[1].y
) &&
3265 (ppt
[1].x
== ppt
[2].x
) &&
3266 (ppt
[2].y
== ppt
[3].y
) &&
3267 (ppt
[3].x
== ppt
[0].x
)) ||
3268 ((ppt
[0].x
== ppt
[1].x
) &&
3269 (ppt
[1].y
== ppt
[2].y
) &&
3270 (ppt
[2].x
== ppt
[3].x
) &&
3271 (ppt
[3].y
== ppt
[0].y
))))
3273 REGION_SetRectRgn(prgn
,
3274 min(ppt
[0].x
, ppt
[2].x
),
3275 min(ppt
[0].y
, ppt
[2].y
),
3276 max(ppt
[0].x
, ppt
[2].x
),
3277 max(ppt
[0].y
, ppt
[2].y
));
3281 for (poly
= total
= 0; poly
< cPolygons
; poly
++)
3282 total
+= pcPoints
[poly
];
3284 pETEs
= ExAllocatePoolWithTag(PagedPool
,
3285 sizeof(EDGE_TABLE_ENTRY
) * total
,
3289 DPRINT1("Failed to allocate %lu edge entries\n", total
);
3293 pts
= FirstPtBlock
.pts
;
3294 REGION_CreateETandAET(pcPoints
, cPolygons
, ppt
, &ET
, &AET
, pETEs
, &SLLBlock
);
3295 pSLL
= ET
.scanlines
.next
;
3296 curPtBlock
= &FirstPtBlock
;
3298 if (iMode
!= WINDING
)
3300 /* For each scanline */
3301 for (y
= ET
.ymin
; y
< ET
.ymax
; y
++)
3303 /* Add a new edge to the active edge table when we
3304 * get to the next edge. */
3305 if (pSLL
!= NULL
&& y
== pSLL
->scanline
)
3307 REGION_loadAET(&AET
, pSLL
->edgelist
);
3313 /* For each active edge */
3316 pts
->x
= pAET
->bres
.minor_axis
, pts
->y
= y
;
3319 /* Send out the buffer */
3320 if (iPts
== NUMPTSTOBUFFER
)
3322 tmpPtBlock
= ExAllocatePoolWithTag(PagedPool
,
3325 if (tmpPtBlock
== NULL
)
3327 DPRINT1("Can't alloc tmpPtBlock\n");
3328 ExFreePoolWithTag(pETEs
, TAG_REGION
);
3332 curPtBlock
->next
= tmpPtBlock
;
3333 curPtBlock
= tmpPtBlock
;
3334 pts
= curPtBlock
->pts
;
3339 EVALUATEEDGEEVENODD(pAET
, pPrevAET
, y
);
3342 REGION_InsertionSort(&AET
);
3347 /* For each scanline */
3348 for (y
= ET
.ymin
; y
< ET
.ymax
; y
++)
3350 /* Add a new edge to the active edge table when we
3351 * get to the next edge. */
3352 if (pSLL
!= NULL
&& y
== pSLL
->scanline
)
3354 REGION_loadAET(&AET
, pSLL
->edgelist
);
3355 REGION_computeWAET(&AET
);
3363 /* For each active edge */
3366 /* Add to the buffer only those edges that
3367 * are in the Winding active edge table. */
3370 pts
->x
= pAET
->bres
.minor_axis
;
3375 /* Send out the buffer */
3376 if (iPts
== NUMPTSTOBUFFER
)
3378 tmpPtBlock
= ExAllocatePoolWithTag(PagedPool
,
3381 if (tmpPtBlock
== NULL
)
3383 DPRINT1("Can't alloc tPB\n");
3384 ExFreePoolWithTag(pETEs
, TAG_REGION
);
3387 curPtBlock
->next
= tmpPtBlock
;
3388 curPtBlock
= tmpPtBlock
;
3389 pts
= curPtBlock
->pts
;
3394 pWETE
= pWETE
->nextWETE
;
3397 EVALUATEEDGEWINDING(pAET
, pPrevAET
, y
, fixWAET
);
3400 /* Recompute the winding active edge table if
3401 * we just resorted or have exited an edge. */
3402 if (REGION_InsertionSort(&AET
) || fixWAET
)
3404 REGION_computeWAET(&AET
);
3410 REGION_FreeStorage(SLLBlock
.next
);
3411 REGION_PtsToRegion(numFullPtBlocks
, iPts
, &FirstPtBlock
, prgn
);
3413 for (curPtBlock
= FirstPtBlock
.next
; numFullPtBlocks
-- > 0;)
3415 tmpPtBlock
= curPtBlock
->next
;
3416 ExFreePoolWithTag(curPtBlock
, TAG_REGION
);
3417 curPtBlock
= tmpPtBlock
;
3420 ExFreePoolWithTag(pETEs
, TAG_REGION
);
3426 GreCreatePolyPolygonRgn(
3427 _In_
const POINT
*ppt
,
3428 _In_
const ULONG
*pcPoints
,
3429 _In_ ULONG cPolygons
,
3435 /* Allocate a new region */
3436 prgn
= REGION_AllocUserRgnWithHandle(0);
3439 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
3443 /* Call the internal function and check for success */
3444 if (REGION_SetPolyPolygonRgn(prgn
, ppt
, pcPoints
, cPolygons
, iMode
))
3446 /* Success, get the handle and unlock the region */
3447 hrgn
= prgn
->BaseObject
.hHmgr
;
3448 REGION_UnlockRgn(prgn
);
3452 /* Failure, delete the region */
3453 REGION_Delete(prgn
);
3469 Rgn
= REGION_LockRgn(hRgn
);
3475 Ret
= REGION_RectInRegion(Rgn
, rc
);
3476 REGION_UnlockRgn(Rgn
);
3482 // NtGdi Exported Functions
3496 /* Validate the combine mode */
3497 if ((iMode
< RGN_AND
) || (iMode
> RGN_COPY
))
3502 /* Validate that we have the required regions */
3503 if ((hrgnDst
== NULL
) ||
3504 (hrgnSrc1
== NULL
) ||
3505 ((iMode
!= RGN_COPY
) && (hrgnSrc2
== NULL
)))
3507 DPRINT1("NtGdiCombineRgn invalid parameters: %p, %p, %p, %d\n",
3508 hrgnDst
, hrgnSrc1
, hrgnSrc2
, iMode
);
3509 EngSetLastError(ERROR_INVALID_HANDLE
);
3513 /* Lock all regions */
3515 ahrgn
[1] = hrgnSrc1
;
3516 ahrgn
[2] = iMode
!= RGN_COPY
? hrgnSrc2
: NULL
;
3517 if (!GDIOBJ_bLockMultipleObjects(3, (HGDIOBJ
*)ahrgn
, (PVOID
*)aprgn
, GDIObjType_RGN_TYPE
))
3519 DPRINT1("NtGdiCombineRgn failed to lock regions: %p, %p, %p, %d\n",
3520 hrgnDst
, hrgnSrc1
, hrgnSrc2
, iMode
);
3524 /* HACK: Sync usermode attributes */
3525 REGION_vSyncRegion(aprgn
[0]);
3526 if (aprgn
[1] != aprgn
[0])
3527 REGION_vSyncRegion(aprgn
[1]);
3528 if ((aprgn
[2] != NULL
) && (aprgn
[2] != aprgn
[0]) && (aprgn
[2] != aprgn
[1]))
3529 REGION_vSyncRegion(aprgn
[2]);
3531 /* Call the internal function */
3532 iResult
= IntGdiCombineRgn(aprgn
[0], aprgn
[1], aprgn
[2], iMode
);
3534 /* Unlock and return */
3535 REGION_UnlockRgn(aprgn
[0]);
3536 REGION_UnlockRgn(aprgn
[1]);
3537 if (aprgn
[2] != NULL
)
3538 REGION_UnlockRgn(aprgn
[2]);
3545 NtGdiCreateEllipticRgn(
3551 return NtGdiCreateRoundRectRgn(Left
,
3569 /* Allocate region data structure with space for 1 RECTL */
3570 pRgn
= REGION_AllocUserRgnWithHandle(1);
3573 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
3577 hRgn
= pRgn
->BaseObject
.hHmgr
;
3579 REGION_SetRectRgn(pRgn
, LeftRect
, TopRect
, RightRect
, BottomRect
);
3580 REGION_UnlockRgn(pRgn
);
3582 DPRINT("Returning %p.\n", hRgn
);
3589 NtGdiCreateRoundRectRgn(
3599 INT asq
, bsq
, d
, xd
, yd
;
3602 /* Make the dimensions sensible */
3617 ellipse_width
= abs(ellipse_width
);
3618 ellipse_height
= abs(ellipse_height
);
3620 /* Check parameters */
3621 if (ellipse_width
> right
-left
)
3622 ellipse_width
= right
-left
;
3623 if (ellipse_height
> bottom
-top
)
3624 ellipse_height
= bottom
-top
;
3626 /* Check if we can do a normal rectangle instead */
3627 if ((ellipse_width
< 2) || (ellipse_height
< 2))
3628 return NtGdiCreateRectRgn(left
, top
, right
, bottom
);
3631 d
= (ellipse_height
< 128) ? ((3 * ellipse_height
) >> 2) : 64;
3632 obj
= REGION_AllocUserRgnWithHandle(d
);
3636 hrgn
= obj
->BaseObject
.hHmgr
;
3638 /* Ellipse algorithm, based on an article by K. Porter
3639 in DDJ Graphics Programming Column, 8/89 */
3640 asq
= ellipse_width
* ellipse_width
/ 4; /* a^2 */
3641 bsq
= ellipse_height
* ellipse_height
/ 4; /* b^2 */
3642 d
= bsq
- asq
* ellipse_height
/ 2 + asq
/ 4; /* b^2 - a^2b + a^2/4 */
3644 yd
= asq
* ellipse_height
; /* 2a^2b */
3646 rect
.left
= left
+ ellipse_width
/ 2;
3647 rect
.right
= right
- ellipse_width
/ 2;
3649 /* Loop to draw first half of quadrant */
3652 /* If nearest pixel is toward the center */
3655 /* Move toward center */
3657 rect
.bottom
= rect
.top
+ 1;
3658 REGION_UnionRectWithRgn(obj
, &rect
);
3659 rect
.top
= --bottom
;
3660 rect
.bottom
= rect
.top
+ 1;
3661 REGION_UnionRectWithRgn(obj
, &rect
);
3666 /* Next horiz point */
3673 /* Loop to draw second half of quadrant */
3674 d
+= (3 * (asq
-bsq
) / 2 - (xd
+yd
)) / 2;
3677 /* next vertical point */
3679 rect
.bottom
= rect
.top
+ 1;
3680 REGION_UnionRectWithRgn(obj
, &rect
);
3681 rect
.top
= --bottom
;
3682 rect
.bottom
= rect
.top
+ 1;
3683 REGION_UnionRectWithRgn(obj
, &rect
);
3685 /* If nearest pixel is outside ellipse */
3688 /* Move away from center */
3699 /* Add the inside rectangle */
3703 rect
.bottom
= bottom
;
3704 REGION_UnionRectWithRgn(obj
, &rect
);
3707 REGION_UnlockRgn(obj
);
3720 PRECTL tRect1
, tRect2
;
3724 /* Check if we got 2 regions */
3725 if ((hSrcRgn1
== NULL
) || (hSrcRgn2
== NULL
))
3730 /* Check if these are the same regions */
3731 if (hSrcRgn1
== hSrcRgn2
)
3733 /* Make sure this region is valid */
3734 if ((GDI_HANDLE_GET_TYPE(hSrcRgn1
) == GDILoObjType_LO_REGION_TYPE
) &&
3735 GreIsHandleValid(hSrcRgn1
))
3742 /* Lock both regions */
3743 ahrgn
[0] = hSrcRgn1
;
3744 ahrgn
[1] = hSrcRgn2
;
3745 if (!GDIOBJ_bLockMultipleObjects(2, (HGDIOBJ
*)ahrgn
, (PVOID
*)aprgn
, GDIObjType_RGN_TYPE
))
3747 DPRINT1("NtGdiEqualRgn failed to lock regions: %p, %p\n",
3748 hSrcRgn1
, hSrcRgn2
);
3752 REGION_vSyncRegion(aprgn
[0]);
3753 REGION_vSyncRegion(aprgn
[1]);
3758 if (rgn1
->rdh
.nCount
!= rgn2
->rdh
.nCount
)
3761 if (rgn1
->rdh
.nCount
== 0)
3767 if ((rgn1
->rdh
.rcBound
.left
!= rgn2
->rdh
.rcBound
.left
) ||
3768 (rgn1
->rdh
.rcBound
.right
!= rgn2
->rdh
.rcBound
.right
) ||
3769 (rgn1
->rdh
.rcBound
.top
!= rgn2
->rdh
.rcBound
.top
) ||
3770 (rgn1
->rdh
.rcBound
.bottom
!= rgn2
->rdh
.rcBound
.bottom
))
3773 tRect1
= rgn1
->Buffer
;
3774 tRect2
= rgn2
->Buffer
;
3776 if ((tRect1
== NULL
) || (tRect2
== NULL
))
3779 for (i
=0; i
< rgn1
->rdh
.nCount
; i
++)
3781 if ((tRect1
[i
].left
!= tRect2
[i
].left
) ||
3782 (tRect1
[i
].right
!= tRect2
[i
].right
) ||
3783 (tRect1
[i
].top
!= tRect2
[i
].top
) ||
3784 (tRect1
[i
].bottom
!= tRect2
[i
].bottom
))
3791 REGION_UnlockRgn(rgn1
);
3792 REGION_UnlockRgn(rgn2
);
3798 NtGdiExtCreateRegion(
3799 OPTIONAL LPXFORM Xform
,
3810 NTSTATUS Status
= STATUS_SUCCESS
;
3814 DPRINT("NtGdiExtCreateRegion\n");
3817 ProbeForRead(RgnData
, Count
, 1);
3818 nCount
= RgnData
->rdh
.nCount
;
3819 iType
= RgnData
->rdh
.iType
;
3820 dwSize
= RgnData
->rdh
.dwSize
;
3821 rects
= (RECT
*)RgnData
->Buffer
;
3823 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3825 Status
= _SEH2_GetExceptionCode();
3829 if (!NT_SUCCESS(Status
))
3831 SetLastNtError(Status
);
3835 /* Check parameters, but don't set last error here */
3836 if ((Count
< sizeof(RGNDATAHEADER
) + nCount
* sizeof(RECT
)) ||
3837 (iType
!= RDH_RECTANGLES
) ||
3838 (dwSize
!= sizeof(RGNDATAHEADER
)))
3843 Region
= REGION_AllocUserRgnWithHandle(nCount
);
3847 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
3850 hRgn
= Region
->BaseObject
.hHmgr
;
3854 /* Insert the rectangles one by one */
3855 for(i
=0; i
<nCount
; i
++)
3857 REGION_UnionRectWithRgn(Region
, &rects
[i
]);
3864 /* Init the XFORMOBJ from the Xform struct */
3865 Status
= STATUS_INVALID_PARAMETER
;
3866 XFORMOBJ_vInit(&xo
, &matrix
);
3867 ret
= XFORMOBJ_iSetXform(&xo
, (XFORML
*)Xform
);
3869 /* Check for error */
3870 if (ret
!= DDI_ERROR
)
3872 /* Apply the coordinate transformation on the rects */
3873 if (XFORMOBJ_bApplyXform(&xo
,
3875 Region
->rdh
.nCount
* 2,
3879 Status
= STATUS_SUCCESS
;
3884 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3886 Status
= _SEH2_GetExceptionCode();
3889 if (!NT_SUCCESS(Status
))
3891 EngSetLastError(ERROR_INVALID_PARAMETER
);
3892 REGION_UnlockRgn(Region
);
3893 GreDeleteObject(hRgn
);
3897 REGION_UnlockRgn(Region
);
3911 NTSTATUS Status
= STATUS_SUCCESS
;
3913 Rgn
= REGION_LockRgn(hRgn
);
3919 ret
= REGION_GetRgnBox(Rgn
, &SafeRect
);
3920 REGION_UnlockRgn(Rgn
);
3928 ProbeForWrite(pRect
, sizeof(RECT
), 1);
3931 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3933 Status
= _SEH2_GetExceptionCode();
3936 if (!NT_SUCCESS(Status
))
3954 DPRINT("NtGdiOffsetRgn: hrgn %p cx %d cy %d\n", hrgn
, cx
, cy
);
3956 /* Lock the region */
3957 prgn
= REGION_LockRgn(hrgn
);
3960 DPRINT1("NtGdiOffsetRgn: failed to lock region %p\n", hrgn
);
3964 /* Call the internal function */
3965 if (!REGION_bOffsetRgn(prgn
, cx
, cy
))
3971 iResult
= REGION_Complexity(prgn
);
3974 /* Unlock and return the result */
3975 REGION_UnlockRgn(prgn
);
3989 /* Lock the region */
3990 prgn
= REGION_LockRgn(hrgn
);
3993 DPRINT1("NtGdiPtInRegion: hrgn error\n");
3997 /* Call the internal function */
3998 bResult
= REGION_PtInRegion(prgn
, x
, y
);
4000 /* Unlock and return the result */
4001 REGION_UnlockRgn(prgn
);
4010 _Inout_ LPRECT prclUnsafe
)
4014 /* Probe and copy the rect */
4017 ProbeForRead(prclUnsafe
, sizeof(RECT
), 1);
4018 rcTemp
= *prclUnsafe
;
4020 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4022 DPRINT1("NtGdiRectInRegion: Exception accessing the rect\n");
4027 /* Call the internal function */
4028 return IntRectInRegion(hrgn
, &rcTemp
);
4042 /* Lock the region */
4043 prgn
= REGION_LockRgn(hrgn
);
4049 /* Call the internal API */
4050 REGION_SetRectRgn(prgn
, xLeft
, yTop
, xRight
, yBottom
);
4052 /* Unlock the region and return success */
4053 REGION_UnlockRgn(prgn
);
4058 * MSDN: GetRegionData, Return Values:
4060 * "If the function succeeds and dwCount specifies an adequate number of bytes,
4061 * the return value is always dwCount. If dwCount is too small or the function
4062 * fails, the return value is 0. If lpRgnData is NULL, the return value is the
4063 * required number of bytes.
4065 * If the function fails, the return value is zero."
4067 _Success_(return!=0)
4073 _In_ ULONG cjBuffer
,
4074 _Out_writes_bytes_to_opt_(cjBuffer
, return) LPRGNDATA lpRgnData
)
4076 ULONG cjRects
, cjSize
;
4079 /* Lock the region */
4080 prgn
= REGION_LockRgn(hrgn
);
4083 EngSetLastError(ERROR_INVALID_HANDLE
);
4087 /* Calculate the region sizes */
4088 cjRects
= prgn
->rdh
.nCount
* sizeof(RECT
);
4089 cjSize
= cjRects
+ sizeof(RGNDATAHEADER
);
4091 /* Check if region data is requested */
4094 /* Check if the buffer is large enough */
4095 if (cjBuffer
>= cjSize
)
4097 /* Probe the buffer and copy the data */
4100 ProbeForWrite(lpRgnData
, cjSize
, sizeof(ULONG
));
4101 RtlCopyMemory(lpRgnData
, &prgn
->rdh
, sizeof(RGNDATAHEADER
));
4102 RtlCopyMemory(lpRgnData
->Buffer
, prgn
->Buffer
, cjRects
);
4103 lpRgnData
->rdh
.iType
= RDH_RECTANGLES
;
4105 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4107 EngSetLastError(ERROR_INVALID_PARAMETER
);
4114 /* Buffer is too small */
4115 EngSetLastError(ERROR_INVALID_PARAMETER
);
4120 /* Unlock the region and return the size */
4121 REGION_UnlockRgn(prgn
);