Fixed small memory corruption.
[wine/dcerpc.git] / objects / region.c
blob0dc7f75ab846494a858d46d17b0c2561552496ce
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_HEAP_LOCK( hrgn );
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 );
942 return result;
945 /***********************************************************************
946 * REGION_SetExtents
947 * Re-calculate the extents of a region
949 static void REGION_SetExtents (WINEREGION *pReg)
951 RECT *pRect, *pRectEnd, *pExtents;
953 if (pReg->numRects == 0)
955 pReg->extents.left = 0;
956 pReg->extents.top = 0;
957 pReg->extents.right = 0;
958 pReg->extents.bottom = 0;
959 return;
962 pExtents = &pReg->extents;
963 pRect = pReg->rects;
964 pRectEnd = &pRect[pReg->numRects - 1];
967 * Since pRect is the first rectangle in the region, it must have the
968 * smallest top and since pRectEnd is the last rectangle in the region,
969 * it must have the largest bottom, because of banding. Initialize left and
970 * right from pRect and pRectEnd, resp., as good things to initialize them
971 * to...
973 pExtents->left = pRect->left;
974 pExtents->top = pRect->top;
975 pExtents->right = pRectEnd->right;
976 pExtents->bottom = pRectEnd->bottom;
978 while (pRect <= pRectEnd)
980 if (pRect->left < pExtents->left)
981 pExtents->left = pRect->left;
982 if (pRect->right > pExtents->right)
983 pExtents->right = pRect->right;
984 pRect++;
988 /***********************************************************************
989 * REGION_CopyRegion
991 static void REGION_CopyRegion(WINEREGION *dst, WINEREGION *src)
993 if (dst != src) /* don't want to copy to itself */
995 if (dst->size < src->numRects)
997 if (! (dst->rects = HeapReAlloc( SystemHeap, 0, dst->rects,
998 src->numRects * sizeof(RECT) )))
999 return;
1000 dst->size = src->numRects;
1002 dst->numRects = src->numRects;
1003 dst->extents.left = src->extents.left;
1004 dst->extents.top = src->extents.top;
1005 dst->extents.right = src->extents.right;
1006 dst->extents.bottom = src->extents.bottom;
1007 dst->type = src->type;
1009 memcpy((char *) dst->rects, (char *) src->rects,
1010 (int) (src->numRects * sizeof(RECT)));
1012 return;
1015 /***********************************************************************
1016 * REGION_Coalesce
1018 * Attempt to merge the rects in the current band with those in the
1019 * previous one. Used only by REGION_RegionOp.
1021 * Results:
1022 * The new index for the previous band.
1024 * Side Effects:
1025 * If coalescing takes place:
1026 * - rectangles in the previous band will have their bottom fields
1027 * altered.
1028 * - pReg->numRects will be decreased.
1031 static INT REGION_Coalesce (
1032 WINEREGION *pReg, /* Region to coalesce */
1033 INT prevStart, /* Index of start of previous band */
1034 INT curStart /* Index of start of current band */
1036 RECT *pPrevRect; /* Current rect in previous band */
1037 RECT *pCurRect; /* Current rect in current band */
1038 RECT *pRegEnd; /* End of region */
1039 INT curNumRects; /* Number of rectangles in current band */
1040 INT prevNumRects; /* Number of rectangles in previous band */
1041 INT bandtop; /* top coordinate for current band */
1043 pRegEnd = &pReg->rects[pReg->numRects];
1045 pPrevRect = &pReg->rects[prevStart];
1046 prevNumRects = curStart - prevStart;
1049 * Figure out how many rectangles are in the current band. Have to do
1050 * this because multiple bands could have been added in REGION_RegionOp
1051 * at the end when one region has been exhausted.
1053 pCurRect = &pReg->rects[curStart];
1054 bandtop = pCurRect->top;
1055 for (curNumRects = 0;
1056 (pCurRect != pRegEnd) && (pCurRect->top == bandtop);
1057 curNumRects++)
1059 pCurRect++;
1062 if (pCurRect != pRegEnd)
1065 * If more than one band was added, we have to find the start
1066 * of the last band added so the next coalescing job can start
1067 * at the right place... (given when multiple bands are added,
1068 * this may be pointless -- see above).
1070 pRegEnd--;
1071 while (pRegEnd[-1].top == pRegEnd->top)
1073 pRegEnd--;
1075 curStart = pRegEnd - pReg->rects;
1076 pRegEnd = pReg->rects + pReg->numRects;
1079 if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
1080 pCurRect -= curNumRects;
1082 * The bands may only be coalesced if the bottom of the previous
1083 * matches the top scanline of the current.
1085 if (pPrevRect->bottom == pCurRect->top)
1088 * Make sure the bands have rects in the same places. This
1089 * assumes that rects have been added in such a way that they
1090 * cover the most area possible. I.e. two rects in a band must
1091 * have some horizontal space between them.
1095 if ((pPrevRect->left != pCurRect->left) ||
1096 (pPrevRect->right != pCurRect->right))
1099 * The bands don't line up so they can't be coalesced.
1101 return (curStart);
1103 pPrevRect++;
1104 pCurRect++;
1105 prevNumRects -= 1;
1106 } while (prevNumRects != 0);
1108 pReg->numRects -= curNumRects;
1109 pCurRect -= curNumRects;
1110 pPrevRect -= curNumRects;
1113 * The bands may be merged, so set the bottom of each rect
1114 * in the previous band to that of the corresponding rect in
1115 * the current band.
1119 pPrevRect->bottom = pCurRect->bottom;
1120 pPrevRect++;
1121 pCurRect++;
1122 curNumRects -= 1;
1123 } while (curNumRects != 0);
1126 * If only one band was added to the region, we have to backup
1127 * curStart to the start of the previous band.
1129 * If more than one band was added to the region, copy the
1130 * other bands down. The assumption here is that the other bands
1131 * came from the same region as the current one and no further
1132 * coalescing can be done on them since it's all been done
1133 * already... curStart is already in the right place.
1135 if (pCurRect == pRegEnd)
1137 curStart = prevStart;
1139 else
1143 *pPrevRect++ = *pCurRect++;
1144 } while (pCurRect != pRegEnd);
1149 return (curStart);
1152 /***********************************************************************
1153 * REGION_RegionOp
1155 * Apply an operation to two regions. Called by REGION_Union,
1156 * REGION_Inverse, REGION_Subtract, REGION_Intersect...
1158 * Results:
1159 * None.
1161 * Side Effects:
1162 * The new region is overwritten.
1164 * Notes:
1165 * The idea behind this function is to view the two regions as sets.
1166 * Together they cover a rectangle of area that this function divides
1167 * into horizontal bands where points are covered only by one region
1168 * or by both. For the first case, the nonOverlapFunc is called with
1169 * each the band and the band's upper and lower extents. For the
1170 * second, the overlapFunc is called to process the entire band. It
1171 * is responsible for clipping the rectangles in the band, though
1172 * this function provides the boundaries.
1173 * At the end of each band, the new region is coalesced, if possible,
1174 * to reduce the number of rectangles in the region.
1177 static void REGION_RegionOp(
1178 WINEREGION *newReg, /* Place to store result */
1179 WINEREGION *reg1, /* First region in operation */
1180 WINEREGION *reg2, /* 2nd region in operation */
1181 void (*overlapFunc)(), /* Function to call for over-lapping bands */
1182 void (*nonOverlap1Func)(), /* Function to call for non-overlapping bands in region 1 */
1183 void (*nonOverlap2Func)() /* Function to call for non-overlapping bands in region 2 */
1185 RECT *r1; /* Pointer into first region */
1186 RECT *r2; /* Pointer into 2d region */
1187 RECT *r1End; /* End of 1st region */
1188 RECT *r2End; /* End of 2d region */
1189 INT ybot; /* Bottom of intersection */
1190 INT ytop; /* Top of intersection */
1191 RECT *oldRects; /* Old rects for newReg */
1192 INT prevBand; /* Index of start of
1193 * previous band in newReg */
1194 INT curBand; /* Index of start of current
1195 * band in newReg */
1196 RECT *r1BandEnd; /* End of current band in r1 */
1197 RECT *r2BandEnd; /* End of current band in r2 */
1198 INT top; /* Top of non-overlapping band */
1199 INT bot; /* Bottom of non-overlapping band */
1202 * Initialization:
1203 * set r1, r2, r1End and r2End appropriately, preserve the important
1204 * parts of the destination region until the end in case it's one of
1205 * the two source regions, then mark the "new" region empty, allocating
1206 * another array of rectangles for it to use.
1208 r1 = reg1->rects;
1209 r2 = reg2->rects;
1210 r1End = r1 + reg1->numRects;
1211 r2End = r2 + reg2->numRects;
1215 * newReg may be one of the src regions so we can't empty it. We keep a
1216 * note of its rects pointer (so that we can free them later), preserve its
1217 * extents and simply set numRects to zero.
1220 oldRects = newReg->rects;
1221 newReg->numRects = 0;
1224 * Allocate a reasonable number of rectangles for the new region. The idea
1225 * is to allocate enough so the individual functions don't need to
1226 * reallocate and copy the array, which is time consuming, yet we don't
1227 * have to worry about using too much memory. I hope to be able to
1228 * nuke the Xrealloc() at the end of this function eventually.
1230 newReg->size = MAX(reg1->numRects,reg2->numRects) * 2;
1232 if (! (newReg->rects = HeapAlloc( SystemHeap, 0,
1233 sizeof(RECT) * newReg->size )))
1235 newReg->size = 0;
1236 return;
1240 * Initialize ybot and ytop.
1241 * In the upcoming loop, ybot and ytop serve different functions depending
1242 * on whether the band being handled is an overlapping or non-overlapping
1243 * band.
1244 * In the case of a non-overlapping band (only one of the regions
1245 * has points in the band), ybot is the bottom of the most recent
1246 * intersection and thus clips the top of the rectangles in that band.
1247 * ytop is the top of the next intersection between the two regions and
1248 * serves to clip the bottom of the rectangles in the current band.
1249 * For an overlapping band (where the two regions intersect), ytop clips
1250 * the top of the rectangles of both regions and ybot clips the bottoms.
1252 if (reg1->extents.top < reg2->extents.top)
1253 ybot = reg1->extents.top;
1254 else
1255 ybot = reg2->extents.top;
1258 * prevBand serves to mark the start of the previous band so rectangles
1259 * can be coalesced into larger rectangles. qv. miCoalesce, above.
1260 * In the beginning, there is no previous band, so prevBand == curBand
1261 * (curBand is set later on, of course, but the first band will always
1262 * start at index 0). prevBand and curBand must be indices because of
1263 * the possible expansion, and resultant moving, of the new region's
1264 * array of rectangles.
1266 prevBand = 0;
1270 curBand = newReg->numRects;
1273 * This algorithm proceeds one source-band (as opposed to a
1274 * destination band, which is determined by where the two regions
1275 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
1276 * rectangle after the last one in the current band for their
1277 * respective regions.
1279 r1BandEnd = r1;
1280 while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top))
1282 r1BandEnd++;
1285 r2BandEnd = r2;
1286 while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top))
1288 r2BandEnd++;
1292 * First handle the band that doesn't intersect, if any.
1294 * Note that attention is restricted to one band in the
1295 * non-intersecting region at once, so if a region has n
1296 * bands between the current position and the next place it overlaps
1297 * the other, this entire loop will be passed through n times.
1299 if (r1->top < r2->top)
1301 top = MAX(r1->top,ybot);
1302 bot = MIN(r1->bottom,r2->top);
1304 if ((top != bot) && (nonOverlap1Func != (void (*)())NULL))
1306 (* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot);
1309 ytop = r2->top;
1311 else if (r2->top < r1->top)
1313 top = MAX(r2->top,ybot);
1314 bot = MIN(r2->bottom,r1->top);
1316 if ((top != bot) && (nonOverlap2Func != (void (*)())NULL))
1318 (* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot);
1321 ytop = r1->top;
1323 else
1325 ytop = r1->top;
1329 * If any rectangles got added to the region, try and coalesce them
1330 * with rectangles from the previous band. Note we could just do
1331 * this test in miCoalesce, but some machines incur a not
1332 * inconsiderable cost for function calls, so...
1334 if (newReg->numRects != curBand)
1336 prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1340 * Now see if we've hit an intersecting band. The two bands only
1341 * intersect if ybot > ytop
1343 ybot = MIN(r1->bottom, r2->bottom);
1344 curBand = newReg->numRects;
1345 if (ybot > ytop)
1347 (* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
1351 if (newReg->numRects != curBand)
1353 prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1357 * If we've finished with a band (bottom == ybot) we skip forward
1358 * in the region to the next band.
1360 if (r1->bottom == ybot)
1362 r1 = r1BandEnd;
1364 if (r2->bottom == ybot)
1366 r2 = r2BandEnd;
1368 } while ((r1 != r1End) && (r2 != r2End));
1371 * Deal with whichever region still has rectangles left.
1373 curBand = newReg->numRects;
1374 if (r1 != r1End)
1376 if (nonOverlap1Func != (void (*)())NULL)
1380 r1BandEnd = r1;
1381 while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top))
1383 r1BandEnd++;
1385 (* nonOverlap1Func) (newReg, r1, r1BandEnd,
1386 MAX(r1->top,ybot), r1->bottom);
1387 r1 = r1BandEnd;
1388 } while (r1 != r1End);
1391 else if ((r2 != r2End) && (nonOverlap2Func != (void (*)())NULL))
1395 r2BandEnd = r2;
1396 while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top))
1398 r2BandEnd++;
1400 (* nonOverlap2Func) (newReg, r2, r2BandEnd,
1401 MAX(r2->top,ybot), r2->bottom);
1402 r2 = r2BandEnd;
1403 } while (r2 != r2End);
1406 if (newReg->numRects != curBand)
1408 (void) REGION_Coalesce (newReg, prevBand, curBand);
1412 * A bit of cleanup. To keep regions from growing without bound,
1413 * we shrink the array of rectangles to match the new number of
1414 * rectangles in the region. This never goes to 0, however...
1416 * Only do this stuff if the number of rectangles allocated is more than
1417 * twice the number of rectangles in the region (a simple optimization...).
1419 if ((newReg->numRects < (newReg->size >> 1)) && (newReg->numRects > 2))
1421 if (REGION_NOT_EMPTY(newReg))
1423 RECT *prev_rects = newReg->rects;
1424 newReg->size = newReg->numRects;
1425 newReg->rects = HeapReAlloc( SystemHeap, 0, newReg->rects,
1426 sizeof(RECT) * newReg->size );
1427 if (! newReg->rects)
1428 newReg->rects = prev_rects;
1430 else
1433 * No point in doing the extra work involved in an Xrealloc if
1434 * the region is empty
1436 newReg->size = 1;
1437 HeapFree( SystemHeap, 0, newReg->rects );
1438 newReg->rects = HeapAlloc( SystemHeap, 0, sizeof(RECT) );
1441 HeapFree( SystemHeap, 0, oldRects );
1442 return;
1445 /***********************************************************************
1446 * Region Intersection
1447 ***********************************************************************/
1450 /***********************************************************************
1451 * REGION_IntersectO
1453 * Handle an overlapping band for REGION_Intersect.
1455 * Results:
1456 * None.
1458 * Side Effects:
1459 * Rectangles may be added to the region.
1462 static void REGION_IntersectO(WINEREGION *pReg, RECT *r1, RECT *r1End,
1463 RECT *r2, RECT *r2End, INT top, INT bottom)
1466 INT left, right;
1467 RECT *pNextRect;
1469 pNextRect = &pReg->rects[pReg->numRects];
1471 while ((r1 != r1End) && (r2 != r2End))
1473 left = MAX(r1->left, r2->left);
1474 right = MIN(r1->right, r2->right);
1477 * If there's any overlap between the two rectangles, add that
1478 * overlap to the new region.
1479 * There's no need to check for subsumption because the only way
1480 * such a need could arise is if some region has two rectangles
1481 * right next to each other. Since that should never happen...
1483 if (left < right)
1485 MEMCHECK(pReg, pNextRect, pReg->rects);
1486 pNextRect->left = left;
1487 pNextRect->top = top;
1488 pNextRect->right = right;
1489 pNextRect->bottom = bottom;
1490 pReg->numRects += 1;
1491 pNextRect++;
1495 * Need to advance the pointers. Shift the one that extends
1496 * to the right the least, since the other still has a chance to
1497 * overlap with that region's next rectangle, if you see what I mean.
1499 if (r1->right < r2->right)
1501 r1++;
1503 else if (r2->right < r1->right)
1505 r2++;
1507 else
1509 r1++;
1510 r2++;
1513 return;
1516 /***********************************************************************
1517 * REGION_IntersectRegion
1519 static void REGION_IntersectRegion(WINEREGION *newReg, WINEREGION *reg1,
1520 WINEREGION *reg2)
1522 /* check for trivial reject */
1523 if ( (!(reg1->numRects)) || (!(reg2->numRects)) ||
1524 (!EXTENTCHECK(&reg1->extents, &reg2->extents)))
1525 newReg->numRects = 0;
1526 else
1527 REGION_RegionOp (newReg, reg1, reg2,
1528 (voidProcp) REGION_IntersectO, (voidProcp) NULL, (voidProcp) NULL);
1531 * Can't alter newReg's extents before we call miRegionOp because
1532 * it might be one of the source regions and miRegionOp depends
1533 * on the extents of those regions being the same. Besides, this
1534 * way there's no checking against rectangles that will be nuked
1535 * due to coalescing, so we have to examine fewer rectangles.
1537 REGION_SetExtents(newReg);
1538 newReg->type = (newReg->numRects) ? COMPLEXREGION : NULLREGION ;
1539 return;
1542 /***********************************************************************
1543 * Region Union
1544 ***********************************************************************/
1546 /***********************************************************************
1547 * REGION_UnionNonO
1549 * Handle a non-overlapping band for the union operation. Just
1550 * Adds the rectangles into the region. Doesn't have to check for
1551 * subsumption or anything.
1553 * Results:
1554 * None.
1556 * Side Effects:
1557 * pReg->numRects is incremented and the final rectangles overwritten
1558 * with the rectangles we're passed.
1561 static void REGION_UnionNonO (WINEREGION *pReg, RECT *r, RECT *rEnd,
1562 INT top, INT bottom)
1564 RECT *pNextRect;
1566 pNextRect = &pReg->rects[pReg->numRects];
1568 while (r != rEnd)
1570 MEMCHECK(pReg, pNextRect, pReg->rects);
1571 pNextRect->left = r->left;
1572 pNextRect->top = top;
1573 pNextRect->right = r->right;
1574 pNextRect->bottom = bottom;
1575 pReg->numRects += 1;
1576 pNextRect++;
1577 r++;
1579 return;
1582 /***********************************************************************
1583 * REGION_UnionO
1585 * Handle an overlapping band for the union operation. Picks the
1586 * left-most rectangle each time and merges it into the region.
1588 * Results:
1589 * None.
1591 * Side Effects:
1592 * Rectangles are overwritten in pReg->rects and pReg->numRects will
1593 * be changed.
1596 static void REGION_UnionO (WINEREGION *pReg, RECT *r1, RECT *r1End,
1597 RECT *r2, RECT *r2End, INT top, INT bottom)
1599 RECT *pNextRect;
1601 pNextRect = &pReg->rects[pReg->numRects];
1603 #define MERGERECT(r) \
1604 if ((pReg->numRects != 0) && \
1605 (pNextRect[-1].top == top) && \
1606 (pNextRect[-1].bottom == bottom) && \
1607 (pNextRect[-1].right >= r->left)) \
1609 if (pNextRect[-1].right < r->right) \
1611 pNextRect[-1].right = r->right; \
1614 else \
1616 MEMCHECK(pReg, pNextRect, pReg->rects); \
1617 pNextRect->top = top; \
1618 pNextRect->bottom = bottom; \
1619 pNextRect->left = r->left; \
1620 pNextRect->right = r->right; \
1621 pReg->numRects += 1; \
1622 pNextRect += 1; \
1624 r++;
1626 while ((r1 != r1End) && (r2 != r2End))
1628 if (r1->left < r2->left)
1630 MERGERECT(r1);
1632 else
1634 MERGERECT(r2);
1638 if (r1 != r1End)
1642 MERGERECT(r1);
1643 } while (r1 != r1End);
1645 else while (r2 != r2End)
1647 MERGERECT(r2);
1649 return;
1652 /***********************************************************************
1653 * REGION_UnionRegion
1655 static void REGION_UnionRegion(WINEREGION *newReg, WINEREGION *reg1,
1656 WINEREGION *reg2)
1658 /* checks all the simple cases */
1661 * Region 1 and 2 are the same or region 1 is empty
1663 if ( (reg1 == reg2) || (!(reg1->numRects)) )
1665 if (newReg != reg2)
1666 REGION_CopyRegion(newReg, reg2);
1667 return;
1671 * if nothing to union (region 2 empty)
1673 if (!(reg2->numRects))
1675 if (newReg != reg1)
1676 REGION_CopyRegion(newReg, reg1);
1677 return;
1681 * Region 1 completely subsumes region 2
1683 if ((reg1->numRects == 1) &&
1684 (reg1->extents.left <= reg2->extents.left) &&
1685 (reg1->extents.top <= reg2->extents.top) &&
1686 (reg1->extents.right >= reg2->extents.right) &&
1687 (reg1->extents.bottom >= reg2->extents.bottom))
1689 if (newReg != reg1)
1690 REGION_CopyRegion(newReg, reg1);
1691 return;
1695 * Region 2 completely subsumes region 1
1697 if ((reg2->numRects == 1) &&
1698 (reg2->extents.left <= reg1->extents.left) &&
1699 (reg2->extents.top <= reg1->extents.top) &&
1700 (reg2->extents.right >= reg1->extents.right) &&
1701 (reg2->extents.bottom >= reg1->extents.bottom))
1703 if (newReg != reg2)
1704 REGION_CopyRegion(newReg, reg2);
1705 return;
1708 REGION_RegionOp (newReg, reg1, reg2, (voidProcp) REGION_UnionO,
1709 (voidProcp) REGION_UnionNonO, (voidProcp) REGION_UnionNonO);
1711 newReg->extents.left = MIN(reg1->extents.left, reg2->extents.left);
1712 newReg->extents.top = MIN(reg1->extents.top, reg2->extents.top);
1713 newReg->extents.right = MAX(reg1->extents.right, reg2->extents.right);
1714 newReg->extents.bottom = MAX(reg1->extents.bottom, reg2->extents.bottom);
1715 newReg->type = (newReg->numRects) ? COMPLEXREGION : NULLREGION ;
1716 return;
1719 /***********************************************************************
1720 * Region Subtraction
1721 ***********************************************************************/
1723 /***********************************************************************
1724 * REGION_SubtractNonO1
1726 * Deal with non-overlapping band for subtraction. Any parts from
1727 * region 2 we discard. Anything from region 1 we add to the region.
1729 * Results:
1730 * None.
1732 * Side Effects:
1733 * pReg may be affected.
1736 static void REGION_SubtractNonO1 (WINEREGION *pReg, RECT *r, RECT *rEnd,
1737 INT top, INT bottom)
1739 RECT *pNextRect;
1741 pNextRect = &pReg->rects[pReg->numRects];
1743 while (r != rEnd)
1745 MEMCHECK(pReg, pNextRect, pReg->rects);
1746 pNextRect->left = r->left;
1747 pNextRect->top = top;
1748 pNextRect->right = r->right;
1749 pNextRect->bottom = bottom;
1750 pReg->numRects += 1;
1751 pNextRect++;
1752 r++;
1754 return;
1758 /***********************************************************************
1759 * REGION_SubtractO
1761 * Overlapping band subtraction. x1 is the left-most point not yet
1762 * checked.
1764 * Results:
1765 * None.
1767 * Side Effects:
1768 * pReg may have rectangles added to it.
1771 static void REGION_SubtractO (WINEREGION *pReg, RECT *r1, RECT *r1End,
1772 RECT *r2, RECT *r2End, INT top, INT bottom)
1774 RECT *pNextRect;
1775 INT left;
1777 left = r1->left;
1778 pNextRect = &pReg->rects[pReg->numRects];
1780 while ((r1 != r1End) && (r2 != r2End))
1782 if (r2->right <= left)
1785 * Subtrahend missed the boat: go to next subtrahend.
1787 r2++;
1789 else if (r2->left <= left)
1792 * Subtrahend preceeds minuend: nuke left edge of minuend.
1794 left = r2->right;
1795 if (left >= r1->right)
1798 * Minuend completely covered: advance to next minuend and
1799 * reset left fence to edge of new minuend.
1801 r1++;
1802 if (r1 != r1End)
1803 left = r1->left;
1805 else
1808 * Subtrahend now used up since it doesn't extend beyond
1809 * minuend
1811 r2++;
1814 else if (r2->left < r1->right)
1817 * Left part of subtrahend covers part of minuend: add uncovered
1818 * part of minuend to region and skip to next subtrahend.
1820 MEMCHECK(pReg, pNextRect, pReg->rects);
1821 pNextRect->left = left;
1822 pNextRect->top = top;
1823 pNextRect->right = r2->left;
1824 pNextRect->bottom = bottom;
1825 pReg->numRects += 1;
1826 pNextRect++;
1827 left = r2->right;
1828 if (left >= r1->right)
1831 * Minuend used up: advance to new...
1833 r1++;
1834 if (r1 != r1End)
1835 left = r1->left;
1837 else
1840 * Subtrahend used up
1842 r2++;
1845 else
1848 * Minuend used up: add any remaining piece before advancing.
1850 if (r1->right > left)
1852 MEMCHECK(pReg, pNextRect, pReg->rects);
1853 pNextRect->left = left;
1854 pNextRect->top = top;
1855 pNextRect->right = r1->right;
1856 pNextRect->bottom = bottom;
1857 pReg->numRects += 1;
1858 pNextRect++;
1860 r1++;
1861 left = r1->left;
1866 * Add remaining minuend rectangles to region.
1868 while (r1 != r1End)
1870 MEMCHECK(pReg, pNextRect, pReg->rects);
1871 pNextRect->left = left;
1872 pNextRect->top = top;
1873 pNextRect->right = r1->right;
1874 pNextRect->bottom = bottom;
1875 pReg->numRects += 1;
1876 pNextRect++;
1877 r1++;
1878 if (r1 != r1End)
1880 left = r1->left;
1883 return;
1886 /***********************************************************************
1887 * REGION_SubtractRegion
1889 * Subtract regS from regM and leave the result in regD.
1890 * S stands for subtrahend, M for minuend and D for difference.
1892 * Results:
1893 * TRUE.
1895 * Side Effects:
1896 * regD is overwritten.
1899 static void REGION_SubtractRegion(WINEREGION *regD, WINEREGION *regM,
1900 WINEREGION *regS )
1902 /* check for trivial reject */
1903 if ( (!(regM->numRects)) || (!(regS->numRects)) ||
1904 (!EXTENTCHECK(&regM->extents, &regS->extents)) )
1906 REGION_CopyRegion(regD, regM);
1907 return;
1910 REGION_RegionOp (regD, regM, regS, (voidProcp) REGION_SubtractO,
1911 (voidProcp) REGION_SubtractNonO1, (voidProcp) NULL);
1914 * Can't alter newReg's extents before we call miRegionOp because
1915 * it might be one of the source regions and miRegionOp depends
1916 * on the extents of those regions being the unaltered. Besides, this
1917 * way there's no checking against rectangles that will be nuked
1918 * due to coalescing, so we have to examine fewer rectangles.
1920 REGION_SetExtents (regD);
1921 regD->type = (regD->numRects) ? COMPLEXREGION : NULLREGION ;
1922 return;
1925 /***********************************************************************
1926 * REGION_XorRegion
1928 static void REGION_XorRegion(WINEREGION *dr, WINEREGION *sra,
1929 WINEREGION *srb)
1931 WINEREGION *tra, *trb;
1933 if ((! (tra = REGION_AllocWineRegion(sra->numRects + 1))) ||
1934 (! (trb = REGION_AllocWineRegion(srb->numRects + 1))))
1935 return;
1936 REGION_SubtractRegion(tra,sra,srb);
1937 REGION_SubtractRegion(trb,srb,sra);
1938 REGION_UnionRegion(dr,tra,trb);
1939 REGION_DestroyWineRegion(tra);
1940 REGION_DestroyWineRegion(trb);
1941 return;
1944 /**************************************************************************
1946 * Poly Regions
1948 *************************************************************************/
1950 #define LARGE_COORDINATE 0x7fffffff /* FIXME */
1951 #define SMALL_COORDINATE 0x80000000
1953 /***********************************************************************
1954 * REGION_InsertEdgeInET
1956 * Insert the given edge into the edge table.
1957 * First we must find the correct bucket in the
1958 * Edge table, then find the right slot in the
1959 * bucket. Finally, we can insert it.
1962 static void REGION_InsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE,
1963 INT scanline, ScanLineListBlock **SLLBlock, INT *iSLLBlock)
1966 EdgeTableEntry *start, *prev;
1967 ScanLineList *pSLL, *pPrevSLL;
1968 ScanLineListBlock *tmpSLLBlock;
1971 * find the right bucket to put the edge into
1973 pPrevSLL = &ET->scanlines;
1974 pSLL = pPrevSLL->next;
1975 while (pSLL && (pSLL->scanline < scanline))
1977 pPrevSLL = pSLL;
1978 pSLL = pSLL->next;
1982 * reassign pSLL (pointer to ScanLineList) if necessary
1984 if ((!pSLL) || (pSLL->scanline > scanline))
1986 if (*iSLLBlock > SLLSPERBLOCK-1)
1988 tmpSLLBlock = HeapAlloc( SystemHeap, 0, sizeof(ScanLineListBlock));
1989 if(!tmpSLLBlock)
1991 WARN("Can't alloc SLLB\n");
1992 return;
1994 (*SLLBlock)->next = tmpSLLBlock;
1995 tmpSLLBlock->next = (ScanLineListBlock *)NULL;
1996 *SLLBlock = tmpSLLBlock;
1997 *iSLLBlock = 0;
1999 pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
2001 pSLL->next = pPrevSLL->next;
2002 pSLL->edgelist = (EdgeTableEntry *)NULL;
2003 pPrevSLL->next = pSLL;
2005 pSLL->scanline = scanline;
2008 * now insert the edge in the right bucket
2010 prev = (EdgeTableEntry *)NULL;
2011 start = pSLL->edgelist;
2012 while (start && (start->bres.minor_axis < ETE->bres.minor_axis))
2014 prev = start;
2015 start = start->next;
2017 ETE->next = start;
2019 if (prev)
2020 prev->next = ETE;
2021 else
2022 pSLL->edgelist = ETE;
2025 /***********************************************************************
2026 * REGION_CreateEdgeTable
2028 * This routine creates the edge table for
2029 * scan converting polygons.
2030 * The Edge Table (ET) looks like:
2032 * EdgeTable
2033 * --------
2034 * | ymax | ScanLineLists
2035 * |scanline|-->------------>-------------->...
2036 * -------- |scanline| |scanline|
2037 * |edgelist| |edgelist|
2038 * --------- ---------
2039 * | |
2040 * | |
2041 * V V
2042 * list of ETEs list of ETEs
2044 * where ETE is an EdgeTableEntry data structure,
2045 * and there is one ScanLineList per scanline at
2046 * which an edge is initially entered.
2049 static void REGION_CreateETandAET(const INT *Count, INT nbpolygons,
2050 const POINT *pts, EdgeTable *ET, EdgeTableEntry *AET,
2051 EdgeTableEntry *pETEs, ScanLineListBlock *pSLLBlock)
2053 const POINT *top, *bottom;
2054 const POINT *PrevPt, *CurrPt, *EndPt;
2055 INT poly, count;
2056 int iSLLBlock = 0;
2057 int dy;
2061 * initialize the Active Edge Table
2063 AET->next = (EdgeTableEntry *)NULL;
2064 AET->back = (EdgeTableEntry *)NULL;
2065 AET->nextWETE = (EdgeTableEntry *)NULL;
2066 AET->bres.minor_axis = SMALL_COORDINATE;
2069 * initialize the Edge Table.
2071 ET->scanlines.next = (ScanLineList *)NULL;
2072 ET->ymax = SMALL_COORDINATE;
2073 ET->ymin = LARGE_COORDINATE;
2074 pSLLBlock->next = (ScanLineListBlock *)NULL;
2076 EndPt = pts - 1;
2077 for(poly = 0; poly < nbpolygons; poly++)
2079 count = Count[poly];
2080 EndPt += count;
2081 if(count < 2)
2082 continue;
2084 PrevPt = EndPt;
2087 * for each vertex in the array of points.
2088 * In this loop we are dealing with two vertices at
2089 * a time -- these make up one edge of the polygon.
2091 while (count--)
2093 CurrPt = pts++;
2096 * find out which point is above and which is below.
2098 if (PrevPt->y > CurrPt->y)
2100 bottom = PrevPt, top = CurrPt;
2101 pETEs->ClockWise = 0;
2103 else
2105 bottom = CurrPt, top = PrevPt;
2106 pETEs->ClockWise = 1;
2110 * don't add horizontal edges to the Edge table.
2112 if (bottom->y != top->y)
2114 pETEs->ymax = bottom->y-1;
2115 /* -1 so we don't get last scanline */
2118 * initialize integer edge algorithm
2120 dy = bottom->y - top->y;
2121 BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres);
2123 REGION_InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock,
2124 &iSLLBlock);
2126 if (PrevPt->y > ET->ymax)
2127 ET->ymax = PrevPt->y;
2128 if (PrevPt->y < ET->ymin)
2129 ET->ymin = PrevPt->y;
2130 pETEs++;
2133 PrevPt = CurrPt;
2138 /***********************************************************************
2139 * REGION_loadAET
2141 * This routine moves EdgeTableEntries from the
2142 * EdgeTable into the Active Edge Table,
2143 * leaving them sorted by smaller x coordinate.
2146 static void REGION_loadAET(EdgeTableEntry *AET, EdgeTableEntry *ETEs)
2148 EdgeTableEntry *pPrevAET;
2149 EdgeTableEntry *tmp;
2151 pPrevAET = AET;
2152 AET = AET->next;
2153 while (ETEs)
2155 while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis))
2157 pPrevAET = AET;
2158 AET = AET->next;
2160 tmp = ETEs->next;
2161 ETEs->next = AET;
2162 if (AET)
2163 AET->back = ETEs;
2164 ETEs->back = pPrevAET;
2165 pPrevAET->next = ETEs;
2166 pPrevAET = ETEs;
2168 ETEs = tmp;
2172 /***********************************************************************
2173 * REGION_computeWAET
2175 * This routine links the AET by the
2176 * nextWETE (winding EdgeTableEntry) link for
2177 * use by the winding number rule. The final
2178 * Active Edge Table (AET) might look something
2179 * like:
2181 * AET
2182 * ---------- --------- ---------
2183 * |ymax | |ymax | |ymax |
2184 * | ... | |... | |... |
2185 * |next |->|next |->|next |->...
2186 * |nextWETE| |nextWETE| |nextWETE|
2187 * --------- --------- ^--------
2188 * | | |
2189 * V-------------------> V---> ...
2192 static void REGION_computeWAET(EdgeTableEntry *AET)
2194 register EdgeTableEntry *pWETE;
2195 register int inside = 1;
2196 register int isInside = 0;
2198 AET->nextWETE = (EdgeTableEntry *)NULL;
2199 pWETE = AET;
2200 AET = AET->next;
2201 while (AET)
2203 if (AET->ClockWise)
2204 isInside++;
2205 else
2206 isInside--;
2208 if ((!inside && !isInside) ||
2209 ( inside && isInside))
2211 pWETE->nextWETE = AET;
2212 pWETE = AET;
2213 inside = !inside;
2215 AET = AET->next;
2217 pWETE->nextWETE = (EdgeTableEntry *)NULL;
2220 /***********************************************************************
2221 * REGION_InsertionSort
2223 * Just a simple insertion sort using
2224 * pointers and back pointers to sort the Active
2225 * Edge Table.
2228 static BOOL REGION_InsertionSort(EdgeTableEntry *AET)
2230 EdgeTableEntry *pETEchase;
2231 EdgeTableEntry *pETEinsert;
2232 EdgeTableEntry *pETEchaseBackTMP;
2233 BOOL changed = FALSE;
2235 AET = AET->next;
2236 while (AET)
2238 pETEinsert = AET;
2239 pETEchase = AET;
2240 while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
2241 pETEchase = pETEchase->back;
2243 AET = AET->next;
2244 if (pETEchase != pETEinsert)
2246 pETEchaseBackTMP = pETEchase->back;
2247 pETEinsert->back->next = AET;
2248 if (AET)
2249 AET->back = pETEinsert->back;
2250 pETEinsert->next = pETEchase;
2251 pETEchase->back->next = pETEinsert;
2252 pETEchase->back = pETEinsert;
2253 pETEinsert->back = pETEchaseBackTMP;
2254 changed = TRUE;
2257 return changed;
2260 /***********************************************************************
2261 * REGION_FreeStorage
2263 * Clean up our act.
2265 static void REGION_FreeStorage(ScanLineListBlock *pSLLBlock)
2267 ScanLineListBlock *tmpSLLBlock;
2269 while (pSLLBlock)
2271 tmpSLLBlock = pSLLBlock->next;
2272 HeapFree( SystemHeap, 0, pSLLBlock );
2273 pSLLBlock = tmpSLLBlock;
2278 /***********************************************************************
2279 * REGION_PtsToRegion
2281 * Create an array of rectangles from a list of points.
2283 static int REGION_PtsToRegion(int numFullPtBlocks, int iCurPtBlock,
2284 POINTBLOCK *FirstPtBlock, WINEREGION *reg)
2286 RECT *rects;
2287 POINT *pts;
2288 POINTBLOCK *CurPtBlock;
2289 int i;
2290 RECT *extents;
2291 INT numRects;
2293 extents = &reg->extents;
2295 numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
2297 if (!(reg->rects = HeapReAlloc( SystemHeap, 0, reg->rects,
2298 sizeof(RECT) * numRects )))
2299 return(0);
2301 reg->size = numRects;
2302 CurPtBlock = FirstPtBlock;
2303 rects = reg->rects - 1;
2304 numRects = 0;
2305 extents->left = LARGE_COORDINATE, extents->right = SMALL_COORDINATE;
2307 for ( ; numFullPtBlocks >= 0; numFullPtBlocks--) {
2308 /* the loop uses 2 points per iteration */
2309 i = NUMPTSTOBUFFER >> 1;
2310 if (!numFullPtBlocks)
2311 i = iCurPtBlock >> 1;
2312 for (pts = CurPtBlock->pts; i--; pts += 2) {
2313 if (pts->x == pts[1].x)
2314 continue;
2315 if (numRects && pts->x == rects->left && pts->y == rects->bottom &&
2316 pts[1].x == rects->right &&
2317 (numRects == 1 || rects[-1].top != rects->top) &&
2318 (i && pts[2].y > pts[1].y)) {
2319 rects->bottom = pts[1].y + 1;
2320 continue;
2322 numRects++;
2323 rects++;
2324 rects->left = pts->x; rects->top = pts->y;
2325 rects->right = pts[1].x; rects->bottom = pts[1].y + 1;
2326 if (rects->left < extents->left)
2327 extents->left = rects->left;
2328 if (rects->right > extents->right)
2329 extents->right = rects->right;
2331 CurPtBlock = CurPtBlock->next;
2334 if (numRects) {
2335 extents->top = reg->rects->top;
2336 extents->bottom = rects->bottom;
2337 } else {
2338 extents->left = 0;
2339 extents->top = 0;
2340 extents->right = 0;
2341 extents->bottom = 0;
2343 reg->numRects = numRects;
2345 return(TRUE);
2348 /***********************************************************************
2349 * CreatePolyPolygonRgn32 (GDI32.57)
2351 HRGN WINAPI CreatePolyPolygonRgn(const POINT *Pts, const INT *Count,
2352 INT nbpolygons, INT mode)
2354 HRGN hrgn;
2355 RGNOBJ *obj;
2356 WINEREGION *region;
2357 register EdgeTableEntry *pAET; /* Active Edge Table */
2358 register INT y; /* current scanline */
2359 register int iPts = 0; /* number of pts in buffer */
2360 register EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/
2361 register ScanLineList *pSLL; /* current scanLineList */
2362 register POINT *pts; /* output buffer */
2363 EdgeTableEntry *pPrevAET; /* ptr to previous AET */
2364 EdgeTable ET; /* header node for ET */
2365 EdgeTableEntry AET; /* header node for AET */
2366 EdgeTableEntry *pETEs; /* EdgeTableEntries pool */
2367 ScanLineListBlock SLLBlock; /* header for scanlinelist */
2368 int fixWAET = FALSE;
2369 POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
2370 POINTBLOCK *tmpPtBlock;
2371 int numFullPtBlocks = 0;
2372 INT poly, total;
2374 if(!(hrgn = REGION_CreateRegion(nbpolygons)))
2375 return 0;
2376 obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
2377 region = obj->rgn;
2379 /* special case a rectangle */
2381 if (((nbpolygons == 1) && ((*Count == 4) ||
2382 ((*Count == 5) && (Pts[4].x == Pts[0].x) && (Pts[4].y == Pts[0].y)))) &&
2383 (((Pts[0].y == Pts[1].y) &&
2384 (Pts[1].x == Pts[2].x) &&
2385 (Pts[2].y == Pts[3].y) &&
2386 (Pts[3].x == Pts[0].x)) ||
2387 ((Pts[0].x == Pts[1].x) &&
2388 (Pts[1].y == Pts[2].y) &&
2389 (Pts[2].x == Pts[3].x) &&
2390 (Pts[3].y == Pts[0].y))))
2392 SetRectRgn( hrgn, MIN(Pts[0].x, Pts[2].x), MIN(Pts[0].y, Pts[2].y),
2393 MAX(Pts[0].x, Pts[2].x), MAX(Pts[0].y, Pts[2].y) );
2394 GDI_HEAP_UNLOCK( hrgn );
2395 return hrgn;
2398 for(poly = total = 0; poly < nbpolygons; poly++)
2399 total += Count[poly];
2400 if (! (pETEs = HeapAlloc( SystemHeap, 0, sizeof(EdgeTableEntry) * total )))
2402 REGION_DeleteObject( hrgn, obj );
2403 return 0;
2405 pts = FirstPtBlock.pts;
2406 REGION_CreateETandAET(Count, nbpolygons, Pts, &ET, &AET, pETEs, &SLLBlock);
2407 pSLL = ET.scanlines.next;
2408 curPtBlock = &FirstPtBlock;
2410 if (mode != WINDING) {
2412 * for each scanline
2414 for (y = ET.ymin; y < ET.ymax; y++) {
2416 * Add a new edge to the active edge table when we
2417 * get to the next edge.
2419 if (pSLL != NULL && y == pSLL->scanline) {
2420 REGION_loadAET(&AET, pSLL->edgelist);
2421 pSLL = pSLL->next;
2423 pPrevAET = &AET;
2424 pAET = AET.next;
2427 * for each active edge
2429 while (pAET) {
2430 pts->x = pAET->bres.minor_axis, pts->y = y;
2431 pts++, iPts++;
2434 * send out the buffer
2436 if (iPts == NUMPTSTOBUFFER) {
2437 tmpPtBlock = HeapAlloc( SystemHeap, 0, sizeof(POINTBLOCK));
2438 if(!tmpPtBlock) {
2439 WARN("Can't alloc tPB\n");
2440 return 0;
2442 curPtBlock->next = tmpPtBlock;
2443 curPtBlock = tmpPtBlock;
2444 pts = curPtBlock->pts;
2445 numFullPtBlocks++;
2446 iPts = 0;
2448 EVALUATEEDGEEVENODD(pAET, pPrevAET, y);
2450 REGION_InsertionSort(&AET);
2453 else {
2455 * for each scanline
2457 for (y = ET.ymin; y < ET.ymax; y++) {
2459 * Add a new edge to the active edge table when we
2460 * get to the next edge.
2462 if (pSLL != NULL && y == pSLL->scanline) {
2463 REGION_loadAET(&AET, pSLL->edgelist);
2464 REGION_computeWAET(&AET);
2465 pSLL = pSLL->next;
2467 pPrevAET = &AET;
2468 pAET = AET.next;
2469 pWETE = pAET;
2472 * for each active edge
2474 while (pAET) {
2476 * add to the buffer only those edges that
2477 * are in the Winding active edge table.
2479 if (pWETE == pAET) {
2480 pts->x = pAET->bres.minor_axis, pts->y = y;
2481 pts++, iPts++;
2484 * send out the buffer
2486 if (iPts == NUMPTSTOBUFFER) {
2487 tmpPtBlock = HeapAlloc( SystemHeap, 0,
2488 sizeof(POINTBLOCK) );
2489 if(!tmpPtBlock) {
2490 WARN("Can't alloc tPB\n");
2491 return 0;
2493 curPtBlock->next = tmpPtBlock;
2494 curPtBlock = tmpPtBlock;
2495 pts = curPtBlock->pts;
2496 numFullPtBlocks++; iPts = 0;
2498 pWETE = pWETE->nextWETE;
2500 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
2504 * recompute the winding active edge table if
2505 * we just resorted or have exited an edge.
2507 if (REGION_InsertionSort(&AET) || fixWAET) {
2508 REGION_computeWAET(&AET);
2509 fixWAET = FALSE;
2513 REGION_FreeStorage(SLLBlock.next);
2514 REGION_PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
2515 for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) {
2516 tmpPtBlock = curPtBlock->next;
2517 HeapFree( SystemHeap, 0, curPtBlock );
2518 curPtBlock = tmpPtBlock;
2520 HeapFree( SystemHeap, 0, pETEs );
2521 GDI_HEAP_UNLOCK( hrgn );
2522 return hrgn;
2526 /***********************************************************************
2527 * CreatePolygonRgn16 (GDI.63)
2529 HRGN16 WINAPI CreatePolygonRgn16( const POINT16 * points, INT16 count,
2530 INT16 mode )
2532 return CreatePolyPolygonRgn16( points, &count, 1, mode );
2535 /***********************************************************************
2536 * CreatePolyPolygonRgn16 (GDI.451)
2538 HRGN16 WINAPI CreatePolyPolygonRgn16( const POINT16 *points,
2539 const INT16 *count, INT16 nbpolygons, INT16 mode )
2541 HRGN hrgn;
2542 int i, npts = 0;
2543 INT *count32;
2544 POINT *points32;
2546 for (i = 0; i < nbpolygons; i++)
2547 npts += count[i];
2548 points32 = HeapAlloc( SystemHeap, 0, npts * sizeof(POINT) );
2549 for (i = 0; i < npts; i++)
2550 CONV_POINT16TO32( &(points[i]), &(points32[i]) );
2552 count32 = HeapAlloc( SystemHeap, 0, nbpolygons * sizeof(INT) );
2553 for (i = 0; i < nbpolygons; i++)
2554 count32[i] = count[i];
2555 hrgn = CreatePolyPolygonRgn( points32, count32, nbpolygons, mode );
2556 HeapFree( SystemHeap, 0, count32 );
2557 HeapFree( SystemHeap, 0, points32 );
2558 return hrgn;
2561 /***********************************************************************
2562 * CreatePolygonRgn32 (GDI32.58)
2564 HRGN WINAPI CreatePolygonRgn( const POINT *points, INT count,
2565 INT mode )
2567 return CreatePolyPolygonRgn( points, &count, 1, mode );
2571 /***********************************************************************
2572 * GetRandomRgn [GDI32.215]
2574 * NOTES
2575 * This function is UNDOCUMENTED, it isn't even in the header file.
2576 * I assume that it will return a HRGN32!??
2578 HRGN WINAPI GetRandomRgn(DWORD dwArg1, DWORD dwArg2, DWORD dwArg3)
2580 FIXME("(0x%08lx 0x%08lx 0x%08lx): empty stub!\n",
2581 dwArg1, dwArg2, dwArg3);
2583 return 0;
2586 /***********************************************************************
2587 * REGION_CropAndOffsetRegion
2589 static BOOL REGION_CropAndOffsetRegion(const POINT* off, const RECT *rect, WINEREGION *rgnSrc, WINEREGION* rgnDst)
2591 if( !rect ) /* just copy and offset */
2593 if( rgnDst == rgnSrc )
2595 if( off->x || off->y )
2596 rect = rgnDst->rects;
2597 else
2598 return TRUE;
2600 else
2601 rect = HeapReAlloc( SystemHeap, 0, rgnDst->rects,
2602 rgnSrc->size * sizeof( RECT ));
2603 if( rect )
2605 INT i;
2607 if( rgnDst != rgnSrc )
2608 memcpy( rgnDst, rgnSrc, sizeof( WINEREGION ));
2610 if( off->x || off->y )
2612 for( i = 0; i < rgnDst->numRects; i++ )
2614 rect[i].left = rgnSrc->rects[i].left + off->x;
2615 rect[i].right = rgnSrc->rects[i].right + off->x;
2616 rect[i].top = rgnSrc->rects[i].top + off->y;
2617 rect[i].bottom = rgnSrc->rects[i].bottom + off->y;
2619 OffsetRect( &rgnDst->extents, off->x, off->y );
2621 else
2622 memcpy( rect, rgnSrc->rects, rgnDst->numRects * sizeof( RECT ));
2623 rgnDst->rects = rect;
2625 else
2626 return FALSE;
2628 else if( IsRectEmpty(rect) || !EXTENTCHECK(rect, &rgnSrc->extents) )
2630 empty:
2631 if( !rgnDst->rects )
2633 rgnDst->rects = HeapAlloc(SystemHeap, 0, RGN_DEFAULT_RECTS * sizeof( RECT ));
2634 if( rgnDst->rects )
2635 rgnDst->size = RGN_DEFAULT_RECTS;
2636 else
2637 return FALSE;
2640 TRACE("cropped to empty!\n");
2641 EMPTY_REGION(rgnDst);
2643 else /* region box and clipping rect appear to intersect */
2645 RECT *lpr;
2646 INT i, j, clipa, clipb;
2647 INT left = rgnSrc->extents.right + off->x;
2648 INT right = rgnSrc->extents.left + off->x;
2650 for( clipa = 0; rgnSrc->rects[clipa].bottom <= rect->top; clipa++ )
2651 ; /* skip bands above the clipping rectangle */
2653 for( clipb = clipa; clipb < rgnSrc->numRects; clipb++ )
2654 if( rgnSrc->rects[clipb].top >= rect->bottom )
2655 break; /* and below it */
2657 /* clipa - index of the first rect in the first intersecting band
2658 * clipb - index of the last rect in the last intersecting band
2661 if((rgnDst != rgnSrc) && (rgnDst->size < (i = (clipb - clipa))))
2663 rgnDst->rects = HeapReAlloc( SystemHeap, 0,
2664 rgnDst->rects, i * sizeof(RECT));
2665 if( !rgnDst->rects ) return FALSE;
2666 rgnDst->size = i;
2669 if( TRACE_ON(region) )
2671 REGION_DumpRegion( rgnSrc );
2672 TRACE("\tclipa = %i, clipb = %i\n", clipa, clipb );
2675 for( i = clipa, j = 0; i < clipb ; i++ )
2677 /* i - src index, j - dst index, j is always <= i for obvious reasons */
2679 lpr = rgnSrc->rects + i;
2680 if( lpr->left < rect->right && lpr->right > rect->left )
2682 rgnDst->rects[j].top = lpr->top + off->y;
2683 rgnDst->rects[j].bottom = lpr->bottom + off->y;
2684 rgnDst->rects[j].left = ((lpr->left > rect->left) ? lpr->left : rect->left) + off->x;
2685 rgnDst->rects[j].right = ((lpr->right < rect->right) ? lpr->right : rect->right) + off->x;
2687 if( rgnDst->rects[j].left < left ) left = rgnDst->rects[j].left;
2688 if( rgnDst->rects[j].right > right ) right = rgnDst->rects[j].right;
2690 j++;
2694 if( j == 0 ) goto empty;
2696 rgnDst->extents.left = left;
2697 rgnDst->extents.right = right;
2699 left = rect->top + off->y;
2700 right = rect->bottom + off->y;
2702 rgnDst->numRects = j--;
2703 for( i = 0; i <= j; i++ ) /* fixup top band */
2704 if( rgnDst->rects[i].top < left )
2705 rgnDst->rects[i].top = left;
2706 else
2707 break;
2709 for( i = j; i >= 0; i-- ) /* fixup bottom band */
2710 if( rgnDst->rects[i].bottom > right )
2711 rgnDst->rects[i].bottom = right;
2712 else
2713 break;
2715 rgnDst->extents.top = rgnDst->rects[0].top;
2716 rgnDst->extents.bottom = rgnDst->rects[j].bottom;
2718 rgnDst->type = (j >= 1) ? COMPLEXREGION : SIMPLEREGION;
2720 if( TRACE_ON(region) )
2722 TRACE("result:\n");
2723 REGION_DumpRegion( rgnDst );
2727 return TRUE;
2730 /***********************************************************************
2731 * REGION_CropRgn
2734 * hSrc: Region to crop and offset.
2735 * lpRect: Clipping rectangle. Can be NULL (no clipping).
2736 * lpPt: Points to offset the cropped region. Can be NULL (no offset).
2738 * hDst: Region to hold the result (a new region is created if it's 0).
2739 * Allowed to be the same region as hSrc in which case everyhting
2740 * will be done in place, with no memory reallocations.
2742 * Returns: hDst if success, 0 otherwise.
2744 HRGN REGION_CropRgn( HRGN hDst, HRGN hSrc, const RECT *lpRect, const POINT *lpPt )
2746 /* Optimization of the following generic code:
2748 HRGN h;
2750 if( lpRect )
2751 h = CreateRectRgn( lpRect->left, lpRect->top, lpRect->right, lpRect->bottom );
2752 else
2753 h = CreateRectRgn( 0, 0, 0, 0 );
2754 if( hDst == 0 ) hDst = h;
2755 if( lpRect )
2756 CombineRgn( hDst, hSrc, h, RGN_AND );
2757 else
2758 CombineRgn( hDst, hSrc, 0, RGN_COPY );
2759 if( lpPt )
2760 OffsetRgn( hDst, lpPt->x, lpPt->y );
2761 if( hDst != h )
2762 DeleteObject( h );
2763 return hDst;
2767 RGNOBJ *objSrc = (RGNOBJ *) GDI_HEAP_LOCK( hSrc );
2769 if(objSrc)
2771 RGNOBJ *objDst;
2772 WINEREGION *rgnDst;
2774 if( hDst )
2776 objDst = (RGNOBJ *) GDI_HEAP_LOCK( hDst );
2777 rgnDst = objDst->rgn;
2779 else
2781 rgnDst = HeapAlloc(SystemHeap, 0, sizeof( WINEREGION ));
2782 if( rgnDst )
2784 rgnDst->size = rgnDst->numRects = 0;
2785 rgnDst->rects = NULL; /* back end will allocate exact number */
2789 if( rgnDst )
2791 POINT pt = { 0, 0 };
2793 if( !lpPt ) lpPt = &pt;
2795 if( lpRect )
2796 TRACE("src %p -> dst %p (%i,%i)-(%i,%i) by (%li,%li)\n", objSrc->rgn, rgnDst,
2797 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom, lpPt->x, lpPt->y );
2798 else
2799 TRACE("src %p -> dst %p by (%li,%li)\n", objSrc->rgn, rgnDst, lpPt->x, lpPt->y );
2801 if( REGION_CropAndOffsetRegion( lpPt, lpRect, objSrc->rgn, rgnDst ) == FALSE )
2803 if( hDst ) /* existing rgn */
2805 GDI_HEAP_UNLOCK(hDst);
2806 hDst = 0;
2807 goto done;
2809 goto fail;
2811 else if( hDst == 0 )
2813 if(!(hDst = GDI_AllocObject( sizeof(RGNOBJ), REGION_MAGIC )))
2815 fail:
2816 if( rgnDst->rects )
2817 HeapFree( SystemHeap, 0, rgnDst->rects );
2818 HeapFree( SystemHeap, 0, rgnDst );
2819 goto done;
2822 objDst = (RGNOBJ *) GDI_HEAP_LOCK( hDst );
2823 objDst->rgn = rgnDst;
2826 GDI_HEAP_UNLOCK(hDst);
2828 else hDst = 0;
2829 done:
2830 GDI_HEAP_UNLOCK(hSrc);
2831 return hDst;
2833 return 0;