2 * GDI region objects. Shamelessly ripped out from the X11 distribution
3 * Thanks for the nice licence.
5 * Copyright 1993, 1994, 1995 Alexandre Julliard
6 * Modifications and additions: Copyright 1998 Huw Davies
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 /************************************************************************
26 Copyright (c) 1987, 1988 X Consortium
28 Permission is hereby granted, free of charge, to any person obtaining a copy
29 of this software and associated documentation files (the "Software"), to deal
30 in the Software without restriction, including without limitation the rights
31 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
32 copies of the Software, and to permit persons to whom the Software is
33 furnished to do so, subject to the following conditions:
35 The above copyright notice and this permission notice shall be included in
36 all copies or substantial portions of the Software.
38 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
40 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
41 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
42 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
43 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
45 Except as contained in this notice, the name of the X Consortium shall not be
46 used in advertising or otherwise to promote the sale, use or other dealings
47 in this Software without prior written authorization from the X Consortium.
50 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
54 Permission to use, copy, modify, and distribute this software and its
55 documentation for any purpose and without fee is hereby granted,
56 provided that the above copyright notice appear in all copies and that
57 both that copyright notice and this permission notice appear in
58 supporting documentation, and that the name of Digital not be
59 used in advertising or publicity pertaining to distribution of the
60 software without specific, written prior permission.
62 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
63 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
64 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
65 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
66 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
67 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
70 ************************************************************************/
72 * The functions in this file implement the Region abstraction, similar to one
73 * used in the X11 sample server. A Region is simply an area, as the name
74 * implies, and is implemented as a "y-x-banded" array of rectangles. To
75 * explain: Each Region is made up of a certain number of rectangles sorted
76 * by y coordinate first, and then by x coordinate.
78 * Furthermore, the rectangles are banded such that every rectangle with a
79 * given upper-left y coordinate (y1) will have the same lower-right y
80 * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
81 * will span the entire vertical distance of the band. This means that some
82 * areas that could be merged into a taller rectangle will be represented as
83 * several shorter rectangles to account for shorter rectangles to its left
84 * or right but within its "vertical scope".
86 * An added constraint on the rectangles is that they must cover as much
87 * horizontal area as possible. E.g. no two rectangles in a band are allowed
90 * Whenever possible, bands will be merged together to cover a greater vertical
91 * distance (and thus reduce the number of rectangles). Two bands can be merged
92 * only if the bottom of one touches the top of the other and they have
93 * rectangles in the same places (of the same width, of course). This maintains
94 * the y-x-banding that's so nice to have...
102 #include "wine/debug.h"
104 WINE_DEFAULT_DEBUG_CHANNEL(region);
113 /* GDI logical region object */
121 static HGDIOBJ REGION_SelectObject( HGDIOBJ handle, void *obj, HDC hdc );
122 static BOOL REGION_DeleteObject( HGDIOBJ handle, void *obj );
124 static const struct gdi_obj_funcs region_funcs =
126 REGION_SelectObject, /* pSelectObject */
127 NULL, /* pGetObject16 */
128 NULL, /* pGetObjectA */
129 NULL, /* pGetObjectW */
130 NULL, /* pUnrealizeObject */
131 REGION_DeleteObject /* pDeleteObject */
134 /* 1 if two RECTs overlap.
135 * 0 if two RECTs do not overlap.
137 #define EXTENTCHECK(r1, r2) \
138 ((r1)->right > (r2)->left && \
139 (r1)->left < (r2)->right && \
140 (r1)->bottom > (r2)->top && \
141 (r1)->top < (r2)->bottom)
144 * Check to see if there is enough memory in the present region.
147 static inline int xmemcheck(WINEREGION *reg, LPRECT *rect, LPRECT *firstrect ) {
148 if (reg->numRects >= (reg->size - 1)) {
149 *firstrect = HeapReAlloc( GetProcessHeap(), 0, *firstrect, (2 * (sizeof(RECT)) * (reg->size)));
153 *rect = (*firstrect)+reg->numRects;
158 #define MEMCHECK(reg, rect, firstrect) xmemcheck(reg,&(rect),&(firstrect))
160 #define EMPTY_REGION(pReg) { \
161 (pReg)->numRects = 0; \
162 (pReg)->extents.left = (pReg)->extents.top = 0; \
163 (pReg)->extents.right = (pReg)->extents.bottom = 0; \
166 #define REGION_NOT_EMPTY(pReg) pReg->numRects
168 #define INRECT(r, x, y) \
169 ( ( ((r).right > x)) && \
170 ( ((r).left <= x)) && \
171 ( ((r).bottom > y)) && \
176 * number of points to buffer before sending them off
177 * to scanlines() : Must be an even number
179 #define NUMPTSTOBUFFER 200
182 * used to allocate buffers for points and link
183 * the buffers together
186 typedef struct _POINTBLOCK {
187 POINT pts[NUMPTSTOBUFFER];
188 struct _POINTBLOCK *next;
194 * This file contains a few macros to help track
195 * the edge of a filled object. The object is assumed
196 * to be filled in scanline order, and thus the
197 * algorithm used is an extension of Bresenham's line
198 * drawing algorithm which assumes that y is always the
200 * Since these pieces of code are the same for any filled shape,
201 * it is more convenient to gather the library in one
202 * place, but since these pieces of code are also in
203 * the inner loops of output primitives, procedure call
204 * overhead is out of the question.
205 * See the author for a derivation if needed.
210 * In scan converting polygons, we want to choose those pixels
211 * which are inside the polygon. Thus, we add .5 to the starting
212 * x coordinate for both left and right edges. Now we choose the
213 * first pixel which is inside the pgon for the left edge and the
214 * first pixel which is outside the pgon for the right edge.
215 * Draw the left pixel, but not the right.
217 * How to add .5 to the starting x coordinate:
218 * If the edge is moving to the right, then subtract dy from the
219 * error term from the general form of the algorithm.
220 * If the edge is moving to the left, then add dy to the error term.
222 * The reason for the difference between edges moving to the left
223 * and edges moving to the right is simple: If an edge is moving
224 * to the right, then we want the algorithm to flip immediately.
225 * If it is moving to the left, then we don't want it to flip until
226 * we traverse an entire pixel.
228 #define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \
229 int dx; /* local storage */ \
232 * if the edge is horizontal, then it is ignored \
233 * and assumed not to be processed. Otherwise, do this stuff. \
237 dx = (x2) - xStart; \
241 incr1 = -2 * dx + 2 * (dy) * m1; \
242 incr2 = -2 * dx + 2 * (dy) * m; \
243 d = 2 * m * (dy) - 2 * dx - 2 * (dy); \
247 incr1 = 2 * dx - 2 * (dy) * m1; \
248 incr2 = 2 * dx - 2 * (dy) * m; \
249 d = -2 * m * (dy) + 2 * dx; \
254 #define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \
277 * This structure contains all of the information needed
278 * to run the bresenham algorithm.
279 * The variables may be hardcoded into the declarations
280 * instead of using this structure to make use of
281 * register declarations.
284 INT minor_axis; /* minor axis */
285 INT d; /* decision variable */
286 INT m, m1; /* slope and slope+1 */
287 INT incr1, incr2; /* error increments */
291 #define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \
292 BRESINITPGON(dmaj, min1, min2, bres.minor_axis, bres.d, \
293 bres.m, bres.m1, bres.incr1, bres.incr2)
295 #define BRESINCRPGONSTRUCT(bres) \
296 BRESINCRPGON(bres.d, bres.minor_axis, bres.m, bres.m1, bres.incr1, bres.incr2)
301 * These are the data structures needed to scan
302 * convert regions. Two different scan conversion
303 * methods are available -- the even-odd method, and
304 * the winding number method.
305 * The even-odd rule states that a point is inside
306 * the polygon if a ray drawn from that point in any
307 * direction will pass through an odd number of
309 * By the winding number rule, a point is decided
310 * to be inside the polygon if a ray drawn from that
311 * point in any direction passes through a different
312 * number of clockwise and counter-clockwise path
315 * These data structures are adapted somewhat from
316 * the algorithm in (Foley/Van Dam) for scan converting
318 * The basic algorithm is to start at the top (smallest y)
319 * of the polygon, stepping down to the bottom of
320 * the polygon by incrementing the y coordinate. We
321 * keep a list of edges which the current scanline crosses,
322 * sorted by x. This list is called the Active Edge Table (AET)
323 * As we change the y-coordinate, we update each entry in
324 * in the active edge table to reflect the edges new xcoord.
325 * This list must be sorted at each scanline in case
326 * two edges intersect.
327 * We also keep a data structure known as the Edge Table (ET),
328 * which keeps track of all the edges which the current
329 * scanline has not yet reached. The ET is basically a
330 * list of ScanLineList structures containing a list of
331 * edges which are entered at a given scanline. There is one
332 * ScanLineList per scanline at which an edge is entered.
333 * When we enter a new edge, we move it from the ET to the AET.
335 * From the AET, we can implement the even-odd rule as in
337 * The winding number rule is a little trickier. We also
338 * keep the EdgeTableEntries in the AET linked by the
339 * nextWETE (winding EdgeTableEntry) link. This allows
340 * the edges to be linked just as before for updating
341 * purposes, but only uses the edges linked by the nextWETE
342 * link as edges representing spans of the polygon to
343 * drawn (as with the even-odd rule).
347 * for the winding number rule
350 #define COUNTERCLOCKWISE -1
352 typedef struct _EdgeTableEntry {
353 INT ymax; /* ycoord at which we exit this edge. */
354 BRESINFO bres; /* Bresenham info to run the edge */
355 struct _EdgeTableEntry *next; /* next in the list */
356 struct _EdgeTableEntry *back; /* for insertion sort */
357 struct _EdgeTableEntry *nextWETE; /* for winding num rule */
358 int ClockWise; /* flag for winding number rule */
362 typedef struct _ScanLineList{
363 INT scanline; /* the scanline represented */
364 EdgeTableEntry *edgelist; /* header node */
365 struct _ScanLineList *next; /* next in the list */
370 INT ymax; /* ymax for the polygon */
371 INT ymin; /* ymin for the polygon */
372 ScanLineList scanlines; /* header node */
377 * Here is a struct to help with storage allocation
378 * so we can allocate a big chunk at a time, and then take
379 * pieces from this heap when we need to.
381 #define SLLSPERBLOCK 25
383 typedef struct _ScanLineListBlock {
384 ScanLineList SLLs[SLLSPERBLOCK];
385 struct _ScanLineListBlock *next;
391 * a few macros for the inner loops of the fill code where
392 * performance considerations don't allow a procedure call.
394 * Evaluate the given edge at the given scanline.
395 * If the edge has expired, then we leave it and fix up
396 * the active edge table; otherwise, we increment the
397 * x value to be ready for the next scanline.
398 * The winding number rule is in effect, so we must notify
399 * the caller when the edge has been removed so he
400 * can reorder the Winding Active Edge Table.
402 #define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \
403 if (pAET->ymax == y) { /* leaving this edge */ \
404 pPrevAET->next = pAET->next; \
405 pAET = pPrevAET->next; \
408 pAET->back = pPrevAET; \
411 BRESINCRPGONSTRUCT(pAET->bres); \
419 * Evaluate the given edge at the given scanline.
420 * If the edge has expired, then we leave it and fix up
421 * the active edge table; otherwise, we increment the
422 * x value to be ready for the next scanline.
423 * The even-odd rule is in effect.
425 #define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \
426 if (pAET->ymax == y) { /* leaving this edge */ \
427 pPrevAET->next = pAET->next; \
428 pAET = pPrevAET->next; \
430 pAET->back = pPrevAET; \
433 BRESINCRPGONSTRUCT(pAET->bres); \
439 typedef void (*voidProcp)();
441 /* Note the parameter order is different from the X11 equivalents */
443 static void REGION_CopyRegion(WINEREGION *d, WINEREGION *s);
444 static void REGION_IntersectRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
445 static void REGION_UnionRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
446 static void REGION_SubtractRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
447 static void REGION_XorRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
448 static void REGION_UnionRectWithRegion(const RECT *rect, WINEREGION *rgn);
450 #define RGN_DEFAULT_RECTS 2
453 /***********************************************************************
456 inline static INT get_region_type( const RGNOBJ *obj )
458 switch(obj->rgn->numRects)
460 case 0: return NULLREGION;
461 case 1: return SIMPLEREGION;
462 default: return COMPLEXREGION;
467 /***********************************************************************
469 * Outputs the contents of a WINEREGION
471 static void REGION_DumpRegion(WINEREGION *pReg)
473 RECT *pRect, *pRectEnd = pReg->rects + pReg->numRects;
475 TRACE("Region %p: %d,%d - %d,%d %d rects\n", pReg,
476 pReg->extents.left, pReg->extents.top,
477 pReg->extents.right, pReg->extents.bottom, pReg->numRects);
478 for(pRect = pReg->rects; pRect < pRectEnd; pRect++)
479 TRACE("\t%d,%d - %d,%d\n", pRect->left, pRect->top,
480 pRect->right, pRect->bottom);
485 /***********************************************************************
486 * REGION_AllocWineRegion
487 * Create a new empty WINEREGION.
489 static WINEREGION *REGION_AllocWineRegion( INT n )
493 if ((pReg = HeapAlloc(GetProcessHeap(), 0, sizeof( WINEREGION ))))
495 if ((pReg->rects = HeapAlloc(GetProcessHeap(), 0, n * sizeof( RECT ))))
501 HeapFree(GetProcessHeap(), 0, pReg);
507 /***********************************************************************
508 * REGION_CreateRegion
509 * Create a new empty region.
511 static HRGN REGION_CreateRegion( INT n )
516 if(!(obj = GDI_AllocObject( sizeof(RGNOBJ), REGION_MAGIC, (HGDIOBJ *)&hrgn,
517 ®ion_funcs ))) return 0;
518 if(!(obj->rgn = REGION_AllocWineRegion(n))) {
519 GDI_FreeObject( hrgn, obj );
522 GDI_ReleaseObj( hrgn );
526 /***********************************************************************
527 * REGION_DestroyWineRegion
529 static void REGION_DestroyWineRegion( WINEREGION* pReg )
531 HeapFree( GetProcessHeap(), 0, pReg->rects );
532 HeapFree( GetProcessHeap(), 0, pReg );
535 /***********************************************************************
536 * REGION_DeleteObject
538 static BOOL REGION_DeleteObject( HGDIOBJ handle, void *obj )
542 TRACE(" %04x\n", handle );
544 REGION_DestroyWineRegion( rgn->rgn );
545 return GDI_FreeObject( handle, obj );
548 /***********************************************************************
549 * REGION_SelectObject
551 static HGDIOBJ REGION_SelectObject( HGDIOBJ handle, void *obj, HDC hdc )
553 return (HGDIOBJ)SelectClipRgn( hdc, handle );
557 /***********************************************************************
558 * OffsetRgn (GDI32.@)
560 INT WINAPI OffsetRgn( HRGN hrgn, INT x, INT y )
562 RGNOBJ * obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
565 TRACE("%04x %d,%d\n", hrgn, x, y);
571 int nbox = obj->rgn->numRects;
572 RECT *pbox = obj->rgn->rects;
582 obj->rgn->extents.left += x;
583 obj->rgn->extents.right += x;
584 obj->rgn->extents.top += y;
585 obj->rgn->extents.bottom += y;
588 ret = get_region_type( obj );
589 GDI_ReleaseObj( hrgn );
594 /***********************************************************************
595 * GetRgnBox (GDI32.@)
597 INT WINAPI GetRgnBox( HRGN hrgn, LPRECT rect )
599 RGNOBJ * obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
603 TRACE(" %04x\n", hrgn );
604 rect->left = obj->rgn->extents.left;
605 rect->top = obj->rgn->extents.top;
606 rect->right = obj->rgn->extents.right;
607 rect->bottom = obj->rgn->extents.bottom;
608 ret = get_region_type( obj );
609 GDI_ReleaseObj(hrgn);
616 /***********************************************************************
617 * CreateRectRgn (GDI32.@)
619 HRGN WINAPI CreateRectRgn(INT left, INT top, INT right, INT bottom)
623 /* Allocate 2 rects by default to reduce the number of reallocs */
625 if (!(hrgn = REGION_CreateRegion(RGN_DEFAULT_RECTS)))
628 SetRectRgn(hrgn, left, top, right, bottom);
633 /***********************************************************************
634 * CreateRectRgnIndirect (GDI32.@)
636 HRGN WINAPI CreateRectRgnIndirect( const RECT* rect )
638 return CreateRectRgn( rect->left, rect->top, rect->right, rect->bottom );
642 /***********************************************************************
643 * SetRectRgn (GDI32.@)
645 * Allows either or both left and top to be greater than right or bottom.
647 BOOL WINAPI SetRectRgn( HRGN hrgn, INT left, INT top,
648 INT right, INT bottom )
652 TRACE(" %04x %d,%d-%d,%d\n",
653 hrgn, left, top, right, bottom );
655 if (!(obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC ))) return FALSE;
657 if (left > right) { INT tmp = left; left = right; right = tmp; }
658 if (top > bottom) { INT tmp = top; top = bottom; bottom = tmp; }
660 if((left != right) && (top != bottom))
662 obj->rgn->rects->left = obj->rgn->extents.left = left;
663 obj->rgn->rects->top = obj->rgn->extents.top = top;
664 obj->rgn->rects->right = obj->rgn->extents.right = right;
665 obj->rgn->rects->bottom = obj->rgn->extents.bottom = bottom;
666 obj->rgn->numRects = 1;
669 EMPTY_REGION(obj->rgn);
671 GDI_ReleaseObj( hrgn );
676 /***********************************************************************
677 * CreateRoundRectRgn (GDI32.@)
679 HRGN WINAPI CreateRoundRectRgn( INT left, INT top,
680 INT right, INT bottom,
681 INT ellipse_width, INT ellipse_height )
685 int asq, bsq, d, xd, yd;
688 /* Make the dimensions sensible */
690 if (left > right) { INT tmp = left; left = right; right = tmp; }
691 if (top > bottom) { INT tmp = top; top = bottom; bottom = tmp; }
693 ellipse_width = abs(ellipse_width);
694 ellipse_height = abs(ellipse_height);
696 /* Check parameters */
698 if (ellipse_width > right-left) ellipse_width = right-left;
699 if (ellipse_height > bottom-top) ellipse_height = bottom-top;
701 /* Check if we can do a normal rectangle instead */
703 if ((ellipse_width < 2) || (ellipse_height < 2))
704 return CreateRectRgn( left, top, right, bottom );
708 d = (ellipse_height < 128) ? ((3 * ellipse_height) >> 2) : 64;
709 if (!(hrgn = REGION_CreateRegion(d))) return 0;
710 if (!(obj = GDI_GetObjPtr( hrgn, REGION_MAGIC ))) return 0;
711 TRACE("(%d,%d-%d,%d %dx%d): ret=%04x\n",
712 left, top, right, bottom, ellipse_width, ellipse_height, hrgn );
714 /* Ellipse algorithm, based on an article by K. Porter */
715 /* in DDJ Graphics Programming Column, 8/89 */
717 asq = ellipse_width * ellipse_width / 4; /* a^2 */
718 bsq = ellipse_height * ellipse_height / 4; /* b^2 */
719 d = bsq - asq * ellipse_height / 2 + asq / 4; /* b^2 - a^2b + a^2/4 */
721 yd = asq * ellipse_height; /* 2a^2b */
723 rect.left = left + ellipse_width / 2;
724 rect.right = right - ellipse_width / 2;
726 /* Loop to draw first half of quadrant */
730 if (d > 0) /* if nearest pixel is toward the center */
732 /* move toward center */
734 rect.bottom = rect.top + 1;
735 REGION_UnionRectWithRegion( &rect, obj->rgn );
737 rect.bottom = rect.top + 1;
738 REGION_UnionRectWithRegion( &rect, obj->rgn );
742 rect.left--; /* next horiz point */
748 /* Loop to draw second half of quadrant */
750 d += (3 * (asq-bsq) / 2 - (xd+yd)) / 2;
753 /* next vertical point */
755 rect.bottom = rect.top + 1;
756 REGION_UnionRectWithRegion( &rect, obj->rgn );
758 rect.bottom = rect.top + 1;
759 REGION_UnionRectWithRegion( &rect, obj->rgn );
760 if (d < 0) /* if nearest pixel is outside ellipse */
762 rect.left--; /* move away from center */
771 /* Add the inside rectangle */
776 rect.bottom = bottom;
777 REGION_UnionRectWithRegion( &rect, obj->rgn );
779 GDI_ReleaseObj( hrgn );
784 /***********************************************************************
785 * CreateEllipticRgn (GDI32.@)
787 HRGN WINAPI CreateEllipticRgn( INT left, INT top,
788 INT right, INT bottom )
790 return CreateRoundRectRgn( left, top, right, bottom,
791 right-left, bottom-top );
795 /***********************************************************************
796 * CreateEllipticRgnIndirect (GDI32.@)
798 HRGN WINAPI CreateEllipticRgnIndirect( const RECT *rect )
800 return CreateRoundRectRgn( rect->left, rect->top, rect->right,
801 rect->bottom, rect->right - rect->left,
802 rect->bottom - rect->top );
805 /***********************************************************************
806 * GetRegionData (GDI32.@)
808 * MSDN: GetRegionData, Return Values:
810 * "If the function succeeds and dwCount specifies an adequate number of bytes,
811 * the return value is always dwCount. If dwCount is too small or the function
812 * fails, the return value is 0. If lpRgnData is NULL, the return value is the
813 * required number of bytes.
815 * If the function fails, the return value is zero."
817 DWORD WINAPI GetRegionData(HRGN hrgn, DWORD count, LPRGNDATA rgndata)
820 RGNOBJ *obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
822 TRACE(" %04x count = %ld, rgndata = %p\n",
823 hrgn, count, rgndata);
827 size = obj->rgn->numRects * sizeof(RECT);
828 if(count < (size + sizeof(RGNDATAHEADER)) || rgndata == NULL)
830 GDI_ReleaseObj( hrgn );
831 if (rgndata) /* buffer is too small, signal it by return 0 */
833 else /* user requested buffer size with rgndata NULL */
834 return size + sizeof(RGNDATAHEADER);
837 rgndata->rdh.dwSize = sizeof(RGNDATAHEADER);
838 rgndata->rdh.iType = RDH_RECTANGLES;
839 rgndata->rdh.nCount = obj->rgn->numRects;
840 rgndata->rdh.nRgnSize = size;
841 rgndata->rdh.rcBound.left = obj->rgn->extents.left;
842 rgndata->rdh.rcBound.top = obj->rgn->extents.top;
843 rgndata->rdh.rcBound.right = obj->rgn->extents.right;
844 rgndata->rdh.rcBound.bottom = obj->rgn->extents.bottom;
846 memcpy( rgndata->Buffer, obj->rgn->rects, size );
848 GDI_ReleaseObj( hrgn );
849 return size + sizeof(RGNDATAHEADER);
853 /***********************************************************************
854 * ExtCreateRegion (GDI32.@)
857 HRGN WINAPI ExtCreateRegion( const XFORM* lpXform, DWORD dwCount, const RGNDATA* rgndata)
861 TRACE(" %p %ld %p = ", lpXform, dwCount, rgndata );
864 WARN("(Xform not implemented - ignored)\n");
866 if( rgndata->rdh.iType != RDH_RECTANGLES )
868 /* FIXME: We can use CreatePolyPolygonRgn() here
869 * for trapezoidal data */
871 WARN("(Unsupported region data)\n");
875 if( (hrgn = REGION_CreateRegion( rgndata->rdh.nCount )) )
877 RECT *pCurRect, *pEndRect;
878 RGNOBJ *obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
881 pEndRect = (RECT *)rgndata->Buffer + rgndata->rdh.nCount;
882 for(pCurRect = (RECT *)rgndata->Buffer; pCurRect < pEndRect; pCurRect++)
883 REGION_UnionRectWithRegion( pCurRect, obj->rgn );
884 GDI_ReleaseObj( hrgn );
886 TRACE("%04x\n", hrgn );
889 else ERR("Could not get pointer to newborn Region!\n");
897 /***********************************************************************
898 * PtInRegion (GDI32.@)
900 BOOL WINAPI PtInRegion( HRGN hrgn, INT x, INT y )
905 if ((obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC )))
909 if (obj->rgn->numRects > 0 && INRECT(obj->rgn->extents, x, y))
910 for (i = 0; i < obj->rgn->numRects; i++)
911 if (INRECT (obj->rgn->rects[i], x, y))
916 GDI_ReleaseObj( hrgn );
922 /***********************************************************************
923 * RectInRegion (GDI32.@)
925 * Returns TRUE if rect is at least partly inside hrgn
927 BOOL WINAPI RectInRegion( HRGN hrgn, const RECT *rect )
932 if ((obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC )))
934 RECT *pCurRect, *pRectEnd;
936 /* this is (just) a useful optimization */
937 if ((obj->rgn->numRects > 0) && EXTENTCHECK(&obj->rgn->extents,
940 for (pCurRect = obj->rgn->rects, pRectEnd = pCurRect +
941 obj->rgn->numRects; pCurRect < pRectEnd; pCurRect++)
943 if (pCurRect->bottom <= rect->top)
944 continue; /* not far enough down yet */
946 if (pCurRect->top >= rect->bottom)
947 break; /* too far down */
949 if (pCurRect->right <= rect->left)
950 continue; /* not far enough over yet */
952 if (pCurRect->left >= rect->right) {
960 GDI_ReleaseObj(hrgn);
965 /***********************************************************************
968 BOOL WINAPI EqualRgn( HRGN hrgn1, HRGN hrgn2 )
973 if ((obj1 = (RGNOBJ *) GDI_GetObjPtr( hrgn1, REGION_MAGIC )))
975 if ((obj2 = (RGNOBJ *) GDI_GetObjPtr( hrgn2, REGION_MAGIC )))
979 if ( obj1->rgn->numRects != obj2->rgn->numRects ) goto done;
980 if ( obj1->rgn->numRects == 0 )
986 if (obj1->rgn->extents.left != obj2->rgn->extents.left) goto done;
987 if (obj1->rgn->extents.right != obj2->rgn->extents.right) goto done;
988 if (obj1->rgn->extents.top != obj2->rgn->extents.top) goto done;
989 if (obj1->rgn->extents.bottom != obj2->rgn->extents.bottom) goto done;
990 for( i = 0; i < obj1->rgn->numRects; i++ )
992 if (obj1->rgn->rects[i].left != obj2->rgn->rects[i].left) goto done;
993 if (obj1->rgn->rects[i].right != obj2->rgn->rects[i].right) goto done;
994 if (obj1->rgn->rects[i].top != obj2->rgn->rects[i].top) goto done;
995 if (obj1->rgn->rects[i].bottom != obj2->rgn->rects[i].bottom) goto done;
999 GDI_ReleaseObj(hrgn2);
1001 GDI_ReleaseObj(hrgn1);
1006 /***********************************************************************
1007 * REGION_UnionRectWithRegion
1008 * Adds a rectangle to a WINEREGION
1010 static void REGION_UnionRectWithRegion(const RECT *rect, WINEREGION *rgn)
1014 region.rects = ®ion.extents;
1015 region.numRects = 1;
1017 region.extents = *rect;
1018 REGION_UnionRegion(rgn, rgn, ®ion);
1022 /***********************************************************************
1023 * REGION_CreateFrameRgn
1025 * Create a region that is a frame around another region.
1026 * Expand all rectangles by +/- x and y, then subtract original region.
1028 BOOL REGION_FrameRgn( HRGN hDest, HRGN hSrc, INT x, INT y )
1031 RGNOBJ *srcObj = (RGNOBJ*) GDI_GetObjPtr( hSrc, REGION_MAGIC );
1033 if (!srcObj) return FALSE;
1034 if (srcObj->rgn->numRects != 0)
1036 RGNOBJ* destObj = (RGNOBJ*) GDI_GetObjPtr( hDest, REGION_MAGIC );
1037 RECT *pRect, *pEndRect;
1040 EMPTY_REGION( destObj->rgn );
1042 pEndRect = srcObj->rgn->rects + srcObj->rgn->numRects;
1043 for(pRect = srcObj->rgn->rects; pRect < pEndRect; pRect++)
1045 tempRect.left = pRect->left - x;
1046 tempRect.top = pRect->top - y;
1047 tempRect.right = pRect->right + x;
1048 tempRect.bottom = pRect->bottom + y;
1049 REGION_UnionRectWithRegion( &tempRect, destObj->rgn );
1051 REGION_SubtractRegion( destObj->rgn, destObj->rgn, srcObj->rgn );
1052 GDI_ReleaseObj ( hDest );
1057 GDI_ReleaseObj( hSrc );
1062 /***********************************************************************
1063 * CombineRgn (GDI32.@)
1065 * Note: The behavior is correct even if src and dest regions are the same.
1067 INT WINAPI CombineRgn(HRGN hDest, HRGN hSrc1, HRGN hSrc2, INT mode)
1069 RGNOBJ *destObj = (RGNOBJ *) GDI_GetObjPtr( hDest, REGION_MAGIC);
1072 TRACE(" %04x,%04x -> %04x mode=%x\n",
1073 hSrc1, hSrc2, hDest, mode );
1076 RGNOBJ *src1Obj = (RGNOBJ *) GDI_GetObjPtr( hSrc1, REGION_MAGIC);
1080 TRACE("dump src1Obj:\n");
1081 if(TRACE_ON(region))
1082 REGION_DumpRegion(src1Obj->rgn);
1083 if (mode == RGN_COPY)
1085 REGION_CopyRegion( destObj->rgn, src1Obj->rgn );
1086 result = get_region_type( destObj );
1090 RGNOBJ *src2Obj = (RGNOBJ *) GDI_GetObjPtr( hSrc2, REGION_MAGIC);
1094 TRACE("dump src2Obj:\n");
1095 if(TRACE_ON(region))
1096 REGION_DumpRegion(src2Obj->rgn);
1100 REGION_IntersectRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn);
1103 REGION_UnionRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn );
1106 REGION_XorRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn );
1109 REGION_SubtractRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn );
1112 result = get_region_type( destObj );
1113 GDI_ReleaseObj( hSrc2 );
1116 GDI_ReleaseObj( hSrc1 );
1118 TRACE("dump destObj:\n");
1119 if(TRACE_ON(region))
1120 REGION_DumpRegion(destObj->rgn);
1122 GDI_ReleaseObj( hDest );
1124 ERR("Invalid rgn=%04x\n", hDest);
1129 /***********************************************************************
1131 * Re-calculate the extents of a region
1133 static void REGION_SetExtents (WINEREGION *pReg)
1135 RECT *pRect, *pRectEnd, *pExtents;
1137 if (pReg->numRects == 0)
1139 pReg->extents.left = 0;
1140 pReg->extents.top = 0;
1141 pReg->extents.right = 0;
1142 pReg->extents.bottom = 0;
1146 pExtents = &pReg->extents;
1147 pRect = pReg->rects;
1148 pRectEnd = &pRect[pReg->numRects - 1];
1151 * Since pRect is the first rectangle in the region, it must have the
1152 * smallest top and since pRectEnd is the last rectangle in the region,
1153 * it must have the largest bottom, because of banding. Initialize left and
1154 * right from pRect and pRectEnd, resp., as good things to initialize them
1157 pExtents->left = pRect->left;
1158 pExtents->top = pRect->top;
1159 pExtents->right = pRectEnd->right;
1160 pExtents->bottom = pRectEnd->bottom;
1162 while (pRect <= pRectEnd)
1164 if (pRect->left < pExtents->left)
1165 pExtents->left = pRect->left;
1166 if (pRect->right > pExtents->right)
1167 pExtents->right = pRect->right;
1172 /***********************************************************************
1175 static void REGION_CopyRegion(WINEREGION *dst, WINEREGION *src)
1177 if (dst != src) /* don't want to copy to itself */
1179 if (dst->size < src->numRects)
1181 if (! (dst->rects = HeapReAlloc( GetProcessHeap(), 0, dst->rects,
1182 src->numRects * sizeof(RECT) )))
1184 dst->size = src->numRects;
1186 dst->numRects = src->numRects;
1187 dst->extents.left = src->extents.left;
1188 dst->extents.top = src->extents.top;
1189 dst->extents.right = src->extents.right;
1190 dst->extents.bottom = src->extents.bottom;
1191 memcpy((char *) dst->rects, (char *) src->rects,
1192 (int) (src->numRects * sizeof(RECT)));
1197 /***********************************************************************
1200 * Attempt to merge the rects in the current band with those in the
1201 * previous one. Used only by REGION_RegionOp.
1204 * The new index for the previous band.
1207 * If coalescing takes place:
1208 * - rectangles in the previous band will have their bottom fields
1210 * - pReg->numRects will be decreased.
1213 static INT REGION_Coalesce (
1214 WINEREGION *pReg, /* Region to coalesce */
1215 INT prevStart, /* Index of start of previous band */
1216 INT curStart /* Index of start of current band */
1218 RECT *pPrevRect; /* Current rect in previous band */
1219 RECT *pCurRect; /* Current rect in current band */
1220 RECT *pRegEnd; /* End of region */
1221 INT curNumRects; /* Number of rectangles in current band */
1222 INT prevNumRects; /* Number of rectangles in previous band */
1223 INT bandtop; /* top coordinate for current band */
1225 pRegEnd = &pReg->rects[pReg->numRects];
1227 pPrevRect = &pReg->rects[prevStart];
1228 prevNumRects = curStart - prevStart;
1231 * Figure out how many rectangles are in the current band. Have to do
1232 * this because multiple bands could have been added in REGION_RegionOp
1233 * at the end when one region has been exhausted.
1235 pCurRect = &pReg->rects[curStart];
1236 bandtop = pCurRect->top;
1237 for (curNumRects = 0;
1238 (pCurRect != pRegEnd) && (pCurRect->top == bandtop);
1244 if (pCurRect != pRegEnd)
1247 * If more than one band was added, we have to find the start
1248 * of the last band added so the next coalescing job can start
1249 * at the right place... (given when multiple bands are added,
1250 * this may be pointless -- see above).
1253 while (pRegEnd[-1].top == pRegEnd->top)
1257 curStart = pRegEnd - pReg->rects;
1258 pRegEnd = pReg->rects + pReg->numRects;
1261 if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
1262 pCurRect -= curNumRects;
1264 * The bands may only be coalesced if the bottom of the previous
1265 * matches the top scanline of the current.
1267 if (pPrevRect->bottom == pCurRect->top)
1270 * Make sure the bands have rects in the same places. This
1271 * assumes that rects have been added in such a way that they
1272 * cover the most area possible. I.e. two rects in a band must
1273 * have some horizontal space between them.
1277 if ((pPrevRect->left != pCurRect->left) ||
1278 (pPrevRect->right != pCurRect->right))
1281 * The bands don't line up so they can't be coalesced.
1288 } while (prevNumRects != 0);
1290 pReg->numRects -= curNumRects;
1291 pCurRect -= curNumRects;
1292 pPrevRect -= curNumRects;
1295 * The bands may be merged, so set the bottom of each rect
1296 * in the previous band to that of the corresponding rect in
1301 pPrevRect->bottom = pCurRect->bottom;
1305 } while (curNumRects != 0);
1308 * If only one band was added to the region, we have to backup
1309 * curStart to the start of the previous band.
1311 * If more than one band was added to the region, copy the
1312 * other bands down. The assumption here is that the other bands
1313 * came from the same region as the current one and no further
1314 * coalescing can be done on them since it's all been done
1315 * already... curStart is already in the right place.
1317 if (pCurRect == pRegEnd)
1319 curStart = prevStart;
1325 *pPrevRect++ = *pCurRect++;
1326 } while (pCurRect != pRegEnd);
1334 /***********************************************************************
1337 * Apply an operation to two regions. Called by REGION_Union,
1338 * REGION_Inverse, REGION_Subtract, REGION_Intersect...
1344 * The new region is overwritten.
1347 * The idea behind this function is to view the two regions as sets.
1348 * Together they cover a rectangle of area that this function divides
1349 * into horizontal bands where points are covered only by one region
1350 * or by both. For the first case, the nonOverlapFunc is called with
1351 * each the band and the band's upper and lower extents. For the
1352 * second, the overlapFunc is called to process the entire band. It
1353 * is responsible for clipping the rectangles in the band, though
1354 * this function provides the boundaries.
1355 * At the end of each band, the new region is coalesced, if possible,
1356 * to reduce the number of rectangles in the region.
1359 static void REGION_RegionOp(
1360 WINEREGION *newReg, /* Place to store result */
1361 WINEREGION *reg1, /* First region in operation */
1362 WINEREGION *reg2, /* 2nd region in operation */
1363 void (*overlapFunc)(), /* Function to call for over-lapping bands */
1364 void (*nonOverlap1Func)(), /* Function to call for non-overlapping bands in region 1 */
1365 void (*nonOverlap2Func)() /* Function to call for non-overlapping bands in region 2 */
1367 RECT *r1; /* Pointer into first region */
1368 RECT *r2; /* Pointer into 2d region */
1369 RECT *r1End; /* End of 1st region */
1370 RECT *r2End; /* End of 2d region */
1371 INT ybot; /* Bottom of intersection */
1372 INT ytop; /* Top of intersection */
1373 RECT *oldRects; /* Old rects for newReg */
1374 INT prevBand; /* Index of start of
1375 * previous band in newReg */
1376 INT curBand; /* Index of start of current
1378 RECT *r1BandEnd; /* End of current band in r1 */
1379 RECT *r2BandEnd; /* End of current band in r2 */
1380 INT top; /* Top of non-overlapping band */
1381 INT bot; /* Bottom of non-overlapping band */
1385 * set r1, r2, r1End and r2End appropriately, preserve the important
1386 * parts of the destination region until the end in case it's one of
1387 * the two source regions, then mark the "new" region empty, allocating
1388 * another array of rectangles for it to use.
1392 r1End = r1 + reg1->numRects;
1393 r2End = r2 + reg2->numRects;
1397 * newReg may be one of the src regions so we can't empty it. We keep a
1398 * note of its rects pointer (so that we can free them later), preserve its
1399 * extents and simply set numRects to zero.
1402 oldRects = newReg->rects;
1403 newReg->numRects = 0;
1406 * Allocate a reasonable number of rectangles for the new region. The idea
1407 * is to allocate enough so the individual functions don't need to
1408 * reallocate and copy the array, which is time consuming, yet we don't
1409 * have to worry about using too much memory. I hope to be able to
1410 * nuke the Xrealloc() at the end of this function eventually.
1412 newReg->size = max(reg1->numRects,reg2->numRects) * 2;
1414 if (! (newReg->rects = HeapAlloc( GetProcessHeap(), 0,
1415 sizeof(RECT) * newReg->size )))
1422 * Initialize ybot and ytop.
1423 * In the upcoming loop, ybot and ytop serve different functions depending
1424 * on whether the band being handled is an overlapping or non-overlapping
1426 * In the case of a non-overlapping band (only one of the regions
1427 * has points in the band), ybot is the bottom of the most recent
1428 * intersection and thus clips the top of the rectangles in that band.
1429 * ytop is the top of the next intersection between the two regions and
1430 * serves to clip the bottom of the rectangles in the current band.
1431 * For an overlapping band (where the two regions intersect), ytop clips
1432 * the top of the rectangles of both regions and ybot clips the bottoms.
1434 if (reg1->extents.top < reg2->extents.top)
1435 ybot = reg1->extents.top;
1437 ybot = reg2->extents.top;
1440 * prevBand serves to mark the start of the previous band so rectangles
1441 * can be coalesced into larger rectangles. qv. miCoalesce, above.
1442 * In the beginning, there is no previous band, so prevBand == curBand
1443 * (curBand is set later on, of course, but the first band will always
1444 * start at index 0). prevBand and curBand must be indices because of
1445 * the possible expansion, and resultant moving, of the new region's
1446 * array of rectangles.
1452 curBand = newReg->numRects;
1455 * This algorithm proceeds one source-band (as opposed to a
1456 * destination band, which is determined by where the two regions
1457 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
1458 * rectangle after the last one in the current band for their
1459 * respective regions.
1462 while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top))
1468 while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top))
1474 * First handle the band that doesn't intersect, if any.
1476 * Note that attention is restricted to one band in the
1477 * non-intersecting region at once, so if a region has n
1478 * bands between the current position and the next place it overlaps
1479 * the other, this entire loop will be passed through n times.
1481 if (r1->top < r2->top)
1483 top = max(r1->top,ybot);
1484 bot = min(r1->bottom,r2->top);
1486 if ((top != bot) && (nonOverlap1Func != (void (*)())NULL))
1488 (* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot);
1493 else if (r2->top < r1->top)
1495 top = max(r2->top,ybot);
1496 bot = min(r2->bottom,r1->top);
1498 if ((top != bot) && (nonOverlap2Func != (void (*)())NULL))
1500 (* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot);
1511 * If any rectangles got added to the region, try and coalesce them
1512 * with rectangles from the previous band. Note we could just do
1513 * this test in miCoalesce, but some machines incur a not
1514 * inconsiderable cost for function calls, so...
1516 if (newReg->numRects != curBand)
1518 prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1522 * Now see if we've hit an intersecting band. The two bands only
1523 * intersect if ybot > ytop
1525 ybot = min(r1->bottom, r2->bottom);
1526 curBand = newReg->numRects;
1529 (* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
1533 if (newReg->numRects != curBand)
1535 prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1539 * If we've finished with a band (bottom == ybot) we skip forward
1540 * in the region to the next band.
1542 if (r1->bottom == ybot)
1546 if (r2->bottom == ybot)
1550 } while ((r1 != r1End) && (r2 != r2End));
1553 * Deal with whichever region still has rectangles left.
1555 curBand = newReg->numRects;
1558 if (nonOverlap1Func != (void (*)())NULL)
1563 while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top))
1567 (* nonOverlap1Func) (newReg, r1, r1BandEnd,
1568 max(r1->top,ybot), r1->bottom);
1570 } while (r1 != r1End);
1573 else if ((r2 != r2End) && (nonOverlap2Func != (void (*)())NULL))
1578 while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top))
1582 (* nonOverlap2Func) (newReg, r2, r2BandEnd,
1583 max(r2->top,ybot), r2->bottom);
1585 } while (r2 != r2End);
1588 if (newReg->numRects != curBand)
1590 (void) REGION_Coalesce (newReg, prevBand, curBand);
1594 * A bit of cleanup. To keep regions from growing without bound,
1595 * we shrink the array of rectangles to match the new number of
1596 * rectangles in the region. This never goes to 0, however...
1598 * Only do this stuff if the number of rectangles allocated is more than
1599 * twice the number of rectangles in the region (a simple optimization...).
1601 if ((newReg->numRects < (newReg->size >> 1)) && (newReg->numRects > 2))
1603 if (REGION_NOT_EMPTY(newReg))
1605 RECT *prev_rects = newReg->rects;
1606 newReg->size = newReg->numRects;
1607 newReg->rects = HeapReAlloc( GetProcessHeap(), 0, newReg->rects,
1608 sizeof(RECT) * newReg->size );
1609 if (! newReg->rects)
1610 newReg->rects = prev_rects;
1615 * No point in doing the extra work involved in an Xrealloc if
1616 * the region is empty
1619 HeapFree( GetProcessHeap(), 0, newReg->rects );
1620 newReg->rects = HeapAlloc( GetProcessHeap(), 0, sizeof(RECT) );
1623 HeapFree( GetProcessHeap(), 0, oldRects );
1627 /***********************************************************************
1628 * Region Intersection
1629 ***********************************************************************/
1632 /***********************************************************************
1635 * Handle an overlapping band for REGION_Intersect.
1641 * Rectangles may be added to the region.
1644 static void REGION_IntersectO(WINEREGION *pReg, RECT *r1, RECT *r1End,
1645 RECT *r2, RECT *r2End, INT top, INT bottom)
1651 pNextRect = &pReg->rects[pReg->numRects];
1653 while ((r1 != r1End) && (r2 != r2End))
1655 left = max(r1->left, r2->left);
1656 right = min(r1->right, r2->right);
1659 * If there's any overlap between the two rectangles, add that
1660 * overlap to the new region.
1661 * There's no need to check for subsumption because the only way
1662 * such a need could arise is if some region has two rectangles
1663 * right next to each other. Since that should never happen...
1667 MEMCHECK(pReg, pNextRect, pReg->rects);
1668 pNextRect->left = left;
1669 pNextRect->top = top;
1670 pNextRect->right = right;
1671 pNextRect->bottom = bottom;
1672 pReg->numRects += 1;
1677 * Need to advance the pointers. Shift the one that extends
1678 * to the right the least, since the other still has a chance to
1679 * overlap with that region's next rectangle, if you see what I mean.
1681 if (r1->right < r2->right)
1685 else if (r2->right < r1->right)
1698 /***********************************************************************
1699 * REGION_IntersectRegion
1701 static void REGION_IntersectRegion(WINEREGION *newReg, WINEREGION *reg1,
1704 /* check for trivial reject */
1705 if ( (!(reg1->numRects)) || (!(reg2->numRects)) ||
1706 (!EXTENTCHECK(®1->extents, ®2->extents)))
1707 newReg->numRects = 0;
1709 REGION_RegionOp (newReg, reg1, reg2,
1710 (voidProcp) REGION_IntersectO, (voidProcp) NULL, (voidProcp) NULL);
1713 * Can't alter newReg's extents before we call miRegionOp because
1714 * it might be one of the source regions and miRegionOp depends
1715 * on the extents of those regions being the same. Besides, this
1716 * way there's no checking against rectangles that will be nuked
1717 * due to coalescing, so we have to examine fewer rectangles.
1719 REGION_SetExtents(newReg);
1722 /***********************************************************************
1724 ***********************************************************************/
1726 /***********************************************************************
1729 * Handle a non-overlapping band for the union operation. Just
1730 * Adds the rectangles into the region. Doesn't have to check for
1731 * subsumption or anything.
1737 * pReg->numRects is incremented and the final rectangles overwritten
1738 * with the rectangles we're passed.
1741 static void REGION_UnionNonO (WINEREGION *pReg, RECT *r, RECT *rEnd,
1742 INT top, INT bottom)
1746 pNextRect = &pReg->rects[pReg->numRects];
1750 MEMCHECK(pReg, pNextRect, pReg->rects);
1751 pNextRect->left = r->left;
1752 pNextRect->top = top;
1753 pNextRect->right = r->right;
1754 pNextRect->bottom = bottom;
1755 pReg->numRects += 1;
1762 /***********************************************************************
1765 * Handle an overlapping band for the union operation. Picks the
1766 * left-most rectangle each time and merges it into the region.
1772 * Rectangles are overwritten in pReg->rects and pReg->numRects will
1776 static void REGION_UnionO (WINEREGION *pReg, RECT *r1, RECT *r1End,
1777 RECT *r2, RECT *r2End, INT top, INT bottom)
1781 pNextRect = &pReg->rects[pReg->numRects];
1783 #define MERGERECT(r) \
1784 if ((pReg->numRects != 0) && \
1785 (pNextRect[-1].top == top) && \
1786 (pNextRect[-1].bottom == bottom) && \
1787 (pNextRect[-1].right >= r->left)) \
1789 if (pNextRect[-1].right < r->right) \
1791 pNextRect[-1].right = r->right; \
1796 MEMCHECK(pReg, pNextRect, pReg->rects); \
1797 pNextRect->top = top; \
1798 pNextRect->bottom = bottom; \
1799 pNextRect->left = r->left; \
1800 pNextRect->right = r->right; \
1801 pReg->numRects += 1; \
1806 while ((r1 != r1End) && (r2 != r2End))
1808 if (r1->left < r2->left)
1823 } while (r1 != r1End);
1825 else while (r2 != r2End)
1832 /***********************************************************************
1833 * REGION_UnionRegion
1835 static void REGION_UnionRegion(WINEREGION *newReg, WINEREGION *reg1,
1838 /* checks all the simple cases */
1841 * Region 1 and 2 are the same or region 1 is empty
1843 if ( (reg1 == reg2) || (!(reg1->numRects)) )
1846 REGION_CopyRegion(newReg, reg2);
1851 * if nothing to union (region 2 empty)
1853 if (!(reg2->numRects))
1856 REGION_CopyRegion(newReg, reg1);
1861 * Region 1 completely subsumes region 2
1863 if ((reg1->numRects == 1) &&
1864 (reg1->extents.left <= reg2->extents.left) &&
1865 (reg1->extents.top <= reg2->extents.top) &&
1866 (reg1->extents.right >= reg2->extents.right) &&
1867 (reg1->extents.bottom >= reg2->extents.bottom))
1870 REGION_CopyRegion(newReg, reg1);
1875 * Region 2 completely subsumes region 1
1877 if ((reg2->numRects == 1) &&
1878 (reg2->extents.left <= reg1->extents.left) &&
1879 (reg2->extents.top <= reg1->extents.top) &&
1880 (reg2->extents.right >= reg1->extents.right) &&
1881 (reg2->extents.bottom >= reg1->extents.bottom))
1884 REGION_CopyRegion(newReg, reg2);
1888 REGION_RegionOp (newReg, reg1, reg2, (voidProcp) REGION_UnionO,
1889 (voidProcp) REGION_UnionNonO, (voidProcp) REGION_UnionNonO);
1891 newReg->extents.left = min(reg1->extents.left, reg2->extents.left);
1892 newReg->extents.top = min(reg1->extents.top, reg2->extents.top);
1893 newReg->extents.right = max(reg1->extents.right, reg2->extents.right);
1894 newReg->extents.bottom = max(reg1->extents.bottom, reg2->extents.bottom);
1897 /***********************************************************************
1898 * Region Subtraction
1899 ***********************************************************************/
1901 /***********************************************************************
1902 * REGION_SubtractNonO1
1904 * Deal with non-overlapping band for subtraction. Any parts from
1905 * region 2 we discard. Anything from region 1 we add to the region.
1911 * pReg may be affected.
1914 static void REGION_SubtractNonO1 (WINEREGION *pReg, RECT *r, RECT *rEnd,
1915 INT top, INT bottom)
1919 pNextRect = &pReg->rects[pReg->numRects];
1923 MEMCHECK(pReg, pNextRect, pReg->rects);
1924 pNextRect->left = r->left;
1925 pNextRect->top = top;
1926 pNextRect->right = r->right;
1927 pNextRect->bottom = bottom;
1928 pReg->numRects += 1;
1936 /***********************************************************************
1939 * Overlapping band subtraction. x1 is the left-most point not yet
1946 * pReg may have rectangles added to it.
1949 static void REGION_SubtractO (WINEREGION *pReg, RECT *r1, RECT *r1End,
1950 RECT *r2, RECT *r2End, INT top, INT bottom)
1956 pNextRect = &pReg->rects[pReg->numRects];
1958 while ((r1 != r1End) && (r2 != r2End))
1960 if (r2->right <= left)
1963 * Subtrahend missed the boat: go to next subtrahend.
1967 else if (r2->left <= left)
1970 * Subtrahend preceeds minuend: nuke left edge of minuend.
1973 if (left >= r1->right)
1976 * Minuend completely covered: advance to next minuend and
1977 * reset left fence to edge of new minuend.
1986 * Subtrahend now used up since it doesn't extend beyond
1992 else if (r2->left < r1->right)
1995 * Left part of subtrahend covers part of minuend: add uncovered
1996 * part of minuend to region and skip to next subtrahend.
1998 MEMCHECK(pReg, pNextRect, pReg->rects);
1999 pNextRect->left = left;
2000 pNextRect->top = top;
2001 pNextRect->right = r2->left;
2002 pNextRect->bottom = bottom;
2003 pReg->numRects += 1;
2006 if (left >= r1->right)
2009 * Minuend used up: advance to new...
2018 * Subtrahend used up
2026 * Minuend used up: add any remaining piece before advancing.
2028 if (r1->right > left)
2030 MEMCHECK(pReg, pNextRect, pReg->rects);
2031 pNextRect->left = left;
2032 pNextRect->top = top;
2033 pNextRect->right = r1->right;
2034 pNextRect->bottom = bottom;
2035 pReg->numRects += 1;
2044 * Add remaining minuend rectangles to region.
2048 MEMCHECK(pReg, pNextRect, pReg->rects);
2049 pNextRect->left = left;
2050 pNextRect->top = top;
2051 pNextRect->right = r1->right;
2052 pNextRect->bottom = bottom;
2053 pReg->numRects += 1;
2064 /***********************************************************************
2065 * REGION_SubtractRegion
2067 * Subtract regS from regM and leave the result in regD.
2068 * S stands for subtrahend, M for minuend and D for difference.
2074 * regD is overwritten.
2077 static void REGION_SubtractRegion(WINEREGION *regD, WINEREGION *regM,
2080 /* check for trivial reject */
2081 if ( (!(regM->numRects)) || (!(regS->numRects)) ||
2082 (!EXTENTCHECK(®M->extents, ®S->extents)) )
2084 REGION_CopyRegion(regD, regM);
2088 REGION_RegionOp (regD, regM, regS, (voidProcp) REGION_SubtractO,
2089 (voidProcp) REGION_SubtractNonO1, (voidProcp) NULL);
2092 * Can't alter newReg's extents before we call miRegionOp because
2093 * it might be one of the source regions and miRegionOp depends
2094 * on the extents of those regions being the unaltered. Besides, this
2095 * way there's no checking against rectangles that will be nuked
2096 * due to coalescing, so we have to examine fewer rectangles.
2098 REGION_SetExtents (regD);
2101 /***********************************************************************
2104 static void REGION_XorRegion(WINEREGION *dr, WINEREGION *sra,
2107 WINEREGION *tra, *trb;
2109 if ((! (tra = REGION_AllocWineRegion(sra->numRects + 1))) ||
2110 (! (trb = REGION_AllocWineRegion(srb->numRects + 1))))
2112 REGION_SubtractRegion(tra,sra,srb);
2113 REGION_SubtractRegion(trb,srb,sra);
2114 REGION_UnionRegion(dr,tra,trb);
2115 REGION_DestroyWineRegion(tra);
2116 REGION_DestroyWineRegion(trb);
2120 /**************************************************************************
2124 *************************************************************************/
2126 #define LARGE_COORDINATE 0x7fffffff /* FIXME */
2127 #define SMALL_COORDINATE 0x80000000
2129 /***********************************************************************
2130 * REGION_InsertEdgeInET
2132 * Insert the given edge into the edge table.
2133 * First we must find the correct bucket in the
2134 * Edge table, then find the right slot in the
2135 * bucket. Finally, we can insert it.
2138 static void REGION_InsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE,
2139 INT scanline, ScanLineListBlock **SLLBlock, INT *iSLLBlock)
2142 EdgeTableEntry *start, *prev;
2143 ScanLineList *pSLL, *pPrevSLL;
2144 ScanLineListBlock *tmpSLLBlock;
2147 * find the right bucket to put the edge into
2149 pPrevSLL = &ET->scanlines;
2150 pSLL = pPrevSLL->next;
2151 while (pSLL && (pSLL->scanline < scanline))
2158 * reassign pSLL (pointer to ScanLineList) if necessary
2160 if ((!pSLL) || (pSLL->scanline > scanline))
2162 if (*iSLLBlock > SLLSPERBLOCK-1)
2164 tmpSLLBlock = HeapAlloc( GetProcessHeap(), 0, sizeof(ScanLineListBlock));
2167 WARN("Can't alloc SLLB\n");
2170 (*SLLBlock)->next = tmpSLLBlock;
2171 tmpSLLBlock->next = (ScanLineListBlock *)NULL;
2172 *SLLBlock = tmpSLLBlock;
2175 pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
2177 pSLL->next = pPrevSLL->next;
2178 pSLL->edgelist = (EdgeTableEntry *)NULL;
2179 pPrevSLL->next = pSLL;
2181 pSLL->scanline = scanline;
2184 * now insert the edge in the right bucket
2186 prev = (EdgeTableEntry *)NULL;
2187 start = pSLL->edgelist;
2188 while (start && (start->bres.minor_axis < ETE->bres.minor_axis))
2191 start = start->next;
2198 pSLL->edgelist = ETE;
2201 /***********************************************************************
2202 * REGION_CreateEdgeTable
2204 * This routine creates the edge table for
2205 * scan converting polygons.
2206 * The Edge Table (ET) looks like:
2210 * | ymax | ScanLineLists
2211 * |scanline|-->------------>-------------->...
2212 * -------- |scanline| |scanline|
2213 * |edgelist| |edgelist|
2214 * --------- ---------
2218 * list of ETEs list of ETEs
2220 * where ETE is an EdgeTableEntry data structure,
2221 * and there is one ScanLineList per scanline at
2222 * which an edge is initially entered.
2225 static void REGION_CreateETandAET(const INT *Count, INT nbpolygons,
2226 const POINT *pts, EdgeTable *ET, EdgeTableEntry *AET,
2227 EdgeTableEntry *pETEs, ScanLineListBlock *pSLLBlock)
2229 const POINT *top, *bottom;
2230 const POINT *PrevPt, *CurrPt, *EndPt;
2237 * initialize the Active Edge Table
2239 AET->next = (EdgeTableEntry *)NULL;
2240 AET->back = (EdgeTableEntry *)NULL;
2241 AET->nextWETE = (EdgeTableEntry *)NULL;
2242 AET->bres.minor_axis = SMALL_COORDINATE;
2245 * initialize the Edge Table.
2247 ET->scanlines.next = (ScanLineList *)NULL;
2248 ET->ymax = SMALL_COORDINATE;
2249 ET->ymin = LARGE_COORDINATE;
2250 pSLLBlock->next = (ScanLineListBlock *)NULL;
2253 for(poly = 0; poly < nbpolygons; poly++)
2255 count = Count[poly];
2263 * for each vertex in the array of points.
2264 * In this loop we are dealing with two vertices at
2265 * a time -- these make up one edge of the polygon.
2272 * find out which point is above and which is below.
2274 if (PrevPt->y > CurrPt->y)
2276 bottom = PrevPt, top = CurrPt;
2277 pETEs->ClockWise = 0;
2281 bottom = CurrPt, top = PrevPt;
2282 pETEs->ClockWise = 1;
2286 * don't add horizontal edges to the Edge table.
2288 if (bottom->y != top->y)
2290 pETEs->ymax = bottom->y-1;
2291 /* -1 so we don't get last scanline */
2294 * initialize integer edge algorithm
2296 dy = bottom->y - top->y;
2297 BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres);
2299 REGION_InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock,
2302 if (PrevPt->y > ET->ymax)
2303 ET->ymax = PrevPt->y;
2304 if (PrevPt->y < ET->ymin)
2305 ET->ymin = PrevPt->y;
2314 /***********************************************************************
2317 * This routine moves EdgeTableEntries from the
2318 * EdgeTable into the Active Edge Table,
2319 * leaving them sorted by smaller x coordinate.
2322 static void REGION_loadAET(EdgeTableEntry *AET, EdgeTableEntry *ETEs)
2324 EdgeTableEntry *pPrevAET;
2325 EdgeTableEntry *tmp;
2331 while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis))
2340 ETEs->back = pPrevAET;
2341 pPrevAET->next = ETEs;
2348 /***********************************************************************
2349 * REGION_computeWAET
2351 * This routine links the AET by the
2352 * nextWETE (winding EdgeTableEntry) link for
2353 * use by the winding number rule. The final
2354 * Active Edge Table (AET) might look something
2358 * ---------- --------- ---------
2359 * |ymax | |ymax | |ymax |
2360 * | ... | |... | |... |
2361 * |next |->|next |->|next |->...
2362 * |nextWETE| |nextWETE| |nextWETE|
2363 * --------- --------- ^--------
2365 * V-------------------> V---> ...
2368 static void REGION_computeWAET(EdgeTableEntry *AET)
2370 register EdgeTableEntry *pWETE;
2371 register int inside = 1;
2372 register int isInside = 0;
2374 AET->nextWETE = (EdgeTableEntry *)NULL;
2384 if ((!inside && !isInside) ||
2385 ( inside && isInside))
2387 pWETE->nextWETE = AET;
2393 pWETE->nextWETE = (EdgeTableEntry *)NULL;
2396 /***********************************************************************
2397 * REGION_InsertionSort
2399 * Just a simple insertion sort using
2400 * pointers and back pointers to sort the Active
2404 static BOOL REGION_InsertionSort(EdgeTableEntry *AET)
2406 EdgeTableEntry *pETEchase;
2407 EdgeTableEntry *pETEinsert;
2408 EdgeTableEntry *pETEchaseBackTMP;
2409 BOOL changed = FALSE;
2416 while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
2417 pETEchase = pETEchase->back;
2420 if (pETEchase != pETEinsert)
2422 pETEchaseBackTMP = pETEchase->back;
2423 pETEinsert->back->next = AET;
2425 AET->back = pETEinsert->back;
2426 pETEinsert->next = pETEchase;
2427 pETEchase->back->next = pETEinsert;
2428 pETEchase->back = pETEinsert;
2429 pETEinsert->back = pETEchaseBackTMP;
2436 /***********************************************************************
2437 * REGION_FreeStorage
2441 static void REGION_FreeStorage(ScanLineListBlock *pSLLBlock)
2443 ScanLineListBlock *tmpSLLBlock;
2447 tmpSLLBlock = pSLLBlock->next;
2448 HeapFree( GetProcessHeap(), 0, pSLLBlock );
2449 pSLLBlock = tmpSLLBlock;
2454 /***********************************************************************
2455 * REGION_PtsToRegion
2457 * Create an array of rectangles from a list of points.
2459 static int REGION_PtsToRegion(int numFullPtBlocks, int iCurPtBlock,
2460 POINTBLOCK *FirstPtBlock, WINEREGION *reg)
2464 POINTBLOCK *CurPtBlock;
2469 extents = ®->extents;
2471 numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
2473 if (!(reg->rects = HeapReAlloc( GetProcessHeap(), 0, reg->rects,
2474 sizeof(RECT) * numRects )))
2477 reg->size = numRects;
2478 CurPtBlock = FirstPtBlock;
2479 rects = reg->rects - 1;
2481 extents->left = LARGE_COORDINATE, extents->right = SMALL_COORDINATE;
2483 for ( ; numFullPtBlocks >= 0; numFullPtBlocks--) {
2484 /* the loop uses 2 points per iteration */
2485 i = NUMPTSTOBUFFER >> 1;
2486 if (!numFullPtBlocks)
2487 i = iCurPtBlock >> 1;
2488 for (pts = CurPtBlock->pts; i--; pts += 2) {
2489 if (pts->x == pts[1].x)
2491 if (numRects && pts->x == rects->left && pts->y == rects->bottom &&
2492 pts[1].x == rects->right &&
2493 (numRects == 1 || rects[-1].top != rects->top) &&
2494 (i && pts[2].y > pts[1].y)) {
2495 rects->bottom = pts[1].y + 1;
2500 rects->left = pts->x; rects->top = pts->y;
2501 rects->right = pts[1].x; rects->bottom = pts[1].y + 1;
2502 if (rects->left < extents->left)
2503 extents->left = rects->left;
2504 if (rects->right > extents->right)
2505 extents->right = rects->right;
2507 CurPtBlock = CurPtBlock->next;
2511 extents->top = reg->rects->top;
2512 extents->bottom = rects->bottom;
2517 extents->bottom = 0;
2519 reg->numRects = numRects;
2524 /***********************************************************************
2525 * CreatePolyPolygonRgn (GDI32.@)
2527 HRGN WINAPI CreatePolyPolygonRgn(const POINT *Pts, const INT *Count,
2528 INT nbpolygons, INT mode)
2533 register EdgeTableEntry *pAET; /* Active Edge Table */
2534 register INT y; /* current scanline */
2535 register int iPts = 0; /* number of pts in buffer */
2536 register EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/
2537 register ScanLineList *pSLL; /* current scanLineList */
2538 register POINT *pts; /* output buffer */
2539 EdgeTableEntry *pPrevAET; /* ptr to previous AET */
2540 EdgeTable ET; /* header node for ET */
2541 EdgeTableEntry AET; /* header node for AET */
2542 EdgeTableEntry *pETEs; /* EdgeTableEntries pool */
2543 ScanLineListBlock SLLBlock; /* header for scanlinelist */
2544 int fixWAET = FALSE;
2545 POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
2546 POINTBLOCK *tmpPtBlock;
2547 int numFullPtBlocks = 0;
2550 if(!(hrgn = REGION_CreateRegion(nbpolygons)))
2552 obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
2555 /* special case a rectangle */
2557 if (((nbpolygons == 1) && ((*Count == 4) ||
2558 ((*Count == 5) && (Pts[4].x == Pts[0].x) && (Pts[4].y == Pts[0].y)))) &&
2559 (((Pts[0].y == Pts[1].y) &&
2560 (Pts[1].x == Pts[2].x) &&
2561 (Pts[2].y == Pts[3].y) &&
2562 (Pts[3].x == Pts[0].x)) ||
2563 ((Pts[0].x == Pts[1].x) &&
2564 (Pts[1].y == Pts[2].y) &&
2565 (Pts[2].x == Pts[3].x) &&
2566 (Pts[3].y == Pts[0].y))))
2568 SetRectRgn( hrgn, min(Pts[0].x, Pts[2].x), min(Pts[0].y, Pts[2].y),
2569 max(Pts[0].x, Pts[2].x), max(Pts[0].y, Pts[2].y) );
2570 GDI_ReleaseObj( hrgn );
2574 for(poly = total = 0; poly < nbpolygons; poly++)
2575 total += Count[poly];
2576 if (! (pETEs = HeapAlloc( GetProcessHeap(), 0, sizeof(EdgeTableEntry) * total )))
2578 REGION_DeleteObject( hrgn, obj );
2581 pts = FirstPtBlock.pts;
2582 REGION_CreateETandAET(Count, nbpolygons, Pts, &ET, &AET, pETEs, &SLLBlock);
2583 pSLL = ET.scanlines.next;
2584 curPtBlock = &FirstPtBlock;
2586 if (mode != WINDING) {
2590 for (y = ET.ymin; y < ET.ymax; y++) {
2592 * Add a new edge to the active edge table when we
2593 * get to the next edge.
2595 if (pSLL != NULL && y == pSLL->scanline) {
2596 REGION_loadAET(&AET, pSLL->edgelist);
2603 * for each active edge
2606 pts->x = pAET->bres.minor_axis, pts->y = y;
2610 * send out the buffer
2612 if (iPts == NUMPTSTOBUFFER) {
2613 tmpPtBlock = HeapAlloc( GetProcessHeap(), 0, sizeof(POINTBLOCK));
2615 WARN("Can't alloc tPB\n");
2618 curPtBlock->next = tmpPtBlock;
2619 curPtBlock = tmpPtBlock;
2620 pts = curPtBlock->pts;
2624 EVALUATEEDGEEVENODD(pAET, pPrevAET, y);
2626 REGION_InsertionSort(&AET);
2633 for (y = ET.ymin; y < ET.ymax; y++) {
2635 * Add a new edge to the active edge table when we
2636 * get to the next edge.
2638 if (pSLL != NULL && y == pSLL->scanline) {
2639 REGION_loadAET(&AET, pSLL->edgelist);
2640 REGION_computeWAET(&AET);
2648 * for each active edge
2652 * add to the buffer only those edges that
2653 * are in the Winding active edge table.
2655 if (pWETE == pAET) {
2656 pts->x = pAET->bres.minor_axis, pts->y = y;
2660 * send out the buffer
2662 if (iPts == NUMPTSTOBUFFER) {
2663 tmpPtBlock = HeapAlloc( GetProcessHeap(), 0,
2664 sizeof(POINTBLOCK) );
2666 WARN("Can't alloc tPB\n");
2667 REGION_DeleteObject( hrgn, obj );
2670 curPtBlock->next = tmpPtBlock;
2671 curPtBlock = tmpPtBlock;
2672 pts = curPtBlock->pts;
2673 numFullPtBlocks++; iPts = 0;
2675 pWETE = pWETE->nextWETE;
2677 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
2681 * recompute the winding active edge table if
2682 * we just resorted or have exited an edge.
2684 if (REGION_InsertionSort(&AET) || fixWAET) {
2685 REGION_computeWAET(&AET);
2690 REGION_FreeStorage(SLLBlock.next);
2691 REGION_PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
2693 for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) {
2694 tmpPtBlock = curPtBlock->next;
2695 HeapFree( GetProcessHeap(), 0, curPtBlock );
2696 curPtBlock = tmpPtBlock;
2698 HeapFree( GetProcessHeap(), 0, pETEs );
2699 GDI_ReleaseObj( hrgn );
2704 /***********************************************************************
2705 * CreatePolygonRgn (GDI32.@)
2707 HRGN WINAPI CreatePolygonRgn( const POINT *points, INT count,
2710 return CreatePolyPolygonRgn( points, &count, 1, mode );
2714 /***********************************************************************
2715 * GetRandomRgn [GDI32.@]
2718 * This function is documented in MSDN online
2720 INT WINAPI GetRandomRgn(HDC hDC, HRGN hRgn, DWORD dwCode)
2724 case 4: /* == SYSRGN ? */
2726 DC *dc = DC_GetDCPtr (hDC);
2731 CombineRgn (hRgn, dc->hVisRgn, 0, RGN_COPY);
2733 * On Windows NT/2000,
2734 * the region returned is in screen coordinates.
2736 * the region returned is in window coordinates
2738 vi.dwOSVersionInfoSize = sizeof(vi);
2739 if (GetVersionExA( &vi ) && vi.dwPlatformId == VER_PLATFORM_WIN32_NT)
2740 GetDCOrgEx(hDC, &org);
2743 OffsetRgn (hRgn, org.x, org.y);
2744 GDI_ReleaseObj( hDC );
2748 return GetClipRgn (hDC, hRgn);
2751 WARN("Unknown dwCode %ld\n", dwCode);
2759 /***********************************************************************
2760 * GetMetaRgn (GDI32.@)
2762 INT WINAPI GetMetaRgn( HDC hdc, HRGN hRgn )
2770 /***********************************************************************
2771 * SetMetaRgn (GDI32.@)
2773 INT WINAPI SetMetaRgn( HDC hdc )