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 PROSRGNDATA 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 ScanLineList structures containing a list of
288 * edges which are entered at a given scanline. There is one
289 * ScanLineList 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 EdgeTableEntries in the AET linked by the
296 * nextWETE (winding EdgeTableEntry) 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 _EdgeTableEntry
311 INT ymax
; /* ycoord at which we exit this edge. */
312 BRESINFO bres
; /* Bresenham info to run the edge */
313 struct _EdgeTableEntry
*next
; /* Next in the list */
314 struct _EdgeTableEntry
*back
; /* For insertion sort */
315 struct _EdgeTableEntry
*nextWETE
; /* For winding num rule */
316 int ClockWise
; /* Flag for winding number rule */
320 typedef struct _ScanLineList
322 INT scanline
; /* The scanline represented */
323 EdgeTableEntry
*edgelist
; /* Header node */
324 struct _ScanLineList
*next
; /* Next in the list */
330 INT ymax
; /* ymax for the polygon */
331 INT ymin
; /* ymin for the polygon */
332 ScanLineList 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 _ScanLineListBlock
345 ScanLineList SLLs
[SLLSPERBLOCK
];
346 struct _ScanLineListBlock
*next
;
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(ROSRGNDATA
*reg
, PRECTL
*rect
, PRECTL
*firstrect
)
413 if ( (reg
->rdh
.nCount
+1) * sizeof(RECT
) >= reg
->rdh
.nRgnSize
)
416 DWORD NewSize
= 2 * reg
->rdh
.nRgnSize
;
417 if (NewSize
< (reg
->rdh
.nCount
+ 1) * sizeof(RECT
))
419 NewSize
= (reg
->rdh
.nCount
+ 1) * sizeof(RECT
);
421 temp
= ExAllocatePoolWithTag(PagedPool
, NewSize
, TAG_REGION
);
428 /* Copy the rectangles */
429 COPY_RECTS(temp
, *firstrect
, reg
->rdh
.nCount
);
431 reg
->rdh
.nRgnSize
= NewSize
;
432 if (*firstrect
!= ®
->rdh
.rcBound
)
434 ExFreePoolWithTag(*firstrect
, TAG_REGION
);
437 *rect
= (*firstrect
)+reg
->rdh
.nCount
;
442 #define MEMCHECK(reg, rect, firstrect) xmemcheck(reg,&(rect),(PRECTL *)&(firstrect))
444 typedef void (FASTCALL
*overlapProcp
)(PROSRGNDATA
, PRECT
, PRECT
, PRECT
, PRECT
, INT
, INT
);
445 typedef void (FASTCALL
*nonOverlapProcp
)(PROSRGNDATA
, PRECT
, PRECT
, INT
, INT
);
447 // Number of points to buffer before sending them off to scanlines() : Must be an even number
448 #define NUMPTSTOBUFFER 200
450 #define RGN_DEFAULT_RECTS 2
452 // Used to allocate buffers for points and link the buffers together
454 typedef struct _POINTBLOCK
456 POINT pts
[NUMPTSTOBUFFER
];
457 struct _POINTBLOCK
*next
;
462 * This function is left there for debugging purposes.
466 IntDumpRegion(HRGN hRgn
)
470 Data
= RGNOBJAPI_Lock(hRgn
, NULL
);
473 DbgPrint("IntDumpRegion called with invalid region!\n");
477 DbgPrint("IntDumpRegion(%x): %d,%d-%d,%d %d\n",
479 Data
->rdh
.rcBound
.left
,
480 Data
->rdh
.rcBound
.top
,
481 Data
->rdh
.rcBound
.right
,
482 Data
->rdh
.rcBound
.bottom
,
485 RGNOBJAPI_Unlock(Data
);
487 #endif /* Not NDEBUG */
492 REGION_Complexity( PROSRGNDATA obj
)
494 if (!obj
) return NULLREGION
;
495 switch(obj
->rdh
.nCount
)
497 DPRINT("Region Complexity -> %lu",obj
->rdh
.nCount
);
498 case 0: return NULLREGION
;
499 case 1: return SIMPLEREGION
;
500 default: return COMPLEXREGION
;
512 if (dst
!= src
) // Don't want to copy to itself
514 if (dst
->rdh
.nRgnSize
< src
->rdh
.nCount
* sizeof(RECT
))
518 temp
= ExAllocatePoolWithTag(PagedPool
, src
->rdh
.nCount
* sizeof(RECT
), TAG_REGION
);
522 if (dst
->Buffer
&& dst
->Buffer
!= &dst
->rdh
.rcBound
)
523 ExFreePoolWithTag(dst
->Buffer
, TAG_REGION
); // Free the old buffer
525 dst
->rdh
.nRgnSize
= src
->rdh
.nCount
* sizeof(RECT
); // Size of region buffer
527 dst
->rdh
.nCount
= src
->rdh
.nCount
; // Number of rectangles present in Buffer
528 dst
->rdh
.rcBound
.left
= src
->rdh
.rcBound
.left
;
529 dst
->rdh
.rcBound
.top
= src
->rdh
.rcBound
.top
;
530 dst
->rdh
.rcBound
.right
= src
->rdh
.rcBound
.right
;
531 dst
->rdh
.rcBound
.bottom
= src
->rdh
.rcBound
.bottom
;
532 dst
->rdh
.iType
= src
->rdh
.iType
;
533 COPY_RECTS(dst
->Buffer
, src
->Buffer
, src
->rdh
.nCount
);
539 REGION_SetExtents(ROSRGNDATA
*pReg
)
541 RECTL
*pRect
, *pRectEnd
, *pExtents
;
543 if (pReg
->rdh
.nCount
== 0)
545 pReg
->rdh
.rcBound
.left
= 0;
546 pReg
->rdh
.rcBound
.top
= 0;
547 pReg
->rdh
.rcBound
.right
= 0;
548 pReg
->rdh
.rcBound
.bottom
= 0;
549 pReg
->rdh
.iType
= RDH_RECTANGLES
;
553 pExtents
= &pReg
->rdh
.rcBound
;
554 pRect
= pReg
->Buffer
;
555 pRectEnd
= pReg
->Buffer
+ pReg
->rdh
.nCount
- 1;
558 * Since pRect is the first rectangle in the region, it must have the
559 * smallest top and since pRectEnd is the last rectangle in the region,
560 * it must have the largest bottom, because of banding. Initialize left and
561 * right from pRect and pRectEnd, resp., as good things to initialize them
564 pExtents
->left
= pRect
->left
;
565 pExtents
->top
= pRect
->top
;
566 pExtents
->right
= pRectEnd
->right
;
567 pExtents
->bottom
= pRectEnd
->bottom
;
569 while (pRect
<= pRectEnd
)
571 if (pRect
->left
< pExtents
->left
)
572 pExtents
->left
= pRect
->left
;
573 if (pRect
->right
> pExtents
->right
)
574 pExtents
->right
= pRect
->right
;
577 pReg
->rdh
.iType
= RDH_RECTANGLES
;
580 // FIXME: This seems to be wrong
581 /***********************************************************************
582 * REGION_CropAndOffsetRegion
585 REGION_CropAndOffsetRegion(
593 const POINT
*off
= offset
;
597 if (!rect
) // Just copy and offset
600 if (rgnDst
== rgnSrc
)
602 if (off
->x
|| off
->y
)
603 xrect
= rgnDst
->Buffer
;
609 xrect
= ExAllocatePoolWithTag(PagedPool
, rgnSrc
->rdh
.nCount
* sizeof(RECT
), TAG_REGION
);
612 if (rgnDst
->Buffer
&& rgnDst
->Buffer
!= &rgnDst
->rdh
.rcBound
)
613 ExFreePoolWithTag(rgnDst
->Buffer
, TAG_REGION
); // Free the old buffer. Will be assigned to xrect below.
616 if (rgnDst
!= rgnSrc
)
621 if (off
->x
|| off
->y
)
624 for (i
= 0; i
< rgnDst
->rdh
.nCount
; i
++)
626 xrect
[i
].left
= (rgnSrc
->Buffer
+ i
)->left
+ off
->x
;
627 xrect
[i
].right
= (rgnSrc
->Buffer
+ i
)->right
+ off
->x
;
628 xrect
[i
].top
= (rgnSrc
->Buffer
+ i
)->top
+ off
->y
;
629 xrect
[i
].bottom
= (rgnSrc
->Buffer
+ i
)->bottom
+ off
->y
;
631 rgnDst
->rdh
.rcBound
.left
+= off
->x
;
632 rgnDst
->rdh
.rcBound
.right
+= off
->x
;
633 rgnDst
->rdh
.rcBound
.top
+= off
->y
;
634 rgnDst
->rdh
.rcBound
.bottom
+= off
->y
;
638 COPY_RECTS(xrect
, rgnSrc
->Buffer
, rgnDst
->rdh
.nCount
);
641 rgnDst
->Buffer
= xrect
;
643 else if ((rect
->left
>= rect
->right
) ||
644 (rect
->top
>= rect
->bottom
) ||
645 !EXTENTCHECK(rect
, &rgnSrc
->rdh
.rcBound
))
649 else // Region box and clipping rect appear to intersect
652 ULONG i
, j
, clipa
, clipb
;
653 INT left
= rgnSrc
->rdh
.rcBound
.right
+ off
->x
;
654 INT right
= rgnSrc
->rdh
.rcBound
.left
+ off
->x
;
656 for (clipa
= 0; (rgnSrc
->Buffer
+ clipa
)->bottom
<= rect
->top
; clipa
++)
657 // Region and rect intersect so we stop before clipa > rgnSrc->rdh.nCount
658 ; // skip bands above the clipping rectangle
660 for (clipb
= clipa
; clipb
< rgnSrc
->rdh
.nCount
; clipb
++)
661 if ((rgnSrc
->Buffer
+ clipb
)->top
>= rect
->bottom
)
662 break; // and below it
664 // clipa - index of the first rect in the first intersecting band
665 // clipb - index of the last rect in the last intersecting band
667 if ((rgnDst
!= rgnSrc
) && (rgnDst
->rdh
.nCount
< (i
= (clipb
- clipa
))))
670 temp
= ExAllocatePoolWithTag(PagedPool
, i
* sizeof(RECT
), TAG_REGION
);
674 if (rgnDst
->Buffer
&& rgnDst
->Buffer
!= &rgnDst
->rdh
.rcBound
)
675 ExFreePoolWithTag(rgnDst
->Buffer
, TAG_REGION
); // free the old buffer
676 rgnDst
->Buffer
= temp
;
677 rgnDst
->rdh
.nCount
= i
;
678 rgnDst
->rdh
.nRgnSize
= i
* sizeof(RECT
);
681 for (i
= clipa
, j
= 0; i
< clipb
; i
++)
683 // i - src index, j - dst index, j is always <= i for obvious reasons
685 lpr
= rgnSrc
->Buffer
+ i
;
687 if (lpr
->left
< rect
->right
&& lpr
->right
> rect
->left
)
689 rpr
= rgnDst
->Buffer
+ j
;
691 rpr
->top
= lpr
->top
+ off
->y
;
692 rpr
->bottom
= lpr
->bottom
+ off
->y
;
693 rpr
->left
= ((lpr
->left
> rect
->left
) ? lpr
->left
: rect
->left
) + off
->x
;
694 rpr
->right
= ((lpr
->right
< rect
->right
) ? lpr
->right
: rect
->right
) + off
->x
;
696 if (rpr
->left
< left
) left
= rpr
->left
;
697 if (rpr
->right
> right
) right
= rpr
->right
;
703 if (j
== 0) goto empty
;
705 rgnDst
->rdh
.rcBound
.left
= left
;
706 rgnDst
->rdh
.rcBound
.right
= right
;
708 left
= rect
->top
+ off
->y
;
709 right
= rect
->bottom
+ off
->y
;
711 rgnDst
->rdh
.nCount
= j
--;
712 for (i
= 0; i
<= j
; i
++) // Fixup top band
713 if ((rgnDst
->Buffer
+ i
)->top
< left
)
714 (rgnDst
->Buffer
+ i
)->top
= left
;
718 for (i
= j
; i
> 0; i
--) // Fixup bottom band
719 if ((rgnDst
->Buffer
+ i
)->bottom
> right
)
720 (rgnDst
->Buffer
+ i
)->bottom
= right
;
724 rgnDst
->rdh
.rcBound
.top
= (rgnDst
->Buffer
)->top
;
725 rgnDst
->rdh
.rcBound
.bottom
= (rgnDst
->Buffer
+ j
)->bottom
;
727 rgnDst
->rdh
.iType
= RDH_RECTANGLES
;
735 rgnDst
->Buffer
= ExAllocatePoolWithTag(PagedPool
, RGN_DEFAULT_RECTS
* sizeof(RECT
), TAG_REGION
);
738 rgnDst
->rdh
.nCount
= RGN_DEFAULT_RECTS
;
739 rgnDst
->rdh
.nRgnSize
= RGN_DEFAULT_RECTS
* sizeof(RECT
);
744 EMPTY_REGION(rgnDst
);
750 * Attempt to merge the rects in the current band with those in the
751 * previous one. Used only by REGION_RegionOp.
754 * The new index for the previous band.
756 * \note Side Effects:
757 * If coalescing takes place:
758 * - rectangles in the previous band will have their bottom fields
760 * - pReg->numRects will be decreased.
765 PROSRGNDATA pReg
, /* Region to coalesce */
766 INT prevStart
, /* Index of start of previous band */
767 INT curStart
/* Index of start of current band */
770 RECTL
*pPrevRect
; /* Current rect in previous band */
771 RECTL
*pCurRect
; /* Current rect in current band */
772 RECTL
*pRegEnd
; /* End of region */
773 INT curNumRects
; /* Number of rectangles in current band */
774 INT prevNumRects
; /* Number of rectangles in previous band */
775 INT bandtop
; /* Top coordinate for current band */
777 pRegEnd
= pReg
->Buffer
+ pReg
->rdh
.nCount
;
778 pPrevRect
= pReg
->Buffer
+ prevStart
;
779 prevNumRects
= curStart
- prevStart
;
782 * Figure out how many rectangles are in the current band. Have to do
783 * this because multiple bands could have been added in REGION_RegionOp
784 * at the end when one region has been exhausted.
786 pCurRect
= pReg
->Buffer
+ curStart
;
787 bandtop
= pCurRect
->top
;
788 for (curNumRects
= 0;
789 (pCurRect
!= pRegEnd
) && (pCurRect
->top
== bandtop
);
795 if (pCurRect
!= pRegEnd
)
798 * If more than one band was added, we have to find the start
799 * of the last band added so the next coalescing job can start
800 * at the right place... (given when multiple bands are added,
801 * this may be pointless -- see above).
804 while ((pRegEnd
-1)->top
== pRegEnd
->top
)
808 curStart
= pRegEnd
- pReg
->Buffer
;
809 pRegEnd
= pReg
->Buffer
+ pReg
->rdh
.nCount
;
812 if ((curNumRects
== prevNumRects
) && (curNumRects
!= 0))
814 pCurRect
-= curNumRects
;
816 * The bands may only be coalesced if the bottom of the previous
817 * matches the top scanline of the current.
819 if (pPrevRect
->bottom
== pCurRect
->top
)
822 * Make sure the bands have rects in the same places. This
823 * assumes that rects have been added in such a way that they
824 * cover the most area possible. I.e. two rects in a band must
825 * have some horizontal space between them.
829 if ((pPrevRect
->left
!= pCurRect
->left
) ||
830 (pPrevRect
->right
!= pCurRect
->right
))
833 * The bands don't line up so they can't be coalesced.
841 while (prevNumRects
!= 0);
843 pReg
->rdh
.nCount
-= curNumRects
;
844 pCurRect
-= curNumRects
;
845 pPrevRect
-= curNumRects
;
848 * The bands may be merged, so set the bottom of each rect
849 * in the previous band to that of the corresponding rect in
854 pPrevRect
->bottom
= pCurRect
->bottom
;
859 while (curNumRects
!= 0);
862 * If only one band was added to the region, we have to backup
863 * curStart to the start of the previous band.
865 * If more than one band was added to the region, copy the
866 * other bands down. The assumption here is that the other bands
867 * came from the same region as the current one and no further
868 * coalescing can be done on them since it's all been done
869 * already... curStart is already in the right place.
871 if (pCurRect
== pRegEnd
)
873 curStart
= prevStart
;
879 *pPrevRect
++ = *pCurRect
++;
881 while (pCurRect
!= pRegEnd
);
889 * Apply an operation to two regions. Called by REGION_Union,
890 * REGION_Inverse, REGION_Subtract, REGION_Intersect...
896 * The new region is overwritten.
898 *\note The idea behind this function is to view the two regions as sets.
899 * Together they cover a rectangle of area that this function divides
900 * into horizontal bands where points are covered only by one region
901 * or by both. For the first case, the nonOverlapFunc is called with
902 * each the band and the band's upper and lower extents. For the
903 * second, the overlapFunc is called to process the entire band. It
904 * is responsible for clipping the rectangles in the band, though
905 * this function provides the boundaries.
906 * At the end of each band, the new region is coalesced, if possible,
907 * to reduce the number of rectangles in the region.
912 ROSRGNDATA
*newReg
, /* Place to store result */
913 ROSRGNDATA
*reg1
, /* First region in operation */
914 ROSRGNDATA
*reg2
, /* 2nd region in operation */
915 overlapProcp overlapFunc
, /* Function to call for over-lapping bands */
916 nonOverlapProcp nonOverlap1Func
, /* Function to call for non-overlapping bands in region 1 */
917 nonOverlapProcp nonOverlap2Func
/* Function to call for non-overlapping bands in region 2 */
920 RECTL
*r1
; /* Pointer into first region */
921 RECTL
*r2
; /* Pointer into 2d region */
922 RECTL
*r1End
; /* End of 1st region */
923 RECTL
*r2End
; /* End of 2d region */
924 INT ybot
; /* Bottom of intersection */
925 INT ytop
; /* Top of intersection */
926 RECTL
*oldRects
; /* Old rects for newReg */
927 ULONG prevBand
; /* Index of start of
928 * Previous band in newReg */
929 ULONG curBand
; /* Index of start of current band in newReg */
930 RECTL
*r1BandEnd
; /* End of current band in r1 */
931 RECTL
*r2BandEnd
; /* End of current band in r2 */
932 ULONG top
; /* Top of non-overlapping band */
933 ULONG bot
; /* Bottom of non-overlapping band */
937 * set r1, r2, r1End and r2End appropriately, preserve the important
938 * parts of the destination region until the end in case it's one of
939 * the two source regions, then mark the "new" region empty, allocating
940 * another array of rectangles for it to use.
944 r1End
= r1
+ reg1
->rdh
.nCount
;
945 r2End
= r2
+ reg2
->rdh
.nCount
;
949 * newReg may be one of the src regions so we can't empty it. We keep a
950 * note of its rects pointer (so that we can free them later), preserve its
951 * extents and simply set numRects to zero.
954 oldRects
= newReg
->Buffer
;
955 newReg
->rdh
.nCount
= 0;
958 * Allocate a reasonable number of rectangles for the new region. The idea
959 * is to allocate enough so the individual functions don't need to
960 * reallocate and copy the array, which is time consuming, yet we don't
961 * have to worry about using too much memory. I hope to be able to
962 * nuke the Xrealloc() at the end of this function eventually.
964 newReg
->rdh
.nRgnSize
= max(reg1
->rdh
.nCount
+ 1,reg2
->rdh
.nCount
) * 2 * sizeof(RECT
);
966 newReg
->Buffer
= ExAllocatePoolWithTag(PagedPool
, newReg
->rdh
.nRgnSize
, TAG_REGION
);
969 newReg
->rdh
.nRgnSize
= 0;
974 * Initialize ybot and ytop.
975 * In the upcoming loop, ybot and ytop serve different functions depending
976 * on whether the band being handled is an overlapping or non-overlapping
978 * In the case of a non-overlapping band (only one of the regions
979 * has points in the band), ybot is the bottom of the most recent
980 * intersection and thus clips the top of the rectangles in that band.
981 * ytop is the top of the next intersection between the two regions and
982 * serves to clip the bottom of the rectangles in the current band.
983 * For an overlapping band (where the two regions intersect), ytop clips
984 * the top of the rectangles of both regions and ybot clips the bottoms.
986 if (reg1
->rdh
.rcBound
.top
< reg2
->rdh
.rcBound
.top
)
987 ybot
= reg1
->rdh
.rcBound
.top
;
989 ybot
= reg2
->rdh
.rcBound
.top
;
992 * prevBand serves to mark the start of the previous band so rectangles
993 * can be coalesced into larger rectangles. qv. miCoalesce, above.
994 * In the beginning, there is no previous band, so prevBand == curBand
995 * (curBand is set later on, of course, but the first band will always
996 * start at index 0). prevBand and curBand must be indices because of
997 * the possible expansion, and resultant moving, of the new region's
998 * array of rectangles.
1004 curBand
= newReg
->rdh
.nCount
;
1007 * This algorithm proceeds one source-band (as opposed to a
1008 * destination band, which is determined by where the two regions
1009 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
1010 * rectangle after the last one in the current band for their
1011 * respective regions.
1014 while ((r1BandEnd
!= r1End
) && (r1BandEnd
->top
== r1
->top
))
1020 while ((r2BandEnd
!= r2End
) && (r2BandEnd
->top
== r2
->top
))
1026 * First handle the band that doesn't intersect, if any.
1028 * Note that attention is restricted to one band in the
1029 * non-intersecting region at once, so if a region has n
1030 * bands between the current position and the next place it overlaps
1031 * the other, this entire loop will be passed through n times.
1033 if (r1
->top
< r2
->top
)
1035 top
= max(r1
->top
,ybot
);
1036 bot
= min(r1
->bottom
,r2
->top
);
1038 if ((top
!= bot
) && (nonOverlap1Func
!= NULL
))
1040 (* nonOverlap1Func
) (newReg
, r1
, r1BandEnd
, top
, bot
);
1045 else if (r2
->top
< r1
->top
)
1047 top
= max(r2
->top
,ybot
);
1048 bot
= min(r2
->bottom
,r1
->top
);
1050 if ((top
!= bot
) && (nonOverlap2Func
!= NULL
))
1052 (* nonOverlap2Func
) (newReg
, r2
, r2BandEnd
, top
, bot
);
1063 * If any rectangles got added to the region, try and coalesce them
1064 * with rectangles from the previous band. Note we could just do
1065 * this test in miCoalesce, but some machines incur a not
1066 * inconsiderable cost for function calls, so...
1068 if (newReg
->rdh
.nCount
!= curBand
)
1070 prevBand
= REGION_Coalesce (newReg
, prevBand
, curBand
);
1074 * Now see if we've hit an intersecting band. The two bands only
1075 * intersect if ybot > ytop
1077 ybot
= min(r1
->bottom
, r2
->bottom
);
1078 curBand
= newReg
->rdh
.nCount
;
1081 (* overlapFunc
) (newReg
, r1
, r1BandEnd
, r2
, r2BandEnd
, ytop
, ybot
);
1084 if (newReg
->rdh
.nCount
!= curBand
)
1086 prevBand
= REGION_Coalesce (newReg
, prevBand
, curBand
);
1090 * If we've finished with a band (bottom == ybot) we skip forward
1091 * in the region to the next band.
1093 if (r1
->bottom
== ybot
)
1097 if (r2
->bottom
== ybot
)
1102 while ((r1
!= r1End
) && (r2
!= r2End
));
1105 * Deal with whichever region still has rectangles left.
1107 curBand
= newReg
->rdh
.nCount
;
1110 if (nonOverlap1Func
!= NULL
)
1115 while ((r1BandEnd
< r1End
) && (r1BandEnd
->top
== r1
->top
))
1119 (* nonOverlap1Func
) (newReg
, r1
, r1BandEnd
,
1120 max(r1
->top
,ybot
), r1
->bottom
);
1123 while (r1
!= r1End
);
1126 else if ((r2
!= r2End
) && (nonOverlap2Func
!= NULL
))
1131 while ((r2BandEnd
< r2End
) && (r2BandEnd
->top
== r2
->top
))
1135 (* nonOverlap2Func
) (newReg
, r2
, r2BandEnd
,
1136 max(r2
->top
,ybot
), r2
->bottom
);
1139 while (r2
!= r2End
);
1142 if (newReg
->rdh
.nCount
!= curBand
)
1144 (void) REGION_Coalesce (newReg
, prevBand
, curBand
);
1148 * A bit of cleanup. To keep regions from growing without bound,
1149 * we shrink the array of rectangles to match the new number of
1150 * rectangles in the region. This never goes to 0, however...
1152 * Only do this stuff if the number of rectangles allocated is more than
1153 * twice the number of rectangles in the region (a simple optimization...).
1155 if ((2 * newReg
->rdh
.nCount
*sizeof(RECT
) < newReg
->rdh
.nRgnSize
&& (newReg
->rdh
.nCount
> 2)))
1157 if (REGION_NOT_EMPTY(newReg
))
1159 RECTL
*prev_rects
= newReg
->Buffer
;
1160 newReg
->Buffer
= ExAllocatePoolWithTag(PagedPool
, newReg
->rdh
.nCount
*sizeof(RECT
), TAG_REGION
);
1162 if (! newReg
->Buffer
)
1163 newReg
->Buffer
= prev_rects
;
1166 newReg
->rdh
.nRgnSize
= newReg
->rdh
.nCount
*sizeof(RECT
);
1167 COPY_RECTS(newReg
->Buffer
, prev_rects
, newReg
->rdh
.nCount
);
1168 if (prev_rects
!= &newReg
->rdh
.rcBound
)
1169 ExFreePoolWithTag(prev_rects
, TAG_REGION
);
1175 * No point in doing the extra work involved in an Xrealloc if
1176 * the region is empty
1178 newReg
->rdh
.nRgnSize
= sizeof(RECT
);
1179 if (newReg
->Buffer
!= &newReg
->rdh
.rcBound
)
1180 ExFreePoolWithTag(newReg
->Buffer
, TAG_REGION
);
1181 newReg
->Buffer
= ExAllocatePoolWithTag(PagedPool
, sizeof(RECT
), TAG_REGION
);
1182 ASSERT(newReg
->Buffer
);
1185 newReg
->rdh
.iType
= RDH_RECTANGLES
;
1187 if (oldRects
!= &newReg
->rdh
.rcBound
)
1188 ExFreePoolWithTag(oldRects
, TAG_REGION
);
1192 /***********************************************************************
1193 * Region Intersection
1194 ***********************************************************************/
1198 * Handle an overlapping band for REGION_Intersect.
1203 * \note Side Effects:
1204 * Rectangles may be added to the region.
1207 static void FASTCALL
1221 pNextRect
= pReg
->Buffer
+ pReg
->rdh
.nCount
;
1223 while ((r1
!= r1End
) && (r2
!= r2End
))
1225 left
= max(r1
->left
, r2
->left
);
1226 right
= min(r1
->right
, r2
->right
);
1229 * If there's any overlap between the two rectangles, add that
1230 * overlap to the new region.
1231 * There's no need to check for subsumption because the only way
1232 * such a need could arise is if some region has two rectangles
1233 * right next to each other. Since that should never happen...
1237 MEMCHECK(pReg
, pNextRect
, pReg
->Buffer
);
1238 pNextRect
->left
= left
;
1239 pNextRect
->top
= top
;
1240 pNextRect
->right
= right
;
1241 pNextRect
->bottom
= bottom
;
1242 pReg
->rdh
.nCount
+= 1;
1247 * Need to advance the pointers. Shift the one that extends
1248 * to the right the least, since the other still has a chance to
1249 * overlap with that region's next rectangle, if you see what I mean.
1251 if (r1
->right
< r2
->right
)
1255 else if (r2
->right
< r1
->right
)
1268 /***********************************************************************
1269 * REGION_IntersectRegion
1271 static void FASTCALL
1272 REGION_IntersectRegion(
1278 /* Check for trivial reject */
1279 if ( (!(reg1
->rdh
.nCount
)) || (!(reg2
->rdh
.nCount
)) ||
1280 (!EXTENTCHECK(®1
->rdh
.rcBound
, ®2
->rdh
.rcBound
)) )
1281 newReg
->rdh
.nCount
= 0;
1283 REGION_RegionOp (newReg
, reg1
, reg2
,
1284 REGION_IntersectO
, NULL
, NULL
);
1287 * Can't alter newReg's extents before we call miRegionOp because
1288 * it might be one of the source regions and miRegionOp depends
1289 * on the extents of those regions being the same. Besides, this
1290 * way there's no checking against rectangles that will be nuked
1291 * due to coalescing, so we have to examine fewer rectangles.
1294 REGION_SetExtents(newReg
);
1297 /***********************************************************************
1299 ***********************************************************************/
1302 * Handle a non-overlapping band for the union operation. Just
1303 * Adds the rectangles into the region. Doesn't have to check for
1304 * subsumption or anything.
1309 * \note Side Effects:
1310 * pReg->numRects is incremented and the final rectangles overwritten
1311 * with the rectangles we're passed.
1314 static void FASTCALL
1325 pNextRect
= pReg
->Buffer
+ pReg
->rdh
.nCount
;
1329 MEMCHECK(pReg
, pNextRect
, pReg
->Buffer
);
1330 pNextRect
->left
= r
->left
;
1331 pNextRect
->top
= top
;
1332 pNextRect
->right
= r
->right
;
1333 pNextRect
->bottom
= bottom
;
1334 pReg
->rdh
.nCount
+= 1;
1342 * Handle an overlapping band for the union operation. Picks the
1343 * left-most rectangle each time and merges it into the region.
1348 * \note Side Effects:
1349 * Rectangles are overwritten in pReg->rects and pReg->numRects will
1353 static void FASTCALL
1366 pNextRect
= pReg
->Buffer
+ pReg
->rdh
.nCount
;
1368 #define MERGERECT(r) \
1369 if ((pReg->rdh.nCount != 0) && \
1370 ((pNextRect-1)->top == top) && \
1371 ((pNextRect-1)->bottom == bottom) && \
1372 ((pNextRect-1)->right >= r->left)) \
1374 if ((pNextRect-1)->right < r->right) \
1376 (pNextRect-1)->right = r->right; \
1381 MEMCHECK(pReg, pNextRect, pReg->Buffer); \
1382 pNextRect->top = top; \
1383 pNextRect->bottom = bottom; \
1384 pNextRect->left = r->left; \
1385 pNextRect->right = r->right; \
1386 pReg->rdh.nCount += 1; \
1391 while ((r1
!= r1End
) && (r2
!= r2End
))
1393 if (r1
->left
< r2
->left
)
1409 while (r1
!= r1End
);
1411 else while (r2
!= r2End
)
1418 /***********************************************************************
1419 * REGION_UnionRegion
1421 static void FASTCALL
1428 /* Checks all the simple cases */
1431 * Region 1 and 2 are the same or region 1 is empty
1433 if (reg1
== reg2
|| 0 == reg1
->rdh
.nCount
||
1434 reg1
->rdh
.rcBound
.right
<= reg1
->rdh
.rcBound
.left
||
1435 reg1
->rdh
.rcBound
.bottom
<= reg1
->rdh
.rcBound
.top
)
1439 REGION_CopyRegion(newReg
, reg2
);
1445 * If nothing to union (region 2 empty)
1447 if (0 == reg2
->rdh
.nCount
||
1448 reg2
->rdh
.rcBound
.right
<= reg2
->rdh
.rcBound
.left
||
1449 reg2
->rdh
.rcBound
.bottom
<= reg2
->rdh
.rcBound
.top
)
1453 REGION_CopyRegion(newReg
, reg1
);
1459 * Region 1 completely subsumes region 2
1461 if (1 == reg1
->rdh
.nCount
&&
1462 reg1
->rdh
.rcBound
.left
<= reg2
->rdh
.rcBound
.left
&&
1463 reg1
->rdh
.rcBound
.top
<= reg2
->rdh
.rcBound
.top
&&
1464 reg2
->rdh
.rcBound
.right
<= reg1
->rdh
.rcBound
.right
&&
1465 reg2
->rdh
.rcBound
.bottom
<= reg1
->rdh
.rcBound
.bottom
)
1469 REGION_CopyRegion(newReg
, reg1
);
1475 * Region 2 completely subsumes region 1
1477 if (1 == reg2
->rdh
.nCount
&&
1478 reg2
->rdh
.rcBound
.left
<= reg1
->rdh
.rcBound
.left
&&
1479 reg2
->rdh
.rcBound
.top
<= reg1
->rdh
.rcBound
.top
&&
1480 reg1
->rdh
.rcBound
.right
<= reg2
->rdh
.rcBound
.right
&&
1481 reg1
->rdh
.rcBound
.bottom
<= reg2
->rdh
.rcBound
.bottom
)
1485 REGION_CopyRegion(newReg
, reg2
);
1490 REGION_RegionOp (newReg
, reg1
, reg2
, REGION_UnionO
,
1491 REGION_UnionNonO
, REGION_UnionNonO
);
1492 newReg
->rdh
.rcBound
.left
= min(reg1
->rdh
.rcBound
.left
, reg2
->rdh
.rcBound
.left
);
1493 newReg
->rdh
.rcBound
.top
= min(reg1
->rdh
.rcBound
.top
, reg2
->rdh
.rcBound
.top
);
1494 newReg
->rdh
.rcBound
.right
= max(reg1
->rdh
.rcBound
.right
, reg2
->rdh
.rcBound
.right
);
1495 newReg
->rdh
.rcBound
.bottom
= max(reg1
->rdh
.rcBound
.bottom
, reg2
->rdh
.rcBound
.bottom
);
1498 /***********************************************************************
1499 * Region Subtraction
1500 ***********************************************************************/
1503 * Deal with non-overlapping band for subtraction. Any parts from
1504 * region 2 we discard. Anything from region 1 we add to the region.
1509 * \note Side Effects:
1510 * pReg may be affected.
1513 static void FASTCALL
1514 REGION_SubtractNonO1(
1524 pNextRect
= pReg
->Buffer
+ pReg
->rdh
.nCount
;
1528 MEMCHECK(pReg
, pNextRect
, pReg
->Buffer
);
1529 pNextRect
->left
= r
->left
;
1530 pNextRect
->top
= top
;
1531 pNextRect
->right
= r
->right
;
1532 pNextRect
->bottom
= bottom
;
1533 pReg
->rdh
.nCount
+= 1;
1542 * Overlapping band subtraction. x1 is the left-most point not yet
1548 * \note Side Effects:
1549 * pReg may have rectangles added to it.
1552 static void FASTCALL
1567 pNextRect
= pReg
->Buffer
+ pReg
->rdh
.nCount
;
1569 while ((r1
!= r1End
) && (r2
!= r2End
))
1571 if (r2
->right
<= left
)
1574 * Subtrahend missed the boat: go to next subtrahend.
1578 else if (r2
->left
<= left
)
1581 * Subtrahend preceeds minuend: nuke left edge of minuend.
1584 if (left
>= r1
->right
)
1587 * Minuend completely covered: advance to next minuend and
1588 * reset left fence to edge of new minuend.
1597 * Subtrahend now used up since it doesn't extend beyond
1603 else if (r2
->left
< r1
->right
)
1606 * Left part of subtrahend covers part of minuend: add uncovered
1607 * part of minuend to region and skip to next subtrahend.
1609 MEMCHECK(pReg
, pNextRect
, pReg
->Buffer
);
1610 pNextRect
->left
= left
;
1611 pNextRect
->top
= top
;
1612 pNextRect
->right
= r2
->left
;
1613 pNextRect
->bottom
= bottom
;
1614 pReg
->rdh
.nCount
+= 1;
1617 if (left
>= r1
->right
)
1620 * Minuend used up: advance to new...
1629 * Subtrahend used up
1637 * Minuend used up: add any remaining piece before advancing.
1639 if (r1
->right
> left
)
1641 MEMCHECK(pReg
, pNextRect
, pReg
->Buffer
);
1642 pNextRect
->left
= left
;
1643 pNextRect
->top
= top
;
1644 pNextRect
->right
= r1
->right
;
1645 pNextRect
->bottom
= bottom
;
1646 pReg
->rdh
.nCount
+= 1;
1656 * Add remaining minuend rectangles to region.
1660 MEMCHECK(pReg
, pNextRect
, pReg
->Buffer
);
1661 pNextRect
->left
= left
;
1662 pNextRect
->top
= top
;
1663 pNextRect
->right
= r1
->right
;
1664 pNextRect
->bottom
= bottom
;
1665 pReg
->rdh
.nCount
+= 1;
1677 * Subtract regS from regM and leave the result in regD.
1678 * S stands for subtrahend, M for minuend and D for difference.
1683 * \note Side Effects:
1684 * regD is overwritten.
1687 static void FASTCALL
1688 REGION_SubtractRegion(
1694 /* Check for trivial reject */
1695 if ( (!(regM
->rdh
.nCount
)) || (!(regS
->rdh
.nCount
)) ||
1696 (!EXTENTCHECK(®M
->rdh
.rcBound
, ®S
->rdh
.rcBound
)) )
1698 REGION_CopyRegion(regD
, regM
);
1702 REGION_RegionOp (regD
, regM
, regS
, REGION_SubtractO
,
1703 REGION_SubtractNonO1
, NULL
);
1706 * Can't alter newReg's extents before we call miRegionOp because
1707 * it might be one of the source regions and miRegionOp depends
1708 * on the extents of those regions being the unaltered. Besides, this
1709 * way there's no checking against rectangles that will be nuked
1710 * due to coalescing, so we have to examine fewer rectangles.
1712 REGION_SetExtents (regD
);
1715 /***********************************************************************
1718 static void FASTCALL
1726 ROSRGNDATA
*tra
, *trb
;
1728 // FIXME: Don't use a handle
1729 tra
= REGION_AllocRgnWithHandle(sra
->rdh
.nCount
+ 1);
1734 htra
= tra
->BaseObject
.hHmgr
;
1736 // FIXME: Don't use a handle
1737 trb
= REGION_AllocRgnWithHandle(srb
->rdh
.nCount
+ 1);
1740 RGNOBJAPI_Unlock(tra
);
1741 GreDeleteObject(htra
);
1744 htrb
= trb
->BaseObject
.hHmgr
;
1746 REGION_SubtractRegion(tra
, sra
, srb
);
1747 REGION_SubtractRegion(trb
, srb
, sra
);
1748 REGION_UnionRegion(dr
, tra
, trb
);
1749 RGNOBJAPI_Unlock(tra
);
1750 RGNOBJAPI_Unlock(trb
);
1752 GreDeleteObject(htra
);
1753 GreDeleteObject(htrb
);
1759 * Adds a rectangle to a REGION
1762 REGION_UnionRectWithRgn(
1769 region
.Buffer
= ®ion
.rdh
.rcBound
;
1770 region
.rdh
.nCount
= 1;
1771 region
.rdh
.nRgnSize
= sizeof(RECT
);
1772 region
.rdh
.rcBound
= *rect
;
1773 REGION_UnionRegion(rgn
, rgn
, ®ion
);
1777 REGION_CreateSimpleFrameRgn(
1786 if ((x
!= 0) || (y
!= 0))
1790 if (rgn
->rdh
.rcBound
.bottom
- rgn
->rdh
.rcBound
.top
> y
* 2 &&
1791 rgn
->rdh
.rcBound
.right
- rgn
->rdh
.rcBound
.left
> x
* 2)
1796 prc
->left
= rgn
->rdh
.rcBound
.left
;
1797 prc
->top
= rgn
->rdh
.rcBound
.top
;
1798 prc
->right
= rgn
->rdh
.rcBound
.right
;
1799 prc
->bottom
= prc
->top
+ y
;
1805 /* Left rectangle */
1806 prc
->left
= rgn
->rdh
.rcBound
.left
;
1807 prc
->top
= rgn
->rdh
.rcBound
.top
+ y
;
1808 prc
->right
= prc
->left
+ x
;
1809 prc
->bottom
= rgn
->rdh
.rcBound
.bottom
- y
;
1812 /* Right rectangle */
1813 prc
->left
= rgn
->rdh
.rcBound
.right
- x
;
1814 prc
->top
= rgn
->rdh
.rcBound
.top
+ y
;
1815 prc
->right
= rgn
->rdh
.rcBound
.right
;
1816 prc
->bottom
= rgn
->rdh
.rcBound
.bottom
- y
;
1822 /* Bottom rectangle */
1823 prc
->left
= rgn
->rdh
.rcBound
.left
;
1824 prc
->top
= rgn
->rdh
.rcBound
.bottom
- y
;
1825 prc
->right
= rgn
->rdh
.rcBound
.right
;
1826 prc
->bottom
= rgn
->rdh
.rcBound
.bottom
;
1833 /* The frame results in a complex region. rcBounds remains
1834 the same, though. */
1835 rgn
->rdh
.nCount
= (DWORD
)(prc
- rc
);
1836 ASSERT(rgn
->rdh
.nCount
> 1);
1837 rgn
->rdh
.nRgnSize
= rgn
->rdh
.nCount
* sizeof(RECT
);
1838 rgn
->Buffer
= ExAllocatePoolWithTag(PagedPool
, rgn
->rdh
.nRgnSize
, TAG_REGION
);
1841 rgn
->rdh
.nRgnSize
= 0;
1845 _PRAGMA_WARNING_SUPPRESS(__WARNING_MAYBE_UNINIT_VAR
) // rc is initialized
1846 COPY_RECTS(rgn
->Buffer
, rc
, rgn
->rdh
.nCount
);
1854 REGION_CreateFrameRgn(
1861 PROSRGNDATA srcObj
, destObj
;
1865 if (!(srcObj
= RGNOBJAPI_Lock(hSrc
, NULL
)))
1869 if (!REGION_NOT_EMPTY(srcObj
))
1871 RGNOBJAPI_Unlock(srcObj
);
1874 if (!(destObj
= RGNOBJAPI_Lock(hDest
, NULL
)))
1876 RGNOBJAPI_Unlock(srcObj
);
1880 EMPTY_REGION(destObj
);
1881 if (!REGION_CopyRegion(destObj
, srcObj
))
1883 RGNOBJAPI_Unlock(destObj
);
1884 RGNOBJAPI_Unlock(srcObj
);
1888 if (REGION_Complexity(srcObj
) == SIMPLEREGION
)
1890 if (!REGION_CreateSimpleFrameRgn(destObj
, x
, y
))
1892 EMPTY_REGION(destObj
);
1893 RGNOBJAPI_Unlock(destObj
);
1894 RGNOBJAPI_Unlock(srcObj
);
1900 /* Original region moved to right */
1901 rc
= srcObj
->Buffer
;
1902 for (i
= 0; i
< srcObj
->rdh
.nCount
; i
++)
1908 REGION_IntersectRegion(destObj
, destObj
, srcObj
);
1910 /* Original region moved to left */
1911 rc
= srcObj
->Buffer
;
1912 for (i
= 0; i
< srcObj
->rdh
.nCount
; i
++)
1918 REGION_IntersectRegion(destObj
, destObj
, srcObj
);
1920 /* Original region moved down */
1921 rc
= srcObj
->Buffer
;
1922 for (i
= 0; i
< srcObj
->rdh
.nCount
; i
++)
1930 REGION_IntersectRegion(destObj
, destObj
, srcObj
);
1932 /* Original region moved up */
1933 rc
= srcObj
->Buffer
;
1934 for (i
= 0; i
< srcObj
->rdh
.nCount
; i
++)
1937 rc
->bottom
-= 2 * y
;
1940 REGION_IntersectRegion(destObj
, destObj
, srcObj
);
1942 /* Restore the original region */
1943 rc
= srcObj
->Buffer
;
1944 for (i
= 0; i
< srcObj
->rdh
.nCount
; i
++)
1950 REGION_SubtractRegion(destObj
, srcObj
, destObj
);
1953 RGNOBJAPI_Unlock(destObj
);
1954 RGNOBJAPI_Unlock(srcObj
);
1965 RECTL
*pCurRect
, *pEndRect
;
1966 PROSRGNDATA srcObj
= NULL
;
1967 PROSRGNDATA destObj
= NULL
;
1975 pdcattr
= dc
->pdcattr
;
1977 if (pdcattr
->iMapMode
== MM_TEXT
) // Requires only a translation
1979 if (NtGdiCombineRgn(hDest
, hSrc
, 0, RGN_COPY
) == ERROR
)
1982 NtGdiOffsetRgn(hDest
, pdcattr
->ptlViewportOrg
.x
- pdcattr
->ptlWindowOrg
.x
,
1983 pdcattr
->ptlViewportOrg
.y
- pdcattr
->ptlWindowOrg
.y
);
1988 if ( !(srcObj
= RGNOBJAPI_Lock(hSrc
, NULL
)) )
1990 if ( !(destObj
= RGNOBJAPI_Lock(hDest
, NULL
)) )
1992 RGNOBJAPI_Unlock(srcObj
);
1995 EMPTY_REGION(destObj
);
1997 pEndRect
= srcObj
->Buffer
+ srcObj
->rdh
.nCount
;
1998 for (pCurRect
= srcObj
->Buffer
; pCurRect
< pEndRect
; pCurRect
++)
2000 tmpRect
= *pCurRect
;
2001 tmpRect
.left
= XLPTODP(pdcattr
, tmpRect
.left
);
2002 tmpRect
.top
= YLPTODP(pdcattr
, tmpRect
.top
);
2003 tmpRect
.right
= XLPTODP(pdcattr
, tmpRect
.right
);
2004 tmpRect
.bottom
= YLPTODP(pdcattr
, tmpRect
.bottom
);
2006 if (tmpRect
.left
> tmpRect
.right
)
2008 INT tmp
= tmpRect
.left
;
2009 tmpRect
.left
= tmpRect
.right
;
2010 tmpRect
.right
= tmp
;
2012 if (tmpRect
.top
> tmpRect
.bottom
)
2014 INT tmp
= tmpRect
.top
;
2015 tmpRect
.top
= tmpRect
.bottom
;
2016 tmpRect
.bottom
= tmp
;
2019 REGION_UnionRectWithRgn(destObj
, &tmpRect
);
2023 RGNOBJAPI_Unlock(srcObj
);
2024 RGNOBJAPI_Unlock(destObj
);
2032 REGION_AllocRgnWithHandle(INT nReg
)
2037 pReg
= (PROSRGNDATA
)GDIOBJ_AllocateObject(GDIObjType_RGN_TYPE
,
2039 BASEFLAG_LOOKASIDE
);
2042 DPRINT1("Could not allocate a palette.\n");
2046 if (!GDIOBJ_hInsertObject(&pReg
->BaseObject
, GDI_OBJ_HMGR_POWNED
))
2048 DPRINT1("Could not insert palette into handle table.\n");
2049 GDIOBJ_vFreeObject(&pReg
->BaseObject
);
2053 //hReg = pReg->BaseObject.hHmgr;
2055 if (nReg
== 0 || nReg
== 1)
2057 /* Testing shows that > 95% of all regions have only 1 rect.
2058 Including that here saves us from having to do another allocation */
2059 pReg
->Buffer
= &pReg
->rdh
.rcBound
;
2063 pReg
->Buffer
= ExAllocatePoolWithTag(PagedPool
, nReg
* sizeof(RECT
), TAG_REGION
);
2066 DPRINT1("Could not allocate region buffer\n");
2067 GDIOBJ_vDeleteObject(&pReg
->BaseObject
);
2073 pReg
->rdh
.dwSize
= sizeof(RGNDATAHEADER
);
2074 pReg
->rdh
.nCount
= nReg
;
2075 pReg
->rdh
.nRgnSize
= nReg
* sizeof(RECT
);
2076 pReg
->prgnattr
= &pReg
->rgnattr
;
2083 REGION_bAllocRgnAttr(PREGION prgn
)
2088 ppi
= PsGetCurrentProcessWin32Process();
2091 prgnattr
= GdiPoolAllocate(ppi
->pPoolRgnAttr
);
2094 DPRINT1("Could not allocate RGN attr\n");
2098 /* Set the object attribute in the handle table */
2099 prgn
->prgnattr
= prgnattr
;
2100 GDIOBJ_vSetObjectAttr(&prgn
->BaseObject
, prgnattr
);
2107 // Allocate User Space Region Handle.
2111 REGION_AllocUserRgnWithHandle(INT nRgn
)
2115 prgn
= REGION_AllocRgnWithHandle(nRgn
);
2121 if (!REGION_bAllocRgnAttr(prgn
))
2131 REGION_vSyncRegion(PREGION pRgn
)
2133 PRGN_ATTR pRgn_Attr
= NULL
;
2135 if (pRgn
&& pRgn
->prgnattr
!= &pRgn
->rgnattr
)
2137 pRgn_Attr
= GDIOBJ_pvGetObjectAttr(&pRgn
->BaseObject
);
2143 if ( !(pRgn_Attr
->AttrFlags
& ATTR_CACHED
) )
2145 if ( pRgn_Attr
->AttrFlags
& (ATTR_RGN_VALID
|ATTR_RGN_DIRTY
) )
2147 switch (pRgn_Attr
->Flags
)
2150 EMPTY_REGION( pRgn
);
2154 REGION_SetRectRgn( pRgn
,
2155 pRgn_Attr
->Rect
.left
,
2156 pRgn_Attr
->Rect
.top
,
2157 pRgn_Attr
->Rect
.right
,
2158 pRgn_Attr
->Rect
.bottom
);
2161 pRgn_Attr
->AttrFlags
&= ~ATTR_RGN_DIRTY
;
2165 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2177 RGNOBJAPI_Lock(HRGN hRgn
, PRGN_ATTR
*ppRgn_Attr
)
2179 PROSRGNDATA pRgn
= NULL
;
2181 pRgn
= REGION_LockRgn(hRgn
);
2183 REGION_vSyncRegion(pRgn
);
2186 *ppRgn_Attr
= pRgn
->prgnattr
;
2193 RGNOBJAPI_Unlock(PROSRGNDATA pRgn
)
2195 PRGN_ATTR pRgn_Attr
;
2197 if (pRgn
&& GreGetObjectOwner(pRgn
->BaseObject
.hHmgr
) == GDI_OBJ_HMGR_POWNED
)
2199 pRgn_Attr
= GDIOBJ_pvGetObjectAttr(&pRgn
->BaseObject
);
2205 if ( pRgn_Attr
->AttrFlags
& ATTR_RGN_VALID
)
2207 pRgn_Attr
->Flags
= REGION_Complexity( pRgn
);
2208 pRgn_Attr
->Rect
.left
= pRgn
->rdh
.rcBound
.left
;
2209 pRgn_Attr
->Rect
.top
= pRgn
->rdh
.rcBound
.top
;
2210 pRgn_Attr
->Rect
.right
= pRgn
->rdh
.rcBound
.right
;
2211 pRgn_Attr
->Rect
.bottom
= pRgn
->rdh
.rcBound
.bottom
;
2214 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2221 REGION_UnlockRgn(pRgn
);
2226 These regions do not use attribute sections and when allocated, use gdiobj
2230 // System Region Functions
2234 IntSysCreateRectpRgn(INT LeftRect
, INT TopRect
, INT RightRect
, INT BottomRect
)
2238 /* Allocate a region, witout a handle */
2239 prgn
= (PREGION
)GDIOBJ_AllocateObject(GDIObjType_RGN_TYPE
, sizeof(REGION
), 0);
2246 prgn
->Buffer
= &prgn
->rdh
.rcBound
;
2247 prgn
->prgnattr
= &prgn
->rgnattr
;
2248 REGION_SetRectRgn(prgn
, LeftRect
, TopRect
, RightRect
, BottomRect
);
2255 IntSysCreateRectRgn(INT LeftRect
, INT TopRect
, INT RightRect
, INT BottomRect
)
2260 /* Allocate a region, witout a handle */
2261 prgn
= (PREGION
)GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_REGION
, sizeof(REGION
));
2268 prgn
->Buffer
= &prgn
->rdh
.rcBound
;
2269 REGION_SetRectRgn(prgn
, LeftRect
, TopRect
, RightRect
, BottomRect
);
2270 hrgn
= prgn
->BaseObject
.hHmgr
;
2271 prgn
->prgnattr
= &prgn
->rgnattr
;
2273 REGION_UnlockRgn(prgn
);
2279 REGION_vCleanup(PVOID ObjectBody
)
2281 PROSRGNDATA pRgn
= (PROSRGNDATA
)ObjectBody
;
2282 PPROCESSINFO ppi
= PsGetCurrentProcessWin32Process();
2285 ASSERT(pRgn
->prgnattr
);
2286 if (pRgn
->prgnattr
!= &pRgn
->rgnattr
)
2287 GdiPoolFree(ppi
->pPoolRgnAttr
, pRgn
->prgnattr
);
2289 if (pRgn
->Buffer
&& pRgn
->Buffer
!= &pRgn
->rdh
.rcBound
)
2290 ExFreePoolWithTag(pRgn
->Buffer
, TAG_REGION
);
2294 REGION_Delete(PROSRGNDATA pRgn
)
2296 if ( pRgn
== prgnDefault
) return;
2297 GDIOBJ_vDeleteObject(&pRgn
->BaseObject
);
2301 IntGdiReleaseRaoRgn(PDC pDC
)
2303 INT Index
= GDI_HANDLE_GET_INDEX(pDC
->BaseObject
.hHmgr
);
2304 PGDI_TABLE_ENTRY Entry
= &GdiHandleTable
->Entries
[Index
];
2305 pDC
->fs
|= DC_FLAG_DIRTY_RAO
;
2306 Entry
->Flags
|= GDI_ENTRY_VALIDATE_VIS
;
2307 RECTL_vSetEmptyRect(&pDC
->erclClip
);
2308 REGION_Delete(pDC
->prgnRao
);
2309 pDC
->prgnRao
= NULL
;
2313 IntGdiReleaseVisRgn(PDC pDC
)
2315 INT Index
= GDI_HANDLE_GET_INDEX(pDC
->BaseObject
.hHmgr
);
2316 PGDI_TABLE_ENTRY Entry
= &GdiHandleTable
->Entries
[Index
];
2317 pDC
->fs
|= DC_FLAG_DIRTY_RAO
;
2318 Entry
->Flags
|= GDI_ENTRY_VALIDATE_VIS
;
2319 RECTL_vSetEmptyRect(&pDC
->erclClip
);
2320 REGION_Delete(pDC
->prgnVis
);
2321 pDC
->prgnVis
= prgnDefault
;
2325 IntUpdateVisRectRgn(PDC pDC
, PROSRGNDATA pRgn
)
2327 INT Index
= GDI_HANDLE_GET_INDEX(pDC
->BaseObject
.hHmgr
);
2328 PGDI_TABLE_ENTRY Entry
= &GdiHandleTable
->Entries
[Index
];
2332 if (Entry
->Flags
& GDI_ENTRY_VALIDATE_VIS
)
2334 pdcattr
= pDC
->pdcattr
;
2336 pdcattr
->VisRectRegion
.Flags
= REGION_Complexity(pRgn
);
2338 if (pRgn
&& pdcattr
->VisRectRegion
.Flags
!= NULLREGION
)
2340 rcl
.left
= pRgn
->rdh
.rcBound
.left
;
2341 rcl
.top
= pRgn
->rdh
.rcBound
.top
;
2342 rcl
.right
= pRgn
->rdh
.rcBound
.right
;
2343 rcl
.bottom
= pRgn
->rdh
.rcBound
.bottom
;
2345 rcl
.left
-= pDC
->erclWindow
.left
;
2346 rcl
.top
-= pDC
->erclWindow
.top
;
2347 rcl
.right
-= pDC
->erclWindow
.left
;
2348 rcl
.bottom
-= pDC
->erclWindow
.top
;
2351 RECTL_vSetEmptyRect(&rcl
);
2353 pdcattr
->VisRectRegion
.Rect
= rcl
;
2355 Entry
->Flags
&= ~GDI_ENTRY_VALIDATE_VIS
;
2361 IntGdiSetRegionOwner(HRGN hRgn
, DWORD OwnerMask
)
2367 prgn
= REGION_LockRgn(hRgn
);
2373 prgnattr
= GDIOBJ_pvGetObjectAttr(&prgn
->BaseObject
);
2376 GDIOBJ_vSetObjectAttr(&prgn
->BaseObject
, NULL
);
2377 prgn
->prgnattr
= NULL
;
2378 ppi
= PsGetCurrentProcessWin32Process();
2379 GdiPoolFree(ppi
->pPoolRgnAttr
, prgnattr
);
2381 RGNOBJAPI_Unlock(prgn
);
2383 return GreSetObjectOwner(hRgn
, OwnerMask
);
2389 PROSRGNDATA prgnDest
,
2390 PROSRGNDATA prgnSrc1
,
2391 PROSRGNDATA prgnSrc2
,
2397 DPRINT("IntGdiCombineRgn: hDest unavailable\n");
2403 DPRINT("IntGdiCombineRgn: hSrc1 unavailable\n");
2407 if (iCombineMode
== RGN_COPY
)
2409 if (!REGION_CopyRegion(prgnDest
, prgnSrc1
))
2411 return REGION_Complexity(prgnDest
);
2416 DPRINT1("IntGdiCombineRgn requires hSrc2 != NULL for combine mode %d!\n", iCombineMode
);
2421 switch (iCombineMode
)
2424 REGION_IntersectRegion(prgnDest
, prgnSrc1
, prgnSrc2
);
2427 REGION_UnionRegion(prgnDest
, prgnSrc1
, prgnSrc2
);
2430 REGION_XorRegion(prgnDest
, prgnSrc1
, prgnSrc2
);
2433 REGION_SubtractRegion(prgnDest
, prgnSrc1
, prgnSrc2
);
2437 return REGION_Complexity(prgnDest
);
2450 *pRect
= Rgn
->rdh
.rcBound
;
2451 ret
= REGION_Complexity(Rgn
);
2455 return 0; // If invalid region return zero
2467 if (!(Rgn
= RGNOBJAPI_Lock(hRgn
, NULL
)))
2472 ret
= REGION_GetRgnBox(Rgn
, pRect
);
2473 RGNOBJAPI_Unlock(Rgn
);
2487 CLIPOBJ
* ClipRegion
;
2493 if (!dc
) return FALSE
;
2494 pdcattr
= dc
->pdcattr
;
2496 ASSERT(!(pdcattr
->ulDirty_
& (DIRTY_FILL
| DC_BRUSH_DIRTY
)));
2498 if (!(tmpVisRgn
= IntSysCreateRectRgn(0, 0, 0, 0))) return FALSE
;
2500 // Transform region into device co-ords
2501 if (!REGION_LPTODP(dc
, tmpVisRgn
, hRgn
) ||
2502 NtGdiOffsetRgn(tmpVisRgn
, dc
->ptlDCOrig
.x
, dc
->ptlDCOrig
.y
) == ERROR
)
2504 GreDeleteObject(tmpVisRgn
);
2508 visrgn
= RGNOBJAPI_Lock(tmpVisRgn
, NULL
);
2511 GreDeleteObject(tmpVisRgn
);
2516 IntGdiCombineRgn(visrgn
, visrgn
, dc
->prgnRao
, RGN_AND
);
2518 ClipRegion
= IntEngCreateClipRegion(visrgn
->rdh
.nCount
,
2520 &visrgn
->rdh
.rcBound
);
2523 BrushOrigin
.x
= pdcattr
->ptlBrushOrigin
.x
;
2524 BrushOrigin
.y
= pdcattr
->ptlBrushOrigin
.y
;
2525 psurf
= dc
->dclevel
.pSurface
;
2526 /* FIXME: Handle psurf == NULL !!!! */
2528 bRet
= IntEngPaint(&psurf
->SurfObj
,
2530 &dc
->eboFill
.BrushObject
,
2532 0xFFFF); // FIXME: Don't know what to put here
2534 RGNOBJAPI_Unlock(visrgn
);
2535 GreDeleteObject(tmpVisRgn
);
2551 if (prgn
->rdh
.nCount
> 0 && INRECT(prgn
->rdh
.rcBound
, X
, Y
))
2554 for (i
= 0; i
< prgn
->rdh
.nCount
; i
++)
2556 if (INRECT(r
[i
], X
, Y
))
2566 REGION_RectInRegion(
2571 PRECTL pCurRect
, pRectEnd
;
2574 /* Swap the coordinates to make right >= left and bottom >= top */
2575 /* (region building rectangles are normalized the same way) */
2576 if( rect
->top
> rect
->bottom
) {
2577 rc
.top
= rect
->bottom
;
2578 rc
.bottom
= rect
->top
;
2581 rc
.bottom
= rect
->bottom
;
2583 if( rect
->right
< rect
->left
) {
2584 rc
.right
= rect
->left
;
2585 rc
.left
= rect
->right
;
2587 rc
.right
= rect
->right
;
2588 rc
.left
= rect
->left
;
2591 /* This is (just) a useful optimization */
2592 if ((Rgn
->rdh
.nCount
> 0) && EXTENTCHECK(&Rgn
->rdh
.rcBound
, &rc
))
2594 for (pCurRect
= Rgn
->Buffer
, pRectEnd
= pCurRect
+
2595 Rgn
->rdh
.nCount
; pCurRect
< pRectEnd
; pCurRect
++)
2597 if (pCurRect
->bottom
<= rc
.top
)
2598 continue; /* Not far enough down yet */
2600 if (pCurRect
->top
>= rc
.bottom
)
2601 break; /* Too far down */
2603 if (pCurRect
->right
<= rc
.left
)
2604 continue; /* Not far enough over yet */
2606 if (pCurRect
->left
>= rc
.right
) {
2628 if (LeftRect
> RightRect
)
2631 LeftRect
= RightRect
;
2634 if (TopRect
> BottomRect
)
2637 TopRect
= BottomRect
;
2641 if ((LeftRect
!= RightRect
) && (TopRect
!= BottomRect
))
2643 firstRect
= rgn
->Buffer
;
2645 firstRect
->left
= rgn
->rdh
.rcBound
.left
= LeftRect
;
2646 firstRect
->top
= rgn
->rdh
.rcBound
.top
= TopRect
;
2647 firstRect
->right
= rgn
->rdh
.rcBound
.right
= RightRect
;
2648 firstRect
->bottom
= rgn
->rdh
.rcBound
.bottom
= BottomRect
;
2649 rgn
->rdh
.nCount
= 1;
2650 rgn
->rdh
.iType
= RDH_RECTANGLES
;
2665 if (XOffset
|| YOffset
)
2667 int nbox
= rgn
->rdh
.nCount
;
2668 PRECTL pbox
= rgn
->Buffer
;
2674 pbox
->left
+= XOffset
;
2675 pbox
->right
+= XOffset
;
2676 pbox
->top
+= YOffset
;
2677 pbox
->bottom
+= YOffset
;
2680 if (rgn
->Buffer
!= &rgn
->rdh
.rcBound
)
2682 rgn
->rdh
.rcBound
.left
+= XOffset
;
2683 rgn
->rdh
.rcBound
.right
+= XOffset
;
2684 rgn
->rdh
.rcBound
.top
+= YOffset
;
2685 rgn
->rdh
.rcBound
.bottom
+= YOffset
;
2689 return REGION_Complexity(rgn
);
2692 /***********************************************************************
2693 * REGION_InsertEdgeInET
2695 * Insert the given edge into the edge table.
2696 * First we must find the correct bucket in the
2697 * Edge table, then find the right slot in the
2698 * bucket. Finally, we can insert it.
2701 static void FASTCALL
2702 REGION_InsertEdgeInET(
2704 EdgeTableEntry
*ETE
,
2706 ScanLineListBlock
**SLLBlock
,
2710 EdgeTableEntry
*start
, *prev
;
2711 ScanLineList
*pSLL
, *pPrevSLL
;
2712 ScanLineListBlock
*tmpSLLBlock
;
2715 * Find the right bucket to put the edge into
2717 pPrevSLL
= &ET
->scanlines
;
2718 pSLL
= pPrevSLL
->next
;
2719 while (pSLL
&& (pSLL
->scanline
< scanline
))
2726 * Reassign pSLL (pointer to ScanLineList) if necessary
2728 if ((!pSLL
) || (pSLL
->scanline
> scanline
))
2730 if (*iSLLBlock
> SLLSPERBLOCK
-1)
2732 tmpSLLBlock
= ExAllocatePoolWithTag(PagedPool
, sizeof(ScanLineListBlock
), TAG_REGION
);
2735 DPRINT1("REGION_InsertEdgeInETL(): Can't alloc SLLB\n");
2736 /* FIXME: Free resources? */
2739 (*SLLBlock
)->next
= tmpSLLBlock
;
2740 tmpSLLBlock
->next
= (ScanLineListBlock
*)NULL
;
2741 *SLLBlock
= tmpSLLBlock
;
2744 pSLL
= &((*SLLBlock
)->SLLs
[(*iSLLBlock
)++]);
2746 pSLL
->next
= pPrevSLL
->next
;
2747 pSLL
->edgelist
= (EdgeTableEntry
*)NULL
;
2748 pPrevSLL
->next
= pSLL
;
2750 pSLL
->scanline
= scanline
;
2753 * Now insert the edge in the right bucket
2755 prev
= (EdgeTableEntry
*)NULL
;
2756 start
= pSLL
->edgelist
;
2757 while (start
&& (start
->bres
.minor_axis
< ETE
->bres
.minor_axis
))
2760 start
= start
->next
;
2767 pSLL
->edgelist
= ETE
;
2770 /***********************************************************************
2773 * This routine moves EdgeTableEntries from the
2774 * EdgeTable into the Active Edge Table,
2775 * leaving them sorted by smaller x coordinate.
2778 static void FASTCALL
2780 EdgeTableEntry
*AET
,
2781 EdgeTableEntry
*ETEs
2784 EdgeTableEntry
*pPrevAET
;
2785 EdgeTableEntry
*tmp
;
2791 while (AET
&& (AET
->bres
.minor_axis
< ETEs
->bres
.minor_axis
))
2800 ETEs
->back
= pPrevAET
;
2801 pPrevAET
->next
= ETEs
;
2808 /***********************************************************************
2809 * REGION_computeWAET
2811 * This routine links the AET by the
2812 * nextWETE (winding EdgeTableEntry) link for
2813 * use by the winding number rule. The final
2814 * Active Edge Table (AET) might look something
2818 * ---------- --------- ---------
2819 * |ymax | |ymax | |ymax |
2820 * | ... | |... | |... |
2821 * |next |->|next |->|next |->...
2822 * |nextWETE| |nextWETE| |nextWETE|
2823 * --------- --------- ^--------
2825 * V-------------------> V---> ...
2828 static void FASTCALL
2829 REGION_computeWAET(EdgeTableEntry
*AET
)
2831 register EdgeTableEntry
*pWETE
;
2832 register int inside
= 1;
2833 register int isInside
= 0;
2835 AET
->nextWETE
= (EdgeTableEntry
*)NULL
;
2845 if ( (!inside
&& !isInside
) ||
2846 ( inside
&& isInside
) )
2848 pWETE
->nextWETE
= AET
;
2854 pWETE
->nextWETE
= (EdgeTableEntry
*)NULL
;
2857 /***********************************************************************
2858 * REGION_InsertionSort
2860 * Just a simple insertion sort using
2861 * pointers and back pointers to sort the Active
2865 static BOOL FASTCALL
2866 REGION_InsertionSort(EdgeTableEntry
*AET
)
2868 EdgeTableEntry
*pETEchase
;
2869 EdgeTableEntry
*pETEinsert
;
2870 EdgeTableEntry
*pETEchaseBackTMP
;
2871 BOOL changed
= FALSE
;
2878 while (pETEchase
->back
->bres
.minor_axis
> AET
->bres
.minor_axis
)
2879 pETEchase
= pETEchase
->back
;
2882 if (pETEchase
!= pETEinsert
)
2884 pETEchaseBackTMP
= pETEchase
->back
;
2885 pETEinsert
->back
->next
= AET
;
2887 AET
->back
= pETEinsert
->back
;
2888 pETEinsert
->next
= pETEchase
;
2889 pETEchase
->back
->next
= pETEinsert
;
2890 pETEchase
->back
= pETEinsert
;
2891 pETEinsert
->back
= pETEchaseBackTMP
;
2898 /***********************************************************************
2899 * REGION_FreeStorage
2903 static void FASTCALL
2904 REGION_FreeStorage(ScanLineListBlock
*pSLLBlock
)
2906 ScanLineListBlock
*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.
2924 int numFullPtBlocks
,
2926 POINTBLOCK
*FirstPtBlock
,
2931 POINTBLOCK
*CurPtBlock
;
2933 RECTL
*extents
, *temp
;
2936 extents
= ®
->rdh
.rcBound
;
2938 numRects
= ((numFullPtBlocks
* NUMPTSTOBUFFER
) + iCurPtBlock
) >> 1;
2940 /* Make sure, we have at least one rect */
2946 if (!(temp
= ExAllocatePoolWithTag(PagedPool
, numRects
* sizeof(RECT
), TAG_REGION
)))
2950 if (reg
->Buffer
!= NULL
)
2952 COPY_RECTS(temp
, reg
->Buffer
, reg
->rdh
.nCount
);
2953 if (reg
->Buffer
!= ®
->rdh
.rcBound
)
2954 ExFreePoolWithTag(reg
->Buffer
, TAG_REGION
);
2958 reg
->rdh
.nCount
= numRects
;
2959 CurPtBlock
= FirstPtBlock
;
2960 rects
= reg
->Buffer
- 1;
2962 extents
->left
= LARGE_COORDINATE
, extents
->right
= SMALL_COORDINATE
;
2964 for ( ; numFullPtBlocks
>= 0; numFullPtBlocks
--)
2966 /* The loop uses 2 points per iteration */
2967 i
= NUMPTSTOBUFFER
>> 1;
2968 if (!numFullPtBlocks
)
2969 i
= iCurPtBlock
>> 1;
2970 for (pts
= CurPtBlock
->pts
; i
--; pts
+= 2)
2972 if (pts
->x
== pts
[1].x
)
2974 if (numRects
&& pts
->x
== rects
->left
&& pts
->y
== rects
->bottom
&&
2975 pts
[1].x
== rects
->right
&&
2976 (numRects
== 1 || rects
[-1].top
!= rects
->top
) &&
2977 (i
&& pts
[2].y
> pts
[1].y
))
2979 rects
->bottom
= pts
[1].y
+ 1;
2984 rects
->left
= pts
->x
;
2985 rects
->top
= pts
->y
;
2986 rects
->right
= pts
[1].x
;
2987 rects
->bottom
= pts
[1].y
+ 1;
2988 if (rects
->left
< extents
->left
)
2989 extents
->left
= rects
->left
;
2990 if (rects
->right
> extents
->right
)
2991 extents
->right
= rects
->right
;
2993 CurPtBlock
= CurPtBlock
->next
;
2998 extents
->top
= reg
->Buffer
->top
;
2999 extents
->bottom
= rects
->bottom
;
3006 extents
->bottom
= 0;
3008 reg
->rdh
.nCount
= numRects
;
3013 /***********************************************************************
3014 * REGION_CreateEdgeTable
3016 * This routine creates the edge table for
3017 * scan converting polygons.
3018 * The Edge Table (ET) looks like:
3022 * | ymax | ScanLineLists
3023 * |scanline|-->------------>-------------->...
3024 * -------- |scanline| |scanline|
3025 * |edgelist| |edgelist|
3026 * --------- ---------
3030 * list of ETEs list of ETEs
3032 * where ETE is an EdgeTableEntry data structure,
3033 * and there is one ScanLineList per scanline at
3034 * which an edge is initially entered.
3037 static void FASTCALL
3038 REGION_CreateETandAET(
3043 EdgeTableEntry
*AET
,
3044 EdgeTableEntry
*pETEs
,
3045 ScanLineListBlock
*pSLLBlock
3048 const POINT
*top
, *bottom
;
3049 const POINT
*PrevPt
, *CurrPt
, *EndPt
;
3056 * Initialize the Active Edge Table
3058 AET
->next
= (EdgeTableEntry
*)NULL
;
3059 AET
->back
= (EdgeTableEntry
*)NULL
;
3060 AET
->nextWETE
= (EdgeTableEntry
*)NULL
;
3061 AET
->bres
.minor_axis
= SMALL_COORDINATE
;
3064 * Initialize the Edge Table.
3066 ET
->scanlines
.next
= (ScanLineList
*)NULL
;
3067 ET
->ymax
= SMALL_COORDINATE
;
3068 ET
->ymin
= LARGE_COORDINATE
;
3069 pSLLBlock
->next
= (ScanLineListBlock
*)NULL
;
3072 for (poly
= 0; poly
< nbpolygons
; poly
++)
3074 count
= Count
[poly
];
3082 * For each vertex in the array of points.
3083 * In this loop we are dealing with two vertices at
3084 * a time -- these make up one edge of the polygon.
3091 * Find out which point is above and which is below.
3093 if (PrevPt
->y
> CurrPt
->y
)
3095 bottom
= PrevPt
, top
= CurrPt
;
3096 pETEs
->ClockWise
= 0;
3100 bottom
= CurrPt
, top
= PrevPt
;
3101 pETEs
->ClockWise
= 1;
3105 * Don't add horizontal edges to the Edge table.
3107 if (bottom
->y
!= top
->y
)
3109 pETEs
->ymax
= bottom
->y
-1;
3110 /* -1 so we don't get last scanline */
3113 * Initialize integer edge algorithm
3115 dy
= bottom
->y
- top
->y
;
3116 BRESINITPGONSTRUCT(dy
, top
->x
, bottom
->x
, pETEs
->bres
);
3118 REGION_InsertEdgeInET(ET
, pETEs
, top
->y
, &pSLLBlock
,
3121 if (PrevPt
->y
> ET
->ymax
)
3122 ET
->ymax
= PrevPt
->y
;
3123 if (PrevPt
->y
< ET
->ymin
)
3124 ET
->ymin
= PrevPt
->y
;
3134 IntCreatePolyPolygonRgn(
3143 EdgeTableEntry
*pAET
; /* Active Edge Table */
3144 INT y
; /* Current scanline */
3145 int iPts
= 0; /* Number of pts in buffer */
3146 EdgeTableEntry
*pWETE
; /* Winding Edge Table Entry */
3147 ScanLineList
*pSLL
; /* Current scanLineList */
3148 POINT
*pts
; /* Output buffer */
3149 EdgeTableEntry
*pPrevAET
; /* Pointer to previous AET */
3150 EdgeTable ET
; /* Header node for ET */
3151 EdgeTableEntry AET
; /* Header node for AET */
3152 EdgeTableEntry
*pETEs
; /* EdgeTableEntries pool */
3153 ScanLineListBlock SLLBlock
; /* Header for scanlinelist */
3154 int fixWAET
= FALSE
;
3155 POINTBLOCK FirstPtBlock
, *curPtBlock
; /* PtBlock buffers */
3156 POINTBLOCK
*tmpPtBlock
;
3157 int numFullPtBlocks
= 0;
3160 if (mode
== 0 || mode
> 2) return 0;
3162 if (!(region
= REGION_AllocUserRgnWithHandle(nbpolygons
)))
3164 hrgn
= region
->BaseObject
.hHmgr
;
3166 /* Special case a rectangle */
3168 if (((nbpolygons
== 1) && ((*Count
== 4) ||
3169 ((*Count
== 5) && (Pts
[4].x
== Pts
[0].x
) && (Pts
[4].y
== Pts
[0].y
)))) &&
3170 (((Pts
[0].y
== Pts
[1].y
) &&
3171 (Pts
[1].x
== Pts
[2].x
) &&
3172 (Pts
[2].y
== Pts
[3].y
) &&
3173 (Pts
[3].x
== Pts
[0].x
)) ||
3174 ((Pts
[0].x
== Pts
[1].x
) &&
3175 (Pts
[1].y
== Pts
[2].y
) &&
3176 (Pts
[2].x
== Pts
[3].x
) &&
3177 (Pts
[3].y
== Pts
[0].y
))))
3179 RGNOBJAPI_Unlock(region
);
3180 NtGdiSetRectRgn(hrgn
, min(Pts
[0].x
, Pts
[2].x
), min(Pts
[0].y
, Pts
[2].y
),
3181 max(Pts
[0].x
, Pts
[2].x
), max(Pts
[0].y
, Pts
[2].y
));
3185 for (poly
= total
= 0; poly
< nbpolygons
; poly
++)
3186 total
+= Count
[poly
];
3187 if (! (pETEs
= ExAllocatePoolWithTag(PagedPool
, sizeof(EdgeTableEntry
) * total
, TAG_REGION
)) )
3189 GreDeleteObject(hrgn
);
3192 pts
= FirstPtBlock
.pts
;
3193 REGION_CreateETandAET(Count
, nbpolygons
, Pts
, &ET
, &AET
, pETEs
, &SLLBlock
);
3194 pSLL
= ET
.scanlines
.next
;
3195 curPtBlock
= &FirstPtBlock
;
3197 if (mode
!= WINDING
)
3202 for (y
= ET
.ymin
; y
< ET
.ymax
; y
++)
3205 * Add a new edge to the active edge table when we
3206 * get to the next edge.
3208 if (pSLL
!= NULL
&& y
== pSLL
->scanline
)
3210 REGION_loadAET(&AET
, pSLL
->edgelist
);
3217 * For each active edge
3221 pts
->x
= pAET
->bres
.minor_axis
, pts
->y
= y
;
3225 * Send out the buffer
3227 if (iPts
== NUMPTSTOBUFFER
)
3229 tmpPtBlock
= ExAllocatePoolWithTag(PagedPool
, sizeof(POINTBLOCK
), TAG_REGION
);
3232 DPRINT1("Can't alloc tPB\n");
3233 ExFreePoolWithTag(pETEs
, TAG_REGION
);
3236 curPtBlock
->next
= tmpPtBlock
;
3237 curPtBlock
= tmpPtBlock
;
3238 pts
= curPtBlock
->pts
;
3242 EVALUATEEDGEEVENODD(pAET
, pPrevAET
, y
);
3244 REGION_InsertionSort(&AET
);
3252 for (y
= ET
.ymin
; y
< ET
.ymax
; y
++)
3255 * Add a new edge to the active edge table when we
3256 * get to the next edge.
3258 if (pSLL
!= NULL
&& y
== pSLL
->scanline
)
3260 REGION_loadAET(&AET
, pSLL
->edgelist
);
3261 REGION_computeWAET(&AET
);
3269 * For each active edge
3274 * Add to the buffer only those edges that
3275 * are in the Winding active edge table.
3279 pts
->x
= pAET
->bres
.minor_axis
, pts
->y
= y
;
3283 * Send out the buffer
3285 if (iPts
== NUMPTSTOBUFFER
)
3287 tmpPtBlock
= ExAllocatePoolWithTag(PagedPool
,
3288 sizeof(POINTBLOCK
), TAG_REGION
);
3291 DPRINT1("Can't alloc tPB\n");
3292 ExFreePoolWithTag(pETEs
, TAG_REGION
);
3293 GreDeleteObject(hrgn
);
3296 curPtBlock
->next
= tmpPtBlock
;
3297 curPtBlock
= tmpPtBlock
;
3298 pts
= curPtBlock
->pts
;
3302 pWETE
= pWETE
->nextWETE
;
3304 EVALUATEEDGEWINDING(pAET
, pPrevAET
, y
, fixWAET
);
3308 * Recompute the winding active edge table if
3309 * we just resorted or have exited an edge.
3311 if (REGION_InsertionSort(&AET
) || fixWAET
)
3313 REGION_computeWAET(&AET
);
3318 REGION_FreeStorage(SLLBlock
.next
);
3319 REGION_PtsToRegion(numFullPtBlocks
, iPts
, &FirstPtBlock
, region
);
3321 for (curPtBlock
= FirstPtBlock
.next
; --numFullPtBlocks
>= 0;)
3323 tmpPtBlock
= curPtBlock
->next
;
3324 ExFreePoolWithTag(curPtBlock
, TAG_REGION
);
3325 curPtBlock
= tmpPtBlock
;
3327 ExFreePoolWithTag(pETEs
, TAG_REGION
);
3328 RGNOBJAPI_Unlock(region
);
3342 if (!(Rgn
= RGNOBJAPI_Lock(hRgn
, NULL
)))
3347 Ret
= REGION_RectInRegion(Rgn
, rc
);
3348 RGNOBJAPI_Unlock(Rgn
);
3354 // NtGdi Exported Functions