Added optional debugging code in object management.
[wine] / objects / region.c
1 /*
2  * GDI region objects. Shamelessly ripped out from the X11 distribution
3  * Thanks for the nice licence.
4  *
5  * Copyright 1993, 1994, 1995 Alexandre Julliard
6  * Modifications and additions: Copyright 1998 Huw Davies
7  *                                        1999 Alex Korobka
8  *
9  */
10
11 /************************************************************************
12
13 Copyright (c) 1987, 1988  X Consortium
14
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:
21
22 The above copyright notice and this permission notice shall be included in
23 all copies or substantial portions of the Software.
24
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.
31
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.
35
36
37 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
38
39                         All Rights Reserved
40
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.  
48
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.
56
57 ************************************************************************/
58 /*
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.
64  *
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".
72  *
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.
76  *
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...
82  */
83
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"
91
92 DEFAULT_DEBUG_CHANNEL(region) 
93
94 typedef void (*voidProcp)();
95
96 /* Note the parameter order is different from the X11 equivalents */
97
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);
104
105 #define RGN_DEFAULT_RECTS       2
106
107 /***********************************************************************
108  *            REGION_DumpRegion
109  *            Outputs the contents of a WINEREGION
110  */
111 static void REGION_DumpRegion(WINEREGION *pReg)
112 {
113     RECT *pRect, *pRectEnd = pReg->rects + pReg->numRects;
114
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;
122 }
123
124
125 /***********************************************************************
126  *            REGION_AllocWineRegion
127  *            Create a new empty WINEREGION.
128  */
129 static WINEREGION *REGION_AllocWineRegion( INT n )
130 {
131     WINEREGION *pReg;
132
133     if ((pReg = HeapAlloc(SystemHeap, 0, sizeof( WINEREGION ))))
134     {
135         if ((pReg->rects = HeapAlloc(SystemHeap, 0, n * sizeof( RECT ))))
136         {
137             pReg->size = n;
138             EMPTY_REGION(pReg);
139             return pReg;
140         }
141         HeapFree(SystemHeap, 0, pReg);
142     }
143     return NULL;
144 }
145
146
147 /***********************************************************************
148  *          REGION_CreateRegion
149  *          Create a new empty region.
150  */
151 static HRGN REGION_CreateRegion( INT n )
152 {
153     HRGN hrgn;
154     RGNOBJ *obj;
155
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;
162     }
163     GDI_HEAP_UNLOCK( hrgn );
164     return hrgn;
165 }
166
167
168 /***********************************************************************
169  *           REGION_DestroyWineRegion
170  */
171 static void REGION_DestroyWineRegion( WINEREGION* pReg )
172 {
173     HeapFree( SystemHeap, 0, pReg->rects );
174     HeapFree( SystemHeap, 0, pReg );
175     return;
176 }
177
178 /***********************************************************************
179  *           REGION_DeleteObject
180  */
181 BOOL REGION_DeleteObject( HRGN hrgn, RGNOBJ * obj )
182 {
183     TRACE(" %04x\n", hrgn );
184
185     REGION_DestroyWineRegion( obj->rgn );
186     return GDI_FreeObject( hrgn );
187 }
188
189 /***********************************************************************
190  *           OffsetRgn16    (GDI.101)
191  */
192 INT16 WINAPI OffsetRgn16( HRGN16 hrgn, INT16 x, INT16 y )
193 {
194     return OffsetRgn( hrgn, x, y );
195 }
196
197 /***********************************************************************
198  *           OffsetRgn32   (GDI32.256)
199  */
200 INT WINAPI OffsetRgn( HRGN hrgn, INT x, INT y )
201 {
202     RGNOBJ * obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
203
204     if (obj && (x || y))
205     {
206         INT ret;
207         int nbox = obj->rgn->numRects;
208         RECT *pbox = obj->rgn->rects;
209         
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++;
218             }
219             obj->rgn->extents.left += x;
220             obj->rgn->extents.right += x;
221             obj->rgn->extents.top += y;
222             obj->rgn->extents.bottom += y;
223         }
224         ret = obj->rgn->type;
225         GDI_HEAP_UNLOCK( hrgn );
226         return ret;
227     }
228     return ERROR;
229 }
230
231
232 /***********************************************************************
233  *           GetRgnBox16    (GDI.134)
234  */
235 INT16 WINAPI GetRgnBox16( HRGN16 hrgn, LPRECT16 rect )
236 {
237     RECT r;
238     INT16 ret = (INT16)GetRgnBox( hrgn, &r );
239     CONV_RECT32TO16( &r, rect );
240     return ret;
241 }
242
243 /***********************************************************************
244  *           GetRgnBox32    (GDI32.219)
245  */
246 INT WINAPI GetRgnBox( HRGN hrgn, LPRECT rect )
247 {
248     RGNOBJ * obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
249     if (obj)
250     {
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;
260     }
261     return ERROR;
262 }
263
264
265 /***********************************************************************
266  *           CreateRectRgn16    (GDI.64)
267  *
268  * NOTE: Doesn't call CreateRectRgn32 because of differences in SetRectRgn16/32
269  */
270 HRGN16 WINAPI CreateRectRgn16(INT16 left, INT16 top, INT16 right, INT16 bottom)
271 {
272     HRGN16 hrgn;
273
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;
279 }
280
281
282 /***********************************************************************
283  *           CreateRectRgn32   (GDI32.59)
284  */
285 HRGN WINAPI CreateRectRgn(INT left, INT top, INT right, INT bottom)
286 {
287     HRGN hrgn;
288
289     /* Allocate 2 rects by default to reduce the number of reallocs */
290
291     if (!(hrgn = REGION_CreateRegion(RGN_DEFAULT_RECTS)))
292         return 0;
293     TRACE("\n");
294     SetRectRgn(hrgn, left, top, right, bottom);
295     return hrgn;
296 }
297
298 /***********************************************************************
299  *           CreateRectRgnIndirect16    (GDI.65)
300  */
301 HRGN16 WINAPI CreateRectRgnIndirect16( const RECT16* rect )
302 {
303     return CreateRectRgn16( rect->left, rect->top, rect->right, rect->bottom );
304 }
305
306
307 /***********************************************************************
308  *           CreateRectRgnIndirect32    (GDI32.60)
309  */
310 HRGN WINAPI CreateRectRgnIndirect( const RECT* rect )
311 {
312     return CreateRectRgn( rect->left, rect->top, rect->right, rect->bottom );
313 }
314
315
316 /***********************************************************************
317  *           SetRectRgn16    (GDI.172)
318  *
319  * NOTE: Win 3.1 sets region to empty if left > right
320  */
321 VOID WINAPI SetRectRgn16( HRGN16 hrgn, INT16 left, INT16 top,
322                           INT16 right, INT16 bottom )
323 {
324     if(left < right)
325         SetRectRgn( hrgn, left, top, right, bottom );
326     else
327         SetRectRgn( hrgn, 0, 0, 0, 0 );
328 }
329
330
331 /***********************************************************************
332  *           SetRectRgn32    (GDI32.332)
333  *
334  * Allows either or both left and top to be greater than right or bottom.
335  */
336 BOOL WINAPI SetRectRgn( HRGN hrgn, INT left, INT top,
337                           INT right, INT bottom )
338 {
339     RGNOBJ * obj;
340
341     TRACE(" %04x %d,%d-%d,%d\n", 
342                    hrgn, left, top, right, bottom );
343     
344     if (!(obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC ))) return FALSE;
345
346     if (left > right) { INT tmp = left; left = right; right = tmp; }
347     if (top > bottom) { INT tmp = top; top = bottom; bottom = tmp; }
348
349     if((left != right) && (top != bottom))
350     {
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;
357     }
358     else
359         EMPTY_REGION(obj->rgn);
360
361     GDI_HEAP_UNLOCK( hrgn );
362     return TRUE;
363 }
364
365
366 /***********************************************************************
367  *           CreateRoundRectRgn16    (GDI.444)
368  *
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. 
372  */
373
374 HRGN16 WINAPI CreateRoundRectRgn16( INT16 left, INT16 top,
375                                     INT16 right, INT16 bottom,
376                                     INT16 ellipse_width, INT16 ellipse_height )
377 {
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 );
383 }
384
385 /***********************************************************************
386  *           CreateRoundRectRgn32    (GDI32.61)
387  */
388 HRGN WINAPI CreateRoundRectRgn( INT left, INT top,
389                                     INT right, INT bottom,
390                                     INT ellipse_width, INT ellipse_height )
391 {
392     RGNOBJ * obj;
393     HRGN hrgn;
394     int asq, bsq, d, xd, yd;
395     RECT rect;
396     
397       /* Check if we can do a normal rectangle instead */
398
399     if ((ellipse_width == 0) || (ellipse_height == 0))
400         return CreateRectRgn( left, top, right, bottom );
401
402       /* Make the dimensions sensible */
403
404     if (left > right) { INT tmp = left; left = right; right = tmp; }
405     if (top > bottom) { INT tmp = top; top = bottom; bottom = tmp; }
406
407     ellipse_width = abs(ellipse_width);
408     ellipse_height = abs(ellipse_height);
409
410       /* Create region */
411
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 );
417
418       /* Check parameters */
419
420     if (ellipse_width > right-left) ellipse_width = right-left;
421     if (ellipse_height > bottom-top) ellipse_height = bottom-top;
422
423       /* Ellipse algorithm, based on an article by K. Porter */
424       /* in DDJ Graphics Programming Column, 8/89 */
425
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 */
431
432     rect.left   = left + ellipse_width / 2;
433     rect.right  = right - ellipse_width / 2;
434
435       /* Loop to draw first half of quadrant */
436
437     while (xd < yd)
438     {
439         if (d > 0)  /* if nearest pixel is toward the center */
440         {
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;
450         }
451         rect.left--;        /* next horiz point */
452         rect.right++;
453         xd += 2*bsq;
454         d  += bsq + xd;
455     }
456
457       /* Loop to draw second half of quadrant */
458
459     d += (3 * (asq-bsq) / 2 - (xd+yd)) / 2;
460     while (yd >= 0)
461     {
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 */
470         {
471             rect.left--;     /* move away from center */
472             rect.right++;
473             xd += 2*bsq;
474             d  += xd;
475         }
476         yd -= 2*asq;
477         d  += asq - yd;
478     }
479
480       /* Add the inside rectangle */
481
482     if (top <= bottom)
483     {
484         rect.top = top;
485         rect.bottom = bottom;
486         REGION_UnionRectWithRegion( &rect, obj->rgn );
487     }
488     obj->rgn->type = SIMPLEREGION; /* FIXME? */
489     GDI_HEAP_UNLOCK( hrgn );
490     return hrgn;
491 }
492
493
494 /***********************************************************************
495  *           CreateEllipticRgn16    (GDI.54)
496  */
497 HRGN16 WINAPI CreateEllipticRgn16( INT16 left, INT16 top,
498                                    INT16 right, INT16 bottom )
499 {
500     return (HRGN16)CreateRoundRectRgn( left, top, right, bottom,
501                                          right-left, bottom-top );
502 }
503
504
505 /***********************************************************************
506  *           CreateEllipticRgn32    (GDI32.39)
507  */
508 HRGN WINAPI CreateEllipticRgn( INT left, INT top,
509                                    INT right, INT bottom )
510 {
511     return CreateRoundRectRgn( left, top, right, bottom,
512                                  right-left, bottom-top );
513 }
514
515
516 /***********************************************************************
517  *           CreateEllipticRgnIndirect16    (GDI.55)
518  */
519 HRGN16 WINAPI CreateEllipticRgnIndirect16( const RECT16 *rect )
520 {
521     return CreateRoundRectRgn( rect->left, rect->top, rect->right,
522                                  rect->bottom, rect->right - rect->left,
523                                  rect->bottom - rect->top );
524 }
525
526
527 /***********************************************************************
528  *           CreateEllipticRgnIndirect32    (GDI32.40)
529  */
530 HRGN WINAPI CreateEllipticRgnIndirect( const RECT *rect )
531 {
532     return CreateRoundRectRgn( rect->left, rect->top, rect->right,
533                                  rect->bottom, rect->right - rect->left,
534                                  rect->bottom - rect->top );
535 }
536
537 /***********************************************************************
538  *           GetRegionData32   (GDI32.217)
539  * 
540  */
541 DWORD WINAPI GetRegionData(HRGN hrgn, DWORD count, LPRGNDATA rgndata)
542 {
543     DWORD size;
544     RGNOBJ *obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
545     
546     TRACE(" %04x count = %ld, rgndata = %p\n",
547                    hrgn, count, rgndata);
548
549     if(!obj) return 0;
550
551     size = obj->rgn->numRects * sizeof(RECT);
552     if(count < (size + sizeof(RGNDATAHEADER)) || rgndata == NULL)
553     {
554         GDI_HEAP_UNLOCK( hrgn );
555         return size + sizeof(RGNDATAHEADER);
556     }
557
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;
566
567     memcpy( rgndata->Buffer, obj->rgn->rects, size );
568
569     GDI_HEAP_UNLOCK( hrgn );
570     return 1;
571 }
572
573 /***********************************************************************
574  *           GetRegionData16   (GDI.607)
575  * FIXME: is LPRGNDATA the same in Win16 and Win32 ?
576  */
577 DWORD WINAPI GetRegionData16(HRGN16 hrgn, DWORD count, LPRGNDATA rgndata)
578 {
579     return GetRegionData((HRGN)hrgn, count, rgndata);
580 }
581
582 /***********************************************************************
583  *           ExtCreateRegion   (GDI32.94)
584  * 
585  */
586 HRGN WINAPI ExtCreateRegion( const XFORM* lpXform, DWORD dwCount, const RGNDATA* rgndata)
587 {
588     HRGN hrgn;
589
590     TRACE(" %p %ld %p = ", lpXform, dwCount, rgndata );
591
592     if( lpXform )
593         WARN("(Xform not implemented - ignored) ");
594     
595     if( rgndata->rdh.iType != RDH_RECTANGLES )
596     {
597         /* FIXME: We can use CreatePolyPolygonRgn() here
598          *        for trapezoidal data */
599
600         WARN("(Unsupported region data) ");
601         goto fail;
602     }
603
604     if( (hrgn = REGION_CreateRegion( rgndata->rdh.nCount )) )
605     {
606         RECT *pCurRect, *pEndRect;
607         RGNOBJ *obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
608
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 );
613
614         TRACE("%04x\n", hrgn );
615         return hrgn;
616     }
617 fail:
618     WARN("Failed\n");
619     return 0;
620 }
621
622 /***********************************************************************
623  *           PtInRegion16    (GDI.161)
624  */
625 BOOL16 WINAPI PtInRegion16( HRGN16 hrgn, INT16 x, INT16 y )
626 {
627     return PtInRegion( hrgn, x, y );
628 }
629
630
631 /***********************************************************************
632  *           PtInRegion32    (GDI32.278)
633  */
634 BOOL WINAPI PtInRegion( HRGN hrgn, INT x, INT y )
635 {
636     RGNOBJ * obj;
637     
638     if ((obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC )))
639     {
640         BOOL ret = FALSE;
641         int i;
642
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;
649     }
650     return FALSE;
651 }
652
653
654 /***********************************************************************
655  *           RectInRegion16    (GDI.181)
656  */
657 BOOL16 WINAPI RectInRegion16( HRGN16 hrgn, const RECT16 *rect )
658 {
659     RECT r32;
660
661     CONV_RECT16TO32(rect, &r32);
662     return (BOOL16)RectInRegion(hrgn, &r32);
663 }
664
665
666 /***********************************************************************
667  *           RectInRegion32    (GDI32.281)
668  *
669  * Returns TRUE if rect is at least partly inside hrgn
670  */
671 BOOL WINAPI RectInRegion( HRGN hrgn, const RECT *rect )
672 {
673     RGNOBJ * obj;
674     
675     if ((obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC )))
676     {
677         RECT *pCurRect, *pRectEnd;
678         BOOL ret = FALSE;
679     
680     /* this is (just) a useful optimization */
681         if ((obj->rgn->numRects > 0) && EXTENTCHECK(&obj->rgn->extents,
682                                                       rect))
683         {
684             for (pCurRect = obj->rgn->rects, pRectEnd = pCurRect +
685              obj->rgn->numRects; pCurRect < pRectEnd; pCurRect++)
686             {
687                 if (pCurRect->bottom <= rect->top)
688                     continue;             /* not far enough down yet */
689
690                 if (pCurRect->top >= rect->bottom) {
691                     ret = FALSE;          /* too far down */
692                     break;
693                 }
694
695                 if (pCurRect->right <= rect->left)
696                     continue;              /* not far enough over yet */
697
698                 if (pCurRect->left >= rect->right) {
699                     continue;
700                 }
701
702                 ret = TRUE;
703                 break;
704             }
705         }
706         GDI_HEAP_UNLOCK(hrgn);
707         return ret;
708     }
709     return FALSE;
710 }
711
712 /***********************************************************************
713  *           EqualRgn16    (GDI.72)
714  */
715 BOOL16 WINAPI EqualRgn16( HRGN16 rgn1, HRGN16 rgn2 )
716 {
717     return EqualRgn( rgn1, rgn2 );
718 }
719
720
721 /***********************************************************************
722  *           EqualRgn32    (GDI32.90)
723  */
724 BOOL WINAPI EqualRgn( HRGN hrgn1, HRGN hrgn2 )
725 {
726     RGNOBJ *obj1, *obj2;
727     BOOL ret = FALSE;
728
729     if ((obj1 = (RGNOBJ *) GDI_GetObjPtr( hrgn1, REGION_MAGIC ))) 
730     {
731         if ((obj2 = (RGNOBJ *) GDI_GetObjPtr( hrgn2, REGION_MAGIC ))) 
732         {
733             int i;
734
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;
744                 }
745             }
746             GDI_HEAP_UNLOCK(hrgn2);
747         }
748         GDI_HEAP_UNLOCK(hrgn1);
749     }
750     return ret;
751 }
752 /***********************************************************************
753  *           REGION_UnionRectWithRegion
754  *           Adds a rectangle to a WINEREGION
755  *           See below for REGION_UnionRectWithRgn
756  */
757 static void REGION_UnionRectWithRegion(const RECT *rect, WINEREGION *rgn)
758 {
759     WINEREGION region;
760
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;
768 }
769
770 /***********************************************************************
771  *           REGION_UnionRectWithRgn
772  *           Adds a rectangle to a HRGN32
773  *           A helper used by scroll.c
774  */
775 BOOL REGION_UnionRectWithRgn( HRGN hrgn, const RECT *lpRect )
776 {
777     RGNOBJ *obj = (RGNOBJ *) GDI_HEAP_LOCK( hrgn );
778
779     if(!obj) return FALSE;
780     REGION_UnionRectWithRegion( lpRect, obj->rgn );
781     GDI_HEAP_UNLOCK(hrgn);
782     return TRUE;
783 }
784
785 /***********************************************************************
786  *           REGION_CreateFrameRgn
787  *
788  * Create a region that is a frame around another region.
789  * Expand all rectangles by +/- x and y, then subtract original region.
790  */
791 BOOL REGION_FrameRgn( HRGN hDest, HRGN hSrc, INT x, INT y )
792 {
793     BOOL bRet;
794     RGNOBJ *srcObj = (RGNOBJ*) GDI_GetObjPtr( hSrc, REGION_MAGIC );
795
796     if (srcObj->rgn->numRects != 0) 
797     {
798         RGNOBJ* destObj = (RGNOBJ*) GDI_GetObjPtr( hDest, REGION_MAGIC );
799         RECT *pRect, *pEndRect;
800         RECT tempRect;
801
802         EMPTY_REGION( destObj->rgn );
803         
804         pEndRect = srcObj->rgn->rects + srcObj->rgn->numRects;
805         for(pRect = srcObj->rgn->rects; pRect < pEndRect; pRect++)
806         {
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 );
812         }
813         REGION_SubtractRegion( destObj->rgn, destObj->rgn, srcObj->rgn );
814         GDI_HEAP_UNLOCK ( hDest );
815         bRet = TRUE;
816     }
817     else
818         bRet = FALSE;
819     GDI_HEAP_UNLOCK( hSrc );
820     return bRet;
821 }
822
823 /***********************************************************************
824  *           REGION_LPTODP
825  *
826  * Convert region to device co-ords for the supplied dc. 
827  */
828 BOOL REGION_LPTODP( HDC hdc, HRGN hDest, HRGN hSrc )
829 {
830     RECT *pCurRect, *pEndRect;
831     RGNOBJ *srcObj, *destObj;
832     DC * dc = DC_GetDCPtr( hdc );
833     RECT tmpRect;
834
835     TRACE(" hdc=%04x dest=%04x src=%04x\n",
836                     hdc, hDest, hSrc) ;
837     
838     if (dc->w.MapMode == MM_TEXT) /* Requires only a translation */
839     {
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;
844     }
845
846     if(!( srcObj = (RGNOBJ *) GDI_GetObjPtr( hSrc, REGION_MAGIC) ))
847         return FALSE;
848     if(!( destObj = (RGNOBJ *) GDI_GetObjPtr( hDest, REGION_MAGIC) ))
849     {
850         GDI_HEAP_UNLOCK( hSrc );
851         return FALSE;
852     }
853     EMPTY_REGION( destObj->rgn );
854
855     pEndRect = srcObj->rgn->rects + srcObj->rgn->numRects;
856     for(pCurRect = srcObj->rgn->rects; pCurRect < pEndRect; pCurRect++)
857     {
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 );
864     }
865     
866     GDI_HEAP_UNLOCK( hDest );
867     GDI_HEAP_UNLOCK( hSrc );
868     return TRUE;
869 }
870     
871 /***********************************************************************
872  *           CombineRgn16    (GDI.451)
873  */
874 INT16 WINAPI CombineRgn16(HRGN16 hDest, HRGN16 hSrc1, HRGN16 hSrc2, INT16 mode)
875 {
876     return (INT16)CombineRgn( hDest, hSrc1, hSrc2, mode );
877 }
878
879
880 /***********************************************************************
881  *           CombineRgn32   (GDI32.19)
882  *
883  * Note: The behavior is correct even if src and dest regions are the same.
884  */
885 INT WINAPI CombineRgn(HRGN hDest, HRGN hSrc1, HRGN hSrc2, INT mode)
886 {
887     RGNOBJ *destObj = (RGNOBJ *) GDI_GetObjPtr( hDest, REGION_MAGIC);
888     INT result = ERROR;
889
890     TRACE(" %04x,%04x -> %04x mode=%x\n", 
891                  hSrc1, hSrc2, hDest, mode );
892     if (destObj)
893     {
894         RGNOBJ *src1Obj = (RGNOBJ *) GDI_GetObjPtr( hSrc1, REGION_MAGIC);
895
896         if (src1Obj)
897         {
898             TRACE("dump:\n");
899             if(TRACE_ON(region)) 
900               REGION_DumpRegion(src1Obj->rgn);
901             if (mode == RGN_COPY)
902             {
903                 REGION_CopyRegion( destObj->rgn, src1Obj->rgn );
904                 result = destObj->rgn->type;
905             }
906             else
907             {
908                 RGNOBJ *src2Obj = (RGNOBJ *) GDI_GetObjPtr( hSrc2, REGION_MAGIC);
909
910                 if (src2Obj)
911                 {
912                     TRACE("dump:\n");
913                     if(TRACE_ON(region)) 
914                       REGION_DumpRegion(src2Obj->rgn);
915                     switch (mode)
916                     {
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;
929                     }
930                     result = destObj->rgn->type;
931                     GDI_HEAP_UNLOCK( hSrc2 );
932                 }
933             }
934             GDI_HEAP_UNLOCK( hSrc1 );
935         }
936         TRACE("dump:\n");
937         if(TRACE_ON(region)) 
938           REGION_DumpRegion(destObj->rgn);
939
940         GDI_HEAP_UNLOCK( hDest );
941     }
942     return result;
943 }
944
945 /***********************************************************************
946  *           REGION_SetExtents
947  *           Re-calculate the extents of a region
948  */
949 static void REGION_SetExtents (WINEREGION *pReg)
950 {
951     RECT *pRect, *pRectEnd, *pExtents;
952
953     if (pReg->numRects == 0)
954     {
955         pReg->extents.left = 0;
956         pReg->extents.top = 0;
957         pReg->extents.right = 0;
958         pReg->extents.bottom = 0;
959         return;
960     }
961
962     pExtents = &pReg->extents;
963     pRect = pReg->rects;
964     pRectEnd = &pRect[pReg->numRects - 1];
965
966     /*
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...
972      */
973     pExtents->left = pRect->left;
974     pExtents->top = pRect->top;
975     pExtents->right = pRectEnd->right;
976     pExtents->bottom = pRectEnd->bottom;
977
978     while (pRect <= pRectEnd)
979     {
980         if (pRect->left < pExtents->left)
981             pExtents->left = pRect->left;
982         if (pRect->right > pExtents->right)
983             pExtents->right = pRect->right;
984         pRect++;
985     }
986 }
987
988 /***********************************************************************
989  *           REGION_CopyRegion
990  */
991 static void REGION_CopyRegion(WINEREGION *dst, WINEREGION *src)
992 {
993     if (dst != src) /*  don't want to copy to itself */
994     {  
995         if (dst->size < src->numRects)
996         {
997             if (! (dst->rects = HeapReAlloc( SystemHeap, 0, dst->rects,
998                                 src->numRects * sizeof(RECT) )))
999                 return;
1000             dst->size = src->numRects;
1001         }
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;
1008
1009         memcpy((char *) dst->rects, (char *) src->rects,
1010                (int) (src->numRects * sizeof(RECT)));
1011     }
1012     return;
1013 }
1014
1015 /***********************************************************************
1016  *           REGION_Coalesce
1017  *
1018  *      Attempt to merge the rects in the current band with those in the
1019  *      previous one. Used only by REGION_RegionOp.
1020  *
1021  * Results:
1022  *      The new index for the previous band.
1023  *
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.
1029  *
1030  */
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 */
1035 ) {
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 */
1042
1043     pRegEnd = &pReg->rects[pReg->numRects];
1044
1045     pPrevRect = &pReg->rects[prevStart];
1046     prevNumRects = curStart - prevStart;
1047
1048     /*
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.
1052      */
1053     pCurRect = &pReg->rects[curStart];
1054     bandtop = pCurRect->top;
1055     for (curNumRects = 0;
1056          (pCurRect != pRegEnd) && (pCurRect->top == bandtop);
1057          curNumRects++)
1058     {
1059         pCurRect++;
1060     }
1061     
1062     if (pCurRect != pRegEnd)
1063     {
1064         /*
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).
1069          */
1070         pRegEnd--;
1071         while (pRegEnd[-1].top == pRegEnd->top)
1072         {
1073             pRegEnd--;
1074         }
1075         curStart = pRegEnd - pReg->rects;
1076         pRegEnd = pReg->rects + pReg->numRects;
1077     }
1078         
1079     if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
1080         pCurRect -= curNumRects;
1081         /*
1082          * The bands may only be coalesced if the bottom of the previous
1083          * matches the top scanline of the current.
1084          */
1085         if (pPrevRect->bottom == pCurRect->top)
1086         {
1087             /*
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.
1092              */
1093             do
1094             {
1095                 if ((pPrevRect->left != pCurRect->left) ||
1096                     (pPrevRect->right != pCurRect->right))
1097                 {
1098                     /*
1099                      * The bands don't line up so they can't be coalesced.
1100                      */
1101                     return (curStart);
1102                 }
1103                 pPrevRect++;
1104                 pCurRect++;
1105                 prevNumRects -= 1;
1106             } while (prevNumRects != 0);
1107
1108             pReg->numRects -= curNumRects;
1109             pCurRect -= curNumRects;
1110             pPrevRect -= curNumRects;
1111
1112             /*
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.
1116              */
1117             do
1118             {
1119                 pPrevRect->bottom = pCurRect->bottom;
1120                 pPrevRect++;
1121                 pCurRect++;
1122                 curNumRects -= 1;
1123             } while (curNumRects != 0);
1124
1125             /*
1126              * If only one band was added to the region, we have to backup
1127              * curStart to the start of the previous band.
1128              *
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.
1134              */
1135             if (pCurRect == pRegEnd)
1136             {
1137                 curStart = prevStart;
1138             }
1139             else
1140             {
1141                 do
1142                 {
1143                     *pPrevRect++ = *pCurRect++;
1144                 } while (pCurRect != pRegEnd);
1145             }
1146             
1147         }
1148     }
1149     return (curStart);
1150 }
1151
1152 /***********************************************************************
1153  *           REGION_RegionOp
1154  *
1155  *      Apply an operation to two regions. Called by REGION_Union,
1156  *      REGION_Inverse, REGION_Subtract, REGION_Intersect...
1157  *
1158  * Results:
1159  *      None.
1160  *
1161  * Side Effects:
1162  *      The new region is overwritten.
1163  *
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.
1175  *
1176  */
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 */
1184 ) {
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 */
1200     
1201     /*
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.
1207      */
1208     r1 = reg1->rects;
1209     r2 = reg2->rects;
1210     r1End = r1 + reg1->numRects;
1211     r2End = r2 + reg2->numRects;
1212     
1213
1214     /*
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. 
1218      */
1219
1220     oldRects = newReg->rects;
1221     newReg->numRects = 0;
1222
1223     /*
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.
1229      */
1230     newReg->size = MAX(reg1->numRects,reg2->numRects) * 2;
1231
1232     if (! (newReg->rects = HeapAlloc( SystemHeap, 0, 
1233                                   sizeof(RECT) * newReg->size )))
1234     {
1235         newReg->size = 0;
1236         return;
1237     }
1238     
1239     /*
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.
1251      */
1252     if (reg1->extents.top < reg2->extents.top)
1253         ybot = reg1->extents.top;
1254     else
1255         ybot = reg2->extents.top;
1256     
1257     /*
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.
1265      */
1266     prevBand = 0;
1267     
1268     do
1269     {
1270         curBand = newReg->numRects;
1271
1272         /*
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.
1278          */
1279         r1BandEnd = r1;
1280         while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top))
1281         {
1282             r1BandEnd++;
1283         }
1284         
1285         r2BandEnd = r2;
1286         while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top))
1287         {
1288             r2BandEnd++;
1289         }
1290         
1291         /*
1292          * First handle the band that doesn't intersect, if any.
1293          *
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.
1298          */
1299         if (r1->top < r2->top)
1300         {
1301             top = MAX(r1->top,ybot);
1302             bot = MIN(r1->bottom,r2->top);
1303
1304             if ((top != bot) && (nonOverlap1Func != (void (*)())NULL))
1305             {
1306                 (* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot);
1307             }
1308
1309             ytop = r2->top;
1310         }
1311         else if (r2->top < r1->top)
1312         {
1313             top = MAX(r2->top,ybot);
1314             bot = MIN(r2->bottom,r1->top);
1315
1316             if ((top != bot) && (nonOverlap2Func != (void (*)())NULL))
1317             {
1318                 (* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot);
1319             }
1320
1321             ytop = r1->top;
1322         }
1323         else
1324         {
1325             ytop = r1->top;
1326         }
1327
1328         /*
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...
1333          */
1334         if (newReg->numRects != curBand)
1335         {
1336             prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1337         }
1338
1339         /*
1340          * Now see if we've hit an intersecting band. The two bands only
1341          * intersect if ybot > ytop
1342          */
1343         ybot = MIN(r1->bottom, r2->bottom);
1344         curBand = newReg->numRects;
1345         if (ybot > ytop)
1346         {
1347             (* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
1348
1349         }
1350         
1351         if (newReg->numRects != curBand)
1352         {
1353             prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1354         }
1355
1356         /*
1357          * If we've finished with a band (bottom == ybot) we skip forward
1358          * in the region to the next band.
1359          */
1360         if (r1->bottom == ybot)
1361         {
1362             r1 = r1BandEnd;
1363         }
1364         if (r2->bottom == ybot)
1365         {
1366             r2 = r2BandEnd;
1367         }
1368     } while ((r1 != r1End) && (r2 != r2End));
1369
1370     /*
1371      * Deal with whichever region still has rectangles left.
1372      */
1373     curBand = newReg->numRects;
1374     if (r1 != r1End)
1375     {
1376         if (nonOverlap1Func != (void (*)())NULL)
1377         {
1378             do
1379             {
1380                 r1BandEnd = r1;
1381                 while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top))
1382                 {
1383                     r1BandEnd++;
1384                 }
1385                 (* nonOverlap1Func) (newReg, r1, r1BandEnd,
1386                                      MAX(r1->top,ybot), r1->bottom);
1387                 r1 = r1BandEnd;
1388             } while (r1 != r1End);
1389         }
1390     }
1391     else if ((r2 != r2End) && (nonOverlap2Func != (void (*)())NULL))
1392     {
1393         do
1394         {
1395             r2BandEnd = r2;
1396             while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top))
1397             {
1398                  r2BandEnd++;
1399             }
1400             (* nonOverlap2Func) (newReg, r2, r2BandEnd,
1401                                 MAX(r2->top,ybot), r2->bottom);
1402             r2 = r2BandEnd;
1403         } while (r2 != r2End);
1404     }
1405
1406     if (newReg->numRects != curBand)
1407     {
1408         (void) REGION_Coalesce (newReg, prevBand, curBand);
1409     }
1410
1411     /*
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...
1415      *
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...).
1418      */
1419     if ((newReg->numRects < (newReg->size >> 1)) && (newReg->numRects > 2))
1420     {
1421         if (REGION_NOT_EMPTY(newReg))
1422         {
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;
1429         }
1430         else
1431         {
1432             /*
1433              * No point in doing the extra work involved in an Xrealloc if
1434              * the region is empty
1435              */
1436             newReg->size = 1;
1437             HeapFree( SystemHeap, 0, newReg->rects );
1438             newReg->rects = HeapAlloc( SystemHeap, 0, sizeof(RECT) );
1439         }
1440     }
1441     HeapFree( SystemHeap, 0, oldRects );
1442     return;
1443 }
1444
1445 /***********************************************************************
1446  *          Region Intersection
1447  ***********************************************************************/
1448
1449
1450 /***********************************************************************
1451  *           REGION_IntersectO
1452  *
1453  * Handle an overlapping band for REGION_Intersect.
1454  *
1455  * Results:
1456  *      None.
1457  *
1458  * Side Effects:
1459  *      Rectangles may be added to the region.
1460  *
1461  */
1462 static void REGION_IntersectO(WINEREGION *pReg,  RECT *r1, RECT *r1End,
1463                 RECT *r2, RECT *r2End, INT top, INT bottom)
1464
1465 {
1466     INT       left, right;
1467     RECT      *pNextRect;
1468
1469     pNextRect = &pReg->rects[pReg->numRects];
1470
1471     while ((r1 != r1End) && (r2 != r2End))
1472     {
1473         left = MAX(r1->left, r2->left);
1474         right = MIN(r1->right, r2->right);
1475
1476         /*
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...
1482          */
1483         if (left < right)
1484         {
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++;
1492         }
1493
1494         /*
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.
1498          */
1499         if (r1->right < r2->right)
1500         {
1501             r1++;
1502         }
1503         else if (r2->right < r1->right)
1504         {
1505             r2++;
1506         }
1507         else
1508         {
1509             r1++;
1510             r2++;
1511         }
1512     }
1513     return;
1514 }
1515
1516 /***********************************************************************
1517  *           REGION_IntersectRegion
1518  */
1519 static void REGION_IntersectRegion(WINEREGION *newReg, WINEREGION *reg1,
1520                                    WINEREGION *reg2)
1521 {
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);
1529     
1530     /*
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.
1536      */
1537     REGION_SetExtents(newReg);
1538     newReg->type = (newReg->numRects) ? COMPLEXREGION : NULLREGION ;
1539     return;
1540 }
1541
1542 /***********************************************************************
1543  *           Region Union
1544  ***********************************************************************/
1545
1546 /***********************************************************************
1547  *           REGION_UnionNonO
1548  *
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.
1552  *
1553  * Results:
1554  *      None.
1555  *
1556  * Side Effects:
1557  *      pReg->numRects is incremented and the final rectangles overwritten
1558  *      with the rectangles we're passed.
1559  *
1560  */
1561 static void REGION_UnionNonO (WINEREGION *pReg, RECT *r, RECT *rEnd,
1562                               INT top, INT bottom)
1563 {
1564     RECT *pNextRect;
1565
1566     pNextRect = &pReg->rects[pReg->numRects];
1567
1568     while (r != rEnd)
1569     {
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++;
1578     }
1579     return;
1580 }
1581
1582 /***********************************************************************
1583  *           REGION_UnionO
1584  *
1585  *      Handle an overlapping band for the union operation. Picks the
1586  *      left-most rectangle each time and merges it into the region.
1587  *
1588  * Results:
1589  *      None.
1590  *
1591  * Side Effects:
1592  *      Rectangles are overwritten in pReg->rects and pReg->numRects will
1593  *      be changed.
1594  *
1595  */
1596 static void REGION_UnionO (WINEREGION *pReg, RECT *r1, RECT *r1End,
1597                            RECT *r2, RECT *r2End, INT top, INT bottom)
1598 {
1599     RECT *pNextRect;
1600     
1601     pNextRect = &pReg->rects[pReg->numRects];
1602
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))  \
1608     {  \
1609         if (pNextRect[-1].right < r->right)  \
1610         {  \
1611             pNextRect[-1].right = r->right;  \
1612         }  \
1613     }  \
1614     else  \
1615     {  \
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;  \
1623     }  \
1624     r++;
1625     
1626     while ((r1 != r1End) && (r2 != r2End))
1627     {
1628         if (r1->left < r2->left)
1629         {
1630             MERGERECT(r1);
1631         }
1632         else
1633         {
1634             MERGERECT(r2);
1635         }
1636     }
1637     
1638     if (r1 != r1End)
1639     {
1640         do
1641         {
1642             MERGERECT(r1);
1643         } while (r1 != r1End);
1644     }
1645     else while (r2 != r2End)
1646     {
1647         MERGERECT(r2);
1648     }
1649     return;
1650 }
1651
1652 /***********************************************************************
1653  *           REGION_UnionRegion
1654  */
1655 static void REGION_UnionRegion(WINEREGION *newReg, WINEREGION *reg1,
1656                                WINEREGION *reg2)
1657 {
1658     /*  checks all the simple cases */
1659
1660     /*
1661      * Region 1 and 2 are the same or region 1 is empty
1662      */
1663     if ( (reg1 == reg2) || (!(reg1->numRects)) )
1664     {
1665         if (newReg != reg2)
1666             REGION_CopyRegion(newReg, reg2);
1667         return;
1668     }
1669
1670     /*
1671      * if nothing to union (region 2 empty)
1672      */
1673     if (!(reg2->numRects))
1674     {
1675         if (newReg != reg1)
1676             REGION_CopyRegion(newReg, reg1);
1677         return;
1678     }
1679
1680     /*
1681      * Region 1 completely subsumes region 2
1682      */
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))
1688     {
1689         if (newReg != reg1)
1690             REGION_CopyRegion(newReg, reg1);
1691         return;
1692     }
1693
1694     /*
1695      * Region 2 completely subsumes region 1
1696      */
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))
1702     {
1703         if (newReg != reg2)
1704             REGION_CopyRegion(newReg, reg2);
1705         return;
1706     }
1707
1708     REGION_RegionOp (newReg, reg1, reg2, (voidProcp) REGION_UnionO, 
1709                 (voidProcp) REGION_UnionNonO, (voidProcp) REGION_UnionNonO);
1710
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;
1717 }
1718
1719 /***********************************************************************
1720  *           Region Subtraction
1721  ***********************************************************************/
1722
1723 /***********************************************************************
1724  *           REGION_SubtractNonO1
1725  *
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.
1728  *
1729  * Results:
1730  *      None.
1731  *
1732  * Side Effects:
1733  *      pReg may be affected.
1734  *
1735  */
1736 static void REGION_SubtractNonO1 (WINEREGION *pReg, RECT *r, RECT *rEnd, 
1737                 INT top, INT bottom)
1738 {
1739     RECT *pNextRect;
1740         
1741     pNextRect = &pReg->rects[pReg->numRects];
1742         
1743     while (r != rEnd)
1744     {
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++;
1753     }
1754     return;
1755 }
1756
1757
1758 /***********************************************************************
1759  *           REGION_SubtractO
1760  *
1761  *      Overlapping band subtraction. x1 is the left-most point not yet
1762  *      checked.
1763  *
1764  * Results:
1765  *      None.
1766  *
1767  * Side Effects:
1768  *      pReg may have rectangles added to it.
1769  *
1770  */
1771 static void REGION_SubtractO (WINEREGION *pReg, RECT *r1, RECT *r1End, 
1772                 RECT *r2, RECT *r2End, INT top, INT bottom)
1773 {
1774     RECT *pNextRect;
1775     INT left;
1776     
1777     left = r1->left;
1778     pNextRect = &pReg->rects[pReg->numRects];
1779
1780     while ((r1 != r1End) && (r2 != r2End))
1781     {
1782         if (r2->right <= left)
1783         {
1784             /*
1785              * Subtrahend missed the boat: go to next subtrahend.
1786              */
1787             r2++;
1788         }
1789         else if (r2->left <= left)
1790         {
1791             /*
1792              * Subtrahend preceeds minuend: nuke left edge of minuend.
1793              */
1794             left = r2->right;
1795             if (left >= r1->right)
1796             {
1797                 /*
1798                  * Minuend completely covered: advance to next minuend and
1799                  * reset left fence to edge of new minuend.
1800                  */
1801                 r1++;
1802                 if (r1 != r1End)
1803                     left = r1->left;
1804             }
1805             else
1806             {
1807                 /*
1808                  * Subtrahend now used up since it doesn't extend beyond
1809                  * minuend
1810                  */
1811                 r2++;
1812             }
1813         }
1814         else if (r2->left < r1->right)
1815         {
1816             /*
1817              * Left part of subtrahend covers part of minuend: add uncovered
1818              * part of minuend to region and skip to next subtrahend.
1819              */
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)
1829             {
1830                 /*
1831                  * Minuend used up: advance to new...
1832                  */
1833                 r1++;
1834                 if (r1 != r1End)
1835                     left = r1->left;
1836             }
1837             else
1838             {
1839                 /*
1840                  * Subtrahend used up
1841                  */
1842                 r2++;
1843             }
1844         }
1845         else
1846         {
1847             /*
1848              * Minuend used up: add any remaining piece before advancing.
1849              */
1850             if (r1->right > left)
1851             {
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++;
1859             }
1860             r1++;
1861             left = r1->left;
1862         }
1863     }
1864
1865     /*
1866      * Add remaining minuend rectangles to region.
1867      */
1868     while (r1 != r1End)
1869     {
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)
1879         {
1880             left = r1->left;
1881         }
1882     }
1883     return;
1884 }
1885         
1886 /***********************************************************************
1887  *           REGION_SubtractRegion
1888  *
1889  *      Subtract regS from regM and leave the result in regD.
1890  *      S stands for subtrahend, M for minuend and D for difference.
1891  *
1892  * Results:
1893  *      TRUE.
1894  *
1895  * Side Effects:
1896  *      regD is overwritten.
1897  *
1898  */
1899 static void REGION_SubtractRegion(WINEREGION *regD, WINEREGION *regM,
1900                                                        WINEREGION *regS )
1901 {
1902    /* check for trivial reject */
1903     if ( (!(regM->numRects)) || (!(regS->numRects))  ||
1904         (!EXTENTCHECK(&regM->extents, &regS->extents)) )
1905     {
1906         REGION_CopyRegion(regD, regM);
1907         return;
1908     }
1909  
1910     REGION_RegionOp (regD, regM, regS, (voidProcp) REGION_SubtractO, 
1911                 (voidProcp) REGION_SubtractNonO1, (voidProcp) NULL);
1912
1913     /*
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.
1919      */
1920     REGION_SetExtents (regD);
1921     regD->type = (regD->numRects) ? COMPLEXREGION : NULLREGION ;
1922     return;
1923 }
1924
1925 /***********************************************************************
1926  *           REGION_XorRegion
1927  */
1928 static void REGION_XorRegion(WINEREGION *dr, WINEREGION *sra,
1929                                                         WINEREGION *srb)
1930 {
1931     WINEREGION *tra, *trb;
1932
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;
1942 }
1943
1944 /**************************************************************************
1945  *
1946  *    Poly Regions
1947  * 
1948  *************************************************************************/
1949
1950 #define LARGE_COORDINATE  0x7fffffff /* FIXME */
1951 #define SMALL_COORDINATE  0x80000000
1952
1953 /***********************************************************************
1954  *     REGION_InsertEdgeInET
1955  *
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.
1960  *
1961  */
1962 static void REGION_InsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE,
1963                 INT scanline, ScanLineListBlock **SLLBlock, INT *iSLLBlock)
1964
1965 {
1966     EdgeTableEntry *start, *prev;
1967     ScanLineList *pSLL, *pPrevSLL;
1968     ScanLineListBlock *tmpSLLBlock;
1969
1970     /*
1971      * find the right bucket to put the edge into
1972      */
1973     pPrevSLL = &ET->scanlines;
1974     pSLL = pPrevSLL->next;
1975     while (pSLL && (pSLL->scanline < scanline)) 
1976     {
1977         pPrevSLL = pSLL;
1978         pSLL = pSLL->next;
1979     }
1980
1981     /*
1982      * reassign pSLL (pointer to ScanLineList) if necessary
1983      */
1984     if ((!pSLL) || (pSLL->scanline > scanline)) 
1985     {
1986         if (*iSLLBlock > SLLSPERBLOCK-1) 
1987         {
1988             tmpSLLBlock = HeapAlloc( SystemHeap, 0, sizeof(ScanLineListBlock));
1989             if(!tmpSLLBlock)
1990             {
1991                 WARN("Can't alloc SLLB\n");
1992                 return;
1993             }
1994             (*SLLBlock)->next = tmpSLLBlock;
1995             tmpSLLBlock->next = (ScanLineListBlock *)NULL;
1996             *SLLBlock = tmpSLLBlock;
1997             *iSLLBlock = 0;
1998         }
1999         pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
2000
2001         pSLL->next = pPrevSLL->next;
2002         pSLL->edgelist = (EdgeTableEntry *)NULL;
2003         pPrevSLL->next = pSLL;
2004     }
2005     pSLL->scanline = scanline;
2006
2007     /*
2008      * now insert the edge in the right bucket
2009      */
2010     prev = (EdgeTableEntry *)NULL;
2011     start = pSLL->edgelist;
2012     while (start && (start->bres.minor_axis < ETE->bres.minor_axis)) 
2013     {
2014         prev = start;
2015         start = start->next;
2016     }
2017     ETE->next = start;
2018
2019     if (prev)
2020         prev->next = ETE;
2021     else
2022         pSLL->edgelist = ETE;
2023 }
2024
2025 /***********************************************************************
2026  *     REGION_CreateEdgeTable
2027  *
2028  *     This routine creates the edge table for
2029  *     scan converting polygons. 
2030  *     The Edge Table (ET) looks like:
2031  *
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
2043  *
2044  *     where ETE is an EdgeTableEntry data structure,
2045  *     and there is one ScanLineList per scanline at
2046  *     which an edge is initially entered.
2047  *
2048  */
2049 static void REGION_CreateETandAET(const INT *Count, INT nbpolygons, 
2050             const POINT *pts, EdgeTable *ET, EdgeTableEntry *AET,
2051             EdgeTableEntry *pETEs, ScanLineListBlock *pSLLBlock)
2052 {
2053     const POINT *top, *bottom;
2054     const POINT *PrevPt, *CurrPt, *EndPt;
2055     INT poly, count;
2056     int iSLLBlock = 0;
2057     int dy;
2058
2059     
2060     /*
2061      *  initialize the Active Edge Table
2062      */
2063     AET->next = (EdgeTableEntry *)NULL;
2064     AET->back = (EdgeTableEntry *)NULL;
2065     AET->nextWETE = (EdgeTableEntry *)NULL;
2066     AET->bres.minor_axis = SMALL_COORDINATE;
2067
2068     /*
2069      *  initialize the Edge Table.
2070      */
2071     ET->scanlines.next = (ScanLineList *)NULL;
2072     ET->ymax = SMALL_COORDINATE;
2073     ET->ymin = LARGE_COORDINATE;
2074     pSLLBlock->next = (ScanLineListBlock *)NULL;
2075
2076     EndPt = pts - 1;
2077     for(poly = 0; poly < nbpolygons; poly++)
2078     {
2079         count = Count[poly];
2080         EndPt += count;
2081         if(count < 2)
2082             continue;
2083         
2084         PrevPt = EndPt;
2085
2086     /*
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.
2090      */
2091         while (count--) 
2092         {
2093             CurrPt = pts++;
2094
2095         /*
2096          *  find out which point is above and which is below.
2097          */
2098             if (PrevPt->y > CurrPt->y) 
2099             {
2100                 bottom = PrevPt, top = CurrPt;
2101                 pETEs->ClockWise = 0;
2102             }
2103             else 
2104             {
2105                 bottom = CurrPt, top = PrevPt;
2106                 pETEs->ClockWise = 1;
2107             }
2108
2109         /*
2110          * don't add horizontal edges to the Edge table.
2111          */
2112             if (bottom->y != top->y) 
2113             {
2114                 pETEs->ymax = bottom->y-1;
2115                                 /* -1 so we don't get last scanline */
2116
2117             /*
2118              *  initialize integer edge algorithm
2119              */
2120                 dy = bottom->y - top->y;
2121                 BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres);
2122
2123                 REGION_InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock, 
2124                                                                 &iSLLBlock);
2125
2126                 if (PrevPt->y > ET->ymax)
2127                   ET->ymax = PrevPt->y;
2128                 if (PrevPt->y < ET->ymin)
2129                   ET->ymin = PrevPt->y;
2130                 pETEs++;
2131             }
2132
2133             PrevPt = CurrPt;
2134         }
2135     }
2136 }
2137
2138 /***********************************************************************
2139  *     REGION_loadAET
2140  *
2141  *     This routine moves EdgeTableEntries from the
2142  *     EdgeTable into the Active Edge Table,
2143  *     leaving them sorted by smaller x coordinate.
2144  *
2145  */
2146 static void REGION_loadAET(EdgeTableEntry *AET, EdgeTableEntry *ETEs)
2147 {
2148     EdgeTableEntry *pPrevAET;
2149     EdgeTableEntry *tmp;
2150
2151     pPrevAET = AET;
2152     AET = AET->next;
2153     while (ETEs) 
2154     {
2155         while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis)) 
2156         {
2157             pPrevAET = AET;
2158             AET = AET->next;
2159         }
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;
2167
2168         ETEs = tmp;
2169     }
2170 }
2171
2172 /***********************************************************************
2173  *     REGION_computeWAET
2174  *
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:
2180  *
2181  *     AET
2182  *     ----------  ---------   ---------
2183  *     |ymax    |  |ymax    |  |ymax    | 
2184  *     | ...    |  |...     |  |...     |
2185  *     |next    |->|next    |->|next    |->...
2186  *     |nextWETE|  |nextWETE|  |nextWETE|
2187  *     ---------   ---------   ^--------
2188  *         |                   |       |
2189  *         V------------------->       V---> ...
2190  *
2191  */
2192 static void REGION_computeWAET(EdgeTableEntry *AET)
2193 {
2194     register EdgeTableEntry *pWETE;
2195     register int inside = 1;
2196     register int isInside = 0;
2197
2198     AET->nextWETE = (EdgeTableEntry *)NULL;
2199     pWETE = AET;
2200     AET = AET->next;
2201     while (AET) 
2202     {
2203         if (AET->ClockWise)
2204             isInside++;
2205         else
2206             isInside--;
2207
2208         if ((!inside && !isInside) ||
2209             ( inside &&  isInside)) 
2210         {
2211             pWETE->nextWETE = AET;
2212             pWETE = AET;
2213             inside = !inside;
2214         }
2215         AET = AET->next;
2216     }
2217     pWETE->nextWETE = (EdgeTableEntry *)NULL;
2218 }
2219
2220 /***********************************************************************
2221  *     REGION_InsertionSort
2222  *
2223  *     Just a simple insertion sort using
2224  *     pointers and back pointers to sort the Active
2225  *     Edge Table.
2226  *
2227  */
2228 static BOOL REGION_InsertionSort(EdgeTableEntry *AET)
2229 {
2230     EdgeTableEntry *pETEchase;
2231     EdgeTableEntry *pETEinsert;
2232     EdgeTableEntry *pETEchaseBackTMP;
2233     BOOL changed = FALSE;
2234
2235     AET = AET->next;
2236     while (AET) 
2237     {
2238         pETEinsert = AET;
2239         pETEchase = AET;
2240         while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
2241             pETEchase = pETEchase->back;
2242
2243         AET = AET->next;
2244         if (pETEchase != pETEinsert) 
2245         {
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;
2255         }
2256     }
2257     return changed;
2258 }
2259
2260 /***********************************************************************
2261  *     REGION_FreeStorage
2262  *
2263  *     Clean up our act.
2264  */
2265 static void REGION_FreeStorage(ScanLineListBlock *pSLLBlock)
2266 {
2267     ScanLineListBlock   *tmpSLLBlock;
2268
2269     while (pSLLBlock) 
2270     {
2271         tmpSLLBlock = pSLLBlock->next;
2272         HeapFree( SystemHeap, 0, pSLLBlock );
2273         pSLLBlock = tmpSLLBlock;
2274     }
2275 }
2276
2277
2278 /***********************************************************************
2279  *     REGION_PtsToRegion
2280  *
2281  *     Create an array of rectangles from a list of points.
2282  */
2283 static int REGION_PtsToRegion(int numFullPtBlocks, int iCurPtBlock, 
2284                        POINTBLOCK *FirstPtBlock, WINEREGION *reg)
2285 {
2286     RECT *rects;
2287     POINT *pts;
2288     POINTBLOCK *CurPtBlock;
2289     int i;
2290     RECT *extents;
2291     INT numRects;
2292  
2293     extents = &reg->extents;
2294  
2295     numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
2296  
2297     if (!(reg->rects = HeapReAlloc( SystemHeap, 0, reg->rects, 
2298                            sizeof(RECT) * numRects )))
2299         return(0);
2300  
2301     reg->size = numRects;
2302     CurPtBlock = FirstPtBlock;
2303     rects = reg->rects - 1;
2304     numRects = 0;
2305     extents->left = LARGE_COORDINATE,  extents->right = SMALL_COORDINATE;
2306  
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;
2321             }
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;
2330         }
2331         CurPtBlock = CurPtBlock->next;
2332     }
2333
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;
2342     }
2343     reg->numRects = numRects;
2344  
2345     return(TRUE);
2346 }
2347
2348 /***********************************************************************
2349  *           CreatePolyPolygonRgn32    (GDI32.57)
2350  */
2351 HRGN WINAPI CreatePolyPolygonRgn(const POINT *Pts, const INT *Count, 
2352                       INT nbpolygons, INT mode)
2353 {
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;
2373
2374     if(!(hrgn = REGION_CreateRegion(nbpolygons)))
2375         return 0;
2376     obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
2377     region = obj->rgn;
2378
2379     /* special case a rectangle */
2380
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))))
2391     {
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;
2396     }
2397     
2398     for(poly = total = 0; poly < nbpolygons; poly++)
2399         total += Count[poly];
2400     if (! (pETEs = HeapAlloc( SystemHeap, 0, sizeof(EdgeTableEntry) * total )))
2401     {
2402         REGION_DeleteObject( hrgn, obj );
2403         return 0;
2404     }
2405     pts = FirstPtBlock.pts;
2406     REGION_CreateETandAET(Count, nbpolygons, Pts, &ET, &AET, pETEs, &SLLBlock);
2407     pSLL = ET.scanlines.next;
2408     curPtBlock = &FirstPtBlock;
2409  
2410     if (mode != WINDING) {
2411         /*
2412          *  for each scanline
2413          */
2414         for (y = ET.ymin; y < ET.ymax; y++) {
2415             /*
2416              *  Add a new edge to the active edge table when we
2417              *  get to the next edge.
2418              */
2419             if (pSLL != NULL && y == pSLL->scanline) {
2420                 REGION_loadAET(&AET, pSLL->edgelist);
2421                 pSLL = pSLL->next;
2422             }
2423             pPrevAET = &AET;
2424             pAET = AET.next;
2425  
2426             /*
2427              *  for each active edge
2428              */
2429             while (pAET) {
2430                 pts->x = pAET->bres.minor_axis,  pts->y = y;
2431                 pts++, iPts++;
2432  
2433                 /*
2434                  *  send out the buffer
2435                  */
2436                 if (iPts == NUMPTSTOBUFFER) {
2437                     tmpPtBlock = HeapAlloc( SystemHeap, 0, sizeof(POINTBLOCK));
2438                     if(!tmpPtBlock) {
2439                         WARN("Can't alloc tPB\n");
2440                         return 0;
2441                     }
2442                     curPtBlock->next = tmpPtBlock;
2443                     curPtBlock = tmpPtBlock;
2444                     pts = curPtBlock->pts;
2445                     numFullPtBlocks++;
2446                     iPts = 0;
2447                 }
2448                 EVALUATEEDGEEVENODD(pAET, pPrevAET, y);
2449             }
2450             REGION_InsertionSort(&AET);
2451         }
2452     }
2453     else {
2454         /*
2455          *  for each scanline
2456          */
2457         for (y = ET.ymin; y < ET.ymax; y++) {
2458             /*
2459              *  Add a new edge to the active edge table when we
2460              *  get to the next edge.
2461              */
2462             if (pSLL != NULL && y == pSLL->scanline) {
2463                 REGION_loadAET(&AET, pSLL->edgelist);
2464                 REGION_computeWAET(&AET);
2465                 pSLL = pSLL->next;
2466             }
2467             pPrevAET = &AET;
2468             pAET = AET.next;
2469             pWETE = pAET;
2470  
2471             /*
2472              *  for each active edge
2473              */
2474             while (pAET) {
2475                 /*
2476                  *  add to the buffer only those edges that
2477                  *  are in the Winding active edge table.
2478                  */
2479                 if (pWETE == pAET) {
2480                     pts->x = pAET->bres.minor_axis,  pts->y = y;
2481                     pts++, iPts++;
2482  
2483                     /*
2484                      *  send out the buffer
2485                      */
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;
2492                         }
2493                         curPtBlock->next = tmpPtBlock;
2494                         curPtBlock = tmpPtBlock;
2495                         pts = curPtBlock->pts;
2496                         numFullPtBlocks++;    iPts = 0;
2497                     }
2498                     pWETE = pWETE->nextWETE;
2499                 }
2500                 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
2501             }
2502  
2503             /*
2504              *  recompute the winding active edge table if
2505              *  we just resorted or have exited an edge.
2506              */
2507             if (REGION_InsertionSort(&AET) || fixWAET) {
2508                 REGION_computeWAET(&AET);
2509                 fixWAET = FALSE;
2510             }
2511         }
2512     }
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;
2519     }
2520     HeapFree( SystemHeap, 0, pETEs );
2521     GDI_HEAP_UNLOCK( hrgn );
2522     return hrgn;
2523 }
2524
2525
2526 /***********************************************************************
2527  *           CreatePolygonRgn16    (GDI.63)
2528  */
2529 HRGN16 WINAPI CreatePolygonRgn16( const POINT16 * points, INT16 count,
2530                                   INT16 mode )
2531 {
2532     return CreatePolyPolygonRgn16( points, &count, 1, mode );
2533 }
2534
2535 /***********************************************************************
2536  *           CreatePolyPolygonRgn16    (GDI.451)
2537  */
2538 HRGN16 WINAPI CreatePolyPolygonRgn16( const POINT16 *points,
2539                 const INT16 *count, INT16 nbpolygons, INT16 mode )
2540 {
2541     HRGN hrgn;
2542     int i, npts = 0;
2543     INT *count32;
2544     POINT *points32;
2545
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]) );
2551
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;
2559 }
2560
2561 /***********************************************************************
2562  *           CreatePolygonRgn32    (GDI32.58)
2563  */
2564 HRGN WINAPI CreatePolygonRgn( const POINT *points, INT count,
2565                                   INT mode )
2566 {
2567     return CreatePolyPolygonRgn( points, &count, 1, mode );
2568 }
2569
2570
2571 /***********************************************************************
2572  * GetRandomRgn [GDI32.215]
2573  *
2574  * NOTES
2575  *     This function is UNDOCUMENTED, it isn't even in the header file.
2576  *     I assume that it will return a HRGN32!??
2577  */
2578 HRGN WINAPI GetRandomRgn(DWORD dwArg1, DWORD dwArg2, DWORD dwArg3)
2579 {
2580     FIXME("(0x%08lx 0x%08lx 0x%08lx): empty stub!\n",
2581            dwArg1, dwArg2, dwArg3);
2582
2583     return 0;
2584 }
2585
2586 /***********************************************************************
2587  *           REGION_CropAndOffsetRegion
2588  */
2589 static BOOL REGION_CropAndOffsetRegion(const POINT* off, const RECT *rect, WINEREGION *rgnSrc, WINEREGION* rgnDst)
2590 {
2591     if( !rect ) /* just copy and offset */
2592     {
2593         if( rgnDst == rgnSrc )
2594         {
2595             if( off->x || off->y )
2596                 rect = rgnDst->rects;
2597             else 
2598                 return TRUE;
2599         }
2600         else
2601             rect = HeapReAlloc( SystemHeap, 0, rgnDst->rects,
2602                                 rgnSrc->size * sizeof( RECT ));
2603         if( rect )
2604         {
2605             INT i;
2606
2607             if( rgnDst != rgnSrc )
2608                 memcpy( rgnDst, rgnSrc, sizeof( WINEREGION ));
2609
2610             if( off->x || off->y )
2611             {
2612                 for( i = 0; i < rgnDst->numRects; i++ )
2613                 {
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;
2618                 }
2619                 OffsetRect( &rgnDst->extents, off->x, off->y );
2620             }
2621             else
2622                 memcpy( rect, rgnSrc->rects, rgnDst->numRects * sizeof( RECT ));
2623             rgnDst->rects = rect;
2624         }
2625         else
2626             return FALSE;
2627     }
2628     else if( IsRectEmpty(rect) || !EXTENTCHECK(rect, &rgnSrc->extents) )
2629     {
2630 empty:
2631         if( !rgnDst->rects )
2632         {
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;
2638         }
2639
2640         TRACE("cropped to empty!\n");
2641         EMPTY_REGION(rgnDst);
2642     }
2643     else /* region box and clipping rect appear to intersect */
2644     {
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;
2649
2650         for( clipa = 0; rgnSrc->rects[clipa].bottom <= rect->top; clipa++ )
2651              ; /* skip bands above the clipping rectangle */
2652
2653         for( clipb = clipa; clipb < rgnSrc->numRects; clipb++ )
2654              if( rgnSrc->rects[clipb].top >= rect->bottom )
2655                  break;    /* and below it */
2656
2657         /* clipa - index of the first rect in the first intersecting band
2658          * clipb - index of the last rect in the last intersecting band
2659          */
2660
2661         if((rgnDst != rgnSrc) && (rgnDst->size < (i = (clipb - clipa))))
2662         {
2663             rgnDst->rects = HeapReAlloc( SystemHeap, 0, 
2664                                 rgnDst->rects, i * sizeof(RECT));
2665             if( !rgnDst->rects ) return FALSE;
2666             rgnDst->size = i;
2667         }
2668
2669         if( TRACE_ON(region) )
2670         {
2671             REGION_DumpRegion( rgnSrc );
2672             TRACE("\tclipa = %i, clipb = %i\n", clipa, clipb );
2673         }
2674
2675         for( i = clipa, j = 0; i < clipb ; i++ )
2676         {
2677              /* i - src index, j - dst index, j is always <= i for obvious reasons */
2678
2679              lpr = rgnSrc->rects + i;
2680              if( lpr->left < rect->right && lpr->right > rect->left )
2681              {
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;
2686
2687                  if( rgnDst->rects[j].left < left ) left = rgnDst->rects[j].left;
2688                  if( rgnDst->rects[j].right > right ) right = rgnDst->rects[j].right;
2689
2690                  j++;
2691              }
2692         }
2693
2694         if( j == 0 ) goto empty;
2695
2696         rgnDst->extents.left = left;
2697         rgnDst->extents.right = right;
2698
2699         left = rect->top + off->y;
2700         right = rect->bottom + off->y;
2701
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;
2708
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;
2714
2715         rgnDst->extents.top = rgnDst->rects[0].top;
2716         rgnDst->extents.bottom = rgnDst->rects[j].bottom;
2717
2718         rgnDst->type = (j >= 1) ? COMPLEXREGION : SIMPLEREGION;
2719
2720         if( TRACE_ON(region) )
2721         {
2722             TRACE("result:\n");
2723             REGION_DumpRegion( rgnDst );
2724         }
2725     }
2726
2727     return TRUE;
2728 }
2729
2730 /***********************************************************************
2731  *           REGION_CropRgn
2732  *
2733  *
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).
2737  *
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.
2741  *
2742  * Returns: hDst if success, 0 otherwise.
2743  */
2744 HRGN REGION_CropRgn( HRGN hDst, HRGN hSrc, const RECT *lpRect, const POINT *lpPt )
2745 {
2746 /*  Optimization of the following generic code:
2747
2748     HRGN h; 
2749
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;
2764
2765 */
2766
2767     RGNOBJ *objSrc = (RGNOBJ *) GDI_HEAP_LOCK( hSrc );
2768
2769     if(objSrc)
2770     {
2771         RGNOBJ *objDst;
2772         WINEREGION *rgnDst;
2773
2774         if( hDst )
2775         {
2776             objDst = (RGNOBJ *) GDI_HEAP_LOCK( hDst );
2777             rgnDst = objDst->rgn;
2778         }
2779         else
2780         {
2781             rgnDst = HeapAlloc(SystemHeap, 0, sizeof( WINEREGION ));
2782             if( rgnDst ) 
2783             {
2784                 rgnDst->size = rgnDst->numRects = 0;
2785                 rgnDst->rects = NULL;   /* back end will allocate exact number */
2786             }
2787         }
2788
2789         if( rgnDst )
2790         {
2791             POINT pt = { 0, 0 };
2792
2793             if( !lpPt ) lpPt = &pt;
2794
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 ); 
2800
2801             if( REGION_CropAndOffsetRegion( lpPt, lpRect, objSrc->rgn, rgnDst ) == FALSE )
2802             {
2803                 if( hDst ) /* existing rgn */
2804                 {
2805                     GDI_HEAP_UNLOCK(hDst);
2806                     hDst = 0;
2807                     goto done;
2808                 }
2809                 goto fail;
2810             }
2811             else if( hDst == 0 )
2812             {
2813                 if(!(hDst = GDI_AllocObject( sizeof(RGNOBJ), REGION_MAGIC )))
2814                 {
2815 fail:
2816                     if( rgnDst->rects )
2817                         HeapFree( SystemHeap, 0, rgnDst->rects );
2818                     HeapFree( SystemHeap, 0, rgnDst );
2819                     goto done;
2820                 }
2821
2822                 objDst = (RGNOBJ *) GDI_HEAP_LOCK( hDst );
2823                 objDst->rgn = rgnDst;
2824             }
2825
2826             GDI_HEAP_UNLOCK(hDst);
2827         }
2828         else hDst = 0;
2829 done:
2830         GDI_HEAP_UNLOCK(hSrc);
2831         return hDst;
2832     }
2833     return 0;
2834 }
2835