Simulate the VGA vertical refresh.
[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  *
8  */
9
10 /************************************************************************
11
12 Copyright (c) 1987, 1988  X Consortium
13
14 Permission is hereby granted, free of charge, to any person obtaining a copy
15 of this software and associated documentation files (the "Software"), to deal
16 in the Software without restriction, including without limitation the rights
17 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18 copies of the Software, and to permit persons to whom the Software is
19 furnished to do so, subject to the following conditions:
20
21 The above copyright notice and this permission notice shall be included in
22 all copies or substantial portions of the Software.
23
24 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
27 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
28 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
31 Except as contained in this notice, the name of the X Consortium shall not be
32 used in advertising or otherwise to promote the sale, use or other dealings
33 in this Software without prior written authorization from the X Consortium.
34
35
36 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
37
38                         All Rights Reserved
39
40 Permission to use, copy, modify, and distribute this software and its 
41 documentation for any purpose and without fee is hereby granted, 
42 provided that the above copyright notice appear in all copies and that
43 both that copyright notice and this permission notice appear in 
44 supporting documentation, and that the name of Digital not be
45 used in advertising or publicity pertaining to distribution of the
46 software without specific, written prior permission.  
47
48 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
49 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
50 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
51 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
52 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
53 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
54 SOFTWARE.
55
56 ************************************************************************/
57 /*
58  * The functions in this file implement the Region abstraction, similar to one
59  * used in the X11 sample server. A Region is simply an area, as the name
60  * implies, and is implemented as a "y-x-banded" array of rectangles. To
61  * explain: Each Region is made up of a certain number of rectangles sorted
62  * by y coordinate first, and then by x coordinate.
63  *
64  * Furthermore, the rectangles are banded such that every rectangle with a
65  * given upper-left y coordinate (y1) will have the same lower-right y
66  * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
67  * will span the entire vertical distance of the band. This means that some
68  * areas that could be merged into a taller rectangle will be represented as
69  * several shorter rectangles to account for shorter rectangles to its left
70  * or right but within its "vertical scope".
71  *
72  * An added constraint on the rectangles is that they must cover as much
73  * horizontal area as possible. E.g. no two rectangles in a band are allowed
74  * to touch.
75  *
76  * Whenever possible, bands will be merged together to cover a greater vertical
77  * distance (and thus reduce the number of rectangles). Two bands can be merged
78  * only if the bottom of one touches the top of the other and they have
79  * rectangles in the same places (of the same width, of course). This maintains
80  * the y-x-banding that's so nice to have...
81  */
82
83 #include "region.h"
84 #include "debug.h"
85 #include "heap.h"
86 #include "dc.h"
87
88 typedef void (*voidProcp)();
89
90 /* Note the parameter order is different from the X11 equivalents */
91
92 static void REGION_CopyRegion(WINEREGION *d, WINEREGION *s);
93 static void REGION_IntersectRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
94 static void REGION_UnionRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
95 static void REGION_SubtractRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
96 static void REGION_XorRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
97 static void REGION_UnionRectWithRegion(const RECT32 *rect, WINEREGION *rgn);
98
99
100 /***********************************************************************
101  *            REGION_DumpRegion
102  *            Outputs the contents of a WINEREGION
103  */
104 static void REGION_DumpRegion(WINEREGION *pReg)
105 {
106     RECT32 *pRect, *pRectEnd = pReg->rects + pReg->numRects;
107
108     TRACE(region, "Region %p: %d,%d - %d,%d %d rects\n", pReg,
109             pReg->extents.left, pReg->extents.top,
110             pReg->extents.right, pReg->extents.bottom, pReg->numRects);
111     for(pRect = pReg->rects; pRect < pRectEnd; pRect++)
112         TRACE(region, "\t%d,%d - %d,%d\n", pRect->left, pRect->top,
113                        pRect->right, pRect->bottom);
114     return;
115 }
116
117 /***********************************************************************
118  *            REGION_AllocWineRegion
119  *            Create a new empty WINEREGION.
120  */
121 static WINEREGION *REGION_AllocWineRegion( void )    
122 {
123     WINEREGION *pReg;
124
125     if ((pReg = HeapAlloc(SystemHeap, 0, sizeof( WINEREGION ))))
126     {
127         if ((pReg->rects = HeapAlloc(SystemHeap, 0, sizeof( RECT32 ))))
128         {
129             pReg->size = 1;
130             EMPTY_REGION(pReg);
131             return pReg;
132         }
133         HeapFree(SystemHeap, 0, pReg);
134     }
135     return NULL;
136 }
137
138 /***********************************************************************
139  *          REGION_CreateRegion
140  *          Create a new empty region.
141  */
142 static HRGN32 REGION_CreateRegion(void)
143 {
144     HRGN32 hrgn;
145     RGNOBJ *obj;
146     
147     if(!(hrgn = GDI_AllocObject( sizeof(RGNOBJ), REGION_MAGIC )))
148         return 0;
149     obj = (RGNOBJ *) GDI_HEAP_LOCK( hrgn );
150     if(!(obj->rgn = REGION_AllocWineRegion())) {
151         GDI_FreeObject( hrgn );
152         return 0;
153     }
154     GDI_HEAP_UNLOCK( hrgn );
155     return hrgn;
156 }
157
158 /***********************************************************************
159  *           REGION_DestroyWineRegion
160  */
161 static void REGION_DestroyWineRegion( WINEREGION* pReg )
162 {
163     HeapFree( SystemHeap, 0, pReg->rects );
164     HeapFree( SystemHeap, 0, pReg );
165     return;
166 }
167
168 /***********************************************************************
169  *           REGION_DeleteObject
170  */
171 BOOL32 REGION_DeleteObject( HRGN32 hrgn, RGNOBJ * obj )
172 {
173     TRACE(region, " %04x\n", hrgn );
174
175     REGION_DestroyWineRegion( obj->rgn );
176     return GDI_FreeObject( hrgn );
177 }
178
179 /***********************************************************************
180  *           OffsetRgn16    (GDI.101)
181  */
182 INT16 WINAPI OffsetRgn16( HRGN16 hrgn, INT16 x, INT16 y )
183 {
184     return OffsetRgn32( hrgn, x, y );
185 }
186
187 /***********************************************************************
188  *           OffsetRgn32   (GDI32.256)
189  */
190 INT32 WINAPI OffsetRgn32( HRGN32 hrgn, INT32 x, INT32 y )
191 {
192     RGNOBJ * obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
193
194     if (obj)
195     {
196         INT32 ret;
197         int nbox = obj->rgn->numRects;
198         RECT32 *pbox = obj->rgn->rects;
199         
200         TRACE(region, " %04x %d,%d\n", hrgn, x, y );
201         if(nbox && (x || y)) {
202             while(nbox--) {
203                 pbox->left += x;
204                 pbox->right += x;
205                 pbox->top += y;
206                 pbox->bottom += y;
207                 pbox++;
208             }
209             obj->rgn->extents.left += x;
210             obj->rgn->extents.right += x;
211             obj->rgn->extents.top += y;
212             obj->rgn->extents.bottom += y;
213         }
214         ret = obj->rgn->type;
215         GDI_HEAP_UNLOCK( hrgn );
216         return ret;
217     }
218     return ERROR;
219 }
220
221
222 /***********************************************************************
223  *           GetRgnBox16    (GDI.134)
224  */
225 INT16 WINAPI GetRgnBox16( HRGN16 hrgn, LPRECT16 rect )
226 {
227     RECT32 r;
228     INT16 ret = (INT16)GetRgnBox32( hrgn, &r );
229     CONV_RECT32TO16( &r, rect );
230     return ret;
231 }
232
233 /***********************************************************************
234  *           GetRgnBox32    (GDI32.219)
235  */
236 INT32 WINAPI GetRgnBox32( HRGN32 hrgn, LPRECT32 rect )
237 {
238     RGNOBJ * obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
239     if (obj)
240     {
241         INT32 ret;
242         TRACE(region, " %04x\n", hrgn );
243         rect->left = obj->rgn->extents.left;
244         rect->top = obj->rgn->extents.top;
245         rect->right = obj->rgn->extents.right;
246         rect->bottom = obj->rgn->extents.bottom;
247         ret = obj->rgn->type;
248         GDI_HEAP_UNLOCK(hrgn);
249         return ret;
250     }
251     return ERROR;
252 }
253
254
255 /***********************************************************************
256  *           CreateRectRgn16    (GDI.64)
257  *
258  * NOTE: Doesn't call CreateRectRgn32 because of differences in SetRectRgn16/32
259  */
260 HRGN16 WINAPI CreateRectRgn16(INT16 left, INT16 top, INT16 right, INT16 bottom)
261 {
262     HRGN16 hrgn;
263
264     if (!(hrgn = (HRGN16)REGION_CreateRegion()))
265         return 0;
266     TRACE(region, "\n");
267     SetRectRgn16(hrgn, left, top, right, bottom);
268     return hrgn;
269 }
270
271
272 /***********************************************************************
273  *           CreateRectRgn32   (GDI32.59)
274  */
275 HRGN32 WINAPI CreateRectRgn32(INT32 left, INT32 top, INT32 right, INT32 bottom)
276 {
277     HRGN32 hrgn;
278
279     if (!(hrgn = REGION_CreateRegion()))
280         return 0;
281     TRACE(region, "\n");
282     SetRectRgn32(hrgn, left, top, right, bottom);
283     return hrgn;
284 }
285
286 /***********************************************************************
287  *           CreateRectRgnIndirect16    (GDI.65)
288  */
289 HRGN16 WINAPI CreateRectRgnIndirect16( const RECT16* rect )
290 {
291     return CreateRectRgn16( rect->left, rect->top, rect->right, rect->bottom );
292 }
293
294
295 /***********************************************************************
296  *           CreateRectRgnIndirect32    (GDI32.60)
297  */
298 HRGN32 WINAPI CreateRectRgnIndirect32( const RECT32* rect )
299 {
300     return CreateRectRgn32( rect->left, rect->top, rect->right, rect->bottom );
301 }
302
303
304 /***********************************************************************
305  *           SetRectRgn16    (GDI.172)
306  *
307  * NOTE: Win 3.1 sets region to empty if left > right
308  */
309 VOID WINAPI SetRectRgn16( HRGN16 hrgn, INT16 left, INT16 top,
310                           INT16 right, INT16 bottom )
311 {
312     if(left < right)
313         SetRectRgn32( hrgn, left, top, right, bottom );
314     else
315         SetRectRgn32( hrgn, 0, 0, 0, 0 );
316 }
317
318
319 /***********************************************************************
320  *           SetRectRgn32    (GDI32.332)
321  *
322  * Allows either or both left and top to be greater than right or bottom.
323  */
324 VOID WINAPI SetRectRgn32( HRGN32 hrgn, INT32 left, INT32 top,
325                           INT32 right, INT32 bottom )
326 {
327     RGNOBJ * obj;
328
329     TRACE(region, " %04x %d,%d-%d,%d\n", 
330                    hrgn, left, top, right, bottom );
331     
332     if (!(obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC ))) return;
333
334     if (left > right) { INT32 tmp = left; left = right; right = tmp; }
335     if (top > bottom) { INT32 tmp = top; top = bottom; bottom = tmp; }
336
337     if((left != right) && (top != bottom))
338     {
339         obj->rgn->rects->left = obj->rgn->extents.left = left;
340         obj->rgn->rects->top = obj->rgn->extents.top = top;
341         obj->rgn->rects->right = obj->rgn->extents.right = right;
342         obj->rgn->rects->bottom = obj->rgn->extents.bottom = bottom;
343         obj->rgn->numRects = 1;
344         obj->rgn->type = SIMPLEREGION;
345     }
346     else
347         EMPTY_REGION(obj->rgn);
348
349     GDI_HEAP_UNLOCK( hrgn );
350 }
351
352
353 /***********************************************************************
354  *           CreateRoundRectRgn16    (GDI.444)
355  *
356  * If either ellipse dimension is zero we call CreateRectRgn16 for its
357  * `special' behaviour. -ve ellipse dimensions can result in GPFs under win3.1
358  * we just let CreateRoundRectRgn32 convert them to +ve values. 
359  */
360
361 HRGN16 WINAPI CreateRoundRectRgn16( INT16 left, INT16 top,
362                                     INT16 right, INT16 bottom,
363                                     INT16 ellipse_width, INT16 ellipse_height )
364 {
365     if( ellipse_width == 0 || ellipse_height == 0 )
366         return CreateRectRgn16( left, top, right, bottom );
367     else
368         return (HRGN16)CreateRoundRectRgn32( left, top, right, bottom,
369                                          ellipse_width, ellipse_height );
370 }
371
372 /***********************************************************************
373  *           CreateRoundRectRgn32    (GDI32.61)
374  */
375 HRGN32 WINAPI CreateRoundRectRgn32( INT32 left, INT32 top,
376                                     INT32 right, INT32 bottom,
377                                     INT32 ellipse_width, INT32 ellipse_height )
378 {
379     RGNOBJ * obj;
380     HRGN32 hrgn;
381     int asq, bsq, d, xd, yd;
382     RECT32 rect;
383     
384       /* Check if we can do a normal rectangle instead */
385
386     if ((ellipse_width == 0) || (ellipse_height == 0))
387         return CreateRectRgn32( left, top, right, bottom );
388
389       /* Make the dimensions sensible */
390
391     if (left > right) { INT32 tmp = left; left = right; right = tmp; }
392     if (top > bottom) { INT32 tmp = top; top = bottom; bottom = tmp; }
393
394     ellipse_width = abs(ellipse_width);
395     ellipse_height = abs(ellipse_height);
396
397       /* Create region */
398
399     if (!(hrgn = REGION_CreateRegion())) return 0;
400     obj = (RGNOBJ *) GDI_HEAP_LOCK( hrgn );
401     TRACE(region,"(%d,%d-%d,%d %dx%d): ret=%04x\n",
402                left, top, right, bottom, ellipse_width, ellipse_height, hrgn );
403
404       /* Check parameters */
405
406     if (ellipse_width > right-left) ellipse_width = right-left;
407     if (ellipse_height > bottom-top) ellipse_height = bottom-top;
408
409       /* Ellipse algorithm, based on an article by K. Porter */
410       /* in DDJ Graphics Programming Column, 8/89 */
411
412     asq = ellipse_width * ellipse_width / 4;        /* a^2 */
413     bsq = ellipse_height * ellipse_height / 4;      /* b^2 */
414     d = bsq - asq * ellipse_height / 2 + asq / 4;   /* b^2 - a^2b + a^2/4 */
415     xd = 0;
416     yd = asq * ellipse_height;                      /* 2a^2b */
417
418     rect.left   = left + ellipse_width / 2;
419     rect.right  = right - ellipse_width / 2;
420
421       /* Loop to draw first half of quadrant */
422
423     while (xd < yd)
424     {
425         if (d > 0)  /* if nearest pixel is toward the center */
426         {
427               /* move toward center */
428             rect.top = top++;
429             rect.bottom = rect.top + 1;
430             REGION_UnionRectWithRegion( &rect, obj->rgn );
431             rect.top = --bottom;
432             rect.bottom = rect.top + 1;
433             REGION_UnionRectWithRegion( &rect, obj->rgn );
434             yd -= 2*asq;
435             d  -= yd;
436         }
437         rect.left--;        /* next horiz point */
438         rect.right++;
439         xd += 2*bsq;
440         d  += bsq + xd;
441     }
442
443       /* Loop to draw second half of quadrant */
444
445     d += (3 * (asq-bsq) / 2 - (xd+yd)) / 2;
446     while (yd >= 0)
447     {
448           /* next vertical point */
449         rect.top = top++;
450         rect.bottom = rect.top + 1;
451         REGION_UnionRectWithRegion( &rect, obj->rgn );
452         rect.top = --bottom;
453         rect.bottom = rect.top + 1;
454         REGION_UnionRectWithRegion( &rect, obj->rgn );
455         if (d < 0)   /* if nearest pixel is outside ellipse */
456         {
457             rect.left--;     /* move away from center */
458             rect.right++;
459             xd += 2*bsq;
460             d  += xd;
461         }
462         yd -= 2*asq;
463         d  += asq - yd;
464     }
465
466       /* Add the inside rectangle */
467
468     if (top <= bottom)
469     {
470         rect.top = top;
471         rect.bottom = bottom;
472         REGION_UnionRectWithRegion( &rect, obj->rgn );
473     }
474     obj->rgn->type = SIMPLEREGION; /* FIXME? */
475     GDI_HEAP_UNLOCK( hrgn );
476     return hrgn;
477 }
478
479
480 /***********************************************************************
481  *           CreateEllipticRgn16    (GDI.54)
482  */
483 HRGN16 WINAPI CreateEllipticRgn16( INT16 left, INT16 top,
484                                    INT16 right, INT16 bottom )
485 {
486     return (HRGN16)CreateRoundRectRgn32( left, top, right, bottom,
487                                          right-left, bottom-top );
488 }
489
490
491 /***********************************************************************
492  *           CreateEllipticRgn32    (GDI32.39)
493  */
494 HRGN32 WINAPI CreateEllipticRgn32( INT32 left, INT32 top,
495                                    INT32 right, INT32 bottom )
496 {
497     return CreateRoundRectRgn32( left, top, right, bottom,
498                                  right-left, bottom-top );
499 }
500
501
502 /***********************************************************************
503  *           CreateEllipticRgnIndirect16    (GDI.55)
504  */
505 HRGN16 WINAPI CreateEllipticRgnIndirect16( const RECT16 *rect )
506 {
507     return CreateRoundRectRgn32( rect->left, rect->top, rect->right,
508                                  rect->bottom, rect->right - rect->left,
509                                  rect->bottom - rect->top );
510 }
511
512
513 /***********************************************************************
514  *           CreateEllipticRgnIndirect32    (GDI32.40)
515  */
516 HRGN32 WINAPI CreateEllipticRgnIndirect32( const RECT32 *rect )
517 {
518     return CreateRoundRectRgn32( rect->left, rect->top, rect->right,
519                                  rect->bottom, rect->right - rect->left,
520                                  rect->bottom - rect->top );
521 }
522
523 /***********************************************************************
524  *           GetRegionData   (GDI32.217)
525  * 
526  */
527 DWORD WINAPI GetRegionData(HRGN32 hrgn, DWORD count, LPRGNDATA rgndata)
528 {
529     DWORD size;
530     RGNOBJ *obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
531     
532     TRACE(region, " %04x count = %ld, rgndata = %p\n",
533                    hrgn, count, rgndata);
534
535     if(!obj) return 0;
536
537     size = obj->rgn->numRects * sizeof(RECT32);
538     if(count < (size + sizeof(RGNDATAHEADER)) || rgndata == NULL)
539     {
540         GDI_HEAP_UNLOCK( hrgn );
541         return size + sizeof(RGNDATAHEADER);
542     }
543
544     rgndata->rdh.dwSize = sizeof(RGNDATAHEADER);
545     rgndata->rdh.iType = RDH_RECTANGLES;
546     rgndata->rdh.nCount = obj->rgn->numRects;
547     rgndata->rdh.nRgnSize = size;
548     rgndata->rdh.rcBound.left = obj->rgn->extents.left;
549     rgndata->rdh.rcBound.top = obj->rgn->extents.top;
550     rgndata->rdh.rcBound.right = obj->rgn->extents.right;
551     rgndata->rdh.rcBound.bottom = obj->rgn->extents.bottom;
552
553     memcpy( rgndata->Buffer, obj->rgn->rects, size );
554
555     GDI_HEAP_UNLOCK( hrgn );
556     return 1;
557 }
558
559 /***********************************************************************
560  *           ExtCreateRegion   (GDI32.94)
561  * 
562  */
563 HRGN32 WINAPI ExtCreateRegion( const XFORM* lpXform, DWORD dwCount, const RGNDATA* rgndata)
564 {
565     HRGN32 hrgn = CreateRectRgn32(0, 0, 0, 0);
566     RGNOBJ *obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
567     RECT32 *pCurRect, *pEndRect;
568
569     TRACE(region, " %p %ld %p. Returning %04x\n",
570                    lpXform, dwCount, rgndata, hrgn);
571     if(!hrgn)
572     {
573         WARN(region, "Can't create a region!\n");
574         return 0;
575     }
576     if(lpXform)
577         WARN(region, "Xform not implemented - ignoring\n");
578     
579     if(rgndata->rdh.iType != RDH_RECTANGLES)
580     {
581         WARN(region, "Type not RDH_RECTANGLES\n");
582         GDI_HEAP_UNLOCK( hrgn );
583         DeleteObject32( hrgn );
584         return 0;
585     }
586
587     pEndRect = (RECT32 *)rgndata->Buffer + rgndata->rdh.nCount;
588     for(pCurRect = (RECT32 *)rgndata->Buffer; pCurRect < pEndRect; pCurRect++)
589         REGION_UnionRectWithRegion( pCurRect, obj->rgn );
590
591     GDI_HEAP_UNLOCK( hrgn );
592     return hrgn;
593 }
594
595 /***********************************************************************
596  *           PtInRegion16    (GDI.161)
597  */
598 BOOL16 WINAPI PtInRegion16( HRGN16 hrgn, INT16 x, INT16 y )
599 {
600     return PtInRegion32( hrgn, x, y );
601 }
602
603
604 /***********************************************************************
605  *           PtInRegion32    (GDI32.278)
606  */
607 BOOL32 WINAPI PtInRegion32( HRGN32 hrgn, INT32 x, INT32 y )
608 {
609     RGNOBJ * obj;
610     
611     if ((obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC )))
612     {
613         BOOL32 ret = FALSE;
614         int i;
615
616         if (obj->rgn->numRects > 0 && INRECT(obj->rgn->extents, x, y))
617             for (i = 0; i < obj->rgn->numRects; i++)
618                 if (INRECT (obj->rgn->rects[i], x, y))
619                     ret = TRUE;
620         GDI_HEAP_UNLOCK( hrgn );
621         return ret;
622     }
623     return FALSE;
624 }
625
626
627 /***********************************************************************
628  *           RectInRegion16    (GDI.181)
629  */
630 BOOL16 WINAPI RectInRegion16( HRGN16 hrgn, const RECT16 *rect )
631 {
632     RECT32 r32;
633
634     CONV_RECT16TO32(rect, &r32);
635     return (BOOL16)RectInRegion32(hrgn, &r32);
636 }
637
638
639 /***********************************************************************
640  *           RectInRegion32    (GDI32.281)
641  *
642  * Returns TRUE if rect is at least partly inside hrgn
643  */
644 BOOL32 WINAPI RectInRegion32( HRGN32 hrgn, const RECT32 *rect )
645 {
646     RGNOBJ * obj;
647     
648     if ((obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC )))
649     {
650         RECT32 *pCurRect, *pRectEnd;
651         BOOL32 ret = FALSE;
652     
653     /* this is (just) a useful optimization */
654         if ((obj->rgn->numRects > 0) && EXTENTCHECK(&obj->rgn->extents,
655                                                       rect))
656         {
657             for (pCurRect = obj->rgn->rects, pRectEnd = pCurRect +
658              obj->rgn->numRects; pCurRect < pRectEnd; pCurRect++)
659             {
660                 if (pCurRect->bottom <= rect->top)
661                     continue;             /* not far enough down yet */
662
663                 if (pCurRect->top >= rect->bottom) {
664                     ret = FALSE;          /* too far down */
665                     break;
666                 }
667
668                 if (pCurRect->right <= rect->left)
669                     continue;              /* not far enough over yet */
670
671                 if (pCurRect->left >= rect->right) {
672                     continue;
673                 }
674
675                 ret = TRUE;
676                 break;
677             }
678         }
679         GDI_HEAP_UNLOCK(hrgn);
680         return ret;
681     }
682     return FALSE;
683 }
684
685 /***********************************************************************
686  *           EqualRgn16    (GDI.72)
687  */
688 BOOL16 WINAPI EqualRgn16( HRGN16 rgn1, HRGN16 rgn2 )
689 {
690     return EqualRgn32( rgn1, rgn2 );
691 }
692
693
694 /***********************************************************************
695  *           EqualRgn32    (GDI32.90)
696  */
697 BOOL32 WINAPI EqualRgn32( HRGN32 hrgn1, HRGN32 hrgn2 )
698 {
699     RGNOBJ *obj1, *obj2;
700     BOOL32 ret = FALSE;
701
702     if ((obj1 = (RGNOBJ *) GDI_GetObjPtr( hrgn1, REGION_MAGIC ))) 
703     {
704         if ((obj2 = (RGNOBJ *) GDI_GetObjPtr( hrgn2, REGION_MAGIC ))) 
705         {
706             int i;
707
708             ret = TRUE;
709             if ( obj1->rgn->numRects != obj2->rgn->numRects ) ret = FALSE;
710             else if ( obj1->rgn->numRects == 0 ) ret = TRUE;
711             else if ( !EqualRect32(&obj1->rgn->extents, &obj2->rgn->extents) )
712                 ret = FALSE;
713             else for( i = 0; i < obj1->rgn->numRects; i++ ) {
714                 if (!EqualRect32(obj1->rgn->rects + i, obj2->rgn->rects + i)) {
715                     ret = FALSE;
716                     break;
717                 }
718             }
719             GDI_HEAP_UNLOCK(hrgn2);
720         }
721         GDI_HEAP_UNLOCK(hrgn1);
722     }
723     return ret;
724 }
725 /***********************************************************************
726  *           REGION_UnionRectWithRegion
727  *           Adds a rectangle to a WINEREGION
728  *           See below for REGION_UnionRectWithRgn
729  */
730 static void REGION_UnionRectWithRegion(const RECT32 *rect, WINEREGION *rgn)
731 {
732     WINEREGION region;
733
734     region.rects = &region.extents;
735     region.numRects = 1;
736     region.size = 1;
737     region.type = SIMPLEREGION;
738     CopyRect32(&(region.extents), rect);
739     REGION_UnionRegion(rgn, rgn, &region);
740     return;
741 }
742
743 /***********************************************************************
744  *           REGION_UnionRectWithRgn
745  *           Adds a rectangle to a HRGN32
746  *           A helper used by scroll.c
747  */
748 BOOL32 REGION_UnionRectWithRgn( HRGN32 hrgn, const RECT32 *lpRect )
749 {
750     RGNOBJ *obj = (RGNOBJ *) GDI_HEAP_LOCK( hrgn );
751
752     if(!obj) return FALSE;
753     REGION_UnionRectWithRegion( lpRect, obj->rgn );
754     GDI_HEAP_UNLOCK(hrgn);
755     return TRUE;
756 }
757
758 /***********************************************************************
759  *           REGION_CreateFrameRgn
760  *
761  * Create a region that is a frame around another region.
762  * Expand all rectangles by +/- x and y, then subtract original region.
763  */
764 BOOL32 REGION_FrameRgn( HRGN32 hDest, HRGN32 hSrc, INT32 x, INT32 y )
765 {
766     BOOL32 bRet;
767     RGNOBJ *srcObj = (RGNOBJ*) GDI_GetObjPtr( hSrc, REGION_MAGIC );
768
769     if (srcObj->rgn->numRects != 0) 
770     {
771         RGNOBJ* destObj = (RGNOBJ*) GDI_GetObjPtr( hDest, REGION_MAGIC );
772         RECT32 *pRect, *pEndRect;
773         RECT32 tempRect;
774
775         EMPTY_REGION( destObj->rgn );
776         
777         pEndRect = srcObj->rgn->rects + srcObj->rgn->numRects;
778         for(pRect = srcObj->rgn->rects; pRect < pEndRect; pRect++)
779         {
780             tempRect.left = pRect->left - x;        
781             tempRect.top = pRect->top - y;
782             tempRect.right = pRect->right + x;
783             tempRect.bottom = pRect->bottom + y;
784             REGION_UnionRectWithRegion( &tempRect, destObj->rgn );
785         }
786         REGION_SubtractRegion( destObj->rgn, destObj->rgn, srcObj->rgn );
787         GDI_HEAP_UNLOCK ( hDest );
788         bRet = TRUE;
789     }
790     else
791         bRet = FALSE;
792     GDI_HEAP_UNLOCK( hSrc );
793     return bRet;
794 }
795
796 /***********************************************************************
797  *           REGION_LPTODP
798  *
799  * Convert region to device co-ords for the supplied dc. 
800  * Used by X11DRV_PaintRgn.
801  */
802 BOOL32 REGION_LPTODP( HDC32 hdc, HRGN32 hDest, HRGN32 hSrc )
803 {
804     RECT32 *pCurRect, *pEndRect;
805     RGNOBJ *srcObj, *destObj;
806     DC * dc = DC_GetDCPtr( hdc );
807     RECT32 tmpRect;
808
809     TRACE(region, " hdc=%04x dest=%04x src=%04x\n",
810                     hdc, hDest, hSrc) ;
811     
812     if (dc->w.MapMode == MM_TEXT) /* Requires only a translation */
813     {
814         if( CombineRgn32( hDest, hSrc, 0, RGN_COPY ) == ERROR ) return FALSE;
815         OffsetRgn32( hDest, dc->vportOrgX - dc->wndOrgX, 
816                      dc->vportOrgY - dc->wndOrgY );
817         return TRUE;
818     }
819
820     if(!( srcObj = (RGNOBJ *) GDI_GetObjPtr( hSrc, REGION_MAGIC) ))
821         return FALSE;
822     if(!( destObj = (RGNOBJ *) GDI_GetObjPtr( hDest, REGION_MAGIC) ))
823     {
824         GDI_HEAP_UNLOCK( hSrc );
825         return FALSE;
826     }
827     EMPTY_REGION( destObj->rgn );
828
829     pEndRect = srcObj->rgn->rects + srcObj->rgn->numRects;
830     for(pCurRect = srcObj->rgn->rects; pCurRect < pEndRect; pCurRect++)
831     {
832         tmpRect = *pCurRect;
833         tmpRect.left = XLPTODP( dc, tmpRect.left );
834         tmpRect.top = YLPTODP( dc, tmpRect.top );
835         tmpRect.right = XLPTODP( dc, tmpRect.right );
836         tmpRect.bottom = YLPTODP( dc, tmpRect.bottom );
837         REGION_UnionRectWithRegion( &tmpRect, destObj->rgn );
838     }
839     
840     GDI_HEAP_UNLOCK( hDest );
841     GDI_HEAP_UNLOCK( hSrc );
842     return TRUE;
843 }
844     
845 /***********************************************************************
846  *           CombineRgn16    (GDI.451)
847  */
848 INT16 WINAPI CombineRgn16(HRGN16 hDest, HRGN16 hSrc1, HRGN16 hSrc2, INT16 mode)
849 {
850     return (INT16)CombineRgn32( hDest, hSrc1, hSrc2, mode );
851 }
852
853
854 /***********************************************************************
855  *           CombineRgn32   (GDI32.19)
856  *
857  * Note: The behavior is correct even if src and dest regions are the same.
858  */
859 INT32 WINAPI CombineRgn32(HRGN32 hDest, HRGN32 hSrc1, HRGN32 hSrc2, INT32 mode)
860 {
861     RGNOBJ *destObj = (RGNOBJ *) GDI_GetObjPtr( hDest, REGION_MAGIC);
862     INT32 result = ERROR;
863
864     TRACE(region, " %04x,%04x -> %04x mode=%x\n", 
865                  hSrc1, hSrc2, hDest, mode );
866     if (destObj)
867     {
868         RGNOBJ *src1Obj = (RGNOBJ *) GDI_GetObjPtr( hSrc1, REGION_MAGIC);
869
870         if (src1Obj)
871         {
872             TRACE(region, "dump:\n");
873             if(TRACE_ON(region)) 
874               REGION_DumpRegion(src1Obj->rgn);
875             if (mode == RGN_COPY)
876             {
877                 REGION_CopyRegion( destObj->rgn, src1Obj->rgn );
878                 result = destObj->rgn->type;
879             }
880             else
881             {
882                 RGNOBJ *src2Obj = (RGNOBJ *) GDI_GetObjPtr( hSrc2, REGION_MAGIC);
883
884                 if (src2Obj)
885                 {
886                     TRACE(region, "dump:\n");
887                     if(TRACE_ON(region)) 
888                       REGION_DumpRegion(src2Obj->rgn);
889                     switch (mode)
890                     {
891                     case RGN_AND:
892                         REGION_IntersectRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn);
893                         break;
894                     case RGN_OR:
895                         REGION_UnionRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn );
896                         break;
897                     case RGN_XOR:
898                         REGION_XorRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn );
899                         break;
900                     case RGN_DIFF:
901                         REGION_SubtractRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn );
902                         break;
903                     }
904                     result = destObj->rgn->type;
905                     GDI_HEAP_UNLOCK( hSrc2 );
906                 }
907             }
908             GDI_HEAP_UNLOCK( hSrc1 );
909         }
910         TRACE(region, "dump:\n");
911         if(TRACE_ON(region)) 
912           REGION_DumpRegion(destObj->rgn);
913
914         GDI_HEAP_UNLOCK( hDest );
915     }
916     return result;
917 }
918
919 /***********************************************************************
920  *           REGION_SetExtents
921  *           Re-calculate the extents of a region
922  */
923 static void REGION_SetExtents (WINEREGION *pReg)
924 {
925     RECT32 *pRect, *pRectEnd, *pExtents;
926
927     if (pReg->numRects == 0)
928     {
929         pReg->extents.left = 0;
930         pReg->extents.top = 0;
931         pReg->extents.right = 0;
932         pReg->extents.bottom = 0;
933         return;
934     }
935
936     pExtents = &pReg->extents;
937     pRect = pReg->rects;
938     pRectEnd = &pRect[pReg->numRects - 1];
939
940     /*
941      * Since pRect is the first rectangle in the region, it must have the
942      * smallest top and since pRectEnd is the last rectangle in the region,
943      * it must have the largest bottom, because of banding. Initialize left and
944      * right from pRect and pRectEnd, resp., as good things to initialize them
945      * to...
946      */
947     pExtents->left = pRect->left;
948     pExtents->top = pRect->top;
949     pExtents->right = pRectEnd->right;
950     pExtents->bottom = pRectEnd->bottom;
951
952     while (pRect <= pRectEnd)
953     {
954         if (pRect->left < pExtents->left)
955             pExtents->left = pRect->left;
956         if (pRect->right > pExtents->right)
957             pExtents->right = pRect->right;
958         pRect++;
959     }
960 }
961
962 /***********************************************************************
963  *           REGION_CopyRegion
964  */
965 static void REGION_CopyRegion(WINEREGION *dst, WINEREGION *src)
966 {
967     if (dst != src) /*  don't want to copy to itself */
968     {  
969         if (dst->size < src->numRects)
970         {
971             if (! (dst->rects = HeapReAlloc( SystemHeap, 0, dst->rects,
972                                 src->numRects * sizeof(RECT32) )))
973                 return;
974             dst->size = src->numRects;
975         }
976         dst->numRects = src->numRects;
977         dst->extents.left = src->extents.left;
978         dst->extents.top = src->extents.top;
979         dst->extents.right = src->extents.right;
980         dst->extents.bottom = src->extents.bottom;
981         dst->type = src->type;
982
983         memcpy((char *) dst->rects, (char *) src->rects,
984                (int) (src->numRects * sizeof(RECT32)));
985     }
986     return;
987 }
988
989 /***********************************************************************
990  *           REGION_Coalesce
991  *
992  *      Attempt to merge the rects in the current band with those in the
993  *      previous one. Used only by REGION_RegionOp.
994  *
995  * Results:
996  *      The new index for the previous band.
997  *
998  * Side Effects:
999  *      If coalescing takes place:
1000  *          - rectangles in the previous band will have their bottom fields
1001  *            altered.
1002  *          - pReg->numRects will be decreased.
1003  *
1004  */
1005 static INT32 REGION_Coalesce (
1006              WINEREGION *pReg, /* Region to coalesce */
1007              INT32 prevStart,  /* Index of start of previous band */
1008              INT32 curStart    /* Index of start of current band */
1009 ) {
1010     RECT32 *pPrevRect;          /* Current rect in previous band */
1011     RECT32 *pCurRect;           /* Current rect in current band */
1012     RECT32 *pRegEnd;            /* End of region */
1013     INT32 curNumRects;          /* Number of rectangles in current band */
1014     INT32 prevNumRects;         /* Number of rectangles in previous band */
1015     INT32 bandtop;               /* top coordinate for current band */
1016
1017     pRegEnd = &pReg->rects[pReg->numRects];
1018
1019     pPrevRect = &pReg->rects[prevStart];
1020     prevNumRects = curStart - prevStart;
1021
1022     /*
1023      * Figure out how many rectangles are in the current band. Have to do
1024      * this because multiple bands could have been added in REGION_RegionOp
1025      * at the end when one region has been exhausted.
1026      */
1027     pCurRect = &pReg->rects[curStart];
1028     bandtop = pCurRect->top;
1029     for (curNumRects = 0;
1030          (pCurRect != pRegEnd) && (pCurRect->top == bandtop);
1031          curNumRects++)
1032     {
1033         pCurRect++;
1034     }
1035     
1036     if (pCurRect != pRegEnd)
1037     {
1038         /*
1039          * If more than one band was added, we have to find the start
1040          * of the last band added so the next coalescing job can start
1041          * at the right place... (given when multiple bands are added,
1042          * this may be pointless -- see above).
1043          */
1044         pRegEnd--;
1045         while (pRegEnd[-1].top == pRegEnd->top)
1046         {
1047             pRegEnd--;
1048         }
1049         curStart = pRegEnd - pReg->rects;
1050         pRegEnd = pReg->rects + pReg->numRects;
1051     }
1052         
1053     if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
1054         pCurRect -= curNumRects;
1055         /*
1056          * The bands may only be coalesced if the bottom of the previous
1057          * matches the top scanline of the current.
1058          */
1059         if (pPrevRect->bottom == pCurRect->top)
1060         {
1061             /*
1062              * Make sure the bands have rects in the same places. This
1063              * assumes that rects have been added in such a way that they
1064              * cover the most area possible. I.e. two rects in a band must
1065              * have some horizontal space between them.
1066              */
1067             do
1068             {
1069                 if ((pPrevRect->left != pCurRect->left) ||
1070                     (pPrevRect->right != pCurRect->right))
1071                 {
1072                     /*
1073                      * The bands don't line up so they can't be coalesced.
1074                      */
1075                     return (curStart);
1076                 }
1077                 pPrevRect++;
1078                 pCurRect++;
1079                 prevNumRects -= 1;
1080             } while (prevNumRects != 0);
1081
1082             pReg->numRects -= curNumRects;
1083             pCurRect -= curNumRects;
1084             pPrevRect -= curNumRects;
1085
1086             /*
1087              * The bands may be merged, so set the bottom of each rect
1088              * in the previous band to that of the corresponding rect in
1089              * the current band.
1090              */
1091             do
1092             {
1093                 pPrevRect->bottom = pCurRect->bottom;
1094                 pPrevRect++;
1095                 pCurRect++;
1096                 curNumRects -= 1;
1097             } while (curNumRects != 0);
1098
1099             /*
1100              * If only one band was added to the region, we have to backup
1101              * curStart to the start of the previous band.
1102              *
1103              * If more than one band was added to the region, copy the
1104              * other bands down. The assumption here is that the other bands
1105              * came from the same region as the current one and no further
1106              * coalescing can be done on them since it's all been done
1107              * already... curStart is already in the right place.
1108              */
1109             if (pCurRect == pRegEnd)
1110             {
1111                 curStart = prevStart;
1112             }
1113             else
1114             {
1115                 do
1116                 {
1117                     *pPrevRect++ = *pCurRect++;
1118                 } while (pCurRect != pRegEnd);
1119             }
1120             
1121         }
1122     }
1123     return (curStart);
1124 }
1125
1126 /***********************************************************************
1127  *           REGION_RegionOp
1128  *
1129  *      Apply an operation to two regions. Called by REGION_Union,
1130  *      REGION_Inverse, REGION_Subtract, REGION_Intersect...
1131  *
1132  * Results:
1133  *      None.
1134  *
1135  * Side Effects:
1136  *      The new region is overwritten.
1137  *
1138  * Notes:
1139  *      The idea behind this function is to view the two regions as sets.
1140  *      Together they cover a rectangle of area that this function divides
1141  *      into horizontal bands where points are covered only by one region
1142  *      or by both. For the first case, the nonOverlapFunc is called with
1143  *      each the band and the band's upper and lower extents. For the
1144  *      second, the overlapFunc is called to process the entire band. It
1145  *      is responsible for clipping the rectangles in the band, though
1146  *      this function provides the boundaries.
1147  *      At the end of each band, the new region is coalesced, if possible,
1148  *      to reduce the number of rectangles in the region.
1149  *
1150  */
1151 static void REGION_RegionOp(
1152             WINEREGION *newReg, /* Place to store result */
1153             WINEREGION *reg1,   /* First region in operation */
1154             WINEREGION *reg2,   /* 2nd region in operation */
1155             void (*overlapFunc)(),     /* Function to call for over-lapping bands */
1156             void (*nonOverlap1Func)(), /* Function to call for non-overlapping bands in region 1 */
1157             void (*nonOverlap2Func)()  /* Function to call for non-overlapping bands in region 2 */
1158 ) {
1159     RECT32 *r1;                         /* Pointer into first region */
1160     RECT32 *r2;                         /* Pointer into 2d region */
1161     RECT32 *r1End;                      /* End of 1st region */
1162     RECT32 *r2End;                      /* End of 2d region */
1163     INT32 ybot;                         /* Bottom of intersection */
1164     INT32 ytop;                         /* Top of intersection */
1165     RECT32 *oldRects;                   /* Old rects for newReg */
1166     INT32 prevBand;                     /* Index of start of
1167                                                  * previous band in newReg */
1168     INT32 curBand;                      /* Index of start of current
1169                                                  * band in newReg */
1170     RECT32 *r1BandEnd;                  /* End of current band in r1 */
1171     RECT32 *r2BandEnd;                  /* End of current band in r2 */
1172     INT32 top;                          /* Top of non-overlapping band */
1173     INT32 bot;                          /* Bottom of non-overlapping band */
1174     
1175     /*
1176      * Initialization:
1177      *  set r1, r2, r1End and r2End appropriately, preserve the important
1178      * parts of the destination region until the end in case it's one of
1179      * the two source regions, then mark the "new" region empty, allocating
1180      * another array of rectangles for it to use.
1181      */
1182     r1 = reg1->rects;
1183     r2 = reg2->rects;
1184     r1End = r1 + reg1->numRects;
1185     r2End = r2 + reg2->numRects;
1186     
1187
1188     /*
1189      * newReg may be one of the src regions so we can't empty it. We keep a 
1190      * note of its rects pointer (so that we can free them later), preserve its
1191      * extents and simply set numRects to zero. 
1192      */
1193
1194     oldRects = newReg->rects;
1195     newReg->numRects = 0;
1196
1197     /*
1198      * Allocate a reasonable number of rectangles for the new region. The idea
1199      * is to allocate enough so the individual functions don't need to
1200      * reallocate and copy the array, which is time consuming, yet we don't
1201      * have to worry about using too much memory. I hope to be able to
1202      * nuke the Xrealloc() at the end of this function eventually.
1203      */
1204     newReg->size = MAX(reg1->numRects,reg2->numRects) * 2;
1205
1206     if (! (newReg->rects = HeapAlloc( SystemHeap, 0, 
1207                                   sizeof(RECT32) * newReg->size )))
1208     {
1209         newReg->size = 0;
1210         return;
1211     }
1212     
1213     /*
1214      * Initialize ybot and ytop.
1215      * In the upcoming loop, ybot and ytop serve different functions depending
1216      * on whether the band being handled is an overlapping or non-overlapping
1217      * band.
1218      *  In the case of a non-overlapping band (only one of the regions
1219      * has points in the band), ybot is the bottom of the most recent
1220      * intersection and thus clips the top of the rectangles in that band.
1221      * ytop is the top of the next intersection between the two regions and
1222      * serves to clip the bottom of the rectangles in the current band.
1223      *  For an overlapping band (where the two regions intersect), ytop clips
1224      * the top of the rectangles of both regions and ybot clips the bottoms.
1225      */
1226     if (reg1->extents.top < reg2->extents.top)
1227         ybot = reg1->extents.top;
1228     else
1229         ybot = reg2->extents.top;
1230     
1231     /*
1232      * prevBand serves to mark the start of the previous band so rectangles
1233      * can be coalesced into larger rectangles. qv. miCoalesce, above.
1234      * In the beginning, there is no previous band, so prevBand == curBand
1235      * (curBand is set later on, of course, but the first band will always
1236      * start at index 0). prevBand and curBand must be indices because of
1237      * the possible expansion, and resultant moving, of the new region's
1238      * array of rectangles.
1239      */
1240     prevBand = 0;
1241     
1242     do
1243     {
1244         curBand = newReg->numRects;
1245
1246         /*
1247          * This algorithm proceeds one source-band (as opposed to a
1248          * destination band, which is determined by where the two regions
1249          * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
1250          * rectangle after the last one in the current band for their
1251          * respective regions.
1252          */
1253         r1BandEnd = r1;
1254         while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top))
1255         {
1256             r1BandEnd++;
1257         }
1258         
1259         r2BandEnd = r2;
1260         while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top))
1261         {
1262             r2BandEnd++;
1263         }
1264         
1265         /*
1266          * First handle the band that doesn't intersect, if any.
1267          *
1268          * Note that attention is restricted to one band in the
1269          * non-intersecting region at once, so if a region has n
1270          * bands between the current position and the next place it overlaps
1271          * the other, this entire loop will be passed through n times.
1272          */
1273         if (r1->top < r2->top)
1274         {
1275             top = MAX(r1->top,ybot);
1276             bot = MIN(r1->bottom,r2->top);
1277
1278             if ((top != bot) && (nonOverlap1Func != (void (*)())NULL))
1279             {
1280                 (* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot);
1281             }
1282
1283             ytop = r2->top;
1284         }
1285         else if (r2->top < r1->top)
1286         {
1287             top = MAX(r2->top,ybot);
1288             bot = MIN(r2->bottom,r1->top);
1289
1290             if ((top != bot) && (nonOverlap2Func != (void (*)())NULL))
1291             {
1292                 (* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot);
1293             }
1294
1295             ytop = r1->top;
1296         }
1297         else
1298         {
1299             ytop = r1->top;
1300         }
1301
1302         /*
1303          * If any rectangles got added to the region, try and coalesce them
1304          * with rectangles from the previous band. Note we could just do
1305          * this test in miCoalesce, but some machines incur a not
1306          * inconsiderable cost for function calls, so...
1307          */
1308         if (newReg->numRects != curBand)
1309         {
1310             prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1311         }
1312
1313         /*
1314          * Now see if we've hit an intersecting band. The two bands only
1315          * intersect if ybot > ytop
1316          */
1317         ybot = MIN(r1->bottom, r2->bottom);
1318         curBand = newReg->numRects;
1319         if (ybot > ytop)
1320         {
1321             (* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
1322
1323         }
1324         
1325         if (newReg->numRects != curBand)
1326         {
1327             prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1328         }
1329
1330         /*
1331          * If we've finished with a band (bottom == ybot) we skip forward
1332          * in the region to the next band.
1333          */
1334         if (r1->bottom == ybot)
1335         {
1336             r1 = r1BandEnd;
1337         }
1338         if (r2->bottom == ybot)
1339         {
1340             r2 = r2BandEnd;
1341         }
1342     } while ((r1 != r1End) && (r2 != r2End));
1343
1344     /*
1345      * Deal with whichever region still has rectangles left.
1346      */
1347     curBand = newReg->numRects;
1348     if (r1 != r1End)
1349     {
1350         if (nonOverlap1Func != (void (*)())NULL)
1351         {
1352             do
1353             {
1354                 r1BandEnd = r1;
1355                 while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top))
1356                 {
1357                     r1BandEnd++;
1358                 }
1359                 (* nonOverlap1Func) (newReg, r1, r1BandEnd,
1360                                      MAX(r1->top,ybot), r1->bottom);
1361                 r1 = r1BandEnd;
1362             } while (r1 != r1End);
1363         }
1364     }
1365     else if ((r2 != r2End) && (nonOverlap2Func != (void (*)())NULL))
1366     {
1367         do
1368         {
1369             r2BandEnd = r2;
1370             while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top))
1371             {
1372                  r2BandEnd++;
1373             }
1374             (* nonOverlap2Func) (newReg, r2, r2BandEnd,
1375                                 MAX(r2->top,ybot), r2->bottom);
1376             r2 = r2BandEnd;
1377         } while (r2 != r2End);
1378     }
1379
1380     if (newReg->numRects != curBand)
1381     {
1382         (void) REGION_Coalesce (newReg, prevBand, curBand);
1383     }
1384
1385     /*
1386      * A bit of cleanup. To keep regions from growing without bound,
1387      * we shrink the array of rectangles to match the new number of
1388      * rectangles in the region. This never goes to 0, however...
1389      *
1390      * Only do this stuff if the number of rectangles allocated is more than
1391      * twice the number of rectangles in the region (a simple optimization...).
1392      */
1393     if (newReg->numRects < (newReg->size >> 1))
1394     {
1395         if (REGION_NOT_EMPTY(newReg))
1396         {
1397             RECT32 *prev_rects = newReg->rects;
1398             newReg->size = newReg->numRects;
1399             newReg->rects = HeapReAlloc( SystemHeap, 0, newReg->rects,
1400                                    sizeof(RECT32) * newReg->size );
1401             if (! newReg->rects)
1402                 newReg->rects = prev_rects;
1403         }
1404         else
1405         {
1406             /*
1407              * No point in doing the extra work involved in an Xrealloc if
1408              * the region is empty
1409              */
1410             newReg->size = 1;
1411             HeapFree( SystemHeap, 0, newReg->rects );
1412             newReg->rects = HeapAlloc( SystemHeap, 0, sizeof(RECT32) );
1413         }
1414     }
1415     HeapFree( SystemHeap, 0, oldRects );
1416     return;
1417 }
1418
1419 /***********************************************************************
1420  *          Region Intersection
1421  ***********************************************************************/
1422
1423
1424 /***********************************************************************
1425  *           REGION_IntersectO
1426  *
1427  * Handle an overlapping band for REGION_Intersect.
1428  *
1429  * Results:
1430  *      None.
1431  *
1432  * Side Effects:
1433  *      Rectangles may be added to the region.
1434  *
1435  */
1436 static void REGION_IntersectO(WINEREGION *pReg,  RECT32 *r1, RECT32 *r1End,
1437                 RECT32 *r2, RECT32 *r2End, INT32 top, INT32 bottom)
1438
1439 {
1440     INT32       left, right;
1441     RECT32      *pNextRect;
1442
1443     pNextRect = &pReg->rects[pReg->numRects];
1444
1445     while ((r1 != r1End) && (r2 != r2End))
1446     {
1447         left = MAX(r1->left, r2->left);
1448         right = MIN(r1->right, r2->right);
1449
1450         /*
1451          * If there's any overlap between the two rectangles, add that
1452          * overlap to the new region.
1453          * There's no need to check for subsumption because the only way
1454          * such a need could arise is if some region has two rectangles
1455          * right next to each other. Since that should never happen...
1456          */
1457         if (left < right)
1458         {
1459             MEMCHECK(pReg, pNextRect, pReg->rects);
1460             pNextRect->left = left;
1461             pNextRect->top = top;
1462             pNextRect->right = right;
1463             pNextRect->bottom = bottom;
1464             pReg->numRects += 1;
1465             pNextRect++;
1466         }
1467
1468         /*
1469          * Need to advance the pointers. Shift the one that extends
1470          * to the right the least, since the other still has a chance to
1471          * overlap with that region's next rectangle, if you see what I mean.
1472          */
1473         if (r1->right < r2->right)
1474         {
1475             r1++;
1476         }
1477         else if (r2->right < r1->right)
1478         {
1479             r2++;
1480         }
1481         else
1482         {
1483             r1++;
1484             r2++;
1485         }
1486     }
1487     return;
1488 }
1489
1490 /***********************************************************************
1491  *           REGION_IntersectRegion
1492  */
1493 static void REGION_IntersectRegion(WINEREGION *newReg, WINEREGION *reg1,
1494                                    WINEREGION *reg2)
1495 {
1496    /* check for trivial reject */
1497     if ( (!(reg1->numRects)) || (!(reg2->numRects))  ||
1498         (!EXTENTCHECK(&reg1->extents, &reg2->extents)))
1499         newReg->numRects = 0;
1500     else
1501         REGION_RegionOp (newReg, reg1, reg2, 
1502          (voidProcp) REGION_IntersectO, (voidProcp) NULL, (voidProcp) NULL);
1503     
1504     /*
1505      * Can't alter newReg's extents before we call miRegionOp because
1506      * it might be one of the source regions and miRegionOp depends
1507      * on the extents of those regions being the same. Besides, this
1508      * way there's no checking against rectangles that will be nuked
1509      * due to coalescing, so we have to examine fewer rectangles.
1510      */
1511     REGION_SetExtents(newReg);
1512     newReg->type = (newReg->numRects) ? COMPLEXREGION : NULLREGION ;
1513     return;
1514 }
1515
1516 /***********************************************************************
1517  *           Region Union
1518  ***********************************************************************/
1519
1520 /***********************************************************************
1521  *           REGION_UnionNonO
1522  *
1523  *      Handle a non-overlapping band for the union operation. Just
1524  *      Adds the rectangles into the region. Doesn't have to check for
1525  *      subsumption or anything.
1526  *
1527  * Results:
1528  *      None.
1529  *
1530  * Side Effects:
1531  *      pReg->numRects is incremented and the final rectangles overwritten
1532  *      with the rectangles we're passed.
1533  *
1534  */
1535 static void REGION_UnionNonO (WINEREGION *pReg, RECT32 *r, RECT32 *rEnd,
1536                               INT32 top, INT32 bottom)
1537 {
1538     RECT32 *pNextRect;
1539
1540     pNextRect = &pReg->rects[pReg->numRects];
1541
1542     while (r != rEnd)
1543     {
1544         MEMCHECK(pReg, pNextRect, pReg->rects);
1545         pNextRect->left = r->left;
1546         pNextRect->top = top;
1547         pNextRect->right = r->right;
1548         pNextRect->bottom = bottom;
1549         pReg->numRects += 1;
1550         pNextRect++;
1551         r++;
1552     }
1553     return;
1554 }
1555
1556 /***********************************************************************
1557  *           REGION_UnionO
1558  *
1559  *      Handle an overlapping band for the union operation. Picks the
1560  *      left-most rectangle each time and merges it into the region.
1561  *
1562  * Results:
1563  *      None.
1564  *
1565  * Side Effects:
1566  *      Rectangles are overwritten in pReg->rects and pReg->numRects will
1567  *      be changed.
1568  *
1569  */
1570 static void REGION_UnionO (WINEREGION *pReg, RECT32 *r1, RECT32 *r1End,
1571                            RECT32 *r2, RECT32 *r2End, INT32 top, INT32 bottom)
1572 {
1573     RECT32 *pNextRect;
1574     
1575     pNextRect = &pReg->rects[pReg->numRects];
1576
1577 #define MERGERECT(r) \
1578     if ((pReg->numRects != 0) &&  \
1579         (pNextRect[-1].top == top) &&  \
1580         (pNextRect[-1].bottom == bottom) &&  \
1581         (pNextRect[-1].right >= r->left))  \
1582     {  \
1583         if (pNextRect[-1].right < r->right)  \
1584         {  \
1585             pNextRect[-1].right = r->right;  \
1586         }  \
1587     }  \
1588     else  \
1589     {  \
1590         MEMCHECK(pReg, pNextRect, pReg->rects);  \
1591         pNextRect->top = top;  \
1592         pNextRect->bottom = bottom;  \
1593         pNextRect->left = r->left;  \
1594         pNextRect->right = r->right;  \
1595         pReg->numRects += 1;  \
1596         pNextRect += 1;  \
1597     }  \
1598     r++;
1599     
1600     while ((r1 != r1End) && (r2 != r2End))
1601     {
1602         if (r1->left < r2->left)
1603         {
1604             MERGERECT(r1);
1605         }
1606         else
1607         {
1608             MERGERECT(r2);
1609         }
1610     }
1611     
1612     if (r1 != r1End)
1613     {
1614         do
1615         {
1616             MERGERECT(r1);
1617         } while (r1 != r1End);
1618     }
1619     else while (r2 != r2End)
1620     {
1621         MERGERECT(r2);
1622     }
1623     return;
1624 }
1625
1626 /***********************************************************************
1627  *           REGION_UnionRegion
1628  */
1629 static void REGION_UnionRegion(WINEREGION *newReg, WINEREGION *reg1,
1630                                WINEREGION *reg2)
1631 {
1632     /*  checks all the simple cases */
1633
1634     /*
1635      * Region 1 and 2 are the same or region 1 is empty
1636      */
1637     if ( (reg1 == reg2) || (!(reg1->numRects)) )
1638     {
1639         if (newReg != reg2)
1640             REGION_CopyRegion(newReg, reg2);
1641         return;
1642     }
1643
1644     /*
1645      * if nothing to union (region 2 empty)
1646      */
1647     if (!(reg2->numRects))
1648     {
1649         if (newReg != reg1)
1650             REGION_CopyRegion(newReg, reg1);
1651         return;
1652     }
1653
1654     /*
1655      * Region 1 completely subsumes region 2
1656      */
1657     if ((reg1->numRects == 1) && 
1658         (reg1->extents.left <= reg2->extents.left) &&
1659         (reg1->extents.top <= reg2->extents.top) &&
1660         (reg1->extents.right >= reg2->extents.right) &&
1661         (reg1->extents.bottom >= reg2->extents.bottom))
1662     {
1663         if (newReg != reg1)
1664             REGION_CopyRegion(newReg, reg1);
1665         return;
1666     }
1667
1668     /*
1669      * Region 2 completely subsumes region 1
1670      */
1671     if ((reg2->numRects == 1) && 
1672         (reg2->extents.left <= reg1->extents.left) &&
1673         (reg2->extents.top <= reg1->extents.top) &&
1674         (reg2->extents.right >= reg1->extents.right) &&
1675         (reg2->extents.bottom >= reg1->extents.bottom))
1676     {
1677         if (newReg != reg2)
1678             REGION_CopyRegion(newReg, reg2);
1679         return;
1680     }
1681
1682     REGION_RegionOp (newReg, reg1, reg2, (voidProcp) REGION_UnionO, 
1683                 (voidProcp) REGION_UnionNonO, (voidProcp) REGION_UnionNonO);
1684
1685     newReg->extents.left = MIN(reg1->extents.left, reg2->extents.left);
1686     newReg->extents.top = MIN(reg1->extents.top, reg2->extents.top);
1687     newReg->extents.right = MAX(reg1->extents.right, reg2->extents.right);
1688     newReg->extents.bottom = MAX(reg1->extents.bottom, reg2->extents.bottom);
1689     newReg->type = (newReg->numRects) ? COMPLEXREGION : NULLREGION ;
1690     return;
1691 }
1692
1693 /***********************************************************************
1694  *           Region Subtraction
1695  ***********************************************************************/
1696
1697 /***********************************************************************
1698  *           REGION_SubtractNonO1
1699  *
1700  *      Deal with non-overlapping band for subtraction. Any parts from
1701  *      region 2 we discard. Anything from region 1 we add to the region.
1702  *
1703  * Results:
1704  *      None.
1705  *
1706  * Side Effects:
1707  *      pReg may be affected.
1708  *
1709  */
1710 static void REGION_SubtractNonO1 (WINEREGION *pReg, RECT32 *r, RECT32 *rEnd, 
1711                 INT32 top, INT32 bottom)
1712 {
1713     RECT32 *pNextRect;
1714         
1715     pNextRect = &pReg->rects[pReg->numRects];
1716         
1717     while (r != rEnd)
1718     {
1719         MEMCHECK(pReg, pNextRect, pReg->rects);
1720         pNextRect->left = r->left;
1721         pNextRect->top = top;
1722         pNextRect->right = r->right;
1723         pNextRect->bottom = bottom;
1724         pReg->numRects += 1;
1725         pNextRect++;
1726         r++;
1727     }
1728     return;
1729 }
1730
1731
1732 /***********************************************************************
1733  *           REGION_SubtractO
1734  *
1735  *      Overlapping band subtraction. x1 is the left-most point not yet
1736  *      checked.
1737  *
1738  * Results:
1739  *      None.
1740  *
1741  * Side Effects:
1742  *      pReg may have rectangles added to it.
1743  *
1744  */
1745 static void REGION_SubtractO (WINEREGION *pReg, RECT32 *r1, RECT32 *r1End, 
1746                 RECT32 *r2, RECT32 *r2End, INT32 top, INT32 bottom)
1747 {
1748     RECT32 *pNextRect;
1749     INT32 left;
1750     
1751     left = r1->left;
1752     pNextRect = &pReg->rects[pReg->numRects];
1753
1754     while ((r1 != r1End) && (r2 != r2End))
1755     {
1756         if (r2->right <= left)
1757         {
1758             /*
1759              * Subtrahend missed the boat: go to next subtrahend.
1760              */
1761             r2++;
1762         }
1763         else if (r2->left <= left)
1764         {
1765             /*
1766              * Subtrahend preceeds minuend: nuke left edge of minuend.
1767              */
1768             left = r2->right;
1769             if (left >= r1->right)
1770             {
1771                 /*
1772                  * Minuend completely covered: advance to next minuend and
1773                  * reset left fence to edge of new minuend.
1774                  */
1775                 r1++;
1776                 if (r1 != r1End)
1777                     left = r1->left;
1778             }
1779             else
1780             {
1781                 /*
1782                  * Subtrahend now used up since it doesn't extend beyond
1783                  * minuend
1784                  */
1785                 r2++;
1786             }
1787         }
1788         else if (r2->left < r1->right)
1789         {
1790             /*
1791              * Left part of subtrahend covers part of minuend: add uncovered
1792              * part of minuend to region and skip to next subtrahend.
1793              */
1794             MEMCHECK(pReg, pNextRect, pReg->rects);
1795             pNextRect->left = left;
1796             pNextRect->top = top;
1797             pNextRect->right = r2->left;
1798             pNextRect->bottom = bottom;
1799             pReg->numRects += 1;
1800             pNextRect++;
1801             left = r2->right;
1802             if (left >= r1->right)
1803             {
1804                 /*
1805                  * Minuend used up: advance to new...
1806                  */
1807                 r1++;
1808                 if (r1 != r1End)
1809                     left = r1->left;
1810             }
1811             else
1812             {
1813                 /*
1814                  * Subtrahend used up
1815                  */
1816                 r2++;
1817             }
1818         }
1819         else
1820         {
1821             /*
1822              * Minuend used up: add any remaining piece before advancing.
1823              */
1824             if (r1->right > left)
1825             {
1826                 MEMCHECK(pReg, pNextRect, pReg->rects);
1827                 pNextRect->left = left;
1828                 pNextRect->top = top;
1829                 pNextRect->right = r1->right;
1830                 pNextRect->bottom = bottom;
1831                 pReg->numRects += 1;
1832                 pNextRect++;
1833             }
1834             r1++;
1835             left = r1->left;
1836         }
1837     }
1838
1839     /*
1840      * Add remaining minuend rectangles to region.
1841      */
1842     while (r1 != r1End)
1843     {
1844         MEMCHECK(pReg, pNextRect, pReg->rects);
1845         pNextRect->left = left;
1846         pNextRect->top = top;
1847         pNextRect->right = r1->right;
1848         pNextRect->bottom = bottom;
1849         pReg->numRects += 1;
1850         pNextRect++;
1851         r1++;
1852         if (r1 != r1End)
1853         {
1854             left = r1->left;
1855         }
1856     }
1857     return;
1858 }
1859         
1860 /***********************************************************************
1861  *           REGION_SubtractRegion
1862  *
1863  *      Subtract regS from regM and leave the result in regD.
1864  *      S stands for subtrahend, M for minuend and D for difference.
1865  *
1866  * Results:
1867  *      TRUE.
1868  *
1869  * Side Effects:
1870  *      regD is overwritten.
1871  *
1872  */
1873 static void REGION_SubtractRegion(WINEREGION *regD, WINEREGION *regM,
1874                                                        WINEREGION *regS )
1875 {
1876    /* check for trivial reject */
1877     if ( (!(regM->numRects)) || (!(regS->numRects))  ||
1878         (!EXTENTCHECK(&regM->extents, &regS->extents)) )
1879     {
1880         REGION_CopyRegion(regD, regM);
1881         return;
1882     }
1883  
1884     REGION_RegionOp (regD, regM, regS, (voidProcp) REGION_SubtractO, 
1885                 (voidProcp) REGION_SubtractNonO1, (voidProcp) NULL);
1886
1887     /*
1888      * Can't alter newReg's extents before we call miRegionOp because
1889      * it might be one of the source regions and miRegionOp depends
1890      * on the extents of those regions being the unaltered. Besides, this
1891      * way there's no checking against rectangles that will be nuked
1892      * due to coalescing, so we have to examine fewer rectangles.
1893      */
1894     REGION_SetExtents (regD);
1895     regD->type = (regD->numRects) ? COMPLEXREGION : NULLREGION ;
1896     return;
1897 }
1898
1899 /***********************************************************************
1900  *           REGION_XorRegion
1901  */
1902 static void REGION_XorRegion(WINEREGION *dr, WINEREGION *sra,
1903                                                         WINEREGION *srb)
1904 {
1905     WINEREGION *tra, *trb;
1906
1907     if ((! (tra = REGION_AllocWineRegion())) || 
1908         (! (trb = REGION_AllocWineRegion())))
1909         return;
1910     REGION_SubtractRegion(tra,sra,srb);
1911     REGION_SubtractRegion(trb,srb,sra);
1912     REGION_UnionRegion(dr,tra,trb);
1913     REGION_DestroyWineRegion(tra);
1914     REGION_DestroyWineRegion(trb);
1915     return;
1916 }
1917
1918 /**************************************************************************
1919  *
1920  *    Poly Regions
1921  * 
1922  *************************************************************************/
1923
1924 #define LARGE_COORDINATE  0x7fffffff /* FIXME */
1925 #define SMALL_COORDINATE  0x80000000
1926
1927 /***********************************************************************
1928  *     REGION_InsertEdgeInET
1929  *
1930  *     Insert the given edge into the edge table.
1931  *     First we must find the correct bucket in the
1932  *     Edge table, then find the right slot in the
1933  *     bucket.  Finally, we can insert it.
1934  *
1935  */
1936 static void REGION_InsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE,
1937                 INT32 scanline, ScanLineListBlock **SLLBlock, INT32 *iSLLBlock)
1938
1939 {
1940     EdgeTableEntry *start, *prev;
1941     ScanLineList *pSLL, *pPrevSLL;
1942     ScanLineListBlock *tmpSLLBlock;
1943
1944     /*
1945      * find the right bucket to put the edge into
1946      */
1947     pPrevSLL = &ET->scanlines;
1948     pSLL = pPrevSLL->next;
1949     while (pSLL && (pSLL->scanline < scanline)) 
1950     {
1951         pPrevSLL = pSLL;
1952         pSLL = pSLL->next;
1953     }
1954
1955     /*
1956      * reassign pSLL (pointer to ScanLineList) if necessary
1957      */
1958     if ((!pSLL) || (pSLL->scanline > scanline)) 
1959     {
1960         if (*iSLLBlock > SLLSPERBLOCK-1) 
1961         {
1962             tmpSLLBlock = HeapAlloc( SystemHeap, 0, sizeof(ScanLineListBlock));
1963             if(!tmpSLLBlock)
1964             {
1965                 WARN(region, "Can't alloc SLLB\n");
1966                 return;
1967             }
1968             (*SLLBlock)->next = tmpSLLBlock;
1969             tmpSLLBlock->next = (ScanLineListBlock *)NULL;
1970             *SLLBlock = tmpSLLBlock;
1971             *iSLLBlock = 0;
1972         }
1973         pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
1974
1975         pSLL->next = pPrevSLL->next;
1976         pSLL->edgelist = (EdgeTableEntry *)NULL;
1977         pPrevSLL->next = pSLL;
1978     }
1979     pSLL->scanline = scanline;
1980
1981     /*
1982      * now insert the edge in the right bucket
1983      */
1984     prev = (EdgeTableEntry *)NULL;
1985     start = pSLL->edgelist;
1986     while (start && (start->bres.minor_axis < ETE->bres.minor_axis)) 
1987     {
1988         prev = start;
1989         start = start->next;
1990     }
1991     ETE->next = start;
1992
1993     if (prev)
1994         prev->next = ETE;
1995     else
1996         pSLL->edgelist = ETE;
1997 }
1998
1999 /***********************************************************************
2000  *     REGION_CreateEdgeTable
2001  *
2002  *     This routine creates the edge table for
2003  *     scan converting polygons. 
2004  *     The Edge Table (ET) looks like:
2005  *
2006  *    EdgeTable
2007  *     --------
2008  *    |  ymax  |        ScanLineLists
2009  *    |scanline|-->------------>-------------->...
2010  *     --------   |scanline|   |scanline|
2011  *                |edgelist|   |edgelist|
2012  *                ---------    ---------
2013  *                    |             |
2014  *                    |             |
2015  *                    V             V
2016  *              list of ETEs   list of ETEs
2017  *
2018  *     where ETE is an EdgeTableEntry data structure,
2019  *     and there is one ScanLineList per scanline at
2020  *     which an edge is initially entered.
2021  *
2022  */
2023 static void REGION_CreateETandAET(const INT32 *Count, INT32 nbpolygons, 
2024             const POINT32 *pts, EdgeTable *ET, EdgeTableEntry *AET,
2025             EdgeTableEntry *pETEs, ScanLineListBlock *pSLLBlock)
2026 {
2027     const POINT32 *top, *bottom;
2028     const POINT32 *PrevPt, *CurrPt, *EndPt;
2029     INT32 poly, count;
2030     int iSLLBlock = 0;
2031     int dy;
2032
2033     
2034     /*
2035      *  initialize the Active Edge Table
2036      */
2037     AET->next = (EdgeTableEntry *)NULL;
2038     AET->back = (EdgeTableEntry *)NULL;
2039     AET->nextWETE = (EdgeTableEntry *)NULL;
2040     AET->bres.minor_axis = SMALL_COORDINATE;
2041
2042     /*
2043      *  initialize the Edge Table.
2044      */
2045     ET->scanlines.next = (ScanLineList *)NULL;
2046     ET->ymax = SMALL_COORDINATE;
2047     ET->ymin = LARGE_COORDINATE;
2048     pSLLBlock->next = (ScanLineListBlock *)NULL;
2049
2050     EndPt = pts - 1;
2051     for(poly = 0; poly < nbpolygons; poly++)
2052     {
2053         count = Count[poly];
2054         EndPt += count;
2055         if(count < 2)
2056             continue;
2057         
2058         PrevPt = EndPt;
2059
2060     /*
2061      *  for each vertex in the array of points.
2062      *  In this loop we are dealing with two vertices at
2063      *  a time -- these make up one edge of the polygon.
2064      */
2065         while (count--) 
2066         {
2067             CurrPt = pts++;
2068
2069         /*
2070          *  find out which point is above and which is below.
2071          */
2072             if (PrevPt->y > CurrPt->y) 
2073             {
2074                 bottom = PrevPt, top = CurrPt;
2075                 pETEs->ClockWise = 0;
2076             }
2077             else 
2078             {
2079                 bottom = CurrPt, top = PrevPt;
2080                 pETEs->ClockWise = 1;
2081             }
2082
2083         /*
2084          * don't add horizontal edges to the Edge table.
2085          */
2086             if (bottom->y != top->y) 
2087             {
2088                 pETEs->ymax = bottom->y-1;
2089                                 /* -1 so we don't get last scanline */
2090
2091             /*
2092              *  initialize integer edge algorithm
2093              */
2094                 dy = bottom->y - top->y;
2095                 BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres);
2096
2097                 REGION_InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock, 
2098                                                                 &iSLLBlock);
2099
2100                 if (PrevPt->y > ET->ymax)
2101                   ET->ymax = PrevPt->y;
2102                 if (PrevPt->y < ET->ymin)
2103                   ET->ymin = PrevPt->y;
2104                 pETEs++;
2105             }
2106
2107             PrevPt = CurrPt;
2108         }
2109     }
2110 }
2111
2112 /***********************************************************************
2113  *     REGION_loadAET
2114  *
2115  *     This routine moves EdgeTableEntries from the
2116  *     EdgeTable into the Active Edge Table,
2117  *     leaving them sorted by smaller x coordinate.
2118  *
2119  */
2120 static void REGION_loadAET(EdgeTableEntry *AET, EdgeTableEntry *ETEs)
2121 {
2122     EdgeTableEntry *pPrevAET;
2123     EdgeTableEntry *tmp;
2124
2125     pPrevAET = AET;
2126     AET = AET->next;
2127     while (ETEs) 
2128     {
2129         while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis)) 
2130         {
2131             pPrevAET = AET;
2132             AET = AET->next;
2133         }
2134         tmp = ETEs->next;
2135         ETEs->next = AET;
2136         if (AET)
2137             AET->back = ETEs;
2138         ETEs->back = pPrevAET;
2139         pPrevAET->next = ETEs;
2140         pPrevAET = ETEs;
2141
2142         ETEs = tmp;
2143     }
2144 }
2145
2146 /***********************************************************************
2147  *     REGION_computeWAET
2148  *
2149  *     This routine links the AET by the
2150  *     nextWETE (winding EdgeTableEntry) link for
2151  *     use by the winding number rule.  The final 
2152  *     Active Edge Table (AET) might look something
2153  *     like:
2154  *
2155  *     AET
2156  *     ----------  ---------   ---------
2157  *     |ymax    |  |ymax    |  |ymax    | 
2158  *     | ...    |  |...     |  |...     |
2159  *     |next    |->|next    |->|next    |->...
2160  *     |nextWETE|  |nextWETE|  |nextWETE|
2161  *     ---------   ---------   ^--------
2162  *         |                   |       |
2163  *         V------------------->       V---> ...
2164  *
2165  */
2166 static void REGION_computeWAET(EdgeTableEntry *AET)
2167 {
2168     register EdgeTableEntry *pWETE;
2169     register int inside = 1;
2170     register int isInside = 0;
2171
2172     AET->nextWETE = (EdgeTableEntry *)NULL;
2173     pWETE = AET;
2174     AET = AET->next;
2175     while (AET) 
2176     {
2177         if (AET->ClockWise)
2178             isInside++;
2179         else
2180             isInside--;
2181
2182         if ((!inside && !isInside) ||
2183             ( inside &&  isInside)) 
2184         {
2185             pWETE->nextWETE = AET;
2186             pWETE = AET;
2187             inside = !inside;
2188         }
2189         AET = AET->next;
2190     }
2191     pWETE->nextWETE = (EdgeTableEntry *)NULL;
2192 }
2193
2194 /***********************************************************************
2195  *     REGION_InsertionSort
2196  *
2197  *     Just a simple insertion sort using
2198  *     pointers and back pointers to sort the Active
2199  *     Edge Table.
2200  *
2201  */
2202 static BOOL32 REGION_InsertionSort(EdgeTableEntry *AET)
2203 {
2204     EdgeTableEntry *pETEchase;
2205     EdgeTableEntry *pETEinsert;
2206     EdgeTableEntry *pETEchaseBackTMP;
2207     BOOL32 changed = FALSE;
2208
2209     AET = AET->next;
2210     while (AET) 
2211     {
2212         pETEinsert = AET;
2213         pETEchase = AET;
2214         while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
2215             pETEchase = pETEchase->back;
2216
2217         AET = AET->next;
2218         if (pETEchase != pETEinsert) 
2219         {
2220             pETEchaseBackTMP = pETEchase->back;
2221             pETEinsert->back->next = AET;
2222             if (AET)
2223                 AET->back = pETEinsert->back;
2224             pETEinsert->next = pETEchase;
2225             pETEchase->back->next = pETEinsert;
2226             pETEchase->back = pETEinsert;
2227             pETEinsert->back = pETEchaseBackTMP;
2228             changed = TRUE;
2229         }
2230     }
2231     return changed;
2232 }
2233
2234 /***********************************************************************
2235  *     REGION_FreeStorage
2236  *
2237  *     Clean up our act.
2238  */
2239 static void REGION_FreeStorage(ScanLineListBlock *pSLLBlock)
2240 {
2241     ScanLineListBlock   *tmpSLLBlock;
2242
2243     while (pSLLBlock) 
2244     {
2245         tmpSLLBlock = pSLLBlock->next;
2246         HeapFree( SystemHeap, 0, pSLLBlock );
2247         pSLLBlock = tmpSLLBlock;
2248     }
2249 }
2250
2251
2252 /***********************************************************************
2253  *     REGION_PtsToRegion
2254  *
2255  *     Create an array of rectangles from a list of points.
2256  */
2257 static int REGION_PtsToRegion(int numFullPtBlocks, int iCurPtBlock, 
2258                        POINTBLOCK *FirstPtBlock, WINEREGION *reg)
2259 {
2260     RECT32 *rects;
2261     POINT32 *pts;
2262     POINTBLOCK *CurPtBlock;
2263     int i;
2264     RECT32 *extents;
2265     INT32 numRects;
2266  
2267     extents = &reg->extents;
2268  
2269     numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
2270  
2271     if (!(reg->rects = HeapReAlloc( SystemHeap, 0, reg->rects, 
2272                            sizeof(RECT32) * numRects )))
2273         return(0);
2274  
2275     reg->size = numRects;
2276     CurPtBlock = FirstPtBlock;
2277     rects = reg->rects - 1;
2278     numRects = 0;
2279     extents->left = LARGE_COORDINATE,  extents->right = SMALL_COORDINATE;
2280  
2281     for ( ; numFullPtBlocks >= 0; numFullPtBlocks--) {
2282         /* the loop uses 2 points per iteration */
2283         i = NUMPTSTOBUFFER >> 1;
2284         if (!numFullPtBlocks)
2285             i = iCurPtBlock >> 1;
2286         for (pts = CurPtBlock->pts; i--; pts += 2) {
2287             if (pts->x == pts[1].x)
2288                 continue;
2289             if (numRects && pts->x == rects->left && pts->y == rects->bottom &&
2290                 pts[1].x == rects->right &&
2291                 (numRects == 1 || rects[-1].top != rects->top) &&
2292                 (i && pts[2].y > pts[1].y)) {
2293                 rects->bottom = pts[1].y + 1;
2294                 continue;
2295             }
2296             numRects++;
2297             rects++;
2298             rects->left = pts->x;  rects->top = pts->y;
2299             rects->right = pts[1].x;  rects->bottom = pts[1].y + 1;
2300             if (rects->left < extents->left)
2301                 extents->left = rects->left;
2302             if (rects->right > extents->right)
2303                 extents->right = rects->right;
2304         }
2305         CurPtBlock = CurPtBlock->next;
2306     }
2307
2308     if (numRects) {
2309         extents->top = reg->rects->top;
2310         extents->bottom = rects->bottom;
2311     } else {
2312         extents->left = 0;
2313         extents->top = 0;
2314         extents->right = 0;
2315         extents->bottom = 0;
2316     }
2317     reg->numRects = numRects;
2318  
2319     return(TRUE);
2320 }
2321
2322 /***********************************************************************
2323  *           CreatePolyPolygonRgn32    (GDI32.57)
2324  */
2325 HRGN32 WINAPI CreatePolyPolygonRgn32(const POINT32 *Pts, const INT32 *Count, 
2326                       INT32 nbpolygons, INT32 mode)
2327 {
2328     HRGN32 hrgn;
2329     RGNOBJ *obj;
2330     WINEREGION *region;
2331     register EdgeTableEntry *pAET;   /* Active Edge Table       */
2332     register INT32 y;                /* current scanline        */
2333     register int iPts = 0;           /* number of pts in buffer */
2334     register EdgeTableEntry *pWETE;  /* Winding Edge Table Entry*/
2335     register ScanLineList *pSLL;     /* current scanLineList    */
2336     register POINT32 *pts;           /* output buffer           */
2337     EdgeTableEntry *pPrevAET;        /* ptr to previous AET     */
2338     EdgeTable ET;                    /* header node for ET      */
2339     EdgeTableEntry AET;              /* header node for AET     */
2340     EdgeTableEntry *pETEs;           /* EdgeTableEntries pool   */
2341     ScanLineListBlock SLLBlock;      /* header for scanlinelist */
2342     int fixWAET = FALSE;
2343     POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers    */
2344     POINTBLOCK *tmpPtBlock;
2345     int numFullPtBlocks = 0;
2346     INT32 poly, total;
2347
2348     if(!(hrgn = REGION_CreateRegion()))
2349         return 0;
2350     obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
2351     region = obj->rgn;
2352
2353     /* special case a rectangle */
2354
2355     if (((nbpolygons == 1) && ((*Count == 4) ||
2356        ((*Count == 5) && (Pts[4].x == Pts[0].x) && (Pts[4].y == Pts[0].y)))) &&
2357         (((Pts[0].y == Pts[1].y) &&
2358           (Pts[1].x == Pts[2].x) &&
2359           (Pts[2].y == Pts[3].y) &&
2360           (Pts[3].x == Pts[0].x)) ||
2361          ((Pts[0].x == Pts[1].x) &&
2362           (Pts[1].y == Pts[2].y) &&
2363           (Pts[2].x == Pts[3].x) &&
2364           (Pts[3].y == Pts[0].y))))
2365     {
2366         SetRectRgn32( hrgn, MIN(Pts[0].x, Pts[2].x), MIN(Pts[0].y, Pts[2].y), 
2367                             MAX(Pts[0].x, Pts[2].x), MAX(Pts[0].y, Pts[2].y) );
2368         GDI_HEAP_UNLOCK( hrgn );
2369         return hrgn;
2370     }
2371     
2372     for(poly = total = 0; poly < nbpolygons; poly++)
2373         total += Count[poly];
2374     if (! (pETEs = HeapAlloc( SystemHeap, 0, sizeof(EdgeTableEntry) * total )))
2375     {
2376         REGION_DeleteObject( hrgn, obj );
2377         return 0;
2378     }
2379     pts = FirstPtBlock.pts;
2380     REGION_CreateETandAET(Count, nbpolygons, Pts, &ET, &AET, pETEs, &SLLBlock);
2381     pSLL = ET.scanlines.next;
2382     curPtBlock = &FirstPtBlock;
2383  
2384     if (mode != WINDING) {
2385         /*
2386          *  for each scanline
2387          */
2388         for (y = ET.ymin; y < ET.ymax; y++) {
2389             /*
2390              *  Add a new edge to the active edge table when we
2391              *  get to the next edge.
2392              */
2393             if (pSLL != NULL && y == pSLL->scanline) {
2394                 REGION_loadAET(&AET, pSLL->edgelist);
2395                 pSLL = pSLL->next;
2396             }
2397             pPrevAET = &AET;
2398             pAET = AET.next;
2399  
2400             /*
2401              *  for each active edge
2402              */
2403             while (pAET) {
2404                 pts->x = pAET->bres.minor_axis,  pts->y = y;
2405                 pts++, iPts++;
2406  
2407                 /*
2408                  *  send out the buffer
2409                  */
2410                 if (iPts == NUMPTSTOBUFFER) {
2411                     tmpPtBlock = HeapAlloc( SystemHeap, 0, sizeof(POINTBLOCK));
2412                     if(!tmpPtBlock) {
2413                         WARN(region, "Can't alloc tPB\n");
2414                         return 0;
2415                     }
2416                     curPtBlock->next = tmpPtBlock;
2417                     curPtBlock = tmpPtBlock;
2418                     pts = curPtBlock->pts;
2419                     numFullPtBlocks++;
2420                     iPts = 0;
2421                 }
2422                 EVALUATEEDGEEVENODD(pAET, pPrevAET, y);
2423             }
2424             REGION_InsertionSort(&AET);
2425         }
2426     }
2427     else {
2428         /*
2429          *  for each scanline
2430          */
2431         for (y = ET.ymin; y < ET.ymax; y++) {
2432             /*
2433              *  Add a new edge to the active edge table when we
2434              *  get to the next edge.
2435              */
2436             if (pSLL != NULL && y == pSLL->scanline) {
2437                 REGION_loadAET(&AET, pSLL->edgelist);
2438                 REGION_computeWAET(&AET);
2439                 pSLL = pSLL->next;
2440             }
2441             pPrevAET = &AET;
2442             pAET = AET.next;
2443             pWETE = pAET;
2444  
2445             /*
2446              *  for each active edge
2447              */
2448             while (pAET) {
2449                 /*
2450                  *  add to the buffer only those edges that
2451                  *  are in the Winding active edge table.
2452                  */
2453                 if (pWETE == pAET) {
2454                     pts->x = pAET->bres.minor_axis,  pts->y = y;
2455                     pts++, iPts++;
2456  
2457                     /*
2458                      *  send out the buffer
2459                      */
2460                     if (iPts == NUMPTSTOBUFFER) {
2461                         tmpPtBlock = HeapAlloc( SystemHeap, 0,
2462                                                sizeof(POINTBLOCK) );
2463                         if(!tmpPtBlock) {
2464                             WARN(region, "Can't alloc tPB\n");
2465                             return 0;
2466                         }
2467                         curPtBlock->next = tmpPtBlock;
2468                         curPtBlock = tmpPtBlock;
2469                         pts = curPtBlock->pts;
2470                         numFullPtBlocks++;    iPts = 0;
2471                     }
2472                     pWETE = pWETE->nextWETE;
2473                 }
2474                 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
2475             }
2476  
2477             /*
2478              *  recompute the winding active edge table if
2479              *  we just resorted or have exited an edge.
2480              */
2481             if (REGION_InsertionSort(&AET) || fixWAET) {
2482                 REGION_computeWAET(&AET);
2483                 fixWAET = FALSE;
2484             }
2485         }
2486     }
2487     REGION_FreeStorage(SLLBlock.next);  
2488     REGION_PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
2489     for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) {
2490         tmpPtBlock = curPtBlock->next;
2491         HeapFree( SystemHeap, 0, curPtBlock );
2492         curPtBlock = tmpPtBlock;
2493     }
2494     HeapFree( SystemHeap, 0, pETEs );
2495     GDI_HEAP_UNLOCK( hrgn );
2496     return hrgn;
2497 }
2498
2499
2500 /***********************************************************************
2501  *           CreatePolygonRgn16    (GDI.63)
2502  */
2503 HRGN16 WINAPI CreatePolygonRgn16( const POINT16 * points, INT16 count,
2504                                   INT16 mode )
2505 {
2506     return CreatePolyPolygonRgn16( points, &count, 1, mode );
2507 }
2508
2509 /***********************************************************************
2510  *           CreatePolyPolygonRgn16    (GDI.451)
2511  */
2512 HRGN16 WINAPI CreatePolyPolygonRgn16( const POINT16 *points,
2513                 const INT16 *count, INT16 nbpolygons, INT16 mode )
2514 {
2515     HRGN32 hrgn;
2516     int i, npts = 0;
2517     INT32 *count32;
2518     POINT32 *points32;
2519
2520     for (i = 0; i < nbpolygons; i++)
2521         npts += count[i];
2522     points32 = HeapAlloc( SystemHeap, 0, npts * sizeof(POINT32) );
2523     for (i = 0; i < npts; i++)
2524         CONV_POINT16TO32( &(points[i]), &(points32[i]) );
2525
2526     count32 = HeapAlloc( SystemHeap, 0, nbpolygons * sizeof(INT32) );
2527     for (i = 0; i < nbpolygons; i++)
2528         count32[i] = count[i];
2529     hrgn = CreatePolyPolygonRgn32( points32, count32, nbpolygons, mode );
2530     HeapFree( SystemHeap, 0, count32 );
2531     HeapFree( SystemHeap, 0, points32 );
2532     return hrgn;
2533 }
2534
2535 /***********************************************************************
2536  *           CreatePolygonRgn32    (GDI32.58)
2537  */
2538 HRGN32 WINAPI CreatePolygonRgn32( const POINT32 *points, INT32 count,
2539                                   INT32 mode )
2540 {
2541     return CreatePolyPolygonRgn32( points, &count, 1, mode );
2542 }
2543
2544
2545 /***********************************************************************
2546  * GetRandomRgn [GDI32.215]
2547  *
2548  * NOTES
2549  *     This function is UNDOCUMENTED, it isn't even in the header file.
2550  *     I assume that it will return a HRGN32!??
2551  */
2552 HRGN32 WINAPI GetRandomRgn(DWORD dwArg1, DWORD dwArg2, DWORD dwArg3)
2553 {
2554     FIXME (region, "(0x%08lx 0x%08lx 0x%08lx): empty stub!\n",
2555            dwArg1, dwArg2, dwArg3);
2556
2557     return 0;
2558 }