Fixes buffer overrun problems with GetDIBits.
[wine.git] / objects / region.c
blob7f53e9d0f4a1569599374de8a34731f97e961bd2
1 /*
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
8 */
10 /************************************************************************
12 Copyright (c) 1987, 1988 X Consortium
14 Permission is hereby granted, free of charge, to any person obtaining a copy
15 of this software and associated documentation files (the "Software"), to deal
16 in the Software without restriction, including without limitation the rights
17 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18 copies of the Software, and to permit persons to whom the Software is
19 furnished to do so, subject to the following conditions:
21 The above copyright notice and this permission notice shall be included in
22 all copies or substantial portions of the Software.
24 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
28 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 Except as contained in this notice, the name of the X Consortium shall not be
32 used in advertising or otherwise to promote the sale, use or other dealings
33 in this Software without prior written authorization from the X Consortium.
36 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
38 All Rights Reserved
40 Permission to use, copy, modify, and distribute this software and its
41 documentation for any purpose and without fee is hereby granted,
42 provided that the above copyright notice appear in all copies and that
43 both that copyright notice and this permission notice appear in
44 supporting documentation, and that the name of Digital not be
45 used in advertising or publicity pertaining to distribution of the
46 software without specific, written prior permission.
48 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
49 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
50 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
51 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
52 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
53 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
54 SOFTWARE.
56 ************************************************************************/
58 * The functions in this file implement the Region abstraction, similar to one
59 * used in the X11 sample server. A Region is simply an area, as the name
60 * implies, and is implemented as a "y-x-banded" array of rectangles. To
61 * explain: Each Region is made up of a certain number of rectangles sorted
62 * by y coordinate first, and then by x coordinate.
64 * Furthermore, the rectangles are banded such that every rectangle with a
65 * given upper-left y coordinate (y1) will have the same lower-right y
66 * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
67 * will span the entire vertical distance of the band. This means that some
68 * areas that could be merged into a taller rectangle will be represented as
69 * several shorter rectangles to account for shorter rectangles to its left
70 * or right but within its "vertical scope".
72 * An added constraint on the rectangles is that they must cover as much
73 * horizontal area as possible. E.g. no two rectangles in a band are allowed
74 * to touch.
76 * Whenever possible, bands will be merged together to cover a greater vertical
77 * distance (and thus reduce the number of rectangles). Two bands can be merged
78 * only if the bottom of one touches the top of the other and they have
79 * rectangles in the same places (of the same width, of course). This maintains
80 * the y-x-banding that's so nice to have...
83 #include "region.h"
84 #include "winuser.h"
85 #include "debug.h"
86 #include "heap.h"
87 #include "dc.h"
89 typedef void (*voidProcp)();
91 /* Note the parameter order is different from the X11 equivalents */
93 static void REGION_CopyRegion(WINEREGION *d, WINEREGION *s);
94 static void REGION_IntersectRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
95 static void REGION_UnionRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
96 static void REGION_SubtractRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
97 static void REGION_XorRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
98 static void REGION_UnionRectWithRegion(const RECT32 *rect, WINEREGION *rgn);
101 /***********************************************************************
102 * REGION_DumpRegion
103 * Outputs the contents of a WINEREGION
105 static void REGION_DumpRegion(WINEREGION *pReg)
107 RECT32 *pRect, *pRectEnd = pReg->rects + pReg->numRects;
109 TRACE(region, "Region %p: %d,%d - %d,%d %d rects\n", pReg,
110 pReg->extents.left, pReg->extents.top,
111 pReg->extents.right, pReg->extents.bottom, pReg->numRects);
112 for(pRect = pReg->rects; pRect < pRectEnd; pRect++)
113 TRACE(region, "\t%d,%d - %d,%d\n", pRect->left, pRect->top,
114 pRect->right, pRect->bottom);
115 return;
118 /***********************************************************************
119 * REGION_AllocWineRegion
120 * Create a new empty WINEREGION.
122 static WINEREGION *REGION_AllocWineRegion( void )
124 WINEREGION *pReg;
126 if ((pReg = HeapAlloc(SystemHeap, 0, sizeof( WINEREGION ))))
128 if ((pReg->rects = HeapAlloc(SystemHeap, 0, sizeof( RECT32 ))))
130 pReg->size = 1;
131 EMPTY_REGION(pReg);
132 return pReg;
134 HeapFree(SystemHeap, 0, pReg);
136 return NULL;
139 /***********************************************************************
140 * REGION_CreateRegion
141 * Create a new empty region.
143 static HRGN32 REGION_CreateRegion(void)
145 HRGN32 hrgn;
146 RGNOBJ *obj;
148 if(!(hrgn = GDI_AllocObject( sizeof(RGNOBJ), REGION_MAGIC )))
149 return 0;
150 obj = (RGNOBJ *) GDI_HEAP_LOCK( hrgn );
151 if(!(obj->rgn = REGION_AllocWineRegion())) {
152 GDI_FreeObject( hrgn );
153 return 0;
155 GDI_HEAP_UNLOCK( hrgn );
156 return hrgn;
159 /***********************************************************************
160 * REGION_DestroyWineRegion
162 static void REGION_DestroyWineRegion( WINEREGION* pReg )
164 HeapFree( SystemHeap, 0, pReg->rects );
165 HeapFree( SystemHeap, 0, pReg );
166 return;
169 /***********************************************************************
170 * REGION_DeleteObject
172 BOOL32 REGION_DeleteObject( HRGN32 hrgn, RGNOBJ * obj )
174 TRACE(region, " %04x\n", hrgn );
176 REGION_DestroyWineRegion( obj->rgn );
177 return GDI_FreeObject( hrgn );
180 /***********************************************************************
181 * OffsetRgn16 (GDI.101)
183 INT16 WINAPI OffsetRgn16( HRGN16 hrgn, INT16 x, INT16 y )
185 return OffsetRgn32( hrgn, x, y );
188 /***********************************************************************
189 * OffsetRgn32 (GDI32.256)
191 INT32 WINAPI OffsetRgn32( HRGN32 hrgn, INT32 x, INT32 y )
193 RGNOBJ * obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
195 if (obj)
197 INT32 ret;
198 int nbox = obj->rgn->numRects;
199 RECT32 *pbox = obj->rgn->rects;
201 TRACE(region, " %04x %d,%d\n", hrgn, x, y );
202 if(nbox && (x || y)) {
203 while(nbox--) {
204 pbox->left += x;
205 pbox->right += x;
206 pbox->top += y;
207 pbox->bottom += y;
208 pbox++;
210 obj->rgn->extents.left += x;
211 obj->rgn->extents.right += x;
212 obj->rgn->extents.top += y;
213 obj->rgn->extents.bottom += y;
215 ret = obj->rgn->type;
216 GDI_HEAP_UNLOCK( hrgn );
217 return ret;
219 return ERROR;
223 /***********************************************************************
224 * GetRgnBox16 (GDI.134)
226 INT16 WINAPI GetRgnBox16( HRGN16 hrgn, LPRECT16 rect )
228 RECT32 r;
229 INT16 ret = (INT16)GetRgnBox32( hrgn, &r );
230 CONV_RECT32TO16( &r, rect );
231 return ret;
234 /***********************************************************************
235 * GetRgnBox32 (GDI32.219)
237 INT32 WINAPI GetRgnBox32( HRGN32 hrgn, LPRECT32 rect )
239 RGNOBJ * obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
240 if (obj)
242 INT32 ret;
243 TRACE(region, " %04x\n", hrgn );
244 rect->left = obj->rgn->extents.left;
245 rect->top = obj->rgn->extents.top;
246 rect->right = obj->rgn->extents.right;
247 rect->bottom = obj->rgn->extents.bottom;
248 ret = obj->rgn->type;
249 GDI_HEAP_UNLOCK(hrgn);
250 return ret;
252 return ERROR;
256 /***********************************************************************
257 * CreateRectRgn16 (GDI.64)
259 * NOTE: Doesn't call CreateRectRgn32 because of differences in SetRectRgn16/32
261 HRGN16 WINAPI CreateRectRgn16(INT16 left, INT16 top, INT16 right, INT16 bottom)
263 HRGN16 hrgn;
265 if (!(hrgn = (HRGN16)REGION_CreateRegion()))
266 return 0;
267 TRACE(region, "\n");
268 SetRectRgn16(hrgn, left, top, right, bottom);
269 return hrgn;
273 /***********************************************************************
274 * CreateRectRgn32 (GDI32.59)
276 HRGN32 WINAPI CreateRectRgn32(INT32 left, INT32 top, INT32 right, INT32 bottom)
278 HRGN32 hrgn;
280 if (!(hrgn = REGION_CreateRegion()))
281 return 0;
282 TRACE(region, "\n");
283 SetRectRgn32(hrgn, left, top, right, bottom);
284 return hrgn;
287 /***********************************************************************
288 * CreateRectRgnIndirect16 (GDI.65)
290 HRGN16 WINAPI CreateRectRgnIndirect16( const RECT16* rect )
292 return CreateRectRgn16( rect->left, rect->top, rect->right, rect->bottom );
296 /***********************************************************************
297 * CreateRectRgnIndirect32 (GDI32.60)
299 HRGN32 WINAPI CreateRectRgnIndirect32( const RECT32* rect )
301 return CreateRectRgn32( rect->left, rect->top, rect->right, rect->bottom );
305 /***********************************************************************
306 * SetRectRgn16 (GDI.172)
308 * NOTE: Win 3.1 sets region to empty if left > right
310 VOID WINAPI SetRectRgn16( HRGN16 hrgn, INT16 left, INT16 top,
311 INT16 right, INT16 bottom )
313 if(left < right)
314 SetRectRgn32( hrgn, left, top, right, bottom );
315 else
316 SetRectRgn32( hrgn, 0, 0, 0, 0 );
320 /***********************************************************************
321 * SetRectRgn32 (GDI32.332)
323 * Allows either or both left and top to be greater than right or bottom.
325 VOID WINAPI SetRectRgn32( HRGN32 hrgn, INT32 left, INT32 top,
326 INT32 right, INT32 bottom )
328 RGNOBJ * obj;
330 TRACE(region, " %04x %d,%d-%d,%d\n",
331 hrgn, left, top, right, bottom );
333 if (!(obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC ))) return;
335 if (left > right) { INT32 tmp = left; left = right; right = tmp; }
336 if (top > bottom) { INT32 tmp = top; top = bottom; bottom = tmp; }
338 if((left != right) && (top != bottom))
340 obj->rgn->rects->left = obj->rgn->extents.left = left;
341 obj->rgn->rects->top = obj->rgn->extents.top = top;
342 obj->rgn->rects->right = obj->rgn->extents.right = right;
343 obj->rgn->rects->bottom = obj->rgn->extents.bottom = bottom;
344 obj->rgn->numRects = 1;
345 obj->rgn->type = SIMPLEREGION;
347 else
348 EMPTY_REGION(obj->rgn);
350 GDI_HEAP_UNLOCK( hrgn );
354 /***********************************************************************
355 * CreateRoundRectRgn16 (GDI.444)
357 * If either ellipse dimension is zero we call CreateRectRgn16 for its
358 * `special' behaviour. -ve ellipse dimensions can result in GPFs under win3.1
359 * we just let CreateRoundRectRgn32 convert them to +ve values.
362 HRGN16 WINAPI CreateRoundRectRgn16( INT16 left, INT16 top,
363 INT16 right, INT16 bottom,
364 INT16 ellipse_width, INT16 ellipse_height )
366 if( ellipse_width == 0 || ellipse_height == 0 )
367 return CreateRectRgn16( left, top, right, bottom );
368 else
369 return (HRGN16)CreateRoundRectRgn32( left, top, right, bottom,
370 ellipse_width, ellipse_height );
373 /***********************************************************************
374 * CreateRoundRectRgn32 (GDI32.61)
376 HRGN32 WINAPI CreateRoundRectRgn32( INT32 left, INT32 top,
377 INT32 right, INT32 bottom,
378 INT32 ellipse_width, INT32 ellipse_height )
380 RGNOBJ * obj;
381 HRGN32 hrgn;
382 int asq, bsq, d, xd, yd;
383 RECT32 rect;
385 /* Check if we can do a normal rectangle instead */
387 if ((ellipse_width == 0) || (ellipse_height == 0))
388 return CreateRectRgn32( left, top, right, bottom );
390 /* Make the dimensions sensible */
392 if (left > right) { INT32 tmp = left; left = right; right = tmp; }
393 if (top > bottom) { INT32 tmp = top; top = bottom; bottom = tmp; }
395 ellipse_width = abs(ellipse_width);
396 ellipse_height = abs(ellipse_height);
398 /* Create region */
400 if (!(hrgn = REGION_CreateRegion())) return 0;
401 obj = (RGNOBJ *) GDI_HEAP_LOCK( hrgn );
402 TRACE(region,"(%d,%d-%d,%d %dx%d): ret=%04x\n",
403 left, top, right, bottom, ellipse_width, ellipse_height, hrgn );
405 /* Check parameters */
407 if (ellipse_width > right-left) ellipse_width = right-left;
408 if (ellipse_height > bottom-top) ellipse_height = bottom-top;
410 /* Ellipse algorithm, based on an article by K. Porter */
411 /* in DDJ Graphics Programming Column, 8/89 */
413 asq = ellipse_width * ellipse_width / 4; /* a^2 */
414 bsq = ellipse_height * ellipse_height / 4; /* b^2 */
415 d = bsq - asq * ellipse_height / 2 + asq / 4; /* b^2 - a^2b + a^2/4 */
416 xd = 0;
417 yd = asq * ellipse_height; /* 2a^2b */
419 rect.left = left + ellipse_width / 2;
420 rect.right = right - ellipse_width / 2;
422 /* Loop to draw first half of quadrant */
424 while (xd < yd)
426 if (d > 0) /* if nearest pixel is toward the center */
428 /* move toward center */
429 rect.top = top++;
430 rect.bottom = rect.top + 1;
431 REGION_UnionRectWithRegion( &rect, obj->rgn );
432 rect.top = --bottom;
433 rect.bottom = rect.top + 1;
434 REGION_UnionRectWithRegion( &rect, obj->rgn );
435 yd -= 2*asq;
436 d -= yd;
438 rect.left--; /* next horiz point */
439 rect.right++;
440 xd += 2*bsq;
441 d += bsq + xd;
444 /* Loop to draw second half of quadrant */
446 d += (3 * (asq-bsq) / 2 - (xd+yd)) / 2;
447 while (yd >= 0)
449 /* next vertical point */
450 rect.top = top++;
451 rect.bottom = rect.top + 1;
452 REGION_UnionRectWithRegion( &rect, obj->rgn );
453 rect.top = --bottom;
454 rect.bottom = rect.top + 1;
455 REGION_UnionRectWithRegion( &rect, obj->rgn );
456 if (d < 0) /* if nearest pixel is outside ellipse */
458 rect.left--; /* move away from center */
459 rect.right++;
460 xd += 2*bsq;
461 d += xd;
463 yd -= 2*asq;
464 d += asq - yd;
467 /* Add the inside rectangle */
469 if (top <= bottom)
471 rect.top = top;
472 rect.bottom = bottom;
473 REGION_UnionRectWithRegion( &rect, obj->rgn );
475 obj->rgn->type = SIMPLEREGION; /* FIXME? */
476 GDI_HEAP_UNLOCK( hrgn );
477 return hrgn;
481 /***********************************************************************
482 * CreateEllipticRgn16 (GDI.54)
484 HRGN16 WINAPI CreateEllipticRgn16( INT16 left, INT16 top,
485 INT16 right, INT16 bottom )
487 return (HRGN16)CreateRoundRectRgn32( left, top, right, bottom,
488 right-left, bottom-top );
492 /***********************************************************************
493 * CreateEllipticRgn32 (GDI32.39)
495 HRGN32 WINAPI CreateEllipticRgn32( INT32 left, INT32 top,
496 INT32 right, INT32 bottom )
498 return CreateRoundRectRgn32( left, top, right, bottom,
499 right-left, bottom-top );
503 /***********************************************************************
504 * CreateEllipticRgnIndirect16 (GDI.55)
506 HRGN16 WINAPI CreateEllipticRgnIndirect16( const RECT16 *rect )
508 return CreateRoundRectRgn32( rect->left, rect->top, rect->right,
509 rect->bottom, rect->right - rect->left,
510 rect->bottom - rect->top );
514 /***********************************************************************
515 * CreateEllipticRgnIndirect32 (GDI32.40)
517 HRGN32 WINAPI CreateEllipticRgnIndirect32( const RECT32 *rect )
519 return CreateRoundRectRgn32( rect->left, rect->top, rect->right,
520 rect->bottom, rect->right - rect->left,
521 rect->bottom - rect->top );
524 /***********************************************************************
525 * GetRegionData32 (GDI32.217)
528 DWORD WINAPI GetRegionData32(HRGN32 hrgn, DWORD count, LPRGNDATA rgndata)
530 DWORD size;
531 RGNOBJ *obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
533 TRACE(region, " %04x count = %ld, rgndata = %p\n",
534 hrgn, count, rgndata);
536 if(!obj) return 0;
538 size = obj->rgn->numRects * sizeof(RECT32);
539 if(count < (size + sizeof(RGNDATAHEADER)) || rgndata == NULL)
541 GDI_HEAP_UNLOCK( hrgn );
542 return size + sizeof(RGNDATAHEADER);
545 rgndata->rdh.dwSize = sizeof(RGNDATAHEADER);
546 rgndata->rdh.iType = RDH_RECTANGLES;
547 rgndata->rdh.nCount = obj->rgn->numRects;
548 rgndata->rdh.nRgnSize = size;
549 rgndata->rdh.rcBound.left = obj->rgn->extents.left;
550 rgndata->rdh.rcBound.top = obj->rgn->extents.top;
551 rgndata->rdh.rcBound.right = obj->rgn->extents.right;
552 rgndata->rdh.rcBound.bottom = obj->rgn->extents.bottom;
554 memcpy( rgndata->Buffer, obj->rgn->rects, size );
556 GDI_HEAP_UNLOCK( hrgn );
557 return 1;
560 /***********************************************************************
561 * GetRegionData16 (GDI.607)
562 * FIXME: is LPRGNDATA the same in Win16 and Win32 ?
564 DWORD WINAPI GetRegionData16(HRGN16 hrgn, DWORD count, LPRGNDATA rgndata)
566 return GetRegionData32((HRGN32)hrgn, count, rgndata);
569 /***********************************************************************
570 * ExtCreateRegion (GDI32.94)
573 HRGN32 WINAPI ExtCreateRegion( const XFORM* lpXform, DWORD dwCount, const RGNDATA* rgndata)
575 HRGN32 hrgn = CreateRectRgn32(0, 0, 0, 0);
576 RGNOBJ *obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
577 RECT32 *pCurRect, *pEndRect;
579 TRACE(region, " %p %ld %p. Returning %04x\n",
580 lpXform, dwCount, rgndata, hrgn);
581 if(!hrgn)
583 WARN(region, "Can't create a region!\n");
584 return 0;
586 if(lpXform)
587 WARN(region, "Xform not implemented - ignoring\n");
589 if(rgndata->rdh.iType != RDH_RECTANGLES)
591 WARN(region, "Type not RDH_RECTANGLES\n");
592 GDI_HEAP_UNLOCK( hrgn );
593 DeleteObject32( hrgn );
594 return 0;
597 pEndRect = (RECT32 *)rgndata->Buffer + rgndata->rdh.nCount;
598 for(pCurRect = (RECT32 *)rgndata->Buffer; pCurRect < pEndRect; pCurRect++)
599 REGION_UnionRectWithRegion( pCurRect, obj->rgn );
601 GDI_HEAP_UNLOCK( hrgn );
602 return hrgn;
605 /***********************************************************************
606 * PtInRegion16 (GDI.161)
608 BOOL16 WINAPI PtInRegion16( HRGN16 hrgn, INT16 x, INT16 y )
610 return PtInRegion32( hrgn, x, y );
614 /***********************************************************************
615 * PtInRegion32 (GDI32.278)
617 BOOL32 WINAPI PtInRegion32( HRGN32 hrgn, INT32 x, INT32 y )
619 RGNOBJ * obj;
621 if ((obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC )))
623 BOOL32 ret = FALSE;
624 int i;
626 if (obj->rgn->numRects > 0 && INRECT(obj->rgn->extents, x, y))
627 for (i = 0; i < obj->rgn->numRects; i++)
628 if (INRECT (obj->rgn->rects[i], x, y))
629 ret = TRUE;
630 GDI_HEAP_UNLOCK( hrgn );
631 return ret;
633 return FALSE;
637 /***********************************************************************
638 * RectInRegion16 (GDI.181)
640 BOOL16 WINAPI RectInRegion16( HRGN16 hrgn, const RECT16 *rect )
642 RECT32 r32;
644 CONV_RECT16TO32(rect, &r32);
645 return (BOOL16)RectInRegion32(hrgn, &r32);
649 /***********************************************************************
650 * RectInRegion32 (GDI32.281)
652 * Returns TRUE if rect is at least partly inside hrgn
654 BOOL32 WINAPI RectInRegion32( HRGN32 hrgn, const RECT32 *rect )
656 RGNOBJ * obj;
658 if ((obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC )))
660 RECT32 *pCurRect, *pRectEnd;
661 BOOL32 ret = FALSE;
663 /* this is (just) a useful optimization */
664 if ((obj->rgn->numRects > 0) && EXTENTCHECK(&obj->rgn->extents,
665 rect))
667 for (pCurRect = obj->rgn->rects, pRectEnd = pCurRect +
668 obj->rgn->numRects; pCurRect < pRectEnd; pCurRect++)
670 if (pCurRect->bottom <= rect->top)
671 continue; /* not far enough down yet */
673 if (pCurRect->top >= rect->bottom) {
674 ret = FALSE; /* too far down */
675 break;
678 if (pCurRect->right <= rect->left)
679 continue; /* not far enough over yet */
681 if (pCurRect->left >= rect->right) {
682 continue;
685 ret = TRUE;
686 break;
689 GDI_HEAP_UNLOCK(hrgn);
690 return ret;
692 return FALSE;
695 /***********************************************************************
696 * EqualRgn16 (GDI.72)
698 BOOL16 WINAPI EqualRgn16( HRGN16 rgn1, HRGN16 rgn2 )
700 return EqualRgn32( rgn1, rgn2 );
704 /***********************************************************************
705 * EqualRgn32 (GDI32.90)
707 BOOL32 WINAPI EqualRgn32( HRGN32 hrgn1, HRGN32 hrgn2 )
709 RGNOBJ *obj1, *obj2;
710 BOOL32 ret = FALSE;
712 if ((obj1 = (RGNOBJ *) GDI_GetObjPtr( hrgn1, REGION_MAGIC )))
714 if ((obj2 = (RGNOBJ *) GDI_GetObjPtr( hrgn2, REGION_MAGIC )))
716 int i;
718 ret = TRUE;
719 if ( obj1->rgn->numRects != obj2->rgn->numRects ) ret = FALSE;
720 else if ( obj1->rgn->numRects == 0 ) ret = TRUE;
721 else if ( !EqualRect32(&obj1->rgn->extents, &obj2->rgn->extents) )
722 ret = FALSE;
723 else for( i = 0; i < obj1->rgn->numRects; i++ ) {
724 if (!EqualRect32(obj1->rgn->rects + i, obj2->rgn->rects + i)) {
725 ret = FALSE;
726 break;
729 GDI_HEAP_UNLOCK(hrgn2);
731 GDI_HEAP_UNLOCK(hrgn1);
733 return ret;
735 /***********************************************************************
736 * REGION_UnionRectWithRegion
737 * Adds a rectangle to a WINEREGION
738 * See below for REGION_UnionRectWithRgn
740 static void REGION_UnionRectWithRegion(const RECT32 *rect, WINEREGION *rgn)
742 WINEREGION region;
744 region.rects = &region.extents;
745 region.numRects = 1;
746 region.size = 1;
747 region.type = SIMPLEREGION;
748 CopyRect32(&(region.extents), rect);
749 REGION_UnionRegion(rgn, rgn, &region);
750 return;
753 /***********************************************************************
754 * REGION_UnionRectWithRgn
755 * Adds a rectangle to a HRGN32
756 * A helper used by scroll.c
758 BOOL32 REGION_UnionRectWithRgn( HRGN32 hrgn, const RECT32 *lpRect )
760 RGNOBJ *obj = (RGNOBJ *) GDI_HEAP_LOCK( hrgn );
762 if(!obj) return FALSE;
763 REGION_UnionRectWithRegion( lpRect, obj->rgn );
764 GDI_HEAP_UNLOCK(hrgn);
765 return TRUE;
768 /***********************************************************************
769 * REGION_CreateFrameRgn
771 * Create a region that is a frame around another region.
772 * Expand all rectangles by +/- x and y, then subtract original region.
774 BOOL32 REGION_FrameRgn( HRGN32 hDest, HRGN32 hSrc, INT32 x, INT32 y )
776 BOOL32 bRet;
777 RGNOBJ *srcObj = (RGNOBJ*) GDI_GetObjPtr( hSrc, REGION_MAGIC );
779 if (srcObj->rgn->numRects != 0)
781 RGNOBJ* destObj = (RGNOBJ*) GDI_GetObjPtr( hDest, REGION_MAGIC );
782 RECT32 *pRect, *pEndRect;
783 RECT32 tempRect;
785 EMPTY_REGION( destObj->rgn );
787 pEndRect = srcObj->rgn->rects + srcObj->rgn->numRects;
788 for(pRect = srcObj->rgn->rects; pRect < pEndRect; pRect++)
790 tempRect.left = pRect->left - x;
791 tempRect.top = pRect->top - y;
792 tempRect.right = pRect->right + x;
793 tempRect.bottom = pRect->bottom + y;
794 REGION_UnionRectWithRegion( &tempRect, destObj->rgn );
796 REGION_SubtractRegion( destObj->rgn, destObj->rgn, srcObj->rgn );
797 GDI_HEAP_UNLOCK ( hDest );
798 bRet = TRUE;
800 else
801 bRet = FALSE;
802 GDI_HEAP_UNLOCK( hSrc );
803 return bRet;
806 /***********************************************************************
807 * REGION_LPTODP
809 * Convert region to device co-ords for the supplied dc.
810 * Used by X11DRV_PaintRgn.
812 BOOL32 REGION_LPTODP( HDC32 hdc, HRGN32 hDest, HRGN32 hSrc )
814 RECT32 *pCurRect, *pEndRect;
815 RGNOBJ *srcObj, *destObj;
816 DC * dc = DC_GetDCPtr( hdc );
817 RECT32 tmpRect;
819 TRACE(region, " hdc=%04x dest=%04x src=%04x\n",
820 hdc, hDest, hSrc) ;
822 if (dc->w.MapMode == MM_TEXT) /* Requires only a translation */
824 if( CombineRgn32( hDest, hSrc, 0, RGN_COPY ) == ERROR ) return FALSE;
825 OffsetRgn32( hDest, dc->vportOrgX - dc->wndOrgX,
826 dc->vportOrgY - dc->wndOrgY );
827 return TRUE;
830 if(!( srcObj = (RGNOBJ *) GDI_GetObjPtr( hSrc, REGION_MAGIC) ))
831 return FALSE;
832 if(!( destObj = (RGNOBJ *) GDI_GetObjPtr( hDest, REGION_MAGIC) ))
834 GDI_HEAP_UNLOCK( hSrc );
835 return FALSE;
837 EMPTY_REGION( destObj->rgn );
839 pEndRect = srcObj->rgn->rects + srcObj->rgn->numRects;
840 for(pCurRect = srcObj->rgn->rects; pCurRect < pEndRect; pCurRect++)
842 tmpRect = *pCurRect;
843 tmpRect.left = XLPTODP( dc, tmpRect.left );
844 tmpRect.top = YLPTODP( dc, tmpRect.top );
845 tmpRect.right = XLPTODP( dc, tmpRect.right );
846 tmpRect.bottom = YLPTODP( dc, tmpRect.bottom );
847 REGION_UnionRectWithRegion( &tmpRect, destObj->rgn );
850 GDI_HEAP_UNLOCK( hDest );
851 GDI_HEAP_UNLOCK( hSrc );
852 return TRUE;
855 /***********************************************************************
856 * CombineRgn16 (GDI.451)
858 INT16 WINAPI CombineRgn16(HRGN16 hDest, HRGN16 hSrc1, HRGN16 hSrc2, INT16 mode)
860 return (INT16)CombineRgn32( hDest, hSrc1, hSrc2, mode );
864 /***********************************************************************
865 * CombineRgn32 (GDI32.19)
867 * Note: The behavior is correct even if src and dest regions are the same.
869 INT32 WINAPI CombineRgn32(HRGN32 hDest, HRGN32 hSrc1, HRGN32 hSrc2, INT32 mode)
871 RGNOBJ *destObj = (RGNOBJ *) GDI_GetObjPtr( hDest, REGION_MAGIC);
872 INT32 result = ERROR;
874 TRACE(region, " %04x,%04x -> %04x mode=%x\n",
875 hSrc1, hSrc2, hDest, mode );
876 if (destObj)
878 RGNOBJ *src1Obj = (RGNOBJ *) GDI_GetObjPtr( hSrc1, REGION_MAGIC);
880 if (src1Obj)
882 TRACE(region, "dump:\n");
883 if(TRACE_ON(region))
884 REGION_DumpRegion(src1Obj->rgn);
885 if (mode == RGN_COPY)
887 REGION_CopyRegion( destObj->rgn, src1Obj->rgn );
888 result = destObj->rgn->type;
890 else
892 RGNOBJ *src2Obj = (RGNOBJ *) GDI_GetObjPtr( hSrc2, REGION_MAGIC);
894 if (src2Obj)
896 TRACE(region, "dump:\n");
897 if(TRACE_ON(region))
898 REGION_DumpRegion(src2Obj->rgn);
899 switch (mode)
901 case RGN_AND:
902 REGION_IntersectRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn);
903 break;
904 case RGN_OR:
905 REGION_UnionRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn );
906 break;
907 case RGN_XOR:
908 REGION_XorRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn );
909 break;
910 case RGN_DIFF:
911 REGION_SubtractRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn );
912 break;
914 result = destObj->rgn->type;
915 GDI_HEAP_UNLOCK( hSrc2 );
918 GDI_HEAP_UNLOCK( hSrc1 );
920 TRACE(region, "dump:\n");
921 if(TRACE_ON(region))
922 REGION_DumpRegion(destObj->rgn);
924 GDI_HEAP_UNLOCK( hDest );
926 return result;
929 /***********************************************************************
930 * REGION_SetExtents
931 * Re-calculate the extents of a region
933 static void REGION_SetExtents (WINEREGION *pReg)
935 RECT32 *pRect, *pRectEnd, *pExtents;
937 if (pReg->numRects == 0)
939 pReg->extents.left = 0;
940 pReg->extents.top = 0;
941 pReg->extents.right = 0;
942 pReg->extents.bottom = 0;
943 return;
946 pExtents = &pReg->extents;
947 pRect = pReg->rects;
948 pRectEnd = &pRect[pReg->numRects - 1];
951 * Since pRect is the first rectangle in the region, it must have the
952 * smallest top and since pRectEnd is the last rectangle in the region,
953 * it must have the largest bottom, because of banding. Initialize left and
954 * right from pRect and pRectEnd, resp., as good things to initialize them
955 * to...
957 pExtents->left = pRect->left;
958 pExtents->top = pRect->top;
959 pExtents->right = pRectEnd->right;
960 pExtents->bottom = pRectEnd->bottom;
962 while (pRect <= pRectEnd)
964 if (pRect->left < pExtents->left)
965 pExtents->left = pRect->left;
966 if (pRect->right > pExtents->right)
967 pExtents->right = pRect->right;
968 pRect++;
972 /***********************************************************************
973 * REGION_CopyRegion
975 static void REGION_CopyRegion(WINEREGION *dst, WINEREGION *src)
977 if (dst != src) /* don't want to copy to itself */
979 if (dst->size < src->numRects)
981 if (! (dst->rects = HeapReAlloc( SystemHeap, 0, dst->rects,
982 src->numRects * sizeof(RECT32) )))
983 return;
984 dst->size = src->numRects;
986 dst->numRects = src->numRects;
987 dst->extents.left = src->extents.left;
988 dst->extents.top = src->extents.top;
989 dst->extents.right = src->extents.right;
990 dst->extents.bottom = src->extents.bottom;
991 dst->type = src->type;
993 memcpy((char *) dst->rects, (char *) src->rects,
994 (int) (src->numRects * sizeof(RECT32)));
996 return;
999 /***********************************************************************
1000 * REGION_Coalesce
1002 * Attempt to merge the rects in the current band with those in the
1003 * previous one. Used only by REGION_RegionOp.
1005 * Results:
1006 * The new index for the previous band.
1008 * Side Effects:
1009 * If coalescing takes place:
1010 * - rectangles in the previous band will have their bottom fields
1011 * altered.
1012 * - pReg->numRects will be decreased.
1015 static INT32 REGION_Coalesce (
1016 WINEREGION *pReg, /* Region to coalesce */
1017 INT32 prevStart, /* Index of start of previous band */
1018 INT32 curStart /* Index of start of current band */
1020 RECT32 *pPrevRect; /* Current rect in previous band */
1021 RECT32 *pCurRect; /* Current rect in current band */
1022 RECT32 *pRegEnd; /* End of region */
1023 INT32 curNumRects; /* Number of rectangles in current band */
1024 INT32 prevNumRects; /* Number of rectangles in previous band */
1025 INT32 bandtop; /* top coordinate for current band */
1027 pRegEnd = &pReg->rects[pReg->numRects];
1029 pPrevRect = &pReg->rects[prevStart];
1030 prevNumRects = curStart - prevStart;
1033 * Figure out how many rectangles are in the current band. Have to do
1034 * this because multiple bands could have been added in REGION_RegionOp
1035 * at the end when one region has been exhausted.
1037 pCurRect = &pReg->rects[curStart];
1038 bandtop = pCurRect->top;
1039 for (curNumRects = 0;
1040 (pCurRect != pRegEnd) && (pCurRect->top == bandtop);
1041 curNumRects++)
1043 pCurRect++;
1046 if (pCurRect != pRegEnd)
1049 * If more than one band was added, we have to find the start
1050 * of the last band added so the next coalescing job can start
1051 * at the right place... (given when multiple bands are added,
1052 * this may be pointless -- see above).
1054 pRegEnd--;
1055 while (pRegEnd[-1].top == pRegEnd->top)
1057 pRegEnd--;
1059 curStart = pRegEnd - pReg->rects;
1060 pRegEnd = pReg->rects + pReg->numRects;
1063 if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
1064 pCurRect -= curNumRects;
1066 * The bands may only be coalesced if the bottom of the previous
1067 * matches the top scanline of the current.
1069 if (pPrevRect->bottom == pCurRect->top)
1072 * Make sure the bands have rects in the same places. This
1073 * assumes that rects have been added in such a way that they
1074 * cover the most area possible. I.e. two rects in a band must
1075 * have some horizontal space between them.
1079 if ((pPrevRect->left != pCurRect->left) ||
1080 (pPrevRect->right != pCurRect->right))
1083 * The bands don't line up so they can't be coalesced.
1085 return (curStart);
1087 pPrevRect++;
1088 pCurRect++;
1089 prevNumRects -= 1;
1090 } while (prevNumRects != 0);
1092 pReg->numRects -= curNumRects;
1093 pCurRect -= curNumRects;
1094 pPrevRect -= curNumRects;
1097 * The bands may be merged, so set the bottom of each rect
1098 * in the previous band to that of the corresponding rect in
1099 * the current band.
1103 pPrevRect->bottom = pCurRect->bottom;
1104 pPrevRect++;
1105 pCurRect++;
1106 curNumRects -= 1;
1107 } while (curNumRects != 0);
1110 * If only one band was added to the region, we have to backup
1111 * curStart to the start of the previous band.
1113 * If more than one band was added to the region, copy the
1114 * other bands down. The assumption here is that the other bands
1115 * came from the same region as the current one and no further
1116 * coalescing can be done on them since it's all been done
1117 * already... curStart is already in the right place.
1119 if (pCurRect == pRegEnd)
1121 curStart = prevStart;
1123 else
1127 *pPrevRect++ = *pCurRect++;
1128 } while (pCurRect != pRegEnd);
1133 return (curStart);
1136 /***********************************************************************
1137 * REGION_RegionOp
1139 * Apply an operation to two regions. Called by REGION_Union,
1140 * REGION_Inverse, REGION_Subtract, REGION_Intersect...
1142 * Results:
1143 * None.
1145 * Side Effects:
1146 * The new region is overwritten.
1148 * Notes:
1149 * The idea behind this function is to view the two regions as sets.
1150 * Together they cover a rectangle of area that this function divides
1151 * into horizontal bands where points are covered only by one region
1152 * or by both. For the first case, the nonOverlapFunc is called with
1153 * each the band and the band's upper and lower extents. For the
1154 * second, the overlapFunc is called to process the entire band. It
1155 * is responsible for clipping the rectangles in the band, though
1156 * this function provides the boundaries.
1157 * At the end of each band, the new region is coalesced, if possible,
1158 * to reduce the number of rectangles in the region.
1161 static void REGION_RegionOp(
1162 WINEREGION *newReg, /* Place to store result */
1163 WINEREGION *reg1, /* First region in operation */
1164 WINEREGION *reg2, /* 2nd region in operation */
1165 void (*overlapFunc)(), /* Function to call for over-lapping bands */
1166 void (*nonOverlap1Func)(), /* Function to call for non-overlapping bands in region 1 */
1167 void (*nonOverlap2Func)() /* Function to call for non-overlapping bands in region 2 */
1169 RECT32 *r1; /* Pointer into first region */
1170 RECT32 *r2; /* Pointer into 2d region */
1171 RECT32 *r1End; /* End of 1st region */
1172 RECT32 *r2End; /* End of 2d region */
1173 INT32 ybot; /* Bottom of intersection */
1174 INT32 ytop; /* Top of intersection */
1175 RECT32 *oldRects; /* Old rects for newReg */
1176 INT32 prevBand; /* Index of start of
1177 * previous band in newReg */
1178 INT32 curBand; /* Index of start of current
1179 * band in newReg */
1180 RECT32 *r1BandEnd; /* End of current band in r1 */
1181 RECT32 *r2BandEnd; /* End of current band in r2 */
1182 INT32 top; /* Top of non-overlapping band */
1183 INT32 bot; /* Bottom of non-overlapping band */
1186 * Initialization:
1187 * set r1, r2, r1End and r2End appropriately, preserve the important
1188 * parts of the destination region until the end in case it's one of
1189 * the two source regions, then mark the "new" region empty, allocating
1190 * another array of rectangles for it to use.
1192 r1 = reg1->rects;
1193 r2 = reg2->rects;
1194 r1End = r1 + reg1->numRects;
1195 r2End = r2 + reg2->numRects;
1199 * newReg may be one of the src regions so we can't empty it. We keep a
1200 * note of its rects pointer (so that we can free them later), preserve its
1201 * extents and simply set numRects to zero.
1204 oldRects = newReg->rects;
1205 newReg->numRects = 0;
1208 * Allocate a reasonable number of rectangles for the new region. The idea
1209 * is to allocate enough so the individual functions don't need to
1210 * reallocate and copy the array, which is time consuming, yet we don't
1211 * have to worry about using too much memory. I hope to be able to
1212 * nuke the Xrealloc() at the end of this function eventually.
1214 newReg->size = MAX(reg1->numRects,reg2->numRects) * 2;
1216 if (! (newReg->rects = HeapAlloc( SystemHeap, 0,
1217 sizeof(RECT32) * newReg->size )))
1219 newReg->size = 0;
1220 return;
1224 * Initialize ybot and ytop.
1225 * In the upcoming loop, ybot and ytop serve different functions depending
1226 * on whether the band being handled is an overlapping or non-overlapping
1227 * band.
1228 * In the case of a non-overlapping band (only one of the regions
1229 * has points in the band), ybot is the bottom of the most recent
1230 * intersection and thus clips the top of the rectangles in that band.
1231 * ytop is the top of the next intersection between the two regions and
1232 * serves to clip the bottom of the rectangles in the current band.
1233 * For an overlapping band (where the two regions intersect), ytop clips
1234 * the top of the rectangles of both regions and ybot clips the bottoms.
1236 if (reg1->extents.top < reg2->extents.top)
1237 ybot = reg1->extents.top;
1238 else
1239 ybot = reg2->extents.top;
1242 * prevBand serves to mark the start of the previous band so rectangles
1243 * can be coalesced into larger rectangles. qv. miCoalesce, above.
1244 * In the beginning, there is no previous band, so prevBand == curBand
1245 * (curBand is set later on, of course, but the first band will always
1246 * start at index 0). prevBand and curBand must be indices because of
1247 * the possible expansion, and resultant moving, of the new region's
1248 * array of rectangles.
1250 prevBand = 0;
1254 curBand = newReg->numRects;
1257 * This algorithm proceeds one source-band (as opposed to a
1258 * destination band, which is determined by where the two regions
1259 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
1260 * rectangle after the last one in the current band for their
1261 * respective regions.
1263 r1BandEnd = r1;
1264 while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top))
1266 r1BandEnd++;
1269 r2BandEnd = r2;
1270 while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top))
1272 r2BandEnd++;
1276 * First handle the band that doesn't intersect, if any.
1278 * Note that attention is restricted to one band in the
1279 * non-intersecting region at once, so if a region has n
1280 * bands between the current position and the next place it overlaps
1281 * the other, this entire loop will be passed through n times.
1283 if (r1->top < r2->top)
1285 top = MAX(r1->top,ybot);
1286 bot = MIN(r1->bottom,r2->top);
1288 if ((top != bot) && (nonOverlap1Func != (void (*)())NULL))
1290 (* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot);
1293 ytop = r2->top;
1295 else if (r2->top < r1->top)
1297 top = MAX(r2->top,ybot);
1298 bot = MIN(r2->bottom,r1->top);
1300 if ((top != bot) && (nonOverlap2Func != (void (*)())NULL))
1302 (* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot);
1305 ytop = r1->top;
1307 else
1309 ytop = r1->top;
1313 * If any rectangles got added to the region, try and coalesce them
1314 * with rectangles from the previous band. Note we could just do
1315 * this test in miCoalesce, but some machines incur a not
1316 * inconsiderable cost for function calls, so...
1318 if (newReg->numRects != curBand)
1320 prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1324 * Now see if we've hit an intersecting band. The two bands only
1325 * intersect if ybot > ytop
1327 ybot = MIN(r1->bottom, r2->bottom);
1328 curBand = newReg->numRects;
1329 if (ybot > ytop)
1331 (* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
1335 if (newReg->numRects != curBand)
1337 prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1341 * If we've finished with a band (bottom == ybot) we skip forward
1342 * in the region to the next band.
1344 if (r1->bottom == ybot)
1346 r1 = r1BandEnd;
1348 if (r2->bottom == ybot)
1350 r2 = r2BandEnd;
1352 } while ((r1 != r1End) && (r2 != r2End));
1355 * Deal with whichever region still has rectangles left.
1357 curBand = newReg->numRects;
1358 if (r1 != r1End)
1360 if (nonOverlap1Func != (void (*)())NULL)
1364 r1BandEnd = r1;
1365 while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top))
1367 r1BandEnd++;
1369 (* nonOverlap1Func) (newReg, r1, r1BandEnd,
1370 MAX(r1->top,ybot), r1->bottom);
1371 r1 = r1BandEnd;
1372 } while (r1 != r1End);
1375 else if ((r2 != r2End) && (nonOverlap2Func != (void (*)())NULL))
1379 r2BandEnd = r2;
1380 while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top))
1382 r2BandEnd++;
1384 (* nonOverlap2Func) (newReg, r2, r2BandEnd,
1385 MAX(r2->top,ybot), r2->bottom);
1386 r2 = r2BandEnd;
1387 } while (r2 != r2End);
1390 if (newReg->numRects != curBand)
1392 (void) REGION_Coalesce (newReg, prevBand, curBand);
1396 * A bit of cleanup. To keep regions from growing without bound,
1397 * we shrink the array of rectangles to match the new number of
1398 * rectangles in the region. This never goes to 0, however...
1400 * Only do this stuff if the number of rectangles allocated is more than
1401 * twice the number of rectangles in the region (a simple optimization...).
1403 if (newReg->numRects < (newReg->size >> 1))
1405 if (REGION_NOT_EMPTY(newReg))
1407 RECT32 *prev_rects = newReg->rects;
1408 newReg->size = newReg->numRects;
1409 newReg->rects = HeapReAlloc( SystemHeap, 0, newReg->rects,
1410 sizeof(RECT32) * newReg->size );
1411 if (! newReg->rects)
1412 newReg->rects = prev_rects;
1414 else
1417 * No point in doing the extra work involved in an Xrealloc if
1418 * the region is empty
1420 newReg->size = 1;
1421 HeapFree( SystemHeap, 0, newReg->rects );
1422 newReg->rects = HeapAlloc( SystemHeap, 0, sizeof(RECT32) );
1425 HeapFree( SystemHeap, 0, oldRects );
1426 return;
1429 /***********************************************************************
1430 * Region Intersection
1431 ***********************************************************************/
1434 /***********************************************************************
1435 * REGION_IntersectO
1437 * Handle an overlapping band for REGION_Intersect.
1439 * Results:
1440 * None.
1442 * Side Effects:
1443 * Rectangles may be added to the region.
1446 static void REGION_IntersectO(WINEREGION *pReg, RECT32 *r1, RECT32 *r1End,
1447 RECT32 *r2, RECT32 *r2End, INT32 top, INT32 bottom)
1450 INT32 left, right;
1451 RECT32 *pNextRect;
1453 pNextRect = &pReg->rects[pReg->numRects];
1455 while ((r1 != r1End) && (r2 != r2End))
1457 left = MAX(r1->left, r2->left);
1458 right = MIN(r1->right, r2->right);
1461 * If there's any overlap between the two rectangles, add that
1462 * overlap to the new region.
1463 * There's no need to check for subsumption because the only way
1464 * such a need could arise is if some region has two rectangles
1465 * right next to each other. Since that should never happen...
1467 if (left < right)
1469 MEMCHECK(pReg, pNextRect, pReg->rects);
1470 pNextRect->left = left;
1471 pNextRect->top = top;
1472 pNextRect->right = right;
1473 pNextRect->bottom = bottom;
1474 pReg->numRects += 1;
1475 pNextRect++;
1479 * Need to advance the pointers. Shift the one that extends
1480 * to the right the least, since the other still has a chance to
1481 * overlap with that region's next rectangle, if you see what I mean.
1483 if (r1->right < r2->right)
1485 r1++;
1487 else if (r2->right < r1->right)
1489 r2++;
1491 else
1493 r1++;
1494 r2++;
1497 return;
1500 /***********************************************************************
1501 * REGION_IntersectRegion
1503 static void REGION_IntersectRegion(WINEREGION *newReg, WINEREGION *reg1,
1504 WINEREGION *reg2)
1506 /* check for trivial reject */
1507 if ( (!(reg1->numRects)) || (!(reg2->numRects)) ||
1508 (!EXTENTCHECK(&reg1->extents, &reg2->extents)))
1509 newReg->numRects = 0;
1510 else
1511 REGION_RegionOp (newReg, reg1, reg2,
1512 (voidProcp) REGION_IntersectO, (voidProcp) NULL, (voidProcp) NULL);
1515 * Can't alter newReg's extents before we call miRegionOp because
1516 * it might be one of the source regions and miRegionOp depends
1517 * on the extents of those regions being the same. Besides, this
1518 * way there's no checking against rectangles that will be nuked
1519 * due to coalescing, so we have to examine fewer rectangles.
1521 REGION_SetExtents(newReg);
1522 newReg->type = (newReg->numRects) ? COMPLEXREGION : NULLREGION ;
1523 return;
1526 /***********************************************************************
1527 * Region Union
1528 ***********************************************************************/
1530 /***********************************************************************
1531 * REGION_UnionNonO
1533 * Handle a non-overlapping band for the union operation. Just
1534 * Adds the rectangles into the region. Doesn't have to check for
1535 * subsumption or anything.
1537 * Results:
1538 * None.
1540 * Side Effects:
1541 * pReg->numRects is incremented and the final rectangles overwritten
1542 * with the rectangles we're passed.
1545 static void REGION_UnionNonO (WINEREGION *pReg, RECT32 *r, RECT32 *rEnd,
1546 INT32 top, INT32 bottom)
1548 RECT32 *pNextRect;
1550 pNextRect = &pReg->rects[pReg->numRects];
1552 while (r != rEnd)
1554 MEMCHECK(pReg, pNextRect, pReg->rects);
1555 pNextRect->left = r->left;
1556 pNextRect->top = top;
1557 pNextRect->right = r->right;
1558 pNextRect->bottom = bottom;
1559 pReg->numRects += 1;
1560 pNextRect++;
1561 r++;
1563 return;
1566 /***********************************************************************
1567 * REGION_UnionO
1569 * Handle an overlapping band for the union operation. Picks the
1570 * left-most rectangle each time and merges it into the region.
1572 * Results:
1573 * None.
1575 * Side Effects:
1576 * Rectangles are overwritten in pReg->rects and pReg->numRects will
1577 * be changed.
1580 static void REGION_UnionO (WINEREGION *pReg, RECT32 *r1, RECT32 *r1End,
1581 RECT32 *r2, RECT32 *r2End, INT32 top, INT32 bottom)
1583 RECT32 *pNextRect;
1585 pNextRect = &pReg->rects[pReg->numRects];
1587 #define MERGERECT(r) \
1588 if ((pReg->numRects != 0) && \
1589 (pNextRect[-1].top == top) && \
1590 (pNextRect[-1].bottom == bottom) && \
1591 (pNextRect[-1].right >= r->left)) \
1593 if (pNextRect[-1].right < r->right) \
1595 pNextRect[-1].right = r->right; \
1598 else \
1600 MEMCHECK(pReg, pNextRect, pReg->rects); \
1601 pNextRect->top = top; \
1602 pNextRect->bottom = bottom; \
1603 pNextRect->left = r->left; \
1604 pNextRect->right = r->right; \
1605 pReg->numRects += 1; \
1606 pNextRect += 1; \
1608 r++;
1610 while ((r1 != r1End) && (r2 != r2End))
1612 if (r1->left < r2->left)
1614 MERGERECT(r1);
1616 else
1618 MERGERECT(r2);
1622 if (r1 != r1End)
1626 MERGERECT(r1);
1627 } while (r1 != r1End);
1629 else while (r2 != r2End)
1631 MERGERECT(r2);
1633 return;
1636 /***********************************************************************
1637 * REGION_UnionRegion
1639 static void REGION_UnionRegion(WINEREGION *newReg, WINEREGION *reg1,
1640 WINEREGION *reg2)
1642 /* checks all the simple cases */
1645 * Region 1 and 2 are the same or region 1 is empty
1647 if ( (reg1 == reg2) || (!(reg1->numRects)) )
1649 if (newReg != reg2)
1650 REGION_CopyRegion(newReg, reg2);
1651 return;
1655 * if nothing to union (region 2 empty)
1657 if (!(reg2->numRects))
1659 if (newReg != reg1)
1660 REGION_CopyRegion(newReg, reg1);
1661 return;
1665 * Region 1 completely subsumes region 2
1667 if ((reg1->numRects == 1) &&
1668 (reg1->extents.left <= reg2->extents.left) &&
1669 (reg1->extents.top <= reg2->extents.top) &&
1670 (reg1->extents.right >= reg2->extents.right) &&
1671 (reg1->extents.bottom >= reg2->extents.bottom))
1673 if (newReg != reg1)
1674 REGION_CopyRegion(newReg, reg1);
1675 return;
1679 * Region 2 completely subsumes region 1
1681 if ((reg2->numRects == 1) &&
1682 (reg2->extents.left <= reg1->extents.left) &&
1683 (reg2->extents.top <= reg1->extents.top) &&
1684 (reg2->extents.right >= reg1->extents.right) &&
1685 (reg2->extents.bottom >= reg1->extents.bottom))
1687 if (newReg != reg2)
1688 REGION_CopyRegion(newReg, reg2);
1689 return;
1692 REGION_RegionOp (newReg, reg1, reg2, (voidProcp) REGION_UnionO,
1693 (voidProcp) REGION_UnionNonO, (voidProcp) REGION_UnionNonO);
1695 newReg->extents.left = MIN(reg1->extents.left, reg2->extents.left);
1696 newReg->extents.top = MIN(reg1->extents.top, reg2->extents.top);
1697 newReg->extents.right = MAX(reg1->extents.right, reg2->extents.right);
1698 newReg->extents.bottom = MAX(reg1->extents.bottom, reg2->extents.bottom);
1699 newReg->type = (newReg->numRects) ? COMPLEXREGION : NULLREGION ;
1700 return;
1703 /***********************************************************************
1704 * Region Subtraction
1705 ***********************************************************************/
1707 /***********************************************************************
1708 * REGION_SubtractNonO1
1710 * Deal with non-overlapping band for subtraction. Any parts from
1711 * region 2 we discard. Anything from region 1 we add to the region.
1713 * Results:
1714 * None.
1716 * Side Effects:
1717 * pReg may be affected.
1720 static void REGION_SubtractNonO1 (WINEREGION *pReg, RECT32 *r, RECT32 *rEnd,
1721 INT32 top, INT32 bottom)
1723 RECT32 *pNextRect;
1725 pNextRect = &pReg->rects[pReg->numRects];
1727 while (r != rEnd)
1729 MEMCHECK(pReg, pNextRect, pReg->rects);
1730 pNextRect->left = r->left;
1731 pNextRect->top = top;
1732 pNextRect->right = r->right;
1733 pNextRect->bottom = bottom;
1734 pReg->numRects += 1;
1735 pNextRect++;
1736 r++;
1738 return;
1742 /***********************************************************************
1743 * REGION_SubtractO
1745 * Overlapping band subtraction. x1 is the left-most point not yet
1746 * checked.
1748 * Results:
1749 * None.
1751 * Side Effects:
1752 * pReg may have rectangles added to it.
1755 static void REGION_SubtractO (WINEREGION *pReg, RECT32 *r1, RECT32 *r1End,
1756 RECT32 *r2, RECT32 *r2End, INT32 top, INT32 bottom)
1758 RECT32 *pNextRect;
1759 INT32 left;
1761 left = r1->left;
1762 pNextRect = &pReg->rects[pReg->numRects];
1764 while ((r1 != r1End) && (r2 != r2End))
1766 if (r2->right <= left)
1769 * Subtrahend missed the boat: go to next subtrahend.
1771 r2++;
1773 else if (r2->left <= left)
1776 * Subtrahend preceeds minuend: nuke left edge of minuend.
1778 left = r2->right;
1779 if (left >= r1->right)
1782 * Minuend completely covered: advance to next minuend and
1783 * reset left fence to edge of new minuend.
1785 r1++;
1786 if (r1 != r1End)
1787 left = r1->left;
1789 else
1792 * Subtrahend now used up since it doesn't extend beyond
1793 * minuend
1795 r2++;
1798 else if (r2->left < r1->right)
1801 * Left part of subtrahend covers part of minuend: add uncovered
1802 * part of minuend to region and skip to next subtrahend.
1804 MEMCHECK(pReg, pNextRect, pReg->rects);
1805 pNextRect->left = left;
1806 pNextRect->top = top;
1807 pNextRect->right = r2->left;
1808 pNextRect->bottom = bottom;
1809 pReg->numRects += 1;
1810 pNextRect++;
1811 left = r2->right;
1812 if (left >= r1->right)
1815 * Minuend used up: advance to new...
1817 r1++;
1818 if (r1 != r1End)
1819 left = r1->left;
1821 else
1824 * Subtrahend used up
1826 r2++;
1829 else
1832 * Minuend used up: add any remaining piece before advancing.
1834 if (r1->right > left)
1836 MEMCHECK(pReg, pNextRect, pReg->rects);
1837 pNextRect->left = left;
1838 pNextRect->top = top;
1839 pNextRect->right = r1->right;
1840 pNextRect->bottom = bottom;
1841 pReg->numRects += 1;
1842 pNextRect++;
1844 r1++;
1845 left = r1->left;
1850 * Add remaining minuend rectangles to region.
1852 while (r1 != r1End)
1854 MEMCHECK(pReg, pNextRect, pReg->rects);
1855 pNextRect->left = left;
1856 pNextRect->top = top;
1857 pNextRect->right = r1->right;
1858 pNextRect->bottom = bottom;
1859 pReg->numRects += 1;
1860 pNextRect++;
1861 r1++;
1862 if (r1 != r1End)
1864 left = r1->left;
1867 return;
1870 /***********************************************************************
1871 * REGION_SubtractRegion
1873 * Subtract regS from regM and leave the result in regD.
1874 * S stands for subtrahend, M for minuend and D for difference.
1876 * Results:
1877 * TRUE.
1879 * Side Effects:
1880 * regD is overwritten.
1883 static void REGION_SubtractRegion(WINEREGION *regD, WINEREGION *regM,
1884 WINEREGION *regS )
1886 /* check for trivial reject */
1887 if ( (!(regM->numRects)) || (!(regS->numRects)) ||
1888 (!EXTENTCHECK(&regM->extents, &regS->extents)) )
1890 REGION_CopyRegion(regD, regM);
1891 return;
1894 REGION_RegionOp (regD, regM, regS, (voidProcp) REGION_SubtractO,
1895 (voidProcp) REGION_SubtractNonO1, (voidProcp) NULL);
1898 * Can't alter newReg's extents before we call miRegionOp because
1899 * it might be one of the source regions and miRegionOp depends
1900 * on the extents of those regions being the unaltered. Besides, this
1901 * way there's no checking against rectangles that will be nuked
1902 * due to coalescing, so we have to examine fewer rectangles.
1904 REGION_SetExtents (regD);
1905 regD->type = (regD->numRects) ? COMPLEXREGION : NULLREGION ;
1906 return;
1909 /***********************************************************************
1910 * REGION_XorRegion
1912 static void REGION_XorRegion(WINEREGION *dr, WINEREGION *sra,
1913 WINEREGION *srb)
1915 WINEREGION *tra, *trb;
1917 if ((! (tra = REGION_AllocWineRegion())) ||
1918 (! (trb = REGION_AllocWineRegion())))
1919 return;
1920 REGION_SubtractRegion(tra,sra,srb);
1921 REGION_SubtractRegion(trb,srb,sra);
1922 REGION_UnionRegion(dr,tra,trb);
1923 REGION_DestroyWineRegion(tra);
1924 REGION_DestroyWineRegion(trb);
1925 return;
1928 /**************************************************************************
1930 * Poly Regions
1932 *************************************************************************/
1934 #define LARGE_COORDINATE 0x7fffffff /* FIXME */
1935 #define SMALL_COORDINATE 0x80000000
1937 /***********************************************************************
1938 * REGION_InsertEdgeInET
1940 * Insert the given edge into the edge table.
1941 * First we must find the correct bucket in the
1942 * Edge table, then find the right slot in the
1943 * bucket. Finally, we can insert it.
1946 static void REGION_InsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE,
1947 INT32 scanline, ScanLineListBlock **SLLBlock, INT32 *iSLLBlock)
1950 EdgeTableEntry *start, *prev;
1951 ScanLineList *pSLL, *pPrevSLL;
1952 ScanLineListBlock *tmpSLLBlock;
1955 * find the right bucket to put the edge into
1957 pPrevSLL = &ET->scanlines;
1958 pSLL = pPrevSLL->next;
1959 while (pSLL && (pSLL->scanline < scanline))
1961 pPrevSLL = pSLL;
1962 pSLL = pSLL->next;
1966 * reassign pSLL (pointer to ScanLineList) if necessary
1968 if ((!pSLL) || (pSLL->scanline > scanline))
1970 if (*iSLLBlock > SLLSPERBLOCK-1)
1972 tmpSLLBlock = HeapAlloc( SystemHeap, 0, sizeof(ScanLineListBlock));
1973 if(!tmpSLLBlock)
1975 WARN(region, "Can't alloc SLLB\n");
1976 return;
1978 (*SLLBlock)->next = tmpSLLBlock;
1979 tmpSLLBlock->next = (ScanLineListBlock *)NULL;
1980 *SLLBlock = tmpSLLBlock;
1981 *iSLLBlock = 0;
1983 pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
1985 pSLL->next = pPrevSLL->next;
1986 pSLL->edgelist = (EdgeTableEntry *)NULL;
1987 pPrevSLL->next = pSLL;
1989 pSLL->scanline = scanline;
1992 * now insert the edge in the right bucket
1994 prev = (EdgeTableEntry *)NULL;
1995 start = pSLL->edgelist;
1996 while (start && (start->bres.minor_axis < ETE->bres.minor_axis))
1998 prev = start;
1999 start = start->next;
2001 ETE->next = start;
2003 if (prev)
2004 prev->next = ETE;
2005 else
2006 pSLL->edgelist = ETE;
2009 /***********************************************************************
2010 * REGION_CreateEdgeTable
2012 * This routine creates the edge table for
2013 * scan converting polygons.
2014 * The Edge Table (ET) looks like:
2016 * EdgeTable
2017 * --------
2018 * | ymax | ScanLineLists
2019 * |scanline|-->------------>-------------->...
2020 * -------- |scanline| |scanline|
2021 * |edgelist| |edgelist|
2022 * --------- ---------
2023 * | |
2024 * | |
2025 * V V
2026 * list of ETEs list of ETEs
2028 * where ETE is an EdgeTableEntry data structure,
2029 * and there is one ScanLineList per scanline at
2030 * which an edge is initially entered.
2033 static void REGION_CreateETandAET(const INT32 *Count, INT32 nbpolygons,
2034 const POINT32 *pts, EdgeTable *ET, EdgeTableEntry *AET,
2035 EdgeTableEntry *pETEs, ScanLineListBlock *pSLLBlock)
2037 const POINT32 *top, *bottom;
2038 const POINT32 *PrevPt, *CurrPt, *EndPt;
2039 INT32 poly, count;
2040 int iSLLBlock = 0;
2041 int dy;
2045 * initialize the Active Edge Table
2047 AET->next = (EdgeTableEntry *)NULL;
2048 AET->back = (EdgeTableEntry *)NULL;
2049 AET->nextWETE = (EdgeTableEntry *)NULL;
2050 AET->bres.minor_axis = SMALL_COORDINATE;
2053 * initialize the Edge Table.
2055 ET->scanlines.next = (ScanLineList *)NULL;
2056 ET->ymax = SMALL_COORDINATE;
2057 ET->ymin = LARGE_COORDINATE;
2058 pSLLBlock->next = (ScanLineListBlock *)NULL;
2060 EndPt = pts - 1;
2061 for(poly = 0; poly < nbpolygons; poly++)
2063 count = Count[poly];
2064 EndPt += count;
2065 if(count < 2)
2066 continue;
2068 PrevPt = EndPt;
2071 * for each vertex in the array of points.
2072 * In this loop we are dealing with two vertices at
2073 * a time -- these make up one edge of the polygon.
2075 while (count--)
2077 CurrPt = pts++;
2080 * find out which point is above and which is below.
2082 if (PrevPt->y > CurrPt->y)
2084 bottom = PrevPt, top = CurrPt;
2085 pETEs->ClockWise = 0;
2087 else
2089 bottom = CurrPt, top = PrevPt;
2090 pETEs->ClockWise = 1;
2094 * don't add horizontal edges to the Edge table.
2096 if (bottom->y != top->y)
2098 pETEs->ymax = bottom->y-1;
2099 /* -1 so we don't get last scanline */
2102 * initialize integer edge algorithm
2104 dy = bottom->y - top->y;
2105 BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres);
2107 REGION_InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock,
2108 &iSLLBlock);
2110 if (PrevPt->y > ET->ymax)
2111 ET->ymax = PrevPt->y;
2112 if (PrevPt->y < ET->ymin)
2113 ET->ymin = PrevPt->y;
2114 pETEs++;
2117 PrevPt = CurrPt;
2122 /***********************************************************************
2123 * REGION_loadAET
2125 * This routine moves EdgeTableEntries from the
2126 * EdgeTable into the Active Edge Table,
2127 * leaving them sorted by smaller x coordinate.
2130 static void REGION_loadAET(EdgeTableEntry *AET, EdgeTableEntry *ETEs)
2132 EdgeTableEntry *pPrevAET;
2133 EdgeTableEntry *tmp;
2135 pPrevAET = AET;
2136 AET = AET->next;
2137 while (ETEs)
2139 while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis))
2141 pPrevAET = AET;
2142 AET = AET->next;
2144 tmp = ETEs->next;
2145 ETEs->next = AET;
2146 if (AET)
2147 AET->back = ETEs;
2148 ETEs->back = pPrevAET;
2149 pPrevAET->next = ETEs;
2150 pPrevAET = ETEs;
2152 ETEs = tmp;
2156 /***********************************************************************
2157 * REGION_computeWAET
2159 * This routine links the AET by the
2160 * nextWETE (winding EdgeTableEntry) link for
2161 * use by the winding number rule. The final
2162 * Active Edge Table (AET) might look something
2163 * like:
2165 * AET
2166 * ---------- --------- ---------
2167 * |ymax | |ymax | |ymax |
2168 * | ... | |... | |... |
2169 * |next |->|next |->|next |->...
2170 * |nextWETE| |nextWETE| |nextWETE|
2171 * --------- --------- ^--------
2172 * | | |
2173 * V-------------------> V---> ...
2176 static void REGION_computeWAET(EdgeTableEntry *AET)
2178 register EdgeTableEntry *pWETE;
2179 register int inside = 1;
2180 register int isInside = 0;
2182 AET->nextWETE = (EdgeTableEntry *)NULL;
2183 pWETE = AET;
2184 AET = AET->next;
2185 while (AET)
2187 if (AET->ClockWise)
2188 isInside++;
2189 else
2190 isInside--;
2192 if ((!inside && !isInside) ||
2193 ( inside && isInside))
2195 pWETE->nextWETE = AET;
2196 pWETE = AET;
2197 inside = !inside;
2199 AET = AET->next;
2201 pWETE->nextWETE = (EdgeTableEntry *)NULL;
2204 /***********************************************************************
2205 * REGION_InsertionSort
2207 * Just a simple insertion sort using
2208 * pointers and back pointers to sort the Active
2209 * Edge Table.
2212 static BOOL32 REGION_InsertionSort(EdgeTableEntry *AET)
2214 EdgeTableEntry *pETEchase;
2215 EdgeTableEntry *pETEinsert;
2216 EdgeTableEntry *pETEchaseBackTMP;
2217 BOOL32 changed = FALSE;
2219 AET = AET->next;
2220 while (AET)
2222 pETEinsert = AET;
2223 pETEchase = AET;
2224 while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
2225 pETEchase = pETEchase->back;
2227 AET = AET->next;
2228 if (pETEchase != pETEinsert)
2230 pETEchaseBackTMP = pETEchase->back;
2231 pETEinsert->back->next = AET;
2232 if (AET)
2233 AET->back = pETEinsert->back;
2234 pETEinsert->next = pETEchase;
2235 pETEchase->back->next = pETEinsert;
2236 pETEchase->back = pETEinsert;
2237 pETEinsert->back = pETEchaseBackTMP;
2238 changed = TRUE;
2241 return changed;
2244 /***********************************************************************
2245 * REGION_FreeStorage
2247 * Clean up our act.
2249 static void REGION_FreeStorage(ScanLineListBlock *pSLLBlock)
2251 ScanLineListBlock *tmpSLLBlock;
2253 while (pSLLBlock)
2255 tmpSLLBlock = pSLLBlock->next;
2256 HeapFree( SystemHeap, 0, pSLLBlock );
2257 pSLLBlock = tmpSLLBlock;
2262 /***********************************************************************
2263 * REGION_PtsToRegion
2265 * Create an array of rectangles from a list of points.
2267 static int REGION_PtsToRegion(int numFullPtBlocks, int iCurPtBlock,
2268 POINTBLOCK *FirstPtBlock, WINEREGION *reg)
2270 RECT32 *rects;
2271 POINT32 *pts;
2272 POINTBLOCK *CurPtBlock;
2273 int i;
2274 RECT32 *extents;
2275 INT32 numRects;
2277 extents = &reg->extents;
2279 numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
2281 if (!(reg->rects = HeapReAlloc( SystemHeap, 0, reg->rects,
2282 sizeof(RECT32) * numRects )))
2283 return(0);
2285 reg->size = numRects;
2286 CurPtBlock = FirstPtBlock;
2287 rects = reg->rects - 1;
2288 numRects = 0;
2289 extents->left = LARGE_COORDINATE, extents->right = SMALL_COORDINATE;
2291 for ( ; numFullPtBlocks >= 0; numFullPtBlocks--) {
2292 /* the loop uses 2 points per iteration */
2293 i = NUMPTSTOBUFFER >> 1;
2294 if (!numFullPtBlocks)
2295 i = iCurPtBlock >> 1;
2296 for (pts = CurPtBlock->pts; i--; pts += 2) {
2297 if (pts->x == pts[1].x)
2298 continue;
2299 if (numRects && pts->x == rects->left && pts->y == rects->bottom &&
2300 pts[1].x == rects->right &&
2301 (numRects == 1 || rects[-1].top != rects->top) &&
2302 (i && pts[2].y > pts[1].y)) {
2303 rects->bottom = pts[1].y + 1;
2304 continue;
2306 numRects++;
2307 rects++;
2308 rects->left = pts->x; rects->top = pts->y;
2309 rects->right = pts[1].x; rects->bottom = pts[1].y + 1;
2310 if (rects->left < extents->left)
2311 extents->left = rects->left;
2312 if (rects->right > extents->right)
2313 extents->right = rects->right;
2315 CurPtBlock = CurPtBlock->next;
2318 if (numRects) {
2319 extents->top = reg->rects->top;
2320 extents->bottom = rects->bottom;
2321 } else {
2322 extents->left = 0;
2323 extents->top = 0;
2324 extents->right = 0;
2325 extents->bottom = 0;
2327 reg->numRects = numRects;
2329 return(TRUE);
2332 /***********************************************************************
2333 * CreatePolyPolygonRgn32 (GDI32.57)
2335 HRGN32 WINAPI CreatePolyPolygonRgn32(const POINT32 *Pts, const INT32 *Count,
2336 INT32 nbpolygons, INT32 mode)
2338 HRGN32 hrgn;
2339 RGNOBJ *obj;
2340 WINEREGION *region;
2341 register EdgeTableEntry *pAET; /* Active Edge Table */
2342 register INT32 y; /* current scanline */
2343 register int iPts = 0; /* number of pts in buffer */
2344 register EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/
2345 register ScanLineList *pSLL; /* current scanLineList */
2346 register POINT32 *pts; /* output buffer */
2347 EdgeTableEntry *pPrevAET; /* ptr to previous AET */
2348 EdgeTable ET; /* header node for ET */
2349 EdgeTableEntry AET; /* header node for AET */
2350 EdgeTableEntry *pETEs; /* EdgeTableEntries pool */
2351 ScanLineListBlock SLLBlock; /* header for scanlinelist */
2352 int fixWAET = FALSE;
2353 POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
2354 POINTBLOCK *tmpPtBlock;
2355 int numFullPtBlocks = 0;
2356 INT32 poly, total;
2358 if(!(hrgn = REGION_CreateRegion()))
2359 return 0;
2360 obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
2361 region = obj->rgn;
2363 /* special case a rectangle */
2365 if (((nbpolygons == 1) && ((*Count == 4) ||
2366 ((*Count == 5) && (Pts[4].x == Pts[0].x) && (Pts[4].y == Pts[0].y)))) &&
2367 (((Pts[0].y == Pts[1].y) &&
2368 (Pts[1].x == Pts[2].x) &&
2369 (Pts[2].y == Pts[3].y) &&
2370 (Pts[3].x == Pts[0].x)) ||
2371 ((Pts[0].x == Pts[1].x) &&
2372 (Pts[1].y == Pts[2].y) &&
2373 (Pts[2].x == Pts[3].x) &&
2374 (Pts[3].y == Pts[0].y))))
2376 SetRectRgn32( hrgn, MIN(Pts[0].x, Pts[2].x), MIN(Pts[0].y, Pts[2].y),
2377 MAX(Pts[0].x, Pts[2].x), MAX(Pts[0].y, Pts[2].y) );
2378 GDI_HEAP_UNLOCK( hrgn );
2379 return hrgn;
2382 for(poly = total = 0; poly < nbpolygons; poly++)
2383 total += Count[poly];
2384 if (! (pETEs = HeapAlloc( SystemHeap, 0, sizeof(EdgeTableEntry) * total )))
2386 REGION_DeleteObject( hrgn, obj );
2387 return 0;
2389 pts = FirstPtBlock.pts;
2390 REGION_CreateETandAET(Count, nbpolygons, Pts, &ET, &AET, pETEs, &SLLBlock);
2391 pSLL = ET.scanlines.next;
2392 curPtBlock = &FirstPtBlock;
2394 if (mode != WINDING) {
2396 * for each scanline
2398 for (y = ET.ymin; y < ET.ymax; y++) {
2400 * Add a new edge to the active edge table when we
2401 * get to the next edge.
2403 if (pSLL != NULL && y == pSLL->scanline) {
2404 REGION_loadAET(&AET, pSLL->edgelist);
2405 pSLL = pSLL->next;
2407 pPrevAET = &AET;
2408 pAET = AET.next;
2411 * for each active edge
2413 while (pAET) {
2414 pts->x = pAET->bres.minor_axis, pts->y = y;
2415 pts++, iPts++;
2418 * send out the buffer
2420 if (iPts == NUMPTSTOBUFFER) {
2421 tmpPtBlock = HeapAlloc( SystemHeap, 0, sizeof(POINTBLOCK));
2422 if(!tmpPtBlock) {
2423 WARN(region, "Can't alloc tPB\n");
2424 return 0;
2426 curPtBlock->next = tmpPtBlock;
2427 curPtBlock = tmpPtBlock;
2428 pts = curPtBlock->pts;
2429 numFullPtBlocks++;
2430 iPts = 0;
2432 EVALUATEEDGEEVENODD(pAET, pPrevAET, y);
2434 REGION_InsertionSort(&AET);
2437 else {
2439 * for each scanline
2441 for (y = ET.ymin; y < ET.ymax; y++) {
2443 * Add a new edge to the active edge table when we
2444 * get to the next edge.
2446 if (pSLL != NULL && y == pSLL->scanline) {
2447 REGION_loadAET(&AET, pSLL->edgelist);
2448 REGION_computeWAET(&AET);
2449 pSLL = pSLL->next;
2451 pPrevAET = &AET;
2452 pAET = AET.next;
2453 pWETE = pAET;
2456 * for each active edge
2458 while (pAET) {
2460 * add to the buffer only those edges that
2461 * are in the Winding active edge table.
2463 if (pWETE == pAET) {
2464 pts->x = pAET->bres.minor_axis, pts->y = y;
2465 pts++, iPts++;
2468 * send out the buffer
2470 if (iPts == NUMPTSTOBUFFER) {
2471 tmpPtBlock = HeapAlloc( SystemHeap, 0,
2472 sizeof(POINTBLOCK) );
2473 if(!tmpPtBlock) {
2474 WARN(region, "Can't alloc tPB\n");
2475 return 0;
2477 curPtBlock->next = tmpPtBlock;
2478 curPtBlock = tmpPtBlock;
2479 pts = curPtBlock->pts;
2480 numFullPtBlocks++; iPts = 0;
2482 pWETE = pWETE->nextWETE;
2484 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
2488 * recompute the winding active edge table if
2489 * we just resorted or have exited an edge.
2491 if (REGION_InsertionSort(&AET) || fixWAET) {
2492 REGION_computeWAET(&AET);
2493 fixWAET = FALSE;
2497 REGION_FreeStorage(SLLBlock.next);
2498 REGION_PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
2499 for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) {
2500 tmpPtBlock = curPtBlock->next;
2501 HeapFree( SystemHeap, 0, curPtBlock );
2502 curPtBlock = tmpPtBlock;
2504 HeapFree( SystemHeap, 0, pETEs );
2505 GDI_HEAP_UNLOCK( hrgn );
2506 return hrgn;
2510 /***********************************************************************
2511 * CreatePolygonRgn16 (GDI.63)
2513 HRGN16 WINAPI CreatePolygonRgn16( const POINT16 * points, INT16 count,
2514 INT16 mode )
2516 return CreatePolyPolygonRgn16( points, &count, 1, mode );
2519 /***********************************************************************
2520 * CreatePolyPolygonRgn16 (GDI.451)
2522 HRGN16 WINAPI CreatePolyPolygonRgn16( const POINT16 *points,
2523 const INT16 *count, INT16 nbpolygons, INT16 mode )
2525 HRGN32 hrgn;
2526 int i, npts = 0;
2527 INT32 *count32;
2528 POINT32 *points32;
2530 for (i = 0; i < nbpolygons; i++)
2531 npts += count[i];
2532 points32 = HeapAlloc( SystemHeap, 0, npts * sizeof(POINT32) );
2533 for (i = 0; i < npts; i++)
2534 CONV_POINT16TO32( &(points[i]), &(points32[i]) );
2536 count32 = HeapAlloc( SystemHeap, 0, nbpolygons * sizeof(INT32) );
2537 for (i = 0; i < nbpolygons; i++)
2538 count32[i] = count[i];
2539 hrgn = CreatePolyPolygonRgn32( points32, count32, nbpolygons, mode );
2540 HeapFree( SystemHeap, 0, count32 );
2541 HeapFree( SystemHeap, 0, points32 );
2542 return hrgn;
2545 /***********************************************************************
2546 * CreatePolygonRgn32 (GDI32.58)
2548 HRGN32 WINAPI CreatePolygonRgn32( const POINT32 *points, INT32 count,
2549 INT32 mode )
2551 return CreatePolyPolygonRgn32( points, &count, 1, mode );
2555 /***********************************************************************
2556 * GetRandomRgn [GDI32.215]
2558 * NOTES
2559 * This function is UNDOCUMENTED, it isn't even in the header file.
2560 * I assume that it will return a HRGN32!??
2562 HRGN32 WINAPI GetRandomRgn(DWORD dwArg1, DWORD dwArg2, DWORD dwArg3)
2564 FIXME (region, "(0x%08lx 0x%08lx 0x%08lx): empty stub!\n",
2565 dwArg1, dwArg2, dwArg3);
2567 return 0;