Murali Pattathe
[wine/multimedia.git] / objects / region.c
blob298782016e82f2b9d2a408ba059b6925bbccdd7c
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
7 * 1999 Alex Korobka
9 */
11 /************************************************************************
13 Copyright (c) 1987, 1988 X Consortium
15 Permission is hereby granted, free of charge, to any person obtaining a copy
16 of this software and associated documentation files (the "Software"), to deal
17 in the Software without restriction, including without limitation the rights
18 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19 copies of the Software, and to permit persons to whom the Software is
20 furnished to do so, subject to the following conditions:
22 The above copyright notice and this permission notice shall be included in
23 all copies or substantial portions of the Software.
25 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
29 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 Except as contained in this notice, the name of the X Consortium shall not be
33 used in advertising or otherwise to promote the sale, use or other dealings
34 in this Software without prior written authorization from the X Consortium.
37 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
39 All Rights Reserved
41 Permission to use, copy, modify, and distribute this software and its
42 documentation for any purpose and without fee is hereby granted,
43 provided that the above copyright notice appear in all copies and that
44 both that copyright notice and this permission notice appear in
45 supporting documentation, and that the name of Digital not be
46 used in advertising or publicity pertaining to distribution of the
47 software without specific, written prior permission.
49 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
50 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
51 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
52 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
53 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
54 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
55 SOFTWARE.
57 ************************************************************************/
59 * The functions in this file implement the Region abstraction, similar to one
60 * used in the X11 sample server. A Region is simply an area, as the name
61 * implies, and is implemented as a "y-x-banded" array of rectangles. To
62 * explain: Each Region is made up of a certain number of rectangles sorted
63 * by y coordinate first, and then by x coordinate.
65 * Furthermore, the rectangles are banded such that every rectangle with a
66 * given upper-left y coordinate (y1) will have the same lower-right y
67 * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
68 * will span the entire vertical distance of the band. This means that some
69 * areas that could be merged into a taller rectangle will be represented as
70 * several shorter rectangles to account for shorter rectangles to its left
71 * or right but within its "vertical scope".
73 * An added constraint on the rectangles is that they must cover as much
74 * horizontal area as possible. E.g. no two rectangles in a band are allowed
75 * to touch.
77 * Whenever possible, bands will be merged together to cover a greater vertical
78 * distance (and thus reduce the number of rectangles). Two bands can be merged
79 * only if the bottom of one touches the top of the other and they have
80 * rectangles in the same places (of the same width, of course). This maintains
81 * the y-x-banding that's so nice to have...
84 #include <stdlib.h>
85 #include <string.h>
86 #include "region.h"
87 #include "winuser.h"
88 #include "debugtools.h"
89 #include "heap.h"
90 #include "dc.h"
92 DEFAULT_DEBUG_CHANNEL(region)
94 typedef void (*voidProcp)();
96 /* Note the parameter order is different from the X11 equivalents */
98 static void REGION_CopyRegion(WINEREGION *d, WINEREGION *s);
99 static void REGION_IntersectRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
100 static void REGION_UnionRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
101 static void REGION_SubtractRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
102 static void REGION_XorRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
103 static void REGION_UnionRectWithRegion(const RECT *rect, WINEREGION *rgn);
105 #define RGN_DEFAULT_RECTS 2
107 /***********************************************************************
108 * REGION_DumpRegion
109 * Outputs the contents of a WINEREGION
111 static void REGION_DumpRegion(WINEREGION *pReg)
113 RECT *pRect, *pRectEnd = pReg->rects + pReg->numRects;
115 TRACE("Region %p: %d,%d - %d,%d %d rects\n", pReg,
116 pReg->extents.left, pReg->extents.top,
117 pReg->extents.right, pReg->extents.bottom, pReg->numRects);
118 for(pRect = pReg->rects; pRect < pRectEnd; pRect++)
119 TRACE("\t%d,%d - %d,%d\n", pRect->left, pRect->top,
120 pRect->right, pRect->bottom);
121 return;
125 /***********************************************************************
126 * REGION_AllocWineRegion
127 * Create a new empty WINEREGION.
129 static WINEREGION *REGION_AllocWineRegion( INT n )
131 WINEREGION *pReg;
133 if ((pReg = HeapAlloc(SystemHeap, 0, sizeof( WINEREGION ))))
135 if ((pReg->rects = HeapAlloc(SystemHeap, 0, n * sizeof( RECT ))))
137 pReg->size = n;
138 EMPTY_REGION(pReg);
139 return pReg;
141 HeapFree(SystemHeap, 0, pReg);
143 return NULL;
147 /***********************************************************************
148 * REGION_CreateRegion
149 * Create a new empty region.
151 static HRGN REGION_CreateRegion( INT n )
153 HRGN hrgn;
154 RGNOBJ *obj;
156 if(!(hrgn = GDI_AllocObject( sizeof(RGNOBJ), REGION_MAGIC )))
157 return 0;
158 obj = (RGNOBJ *) GDI_HEAP_LOCK( hrgn );
159 if(!(obj->rgn = REGION_AllocWineRegion(n))) {
160 GDI_FreeObject( hrgn );
161 return 0;
163 GDI_HEAP_UNLOCK( hrgn );
164 return hrgn;
168 /***********************************************************************
169 * REGION_DestroyWineRegion
171 static void REGION_DestroyWineRegion( WINEREGION* pReg )
173 HeapFree( SystemHeap, 0, pReg->rects );
174 HeapFree( SystemHeap, 0, pReg );
175 return;
178 /***********************************************************************
179 * REGION_DeleteObject
181 BOOL REGION_DeleteObject( HRGN hrgn, RGNOBJ * obj )
183 TRACE(" %04x\n", hrgn );
185 REGION_DestroyWineRegion( obj->rgn );
186 return GDI_FreeObject( hrgn );
189 /***********************************************************************
190 * OffsetRgn16 (GDI.101)
192 INT16 WINAPI OffsetRgn16( HRGN16 hrgn, INT16 x, INT16 y )
194 return OffsetRgn( hrgn, x, y );
197 /***********************************************************************
198 * OffsetRgn32 (GDI32.256)
200 INT WINAPI OffsetRgn( HRGN hrgn, INT x, INT y )
202 RGNOBJ * obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
204 if (obj && (x || y))
206 INT ret;
207 int nbox = obj->rgn->numRects;
208 RECT *pbox = obj->rgn->rects;
210 TRACE(" %04x %d,%d\n", hrgn, x, y );
211 if(nbox) {
212 while(nbox--) {
213 pbox->left += x;
214 pbox->right += x;
215 pbox->top += y;
216 pbox->bottom += y;
217 pbox++;
219 obj->rgn->extents.left += x;
220 obj->rgn->extents.right += x;
221 obj->rgn->extents.top += y;
222 obj->rgn->extents.bottom += y;
224 ret = obj->rgn->type;
225 GDI_HEAP_UNLOCK( hrgn );
226 return ret;
228 return ERROR;
232 /***********************************************************************
233 * GetRgnBox16 (GDI.134)
235 INT16 WINAPI GetRgnBox16( HRGN16 hrgn, LPRECT16 rect )
237 RECT r;
238 INT16 ret = (INT16)GetRgnBox( hrgn, &r );
239 CONV_RECT32TO16( &r, rect );
240 return ret;
243 /***********************************************************************
244 * GetRgnBox32 (GDI32.219)
246 INT WINAPI GetRgnBox( HRGN hrgn, LPRECT rect )
248 RGNOBJ * obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
249 if (obj)
251 INT ret;
252 TRACE(" %04x\n", hrgn );
253 rect->left = obj->rgn->extents.left;
254 rect->top = obj->rgn->extents.top;
255 rect->right = obj->rgn->extents.right;
256 rect->bottom = obj->rgn->extents.bottom;
257 ret = obj->rgn->type;
258 GDI_HEAP_UNLOCK(hrgn);
259 return ret;
261 return ERROR;
265 /***********************************************************************
266 * CreateRectRgn16 (GDI.64)
268 * NOTE: Doesn't call CreateRectRgn32 because of differences in SetRectRgn16/32
270 HRGN16 WINAPI CreateRectRgn16(INT16 left, INT16 top, INT16 right, INT16 bottom)
272 HRGN16 hrgn;
274 if (!(hrgn = (HRGN16)REGION_CreateRegion(RGN_DEFAULT_RECTS)))
275 return 0;
276 TRACE("\n");
277 SetRectRgn16(hrgn, left, top, right, bottom);
278 return hrgn;
282 /***********************************************************************
283 * CreateRectRgn32 (GDI32.59)
285 HRGN WINAPI CreateRectRgn(INT left, INT top, INT right, INT bottom)
287 HRGN hrgn;
289 /* Allocate 2 rects by default to reduce the number of reallocs */
291 if (!(hrgn = REGION_CreateRegion(RGN_DEFAULT_RECTS)))
292 return 0;
293 TRACE("\n");
294 SetRectRgn(hrgn, left, top, right, bottom);
295 return hrgn;
298 /***********************************************************************
299 * CreateRectRgnIndirect16 (GDI.65)
301 HRGN16 WINAPI CreateRectRgnIndirect16( const RECT16* rect )
303 return CreateRectRgn16( rect->left, rect->top, rect->right, rect->bottom );
307 /***********************************************************************
308 * CreateRectRgnIndirect32 (GDI32.60)
310 HRGN WINAPI CreateRectRgnIndirect( const RECT* rect )
312 return CreateRectRgn( rect->left, rect->top, rect->right, rect->bottom );
316 /***********************************************************************
317 * SetRectRgn16 (GDI.172)
319 * NOTE: Win 3.1 sets region to empty if left > right
321 VOID WINAPI SetRectRgn16( HRGN16 hrgn, INT16 left, INT16 top,
322 INT16 right, INT16 bottom )
324 if(left < right)
325 SetRectRgn( hrgn, left, top, right, bottom );
326 else
327 SetRectRgn( hrgn, 0, 0, 0, 0 );
331 /***********************************************************************
332 * SetRectRgn32 (GDI32.332)
334 * Allows either or both left and top to be greater than right or bottom.
336 BOOL WINAPI SetRectRgn( HRGN hrgn, INT left, INT top,
337 INT right, INT bottom )
339 RGNOBJ * obj;
341 TRACE(" %04x %d,%d-%d,%d\n",
342 hrgn, left, top, right, bottom );
344 if (!(obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC ))) return FALSE;
346 if (left > right) { INT tmp = left; left = right; right = tmp; }
347 if (top > bottom) { INT tmp = top; top = bottom; bottom = tmp; }
349 if((left != right) && (top != bottom))
351 obj->rgn->rects->left = obj->rgn->extents.left = left;
352 obj->rgn->rects->top = obj->rgn->extents.top = top;
353 obj->rgn->rects->right = obj->rgn->extents.right = right;
354 obj->rgn->rects->bottom = obj->rgn->extents.bottom = bottom;
355 obj->rgn->numRects = 1;
356 obj->rgn->type = SIMPLEREGION;
358 else
359 EMPTY_REGION(obj->rgn);
361 GDI_HEAP_UNLOCK( hrgn );
362 return TRUE;
366 /***********************************************************************
367 * CreateRoundRectRgn16 (GDI.444)
369 * If either ellipse dimension is zero we call CreateRectRgn16 for its
370 * `special' behaviour. -ve ellipse dimensions can result in GPFs under win3.1
371 * we just let CreateRoundRectRgn32 convert them to +ve values.
374 HRGN16 WINAPI CreateRoundRectRgn16( INT16 left, INT16 top,
375 INT16 right, INT16 bottom,
376 INT16 ellipse_width, INT16 ellipse_height )
378 if( ellipse_width == 0 || ellipse_height == 0 )
379 return CreateRectRgn16( left, top, right, bottom );
380 else
381 return (HRGN16)CreateRoundRectRgn( left, top, right, bottom,
382 ellipse_width, ellipse_height );
385 /***********************************************************************
386 * CreateRoundRectRgn32 (GDI32.61)
388 HRGN WINAPI CreateRoundRectRgn( INT left, INT top,
389 INT right, INT bottom,
390 INT ellipse_width, INT ellipse_height )
392 RGNOBJ * obj;
393 HRGN hrgn;
394 int asq, bsq, d, xd, yd;
395 RECT rect;
397 /* Check if we can do a normal rectangle instead */
399 if ((ellipse_width == 0) || (ellipse_height == 0))
400 return CreateRectRgn( left, top, right, bottom );
402 /* Make the dimensions sensible */
404 if (left > right) { INT tmp = left; left = right; right = tmp; }
405 if (top > bottom) { INT tmp = top; top = bottom; bottom = tmp; }
407 ellipse_width = abs(ellipse_width);
408 ellipse_height = abs(ellipse_height);
410 /* Create region */
412 d = (ellipse_height < 128) ? ((3 * ellipse_height) >> 2) : 64;
413 if (!(hrgn = REGION_CreateRegion(d))) return 0;
414 obj = (RGNOBJ *) GDI_HEAP_LOCK( hrgn );
415 TRACE("(%d,%d-%d,%d %dx%d): ret=%04x\n",
416 left, top, right, bottom, ellipse_width, ellipse_height, hrgn );
418 /* Check parameters */
420 if (ellipse_width > right-left) ellipse_width = right-left;
421 if (ellipse_height > bottom-top) ellipse_height = bottom-top;
423 /* Ellipse algorithm, based on an article by K. Porter */
424 /* in DDJ Graphics Programming Column, 8/89 */
426 asq = ellipse_width * ellipse_width / 4; /* a^2 */
427 bsq = ellipse_height * ellipse_height / 4; /* b^2 */
428 d = bsq - asq * ellipse_height / 2 + asq / 4; /* b^2 - a^2b + a^2/4 */
429 xd = 0;
430 yd = asq * ellipse_height; /* 2a^2b */
432 rect.left = left + ellipse_width / 2;
433 rect.right = right - ellipse_width / 2;
435 /* Loop to draw first half of quadrant */
437 while (xd < yd)
439 if (d > 0) /* if nearest pixel is toward the center */
441 /* move toward center */
442 rect.top = top++;
443 rect.bottom = rect.top + 1;
444 REGION_UnionRectWithRegion( &rect, obj->rgn );
445 rect.top = --bottom;
446 rect.bottom = rect.top + 1;
447 REGION_UnionRectWithRegion( &rect, obj->rgn );
448 yd -= 2*asq;
449 d -= yd;
451 rect.left--; /* next horiz point */
452 rect.right++;
453 xd += 2*bsq;
454 d += bsq + xd;
457 /* Loop to draw second half of quadrant */
459 d += (3 * (asq-bsq) / 2 - (xd+yd)) / 2;
460 while (yd >= 0)
462 /* next vertical point */
463 rect.top = top++;
464 rect.bottom = rect.top + 1;
465 REGION_UnionRectWithRegion( &rect, obj->rgn );
466 rect.top = --bottom;
467 rect.bottom = rect.top + 1;
468 REGION_UnionRectWithRegion( &rect, obj->rgn );
469 if (d < 0) /* if nearest pixel is outside ellipse */
471 rect.left--; /* move away from center */
472 rect.right++;
473 xd += 2*bsq;
474 d += xd;
476 yd -= 2*asq;
477 d += asq - yd;
480 /* Add the inside rectangle */
482 if (top <= bottom)
484 rect.top = top;
485 rect.bottom = bottom;
486 REGION_UnionRectWithRegion( &rect, obj->rgn );
488 obj->rgn->type = SIMPLEREGION; /* FIXME? */
489 GDI_HEAP_UNLOCK( hrgn );
490 return hrgn;
494 /***********************************************************************
495 * CreateEllipticRgn16 (GDI.54)
497 HRGN16 WINAPI CreateEllipticRgn16( INT16 left, INT16 top,
498 INT16 right, INT16 bottom )
500 return (HRGN16)CreateRoundRectRgn( left, top, right, bottom,
501 right-left, bottom-top );
505 /***********************************************************************
506 * CreateEllipticRgn32 (GDI32.39)
508 HRGN WINAPI CreateEllipticRgn( INT left, INT top,
509 INT right, INT bottom )
511 return CreateRoundRectRgn( left, top, right, bottom,
512 right-left, bottom-top );
516 /***********************************************************************
517 * CreateEllipticRgnIndirect16 (GDI.55)
519 HRGN16 WINAPI CreateEllipticRgnIndirect16( const RECT16 *rect )
521 return CreateRoundRectRgn( rect->left, rect->top, rect->right,
522 rect->bottom, rect->right - rect->left,
523 rect->bottom - rect->top );
527 /***********************************************************************
528 * CreateEllipticRgnIndirect32 (GDI32.40)
530 HRGN WINAPI CreateEllipticRgnIndirect( const RECT *rect )
532 return CreateRoundRectRgn( rect->left, rect->top, rect->right,
533 rect->bottom, rect->right - rect->left,
534 rect->bottom - rect->top );
537 /***********************************************************************
538 * GetRegionData32 (GDI32.217)
541 DWORD WINAPI GetRegionData(HRGN hrgn, DWORD count, LPRGNDATA rgndata)
543 DWORD size;
544 RGNOBJ *obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
546 TRACE(" %04x count = %ld, rgndata = %p\n",
547 hrgn, count, rgndata);
549 if(!obj) return 0;
551 size = obj->rgn->numRects * sizeof(RECT);
552 if(count < (size + sizeof(RGNDATAHEADER)) || rgndata == NULL)
554 GDI_HEAP_UNLOCK( hrgn );
555 return size + sizeof(RGNDATAHEADER);
558 rgndata->rdh.dwSize = sizeof(RGNDATAHEADER);
559 rgndata->rdh.iType = RDH_RECTANGLES;
560 rgndata->rdh.nCount = obj->rgn->numRects;
561 rgndata->rdh.nRgnSize = size;
562 rgndata->rdh.rcBound.left = obj->rgn->extents.left;
563 rgndata->rdh.rcBound.top = obj->rgn->extents.top;
564 rgndata->rdh.rcBound.right = obj->rgn->extents.right;
565 rgndata->rdh.rcBound.bottom = obj->rgn->extents.bottom;
567 memcpy( rgndata->Buffer, obj->rgn->rects, size );
569 GDI_HEAP_UNLOCK( hrgn );
570 return 1;
573 /***********************************************************************
574 * GetRegionData16 (GDI.607)
575 * FIXME: is LPRGNDATA the same in Win16 and Win32 ?
577 DWORD WINAPI GetRegionData16(HRGN16 hrgn, DWORD count, LPRGNDATA rgndata)
579 return GetRegionData((HRGN)hrgn, count, rgndata);
582 /***********************************************************************
583 * ExtCreateRegion (GDI32.94)
586 HRGN WINAPI ExtCreateRegion( const XFORM* lpXform, DWORD dwCount, const RGNDATA* rgndata)
588 HRGN hrgn;
590 TRACE(" %p %ld %p = ", lpXform, dwCount, rgndata );
592 if( lpXform )
593 WARN("(Xform not implemented - ignored) ");
595 if( rgndata->rdh.iType != RDH_RECTANGLES )
597 /* FIXME: We can use CreatePolyPolygonRgn() here
598 * for trapezoidal data */
600 WARN("(Unsupported region data) ");
601 goto fail;
604 if( (hrgn = REGION_CreateRegion( rgndata->rdh.nCount )) )
606 RECT *pCurRect, *pEndRect;
607 RGNOBJ *obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
609 pEndRect = (RECT *)rgndata->Buffer + rgndata->rdh.nCount;
610 for(pCurRect = (RECT *)rgndata->Buffer; pCurRect < pEndRect; pCurRect++)
611 REGION_UnionRectWithRegion( pCurRect, obj->rgn );
612 GDI_HEAP_UNLOCK( hrgn );
614 TRACE("%04x\n", hrgn );
615 return hrgn;
617 fail:
618 WARN("Failed\n");
619 return 0;
622 /***********************************************************************
623 * PtInRegion16 (GDI.161)
625 BOOL16 WINAPI PtInRegion16( HRGN16 hrgn, INT16 x, INT16 y )
627 return PtInRegion( hrgn, x, y );
631 /***********************************************************************
632 * PtInRegion32 (GDI32.278)
634 BOOL WINAPI PtInRegion( HRGN hrgn, INT x, INT y )
636 RGNOBJ * obj;
638 if ((obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC )))
640 BOOL ret = FALSE;
641 int i;
643 if (obj->rgn->numRects > 0 && INRECT(obj->rgn->extents, x, y))
644 for (i = 0; i < obj->rgn->numRects; i++)
645 if (INRECT (obj->rgn->rects[i], x, y))
646 ret = TRUE;
647 GDI_HEAP_UNLOCK( hrgn );
648 return ret;
650 return FALSE;
654 /***********************************************************************
655 * RectInRegion16 (GDI.181)
657 BOOL16 WINAPI RectInRegion16( HRGN16 hrgn, const RECT16 *rect )
659 RECT r32;
661 CONV_RECT16TO32(rect, &r32);
662 return (BOOL16)RectInRegion(hrgn, &r32);
666 /***********************************************************************
667 * RectInRegion32 (GDI32.281)
669 * Returns TRUE if rect is at least partly inside hrgn
671 BOOL WINAPI RectInRegion( HRGN hrgn, const RECT *rect )
673 RGNOBJ * obj;
675 if ((obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC )))
677 RECT *pCurRect, *pRectEnd;
678 BOOL ret = FALSE;
680 /* this is (just) a useful optimization */
681 if ((obj->rgn->numRects > 0) && EXTENTCHECK(&obj->rgn->extents,
682 rect))
684 for (pCurRect = obj->rgn->rects, pRectEnd = pCurRect +
685 obj->rgn->numRects; pCurRect < pRectEnd; pCurRect++)
687 if (pCurRect->bottom <= rect->top)
688 continue; /* not far enough down yet */
690 if (pCurRect->top >= rect->bottom) {
691 ret = FALSE; /* too far down */
692 break;
695 if (pCurRect->right <= rect->left)
696 continue; /* not far enough over yet */
698 if (pCurRect->left >= rect->right) {
699 continue;
702 ret = TRUE;
703 break;
706 GDI_HEAP_UNLOCK(hrgn);
707 return ret;
709 return FALSE;
712 /***********************************************************************
713 * EqualRgn16 (GDI.72)
715 BOOL16 WINAPI EqualRgn16( HRGN16 rgn1, HRGN16 rgn2 )
717 return EqualRgn( rgn1, rgn2 );
721 /***********************************************************************
722 * EqualRgn32 (GDI32.90)
724 BOOL WINAPI EqualRgn( HRGN hrgn1, HRGN hrgn2 )
726 RGNOBJ *obj1, *obj2;
727 BOOL ret = FALSE;
729 if ((obj1 = (RGNOBJ *) GDI_GetObjPtr( hrgn1, REGION_MAGIC )))
731 if ((obj2 = (RGNOBJ *) GDI_GetObjPtr( hrgn2, REGION_MAGIC )))
733 int i;
735 ret = TRUE;
736 if ( obj1->rgn->numRects != obj2->rgn->numRects ) ret = FALSE;
737 else if ( obj1->rgn->numRects == 0 ) ret = TRUE;
738 else if ( !EqualRect(&obj1->rgn->extents, &obj2->rgn->extents) )
739 ret = FALSE;
740 else for( i = 0; i < obj1->rgn->numRects; i++ ) {
741 if (!EqualRect(obj1->rgn->rects + i, obj2->rgn->rects + i)) {
742 ret = FALSE;
743 break;
746 GDI_HEAP_UNLOCK(hrgn2);
748 GDI_HEAP_UNLOCK(hrgn1);
750 return ret;
752 /***********************************************************************
753 * REGION_UnionRectWithRegion
754 * Adds a rectangle to a WINEREGION
755 * See below for REGION_UnionRectWithRgn
757 static void REGION_UnionRectWithRegion(const RECT *rect, WINEREGION *rgn)
759 WINEREGION region;
761 region.rects = &region.extents;
762 region.numRects = 1;
763 region.size = 1;
764 region.type = SIMPLEREGION;
765 CopyRect(&(region.extents), rect);
766 REGION_UnionRegion(rgn, rgn, &region);
767 return;
770 /***********************************************************************
771 * REGION_UnionRectWithRgn
772 * Adds a rectangle to a HRGN32
773 * A helper used by scroll.c
775 BOOL REGION_UnionRectWithRgn( HRGN hrgn, const RECT *lpRect )
777 RGNOBJ *obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
779 if(!obj) return FALSE;
780 REGION_UnionRectWithRegion( lpRect, obj->rgn );
781 GDI_HEAP_UNLOCK(hrgn);
782 return TRUE;
785 /***********************************************************************
786 * REGION_CreateFrameRgn
788 * Create a region that is a frame around another region.
789 * Expand all rectangles by +/- x and y, then subtract original region.
791 BOOL REGION_FrameRgn( HRGN hDest, HRGN hSrc, INT x, INT y )
793 BOOL bRet;
794 RGNOBJ *srcObj = (RGNOBJ*) GDI_GetObjPtr( hSrc, REGION_MAGIC );
796 if (srcObj->rgn->numRects != 0)
798 RGNOBJ* destObj = (RGNOBJ*) GDI_GetObjPtr( hDest, REGION_MAGIC );
799 RECT *pRect, *pEndRect;
800 RECT tempRect;
802 EMPTY_REGION( destObj->rgn );
804 pEndRect = srcObj->rgn->rects + srcObj->rgn->numRects;
805 for(pRect = srcObj->rgn->rects; pRect < pEndRect; pRect++)
807 tempRect.left = pRect->left - x;
808 tempRect.top = pRect->top - y;
809 tempRect.right = pRect->right + x;
810 tempRect.bottom = pRect->bottom + y;
811 REGION_UnionRectWithRegion( &tempRect, destObj->rgn );
813 REGION_SubtractRegion( destObj->rgn, destObj->rgn, srcObj->rgn );
814 GDI_HEAP_UNLOCK ( hDest );
815 bRet = TRUE;
817 else
818 bRet = FALSE;
819 GDI_HEAP_UNLOCK( hSrc );
820 return bRet;
823 /***********************************************************************
824 * REGION_LPTODP
826 * Convert region to device co-ords for the supplied dc.
828 BOOL REGION_LPTODP( HDC hdc, HRGN hDest, HRGN hSrc )
830 RECT *pCurRect, *pEndRect;
831 RGNOBJ *srcObj, *destObj;
832 DC * dc = DC_GetDCPtr( hdc );
833 RECT tmpRect;
835 TRACE(" hdc=%04x dest=%04x src=%04x\n",
836 hdc, hDest, hSrc) ;
838 if (dc->w.MapMode == MM_TEXT) /* Requires only a translation */
840 if( CombineRgn( hDest, hSrc, 0, RGN_COPY ) == ERROR ) return FALSE;
841 OffsetRgn( hDest, dc->vportOrgX - dc->wndOrgX,
842 dc->vportOrgY - dc->wndOrgY );
843 return TRUE;
846 if(!( srcObj = (RGNOBJ *) GDI_GetObjPtr( hSrc, REGION_MAGIC) ))
847 return FALSE;
848 if(!( destObj = (RGNOBJ *) GDI_GetObjPtr( hDest, REGION_MAGIC) ))
850 GDI_HEAP_UNLOCK( hSrc );
851 return FALSE;
853 EMPTY_REGION( destObj->rgn );
855 pEndRect = srcObj->rgn->rects + srcObj->rgn->numRects;
856 for(pCurRect = srcObj->rgn->rects; pCurRect < pEndRect; pCurRect++)
858 tmpRect = *pCurRect;
859 tmpRect.left = XLPTODP( dc, tmpRect.left );
860 tmpRect.top = YLPTODP( dc, tmpRect.top );
861 tmpRect.right = XLPTODP( dc, tmpRect.right );
862 tmpRect.bottom = YLPTODP( dc, tmpRect.bottom );
863 REGION_UnionRectWithRegion( &tmpRect, destObj->rgn );
866 GDI_HEAP_UNLOCK( hDest );
867 GDI_HEAP_UNLOCK( hSrc );
868 return TRUE;
871 /***********************************************************************
872 * CombineRgn16 (GDI.451)
874 INT16 WINAPI CombineRgn16(HRGN16 hDest, HRGN16 hSrc1, HRGN16 hSrc2, INT16 mode)
876 return (INT16)CombineRgn( hDest, hSrc1, hSrc2, mode );
880 /***********************************************************************
881 * CombineRgn32 (GDI32.19)
883 * Note: The behavior is correct even if src and dest regions are the same.
885 INT WINAPI CombineRgn(HRGN hDest, HRGN hSrc1, HRGN hSrc2, INT mode)
887 RGNOBJ *destObj = (RGNOBJ *) GDI_GetObjPtr( hDest, REGION_MAGIC);
888 INT result = ERROR;
890 TRACE(" %04x,%04x -> %04x mode=%x\n",
891 hSrc1, hSrc2, hDest, mode );
892 if (destObj)
894 RGNOBJ *src1Obj = (RGNOBJ *) GDI_GetObjPtr( hSrc1, REGION_MAGIC);
896 if (src1Obj)
898 TRACE("dump:\n");
899 if(TRACE_ON(region))
900 REGION_DumpRegion(src1Obj->rgn);
901 if (mode == RGN_COPY)
903 REGION_CopyRegion( destObj->rgn, src1Obj->rgn );
904 result = destObj->rgn->type;
906 else
908 RGNOBJ *src2Obj = (RGNOBJ *) GDI_GetObjPtr( hSrc2, REGION_MAGIC);
910 if (src2Obj)
912 TRACE("dump:\n");
913 if(TRACE_ON(region))
914 REGION_DumpRegion(src2Obj->rgn);
915 switch (mode)
917 case RGN_AND:
918 REGION_IntersectRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn);
919 break;
920 case RGN_OR:
921 REGION_UnionRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn );
922 break;
923 case RGN_XOR:
924 REGION_XorRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn );
925 break;
926 case RGN_DIFF:
927 REGION_SubtractRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn );
928 break;
930 result = destObj->rgn->type;
931 GDI_HEAP_UNLOCK( hSrc2 );
934 GDI_HEAP_UNLOCK( hSrc1 );
936 TRACE("dump:\n");
937 if(TRACE_ON(region))
938 REGION_DumpRegion(destObj->rgn);
940 GDI_HEAP_UNLOCK( hDest );
941 } else {
942 ERR("Invalid rgn=%04x\n", hDest);
944 return result;
947 /***********************************************************************
948 * REGION_SetExtents
949 * Re-calculate the extents of a region
951 static void REGION_SetExtents (WINEREGION *pReg)
953 RECT *pRect, *pRectEnd, *pExtents;
955 if (pReg->numRects == 0)
957 pReg->extents.left = 0;
958 pReg->extents.top = 0;
959 pReg->extents.right = 0;
960 pReg->extents.bottom = 0;
961 return;
964 pExtents = &pReg->extents;
965 pRect = pReg->rects;
966 pRectEnd = &pRect[pReg->numRects - 1];
969 * Since pRect is the first rectangle in the region, it must have the
970 * smallest top and since pRectEnd is the last rectangle in the region,
971 * it must have the largest bottom, because of banding. Initialize left and
972 * right from pRect and pRectEnd, resp., as good things to initialize them
973 * to...
975 pExtents->left = pRect->left;
976 pExtents->top = pRect->top;
977 pExtents->right = pRectEnd->right;
978 pExtents->bottom = pRectEnd->bottom;
980 while (pRect <= pRectEnd)
982 if (pRect->left < pExtents->left)
983 pExtents->left = pRect->left;
984 if (pRect->right > pExtents->right)
985 pExtents->right = pRect->right;
986 pRect++;
990 /***********************************************************************
991 * REGION_CopyRegion
993 static void REGION_CopyRegion(WINEREGION *dst, WINEREGION *src)
995 if (dst != src) /* don't want to copy to itself */
997 if (dst->size < src->numRects)
999 if (! (dst->rects = HeapReAlloc( SystemHeap, 0, dst->rects,
1000 src->numRects * sizeof(RECT) )))
1001 return;
1002 dst->size = src->numRects;
1004 dst->numRects = src->numRects;
1005 dst->extents.left = src->extents.left;
1006 dst->extents.top = src->extents.top;
1007 dst->extents.right = src->extents.right;
1008 dst->extents.bottom = src->extents.bottom;
1009 dst->type = src->type;
1011 memcpy((char *) dst->rects, (char *) src->rects,
1012 (int) (src->numRects * sizeof(RECT)));
1014 return;
1017 /***********************************************************************
1018 * REGION_Coalesce
1020 * Attempt to merge the rects in the current band with those in the
1021 * previous one. Used only by REGION_RegionOp.
1023 * Results:
1024 * The new index for the previous band.
1026 * Side Effects:
1027 * If coalescing takes place:
1028 * - rectangles in the previous band will have their bottom fields
1029 * altered.
1030 * - pReg->numRects will be decreased.
1033 static INT REGION_Coalesce (
1034 WINEREGION *pReg, /* Region to coalesce */
1035 INT prevStart, /* Index of start of previous band */
1036 INT curStart /* Index of start of current band */
1038 RECT *pPrevRect; /* Current rect in previous band */
1039 RECT *pCurRect; /* Current rect in current band */
1040 RECT *pRegEnd; /* End of region */
1041 INT curNumRects; /* Number of rectangles in current band */
1042 INT prevNumRects; /* Number of rectangles in previous band */
1043 INT bandtop; /* top coordinate for current band */
1045 pRegEnd = &pReg->rects[pReg->numRects];
1047 pPrevRect = &pReg->rects[prevStart];
1048 prevNumRects = curStart - prevStart;
1051 * Figure out how many rectangles are in the current band. Have to do
1052 * this because multiple bands could have been added in REGION_RegionOp
1053 * at the end when one region has been exhausted.
1055 pCurRect = &pReg->rects[curStart];
1056 bandtop = pCurRect->top;
1057 for (curNumRects = 0;
1058 (pCurRect != pRegEnd) && (pCurRect->top == bandtop);
1059 curNumRects++)
1061 pCurRect++;
1064 if (pCurRect != pRegEnd)
1067 * If more than one band was added, we have to find the start
1068 * of the last band added so the next coalescing job can start
1069 * at the right place... (given when multiple bands are added,
1070 * this may be pointless -- see above).
1072 pRegEnd--;
1073 while (pRegEnd[-1].top == pRegEnd->top)
1075 pRegEnd--;
1077 curStart = pRegEnd - pReg->rects;
1078 pRegEnd = pReg->rects + pReg->numRects;
1081 if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
1082 pCurRect -= curNumRects;
1084 * The bands may only be coalesced if the bottom of the previous
1085 * matches the top scanline of the current.
1087 if (pPrevRect->bottom == pCurRect->top)
1090 * Make sure the bands have rects in the same places. This
1091 * assumes that rects have been added in such a way that they
1092 * cover the most area possible. I.e. two rects in a band must
1093 * have some horizontal space between them.
1097 if ((pPrevRect->left != pCurRect->left) ||
1098 (pPrevRect->right != pCurRect->right))
1101 * The bands don't line up so they can't be coalesced.
1103 return (curStart);
1105 pPrevRect++;
1106 pCurRect++;
1107 prevNumRects -= 1;
1108 } while (prevNumRects != 0);
1110 pReg->numRects -= curNumRects;
1111 pCurRect -= curNumRects;
1112 pPrevRect -= curNumRects;
1115 * The bands may be merged, so set the bottom of each rect
1116 * in the previous band to that of the corresponding rect in
1117 * the current band.
1121 pPrevRect->bottom = pCurRect->bottom;
1122 pPrevRect++;
1123 pCurRect++;
1124 curNumRects -= 1;
1125 } while (curNumRects != 0);
1128 * If only one band was added to the region, we have to backup
1129 * curStart to the start of the previous band.
1131 * If more than one band was added to the region, copy the
1132 * other bands down. The assumption here is that the other bands
1133 * came from the same region as the current one and no further
1134 * coalescing can be done on them since it's all been done
1135 * already... curStart is already in the right place.
1137 if (pCurRect == pRegEnd)
1139 curStart = prevStart;
1141 else
1145 *pPrevRect++ = *pCurRect++;
1146 } while (pCurRect != pRegEnd);
1151 return (curStart);
1154 /***********************************************************************
1155 * REGION_RegionOp
1157 * Apply an operation to two regions. Called by REGION_Union,
1158 * REGION_Inverse, REGION_Subtract, REGION_Intersect...
1160 * Results:
1161 * None.
1163 * Side Effects:
1164 * The new region is overwritten.
1166 * Notes:
1167 * The idea behind this function is to view the two regions as sets.
1168 * Together they cover a rectangle of area that this function divides
1169 * into horizontal bands where points are covered only by one region
1170 * or by both. For the first case, the nonOverlapFunc is called with
1171 * each the band and the band's upper and lower extents. For the
1172 * second, the overlapFunc is called to process the entire band. It
1173 * is responsible for clipping the rectangles in the band, though
1174 * this function provides the boundaries.
1175 * At the end of each band, the new region is coalesced, if possible,
1176 * to reduce the number of rectangles in the region.
1179 static void REGION_RegionOp(
1180 WINEREGION *newReg, /* Place to store result */
1181 WINEREGION *reg1, /* First region in operation */
1182 WINEREGION *reg2, /* 2nd region in operation */
1183 void (*overlapFunc)(), /* Function to call for over-lapping bands */
1184 void (*nonOverlap1Func)(), /* Function to call for non-overlapping bands in region 1 */
1185 void (*nonOverlap2Func)() /* Function to call for non-overlapping bands in region 2 */
1187 RECT *r1; /* Pointer into first region */
1188 RECT *r2; /* Pointer into 2d region */
1189 RECT *r1End; /* End of 1st region */
1190 RECT *r2End; /* End of 2d region */
1191 INT ybot; /* Bottom of intersection */
1192 INT ytop; /* Top of intersection */
1193 RECT *oldRects; /* Old rects for newReg */
1194 INT prevBand; /* Index of start of
1195 * previous band in newReg */
1196 INT curBand; /* Index of start of current
1197 * band in newReg */
1198 RECT *r1BandEnd; /* End of current band in r1 */
1199 RECT *r2BandEnd; /* End of current band in r2 */
1200 INT top; /* Top of non-overlapping band */
1201 INT bot; /* Bottom of non-overlapping band */
1204 * Initialization:
1205 * set r1, r2, r1End and r2End appropriately, preserve the important
1206 * parts of the destination region until the end in case it's one of
1207 * the two source regions, then mark the "new" region empty, allocating
1208 * another array of rectangles for it to use.
1210 r1 = reg1->rects;
1211 r2 = reg2->rects;
1212 r1End = r1 + reg1->numRects;
1213 r2End = r2 + reg2->numRects;
1217 * newReg may be one of the src regions so we can't empty it. We keep a
1218 * note of its rects pointer (so that we can free them later), preserve its
1219 * extents and simply set numRects to zero.
1222 oldRects = newReg->rects;
1223 newReg->numRects = 0;
1226 * Allocate a reasonable number of rectangles for the new region. The idea
1227 * is to allocate enough so the individual functions don't need to
1228 * reallocate and copy the array, which is time consuming, yet we don't
1229 * have to worry about using too much memory. I hope to be able to
1230 * nuke the Xrealloc() at the end of this function eventually.
1232 newReg->size = MAX(reg1->numRects,reg2->numRects) * 2;
1234 if (! (newReg->rects = HeapAlloc( SystemHeap, 0,
1235 sizeof(RECT) * newReg->size )))
1237 newReg->size = 0;
1238 return;
1242 * Initialize ybot and ytop.
1243 * In the upcoming loop, ybot and ytop serve different functions depending
1244 * on whether the band being handled is an overlapping or non-overlapping
1245 * band.
1246 * In the case of a non-overlapping band (only one of the regions
1247 * has points in the band), ybot is the bottom of the most recent
1248 * intersection and thus clips the top of the rectangles in that band.
1249 * ytop is the top of the next intersection between the two regions and
1250 * serves to clip the bottom of the rectangles in the current band.
1251 * For an overlapping band (where the two regions intersect), ytop clips
1252 * the top of the rectangles of both regions and ybot clips the bottoms.
1254 if (reg1->extents.top < reg2->extents.top)
1255 ybot = reg1->extents.top;
1256 else
1257 ybot = reg2->extents.top;
1260 * prevBand serves to mark the start of the previous band so rectangles
1261 * can be coalesced into larger rectangles. qv. miCoalesce, above.
1262 * In the beginning, there is no previous band, so prevBand == curBand
1263 * (curBand is set later on, of course, but the first band will always
1264 * start at index 0). prevBand and curBand must be indices because of
1265 * the possible expansion, and resultant moving, of the new region's
1266 * array of rectangles.
1268 prevBand = 0;
1272 curBand = newReg->numRects;
1275 * This algorithm proceeds one source-band (as opposed to a
1276 * destination band, which is determined by where the two regions
1277 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
1278 * rectangle after the last one in the current band for their
1279 * respective regions.
1281 r1BandEnd = r1;
1282 while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top))
1284 r1BandEnd++;
1287 r2BandEnd = r2;
1288 while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top))
1290 r2BandEnd++;
1294 * First handle the band that doesn't intersect, if any.
1296 * Note that attention is restricted to one band in the
1297 * non-intersecting region at once, so if a region has n
1298 * bands between the current position and the next place it overlaps
1299 * the other, this entire loop will be passed through n times.
1301 if (r1->top < r2->top)
1303 top = MAX(r1->top,ybot);
1304 bot = MIN(r1->bottom,r2->top);
1306 if ((top != bot) && (nonOverlap1Func != (void (*)())NULL))
1308 (* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot);
1311 ytop = r2->top;
1313 else if (r2->top < r1->top)
1315 top = MAX(r2->top,ybot);
1316 bot = MIN(r2->bottom,r1->top);
1318 if ((top != bot) && (nonOverlap2Func != (void (*)())NULL))
1320 (* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot);
1323 ytop = r1->top;
1325 else
1327 ytop = r1->top;
1331 * If any rectangles got added to the region, try and coalesce them
1332 * with rectangles from the previous band. Note we could just do
1333 * this test in miCoalesce, but some machines incur a not
1334 * inconsiderable cost for function calls, so...
1336 if (newReg->numRects != curBand)
1338 prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1342 * Now see if we've hit an intersecting band. The two bands only
1343 * intersect if ybot > ytop
1345 ybot = MIN(r1->bottom, r2->bottom);
1346 curBand = newReg->numRects;
1347 if (ybot > ytop)
1349 (* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
1353 if (newReg->numRects != curBand)
1355 prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1359 * If we've finished with a band (bottom == ybot) we skip forward
1360 * in the region to the next band.
1362 if (r1->bottom == ybot)
1364 r1 = r1BandEnd;
1366 if (r2->bottom == ybot)
1368 r2 = r2BandEnd;
1370 } while ((r1 != r1End) && (r2 != r2End));
1373 * Deal with whichever region still has rectangles left.
1375 curBand = newReg->numRects;
1376 if (r1 != r1End)
1378 if (nonOverlap1Func != (void (*)())NULL)
1382 r1BandEnd = r1;
1383 while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top))
1385 r1BandEnd++;
1387 (* nonOverlap1Func) (newReg, r1, r1BandEnd,
1388 MAX(r1->top,ybot), r1->bottom);
1389 r1 = r1BandEnd;
1390 } while (r1 != r1End);
1393 else if ((r2 != r2End) && (nonOverlap2Func != (void (*)())NULL))
1397 r2BandEnd = r2;
1398 while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top))
1400 r2BandEnd++;
1402 (* nonOverlap2Func) (newReg, r2, r2BandEnd,
1403 MAX(r2->top,ybot), r2->bottom);
1404 r2 = r2BandEnd;
1405 } while (r2 != r2End);
1408 if (newReg->numRects != curBand)
1410 (void) REGION_Coalesce (newReg, prevBand, curBand);
1414 * A bit of cleanup. To keep regions from growing without bound,
1415 * we shrink the array of rectangles to match the new number of
1416 * rectangles in the region. This never goes to 0, however...
1418 * Only do this stuff if the number of rectangles allocated is more than
1419 * twice the number of rectangles in the region (a simple optimization...).
1421 if ((newReg->numRects < (newReg->size >> 1)) && (newReg->numRects > 2))
1423 if (REGION_NOT_EMPTY(newReg))
1425 RECT *prev_rects = newReg->rects;
1426 newReg->size = newReg->numRects;
1427 newReg->rects = HeapReAlloc( SystemHeap, 0, newReg->rects,
1428 sizeof(RECT) * newReg->size );
1429 if (! newReg->rects)
1430 newReg->rects = prev_rects;
1432 else
1435 * No point in doing the extra work involved in an Xrealloc if
1436 * the region is empty
1438 newReg->size = 1;
1439 HeapFree( SystemHeap, 0, newReg->rects );
1440 newReg->rects = HeapAlloc( SystemHeap, 0, sizeof(RECT) );
1443 HeapFree( SystemHeap, 0, oldRects );
1444 return;
1447 /***********************************************************************
1448 * Region Intersection
1449 ***********************************************************************/
1452 /***********************************************************************
1453 * REGION_IntersectO
1455 * Handle an overlapping band for REGION_Intersect.
1457 * Results:
1458 * None.
1460 * Side Effects:
1461 * Rectangles may be added to the region.
1464 static void REGION_IntersectO(WINEREGION *pReg, RECT *r1, RECT *r1End,
1465 RECT *r2, RECT *r2End, INT top, INT bottom)
1468 INT left, right;
1469 RECT *pNextRect;
1471 pNextRect = &pReg->rects[pReg->numRects];
1473 while ((r1 != r1End) && (r2 != r2End))
1475 left = MAX(r1->left, r2->left);
1476 right = MIN(r1->right, r2->right);
1479 * If there's any overlap between the two rectangles, add that
1480 * overlap to the new region.
1481 * There's no need to check for subsumption because the only way
1482 * such a need could arise is if some region has two rectangles
1483 * right next to each other. Since that should never happen...
1485 if (left < right)
1487 MEMCHECK(pReg, pNextRect, pReg->rects);
1488 pNextRect->left = left;
1489 pNextRect->top = top;
1490 pNextRect->right = right;
1491 pNextRect->bottom = bottom;
1492 pReg->numRects += 1;
1493 pNextRect++;
1497 * Need to advance the pointers. Shift the one that extends
1498 * to the right the least, since the other still has a chance to
1499 * overlap with that region's next rectangle, if you see what I mean.
1501 if (r1->right < r2->right)
1503 r1++;
1505 else if (r2->right < r1->right)
1507 r2++;
1509 else
1511 r1++;
1512 r2++;
1515 return;
1518 /***********************************************************************
1519 * REGION_IntersectRegion
1521 static void REGION_IntersectRegion(WINEREGION *newReg, WINEREGION *reg1,
1522 WINEREGION *reg2)
1524 /* check for trivial reject */
1525 if ( (!(reg1->numRects)) || (!(reg2->numRects)) ||
1526 (!EXTENTCHECK(&reg1->extents, &reg2->extents)))
1527 newReg->numRects = 0;
1528 else
1529 REGION_RegionOp (newReg, reg1, reg2,
1530 (voidProcp) REGION_IntersectO, (voidProcp) NULL, (voidProcp) NULL);
1533 * Can't alter newReg's extents before we call miRegionOp because
1534 * it might be one of the source regions and miRegionOp depends
1535 * on the extents of those regions being the same. Besides, this
1536 * way there's no checking against rectangles that will be nuked
1537 * due to coalescing, so we have to examine fewer rectangles.
1539 REGION_SetExtents(newReg);
1540 newReg->type = (newReg->numRects) ? COMPLEXREGION : NULLREGION ;
1541 return;
1544 /***********************************************************************
1545 * Region Union
1546 ***********************************************************************/
1548 /***********************************************************************
1549 * REGION_UnionNonO
1551 * Handle a non-overlapping band for the union operation. Just
1552 * Adds the rectangles into the region. Doesn't have to check for
1553 * subsumption or anything.
1555 * Results:
1556 * None.
1558 * Side Effects:
1559 * pReg->numRects is incremented and the final rectangles overwritten
1560 * with the rectangles we're passed.
1563 static void REGION_UnionNonO (WINEREGION *pReg, RECT *r, RECT *rEnd,
1564 INT top, INT bottom)
1566 RECT *pNextRect;
1568 pNextRect = &pReg->rects[pReg->numRects];
1570 while (r != rEnd)
1572 MEMCHECK(pReg, pNextRect, pReg->rects);
1573 pNextRect->left = r->left;
1574 pNextRect->top = top;
1575 pNextRect->right = r->right;
1576 pNextRect->bottom = bottom;
1577 pReg->numRects += 1;
1578 pNextRect++;
1579 r++;
1581 return;
1584 /***********************************************************************
1585 * REGION_UnionO
1587 * Handle an overlapping band for the union operation. Picks the
1588 * left-most rectangle each time and merges it into the region.
1590 * Results:
1591 * None.
1593 * Side Effects:
1594 * Rectangles are overwritten in pReg->rects and pReg->numRects will
1595 * be changed.
1598 static void REGION_UnionO (WINEREGION *pReg, RECT *r1, RECT *r1End,
1599 RECT *r2, RECT *r2End, INT top, INT bottom)
1601 RECT *pNextRect;
1603 pNextRect = &pReg->rects[pReg->numRects];
1605 #define MERGERECT(r) \
1606 if ((pReg->numRects != 0) && \
1607 (pNextRect[-1].top == top) && \
1608 (pNextRect[-1].bottom == bottom) && \
1609 (pNextRect[-1].right >= r->left)) \
1611 if (pNextRect[-1].right < r->right) \
1613 pNextRect[-1].right = r->right; \
1616 else \
1618 MEMCHECK(pReg, pNextRect, pReg->rects); \
1619 pNextRect->top = top; \
1620 pNextRect->bottom = bottom; \
1621 pNextRect->left = r->left; \
1622 pNextRect->right = r->right; \
1623 pReg->numRects += 1; \
1624 pNextRect += 1; \
1626 r++;
1628 while ((r1 != r1End) && (r2 != r2End))
1630 if (r1->left < r2->left)
1632 MERGERECT(r1);
1634 else
1636 MERGERECT(r2);
1640 if (r1 != r1End)
1644 MERGERECT(r1);
1645 } while (r1 != r1End);
1647 else while (r2 != r2End)
1649 MERGERECT(r2);
1651 return;
1654 /***********************************************************************
1655 * REGION_UnionRegion
1657 static void REGION_UnionRegion(WINEREGION *newReg, WINEREGION *reg1,
1658 WINEREGION *reg2)
1660 /* checks all the simple cases */
1663 * Region 1 and 2 are the same or region 1 is empty
1665 if ( (reg1 == reg2) || (!(reg1->numRects)) )
1667 if (newReg != reg2)
1668 REGION_CopyRegion(newReg, reg2);
1669 return;
1673 * if nothing to union (region 2 empty)
1675 if (!(reg2->numRects))
1677 if (newReg != reg1)
1678 REGION_CopyRegion(newReg, reg1);
1679 return;
1683 * Region 1 completely subsumes region 2
1685 if ((reg1->numRects == 1) &&
1686 (reg1->extents.left <= reg2->extents.left) &&
1687 (reg1->extents.top <= reg2->extents.top) &&
1688 (reg1->extents.right >= reg2->extents.right) &&
1689 (reg1->extents.bottom >= reg2->extents.bottom))
1691 if (newReg != reg1)
1692 REGION_CopyRegion(newReg, reg1);
1693 return;
1697 * Region 2 completely subsumes region 1
1699 if ((reg2->numRects == 1) &&
1700 (reg2->extents.left <= reg1->extents.left) &&
1701 (reg2->extents.top <= reg1->extents.top) &&
1702 (reg2->extents.right >= reg1->extents.right) &&
1703 (reg2->extents.bottom >= reg1->extents.bottom))
1705 if (newReg != reg2)
1706 REGION_CopyRegion(newReg, reg2);
1707 return;
1710 REGION_RegionOp (newReg, reg1, reg2, (voidProcp) REGION_UnionO,
1711 (voidProcp) REGION_UnionNonO, (voidProcp) REGION_UnionNonO);
1713 newReg->extents.left = MIN(reg1->extents.left, reg2->extents.left);
1714 newReg->extents.top = MIN(reg1->extents.top, reg2->extents.top);
1715 newReg->extents.right = MAX(reg1->extents.right, reg2->extents.right);
1716 newReg->extents.bottom = MAX(reg1->extents.bottom, reg2->extents.bottom);
1717 newReg->type = (newReg->numRects) ? COMPLEXREGION : NULLREGION ;
1718 return;
1721 /***********************************************************************
1722 * Region Subtraction
1723 ***********************************************************************/
1725 /***********************************************************************
1726 * REGION_SubtractNonO1
1728 * Deal with non-overlapping band for subtraction. Any parts from
1729 * region 2 we discard. Anything from region 1 we add to the region.
1731 * Results:
1732 * None.
1734 * Side Effects:
1735 * pReg may be affected.
1738 static void REGION_SubtractNonO1 (WINEREGION *pReg, RECT *r, RECT *rEnd,
1739 INT top, INT bottom)
1741 RECT *pNextRect;
1743 pNextRect = &pReg->rects[pReg->numRects];
1745 while (r != rEnd)
1747 MEMCHECK(pReg, pNextRect, pReg->rects);
1748 pNextRect->left = r->left;
1749 pNextRect->top = top;
1750 pNextRect->right = r->right;
1751 pNextRect->bottom = bottom;
1752 pReg->numRects += 1;
1753 pNextRect++;
1754 r++;
1756 return;
1760 /***********************************************************************
1761 * REGION_SubtractO
1763 * Overlapping band subtraction. x1 is the left-most point not yet
1764 * checked.
1766 * Results:
1767 * None.
1769 * Side Effects:
1770 * pReg may have rectangles added to it.
1773 static void REGION_SubtractO (WINEREGION *pReg, RECT *r1, RECT *r1End,
1774 RECT *r2, RECT *r2End, INT top, INT bottom)
1776 RECT *pNextRect;
1777 INT left;
1779 left = r1->left;
1780 pNextRect = &pReg->rects[pReg->numRects];
1782 while ((r1 != r1End) && (r2 != r2End))
1784 if (r2->right <= left)
1787 * Subtrahend missed the boat: go to next subtrahend.
1789 r2++;
1791 else if (r2->left <= left)
1794 * Subtrahend preceeds minuend: nuke left edge of minuend.
1796 left = r2->right;
1797 if (left >= r1->right)
1800 * Minuend completely covered: advance to next minuend and
1801 * reset left fence to edge of new minuend.
1803 r1++;
1804 if (r1 != r1End)
1805 left = r1->left;
1807 else
1810 * Subtrahend now used up since it doesn't extend beyond
1811 * minuend
1813 r2++;
1816 else if (r2->left < r1->right)
1819 * Left part of subtrahend covers part of minuend: add uncovered
1820 * part of minuend to region and skip to next subtrahend.
1822 MEMCHECK(pReg, pNextRect, pReg->rects);
1823 pNextRect->left = left;
1824 pNextRect->top = top;
1825 pNextRect->right = r2->left;
1826 pNextRect->bottom = bottom;
1827 pReg->numRects += 1;
1828 pNextRect++;
1829 left = r2->right;
1830 if (left >= r1->right)
1833 * Minuend used up: advance to new...
1835 r1++;
1836 if (r1 != r1End)
1837 left = r1->left;
1839 else
1842 * Subtrahend used up
1844 r2++;
1847 else
1850 * Minuend used up: add any remaining piece before advancing.
1852 if (r1->right > left)
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++;
1862 r1++;
1863 left = r1->left;
1868 * Add remaining minuend rectangles to region.
1870 while (r1 != r1End)
1872 MEMCHECK(pReg, pNextRect, pReg->rects);
1873 pNextRect->left = left;
1874 pNextRect->top = top;
1875 pNextRect->right = r1->right;
1876 pNextRect->bottom = bottom;
1877 pReg->numRects += 1;
1878 pNextRect++;
1879 r1++;
1880 if (r1 != r1End)
1882 left = r1->left;
1885 return;
1888 /***********************************************************************
1889 * REGION_SubtractRegion
1891 * Subtract regS from regM and leave the result in regD.
1892 * S stands for subtrahend, M for minuend and D for difference.
1894 * Results:
1895 * TRUE.
1897 * Side Effects:
1898 * regD is overwritten.
1901 static void REGION_SubtractRegion(WINEREGION *regD, WINEREGION *regM,
1902 WINEREGION *regS )
1904 /* check for trivial reject */
1905 if ( (!(regM->numRects)) || (!(regS->numRects)) ||
1906 (!EXTENTCHECK(&regM->extents, &regS->extents)) )
1908 REGION_CopyRegion(regD, regM);
1909 return;
1912 REGION_RegionOp (regD, regM, regS, (voidProcp) REGION_SubtractO,
1913 (voidProcp) REGION_SubtractNonO1, (voidProcp) NULL);
1916 * Can't alter newReg's extents before we call miRegionOp because
1917 * it might be one of the source regions and miRegionOp depends
1918 * on the extents of those regions being the unaltered. Besides, this
1919 * way there's no checking against rectangles that will be nuked
1920 * due to coalescing, so we have to examine fewer rectangles.
1922 REGION_SetExtents (regD);
1923 regD->type = (regD->numRects) ? COMPLEXREGION : NULLREGION ;
1924 return;
1927 /***********************************************************************
1928 * REGION_XorRegion
1930 static void REGION_XorRegion(WINEREGION *dr, WINEREGION *sra,
1931 WINEREGION *srb)
1933 WINEREGION *tra, *trb;
1935 if ((! (tra = REGION_AllocWineRegion(sra->numRects + 1))) ||
1936 (! (trb = REGION_AllocWineRegion(srb->numRects + 1))))
1937 return;
1938 REGION_SubtractRegion(tra,sra,srb);
1939 REGION_SubtractRegion(trb,srb,sra);
1940 REGION_UnionRegion(dr,tra,trb);
1941 REGION_DestroyWineRegion(tra);
1942 REGION_DestroyWineRegion(trb);
1943 return;
1946 /**************************************************************************
1948 * Poly Regions
1950 *************************************************************************/
1952 #define LARGE_COORDINATE 0x7fffffff /* FIXME */
1953 #define SMALL_COORDINATE 0x80000000
1955 /***********************************************************************
1956 * REGION_InsertEdgeInET
1958 * Insert the given edge into the edge table.
1959 * First we must find the correct bucket in the
1960 * Edge table, then find the right slot in the
1961 * bucket. Finally, we can insert it.
1964 static void REGION_InsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE,
1965 INT scanline, ScanLineListBlock **SLLBlock, INT *iSLLBlock)
1968 EdgeTableEntry *start, *prev;
1969 ScanLineList *pSLL, *pPrevSLL;
1970 ScanLineListBlock *tmpSLLBlock;
1973 * find the right bucket to put the edge into
1975 pPrevSLL = &ET->scanlines;
1976 pSLL = pPrevSLL->next;
1977 while (pSLL && (pSLL->scanline < scanline))
1979 pPrevSLL = pSLL;
1980 pSLL = pSLL->next;
1984 * reassign pSLL (pointer to ScanLineList) if necessary
1986 if ((!pSLL) || (pSLL->scanline > scanline))
1988 if (*iSLLBlock > SLLSPERBLOCK-1)
1990 tmpSLLBlock = HeapAlloc( SystemHeap, 0, sizeof(ScanLineListBlock));
1991 if(!tmpSLLBlock)
1993 WARN("Can't alloc SLLB\n");
1994 return;
1996 (*SLLBlock)->next = tmpSLLBlock;
1997 tmpSLLBlock->next = (ScanLineListBlock *)NULL;
1998 *SLLBlock = tmpSLLBlock;
1999 *iSLLBlock = 0;
2001 pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
2003 pSLL->next = pPrevSLL->next;
2004 pSLL->edgelist = (EdgeTableEntry *)NULL;
2005 pPrevSLL->next = pSLL;
2007 pSLL->scanline = scanline;
2010 * now insert the edge in the right bucket
2012 prev = (EdgeTableEntry *)NULL;
2013 start = pSLL->edgelist;
2014 while (start && (start->bres.minor_axis < ETE->bres.minor_axis))
2016 prev = start;
2017 start = start->next;
2019 ETE->next = start;
2021 if (prev)
2022 prev->next = ETE;
2023 else
2024 pSLL->edgelist = ETE;
2027 /***********************************************************************
2028 * REGION_CreateEdgeTable
2030 * This routine creates the edge table for
2031 * scan converting polygons.
2032 * The Edge Table (ET) looks like:
2034 * EdgeTable
2035 * --------
2036 * | ymax | ScanLineLists
2037 * |scanline|-->------------>-------------->...
2038 * -------- |scanline| |scanline|
2039 * |edgelist| |edgelist|
2040 * --------- ---------
2041 * | |
2042 * | |
2043 * V V
2044 * list of ETEs list of ETEs
2046 * where ETE is an EdgeTableEntry data structure,
2047 * and there is one ScanLineList per scanline at
2048 * which an edge is initially entered.
2051 static void REGION_CreateETandAET(const INT *Count, INT nbpolygons,
2052 const POINT *pts, EdgeTable *ET, EdgeTableEntry *AET,
2053 EdgeTableEntry *pETEs, ScanLineListBlock *pSLLBlock)
2055 const POINT *top, *bottom;
2056 const POINT *PrevPt, *CurrPt, *EndPt;
2057 INT poly, count;
2058 int iSLLBlock = 0;
2059 int dy;
2063 * initialize the Active Edge Table
2065 AET->next = (EdgeTableEntry *)NULL;
2066 AET->back = (EdgeTableEntry *)NULL;
2067 AET->nextWETE = (EdgeTableEntry *)NULL;
2068 AET->bres.minor_axis = SMALL_COORDINATE;
2071 * initialize the Edge Table.
2073 ET->scanlines.next = (ScanLineList *)NULL;
2074 ET->ymax = SMALL_COORDINATE;
2075 ET->ymin = LARGE_COORDINATE;
2076 pSLLBlock->next = (ScanLineListBlock *)NULL;
2078 EndPt = pts - 1;
2079 for(poly = 0; poly < nbpolygons; poly++)
2081 count = Count[poly];
2082 EndPt += count;
2083 if(count < 2)
2084 continue;
2086 PrevPt = EndPt;
2089 * for each vertex in the array of points.
2090 * In this loop we are dealing with two vertices at
2091 * a time -- these make up one edge of the polygon.
2093 while (count--)
2095 CurrPt = pts++;
2098 * find out which point is above and which is below.
2100 if (PrevPt->y > CurrPt->y)
2102 bottom = PrevPt, top = CurrPt;
2103 pETEs->ClockWise = 0;
2105 else
2107 bottom = CurrPt, top = PrevPt;
2108 pETEs->ClockWise = 1;
2112 * don't add horizontal edges to the Edge table.
2114 if (bottom->y != top->y)
2116 pETEs->ymax = bottom->y-1;
2117 /* -1 so we don't get last scanline */
2120 * initialize integer edge algorithm
2122 dy = bottom->y - top->y;
2123 BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres);
2125 REGION_InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock,
2126 &iSLLBlock);
2128 if (PrevPt->y > ET->ymax)
2129 ET->ymax = PrevPt->y;
2130 if (PrevPt->y < ET->ymin)
2131 ET->ymin = PrevPt->y;
2132 pETEs++;
2135 PrevPt = CurrPt;
2140 /***********************************************************************
2141 * REGION_loadAET
2143 * This routine moves EdgeTableEntries from the
2144 * EdgeTable into the Active Edge Table,
2145 * leaving them sorted by smaller x coordinate.
2148 static void REGION_loadAET(EdgeTableEntry *AET, EdgeTableEntry *ETEs)
2150 EdgeTableEntry *pPrevAET;
2151 EdgeTableEntry *tmp;
2153 pPrevAET = AET;
2154 AET = AET->next;
2155 while (ETEs)
2157 while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis))
2159 pPrevAET = AET;
2160 AET = AET->next;
2162 tmp = ETEs->next;
2163 ETEs->next = AET;
2164 if (AET)
2165 AET->back = ETEs;
2166 ETEs->back = pPrevAET;
2167 pPrevAET->next = ETEs;
2168 pPrevAET = ETEs;
2170 ETEs = tmp;
2174 /***********************************************************************
2175 * REGION_computeWAET
2177 * This routine links the AET by the
2178 * nextWETE (winding EdgeTableEntry) link for
2179 * use by the winding number rule. The final
2180 * Active Edge Table (AET) might look something
2181 * like:
2183 * AET
2184 * ---------- --------- ---------
2185 * |ymax | |ymax | |ymax |
2186 * | ... | |... | |... |
2187 * |next |->|next |->|next |->...
2188 * |nextWETE| |nextWETE| |nextWETE|
2189 * --------- --------- ^--------
2190 * | | |
2191 * V-------------------> V---> ...
2194 static void REGION_computeWAET(EdgeTableEntry *AET)
2196 register EdgeTableEntry *pWETE;
2197 register int inside = 1;
2198 register int isInside = 0;
2200 AET->nextWETE = (EdgeTableEntry *)NULL;
2201 pWETE = AET;
2202 AET = AET->next;
2203 while (AET)
2205 if (AET->ClockWise)
2206 isInside++;
2207 else
2208 isInside--;
2210 if ((!inside && !isInside) ||
2211 ( inside && isInside))
2213 pWETE->nextWETE = AET;
2214 pWETE = AET;
2215 inside = !inside;
2217 AET = AET->next;
2219 pWETE->nextWETE = (EdgeTableEntry *)NULL;
2222 /***********************************************************************
2223 * REGION_InsertionSort
2225 * Just a simple insertion sort using
2226 * pointers and back pointers to sort the Active
2227 * Edge Table.
2230 static BOOL REGION_InsertionSort(EdgeTableEntry *AET)
2232 EdgeTableEntry *pETEchase;
2233 EdgeTableEntry *pETEinsert;
2234 EdgeTableEntry *pETEchaseBackTMP;
2235 BOOL changed = FALSE;
2237 AET = AET->next;
2238 while (AET)
2240 pETEinsert = AET;
2241 pETEchase = AET;
2242 while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
2243 pETEchase = pETEchase->back;
2245 AET = AET->next;
2246 if (pETEchase != pETEinsert)
2248 pETEchaseBackTMP = pETEchase->back;
2249 pETEinsert->back->next = AET;
2250 if (AET)
2251 AET->back = pETEinsert->back;
2252 pETEinsert->next = pETEchase;
2253 pETEchase->back->next = pETEinsert;
2254 pETEchase->back = pETEinsert;
2255 pETEinsert->back = pETEchaseBackTMP;
2256 changed = TRUE;
2259 return changed;
2262 /***********************************************************************
2263 * REGION_FreeStorage
2265 * Clean up our act.
2267 static void REGION_FreeStorage(ScanLineListBlock *pSLLBlock)
2269 ScanLineListBlock *tmpSLLBlock;
2271 while (pSLLBlock)
2273 tmpSLLBlock = pSLLBlock->next;
2274 HeapFree( SystemHeap, 0, pSLLBlock );
2275 pSLLBlock = tmpSLLBlock;
2280 /***********************************************************************
2281 * REGION_PtsToRegion
2283 * Create an array of rectangles from a list of points.
2285 static int REGION_PtsToRegion(int numFullPtBlocks, int iCurPtBlock,
2286 POINTBLOCK *FirstPtBlock, WINEREGION *reg)
2288 RECT *rects;
2289 POINT *pts;
2290 POINTBLOCK *CurPtBlock;
2291 int i;
2292 RECT *extents;
2293 INT numRects;
2295 extents = &reg->extents;
2297 numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
2299 if (!(reg->rects = HeapReAlloc( SystemHeap, 0, reg->rects,
2300 sizeof(RECT) * numRects )))
2301 return(0);
2303 reg->size = numRects;
2304 CurPtBlock = FirstPtBlock;
2305 rects = reg->rects - 1;
2306 numRects = 0;
2307 extents->left = LARGE_COORDINATE, extents->right = SMALL_COORDINATE;
2309 for ( ; numFullPtBlocks >= 0; numFullPtBlocks--) {
2310 /* the loop uses 2 points per iteration */
2311 i = NUMPTSTOBUFFER >> 1;
2312 if (!numFullPtBlocks)
2313 i = iCurPtBlock >> 1;
2314 for (pts = CurPtBlock->pts; i--; pts += 2) {
2315 if (pts->x == pts[1].x)
2316 continue;
2317 if (numRects && pts->x == rects->left && pts->y == rects->bottom &&
2318 pts[1].x == rects->right &&
2319 (numRects == 1 || rects[-1].top != rects->top) &&
2320 (i && pts[2].y > pts[1].y)) {
2321 rects->bottom = pts[1].y + 1;
2322 continue;
2324 numRects++;
2325 rects++;
2326 rects->left = pts->x; rects->top = pts->y;
2327 rects->right = pts[1].x; rects->bottom = pts[1].y + 1;
2328 if (rects->left < extents->left)
2329 extents->left = rects->left;
2330 if (rects->right > extents->right)
2331 extents->right = rects->right;
2333 CurPtBlock = CurPtBlock->next;
2336 if (numRects) {
2337 extents->top = reg->rects->top;
2338 extents->bottom = rects->bottom;
2339 } else {
2340 extents->left = 0;
2341 extents->top = 0;
2342 extents->right = 0;
2343 extents->bottom = 0;
2345 reg->numRects = numRects;
2347 return(TRUE);
2350 /***********************************************************************
2351 * CreatePolyPolygonRgn32 (GDI32.57)
2353 HRGN WINAPI CreatePolyPolygonRgn(const POINT *Pts, const INT *Count,
2354 INT nbpolygons, INT mode)
2356 HRGN hrgn;
2357 RGNOBJ *obj;
2358 WINEREGION *region;
2359 register EdgeTableEntry *pAET; /* Active Edge Table */
2360 register INT y; /* current scanline */
2361 register int iPts = 0; /* number of pts in buffer */
2362 register EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/
2363 register ScanLineList *pSLL; /* current scanLineList */
2364 register POINT *pts; /* output buffer */
2365 EdgeTableEntry *pPrevAET; /* ptr to previous AET */
2366 EdgeTable ET; /* header node for ET */
2367 EdgeTableEntry AET; /* header node for AET */
2368 EdgeTableEntry *pETEs; /* EdgeTableEntries pool */
2369 ScanLineListBlock SLLBlock; /* header for scanlinelist */
2370 int fixWAET = FALSE;
2371 POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
2372 POINTBLOCK *tmpPtBlock;
2373 int numFullPtBlocks = 0;
2374 INT poly, total;
2376 if(!(hrgn = REGION_CreateRegion(nbpolygons)))
2377 return 0;
2378 obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
2379 region = obj->rgn;
2381 /* special case a rectangle */
2383 if (((nbpolygons == 1) && ((*Count == 4) ||
2384 ((*Count == 5) && (Pts[4].x == Pts[0].x) && (Pts[4].y == Pts[0].y)))) &&
2385 (((Pts[0].y == Pts[1].y) &&
2386 (Pts[1].x == Pts[2].x) &&
2387 (Pts[2].y == Pts[3].y) &&
2388 (Pts[3].x == Pts[0].x)) ||
2389 ((Pts[0].x == Pts[1].x) &&
2390 (Pts[1].y == Pts[2].y) &&
2391 (Pts[2].x == Pts[3].x) &&
2392 (Pts[3].y == Pts[0].y))))
2394 SetRectRgn( hrgn, MIN(Pts[0].x, Pts[2].x), MIN(Pts[0].y, Pts[2].y),
2395 MAX(Pts[0].x, Pts[2].x), MAX(Pts[0].y, Pts[2].y) );
2396 GDI_HEAP_UNLOCK( hrgn );
2397 return hrgn;
2400 for(poly = total = 0; poly < nbpolygons; poly++)
2401 total += Count[poly];
2402 if (! (pETEs = HeapAlloc( SystemHeap, 0, sizeof(EdgeTableEntry) * total )))
2404 REGION_DeleteObject( hrgn, obj );
2405 return 0;
2407 pts = FirstPtBlock.pts;
2408 REGION_CreateETandAET(Count, nbpolygons, Pts, &ET, &AET, pETEs, &SLLBlock);
2409 pSLL = ET.scanlines.next;
2410 curPtBlock = &FirstPtBlock;
2412 if (mode != WINDING) {
2414 * for each scanline
2416 for (y = ET.ymin; y < ET.ymax; y++) {
2418 * Add a new edge to the active edge table when we
2419 * get to the next edge.
2421 if (pSLL != NULL && y == pSLL->scanline) {
2422 REGION_loadAET(&AET, pSLL->edgelist);
2423 pSLL = pSLL->next;
2425 pPrevAET = &AET;
2426 pAET = AET.next;
2429 * for each active edge
2431 while (pAET) {
2432 pts->x = pAET->bres.minor_axis, pts->y = y;
2433 pts++, iPts++;
2436 * send out the buffer
2438 if (iPts == NUMPTSTOBUFFER) {
2439 tmpPtBlock = HeapAlloc( SystemHeap, 0, sizeof(POINTBLOCK));
2440 if(!tmpPtBlock) {
2441 WARN("Can't alloc tPB\n");
2442 return 0;
2444 curPtBlock->next = tmpPtBlock;
2445 curPtBlock = tmpPtBlock;
2446 pts = curPtBlock->pts;
2447 numFullPtBlocks++;
2448 iPts = 0;
2450 EVALUATEEDGEEVENODD(pAET, pPrevAET, y);
2452 REGION_InsertionSort(&AET);
2455 else {
2457 * for each scanline
2459 for (y = ET.ymin; y < ET.ymax; y++) {
2461 * Add a new edge to the active edge table when we
2462 * get to the next edge.
2464 if (pSLL != NULL && y == pSLL->scanline) {
2465 REGION_loadAET(&AET, pSLL->edgelist);
2466 REGION_computeWAET(&AET);
2467 pSLL = pSLL->next;
2469 pPrevAET = &AET;
2470 pAET = AET.next;
2471 pWETE = pAET;
2474 * for each active edge
2476 while (pAET) {
2478 * add to the buffer only those edges that
2479 * are in the Winding active edge table.
2481 if (pWETE == pAET) {
2482 pts->x = pAET->bres.minor_axis, pts->y = y;
2483 pts++, iPts++;
2486 * send out the buffer
2488 if (iPts == NUMPTSTOBUFFER) {
2489 tmpPtBlock = HeapAlloc( SystemHeap, 0,
2490 sizeof(POINTBLOCK) );
2491 if(!tmpPtBlock) {
2492 WARN("Can't alloc tPB\n");
2493 return 0;
2495 curPtBlock->next = tmpPtBlock;
2496 curPtBlock = tmpPtBlock;
2497 pts = curPtBlock->pts;
2498 numFullPtBlocks++; iPts = 0;
2500 pWETE = pWETE->nextWETE;
2502 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
2506 * recompute the winding active edge table if
2507 * we just resorted or have exited an edge.
2509 if (REGION_InsertionSort(&AET) || fixWAET) {
2510 REGION_computeWAET(&AET);
2511 fixWAET = FALSE;
2515 REGION_FreeStorage(SLLBlock.next);
2516 REGION_PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
2517 for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) {
2518 tmpPtBlock = curPtBlock->next;
2519 HeapFree( SystemHeap, 0, curPtBlock );
2520 curPtBlock = tmpPtBlock;
2522 HeapFree( SystemHeap, 0, pETEs );
2523 GDI_HEAP_UNLOCK( hrgn );
2524 return hrgn;
2528 /***********************************************************************
2529 * CreatePolygonRgn16 (GDI.63)
2531 HRGN16 WINAPI CreatePolygonRgn16( const POINT16 * points, INT16 count,
2532 INT16 mode )
2534 return CreatePolyPolygonRgn16( points, &count, 1, mode );
2537 /***********************************************************************
2538 * CreatePolyPolygonRgn16 (GDI.451)
2540 HRGN16 WINAPI CreatePolyPolygonRgn16( const POINT16 *points,
2541 const INT16 *count, INT16 nbpolygons, INT16 mode )
2543 HRGN hrgn;
2544 int i, npts = 0;
2545 INT *count32;
2546 POINT *points32;
2548 for (i = 0; i < nbpolygons; i++)
2549 npts += count[i];
2550 points32 = HeapAlloc( SystemHeap, 0, npts * sizeof(POINT) );
2551 for (i = 0; i < npts; i++)
2552 CONV_POINT16TO32( &(points[i]), &(points32[i]) );
2554 count32 = HeapAlloc( SystemHeap, 0, nbpolygons * sizeof(INT) );
2555 for (i = 0; i < nbpolygons; i++)
2556 count32[i] = count[i];
2557 hrgn = CreatePolyPolygonRgn( points32, count32, nbpolygons, mode );
2558 HeapFree( SystemHeap, 0, count32 );
2559 HeapFree( SystemHeap, 0, points32 );
2560 return hrgn;
2563 /***********************************************************************
2564 * CreatePolygonRgn32 (GDI32.58)
2566 HRGN WINAPI CreatePolygonRgn( const POINT *points, INT count,
2567 INT mode )
2569 return CreatePolyPolygonRgn( points, &count, 1, mode );
2573 /***********************************************************************
2574 * GetRandomRgn [GDI32.215]
2576 * NOTES
2577 * This function is documented in MSDN online
2579 INT WINAPI GetRandomRgn(HDC hDC, HRGN hRgn, DWORD dwCode)
2581 switch (dwCode)
2583 case 4: /* == SYSRGN ? */
2585 DC *dc = DC_GetDCPtr (hDC);
2586 OSVERSIONINFOA vi;
2587 POINT org;
2588 CombineRgn (hRgn, dc->w.hVisRgn, 0, RGN_COPY);
2590 * On Windows NT/2000,
2591 * the region returned is in screen coordinates.
2592 * On Windows 95/98,
2593 * the region returned is in window coordinates
2595 vi.dwOSVersionInfoSize = sizeof(vi);
2596 if (GetVersionExA( &vi ) && vi.dwPlatformId == VER_PLATFORM_WIN32_NT)
2597 GetDCOrgEx(hDC, &org);
2598 else
2599 org.x = org.y = 0;
2600 org.x -= dc->w.DCOrgX;
2601 org.y -= dc->w.DCOrgY;
2602 OffsetRgn (hRgn, org.x, org.y);
2604 return 1;
2606 /* case 1:
2607 return GetClipRgn (hDC, hRgn);
2609 default:
2610 WARN("Unknown dwCode %ld\n", dwCode);
2611 return -1;
2614 return -1;
2617 /***********************************************************************
2618 * REGION_CropAndOffsetRegion
2620 static BOOL REGION_CropAndOffsetRegion(const POINT* off, const RECT *rect, WINEREGION *rgnSrc, WINEREGION* rgnDst)
2623 if( !rect ) /* just copy and offset */
2625 RECT *xrect;
2626 if( rgnDst == rgnSrc )
2628 if( off->x || off->y )
2629 xrect = rgnDst->rects;
2630 else
2631 return TRUE;
2633 else
2634 xrect = HeapReAlloc( SystemHeap, 0, rgnDst->rects,
2635 rgnSrc->size * sizeof( RECT ));
2636 if( xrect )
2638 INT i;
2640 if( rgnDst != rgnSrc )
2641 memcpy( rgnDst, rgnSrc, sizeof( WINEREGION ));
2643 if( off->x || off->y )
2645 for( i = 0; i < rgnDst->numRects; i++ )
2647 xrect[i].left = rgnSrc->rects[i].left + off->x;
2648 xrect[i].right = rgnSrc->rects[i].right + off->x;
2649 xrect[i].top = rgnSrc->rects[i].top + off->y;
2650 xrect[i].bottom = rgnSrc->rects[i].bottom + off->y;
2652 OffsetRect( &rgnDst->extents, off->x, off->y );
2654 else
2655 memcpy( xrect, rgnSrc->rects, rgnDst->numRects * sizeof(RECT));
2656 rgnDst->rects = xrect;
2657 } else
2658 return FALSE;
2660 else if( IsRectEmpty(rect) || !EXTENTCHECK(rect, &rgnSrc->extents) )
2662 empty:
2663 if( !rgnDst->rects )
2665 rgnDst->rects = HeapAlloc(SystemHeap, 0, RGN_DEFAULT_RECTS * sizeof( RECT ));
2666 if( rgnDst->rects )
2667 rgnDst->size = RGN_DEFAULT_RECTS;
2668 else
2669 return FALSE;
2672 TRACE("cropped to empty!\n");
2673 EMPTY_REGION(rgnDst);
2675 else /* region box and clipping rect appear to intersect */
2677 RECT *lpr;
2678 INT i, j, clipa, clipb;
2679 INT left = rgnSrc->extents.right + off->x;
2680 INT right = rgnSrc->extents.left + off->x;
2682 for( clipa = 0; rgnSrc->rects[clipa].bottom <= rect->top; clipa++ )
2683 ; /* skip bands above the clipping rectangle */
2685 for( clipb = clipa; clipb < rgnSrc->numRects; clipb++ )
2686 if( rgnSrc->rects[clipb].top >= rect->bottom )
2687 break; /* and below it */
2689 /* clipa - index of the first rect in the first intersecting band
2690 * clipb - index of the last rect in the last intersecting band
2693 if((rgnDst != rgnSrc) && (rgnDst->size < (i = (clipb - clipa))))
2695 rgnDst->rects = HeapReAlloc( SystemHeap, 0,
2696 rgnDst->rects, i * sizeof(RECT));
2697 if( !rgnDst->rects ) return FALSE;
2698 rgnDst->size = i;
2701 if( TRACE_ON(region) )
2703 REGION_DumpRegion( rgnSrc );
2704 TRACE("\tclipa = %i, clipb = %i\n", clipa, clipb );
2707 for( i = clipa, j = 0; i < clipb ; i++ )
2709 /* i - src index, j - dst index, j is always <= i for obvious reasons */
2711 lpr = rgnSrc->rects + i;
2712 if( lpr->left < rect->right && lpr->right > rect->left )
2714 rgnDst->rects[j].top = lpr->top + off->y;
2715 rgnDst->rects[j].bottom = lpr->bottom + off->y;
2716 rgnDst->rects[j].left = ((lpr->left > rect->left) ? lpr->left : rect->left) + off->x;
2717 rgnDst->rects[j].right = ((lpr->right < rect->right) ? lpr->right : rect->right) + off->x;
2719 if( rgnDst->rects[j].left < left ) left = rgnDst->rects[j].left;
2720 if( rgnDst->rects[j].right > right ) right = rgnDst->rects[j].right;
2722 j++;
2726 if( j == 0 ) goto empty;
2728 rgnDst->extents.left = left;
2729 rgnDst->extents.right = right;
2731 left = rect->top + off->y;
2732 right = rect->bottom + off->y;
2734 rgnDst->numRects = j--;
2735 for( i = 0; i <= j; i++ ) /* fixup top band */
2736 if( rgnDst->rects[i].top < left )
2737 rgnDst->rects[i].top = left;
2738 else
2739 break;
2741 for( i = j; i >= 0; i-- ) /* fixup bottom band */
2742 if( rgnDst->rects[i].bottom > right )
2743 rgnDst->rects[i].bottom = right;
2744 else
2745 break;
2747 rgnDst->extents.top = rgnDst->rects[0].top;
2748 rgnDst->extents.bottom = rgnDst->rects[j].bottom;
2750 rgnDst->type = (j >= 1) ? COMPLEXREGION : SIMPLEREGION;
2752 if( TRACE_ON(region) )
2754 TRACE("result:\n");
2755 REGION_DumpRegion( rgnDst );
2759 return TRUE;
2762 /***********************************************************************
2763 * REGION_CropRgn
2766 * hSrc: Region to crop and offset.
2767 * lpRect: Clipping rectangle. Can be NULL (no clipping).
2768 * lpPt: Points to offset the cropped region. Can be NULL (no offset).
2770 * hDst: Region to hold the result (a new region is created if it's 0).
2771 * Allowed to be the same region as hSrc in which case everything
2772 * will be done in place, with no memory reallocations.
2774 * Returns: hDst if success, 0 otherwise.
2776 HRGN REGION_CropRgn( HRGN hDst, HRGN hSrc, const RECT *lpRect, const POINT *lpPt )
2778 /* Optimization of the following generic code:
2780 HRGN h;
2782 if( lpRect )
2783 h = CreateRectRgn( lpRect->left, lpRect->top, lpRect->right, lpRect->bottom );
2784 else
2785 h = CreateRectRgn( 0, 0, 0, 0 );
2786 if( hDst == 0 ) hDst = h;
2787 if( lpRect )
2788 CombineRgn( hDst, hSrc, h, RGN_AND );
2789 else
2790 CombineRgn( hDst, hSrc, 0, RGN_COPY );
2791 if( lpPt )
2792 OffsetRgn( hDst, lpPt->x, lpPt->y );
2793 if( hDst != h )
2794 DeleteObject( h );
2795 return hDst;
2799 RGNOBJ *objSrc = (RGNOBJ *) GDI_GetObjPtr( hSrc, REGION_MAGIC );
2801 if(objSrc)
2803 RGNOBJ *objDst;
2804 WINEREGION *rgnDst;
2806 if( hDst )
2808 if (!(objDst = (RGNOBJ *) GDI_GetObjPtr( hDst, REGION_MAGIC )))
2810 hDst = 0;
2811 goto done;
2813 rgnDst = objDst->rgn;
2815 else
2817 if ((rgnDst = HeapAlloc(SystemHeap, 0, sizeof( WINEREGION ))))
2819 rgnDst->size = rgnDst->numRects = 0;
2820 rgnDst->rects = NULL; /* back end will allocate exact number */
2824 if( rgnDst )
2826 POINT pt = { 0, 0 };
2828 if( !lpPt ) lpPt = &pt;
2830 if( lpRect )
2831 TRACE("src %p -> dst %p (%i,%i)-(%i,%i) by (%li,%li)\n", objSrc->rgn, rgnDst,
2832 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom, lpPt->x, lpPt->y );
2833 else
2834 TRACE("src %p -> dst %p by (%li,%li)\n", objSrc->rgn, rgnDst, lpPt->x, lpPt->y );
2836 if( REGION_CropAndOffsetRegion( lpPt, lpRect, objSrc->rgn, rgnDst ) == FALSE )
2838 if( hDst ) /* existing rgn */
2840 GDI_HEAP_UNLOCK(hDst);
2841 hDst = 0;
2842 goto done;
2844 goto fail;
2846 else if( hDst == 0 )
2848 if(!(hDst = GDI_AllocObject( sizeof(RGNOBJ), REGION_MAGIC )))
2850 fail:
2851 if( rgnDst->rects )
2852 HeapFree( SystemHeap, 0, rgnDst->rects );
2853 HeapFree( SystemHeap, 0, rgnDst );
2854 goto done;
2857 objDst = (RGNOBJ *) GDI_HEAP_LOCK( hDst );
2858 objDst->rgn = rgnDst;
2861 GDI_HEAP_UNLOCK(hDst);
2863 else hDst = 0;
2864 done:
2865 GDI_HEAP_UNLOCK(hSrc);
2866 return hDst;
2868 return 0;
2871 /***********************************************************************
2872 * GetMetaRgn (GDI.328)
2874 INT WINAPI GetMetaRgn( HDC hdc, HRGN hRgn )
2876 FIXME( "stub\n" );
2878 return 0;
2882 /***********************************************************************
2883 * SetMetaRgn (GDI.455)
2885 INT WINAPI SetMetaRgn( HDC hdc )
2887 FIXME( "stub\n" );
2889 return ERROR;