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
409 * Check to see if there is enough memory in the present region.
411 static __inline INT
xmemcheck(PREGION reg
, PRECTL
*rect
, PRECTL
*firstrect
)
413 if ((reg
->rdh
.nCount
+1) * sizeof(RECT
) >= reg
->rdh
.nRgnSize
)
416 DWORD NewSize
= 2 * reg
->rdh
.nRgnSize
;
418 if (NewSize
< (reg
->rdh
.nCount
+ 1) * sizeof(RECT
))
420 NewSize
= (reg
->rdh
.nCount
+ 1) * sizeof(RECT
);
423 temp
= ExAllocatePoolWithTag(PagedPool
, NewSize
, TAG_REGION
);
429 /* Copy the rectangles */
430 COPY_RECTS(temp
, *firstrect
, reg
->rdh
.nCount
);
432 reg
->rdh
.nRgnSize
= NewSize
;
433 if (*firstrect
!= ®
->rdh
.rcBound
)
435 ExFreePoolWithTag(*firstrect
, TAG_REGION
);
439 *rect
= (*firstrect
) + reg
->rdh
.nCount
;
444 #define MEMCHECK(reg, rect, firstrect) xmemcheck(reg,&(rect),(PRECTL *)&(firstrect))
446 typedef VOID (FASTCALL
*overlapProcp
)(PREGION
, PRECT
, PRECT
, PRECT
, PRECT
, INT
, INT
);
447 typedef VOID (FASTCALL
*nonOverlapProcp
)(PREGION
, PRECT
, PRECT
, INT
, INT
);
449 // Number of points to buffer before sending them off to scanlines() : Must be an even number
450 #define NUMPTSTOBUFFER 200
452 #define RGN_DEFAULT_RECTS 2
454 // Used to allocate buffers for points and link the buffers together
455 typedef struct _POINTBLOCK
457 POINT pts
[NUMPTSTOBUFFER
];
458 struct _POINTBLOCK
*next
;
463 * This function is left there for debugging purposes.
467 IntDumpRegion(HRGN hRgn
)
471 Data
= RGNOBJAPI_Lock(hRgn
, NULL
);
474 DbgPrint("IntDumpRegion called with invalid region!\n");
478 DbgPrint("IntDumpRegion(%x): %d,%d-%d,%d %d\n",
480 Data
->rdh
.rcBound
.left
,
481 Data
->rdh
.rcBound
.top
,
482 Data
->rdh
.rcBound
.right
,
483 Data
->rdh
.rcBound
.bottom
,
486 RGNOBJAPI_Unlock(Data
);
488 #endif /* Not NDEBUG */
493 REGION_Complexity(PREGION prgn
)
498 DPRINT("Region Complexity -> %lu", prgn
->rdh
.nCount
);
499 switch (prgn
->rdh
.nCount
)
506 return COMPLEXREGION
;
517 /* Only copy if source and dest are not equal */
520 /* Check if we need to increase our buffer */
521 if (dst
->rdh
.nRgnSize
< src
->rdh
.nCount
* sizeof(RECT
))
525 /* Allocate a new buffer */
526 temp
= ExAllocatePoolWithTag(PagedPool
,
527 src
->rdh
.nCount
* sizeof(RECT
),
532 /* Free the old buffer */
533 if ((dst
->Buffer
!= NULL
) && (dst
->Buffer
!= &dst
->rdh
.rcBound
))
534 ExFreePoolWithTag(dst
->Buffer
, TAG_REGION
);
536 /* Set the new buffer and the size */
538 dst
->rdh
.nRgnSize
= src
->rdh
.nCount
* sizeof(RECT
);
541 dst
->rdh
.nCount
= src
->rdh
.nCount
;
542 dst
->rdh
.rcBound
.left
= src
->rdh
.rcBound
.left
;
543 dst
->rdh
.rcBound
.top
= src
->rdh
.rcBound
.top
;
544 dst
->rdh
.rcBound
.right
= src
->rdh
.rcBound
.right
;
545 dst
->rdh
.rcBound
.bottom
= src
->rdh
.rcBound
.bottom
;
546 dst
->rdh
.iType
= src
->rdh
.iType
;
547 COPY_RECTS(dst
->Buffer
, src
->Buffer
, src
->rdh
.nCount
);
559 RECTL
*pRect
, *pRectEnd
, *pExtents
;
561 /* Quick check for NULLREGION */
562 if (pReg
->rdh
.nCount
== 0)
564 pReg
->rdh
.rcBound
.left
= 0;
565 pReg
->rdh
.rcBound
.top
= 0;
566 pReg
->rdh
.rcBound
.right
= 0;
567 pReg
->rdh
.rcBound
.bottom
= 0;
568 pReg
->rdh
.iType
= RDH_RECTANGLES
;
572 pExtents
= &pReg
->rdh
.rcBound
;
573 pRect
= pReg
->Buffer
;
574 pRectEnd
= pReg
->Buffer
+ pReg
->rdh
.nCount
- 1;
576 /* Since pRect is the first rectangle in the region, it must have the
577 * smallest top and since pRectEnd is the last rectangle in the region,
578 * it must have the largest bottom, because of banding. Initialize left and
579 * right from pRect and pRectEnd, resp., as good things to initialize them
581 pExtents
->left
= pRect
->left
;
582 pExtents
->top
= pRect
->top
;
583 pExtents
->right
= pRectEnd
->right
;
584 pExtents
->bottom
= pRectEnd
->bottom
;
586 while (pRect
<= pRectEnd
)
588 if (pRect
->left
< pExtents
->left
)
589 pExtents
->left
= pRect
->left
;
590 if (pRect
->right
> pExtents
->right
)
591 pExtents
->right
= pRect
->right
;
595 pReg
->rdh
.iType
= RDH_RECTANGLES
;
598 // FIXME: This function needs review and testing
599 /***********************************************************************
610 ULONG i
, j
, clipa
, clipb
, nRgnSize
;
614 INT bottom
= MINLONG
;
616 if ((rect
->left
>= rect
->right
) ||
617 (rect
->top
>= rect
->bottom
) ||
618 (EXTENTCHECK(rect
, &rgnSrc
->rdh
.rcBound
) == 0))
623 /* Skip all rects that are completely above our intersect rect */
624 for (clipa
= 0; clipa
< rgnSrc
->rdh
.nCount
; clipa
++)
626 /* bottom is exclusive, so break when we go above it */
627 if (rgnSrc
->Buffer
[clipa
].bottom
> rect
->top
) break;
630 /* Bail out, if there is nothing left */
631 if (clipa
== rgnSrc
->rdh
.nCount
) goto empty
;
633 /* Find the last rect that is still within the intersect rect (exclusive) */
634 for (clipb
= clipa
; clipb
< rgnSrc
->rdh
.nCount
; clipb
++)
636 /* bottom is exclusive, so stop, when we start at that y pos */
637 if (rgnSrc
->Buffer
[clipb
].top
>= rect
->bottom
) break;
640 /* Bail out, if there is nothing left */
641 if (clipb
== clipa
) goto empty
;
643 // clipa - index of the first rect in the first intersecting band
644 // clipb - index of the last rect in the last intersecting band plus 1
646 /* Check if the buffer in the dest region is large enough,
647 otherwise allocate a new one */
648 nRgnSize
= (clipb
- clipa
) * sizeof(RECT
);
649 if ((rgnDst
!= rgnSrc
) && (rgnDst
->rdh
.nRgnSize
< nRgnSize
))
652 temp
= ExAllocatePoolWithTag(PagedPool
, nRgnSize
, TAG_REGION
);
656 /* Free the old buffer */
657 if (rgnDst
->Buffer
&& (rgnDst
->Buffer
!= &rgnDst
->rdh
.rcBound
))
658 ExFreePoolWithTag(rgnDst
->Buffer
, TAG_REGION
);
660 rgnDst
->Buffer
= temp
;
661 rgnDst
->rdh
.nCount
= 0;
662 rgnDst
->rdh
.nRgnSize
= nRgnSize
;
663 rgnDst
->rdh
.iType
= RDH_RECTANGLES
;
666 /* Loop all rects within the intersect rect from the y perspective */
667 for (i
= clipa
, j
= 0; i
< clipb
; i
++)
669 /* i - src index, j - dst index, j is always <= i for obvious reasons */
671 lpr
= &rgnSrc
->Buffer
[i
];
673 /* Make sure the source rect is not retarded */
674 ASSERT(lpr
->bottom
> rect
->top
);
675 ASSERT(lpr
->right
> rect
->left
);
677 /* We already checked above, this should hold true */
678 ASSERT(lpr
->bottom
> rect
->top
);
679 ASSERT(lpr
->top
< rect
->bottom
);
681 /* Check if this rect is really inside the intersect rect */
682 if ((lpr
->left
< rect
->right
) && (lpr
->right
> rect
->left
))
684 rpr
= &rgnDst
->Buffer
[j
];
686 /* Crop the rect with the intersect rect and add offset */
687 rpr
->top
= max(lpr
->top
, rect
->top
);
688 rpr
->bottom
= min(lpr
->bottom
, rect
->bottom
);
689 rpr
->left
= max(lpr
->left
, rect
->left
);
690 rpr
->right
= min(lpr
->right
, rect
->right
);
692 /* Make sure the resulting rect is not retarded */
693 ASSERT(lpr
->bottom
> rect
->top
);
694 ASSERT(lpr
->right
> rect
->left
);
696 /* Track new bounds */
697 if (rpr
->left
< left
) left
= rpr
->left
;
698 if (rpr
->right
> right
) right
= rpr
->right
;
699 if (rpr
->top
< top
) top
= rpr
->top
;
700 if (rpr
->bottom
> bottom
) bottom
= rpr
->bottom
;
702 /* Next target rect */
707 if (j
== 0) goto empty
;
709 /* Update the bounds rect */
710 rgnDst
->rdh
.rcBound
.left
= left
;
711 rgnDst
->rdh
.rcBound
.right
= right
;
712 rgnDst
->rdh
.rcBound
.top
= top
;
713 rgnDst
->rdh
.rcBound
.bottom
= bottom
;
715 /* Set new rect count */
716 rgnDst
->rdh
.nCount
= j
;
718 return REGION_Complexity(rgnDst
);
721 if (rgnDst
->Buffer
== NULL
)
723 rgnDst
->Buffer
= &rgnDst
->rdh
.rcBound
;
726 EMPTY_REGION(rgnDst
);
732 * Attempt to merge the rects in the current band with those in the
733 * previous one. Used only by REGION_RegionOp.
736 * The new index for the previous band.
738 * \note Side Effects:
739 * If coalescing takes place:
740 * - rectangles in the previous band will have their bottom fields
742 * - pReg->numRects will be decreased.
749 PREGION pReg
, /* Region to coalesce */
750 INT prevStart
, /* Index of start of previous band */
751 INT curStart
) /* Index of start of current band */
753 RECTL
*pPrevRect
; /* Current rect in previous band */
754 RECTL
*pCurRect
; /* Current rect in current band */
755 RECTL
*pRegEnd
; /* End of region */
756 INT curNumRects
; /* Number of rectangles in current band */
757 INT prevNumRects
; /* Number of rectangles in previous band */
758 INT bandtop
; /* Top coordinate for current band */
760 pRegEnd
= pReg
->Buffer
+ pReg
->rdh
.nCount
;
761 pPrevRect
= pReg
->Buffer
+ prevStart
;
762 prevNumRects
= curStart
- prevStart
;
764 /* Figure out how many rectangles are in the current band. Have to do
765 * this because multiple bands could have been added in REGION_RegionOp
766 * at the end when one region has been exhausted. */
767 pCurRect
= pReg
->Buffer
+ curStart
;
768 bandtop
= pCurRect
->top
;
769 for (curNumRects
= 0;
770 (pCurRect
!= pRegEnd
) && (pCurRect
->top
== bandtop
);
776 if (pCurRect
!= pRegEnd
)
778 /* If more than one band was added, we have to find the start
779 * of the last band added so the next coalescing job can start
780 * at the right place... (given when multiple bands are added,
781 * this may be pointless -- see above). */
783 while ((pRegEnd
-1)->top
== pRegEnd
->top
)
788 curStart
= pRegEnd
- pReg
->Buffer
;
789 pRegEnd
= pReg
->Buffer
+ pReg
->rdh
.nCount
;
792 if ((curNumRects
== prevNumRects
) && (curNumRects
!= 0))
794 pCurRect
-= curNumRects
;
796 /* The bands may only be coalesced if the bottom of the previous
797 * matches the top scanline of the current. */
798 if (pPrevRect
->bottom
== pCurRect
->top
)
800 /* Make sure the bands have rects in the same places. This
801 * assumes that rects have been added in such a way that they
802 * cover the most area possible. I.e. two rects in a band must
803 * have some horizontal space between them. */
806 if ((pPrevRect
->left
!= pCurRect
->left
) ||
807 (pPrevRect
->right
!= pCurRect
->right
))
809 /* The bands don't line up so they can't be coalesced. */
817 while (prevNumRects
!= 0);
819 pReg
->rdh
.nCount
-= curNumRects
;
820 pCurRect
-= curNumRects
;
821 pPrevRect
-= curNumRects
;
823 /* The bands may be merged, so set the bottom of each rect
824 * in the previous band to that of the corresponding rect in
825 * the current band. */
828 pPrevRect
->bottom
= pCurRect
->bottom
;
833 while (curNumRects
!= 0);
835 /* If only one band was added to the region, we have to backup
836 * curStart to the start of the previous band.
838 * If more than one band was added to the region, copy the
839 * other bands down. The assumption here is that the other bands
840 * came from the same region as the current one and no further
841 * coalescing can be done on them since it's all been done
842 * already... curStart is already in the right place. */
843 if (pCurRect
== pRegEnd
)
845 curStart
= prevStart
;
851 *pPrevRect
++ = *pCurRect
++;
853 while (pCurRect
!= pRegEnd
);
862 * Apply an operation to two regions. Called by REGION_Union,
863 * REGION_Inverse, REGION_Subtract, REGION_Intersect...
869 * The new region is overwritten.
871 *\note The idea behind this function is to view the two regions as sets.
872 * Together they cover a rectangle of area that this function divides
873 * into horizontal bands where points are covered only by one region
874 * or by both. For the first case, the nonOverlapFunc is called with
875 * each the band and the band's upper and lower extents. For the
876 * second, the overlapFunc is called to process the entire band. It
877 * is responsible for clipping the rectangles in the band, though
878 * this function provides the boundaries.
879 * At the end of each band, the new region is coalesced, if possible,
880 * to reduce the number of rectangles in the region.
887 PREGION newReg
, /* Place to store result */
888 PREGION reg1
, /* First region in operation */
889 PREGION reg2
, /* 2nd region in operation */
890 overlapProcp overlapFunc
, /* Function to call for over-lapping bands */
891 nonOverlapProcp nonOverlap1Func
, /* Function to call for non-overlapping bands in region 1 */
892 nonOverlapProcp nonOverlap2Func
) /* Function to call for non-overlapping bands in region 2 */
894 RECTL
*r1
; /* Pointer into first region */
895 RECTL
*r2
; /* Pointer into 2d region */
896 RECTL
*r1End
; /* End of 1st region */
897 RECTL
*r2End
; /* End of 2d region */
898 INT ybot
; /* Bottom of intersection */
899 INT ytop
; /* Top of intersection */
900 RECTL
*oldRects
; /* Old rects for newReg */
901 ULONG prevBand
; /* Index of start of
902 * Previous band in newReg */
903 ULONG curBand
; /* Index of start of current band in newReg */
904 RECTL
*r1BandEnd
; /* End of current band in r1 */
905 RECTL
*r2BandEnd
; /* End of current band in r2 */
906 ULONG top
; /* Top of non-overlapping band */
907 ULONG bot
; /* Bottom of non-overlapping band */
910 * set r1, r2, r1End and r2End appropriately, preserve the important
911 * parts of the destination region until the end in case it's one of
912 * the two source regions, then mark the "new" region empty, allocating
913 * another array of rectangles for it to use. */
916 r1End
= r1
+ reg1
->rdh
.nCount
;
917 r2End
= r2
+ reg2
->rdh
.nCount
;
919 /* newReg may be one of the src regions so we can't empty it. We keep a
920 * note of its rects pointer (so that we can free them later), preserve its
921 * extents and simply set numRects to zero. */
922 oldRects
= newReg
->Buffer
;
923 newReg
->rdh
.nCount
= 0;
925 /* Allocate a reasonable number of rectangles for the new region. The idea
926 * is to allocate enough so the individual functions don't need to
927 * reallocate and copy the array, which is time consuming, yet we don't
928 * have to worry about using too much memory. I hope to be able to
929 * nuke the Xrealloc() at the end of this function eventually. */
930 newReg
->rdh
.nRgnSize
= max(reg1
->rdh
.nCount
+ 1, reg2
->rdh
.nCount
) * 2 * sizeof(RECT
);
932 newReg
->Buffer
= ExAllocatePoolWithTag(PagedPool
,
933 newReg
->rdh
.nRgnSize
,
935 if (newReg
->Buffer
== NULL
)
937 newReg
->rdh
.nRgnSize
= 0;
941 /* Initialize ybot and ytop.
942 * In the upcoming loop, ybot and ytop serve different functions depending
943 * on whether the band being handled is an overlapping or non-overlapping
945 * In the case of a non-overlapping band (only one of the regions
946 * has points in the band), ybot is the bottom of the most recent
947 * intersection and thus clips the top of the rectangles in that band.
948 * ytop is the top of the next intersection between the two regions and
949 * serves to clip the bottom of the rectangles in the current band.
950 * For an overlapping band (where the two regions intersect), ytop clips
951 * the top of the rectangles of both regions and ybot clips the bottoms. */
952 if (reg1
->rdh
.rcBound
.top
< reg2
->rdh
.rcBound
.top
)
953 ybot
= reg1
->rdh
.rcBound
.top
;
955 ybot
= reg2
->rdh
.rcBound
.top
;
957 /* prevBand serves to mark the start of the previous band so rectangles
958 * can be coalesced into larger rectangles. qv. miCoalesce, above.
959 * In the beginning, there is no previous band, so prevBand == curBand
960 * (curBand is set later on, of course, but the first band will always
961 * start at index 0). prevBand and curBand must be indices because of
962 * the possible expansion, and resultant moving, of the new region's
963 * array of rectangles. */
967 curBand
= newReg
->rdh
.nCount
;
969 /* This algorithm proceeds one source-band (as opposed to a
970 * destination band, which is determined by where the two regions
971 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
972 * rectangle after the last one in the current band for their
973 * respective regions. */
975 while ((r1BandEnd
!= r1End
) && (r1BandEnd
->top
== r1
->top
))
981 while ((r2BandEnd
!= r2End
) && (r2BandEnd
->top
== r2
->top
))
986 /* First handle the band that doesn't intersect, if any.
988 * Note that attention is restricted to one band in the
989 * non-intersecting region at once, so if a region has n
990 * bands between the current position and the next place it overlaps
991 * the other, this entire loop will be passed through n times. */
992 if (r1
->top
< r2
->top
)
994 top
= max(r1
->top
,ybot
);
995 bot
= min(r1
->bottom
,r2
->top
);
997 if ((top
!= bot
) && (nonOverlap1Func
!= NULL
))
999 (*nonOverlap1Func
)(newReg
, r1
, r1BandEnd
, top
, bot
);
1004 else if (r2
->top
< r1
->top
)
1006 top
= max(r2
->top
,ybot
);
1007 bot
= min(r2
->bottom
,r1
->top
);
1009 if ((top
!= bot
) && (nonOverlap2Func
!= NULL
))
1011 (*nonOverlap2Func
)(newReg
, r2
, r2BandEnd
, top
, bot
);
1021 /* If any rectangles got added to the region, try and coalesce them
1022 * with rectangles from the previous band. Note we could just do
1023 * this test in miCoalesce, but some machines incur a not
1024 * inconsiderable cost for function calls, so... */
1025 if (newReg
->rdh
.nCount
!= curBand
)
1027 prevBand
= REGION_Coalesce(newReg
, prevBand
, curBand
);
1030 /* Now see if we've hit an intersecting band. The two bands only
1031 * intersect if ybot > ytop */
1032 ybot
= min(r1
->bottom
, r2
->bottom
);
1033 curBand
= newReg
->rdh
.nCount
;
1036 (*overlapFunc
)(newReg
, r1
, r1BandEnd
, r2
, r2BandEnd
, ytop
, ybot
);
1039 if (newReg
->rdh
.nCount
!= curBand
)
1041 prevBand
= REGION_Coalesce(newReg
, prevBand
, curBand
);
1044 /* If we've finished with a band (bottom == ybot) we skip forward
1045 * in the region to the next band. */
1046 if (r1
->bottom
== ybot
)
1050 if (r2
->bottom
== ybot
)
1055 while ((r1
!= r1End
) && (r2
!= r2End
));
1057 /* Deal with whichever region still has rectangles left. */
1058 curBand
= newReg
->rdh
.nCount
;
1061 if (nonOverlap1Func
!= NULL
)
1066 while ((r1BandEnd
< r1End
) && (r1BandEnd
->top
== r1
->top
))
1071 (*nonOverlap1Func
)(newReg
,
1078 while (r1
!= r1End
);
1081 else if ((r2
!= r2End
) && (nonOverlap2Func
!= NULL
))
1086 while ((r2BandEnd
< r2End
) && (r2BandEnd
->top
== r2
->top
))
1091 (*nonOverlap2Func
)(newReg
,
1098 while (r2
!= r2End
);
1101 if (newReg
->rdh
.nCount
!= curBand
)
1103 (VOID
)REGION_Coalesce(newReg
, prevBand
, curBand
);
1106 /* A bit of cleanup. To keep regions from growing without bound,
1107 * we shrink the array of rectangles to match the new number of
1108 * rectangles in the region. This never goes to 0, however...
1110 * Only do this stuff if the number of rectangles allocated is more than
1111 * twice the number of rectangles in the region (a simple optimization...). */
1112 if ((newReg
->rdh
.nRgnSize
> (2 * newReg
->rdh
.nCount
* sizeof(RECT
))) &&
1113 (newReg
->rdh
.nCount
> 2))
1115 if (REGION_NOT_EMPTY(newReg
))
1117 RECTL
*prev_rects
= newReg
->Buffer
;
1118 newReg
->Buffer
= ExAllocatePoolWithTag(PagedPool
,
1119 newReg
->rdh
.nCount
* sizeof(RECT
),
1122 if (newReg
->Buffer
== NULL
)
1124 newReg
->Buffer
= prev_rects
;
1128 newReg
->rdh
.nRgnSize
= newReg
->rdh
.nCount
*sizeof(RECT
);
1129 COPY_RECTS(newReg
->Buffer
, prev_rects
, newReg
->rdh
.nCount
);
1130 if (prev_rects
!= &newReg
->rdh
.rcBound
)
1131 ExFreePoolWithTag(prev_rects
, TAG_REGION
);
1136 /* No point in doing the extra work involved in an Xrealloc if
1137 * the region is empty */
1138 newReg
->rdh
.nRgnSize
= sizeof(RECT
);
1139 if (newReg
->Buffer
!= &newReg
->rdh
.rcBound
)
1140 ExFreePoolWithTag(newReg
->Buffer
, TAG_REGION
);
1142 newReg
->Buffer
= ExAllocatePoolWithTag(PagedPool
,
1145 ASSERT(newReg
->Buffer
);
1149 newReg
->rdh
.iType
= RDH_RECTANGLES
;
1151 if (oldRects
!= &newReg
->rdh
.rcBound
)
1152 ExFreePoolWithTag(oldRects
, TAG_REGION
);
1156 /***********************************************************************
1157 * Region Intersection
1158 ***********************************************************************/
1162 * Handle an overlapping band for REGION_Intersect.
1167 * \note Side Effects:
1168 * Rectangles may be added to the region.
1186 pNextRect
= pReg
->Buffer
+ pReg
->rdh
.nCount
;
1188 while ((r1
!= r1End
) && (r2
!= r2End
))
1190 left
= max(r1
->left
, r2
->left
);
1191 right
= min(r1
->right
, r2
->right
);
1193 /* If there's any overlap between the two rectangles, add that
1194 * overlap to the new region.
1195 * There's no need to check for subsumption because the only way
1196 * such a need could arise is if some region has two rectangles
1197 * right next to each other. Since that should never happen... */
1200 MEMCHECK(pReg
, pNextRect
, pReg
->Buffer
);
1201 pNextRect
->left
= left
;
1202 pNextRect
->top
= top
;
1203 pNextRect
->right
= right
;
1204 pNextRect
->bottom
= bottom
;
1205 pReg
->rdh
.nCount
+= 1;
1209 /* Need to advance the pointers. Shift the one that extends
1210 * to the right the least, since the other still has a chance to
1211 * overlap with that region's next rectangle, if you see what I mean. */
1212 if (r1
->right
< r2
->right
)
1216 else if (r2
->right
< r1
->right
)
1230 /***********************************************************************
1231 * REGION_IntersectRegion
1236 REGION_IntersectRegion(
1241 /* Check for trivial reject */
1242 if ((reg1
->rdh
.nCount
== 0) ||
1243 (reg2
->rdh
.nCount
== 0) ||
1244 (EXTENTCHECK(®1
->rdh
.rcBound
, ®2
->rdh
.rcBound
) == 0))
1246 newReg
->rdh
.nCount
= 0;
1250 REGION_RegionOp(newReg
,
1258 /* Can't alter newReg's extents before we call miRegionOp because
1259 * it might be one of the source regions and miRegionOp depends
1260 * on the extents of those regions being the same. Besides, this
1261 * way there's no checking against rectangles that will be nuked
1262 * due to coalescing, so we have to examine fewer rectangles. */
1263 REGION_SetExtents(newReg
);
1266 /***********************************************************************
1268 ***********************************************************************/
1271 * Handle a non-overlapping band for the union operation. Just
1272 * Adds the rectangles into the region. Doesn't have to check for
1273 * subsumption or anything.
1278 * \note Side Effects:
1279 * pReg->numRects is incremented and the final rectangles overwritten
1280 * with the rectangles we're passed.
1295 pNextRect
= pReg
->Buffer
+ pReg
->rdh
.nCount
;
1299 MEMCHECK(pReg
, pNextRect
, pReg
->Buffer
);
1300 pNextRect
->left
= r
->left
;
1301 pNextRect
->top
= top
;
1302 pNextRect
->right
= r
->right
;
1303 pNextRect
->bottom
= bottom
;
1304 pReg
->rdh
.nCount
+= 1;
1313 * Handle an overlapping band for the union operation. Picks the
1314 * left-most rectangle each time and merges it into the region.
1319 * \note Side Effects:
1320 * Rectangles are overwritten in pReg->rects and pReg->numRects will
1338 pNextRect
= pReg
->Buffer
+ pReg
->rdh
.nCount
;
1340 #define MERGERECT(r) \
1341 if ((pReg->rdh.nCount != 0) && \
1342 ((pNextRect-1)->top == top) && \
1343 ((pNextRect-1)->bottom == bottom) && \
1344 ((pNextRect-1)->right >= r->left)) \
1346 if ((pNextRect-1)->right < r->right) \
1348 (pNextRect-1)->right = r->right; \
1353 MEMCHECK(pReg, pNextRect, pReg->Buffer); \
1354 pNextRect->top = top; \
1355 pNextRect->bottom = bottom; \
1356 pNextRect->left = r->left; \
1357 pNextRect->right = r->right; \
1358 pReg->rdh.nCount += 1; \
1363 while ((r1
!= r1End
) && (r2
!= r2End
))
1365 if (r1
->left
< r2
->left
)
1381 while (r1
!= r1End
);
1394 /***********************************************************************
1395 * REGION_UnionRegion
1405 /* Checks all the simple cases
1406 * Region 1 and 2 are the same or region 1 is empty */
1407 if ((reg1
== reg2
) || (reg1
->rdh
.nCount
== 0) ||
1408 (reg1
->rdh
.rcBound
.right
<= reg1
->rdh
.rcBound
.left
) ||
1409 (reg1
->rdh
.rcBound
.bottom
<= reg1
->rdh
.rcBound
.top
))
1413 REGION_CopyRegion(newReg
, reg2
);
1419 /* If nothing to union (region 2 empty) */
1420 if ((reg2
->rdh
.nCount
== 0) ||
1421 (reg2
->rdh
.rcBound
.right
<= reg2
->rdh
.rcBound
.left
) ||
1422 (reg2
->rdh
.rcBound
.bottom
<= reg2
->rdh
.rcBound
.top
))
1426 REGION_CopyRegion(newReg
, reg1
);
1432 /* Region 1 completely subsumes region 2 */
1433 if ((reg1
->rdh
.nCount
== 1) &&
1434 (reg1
->rdh
.rcBound
.left
<= reg2
->rdh
.rcBound
.left
) &&
1435 (reg1
->rdh
.rcBound
.top
<= reg2
->rdh
.rcBound
.top
) &&
1436 (reg2
->rdh
.rcBound
.right
<= reg1
->rdh
.rcBound
.right
) &&
1437 (reg2
->rdh
.rcBound
.bottom
<= reg1
->rdh
.rcBound
.bottom
))
1441 REGION_CopyRegion(newReg
, reg1
);
1447 /* Region 2 completely subsumes region 1 */
1448 if ((reg2
->rdh
.nCount
== 1) &&
1449 (reg2
->rdh
.rcBound
.left
<= reg1
->rdh
.rcBound
.left
) &&
1450 (reg2
->rdh
.rcBound
.top
<= reg1
->rdh
.rcBound
.top
) &&
1451 (reg1
->rdh
.rcBound
.right
<= reg2
->rdh
.rcBound
.right
) &&
1452 (reg1
->rdh
.rcBound
.bottom
<= reg2
->rdh
.rcBound
.bottom
))
1456 REGION_CopyRegion(newReg
, reg2
);
1462 REGION_RegionOp(newReg
,
1469 newReg
->rdh
.rcBound
.left
= min(reg1
->rdh
.rcBound
.left
, reg2
->rdh
.rcBound
.left
);
1470 newReg
->rdh
.rcBound
.top
= min(reg1
->rdh
.rcBound
.top
, reg2
->rdh
.rcBound
.top
);
1471 newReg
->rdh
.rcBound
.right
= max(reg1
->rdh
.rcBound
.right
, reg2
->rdh
.rcBound
.right
);
1472 newReg
->rdh
.rcBound
.bottom
= max(reg1
->rdh
.rcBound
.bottom
, reg2
->rdh
.rcBound
.bottom
);
1475 /***********************************************************************
1476 * Region Subtraction
1477 ***********************************************************************/
1480 * Deal with non-overlapping band for subtraction. Any parts from
1481 * region 2 we discard. Anything from region 1 we add to the region.
1486 * \note Side Effects:
1487 * pReg may be affected.
1493 REGION_SubtractNonO1(
1502 pNextRect
= pReg
->Buffer
+ pReg
->rdh
.nCount
;
1506 MEMCHECK(pReg
, pNextRect
, pReg
->Buffer
);
1507 pNextRect
->left
= r
->left
;
1508 pNextRect
->top
= top
;
1509 pNextRect
->right
= r
->right
;
1510 pNextRect
->bottom
= bottom
;
1511 pReg
->rdh
.nCount
+= 1;
1521 * Overlapping band subtraction. x1 is the left-most point not yet
1527 * \note Side Effects:
1528 * pReg may have rectangles added to it.
1547 pNextRect
= pReg
->Buffer
+ pReg
->rdh
.nCount
;
1549 while ((r1
!= r1End
) && (r2
!= r2End
))
1551 if (r2
->right
<= left
)
1553 /* Subtrahend missed the boat: go to next subtrahend. */
1556 else if (r2
->left
<= left
)
1558 /* Subtrahend preceeds minuend: nuke left edge of minuend. */
1560 if (left
>= r1
->right
)
1562 /* Minuend completely covered: advance to next minuend and
1563 * reset left fence to edge of new minuend. */
1570 /* Subtrahend now used up since it doesn't extend beyond
1575 else if (r2
->left
< r1
->right
)
1577 /* Left part of subtrahend covers part of minuend: add uncovered
1578 * part of minuend to region and skip to next subtrahend. */
1579 MEMCHECK(pReg
, pNextRect
, pReg
->Buffer
);
1580 pNextRect
->left
= left
;
1581 pNextRect
->top
= top
;
1582 pNextRect
->right
= r2
->left
;
1583 pNextRect
->bottom
= bottom
;
1584 pReg
->rdh
.nCount
+= 1;
1587 if (left
>= r1
->right
)
1589 /* Minuend used up: advance to new... */
1596 /* Subtrahend used up */
1602 /* Minuend used up: add any remaining piece before advancing. */
1603 if (r1
->right
> left
)
1605 MEMCHECK(pReg
, pNextRect
, pReg
->Buffer
);
1606 pNextRect
->left
= left
;
1607 pNextRect
->top
= top
;
1608 pNextRect
->right
= r1
->right
;
1609 pNextRect
->bottom
= bottom
;
1610 pReg
->rdh
.nCount
+= 1;
1620 /* Add remaining minuend rectangles to region. */
1623 MEMCHECK(pReg
, pNextRect
, pReg
->Buffer
);
1624 pNextRect
->left
= left
;
1625 pNextRect
->top
= top
;
1626 pNextRect
->right
= r1
->right
;
1627 pNextRect
->bottom
= bottom
;
1628 pReg
->rdh
.nCount
+= 1;
1641 * Subtract regS from regM and leave the result in regD.
1642 * S stands for subtrahend, M for minuend and D for difference.
1647 * \note Side Effects:
1648 * regD is overwritten.
1654 REGION_SubtractRegion(
1659 /* Check for trivial reject */
1660 if ((regM
->rdh
.nCount
== 0) ||
1661 (regS
->rdh
.nCount
== 0) ||
1662 (EXTENTCHECK(®M
->rdh
.rcBound
, ®S
->rdh
.rcBound
) == 0))
1664 REGION_CopyRegion(regD
, regM
);
1668 REGION_RegionOp(regD
,
1672 REGION_SubtractNonO1
,
1675 /* Can't alter newReg's extents before we call miRegionOp because
1676 * it might be one of the source regions and miRegionOp depends
1677 * on the extents of those regions being the unaltered. Besides, this
1678 * way there's no checking against rectangles that will be nuked
1679 * due to coalescing, so we have to examine fewer rectangles. */
1680 REGION_SetExtents(regD
);
1683 /***********************************************************************
1697 // FIXME: Don't use a handle
1698 tra
= REGION_AllocRgnWithHandle(sra
->rdh
.nCount
+ 1);
1703 htra
= tra
->BaseObject
.hHmgr
;
1705 // FIXME: Don't use a handle
1706 trb
= REGION_AllocRgnWithHandle(srb
->rdh
.nCount
+ 1);
1709 RGNOBJAPI_Unlock(tra
);
1710 GreDeleteObject(htra
);
1713 htrb
= trb
->BaseObject
.hHmgr
;
1715 REGION_SubtractRegion(tra
, sra
, srb
);
1716 REGION_SubtractRegion(trb
, srb
, sra
);
1717 REGION_UnionRegion(dr
, tra
, trb
);
1718 RGNOBJAPI_Unlock(tra
);
1719 RGNOBJAPI_Unlock(trb
);
1721 GreDeleteObject(htra
);
1722 GreDeleteObject(htrb
);
1728 * Adds a rectangle to a REGION
1732 REGION_UnionRectWithRgn(
1738 region
.Buffer
= ®ion
.rdh
.rcBound
;
1739 region
.rdh
.nCount
= 1;
1740 region
.rdh
.nRgnSize
= sizeof(RECT
);
1741 region
.rdh
.rcBound
= *rect
;
1742 REGION_UnionRegion(rgn
, rgn
, ®ion
);
1747 REGION_SubtractRectFromRgn(
1754 rgnLocal
.Buffer
= &rgnLocal
.rdh
.rcBound
;
1755 rgnLocal
.rdh
.nCount
= 1;
1756 rgnLocal
.rdh
.nRgnSize
= sizeof(RECT
);
1757 rgnLocal
.rdh
.rcBound
= *prcl
;
1758 REGION_SubtractRegion(prgnDest
, prgnSrc
, &rgnLocal
);
1759 return REGION_Complexity(prgnDest
);
1764 REGION_CreateSimpleFrameRgn(
1772 if ((x
!= 0) || (y
!= 0))
1776 if ((rgn
->rdh
.rcBound
.bottom
- rgn
->rdh
.rcBound
.top
> y
* 2) &&
1777 (rgn
->rdh
.rcBound
.right
- rgn
->rdh
.rcBound
.left
> x
* 2))
1782 prc
->left
= rgn
->rdh
.rcBound
.left
;
1783 prc
->top
= rgn
->rdh
.rcBound
.top
;
1784 prc
->right
= rgn
->rdh
.rcBound
.right
;
1785 prc
->bottom
= prc
->top
+ y
;
1791 /* Left rectangle */
1792 prc
->left
= rgn
->rdh
.rcBound
.left
;
1793 prc
->top
= rgn
->rdh
.rcBound
.top
+ y
;
1794 prc
->right
= prc
->left
+ x
;
1795 prc
->bottom
= rgn
->rdh
.rcBound
.bottom
- y
;
1798 /* Right rectangle */
1799 prc
->left
= rgn
->rdh
.rcBound
.right
- x
;
1800 prc
->top
= rgn
->rdh
.rcBound
.top
+ y
;
1801 prc
->right
= rgn
->rdh
.rcBound
.right
;
1802 prc
->bottom
= rgn
->rdh
.rcBound
.bottom
- y
;
1808 /* Bottom rectangle */
1809 prc
->left
= rgn
->rdh
.rcBound
.left
;
1810 prc
->top
= rgn
->rdh
.rcBound
.bottom
- y
;
1811 prc
->right
= rgn
->rdh
.rcBound
.right
;
1812 prc
->bottom
= rgn
->rdh
.rcBound
.bottom
;
1819 /* The frame results in a complex region. rcBounds remains
1820 the same, though. */
1821 rgn
->rdh
.nCount
= (DWORD
)(prc
- rc
);
1822 ASSERT(rgn
->rdh
.nCount
> 1);
1823 rgn
->rdh
.nRgnSize
= rgn
->rdh
.nCount
* sizeof(RECT
);
1824 rgn
->Buffer
= ExAllocatePoolWithTag(PagedPool
,
1827 if (rgn
->Buffer
== NULL
)
1829 rgn
->rdh
.nRgnSize
= 0;
1833 _PRAGMA_WARNING_SUPPRESS(__WARNING_MAYBE_UNINIT_VAR
) // rc is initialized
1834 COPY_RECTS(rgn
->Buffer
, rc
, rgn
->rdh
.nCount
);
1843 REGION_bMakeFrameRegion(
1844 _Inout_ PREGION prgnDest
,
1845 _In_ PREGION prgnSrc
,
1850 if (!REGION_NOT_EMPTY(prgnSrc
))
1855 if (!REGION_CopyRegion(prgnDest
, prgnSrc
))
1860 if (REGION_Complexity(prgnSrc
) == SIMPLEREGION
)
1862 if (!REGION_CreateSimpleFrameRgn(prgnDest
, cx
, cy
))
1869 /* Move the source region to the bottom-right */
1870 REGION_bOffsetRgn(prgnSrc
, cx
, cy
);
1872 /* Intersect with the source region (this crops the top-left frame) */
1873 REGION_IntersectRegion(prgnDest
, prgnDest
, prgnSrc
);
1875 /* Move the source region to the bottom-left */
1876 REGION_bOffsetRgn(prgnSrc
, -2 * cx
, 0);
1878 /* Intersect with the source region (this crops the top-right frame) */
1879 REGION_IntersectRegion(prgnDest
, prgnDest
, prgnSrc
);
1881 /* Move the source region to the top-left */
1882 REGION_bOffsetRgn(prgnSrc
, 0, -2 * cy
);
1884 /* Intersect with the source region (this crops the bottom-right frame) */
1885 REGION_IntersectRegion(prgnDest
, prgnDest
, prgnSrc
);
1887 /* Move the source region to the top-right */
1888 REGION_bOffsetRgn(prgnSrc
, 2 * cx
, 0);
1890 /* Intersect with the source region (this crops the bottom-left frame) */
1891 REGION_IntersectRegion(prgnDest
, prgnDest
, prgnSrc
);
1893 /* Move the source region back to the original position */
1894 REGION_bOffsetRgn(prgnSrc
, -cx
, cy
);
1896 /* Finally subtract the cropped region from the source */
1897 REGION_SubtractRegion(prgnDest
, prgnSrc
, prgnDest
);
1910 PREGION prgnFrame
, prgnSrc
;
1913 /* Allocate a new region */
1914 prgnFrame
= REGION_AllocUserRgnWithHandle(1);
1915 if (prgnFrame
== NULL
)
1917 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1921 /* Lock the source region */
1922 prgnSrc
= RGNOBJAPI_Lock(hrgn
, NULL
);
1923 if (prgnSrc
== NULL
)
1925 REGION_Delete(prgnFrame
);
1929 if (REGION_bMakeFrameRegion(prgnFrame
, prgnSrc
, cx
, cy
))
1931 hrgnFrame
= prgnFrame
->BaseObject
.hHmgr
;
1932 RGNOBJAPI_Unlock(prgnFrame
);
1936 REGION_Delete(prgnFrame
);
1940 RGNOBJAPI_Unlock(prgnSrc
);
1947 _Inout_ PREGION prgn
,
1957 /* Check if this is a scaling only matrix (off-diagonal elements are 0 */
1958 if (pmx
->flAccel
& XFORM_SCALE
)
1960 /* Check if this is a translation only matrix */
1961 if (pmx
->flAccel
& XFORM_UNITY
)
1963 /* Just offset the region */
1964 return REGION_bOffsetRgn(prgn
, (pmx
->fxDx
+ 8) / 16, (pmx
->fxDy
+ 8) / 16);
1968 /* Initialize the xform object */
1969 XFORMOBJ_vInit(&xo
, pmx
);
1971 /* Scaling can move the rects out of the coordinate space, so
1972 * we first need to check whether we can apply the transformation
1973 * on the bounds rect without modifying the region */
1974 if (!XFORMOBJ_bApplyXform(&xo
, XF_LTOL
, 2, &prgn
->rdh
.rcBound
, &rect
))
1979 /* Apply the xform to the rects in the region */
1980 if (!XFORMOBJ_bApplyXform(&xo
,
1982 prgn
->rdh
.nCount
* 2,
1986 /* This can not happen, since we already checked the bounds! */
1991 RECTL_vSetEmptyRect(&prgn
->rdh
.rcBound
);
1993 /* Loop all rects in the region */
1994 for (i
= 0; i
< prgn
->rdh
.nCount
; i
++)
1996 /* Make sure the rect is well-ordered after the xform */
1997 RECTL_vMakeWellOrdered(&prgn
->Buffer
[i
]);
2000 RECTL_bUnionRect(&prgn
->rdh
.rcBound
,
2005 /* Loop all rects in the region */
2006 for (i
= 0; i
< prgn
->rdh
.nCount
- 1; i
++)
2008 for (j
= i
; i
< prgn
->rdh
.nCount
; i
++)
2010 NT_ASSERT(prgn
->Buffer
[i
].top
< prgn
->Buffer
[i
].bottom
);
2011 NT_ASSERT(prgn
->Buffer
[j
].top
>= prgn
->Buffer
[i
].top
);
2020 /* Allocate a buffer for the polygons */
2021 cjSize
= prgn
->rdh
.nCount
* (4 * sizeof(POINT
) + sizeof(ULONG
));
2022 ppt
= ExAllocatePoolWithTag(PagedPool
, cjSize
, GDITAG_REGION
);
2028 /* Fill the buffer with the rects */
2029 pcPoints
= (PULONG
)&ppt
[4 * prgn
->rdh
.nCount
];
2030 for (i
= 0; i
< prgn
->rdh
.nCount
; i
++)
2032 /* Make sure the rect is within the legal range */
2034 ppt
[4 * i
+ 0].x
= prgn
->Buffer
[i
].left
;
2035 ppt
[4 * i
+ 0].y
= prgn
->Buffer
[i
].top
;
2036 ppt
[4 * i
+ 1].x
= prgn
->Buffer
[i
].right
;
2037 ppt
[4 * i
+ 1].y
= prgn
->Buffer
[i
].top
;
2038 ppt
[4 * i
+ 2].x
= prgn
->Buffer
[i
].right
;
2039 ppt
[4 * i
+ 2].y
= prgn
->Buffer
[i
].bottom
;
2040 ppt
[4 * i
+ 3].x
= prgn
->Buffer
[i
].left
;
2041 ppt
[4 * i
+ 3].y
= prgn
->Buffer
[i
].bottom
;
2044 /* Initialize the xform object */
2045 XFORMOBJ_vInit(&xo
, pmx
);
2047 /* Apply the xform to the rects in the buffer */
2048 if (!XFORMOBJ_bApplyXform(&xo
,
2050 prgn
->rdh
.nCount
* 2,
2054 /* This means, there were coordinates that would go outside of
2055 the coordinate space after the transformation */
2056 ExFreePoolWithTag(ppt
, GDITAG_REGION
);
2060 /* Now use the polygons to create a polygon region */
2061 bResult
= REGION_SetPolyPolygonRgn(prgn
,
2067 /* Free the polygon buffer */
2068 ExFreePoolWithTag(ppt
, GDITAG_REGION
);
2078 REGION_AllocRgnWithHandle(
2084 pReg
= (PREGION
)GDIOBJ_AllocateObject(GDIObjType_RGN_TYPE
,
2086 BASEFLAG_LOOKASIDE
);
2089 DPRINT1("Could not allocate a palette.\n");
2093 if (!GDIOBJ_hInsertObject(&pReg
->BaseObject
, GDI_OBJ_HMGR_POWNED
))
2095 DPRINT1("Could not insert palette into handle table.\n");
2096 GDIOBJ_vFreeObject(&pReg
->BaseObject
);
2100 //hReg = pReg->BaseObject.hHmgr;
2102 if ((nReg
== 0) || (nReg
== 1))
2104 /* Testing shows that > 95% of all regions have only 1 rect.
2105 Including that here saves us from having to do another allocation */
2106 pReg
->Buffer
= &pReg
->rdh
.rcBound
;
2110 pReg
->Buffer
= ExAllocatePoolWithTag(PagedPool
,
2111 nReg
* sizeof(RECT
),
2113 if (pReg
->Buffer
== NULL
)
2115 DPRINT1("Could not allocate region buffer\n");
2116 GDIOBJ_vDeleteObject(&pReg
->BaseObject
);
2122 pReg
->rdh
.dwSize
= sizeof(RGNDATAHEADER
);
2123 pReg
->rdh
.nCount
= nReg
;
2124 pReg
->rdh
.nRgnSize
= nReg
* sizeof(RECT
);
2125 pReg
->prgnattr
= &pReg
->rgnattr
;
2132 REGION_bAllocRgnAttr(
2138 ppi
= PsGetCurrentProcessWin32Process();
2141 prgnattr
= GdiPoolAllocate(ppi
->pPoolRgnAttr
);
2142 if (prgnattr
== NULL
)
2144 DPRINT1("Could not allocate RGN attr\n");
2148 /* Set the object attribute in the handle table */
2149 prgn
->prgnattr
= prgnattr
;
2150 GDIOBJ_vSetObjectAttr(&prgn
->BaseObject
, prgnattr
);
2157 // Allocate User Space Region Handle.
2161 REGION_AllocUserRgnWithHandle(
2166 prgn
= REGION_AllocRgnWithHandle(nRgn
);
2172 if (!REGION_bAllocRgnAttr(prgn
))
2185 PRGN_ATTR pRgn_Attr
= NULL
;
2187 if (pRgn
&& pRgn
->prgnattr
!= &pRgn
->rgnattr
)
2189 pRgn_Attr
= GDIOBJ_pvGetObjectAttr(&pRgn
->BaseObject
);
2195 if ( !(pRgn_Attr
->AttrFlags
& ATTR_CACHED
) )
2197 if ( pRgn_Attr
->AttrFlags
& (ATTR_RGN_VALID
|ATTR_RGN_DIRTY
) )
2199 switch (pRgn_Attr
->iComplexity
)
2202 EMPTY_REGION( pRgn
);
2206 REGION_SetRectRgn( pRgn
,
2207 pRgn_Attr
->Rect
.left
,
2208 pRgn_Attr
->Rect
.top
,
2209 pRgn_Attr
->Rect
.right
,
2210 pRgn_Attr
->Rect
.bottom
);
2213 pRgn_Attr
->AttrFlags
&= ~ATTR_RGN_DIRTY
;
2217 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2231 PRGN_ATTR
*ppRgn_Attr
)
2235 pRgn
= REGION_LockRgn(hRgn
);
2239 REGION_vSyncRegion(pRgn
);
2242 *ppRgn_Attr
= pRgn
->prgnattr
;
2252 PRGN_ATTR pRgn_Attr
;
2254 if (pRgn
&& GreGetObjectOwner(pRgn
->BaseObject
.hHmgr
) == GDI_OBJ_HMGR_POWNED
)
2256 pRgn_Attr
= GDIOBJ_pvGetObjectAttr(&pRgn
->BaseObject
);
2262 if ( pRgn_Attr
->AttrFlags
& ATTR_RGN_VALID
)
2264 pRgn_Attr
->iComplexity
= REGION_Complexity( pRgn
);
2265 pRgn_Attr
->Rect
.left
= pRgn
->rdh
.rcBound
.left
;
2266 pRgn_Attr
->Rect
.top
= pRgn
->rdh
.rcBound
.top
;
2267 pRgn_Attr
->Rect
.right
= pRgn
->rdh
.rcBound
.right
;
2268 pRgn_Attr
->Rect
.bottom
= pRgn
->rdh
.rcBound
.bottom
;
2271 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2278 REGION_UnlockRgn(pRgn
);
2283 These regions do not use attribute sections and when allocated, use gdiobj
2287 // System Region Functions
2291 IntSysCreateRectpRgn(
2299 /* Allocate a region, witout a handle */
2300 prgn
= (PREGION
)GDIOBJ_AllocateObject(GDIObjType_RGN_TYPE
, sizeof(REGION
), BASEFLAG_LOOKASIDE
);
2307 prgn
->Buffer
= &prgn
->rdh
.rcBound
;
2308 prgn
->prgnattr
= &prgn
->rgnattr
;
2309 REGION_SetRectRgn(prgn
, LeftRect
, TopRect
, RightRect
, BottomRect
);
2316 REGION_vCleanup(PVOID ObjectBody
)
2318 PREGION pRgn
= (PREGION
)ObjectBody
;
2319 PPROCESSINFO ppi
= PsGetCurrentProcessWin32Process();
2322 ASSERT(pRgn
->prgnattr
);
2323 if (pRgn
->prgnattr
!= &pRgn
->rgnattr
)
2324 GdiPoolFree(ppi
->pPoolRgnAttr
, pRgn
->prgnattr
);
2326 if (pRgn
->Buffer
&& pRgn
->Buffer
!= &pRgn
->rdh
.rcBound
)
2327 ExFreePoolWithTag(pRgn
->Buffer
, TAG_REGION
);
2332 REGION_Delete(PREGION pRgn
)
2334 if (pRgn
== prgnDefault
)
2337 GDIOBJ_vDeleteObject(&pRgn
->BaseObject
);
2342 IntGdiSetRegionOwner(HRGN hRgn
, DWORD OwnerMask
)
2348 prgn
= RGNOBJAPI_Lock(hRgn
, &prgnattr
);
2354 if (prgnattr
!= &prgn
->rgnattr
)
2356 GDIOBJ_vSetObjectAttr(&prgn
->BaseObject
, NULL
);
2357 prgn
->prgnattr
= &prgn
->rgnattr
;
2358 ppi
= PsGetCurrentProcessWin32Process();
2359 GdiPoolFree(ppi
->pPoolRgnAttr
, prgnattr
);
2362 RGNOBJAPI_Unlock(prgn
);
2364 return GreSetObjectOwner(hRgn
, OwnerMask
);
2376 if (prgnDest
== NULL
)
2378 DPRINT("IntGdiCombineRgn: hDest unavailable\n");
2382 if (prgnSrc1
== NULL
)
2384 DPRINT("IntGdiCombineRgn: hSrc1 unavailable\n");
2388 if (iCombineMode
== RGN_COPY
)
2390 if (!REGION_CopyRegion(prgnDest
, prgnSrc1
))
2393 return REGION_Complexity(prgnDest
);
2396 if (prgnSrc2
== NULL
)
2398 DPRINT1("IntGdiCombineRgn requires hSrc2 != NULL for combine mode %d!\n", iCombineMode
);
2403 switch (iCombineMode
)
2406 REGION_IntersectRegion(prgnDest
, prgnSrc1
, prgnSrc2
);
2409 REGION_UnionRegion(prgnDest
, prgnSrc1
, prgnSrc2
);
2412 REGION_XorRegion(prgnDest
, prgnSrc1
, prgnSrc2
);
2415 REGION_SubtractRegion(prgnDest
, prgnSrc1
, prgnSrc2
);
2419 return REGION_Complexity(prgnDest
);
2432 *pRect
= Rgn
->rdh
.rcBound
;
2433 ret
= REGION_Complexity(Rgn
);
2437 return 0; // If invalid region return zero
2449 Rgn
= RGNOBJAPI_Lock(hRgn
, NULL
);
2455 ret
= REGION_GetRgnBox(Rgn
, pRect
);
2456 RGNOBJAPI_Unlock(Rgn
);
2472 if (prgn
->rdh
.nCount
> 0 && INRECT(prgn
->rdh
.rcBound
, X
, Y
))
2475 for (i
= 0; i
< prgn
->rdh
.nCount
; i
++)
2477 if (INRECT(r
[i
], X
, Y
))
2487 REGION_RectInRegion(
2491 PRECTL pCurRect
, pRectEnd
;
2494 /* Swap the coordinates to make right >= left and bottom >= top */
2495 /* (region building rectangles are normalized the same way) */
2496 if (rect
->top
> rect
->bottom
)
2498 rc
.top
= rect
->bottom
;
2499 rc
.bottom
= rect
->top
;
2504 rc
.bottom
= rect
->bottom
;
2507 if (rect
->right
< rect
->left
)
2509 rc
.right
= rect
->left
;
2510 rc
.left
= rect
->right
;
2514 rc
.right
= rect
->right
;
2515 rc
.left
= rect
->left
;
2518 /* This is (just) a useful optimization */
2519 if ((Rgn
->rdh
.nCount
> 0) && EXTENTCHECK(&Rgn
->rdh
.rcBound
, &rc
))
2521 for (pCurRect
= Rgn
->Buffer
, pRectEnd
= pCurRect
+
2522 Rgn
->rdh
.nCount
; pCurRect
< pRectEnd
; pCurRect
++)
2524 if (pCurRect
->bottom
<= rc
.top
)
2525 continue; /* Not far enough down yet */
2527 if (pCurRect
->top
>= rc
.bottom
)
2528 break; /* Too far down */
2530 if (pCurRect
->right
<= rc
.left
)
2531 continue; /* Not far enough over yet */
2533 if (pCurRect
->left
>= rc
.right
)
2556 if (LeftRect
> RightRect
)
2559 LeftRect
= RightRect
;
2563 if (TopRect
> BottomRect
)
2566 TopRect
= BottomRect
;
2570 if ((LeftRect
!= RightRect
) && (TopRect
!= BottomRect
))
2572 firstRect
= rgn
->Buffer
;
2574 firstRect
->left
= rgn
->rdh
.rcBound
.left
= LeftRect
;
2575 firstRect
->top
= rgn
->rdh
.rcBound
.top
= TopRect
;
2576 firstRect
->right
= rgn
->rdh
.rcBound
.right
= RightRect
;
2577 firstRect
->bottom
= rgn
->rdh
.rcBound
.bottom
= BottomRect
;
2578 rgn
->rdh
.nCount
= 1;
2579 rgn
->rdh
.iType
= RDH_RECTANGLES
;
2590 _Inout_ PREGION prgn
,
2597 NT_ASSERT(prgn
!= NULL
);
2599 /* Check for trivial case */
2600 if ((cx
== 0) && (cy
== 0))
2605 /* Check for empty regions, we ignore the offset values here */
2606 if (prgn
->rdh
.nCount
== 0)
2611 /* Make sure the offset is within the legal range */
2612 if ((cx
> MAX_COORD
) || (cx
< MIN_COORD
) ||
2613 (cy
> MAX_COORD
) || (cy
< MIN_COORD
))
2618 /* Are we moving right? */
2621 /* Check if we stay inside the bounds on the right side */
2622 if (prgn
->rdh
.rcBound
.right
> (MAX_COORD
- cx
))
2629 /* Check if we stay inside the bounds on the left side */
2630 if (prgn
->rdh
.rcBound
.left
< (MIN_COORD
- cx
))
2636 /* Are we moving down? */
2639 /* Check if we stay inside the bounds on the right side */
2640 if (prgn
->rdh
.rcBound
.bottom
> (MAX_COORD
- cy
))
2647 /* Check if we stay inside the bounds on the left side */
2648 if (prgn
->rdh
.rcBound
.top
< (MIN_COORD
- cy
))
2654 /* Loop to move the rects */
2655 prcl
= prgn
->Buffer
;
2656 for (i
= 0; i
< prgn
->rdh
.nCount
; i
++)
2659 prcl
[i
].right
+= cx
;
2661 prcl
[i
].bottom
+= cy
;
2664 /* Finally update the bounds rect */
2665 if (prgn
->Buffer
!= &prgn
->rdh
.rcBound
)
2667 prgn
->rdh
.rcBound
.left
+= cx
;
2668 prgn
->rdh
.rcBound
.right
+= cx
;
2669 prgn
->rdh
.rcBound
.top
+= cy
;
2670 prgn
->rdh
.rcBound
.bottom
+= cy
;
2676 /***********************************************************************
2677 * REGION_InsertEdgeInET
2679 * Insert the given edge into the edge table.
2680 * First we must find the correct bucket in the
2681 * Edge table, then find the right slot in the
2682 * bucket. Finally, we can insert it.
2688 REGION_InsertEdgeInET(
2690 EDGE_TABLE_ENTRY
*ETE
,
2692 SCANLINE_LISTBLOCK
**SLLBlock
,
2695 EDGE_TABLE_ENTRY
*start
, *prev
;
2696 SCANLINE_LIST
*pSLL
, *pPrevSLL
;
2697 SCANLINE_LISTBLOCK
*tmpSLLBlock
;
2699 /* Find the right bucket to put the edge into */
2700 pPrevSLL
= &ET
->scanlines
;
2701 pSLL
= pPrevSLL
->next
;
2702 while (pSLL
&& (pSLL
->scanline
< scanline
))
2708 /* Reassign pSLL (pointer to SCANLINE_LIST) if necessary */
2709 if ((!pSLL
) || (pSLL
->scanline
> scanline
))
2711 if (*iSLLBlock
> SLLSPERBLOCK
-1)
2713 tmpSLLBlock
= ExAllocatePoolWithTag(PagedPool
,
2714 sizeof(SCANLINE_LISTBLOCK
),
2716 if (tmpSLLBlock
== NULL
)
2718 DPRINT1("REGION_InsertEdgeInETL(): Can't alloc SLLB\n");
2719 /* FIXME: Free resources? */
2723 (*SLLBlock
)->next
= tmpSLLBlock
;
2724 tmpSLLBlock
->next
= (SCANLINE_LISTBLOCK
*)NULL
;
2725 *SLLBlock
= tmpSLLBlock
;
2729 pSLL
= &((*SLLBlock
)->SLLs
[(*iSLLBlock
)++]);
2731 pSLL
->next
= pPrevSLL
->next
;
2732 pSLL
->edgelist
= (EDGE_TABLE_ENTRY
*)NULL
;
2733 pPrevSLL
->next
= pSLL
;
2736 pSLL
->scanline
= scanline
;
2738 /* Now insert the edge in the right bucket */
2739 prev
= (EDGE_TABLE_ENTRY
*)NULL
;
2740 start
= pSLL
->edgelist
;
2741 while (start
&& (start
->bres
.minor_axis
< ETE
->bres
.minor_axis
))
2744 start
= start
->next
;
2752 pSLL
->edgelist
= ETE
;
2755 /***********************************************************************
2758 * This routine moves EDGE_TABLEEntries from the
2759 * EDGE_TABLE into the Active Edge Table,
2760 * leaving them sorted by smaller x coordinate.
2767 EDGE_TABLE_ENTRY
*AET
,
2768 EDGE_TABLE_ENTRY
*ETEs
)
2770 EDGE_TABLE_ENTRY
*pPrevAET
;
2771 EDGE_TABLE_ENTRY
*tmp
;
2777 while (AET
&& (AET
->bres
.minor_axis
< ETEs
->bres
.minor_axis
))
2788 ETEs
->back
= pPrevAET
;
2789 pPrevAET
->next
= ETEs
;
2796 /***********************************************************************
2797 * REGION_computeWAET
2799 * This routine links the AET by the
2800 * nextWETE (winding EDGE_TABLE_ENTRY) link for
2801 * use by the winding number rule. The final
2802 * Active Edge Table (AET) might look something
2806 * ---------- --------- ---------
2807 * |ymax | |ymax | |ymax |
2808 * | ... | |... | |... |
2809 * |next |->|next |->|next |->...
2810 * |nextWETE| |nextWETE| |nextWETE|
2811 * --------- --------- ^--------
2813 * V-------------------> V---> ...
2820 EDGE_TABLE_ENTRY
*AET
)
2822 register EDGE_TABLE_ENTRY
*pWETE
;
2823 register INT inside
= 1;
2824 register INT isInside
= 0;
2826 AET
->nextWETE
= (EDGE_TABLE_ENTRY
*)NULL
;
2836 if ((!inside
&& !isInside
) ||
2837 ( inside
&& isInside
))
2839 pWETE
->nextWETE
= AET
;
2846 pWETE
->nextWETE
= (EDGE_TABLE_ENTRY
*)NULL
;
2849 /***********************************************************************
2850 * REGION_InsertionSort
2852 * Just a simple insertion sort using
2853 * pointers and back pointers to sort the Active
2860 REGION_InsertionSort(
2861 EDGE_TABLE_ENTRY
*AET
)
2863 EDGE_TABLE_ENTRY
*pETEchase
;
2864 EDGE_TABLE_ENTRY
*pETEinsert
;
2865 EDGE_TABLE_ENTRY
*pETEchaseBackTMP
;
2866 BOOL changed
= FALSE
;
2873 while (pETEchase
->back
->bres
.minor_axis
> AET
->bres
.minor_axis
)
2874 pETEchase
= pETEchase
->back
;
2877 if (pETEchase
!= pETEinsert
)
2879 pETEchaseBackTMP
= pETEchase
->back
;
2880 pETEinsert
->back
->next
= AET
;
2882 AET
->back
= pETEinsert
->back
;
2884 pETEinsert
->next
= pETEchase
;
2885 pETEchase
->back
->next
= pETEinsert
;
2886 pETEchase
->back
= pETEinsert
;
2887 pETEinsert
->back
= pETEchaseBackTMP
;
2895 /***********************************************************************
2896 * REGION_FreeStorage
2904 SCANLINE_LISTBLOCK
*pSLLBlock
)
2906 SCANLINE_LISTBLOCK
*tmpSLLBlock
;
2910 tmpSLLBlock
= pSLLBlock
->next
;
2911 ExFreePoolWithTag(pSLLBlock
, TAG_REGION
);
2912 pSLLBlock
= tmpSLLBlock
;
2917 /***********************************************************************
2918 * REGION_PtsToRegion
2920 * Create an array of rectangles from a list of points.
2926 INT numFullPtBlocks
,
2928 POINTBLOCK
*FirstPtBlock
,
2933 POINTBLOCK
*CurPtBlock
;
2935 RECTL
*extents
, *temp
;
2938 extents
= ®
->rdh
.rcBound
;
2940 numRects
= ((numFullPtBlocks
* NUMPTSTOBUFFER
) + iCurPtBlock
) >> 1;
2942 /* Make sure, we have at least one rect */
2948 temp
= ExAllocatePoolWithTag(PagedPool
, numRects
* sizeof(RECT
), TAG_REGION
);
2954 if (reg
->Buffer
!= NULL
)
2956 COPY_RECTS(temp
, reg
->Buffer
, reg
->rdh
.nCount
);
2957 if (reg
->Buffer
!= ®
->rdh
.rcBound
)
2958 ExFreePoolWithTag(reg
->Buffer
, TAG_REGION
);
2962 reg
->rdh
.nCount
= numRects
;
2963 CurPtBlock
= FirstPtBlock
;
2964 rects
= reg
->Buffer
- 1;
2966 extents
->left
= LARGE_COORDINATE
, extents
->right
= SMALL_COORDINATE
;
2968 for ( ; numFullPtBlocks
>= 0; numFullPtBlocks
--)
2970 /* The loop uses 2 points per iteration */
2971 i
= NUMPTSTOBUFFER
>> 1;
2972 if (numFullPtBlocks
== 0)
2973 i
= iCurPtBlock
>> 1;
2975 for (pts
= CurPtBlock
->pts
; i
--; pts
+= 2)
2977 if (pts
->x
== pts
[1].x
)
2980 if ((numRects
&& pts
->x
== rects
->left
) &&
2981 (pts
->y
== rects
->bottom
) &&
2982 (pts
[1].x
== rects
->right
) &&
2983 ((numRects
== 1) || (rects
[-1].top
!= rects
->top
)) &&
2984 (i
&& pts
[2].y
> pts
[1].y
))
2986 rects
->bottom
= pts
[1].y
+ 1;
2992 rects
->left
= pts
->x
;
2993 rects
->top
= pts
->y
;
2994 rects
->right
= pts
[1].x
;
2995 rects
->bottom
= pts
[1].y
+ 1;
2997 if (rects
->left
< extents
->left
)
2998 extents
->left
= rects
->left
;
2999 if (rects
->right
> extents
->right
)
3000 extents
->right
= rects
->right
;
3003 CurPtBlock
= CurPtBlock
->next
;
3008 extents
->top
= reg
->Buffer
->top
;
3009 extents
->bottom
= rects
->bottom
;
3016 extents
->bottom
= 0;
3019 reg
->rdh
.nCount
= numRects
;
3024 /***********************************************************************
3025 * REGION_CreateETandAET
3027 * This routine creates the edge table for
3028 * scan converting polygons.
3029 * The Edge Table (ET) looks like:
3033 * | ymax | SCANLINE_LISTs
3034 * |scanline|-->------------>-------------->...
3035 * -------- |scanline| |scanline|
3036 * |edgelist| |edgelist|
3037 * --------- ---------
3041 * list of ETEs list of ETEs
3043 * where ETE is an EDGE_TABLE_ENTRY data structure,
3044 * and there is one SCANLINE_LIST per scanline at
3045 * which an edge is initially entered.
3051 REGION_CreateETandAET(
3056 EDGE_TABLE_ENTRY
*AET
,
3057 EDGE_TABLE_ENTRY
*pETEs
,
3058 SCANLINE_LISTBLOCK
*pSLLBlock
)
3060 const POINT
*top
, *bottom
;
3061 const POINT
*PrevPt
, *CurrPt
, *EndPt
;
3066 /* Initialize the Active Edge Table */
3067 AET
->next
= (EDGE_TABLE_ENTRY
*)NULL
;
3068 AET
->back
= (EDGE_TABLE_ENTRY
*)NULL
;
3069 AET
->nextWETE
= (EDGE_TABLE_ENTRY
*)NULL
;
3070 AET
->bres
.minor_axis
= SMALL_COORDINATE
;
3072 /* Initialize the Edge Table. */
3073 ET
->scanlines
.next
= (SCANLINE_LIST
*)NULL
;
3074 ET
->ymax
= SMALL_COORDINATE
;
3075 ET
->ymin
= LARGE_COORDINATE
;
3076 pSLLBlock
->next
= (SCANLINE_LISTBLOCK
*)NULL
;
3079 for (poly
= 0; poly
< nbpolygons
; poly
++)
3081 count
= Count
[poly
];
3088 /* For each vertex in the array of points.
3089 * In this loop we are dealing with two vertices at
3090 * a time -- these make up one edge of the polygon. */
3095 /* Find out which point is above and which is below. */
3096 if (PrevPt
->y
> CurrPt
->y
)
3098 bottom
= PrevPt
, top
= CurrPt
;
3099 pETEs
->ClockWise
= 0;
3103 bottom
= CurrPt
, top
= PrevPt
;
3104 pETEs
->ClockWise
= 1;
3107 /* Don't add horizontal edges to the Edge table. */
3108 if (bottom
->y
!= top
->y
)
3110 /* -1 so we don't get last scanline */
3111 pETEs
->ymax
= bottom
->y
- 1;
3113 /* Initialize integer edge algorithm */
3114 dy
= bottom
->y
- top
->y
;
3115 BRESINITPGONSTRUCT(dy
, top
->x
, bottom
->x
, pETEs
->bres
);
3117 REGION_InsertEdgeInET(ET
,
3123 if (PrevPt
->y
> ET
->ymax
)
3124 ET
->ymax
= PrevPt
->y
;
3125 if (PrevPt
->y
< ET
->ymin
)
3126 ET
->ymin
= PrevPt
->y
;
3137 REGION_SetPolyPolygonRgn(
3138 _Inout_ PREGION prgn
,
3139 _In_
const POINT
*ppt
,
3140 _In_
const ULONG
*pcPoints
,
3141 _In_ ULONG cPolygons
,
3144 EDGE_TABLE_ENTRY
*pAET
; /* Active Edge Table */
3145 INT y
; /* Current scanline */
3146 INT iPts
= 0; /* Number of pts in buffer */
3147 EDGE_TABLE_ENTRY
*pWETE
; /* Winding Edge Table Entry */
3148 SCANLINE_LIST
*pSLL
; /* Current SCANLINE_LIST */
3149 POINT
*pts
; /* Output buffer */
3150 EDGE_TABLE_ENTRY
*pPrevAET
; /* Pointer to previous AET */
3151 EDGE_TABLE ET
; /* Header node for ET */
3152 EDGE_TABLE_ENTRY AET
; /* Header node for AET */
3153 EDGE_TABLE_ENTRY
*pETEs
; /* EDGE_TABLEEntries pool */
3154 SCANLINE_LISTBLOCK SLLBlock
; /* Header for SCANLINE_LIST */
3155 INT fixWAET
= FALSE
;
3156 POINTBLOCK FirstPtBlock
, *curPtBlock
; /* PtBlock buffers */
3157 POINTBLOCK
*tmpPtBlock
;
3158 UINT numFullPtBlocks
= 0;
3161 /* Check if iMode is valid */
3162 if ((iMode
!= ALTERNATE
) && (iMode
!= WINDING
))
3165 /* Special case a rectangle */
3166 if (((cPolygons
== 1) && ((pcPoints
[0] == 4) ||
3167 ((pcPoints
[0] == 5) && (ppt
[4].x
== ppt
[0].x
) && (ppt
[4].y
== ppt
[0].y
)))) &&
3168 (((ppt
[0].y
== ppt
[1].y
) &&
3169 (ppt
[1].x
== ppt
[2].x
) &&
3170 (ppt
[2].y
== ppt
[3].y
) &&
3171 (ppt
[3].x
== ppt
[0].x
)) ||
3172 ((ppt
[0].x
== ppt
[1].x
) &&
3173 (ppt
[1].y
== ppt
[2].y
) &&
3174 (ppt
[2].x
== ppt
[3].x
) &&
3175 (ppt
[3].y
== ppt
[0].y
))))
3177 REGION_SetRectRgn(prgn
,
3178 min(ppt
[0].x
, ppt
[2].x
),
3179 min(ppt
[0].y
, ppt
[2].y
),
3180 max(ppt
[0].x
, ppt
[2].x
),
3181 max(ppt
[0].y
, ppt
[2].y
));
3185 for (poly
= total
= 0; poly
< cPolygons
; poly
++)
3186 total
+= pcPoints
[poly
];
3188 pETEs
= ExAllocatePoolWithTag(PagedPool
,
3189 sizeof(EDGE_TABLE_ENTRY
) * total
,
3196 pts
= FirstPtBlock
.pts
;
3197 REGION_CreateETandAET(pcPoints
, cPolygons
, ppt
, &ET
, &AET
, pETEs
, &SLLBlock
);
3198 pSLL
= ET
.scanlines
.next
;
3199 curPtBlock
= &FirstPtBlock
;
3201 if (iMode
!= WINDING
)
3203 /* For each scanline */
3204 for (y
= ET
.ymin
; y
< ET
.ymax
; y
++)
3206 /* Add a new edge to the active edge table when we
3207 * get to the next edge. */
3208 if (pSLL
!= NULL
&& y
== pSLL
->scanline
)
3210 REGION_loadAET(&AET
, pSLL
->edgelist
);
3216 /* For each active edge */
3219 pts
->x
= pAET
->bres
.minor_axis
, pts
->y
= y
;
3222 /* Send out the buffer */
3223 if (iPts
== NUMPTSTOBUFFER
)
3225 tmpPtBlock
= ExAllocatePoolWithTag(PagedPool
,
3228 if (tmpPtBlock
== NULL
)
3230 DPRINT1("Can't alloc tPB\n");
3231 ExFreePoolWithTag(pETEs
, TAG_REGION
);
3235 curPtBlock
->next
= tmpPtBlock
;
3236 curPtBlock
= tmpPtBlock
;
3237 pts
= curPtBlock
->pts
;
3242 EVALUATEEDGEEVENODD(pAET
, pPrevAET
, y
);
3245 REGION_InsertionSort(&AET
);
3250 /* For each scanline */
3251 for (y
= ET
.ymin
; y
< ET
.ymax
; y
++)
3253 /* Add a new edge to the active edge table when we
3254 * get to the next edge. */
3255 if (pSLL
!= NULL
&& y
== pSLL
->scanline
)
3257 REGION_loadAET(&AET
, pSLL
->edgelist
);
3258 REGION_computeWAET(&AET
);
3266 /* For each active edge */
3269 /* Add to the buffer only those edges that
3270 * are in the Winding active edge table. */
3273 pts
->x
= pAET
->bres
.minor_axis
;
3278 /* Send out the buffer */
3279 if (iPts
== NUMPTSTOBUFFER
)
3281 tmpPtBlock
= ExAllocatePoolWithTag(PagedPool
,
3284 if (tmpPtBlock
== NULL
)
3286 DPRINT1("Can't alloc tPB\n");
3287 ExFreePoolWithTag(pETEs
, TAG_REGION
);
3290 curPtBlock
->next
= tmpPtBlock
;
3291 curPtBlock
= tmpPtBlock
;
3292 pts
= curPtBlock
->pts
;
3297 pWETE
= pWETE
->nextWETE
;
3300 EVALUATEEDGEWINDING(pAET
, pPrevAET
, y
, fixWAET
);
3303 /* Recompute the winding active edge table if
3304 * we just resorted or have exited an edge. */
3305 if (REGION_InsertionSort(&AET
) || fixWAET
)
3307 REGION_computeWAET(&AET
);
3313 REGION_FreeStorage(SLLBlock
.next
);
3314 REGION_PtsToRegion(numFullPtBlocks
, iPts
, &FirstPtBlock
, prgn
);
3316 for (curPtBlock
= FirstPtBlock
.next
; numFullPtBlocks
-- > 0;)
3318 tmpPtBlock
= curPtBlock
->next
;
3319 ExFreePoolWithTag(curPtBlock
, TAG_REGION
);
3320 curPtBlock
= tmpPtBlock
;
3323 ExFreePoolWithTag(pETEs
, TAG_REGION
);
3329 GreCreatePolyPolygonRgn(
3330 _In_
const POINT
*ppt
,
3331 _In_
const ULONG
*pcPoints
,
3332 _In_ ULONG cPolygons
,
3338 /* Allocate a new region */
3339 prgn
= REGION_AllocUserRgnWithHandle(0);
3342 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
3346 /* Call the internal function and check for success */
3347 if (REGION_SetPolyPolygonRgn(prgn
, ppt
, pcPoints
, cPolygons
, iMode
))
3349 /* Success, get the handle and unlock the region */
3350 hrgn
= prgn
->BaseObject
.hHmgr
;
3351 RGNOBJAPI_Unlock(prgn
);
3355 /* Failure, delete the region */
3356 REGION_Delete(prgn
);
3372 Rgn
= RGNOBJAPI_Lock(hRgn
, NULL
);
3378 Ret
= REGION_RectInRegion(Rgn
, rc
);
3379 RGNOBJAPI_Unlock(Rgn
);
3385 // NtGdi Exported Functions
3399 /* Validate the combine mode */
3400 if ((iMode
< RGN_AND
) || (iMode
> RGN_COPY
))
3405 /* Validate that we have the required regions */
3406 if ((hrgnDst
== NULL
) ||
3407 (hrgnSrc1
== NULL
) ||
3408 ((iMode
!= RGN_COPY
) && (hrgnSrc2
== NULL
)))
3410 DPRINT1("NtGdiCombineRgn: %p, %p, %p, %d\n",
3411 hrgnDst
, hrgnSrc1
, hrgnSrc2
, iMode
);
3415 /* Lock all regions */
3417 ahrgn
[1] = hrgnSrc1
;
3418 ahrgn
[2] = iMode
!= RGN_COPY
? hrgnSrc2
: NULL
;
3419 if (!GDIOBJ_bLockMultipleObjects(3, (HGDIOBJ
*)ahrgn
, (PVOID
*)aprgn
, GDIObjType_RGN_TYPE
))
3421 DPRINT1("NtGdiCombineRgn: %p, %p, %p, %d\n",
3422 hrgnDst
, hrgnSrc1
, hrgnSrc2
, iMode
);
3426 /* HACK: Sync usermode attributes */
3427 REGION_vSyncRegion(aprgn
[0]);
3428 REGION_vSyncRegion(aprgn
[1]);
3429 if (aprgn
[2]) REGION_vSyncRegion(aprgn
[2]);
3431 /* Call the internal function */
3432 iResult
= IntGdiCombineRgn(aprgn
[0], aprgn
[1], aprgn
[2], iMode
);
3434 /// FIXME: need to sync user attr back
3436 /* Cleanup and return */
3437 REGION_UnlockRgn(aprgn
[0]);
3438 REGION_UnlockRgn(aprgn
[1]);
3440 REGION_UnlockRgn(aprgn
[2]);
3447 NtGdiCreateEllipticRgn(
3453 return NtGdiCreateRoundRectRgn(Left
,
3471 /* Allocate region data structure with space for 1 RECTL */
3472 pRgn
= REGION_AllocUserRgnWithHandle(1);
3475 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
3479 hRgn
= pRgn
->BaseObject
.hHmgr
;
3481 REGION_SetRectRgn(pRgn
, LeftRect
, TopRect
, RightRect
, BottomRect
);
3482 RGNOBJAPI_Unlock(pRgn
);
3484 DPRINT("Returning %p.\n", hRgn
);
3491 NtGdiCreateRoundRectRgn(
3501 INT asq
, bsq
, d
, xd
, yd
;
3504 /* Make the dimensions sensible */
3519 ellipse_width
= abs(ellipse_width
);
3520 ellipse_height
= abs(ellipse_height
);
3522 /* Check parameters */
3523 if (ellipse_width
> right
-left
)
3524 ellipse_width
= right
-left
;
3525 if (ellipse_height
> bottom
-top
)
3526 ellipse_height
= bottom
-top
;
3528 /* Check if we can do a normal rectangle instead */
3529 if ((ellipse_width
< 2) || (ellipse_height
< 2))
3530 return NtGdiCreateRectRgn(left
, top
, right
, bottom
);
3533 d
= (ellipse_height
< 128) ? ((3 * ellipse_height
) >> 2) : 64;
3534 obj
= REGION_AllocUserRgnWithHandle(d
);
3538 hrgn
= obj
->BaseObject
.hHmgr
;
3540 /* Ellipse algorithm, based on an article by K. Porter
3541 in DDJ Graphics Programming Column, 8/89 */
3542 asq
= ellipse_width
* ellipse_width
/ 4; /* a^2 */
3543 bsq
= ellipse_height
* ellipse_height
/ 4; /* b^2 */
3544 d
= bsq
- asq
* ellipse_height
/ 2 + asq
/ 4; /* b^2 - a^2b + a^2/4 */
3546 yd
= asq
* ellipse_height
; /* 2a^2b */
3548 rect
.left
= left
+ ellipse_width
/ 2;
3549 rect
.right
= right
- ellipse_width
/ 2;
3551 /* Loop to draw first half of quadrant */
3554 /* If nearest pixel is toward the center */
3557 /* Move toward center */
3559 rect
.bottom
= rect
.top
+ 1;
3560 REGION_UnionRectWithRgn(obj
, &rect
);
3561 rect
.top
= --bottom
;
3562 rect
.bottom
= rect
.top
+ 1;
3563 REGION_UnionRectWithRgn(obj
, &rect
);
3568 /* Next horiz point */
3575 /* Loop to draw second half of quadrant */
3576 d
+= (3 * (asq
-bsq
) / 2 - (xd
+yd
)) / 2;
3579 /* next vertical point */
3581 rect
.bottom
= rect
.top
+ 1;
3582 REGION_UnionRectWithRgn(obj
, &rect
);
3583 rect
.top
= --bottom
;
3584 rect
.bottom
= rect
.top
+ 1;
3585 REGION_UnionRectWithRgn(obj
, &rect
);
3587 /* If nearest pixel is outside ellipse */
3590 /* Move away from center */
3601 /* Add the inside rectangle */
3605 rect
.bottom
= bottom
;
3606 REGION_UnionRectWithRgn(obj
, &rect
);
3609 RGNOBJAPI_Unlock(obj
);
3620 PRECTL tRect1
, tRect2
;
3624 /// FIXME: need to use GDIOBJ_LockMultipleObjects
3626 rgn1
= RGNOBJAPI_Lock(hSrcRgn1
, NULL
);
3630 rgn2
= RGNOBJAPI_Lock(hSrcRgn2
, NULL
);
3633 RGNOBJAPI_Unlock(rgn1
);
3637 if (rgn1
->rdh
.nCount
!= rgn2
->rdh
.nCount
)
3640 if (rgn1
->rdh
.nCount
== 0)
3646 if ((rgn1
->rdh
.rcBound
.left
!= rgn2
->rdh
.rcBound
.left
) ||
3647 (rgn1
->rdh
.rcBound
.right
!= rgn2
->rdh
.rcBound
.right
) ||
3648 (rgn1
->rdh
.rcBound
.top
!= rgn2
->rdh
.rcBound
.top
) ||
3649 (rgn1
->rdh
.rcBound
.bottom
!= rgn2
->rdh
.rcBound
.bottom
))
3652 tRect1
= rgn1
->Buffer
;
3653 tRect2
= rgn2
->Buffer
;
3655 if ((tRect1
== NULL
) || (tRect2
== NULL
))
3658 for (i
=0; i
< rgn1
->rdh
.nCount
; i
++)
3660 if ((tRect1
[i
].left
!= tRect2
[i
].left
) ||
3661 (tRect1
[i
].right
!= tRect2
[i
].right
) ||
3662 (tRect1
[i
].top
!= tRect2
[i
].top
) ||
3663 (tRect1
[i
].bottom
!= tRect2
[i
].bottom
))
3670 RGNOBJAPI_Unlock(rgn1
);
3671 RGNOBJAPI_Unlock(rgn2
);
3677 NtGdiExtCreateRegion(
3678 OPTIONAL LPXFORM Xform
,
3689 NTSTATUS Status
= STATUS_SUCCESS
;
3693 DPRINT("NtGdiExtCreateRegion\n");
3696 ProbeForRead(RgnData
, Count
, 1);
3697 nCount
= RgnData
->rdh
.nCount
;
3698 iType
= RgnData
->rdh
.iType
;
3699 dwSize
= RgnData
->rdh
.dwSize
;
3700 rects
= (RECT
*)RgnData
->Buffer
;
3702 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3704 Status
= _SEH2_GetExceptionCode();
3708 if (!NT_SUCCESS(Status
))
3710 SetLastNtError(Status
);
3714 /* Check parameters, but don't set last error here */
3715 if ((Count
< sizeof(RGNDATAHEADER
) + nCount
* sizeof(RECT
)) ||
3716 (iType
!= RDH_RECTANGLES
) ||
3717 (dwSize
!= sizeof(RGNDATAHEADER
)))
3722 Region
= REGION_AllocUserRgnWithHandle(nCount
);
3726 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
3729 hRgn
= Region
->BaseObject
.hHmgr
;
3733 /* Insert the rectangles one by one */
3734 for(i
=0; i
<nCount
; i
++)
3736 REGION_UnionRectWithRgn(Region
, &rects
[i
]);
3743 /* Init the XFORMOBJ from the Xform struct */
3744 Status
= STATUS_INVALID_PARAMETER
;
3745 XFORMOBJ_vInit(&xo
, &matrix
);
3746 ret
= XFORMOBJ_iSetXform(&xo
, (XFORML
*)Xform
);
3748 /* Check for error */
3749 if (ret
!= DDI_ERROR
)
3751 /* Apply the coordinate transformation on the rects */
3752 if (XFORMOBJ_bApplyXform(&xo
,
3754 Region
->rdh
.nCount
* 2,
3758 Status
= STATUS_SUCCESS
;
3763 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3765 Status
= _SEH2_GetExceptionCode();
3768 if (!NT_SUCCESS(Status
))
3770 EngSetLastError(ERROR_INVALID_PARAMETER
);
3771 RGNOBJAPI_Unlock(Region
);
3772 GreDeleteObject(hRgn
);
3776 RGNOBJAPI_Unlock(Region
);
3790 NTSTATUS Status
= STATUS_SUCCESS
;
3792 Rgn
= RGNOBJAPI_Lock(hRgn
, NULL
);
3798 ret
= REGION_GetRgnBox(Rgn
, &SafeRect
);
3799 RGNOBJAPI_Unlock(Rgn
);
3807 ProbeForWrite(pRect
, sizeof(RECT
), 1);
3810 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3812 Status
= _SEH2_GetExceptionCode();
3815 if (!NT_SUCCESS(Status
))
3833 DPRINT("NtGdiOffsetRgn: hRgn %p Xoffs %d Yoffs %d rgn %p\n", hRgn
, XOffset
, YOffset
, rgn
);
3835 rgn
= RGNOBJAPI_Lock(hRgn
, NULL
);
3838 DPRINT("NtGdiOffsetRgn: hRgn error\n");
3842 if (!REGION_bOffsetRgn(rgn
, XOffset
, YOffset
))
3848 ret
= REGION_Complexity(rgn
);
3851 RGNOBJAPI_Unlock(rgn
);
3865 prgn
= RGNOBJAPI_Lock(hRgn
, NULL
);
3869 ret
= REGION_PtInRegion(prgn
, X
, Y
);
3871 RGNOBJAPI_Unlock(prgn
);
3882 NTSTATUS Status
= STATUS_SUCCESS
;
3886 ProbeForRead(unsaferc
, sizeof(RECT
), 1);
3889 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3891 Status
= _SEH2_GetExceptionCode();
3895 if (!NT_SUCCESS(Status
))
3897 SetLastNtError(Status
);
3898 DPRINT1("NtGdiRectInRegion: Bogus rc\n");
3902 return IntRectInRegion(hRgn
, &rc
);
3916 rgn
= RGNOBJAPI_Lock(hRgn
, NULL
);
3919 return 0; // Per documentation
3922 REGION_SetRectRgn(rgn
, LeftRect
, TopRect
, RightRect
, BottomRect
);
3924 RGNOBJAPI_Unlock(rgn
);
3929 * MSDN: GetRegionData, Return Values:
3931 * "If the function succeeds and dwCount specifies an adequate number of bytes,
3932 * the return value is always dwCount. If dwCount is too small or the function
3933 * fails, the return value is 0. If lpRgnData is NULL, the return value is the
3934 * required number of bytes.
3936 * If the function fails, the return value is zero."
3938 _Success_(return!=0)
3943 _In_ ULONG cjBuffer
,
3944 _Out_opt_bytecap_(cjBuffer
) LPRGNDATA lpRgnData
)
3946 ULONG cjRects
, cjSize
;
3949 /* Lock the region */
3950 prgn
= RGNOBJAPI_Lock(hrgn
, NULL
);
3953 EngSetLastError(ERROR_INVALID_HANDLE
);
3957 /* Calculate the region sizes */
3958 cjRects
= prgn
->rdh
.nCount
* sizeof(RECT
);
3959 cjSize
= cjRects
+ sizeof(RGNDATAHEADER
);
3961 /* Check if region data is requested */
3964 /* Check if the buffer is large enough */
3965 if (cjBuffer
>= cjSize
)
3967 /* Probe the buffer and copy the data */
3970 ProbeForWrite(lpRgnData
, cjSize
, sizeof(ULONG
));
3971 RtlCopyMemory(lpRgnData
, &prgn
->rdh
, sizeof(RGNDATAHEADER
));
3972 RtlCopyMemory(lpRgnData
->Buffer
, prgn
->Buffer
, cjRects
);
3973 lpRgnData
->rdh
.iType
= RDH_RECTANGLES
;
3975 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3977 EngSetLastError(ERROR_INVALID_PARAMETER
);
3984 /* Buffer is too small */
3985 EngSetLastError(ERROR_INVALID_PARAMETER
);
3990 /* Unlock the region and return the size */
3991 RGNOBJAPI_Unlock(prgn
);