Return 2 in case of a usage error.
[wine.git] / objects / region.c
blobe82db2a65fa31cafe128ca6edcf0b5fcd81091e1
1 /*
2 * GDI region objects. Shamelessly ripped out from the X11 distribution
3 * Thanks for the nice licence.
5 * Copyright 1993, 1994, 1995 Alexandre Julliard
6 * Modifications and additions: Copyright 1998 Huw Davies
7 * 1999 Alex Korobka
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 /************************************************************************
26 Copyright (c) 1987, 1988 X Consortium
28 Permission is hereby granted, free of charge, to any person obtaining a copy
29 of this software and associated documentation files (the "Software"), to deal
30 in the Software without restriction, including without limitation the rights
31 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
32 copies of the Software, and to permit persons to whom the Software is
33 furnished to do so, subject to the following conditions:
35 The above copyright notice and this permission notice shall be included in
36 all copies or substantial portions of the Software.
38 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
40 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
41 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
42 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
43 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
45 Except as contained in this notice, the name of the X Consortium shall not be
46 used in advertising or otherwise to promote the sale, use or other dealings
47 in this Software without prior written authorization from the X Consortium.
50 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
52 All Rights Reserved
54 Permission to use, copy, modify, and distribute this software and its
55 documentation for any purpose and without fee is hereby granted,
56 provided that the above copyright notice appear in all copies and that
57 both that copyright notice and this permission notice appear in
58 supporting documentation, and that the name of Digital not be
59 used in advertising or publicity pertaining to distribution of the
60 software without specific, written prior permission.
62 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
63 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
64 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
65 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
66 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
67 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
68 SOFTWARE.
70 ************************************************************************/
72 * The functions in this file implement the Region abstraction, similar to one
73 * used in the X11 sample server. A Region is simply an area, as the name
74 * implies, and is implemented as a "y-x-banded" array of rectangles. To
75 * explain: Each Region is made up of a certain number of rectangles sorted
76 * by y coordinate first, and then by x coordinate.
78 * Furthermore, the rectangles are banded such that every rectangle with a
79 * given upper-left y coordinate (y1) will have the same lower-right y
80 * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
81 * will span the entire vertical distance of the band. This means that some
82 * areas that could be merged into a taller rectangle will be represented as
83 * several shorter rectangles to account for shorter rectangles to its left
84 * or right but within its "vertical scope".
86 * An added constraint on the rectangles is that they must cover as much
87 * horizontal area as possible. E.g. no two rectangles in a band are allowed
88 * to touch.
90 * Whenever possible, bands will be merged together to cover a greater vertical
91 * distance (and thus reduce the number of rectangles). Two bands can be merged
92 * only if the bottom of one touches the top of the other and they have
93 * rectangles in the same places (of the same width, of course). This maintains
94 * the y-x-banding that's so nice to have...
97 #include <stdarg.h>
98 #include <stdlib.h>
99 #include <string.h>
100 #include "windef.h"
101 #include "winbase.h"
102 #include "wingdi.h"
103 #include "gdi.h"
104 #include "wine/debug.h"
106 WINE_DEFAULT_DEBUG_CHANNEL(region);
108 typedef struct {
109 INT size;
110 INT numRects;
111 RECT *rects;
112 RECT extents;
113 } WINEREGION;
115 /* GDI logical region object */
116 typedef struct
118 GDIOBJHDR header;
119 WINEREGION *rgn;
120 } RGNOBJ;
123 static HGDIOBJ REGION_SelectObject( HGDIOBJ handle, void *obj, HDC hdc );
124 static BOOL REGION_DeleteObject( HGDIOBJ handle, void *obj );
126 static const struct gdi_obj_funcs region_funcs =
128 REGION_SelectObject, /* pSelectObject */
129 NULL, /* pGetObject16 */
130 NULL, /* pGetObjectA */
131 NULL, /* pGetObjectW */
132 NULL, /* pUnrealizeObject */
133 REGION_DeleteObject /* pDeleteObject */
136 /* 1 if two RECTs overlap.
137 * 0 if two RECTs do not overlap.
139 #define EXTENTCHECK(r1, r2) \
140 ((r1)->right > (r2)->left && \
141 (r1)->left < (r2)->right && \
142 (r1)->bottom > (r2)->top && \
143 (r1)->top < (r2)->bottom)
146 * Check to see if there is enough memory in the present region.
149 static inline int xmemcheck(WINEREGION *reg, LPRECT *rect, LPRECT *firstrect ) {
150 if (reg->numRects >= (reg->size - 1)) {
151 *firstrect = HeapReAlloc( GetProcessHeap(), 0, *firstrect, (2 * (sizeof(RECT)) * (reg->size)));
152 if (*firstrect == 0)
153 return 0;
154 reg->size *= 2;
155 *rect = (*firstrect)+reg->numRects;
157 return 1;
160 #define MEMCHECK(reg, rect, firstrect) xmemcheck(reg,&(rect),&(firstrect))
162 #define EMPTY_REGION(pReg) { \
163 (pReg)->numRects = 0; \
164 (pReg)->extents.left = (pReg)->extents.top = 0; \
165 (pReg)->extents.right = (pReg)->extents.bottom = 0; \
168 #define REGION_NOT_EMPTY(pReg) pReg->numRects
170 #define INRECT(r, x, y) \
171 ( ( ((r).right > x)) && \
172 ( ((r).left <= x)) && \
173 ( ((r).bottom > y)) && \
174 ( ((r).top <= y)) )
178 * number of points to buffer before sending them off
179 * to scanlines() : Must be an even number
181 #define NUMPTSTOBUFFER 200
184 * used to allocate buffers for points and link
185 * the buffers together
188 typedef struct _POINTBLOCK {
189 POINT pts[NUMPTSTOBUFFER];
190 struct _POINTBLOCK *next;
191 } POINTBLOCK;
196 * This file contains a few macros to help track
197 * the edge of a filled object. The object is assumed
198 * to be filled in scanline order, and thus the
199 * algorithm used is an extension of Bresenham's line
200 * drawing algorithm which assumes that y is always the
201 * major axis.
202 * Since these pieces of code are the same for any filled shape,
203 * it is more convenient to gather the library in one
204 * place, but since these pieces of code are also in
205 * the inner loops of output primitives, procedure call
206 * overhead is out of the question.
207 * See the author for a derivation if needed.
212 * In scan converting polygons, we want to choose those pixels
213 * which are inside the polygon. Thus, we add .5 to the starting
214 * x coordinate for both left and right edges. Now we choose the
215 * first pixel which is inside the pgon for the left edge and the
216 * first pixel which is outside the pgon for the right edge.
217 * Draw the left pixel, but not the right.
219 * How to add .5 to the starting x coordinate:
220 * If the edge is moving to the right, then subtract dy from the
221 * error term from the general form of the algorithm.
222 * If the edge is moving to the left, then add dy to the error term.
224 * The reason for the difference between edges moving to the left
225 * and edges moving to the right is simple: If an edge is moving
226 * to the right, then we want the algorithm to flip immediately.
227 * If it is moving to the left, then we don't want it to flip until
228 * we traverse an entire pixel.
230 #define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \
231 int dx; /* local storage */ \
233 /* \
234 * if the edge is horizontal, then it is ignored \
235 * and assumed not to be processed. Otherwise, do this stuff. \
236 */ \
237 if ((dy) != 0) { \
238 xStart = (x1); \
239 dx = (x2) - xStart; \
240 if (dx < 0) { \
241 m = dx / (dy); \
242 m1 = m - 1; \
243 incr1 = -2 * dx + 2 * (dy) * m1; \
244 incr2 = -2 * dx + 2 * (dy) * m; \
245 d = 2 * m * (dy) - 2 * dx - 2 * (dy); \
246 } else { \
247 m = dx / (dy); \
248 m1 = m + 1; \
249 incr1 = 2 * dx - 2 * (dy) * m1; \
250 incr2 = 2 * dx - 2 * (dy) * m; \
251 d = -2 * m * (dy) + 2 * dx; \
256 #define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \
257 if (m1 > 0) { \
258 if (d > 0) { \
259 minval += m1; \
260 d += incr1; \
262 else { \
263 minval += m; \
264 d += incr2; \
266 } else {\
267 if (d >= 0) { \
268 minval += m1; \
269 d += incr1; \
271 else { \
272 minval += m; \
273 d += incr2; \
279 * This structure contains all of the information needed
280 * to run the bresenham algorithm.
281 * The variables may be hardcoded into the declarations
282 * instead of using this structure to make use of
283 * register declarations.
285 typedef struct {
286 INT minor_axis; /* minor axis */
287 INT d; /* decision variable */
288 INT m, m1; /* slope and slope+1 */
289 INT incr1, incr2; /* error increments */
290 } BRESINFO;
293 #define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \
294 BRESINITPGON(dmaj, min1, min2, bres.minor_axis, bres.d, \
295 bres.m, bres.m1, bres.incr1, bres.incr2)
297 #define BRESINCRPGONSTRUCT(bres) \
298 BRESINCRPGON(bres.d, bres.minor_axis, bres.m, bres.m1, bres.incr1, bres.incr2)
303 * These are the data structures needed to scan
304 * convert regions. Two different scan conversion
305 * methods are available -- the even-odd method, and
306 * the winding number method.
307 * The even-odd rule states that a point is inside
308 * the polygon if a ray drawn from that point in any
309 * direction will pass through an odd number of
310 * path segments.
311 * By the winding number rule, a point is decided
312 * to be inside the polygon if a ray drawn from that
313 * point in any direction passes through a different
314 * number of clockwise and counter-clockwise path
315 * segments.
317 * These data structures are adapted somewhat from
318 * the algorithm in (Foley/Van Dam) for scan converting
319 * polygons.
320 * The basic algorithm is to start at the top (smallest y)
321 * of the polygon, stepping down to the bottom of
322 * the polygon by incrementing the y coordinate. We
323 * keep a list of edges which the current scanline crosses,
324 * sorted by x. This list is called the Active Edge Table (AET)
325 * As we change the y-coordinate, we update each entry in
326 * in the active edge table to reflect the edges new xcoord.
327 * This list must be sorted at each scanline in case
328 * two edges intersect.
329 * We also keep a data structure known as the Edge Table (ET),
330 * which keeps track of all the edges which the current
331 * scanline has not yet reached. The ET is basically a
332 * list of ScanLineList structures containing a list of
333 * edges which are entered at a given scanline. There is one
334 * ScanLineList per scanline at which an edge is entered.
335 * When we enter a new edge, we move it from the ET to the AET.
337 * From the AET, we can implement the even-odd rule as in
338 * (Foley/Van Dam).
339 * The winding number rule is a little trickier. We also
340 * keep the EdgeTableEntries in the AET linked by the
341 * nextWETE (winding EdgeTableEntry) link. This allows
342 * the edges to be linked just as before for updating
343 * purposes, but only uses the edges linked by the nextWETE
344 * link as edges representing spans of the polygon to
345 * drawn (as with the even-odd rule).
349 * for the winding number rule
351 #define CLOCKWISE 1
352 #define COUNTERCLOCKWISE -1
354 typedef struct _EdgeTableEntry {
355 INT ymax; /* ycoord at which we exit this edge. */
356 BRESINFO bres; /* Bresenham info to run the edge */
357 struct _EdgeTableEntry *next; /* next in the list */
358 struct _EdgeTableEntry *back; /* for insertion sort */
359 struct _EdgeTableEntry *nextWETE; /* for winding num rule */
360 int ClockWise; /* flag for winding number rule */
361 } EdgeTableEntry;
364 typedef struct _ScanLineList{
365 INT scanline; /* the scanline represented */
366 EdgeTableEntry *edgelist; /* header node */
367 struct _ScanLineList *next; /* next in the list */
368 } ScanLineList;
371 typedef struct {
372 INT ymax; /* ymax for the polygon */
373 INT ymin; /* ymin for the polygon */
374 ScanLineList scanlines; /* header node */
375 } EdgeTable;
379 * Here is a struct to help with storage allocation
380 * so we can allocate a big chunk at a time, and then take
381 * pieces from this heap when we need to.
383 #define SLLSPERBLOCK 25
385 typedef struct _ScanLineListBlock {
386 ScanLineList SLLs[SLLSPERBLOCK];
387 struct _ScanLineListBlock *next;
388 } ScanLineListBlock;
393 * a few macros for the inner loops of the fill code where
394 * performance considerations don't allow a procedure call.
396 * Evaluate the given edge at the given scanline.
397 * If the edge has expired, then we leave it and fix up
398 * the active edge table; otherwise, we increment the
399 * x value to be ready for the next scanline.
400 * The winding number rule is in effect, so we must notify
401 * the caller when the edge has been removed so he
402 * can reorder the Winding Active Edge Table.
404 #define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \
405 if (pAET->ymax == y) { /* leaving this edge */ \
406 pPrevAET->next = pAET->next; \
407 pAET = pPrevAET->next; \
408 fixWAET = 1; \
409 if (pAET) \
410 pAET->back = pPrevAET; \
412 else { \
413 BRESINCRPGONSTRUCT(pAET->bres); \
414 pPrevAET = pAET; \
415 pAET = pAET->next; \
421 * Evaluate the given edge at the given scanline.
422 * If the edge has expired, then we leave it and fix up
423 * the active edge table; otherwise, we increment the
424 * x value to be ready for the next scanline.
425 * The even-odd rule is in effect.
427 #define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \
428 if (pAET->ymax == y) { /* leaving this edge */ \
429 pPrevAET->next = pAET->next; \
430 pAET = pPrevAET->next; \
431 if (pAET) \
432 pAET->back = pPrevAET; \
434 else { \
435 BRESINCRPGONSTRUCT(pAET->bres); \
436 pPrevAET = pAET; \
437 pAET = pAET->next; \
441 typedef void (*voidProcp)();
443 /* Note the parameter order is different from the X11 equivalents */
445 static void REGION_CopyRegion(WINEREGION *d, WINEREGION *s);
446 static void REGION_IntersectRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
447 static void REGION_UnionRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
448 static void REGION_SubtractRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
449 static void REGION_XorRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
450 static void REGION_UnionRectWithRegion(const RECT *rect, WINEREGION *rgn);
452 #define RGN_DEFAULT_RECTS 2
455 /***********************************************************************
456 * get_region_type
458 inline static INT get_region_type( const RGNOBJ *obj )
460 switch(obj->rgn->numRects)
462 case 0: return NULLREGION;
463 case 1: return SIMPLEREGION;
464 default: return COMPLEXREGION;
469 /***********************************************************************
470 * REGION_DumpRegion
471 * Outputs the contents of a WINEREGION
473 static void REGION_DumpRegion(WINEREGION *pReg)
475 RECT *pRect, *pRectEnd = pReg->rects + pReg->numRects;
477 TRACE("Region %p: %ld,%ld - %ld,%ld %d rects\n", pReg,
478 pReg->extents.left, pReg->extents.top,
479 pReg->extents.right, pReg->extents.bottom, pReg->numRects);
480 for(pRect = pReg->rects; pRect < pRectEnd; pRect++)
481 TRACE("\t%ld,%ld - %ld,%ld\n", pRect->left, pRect->top,
482 pRect->right, pRect->bottom);
483 return;
487 /***********************************************************************
488 * REGION_AllocWineRegion
489 * Create a new empty WINEREGION.
491 static WINEREGION *REGION_AllocWineRegion( INT n )
493 WINEREGION *pReg;
495 if ((pReg = HeapAlloc(GetProcessHeap(), 0, sizeof( WINEREGION ))))
497 if ((pReg->rects = HeapAlloc(GetProcessHeap(), 0, n * sizeof( RECT ))))
499 pReg->size = n;
500 EMPTY_REGION(pReg);
501 return pReg;
503 HeapFree(GetProcessHeap(), 0, pReg);
505 return NULL;
509 /***********************************************************************
510 * REGION_CreateRegion
511 * Create a new empty region.
513 static HRGN REGION_CreateRegion( INT n )
515 HRGN hrgn;
516 RGNOBJ *obj;
518 if(!(obj = GDI_AllocObject( sizeof(RGNOBJ), REGION_MAGIC, (HGDIOBJ *)&hrgn,
519 &region_funcs ))) return 0;
520 if(!(obj->rgn = REGION_AllocWineRegion(n))) {
521 GDI_FreeObject( hrgn, obj );
522 return 0;
524 GDI_ReleaseObj( hrgn );
525 return hrgn;
528 /***********************************************************************
529 * REGION_DestroyWineRegion
531 static void REGION_DestroyWineRegion( WINEREGION* pReg )
533 HeapFree( GetProcessHeap(), 0, pReg->rects );
534 HeapFree( GetProcessHeap(), 0, pReg );
537 /***********************************************************************
538 * REGION_DeleteObject
540 static BOOL REGION_DeleteObject( HGDIOBJ handle, void *obj )
542 RGNOBJ *rgn = obj;
544 TRACE(" %p\n", handle );
546 REGION_DestroyWineRegion( rgn->rgn );
547 return GDI_FreeObject( handle, obj );
550 /***********************************************************************
551 * REGION_SelectObject
553 static HGDIOBJ REGION_SelectObject( HGDIOBJ handle, void *obj, HDC hdc )
555 return (HGDIOBJ)SelectClipRgn( hdc, handle );
559 /***********************************************************************
560 * OffsetRgn (GDI32.@)
562 INT WINAPI OffsetRgn( HRGN hrgn, INT x, INT y )
564 RGNOBJ * obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
565 INT ret;
567 TRACE("%p %d,%d\n", hrgn, x, y);
569 if (!obj)
570 return ERROR;
572 if(x || y) {
573 int nbox = obj->rgn->numRects;
574 RECT *pbox = obj->rgn->rects;
576 if(nbox) {
577 while(nbox--) {
578 pbox->left += x;
579 pbox->right += x;
580 pbox->top += y;
581 pbox->bottom += y;
582 pbox++;
584 obj->rgn->extents.left += x;
585 obj->rgn->extents.right += x;
586 obj->rgn->extents.top += y;
587 obj->rgn->extents.bottom += y;
590 ret = get_region_type( obj );
591 GDI_ReleaseObj( hrgn );
592 return ret;
596 /***********************************************************************
597 * GetRgnBox (GDI32.@)
599 INT WINAPI GetRgnBox( HRGN hrgn, LPRECT rect )
601 RGNOBJ * obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
602 if (obj)
604 INT ret;
605 rect->left = obj->rgn->extents.left;
606 rect->top = obj->rgn->extents.top;
607 rect->right = obj->rgn->extents.right;
608 rect->bottom = obj->rgn->extents.bottom;
609 TRACE("%p (%ld,%ld-%ld,%ld)\n", hrgn,
610 rect->left, rect->top, rect->right, rect->bottom);
611 ret = get_region_type( obj );
612 GDI_ReleaseObj(hrgn);
613 return ret;
615 return ERROR;
619 /***********************************************************************
620 * CreateRectRgn (GDI32.@)
622 HRGN WINAPI CreateRectRgn(INT left, INT top, INT right, INT bottom)
624 HRGN hrgn;
626 /* Allocate 2 rects by default to reduce the number of reallocs */
628 if (!(hrgn = REGION_CreateRegion(RGN_DEFAULT_RECTS)))
629 return 0;
630 TRACE("%d,%d-%d,%d\n", left, top, right, bottom);
631 SetRectRgn(hrgn, left, top, right, bottom);
632 return hrgn;
636 /***********************************************************************
637 * CreateRectRgnIndirect (GDI32.@)
639 HRGN WINAPI CreateRectRgnIndirect( const RECT* rect )
641 return CreateRectRgn( rect->left, rect->top, rect->right, rect->bottom );
645 /***********************************************************************
646 * SetRectRgn (GDI32.@)
648 * Allows either or both left and top to be greater than right or bottom.
650 BOOL WINAPI SetRectRgn( HRGN hrgn, INT left, INT top,
651 INT right, INT bottom )
653 RGNOBJ * obj;
655 TRACE("%p %d,%d-%d,%d\n", hrgn, left, top, right, bottom );
657 if (!(obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC ))) return FALSE;
659 if (left > right) { INT tmp = left; left = right; right = tmp; }
660 if (top > bottom) { INT tmp = top; top = bottom; bottom = tmp; }
662 if((left != right) && (top != bottom))
664 obj->rgn->rects->left = obj->rgn->extents.left = left;
665 obj->rgn->rects->top = obj->rgn->extents.top = top;
666 obj->rgn->rects->right = obj->rgn->extents.right = right;
667 obj->rgn->rects->bottom = obj->rgn->extents.bottom = bottom;
668 obj->rgn->numRects = 1;
670 else
671 EMPTY_REGION(obj->rgn);
673 GDI_ReleaseObj( hrgn );
674 return TRUE;
678 /***********************************************************************
679 * CreateRoundRectRgn (GDI32.@)
681 HRGN WINAPI CreateRoundRectRgn( INT left, INT top,
682 INT right, INT bottom,
683 INT ellipse_width, INT ellipse_height )
685 RGNOBJ * obj;
686 HRGN hrgn;
687 int asq, bsq, d, xd, yd;
688 RECT rect;
690 /* Make the dimensions sensible */
692 if (left > right) { INT tmp = left; left = right; right = tmp; }
693 if (top > bottom) { INT tmp = top; top = bottom; bottom = tmp; }
695 ellipse_width = abs(ellipse_width);
696 ellipse_height = abs(ellipse_height);
698 /* Check parameters */
700 if (ellipse_width > right-left) ellipse_width = right-left;
701 if (ellipse_height > bottom-top) ellipse_height = bottom-top;
703 /* Check if we can do a normal rectangle instead */
705 if ((ellipse_width < 2) || (ellipse_height < 2))
706 return CreateRectRgn( left, top, right, bottom );
708 /* Create region */
710 d = (ellipse_height < 128) ? ((3 * ellipse_height) >> 2) : 64;
711 if (!(hrgn = REGION_CreateRegion(d))) return 0;
712 if (!(obj = GDI_GetObjPtr( hrgn, REGION_MAGIC ))) return 0;
713 TRACE("(%d,%d-%d,%d %dx%d): ret=%p\n",
714 left, top, right, bottom, ellipse_width, ellipse_height, hrgn );
716 /* Ellipse algorithm, based on an article by K. Porter */
717 /* in DDJ Graphics Programming Column, 8/89 */
719 asq = ellipse_width * ellipse_width / 4; /* a^2 */
720 bsq = ellipse_height * ellipse_height / 4; /* b^2 */
721 d = bsq - asq * ellipse_height / 2 + asq / 4; /* b^2 - a^2b + a^2/4 */
722 xd = 0;
723 yd = asq * ellipse_height; /* 2a^2b */
725 rect.left = left + ellipse_width / 2;
726 rect.right = right - ellipse_width / 2;
728 /* Loop to draw first half of quadrant */
730 while (xd < yd)
732 if (d > 0) /* if nearest pixel is toward the center */
734 /* move toward center */
735 rect.top = top++;
736 rect.bottom = rect.top + 1;
737 REGION_UnionRectWithRegion( &rect, obj->rgn );
738 rect.top = --bottom;
739 rect.bottom = rect.top + 1;
740 REGION_UnionRectWithRegion( &rect, obj->rgn );
741 yd -= 2*asq;
742 d -= yd;
744 rect.left--; /* next horiz point */
745 rect.right++;
746 xd += 2*bsq;
747 d += bsq + xd;
750 /* Loop to draw second half of quadrant */
752 d += (3 * (asq-bsq) / 2 - (xd+yd)) / 2;
753 while (yd >= 0)
755 /* next vertical point */
756 rect.top = top++;
757 rect.bottom = rect.top + 1;
758 REGION_UnionRectWithRegion( &rect, obj->rgn );
759 rect.top = --bottom;
760 rect.bottom = rect.top + 1;
761 REGION_UnionRectWithRegion( &rect, obj->rgn );
762 if (d < 0) /* if nearest pixel is outside ellipse */
764 rect.left--; /* move away from center */
765 rect.right++;
766 xd += 2*bsq;
767 d += xd;
769 yd -= 2*asq;
770 d += asq - yd;
773 /* Add the inside rectangle */
775 if (top <= bottom)
777 rect.top = top;
778 rect.bottom = bottom;
779 REGION_UnionRectWithRegion( &rect, obj->rgn );
781 GDI_ReleaseObj( hrgn );
782 return hrgn;
786 /***********************************************************************
787 * CreateEllipticRgn (GDI32.@)
789 HRGN WINAPI CreateEllipticRgn( INT left, INT top,
790 INT right, INT bottom )
792 return CreateRoundRectRgn( left, top, right, bottom,
793 right-left, bottom-top );
797 /***********************************************************************
798 * CreateEllipticRgnIndirect (GDI32.@)
800 HRGN WINAPI CreateEllipticRgnIndirect( const RECT *rect )
802 return CreateRoundRectRgn( rect->left, rect->top, rect->right,
803 rect->bottom, rect->right - rect->left,
804 rect->bottom - rect->top );
807 /***********************************************************************
808 * GetRegionData (GDI32.@)
810 * MSDN: GetRegionData, Return Values:
812 * "If the function succeeds and dwCount specifies an adequate number of bytes,
813 * the return value is always dwCount. If dwCount is too small or the function
814 * fails, the return value is 0. If lpRgnData is NULL, the return value is the
815 * required number of bytes.
817 * If the function fails, the return value is zero."
819 DWORD WINAPI GetRegionData(HRGN hrgn, DWORD count, LPRGNDATA rgndata)
821 DWORD size;
822 RGNOBJ *obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
824 TRACE(" %p count = %ld, rgndata = %p\n", hrgn, count, rgndata);
826 if(!obj) return 0;
828 size = obj->rgn->numRects * sizeof(RECT);
829 if(count < (size + sizeof(RGNDATAHEADER)) || rgndata == NULL)
831 GDI_ReleaseObj( hrgn );
832 if (rgndata) /* buffer is too small, signal it by return 0 */
833 return 0;
834 else /* user requested buffer size with rgndata NULL */
835 return size + sizeof(RGNDATAHEADER);
838 rgndata->rdh.dwSize = sizeof(RGNDATAHEADER);
839 rgndata->rdh.iType = RDH_RECTANGLES;
840 rgndata->rdh.nCount = obj->rgn->numRects;
841 rgndata->rdh.nRgnSize = size;
842 rgndata->rdh.rcBound.left = obj->rgn->extents.left;
843 rgndata->rdh.rcBound.top = obj->rgn->extents.top;
844 rgndata->rdh.rcBound.right = obj->rgn->extents.right;
845 rgndata->rdh.rcBound.bottom = obj->rgn->extents.bottom;
847 memcpy( rgndata->Buffer, obj->rgn->rects, size );
849 GDI_ReleaseObj( hrgn );
850 return size + sizeof(RGNDATAHEADER);
854 /***********************************************************************
855 * ExtCreateRegion (GDI32.@)
858 HRGN WINAPI ExtCreateRegion( const XFORM* lpXform, DWORD dwCount, const RGNDATA* rgndata)
860 HRGN hrgn;
862 TRACE(" %p %ld %p = ", lpXform, dwCount, rgndata );
864 if( lpXform )
865 WARN("(Xform not implemented - ignored)\n");
867 if( rgndata->rdh.iType != RDH_RECTANGLES )
869 /* FIXME: We can use CreatePolyPolygonRgn() here
870 * for trapezoidal data */
872 WARN("(Unsupported region data)\n");
873 goto fail;
876 if( (hrgn = REGION_CreateRegion( rgndata->rdh.nCount )) )
878 RECT *pCurRect, *pEndRect;
879 RGNOBJ *obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
881 if (obj) {
882 pEndRect = (RECT *)rgndata->Buffer + rgndata->rdh.nCount;
883 for(pCurRect = (RECT *)rgndata->Buffer; pCurRect < pEndRect; pCurRect++)
884 REGION_UnionRectWithRegion( pCurRect, obj->rgn );
885 GDI_ReleaseObj( hrgn );
887 TRACE("%p\n", hrgn );
888 return hrgn;
890 else ERR("Could not get pointer to newborn Region!\n");
892 fail:
893 WARN("Failed\n");
894 return 0;
898 /***********************************************************************
899 * PtInRegion (GDI32.@)
901 BOOL WINAPI PtInRegion( HRGN hrgn, INT x, INT y )
903 RGNOBJ * obj;
904 BOOL ret = FALSE;
906 if ((obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC )))
908 int i;
910 if (obj->rgn->numRects > 0 && INRECT(obj->rgn->extents, x, y))
911 for (i = 0; i < obj->rgn->numRects; i++)
912 if (INRECT (obj->rgn->rects[i], x, y))
914 ret = TRUE;
915 break;
917 GDI_ReleaseObj( hrgn );
919 return ret;
923 /***********************************************************************
924 * RectInRegion (GDI32.@)
926 * Returns TRUE if rect is at least partly inside hrgn
928 BOOL WINAPI RectInRegion( HRGN hrgn, const RECT *rect )
930 RGNOBJ * obj;
931 BOOL ret = FALSE;
933 if ((obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC )))
935 RECT *pCurRect, *pRectEnd;
937 /* this is (just) a useful optimization */
938 if ((obj->rgn->numRects > 0) && EXTENTCHECK(&obj->rgn->extents,
939 rect))
941 for (pCurRect = obj->rgn->rects, pRectEnd = pCurRect +
942 obj->rgn->numRects; pCurRect < pRectEnd; pCurRect++)
944 if (pCurRect->bottom <= rect->top)
945 continue; /* not far enough down yet */
947 if (pCurRect->top >= rect->bottom)
948 break; /* too far down */
950 if (pCurRect->right <= rect->left)
951 continue; /* not far enough over yet */
953 if (pCurRect->left >= rect->right) {
954 continue;
957 ret = TRUE;
958 break;
961 GDI_ReleaseObj(hrgn);
963 return ret;
966 /***********************************************************************
967 * EqualRgn (GDI32.@)
969 BOOL WINAPI EqualRgn( HRGN hrgn1, HRGN hrgn2 )
971 RGNOBJ *obj1, *obj2;
972 BOOL ret = FALSE;
974 if ((obj1 = (RGNOBJ *) GDI_GetObjPtr( hrgn1, REGION_MAGIC )))
976 if ((obj2 = (RGNOBJ *) GDI_GetObjPtr( hrgn2, REGION_MAGIC )))
978 int i;
980 if ( obj1->rgn->numRects != obj2->rgn->numRects ) goto done;
981 if ( obj1->rgn->numRects == 0 )
983 ret = TRUE;
984 goto done;
987 if (obj1->rgn->extents.left != obj2->rgn->extents.left) goto done;
988 if (obj1->rgn->extents.right != obj2->rgn->extents.right) goto done;
989 if (obj1->rgn->extents.top != obj2->rgn->extents.top) goto done;
990 if (obj1->rgn->extents.bottom != obj2->rgn->extents.bottom) goto done;
991 for( i = 0; i < obj1->rgn->numRects; i++ )
993 if (obj1->rgn->rects[i].left != obj2->rgn->rects[i].left) goto done;
994 if (obj1->rgn->rects[i].right != obj2->rgn->rects[i].right) goto done;
995 if (obj1->rgn->rects[i].top != obj2->rgn->rects[i].top) goto done;
996 if (obj1->rgn->rects[i].bottom != obj2->rgn->rects[i].bottom) goto done;
998 ret = TRUE;
999 done:
1000 GDI_ReleaseObj(hrgn2);
1002 GDI_ReleaseObj(hrgn1);
1004 return ret;
1007 /***********************************************************************
1008 * REGION_UnionRectWithRegion
1009 * Adds a rectangle to a WINEREGION
1011 static void REGION_UnionRectWithRegion(const RECT *rect, WINEREGION *rgn)
1013 WINEREGION region;
1015 region.rects = &region.extents;
1016 region.numRects = 1;
1017 region.size = 1;
1018 region.extents = *rect;
1019 REGION_UnionRegion(rgn, rgn, &region);
1023 /***********************************************************************
1024 * REGION_CreateFrameRgn
1026 * Create a region that is a frame around another region.
1027 * Expand all rectangles by +/- x and y, then subtract original region.
1029 BOOL REGION_FrameRgn( HRGN hDest, HRGN hSrc, INT x, INT y )
1031 BOOL bRet;
1032 RGNOBJ *srcObj = (RGNOBJ*) GDI_GetObjPtr( hSrc, REGION_MAGIC );
1034 if (!srcObj) return FALSE;
1035 if (srcObj->rgn->numRects != 0)
1037 RGNOBJ* destObj = (RGNOBJ*) GDI_GetObjPtr( hDest, REGION_MAGIC );
1038 RECT *pRect, *pEndRect;
1039 RECT tempRect;
1041 EMPTY_REGION( destObj->rgn );
1043 pEndRect = srcObj->rgn->rects + srcObj->rgn->numRects;
1044 for(pRect = srcObj->rgn->rects; pRect < pEndRect; pRect++)
1046 tempRect.left = pRect->left - x;
1047 tempRect.top = pRect->top - y;
1048 tempRect.right = pRect->right + x;
1049 tempRect.bottom = pRect->bottom + y;
1050 REGION_UnionRectWithRegion( &tempRect, destObj->rgn );
1052 REGION_SubtractRegion( destObj->rgn, destObj->rgn, srcObj->rgn );
1053 GDI_ReleaseObj ( hDest );
1054 bRet = TRUE;
1056 else
1057 bRet = FALSE;
1058 GDI_ReleaseObj( hSrc );
1059 return bRet;
1063 /***********************************************************************
1064 * CombineRgn (GDI32.@)
1066 * Note: The behavior is correct even if src and dest regions are the same.
1068 INT WINAPI CombineRgn(HRGN hDest, HRGN hSrc1, HRGN hSrc2, INT mode)
1070 RGNOBJ *destObj = (RGNOBJ *) GDI_GetObjPtr( hDest, REGION_MAGIC);
1071 INT result = ERROR;
1073 TRACE(" %p,%p -> %p mode=%x\n", hSrc1, hSrc2, hDest, mode );
1074 if (destObj)
1076 RGNOBJ *src1Obj = (RGNOBJ *) GDI_GetObjPtr( hSrc1, REGION_MAGIC);
1078 if (src1Obj)
1080 TRACE("dump src1Obj:\n");
1081 if(TRACE_ON(region))
1082 REGION_DumpRegion(src1Obj->rgn);
1083 if (mode == RGN_COPY)
1085 REGION_CopyRegion( destObj->rgn, src1Obj->rgn );
1086 result = get_region_type( destObj );
1088 else
1090 RGNOBJ *src2Obj = (RGNOBJ *) GDI_GetObjPtr( hSrc2, REGION_MAGIC);
1092 if (src2Obj)
1094 TRACE("dump src2Obj:\n");
1095 if(TRACE_ON(region))
1096 REGION_DumpRegion(src2Obj->rgn);
1097 switch (mode)
1099 case RGN_AND:
1100 REGION_IntersectRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn);
1101 break;
1102 case RGN_OR:
1103 REGION_UnionRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn );
1104 break;
1105 case RGN_XOR:
1106 REGION_XorRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn );
1107 break;
1108 case RGN_DIFF:
1109 REGION_SubtractRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn );
1110 break;
1112 result = get_region_type( destObj );
1113 GDI_ReleaseObj( hSrc2 );
1116 GDI_ReleaseObj( hSrc1 );
1118 TRACE("dump destObj:\n");
1119 if(TRACE_ON(region))
1120 REGION_DumpRegion(destObj->rgn);
1122 GDI_ReleaseObj( hDest );
1123 } else {
1124 ERR("Invalid rgn=%p\n", hDest);
1126 return result;
1129 /***********************************************************************
1130 * REGION_SetExtents
1131 * Re-calculate the extents of a region
1133 static void REGION_SetExtents (WINEREGION *pReg)
1135 RECT *pRect, *pRectEnd, *pExtents;
1137 if (pReg->numRects == 0)
1139 pReg->extents.left = 0;
1140 pReg->extents.top = 0;
1141 pReg->extents.right = 0;
1142 pReg->extents.bottom = 0;
1143 return;
1146 pExtents = &pReg->extents;
1147 pRect = pReg->rects;
1148 pRectEnd = &pRect[pReg->numRects - 1];
1151 * Since pRect is the first rectangle in the region, it must have the
1152 * smallest top and since pRectEnd is the last rectangle in the region,
1153 * it must have the largest bottom, because of banding. Initialize left and
1154 * right from pRect and pRectEnd, resp., as good things to initialize them
1155 * to...
1157 pExtents->left = pRect->left;
1158 pExtents->top = pRect->top;
1159 pExtents->right = pRectEnd->right;
1160 pExtents->bottom = pRectEnd->bottom;
1162 while (pRect <= pRectEnd)
1164 if (pRect->left < pExtents->left)
1165 pExtents->left = pRect->left;
1166 if (pRect->right > pExtents->right)
1167 pExtents->right = pRect->right;
1168 pRect++;
1172 /***********************************************************************
1173 * REGION_CopyRegion
1175 static void REGION_CopyRegion(WINEREGION *dst, WINEREGION *src)
1177 if (dst != src) /* don't want to copy to itself */
1179 if (dst->size < src->numRects)
1181 if (! (dst->rects = HeapReAlloc( GetProcessHeap(), 0, dst->rects,
1182 src->numRects * sizeof(RECT) )))
1183 return;
1184 dst->size = src->numRects;
1186 dst->numRects = src->numRects;
1187 dst->extents.left = src->extents.left;
1188 dst->extents.top = src->extents.top;
1189 dst->extents.right = src->extents.right;
1190 dst->extents.bottom = src->extents.bottom;
1191 memcpy((char *) dst->rects, (char *) src->rects,
1192 (int) (src->numRects * sizeof(RECT)));
1194 return;
1197 /***********************************************************************
1198 * REGION_Coalesce
1200 * Attempt to merge the rects in the current band with those in the
1201 * previous one. Used only by REGION_RegionOp.
1203 * Results:
1204 * The new index for the previous band.
1206 * Side Effects:
1207 * If coalescing takes place:
1208 * - rectangles in the previous band will have their bottom fields
1209 * altered.
1210 * - pReg->numRects will be decreased.
1213 static INT REGION_Coalesce (
1214 WINEREGION *pReg, /* Region to coalesce */
1215 INT prevStart, /* Index of start of previous band */
1216 INT curStart /* Index of start of current band */
1218 RECT *pPrevRect; /* Current rect in previous band */
1219 RECT *pCurRect; /* Current rect in current band */
1220 RECT *pRegEnd; /* End of region */
1221 INT curNumRects; /* Number of rectangles in current band */
1222 INT prevNumRects; /* Number of rectangles in previous band */
1223 INT bandtop; /* top coordinate for current band */
1225 pRegEnd = &pReg->rects[pReg->numRects];
1227 pPrevRect = &pReg->rects[prevStart];
1228 prevNumRects = curStart - prevStart;
1231 * Figure out how many rectangles are in the current band. Have to do
1232 * this because multiple bands could have been added in REGION_RegionOp
1233 * at the end when one region has been exhausted.
1235 pCurRect = &pReg->rects[curStart];
1236 bandtop = pCurRect->top;
1237 for (curNumRects = 0;
1238 (pCurRect != pRegEnd) && (pCurRect->top == bandtop);
1239 curNumRects++)
1241 pCurRect++;
1244 if (pCurRect != pRegEnd)
1247 * If more than one band was added, we have to find the start
1248 * of the last band added so the next coalescing job can start
1249 * at the right place... (given when multiple bands are added,
1250 * this may be pointless -- see above).
1252 pRegEnd--;
1253 while (pRegEnd[-1].top == pRegEnd->top)
1255 pRegEnd--;
1257 curStart = pRegEnd - pReg->rects;
1258 pRegEnd = pReg->rects + pReg->numRects;
1261 if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
1262 pCurRect -= curNumRects;
1264 * The bands may only be coalesced if the bottom of the previous
1265 * matches the top scanline of the current.
1267 if (pPrevRect->bottom == pCurRect->top)
1270 * Make sure the bands have rects in the same places. This
1271 * assumes that rects have been added in such a way that they
1272 * cover the most area possible. I.e. two rects in a band must
1273 * have some horizontal space between them.
1277 if ((pPrevRect->left != pCurRect->left) ||
1278 (pPrevRect->right != pCurRect->right))
1281 * The bands don't line up so they can't be coalesced.
1283 return (curStart);
1285 pPrevRect++;
1286 pCurRect++;
1287 prevNumRects -= 1;
1288 } while (prevNumRects != 0);
1290 pReg->numRects -= curNumRects;
1291 pCurRect -= curNumRects;
1292 pPrevRect -= curNumRects;
1295 * The bands may be merged, so set the bottom of each rect
1296 * in the previous band to that of the corresponding rect in
1297 * the current band.
1301 pPrevRect->bottom = pCurRect->bottom;
1302 pPrevRect++;
1303 pCurRect++;
1304 curNumRects -= 1;
1305 } while (curNumRects != 0);
1308 * If only one band was added to the region, we have to backup
1309 * curStart to the start of the previous band.
1311 * If more than one band was added to the region, copy the
1312 * other bands down. The assumption here is that the other bands
1313 * came from the same region as the current one and no further
1314 * coalescing can be done on them since it's all been done
1315 * already... curStart is already in the right place.
1317 if (pCurRect == pRegEnd)
1319 curStart = prevStart;
1321 else
1325 *pPrevRect++ = *pCurRect++;
1326 } while (pCurRect != pRegEnd);
1331 return (curStart);
1334 /***********************************************************************
1335 * REGION_RegionOp
1337 * Apply an operation to two regions. Called by REGION_Union,
1338 * REGION_Inverse, REGION_Subtract, REGION_Intersect...
1340 * Results:
1341 * None.
1343 * Side Effects:
1344 * The new region is overwritten.
1346 * Notes:
1347 * The idea behind this function is to view the two regions as sets.
1348 * Together they cover a rectangle of area that this function divides
1349 * into horizontal bands where points are covered only by one region
1350 * or by both. For the first case, the nonOverlapFunc is called with
1351 * each the band and the band's upper and lower extents. For the
1352 * second, the overlapFunc is called to process the entire band. It
1353 * is responsible for clipping the rectangles in the band, though
1354 * this function provides the boundaries.
1355 * At the end of each band, the new region is coalesced, if possible,
1356 * to reduce the number of rectangles in the region.
1359 static void REGION_RegionOp(
1360 WINEREGION *newReg, /* Place to store result */
1361 WINEREGION *reg1, /* First region in operation */
1362 WINEREGION *reg2, /* 2nd region in operation */
1363 void (*overlapFunc)(), /* Function to call for over-lapping bands */
1364 void (*nonOverlap1Func)(), /* Function to call for non-overlapping bands in region 1 */
1365 void (*nonOverlap2Func)() /* Function to call for non-overlapping bands in region 2 */
1367 RECT *r1; /* Pointer into first region */
1368 RECT *r2; /* Pointer into 2d region */
1369 RECT *r1End; /* End of 1st region */
1370 RECT *r2End; /* End of 2d region */
1371 INT ybot; /* Bottom of intersection */
1372 INT ytop; /* Top of intersection */
1373 RECT *oldRects; /* Old rects for newReg */
1374 INT prevBand; /* Index of start of
1375 * previous band in newReg */
1376 INT curBand; /* Index of start of current
1377 * band in newReg */
1378 RECT *r1BandEnd; /* End of current band in r1 */
1379 RECT *r2BandEnd; /* End of current band in r2 */
1380 INT top; /* Top of non-overlapping band */
1381 INT bot; /* Bottom of non-overlapping band */
1384 * Initialization:
1385 * set r1, r2, r1End and r2End appropriately, preserve the important
1386 * parts of the destination region until the end in case it's one of
1387 * the two source regions, then mark the "new" region empty, allocating
1388 * another array of rectangles for it to use.
1390 r1 = reg1->rects;
1391 r2 = reg2->rects;
1392 r1End = r1 + reg1->numRects;
1393 r2End = r2 + reg2->numRects;
1397 * newReg may be one of the src regions so we can't empty it. We keep a
1398 * note of its rects pointer (so that we can free them later), preserve its
1399 * extents and simply set numRects to zero.
1402 oldRects = newReg->rects;
1403 newReg->numRects = 0;
1406 * Allocate a reasonable number of rectangles for the new region. The idea
1407 * is to allocate enough so the individual functions don't need to
1408 * reallocate and copy the array, which is time consuming, yet we don't
1409 * have to worry about using too much memory. I hope to be able to
1410 * nuke the Xrealloc() at the end of this function eventually.
1412 newReg->size = max(reg1->numRects,reg2->numRects) * 2;
1414 if (! (newReg->rects = HeapAlloc( GetProcessHeap(), 0,
1415 sizeof(RECT) * newReg->size )))
1417 newReg->size = 0;
1418 return;
1422 * Initialize ybot and ytop.
1423 * In the upcoming loop, ybot and ytop serve different functions depending
1424 * on whether the band being handled is an overlapping or non-overlapping
1425 * band.
1426 * In the case of a non-overlapping band (only one of the regions
1427 * has points in the band), ybot is the bottom of the most recent
1428 * intersection and thus clips the top of the rectangles in that band.
1429 * ytop is the top of the next intersection between the two regions and
1430 * serves to clip the bottom of the rectangles in the current band.
1431 * For an overlapping band (where the two regions intersect), ytop clips
1432 * the top of the rectangles of both regions and ybot clips the bottoms.
1434 if (reg1->extents.top < reg2->extents.top)
1435 ybot = reg1->extents.top;
1436 else
1437 ybot = reg2->extents.top;
1440 * prevBand serves to mark the start of the previous band so rectangles
1441 * can be coalesced into larger rectangles. qv. miCoalesce, above.
1442 * In the beginning, there is no previous band, so prevBand == curBand
1443 * (curBand is set later on, of course, but the first band will always
1444 * start at index 0). prevBand and curBand must be indices because of
1445 * the possible expansion, and resultant moving, of the new region's
1446 * array of rectangles.
1448 prevBand = 0;
1452 curBand = newReg->numRects;
1455 * This algorithm proceeds one source-band (as opposed to a
1456 * destination band, which is determined by where the two regions
1457 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
1458 * rectangle after the last one in the current band for their
1459 * respective regions.
1461 r1BandEnd = r1;
1462 while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top))
1464 r1BandEnd++;
1467 r2BandEnd = r2;
1468 while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top))
1470 r2BandEnd++;
1474 * First handle the band that doesn't intersect, if any.
1476 * Note that attention is restricted to one band in the
1477 * non-intersecting region at once, so if a region has n
1478 * bands between the current position and the next place it overlaps
1479 * the other, this entire loop will be passed through n times.
1481 if (r1->top < r2->top)
1483 top = max(r1->top,ybot);
1484 bot = min(r1->bottom,r2->top);
1486 if ((top != bot) && (nonOverlap1Func != (void (*)())NULL))
1488 (* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot);
1491 ytop = r2->top;
1493 else if (r2->top < r1->top)
1495 top = max(r2->top,ybot);
1496 bot = min(r2->bottom,r1->top);
1498 if ((top != bot) && (nonOverlap2Func != (void (*)())NULL))
1500 (* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot);
1503 ytop = r1->top;
1505 else
1507 ytop = r1->top;
1511 * If any rectangles got added to the region, try and coalesce them
1512 * with rectangles from the previous band. Note we could just do
1513 * this test in miCoalesce, but some machines incur a not
1514 * inconsiderable cost for function calls, so...
1516 if (newReg->numRects != curBand)
1518 prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1522 * Now see if we've hit an intersecting band. The two bands only
1523 * intersect if ybot > ytop
1525 ybot = min(r1->bottom, r2->bottom);
1526 curBand = newReg->numRects;
1527 if (ybot > ytop)
1529 (* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
1533 if (newReg->numRects != curBand)
1535 prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1539 * If we've finished with a band (bottom == ybot) we skip forward
1540 * in the region to the next band.
1542 if (r1->bottom == ybot)
1544 r1 = r1BandEnd;
1546 if (r2->bottom == ybot)
1548 r2 = r2BandEnd;
1550 } while ((r1 != r1End) && (r2 != r2End));
1553 * Deal with whichever region still has rectangles left.
1555 curBand = newReg->numRects;
1556 if (r1 != r1End)
1558 if (nonOverlap1Func != (void (*)())NULL)
1562 r1BandEnd = r1;
1563 while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top))
1565 r1BandEnd++;
1567 (* nonOverlap1Func) (newReg, r1, r1BandEnd,
1568 max(r1->top,ybot), r1->bottom);
1569 r1 = r1BandEnd;
1570 } while (r1 != r1End);
1573 else if ((r2 != r2End) && (nonOverlap2Func != (void (*)())NULL))
1577 r2BandEnd = r2;
1578 while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top))
1580 r2BandEnd++;
1582 (* nonOverlap2Func) (newReg, r2, r2BandEnd,
1583 max(r2->top,ybot), r2->bottom);
1584 r2 = r2BandEnd;
1585 } while (r2 != r2End);
1588 if (newReg->numRects != curBand)
1590 (void) REGION_Coalesce (newReg, prevBand, curBand);
1594 * A bit of cleanup. To keep regions from growing without bound,
1595 * we shrink the array of rectangles to match the new number of
1596 * rectangles in the region. This never goes to 0, however...
1598 * Only do this stuff if the number of rectangles allocated is more than
1599 * twice the number of rectangles in the region (a simple optimization...).
1601 if ((newReg->numRects < (newReg->size >> 1)) && (newReg->numRects > 2))
1603 if (REGION_NOT_EMPTY(newReg))
1605 RECT *prev_rects = newReg->rects;
1606 newReg->size = newReg->numRects;
1607 newReg->rects = HeapReAlloc( GetProcessHeap(), 0, newReg->rects,
1608 sizeof(RECT) * newReg->size );
1609 if (! newReg->rects)
1610 newReg->rects = prev_rects;
1612 else
1615 * No point in doing the extra work involved in an Xrealloc if
1616 * the region is empty
1618 newReg->size = 1;
1619 HeapFree( GetProcessHeap(), 0, newReg->rects );
1620 newReg->rects = HeapAlloc( GetProcessHeap(), 0, sizeof(RECT) );
1623 HeapFree( GetProcessHeap(), 0, oldRects );
1624 return;
1627 /***********************************************************************
1628 * Region Intersection
1629 ***********************************************************************/
1632 /***********************************************************************
1633 * REGION_IntersectO
1635 * Handle an overlapping band for REGION_Intersect.
1637 * Results:
1638 * None.
1640 * Side Effects:
1641 * Rectangles may be added to the region.
1644 static void REGION_IntersectO(WINEREGION *pReg, RECT *r1, RECT *r1End,
1645 RECT *r2, RECT *r2End, INT top, INT bottom)
1648 INT left, right;
1649 RECT *pNextRect;
1651 pNextRect = &pReg->rects[pReg->numRects];
1653 while ((r1 != r1End) && (r2 != r2End))
1655 left = max(r1->left, r2->left);
1656 right = min(r1->right, r2->right);
1659 * If there's any overlap between the two rectangles, add that
1660 * overlap to the new region.
1661 * There's no need to check for subsumption because the only way
1662 * such a need could arise is if some region has two rectangles
1663 * right next to each other. Since that should never happen...
1665 if (left < right)
1667 MEMCHECK(pReg, pNextRect, pReg->rects);
1668 pNextRect->left = left;
1669 pNextRect->top = top;
1670 pNextRect->right = right;
1671 pNextRect->bottom = bottom;
1672 pReg->numRects += 1;
1673 pNextRect++;
1677 * Need to advance the pointers. Shift the one that extends
1678 * to the right the least, since the other still has a chance to
1679 * overlap with that region's next rectangle, if you see what I mean.
1681 if (r1->right < r2->right)
1683 r1++;
1685 else if (r2->right < r1->right)
1687 r2++;
1689 else
1691 r1++;
1692 r2++;
1695 return;
1698 /***********************************************************************
1699 * REGION_IntersectRegion
1701 static void REGION_IntersectRegion(WINEREGION *newReg, WINEREGION *reg1,
1702 WINEREGION *reg2)
1704 /* check for trivial reject */
1705 if ( (!(reg1->numRects)) || (!(reg2->numRects)) ||
1706 (!EXTENTCHECK(&reg1->extents, &reg2->extents)))
1707 newReg->numRects = 0;
1708 else
1709 REGION_RegionOp (newReg, reg1, reg2,
1710 (voidProcp) REGION_IntersectO, (voidProcp) NULL, (voidProcp) NULL);
1713 * Can't alter newReg's extents before we call miRegionOp because
1714 * it might be one of the source regions and miRegionOp depends
1715 * on the extents of those regions being the same. Besides, this
1716 * way there's no checking against rectangles that will be nuked
1717 * due to coalescing, so we have to examine fewer rectangles.
1719 REGION_SetExtents(newReg);
1722 /***********************************************************************
1723 * Region Union
1724 ***********************************************************************/
1726 /***********************************************************************
1727 * REGION_UnionNonO
1729 * Handle a non-overlapping band for the union operation. Just
1730 * Adds the rectangles into the region. Doesn't have to check for
1731 * subsumption or anything.
1733 * Results:
1734 * None.
1736 * Side Effects:
1737 * pReg->numRects is incremented and the final rectangles overwritten
1738 * with the rectangles we're passed.
1741 static void REGION_UnionNonO (WINEREGION *pReg, RECT *r, RECT *rEnd,
1742 INT top, INT bottom)
1744 RECT *pNextRect;
1746 pNextRect = &pReg->rects[pReg->numRects];
1748 while (r != rEnd)
1750 MEMCHECK(pReg, pNextRect, pReg->rects);
1751 pNextRect->left = r->left;
1752 pNextRect->top = top;
1753 pNextRect->right = r->right;
1754 pNextRect->bottom = bottom;
1755 pReg->numRects += 1;
1756 pNextRect++;
1757 r++;
1759 return;
1762 /***********************************************************************
1763 * REGION_UnionO
1765 * Handle an overlapping band for the union operation. Picks the
1766 * left-most rectangle each time and merges it into the region.
1768 * Results:
1769 * None.
1771 * Side Effects:
1772 * Rectangles are overwritten in pReg->rects and pReg->numRects will
1773 * be changed.
1776 static void REGION_UnionO (WINEREGION *pReg, RECT *r1, RECT *r1End,
1777 RECT *r2, RECT *r2End, INT top, INT bottom)
1779 RECT *pNextRect;
1781 pNextRect = &pReg->rects[pReg->numRects];
1783 #define MERGERECT(r) \
1784 if ((pReg->numRects != 0) && \
1785 (pNextRect[-1].top == top) && \
1786 (pNextRect[-1].bottom == bottom) && \
1787 (pNextRect[-1].right >= r->left)) \
1789 if (pNextRect[-1].right < r->right) \
1791 pNextRect[-1].right = r->right; \
1794 else \
1796 MEMCHECK(pReg, pNextRect, pReg->rects); \
1797 pNextRect->top = top; \
1798 pNextRect->bottom = bottom; \
1799 pNextRect->left = r->left; \
1800 pNextRect->right = r->right; \
1801 pReg->numRects += 1; \
1802 pNextRect += 1; \
1804 r++;
1806 while ((r1 != r1End) && (r2 != r2End))
1808 if (r1->left < r2->left)
1810 MERGERECT(r1);
1812 else
1814 MERGERECT(r2);
1818 if (r1 != r1End)
1822 MERGERECT(r1);
1823 } while (r1 != r1End);
1825 else while (r2 != r2End)
1827 MERGERECT(r2);
1829 return;
1832 /***********************************************************************
1833 * REGION_UnionRegion
1835 static void REGION_UnionRegion(WINEREGION *newReg, WINEREGION *reg1,
1836 WINEREGION *reg2)
1838 /* checks all the simple cases */
1841 * Region 1 and 2 are the same or region 1 is empty
1843 if ( (reg1 == reg2) || (!(reg1->numRects)) )
1845 if (newReg != reg2)
1846 REGION_CopyRegion(newReg, reg2);
1847 return;
1851 * if nothing to union (region 2 empty)
1853 if (!(reg2->numRects))
1855 if (newReg != reg1)
1856 REGION_CopyRegion(newReg, reg1);
1857 return;
1861 * Region 1 completely subsumes region 2
1863 if ((reg1->numRects == 1) &&
1864 (reg1->extents.left <= reg2->extents.left) &&
1865 (reg1->extents.top <= reg2->extents.top) &&
1866 (reg1->extents.right >= reg2->extents.right) &&
1867 (reg1->extents.bottom >= reg2->extents.bottom))
1869 if (newReg != reg1)
1870 REGION_CopyRegion(newReg, reg1);
1871 return;
1875 * Region 2 completely subsumes region 1
1877 if ((reg2->numRects == 1) &&
1878 (reg2->extents.left <= reg1->extents.left) &&
1879 (reg2->extents.top <= reg1->extents.top) &&
1880 (reg2->extents.right >= reg1->extents.right) &&
1881 (reg2->extents.bottom >= reg1->extents.bottom))
1883 if (newReg != reg2)
1884 REGION_CopyRegion(newReg, reg2);
1885 return;
1888 REGION_RegionOp (newReg, reg1, reg2, (voidProcp) REGION_UnionO,
1889 (voidProcp) REGION_UnionNonO, (voidProcp) REGION_UnionNonO);
1891 newReg->extents.left = min(reg1->extents.left, reg2->extents.left);
1892 newReg->extents.top = min(reg1->extents.top, reg2->extents.top);
1893 newReg->extents.right = max(reg1->extents.right, reg2->extents.right);
1894 newReg->extents.bottom = max(reg1->extents.bottom, reg2->extents.bottom);
1897 /***********************************************************************
1898 * Region Subtraction
1899 ***********************************************************************/
1901 /***********************************************************************
1902 * REGION_SubtractNonO1
1904 * Deal with non-overlapping band for subtraction. Any parts from
1905 * region 2 we discard. Anything from region 1 we add to the region.
1907 * Results:
1908 * None.
1910 * Side Effects:
1911 * pReg may be affected.
1914 static void REGION_SubtractNonO1 (WINEREGION *pReg, RECT *r, RECT *rEnd,
1915 INT top, INT bottom)
1917 RECT *pNextRect;
1919 pNextRect = &pReg->rects[pReg->numRects];
1921 while (r != rEnd)
1923 MEMCHECK(pReg, pNextRect, pReg->rects);
1924 pNextRect->left = r->left;
1925 pNextRect->top = top;
1926 pNextRect->right = r->right;
1927 pNextRect->bottom = bottom;
1928 pReg->numRects += 1;
1929 pNextRect++;
1930 r++;
1932 return;
1936 /***********************************************************************
1937 * REGION_SubtractO
1939 * Overlapping band subtraction. x1 is the left-most point not yet
1940 * checked.
1942 * Results:
1943 * None.
1945 * Side Effects:
1946 * pReg may have rectangles added to it.
1949 static void REGION_SubtractO (WINEREGION *pReg, RECT *r1, RECT *r1End,
1950 RECT *r2, RECT *r2End, INT top, INT bottom)
1952 RECT *pNextRect;
1953 INT left;
1955 left = r1->left;
1956 pNextRect = &pReg->rects[pReg->numRects];
1958 while ((r1 != r1End) && (r2 != r2End))
1960 if (r2->right <= left)
1963 * Subtrahend missed the boat: go to next subtrahend.
1965 r2++;
1967 else if (r2->left <= left)
1970 * Subtrahend preceeds minuend: nuke left edge of minuend.
1972 left = r2->right;
1973 if (left >= r1->right)
1976 * Minuend completely covered: advance to next minuend and
1977 * reset left fence to edge of new minuend.
1979 r1++;
1980 if (r1 != r1End)
1981 left = r1->left;
1983 else
1986 * Subtrahend now used up since it doesn't extend beyond
1987 * minuend
1989 r2++;
1992 else if (r2->left < r1->right)
1995 * Left part of subtrahend covers part of minuend: add uncovered
1996 * part of minuend to region and skip to next subtrahend.
1998 MEMCHECK(pReg, pNextRect, pReg->rects);
1999 pNextRect->left = left;
2000 pNextRect->top = top;
2001 pNextRect->right = r2->left;
2002 pNextRect->bottom = bottom;
2003 pReg->numRects += 1;
2004 pNextRect++;
2005 left = r2->right;
2006 if (left >= r1->right)
2009 * Minuend used up: advance to new...
2011 r1++;
2012 if (r1 != r1End)
2013 left = r1->left;
2015 else
2018 * Subtrahend used up
2020 r2++;
2023 else
2026 * Minuend used up: add any remaining piece before advancing.
2028 if (r1->right > left)
2030 MEMCHECK(pReg, pNextRect, pReg->rects);
2031 pNextRect->left = left;
2032 pNextRect->top = top;
2033 pNextRect->right = r1->right;
2034 pNextRect->bottom = bottom;
2035 pReg->numRects += 1;
2036 pNextRect++;
2038 r1++;
2039 left = r1->left;
2044 * Add remaining minuend rectangles to region.
2046 while (r1 != r1End)
2048 MEMCHECK(pReg, pNextRect, pReg->rects);
2049 pNextRect->left = left;
2050 pNextRect->top = top;
2051 pNextRect->right = r1->right;
2052 pNextRect->bottom = bottom;
2053 pReg->numRects += 1;
2054 pNextRect++;
2055 r1++;
2056 if (r1 != r1End)
2058 left = r1->left;
2061 return;
2064 /***********************************************************************
2065 * REGION_SubtractRegion
2067 * Subtract regS from regM and leave the result in regD.
2068 * S stands for subtrahend, M for minuend and D for difference.
2070 * Results:
2071 * TRUE.
2073 * Side Effects:
2074 * regD is overwritten.
2077 static void REGION_SubtractRegion(WINEREGION *regD, WINEREGION *regM,
2078 WINEREGION *regS )
2080 /* check for trivial reject */
2081 if ( (!(regM->numRects)) || (!(regS->numRects)) ||
2082 (!EXTENTCHECK(&regM->extents, &regS->extents)) )
2084 REGION_CopyRegion(regD, regM);
2085 return;
2088 REGION_RegionOp (regD, regM, regS, (voidProcp) REGION_SubtractO,
2089 (voidProcp) REGION_SubtractNonO1, (voidProcp) NULL);
2092 * Can't alter newReg's extents before we call miRegionOp because
2093 * it might be one of the source regions and miRegionOp depends
2094 * on the extents of those regions being the unaltered. Besides, this
2095 * way there's no checking against rectangles that will be nuked
2096 * due to coalescing, so we have to examine fewer rectangles.
2098 REGION_SetExtents (regD);
2101 /***********************************************************************
2102 * REGION_XorRegion
2104 static void REGION_XorRegion(WINEREGION *dr, WINEREGION *sra,
2105 WINEREGION *srb)
2107 WINEREGION *tra, *trb;
2109 if ((! (tra = REGION_AllocWineRegion(sra->numRects + 1))) ||
2110 (! (trb = REGION_AllocWineRegion(srb->numRects + 1))))
2111 return;
2112 REGION_SubtractRegion(tra,sra,srb);
2113 REGION_SubtractRegion(trb,srb,sra);
2114 REGION_UnionRegion(dr,tra,trb);
2115 REGION_DestroyWineRegion(tra);
2116 REGION_DestroyWineRegion(trb);
2117 return;
2120 /**************************************************************************
2122 * Poly Regions
2124 *************************************************************************/
2126 #define LARGE_COORDINATE 0x7fffffff /* FIXME */
2127 #define SMALL_COORDINATE 0x80000000
2129 /***********************************************************************
2130 * REGION_InsertEdgeInET
2132 * Insert the given edge into the edge table.
2133 * First we must find the correct bucket in the
2134 * Edge table, then find the right slot in the
2135 * bucket. Finally, we can insert it.
2138 static void REGION_InsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE,
2139 INT scanline, ScanLineListBlock **SLLBlock, INT *iSLLBlock)
2142 EdgeTableEntry *start, *prev;
2143 ScanLineList *pSLL, *pPrevSLL;
2144 ScanLineListBlock *tmpSLLBlock;
2147 * find the right bucket to put the edge into
2149 pPrevSLL = &ET->scanlines;
2150 pSLL = pPrevSLL->next;
2151 while (pSLL && (pSLL->scanline < scanline))
2153 pPrevSLL = pSLL;
2154 pSLL = pSLL->next;
2158 * reassign pSLL (pointer to ScanLineList) if necessary
2160 if ((!pSLL) || (pSLL->scanline > scanline))
2162 if (*iSLLBlock > SLLSPERBLOCK-1)
2164 tmpSLLBlock = HeapAlloc( GetProcessHeap(), 0, sizeof(ScanLineListBlock));
2165 if(!tmpSLLBlock)
2167 WARN("Can't alloc SLLB\n");
2168 return;
2170 (*SLLBlock)->next = tmpSLLBlock;
2171 tmpSLLBlock->next = (ScanLineListBlock *)NULL;
2172 *SLLBlock = tmpSLLBlock;
2173 *iSLLBlock = 0;
2175 pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
2177 pSLL->next = pPrevSLL->next;
2178 pSLL->edgelist = (EdgeTableEntry *)NULL;
2179 pPrevSLL->next = pSLL;
2181 pSLL->scanline = scanline;
2184 * now insert the edge in the right bucket
2186 prev = (EdgeTableEntry *)NULL;
2187 start = pSLL->edgelist;
2188 while (start && (start->bres.minor_axis < ETE->bres.minor_axis))
2190 prev = start;
2191 start = start->next;
2193 ETE->next = start;
2195 if (prev)
2196 prev->next = ETE;
2197 else
2198 pSLL->edgelist = ETE;
2201 /***********************************************************************
2202 * REGION_CreateEdgeTable
2204 * This routine creates the edge table for
2205 * scan converting polygons.
2206 * The Edge Table (ET) looks like:
2208 * EdgeTable
2209 * --------
2210 * | ymax | ScanLineLists
2211 * |scanline|-->------------>-------------->...
2212 * -------- |scanline| |scanline|
2213 * |edgelist| |edgelist|
2214 * --------- ---------
2215 * | |
2216 * | |
2217 * V V
2218 * list of ETEs list of ETEs
2220 * where ETE is an EdgeTableEntry data structure,
2221 * and there is one ScanLineList per scanline at
2222 * which an edge is initially entered.
2225 static void REGION_CreateETandAET(const INT *Count, INT nbpolygons,
2226 const POINT *pts, EdgeTable *ET, EdgeTableEntry *AET,
2227 EdgeTableEntry *pETEs, ScanLineListBlock *pSLLBlock)
2229 const POINT *top, *bottom;
2230 const POINT *PrevPt, *CurrPt, *EndPt;
2231 INT poly, count;
2232 int iSLLBlock = 0;
2233 int dy;
2237 * initialize the Active Edge Table
2239 AET->next = (EdgeTableEntry *)NULL;
2240 AET->back = (EdgeTableEntry *)NULL;
2241 AET->nextWETE = (EdgeTableEntry *)NULL;
2242 AET->bres.minor_axis = SMALL_COORDINATE;
2245 * initialize the Edge Table.
2247 ET->scanlines.next = (ScanLineList *)NULL;
2248 ET->ymax = SMALL_COORDINATE;
2249 ET->ymin = LARGE_COORDINATE;
2250 pSLLBlock->next = (ScanLineListBlock *)NULL;
2252 EndPt = pts - 1;
2253 for(poly = 0; poly < nbpolygons; poly++)
2255 count = Count[poly];
2256 EndPt += count;
2257 if(count < 2)
2258 continue;
2260 PrevPt = EndPt;
2263 * for each vertex in the array of points.
2264 * In this loop we are dealing with two vertices at
2265 * a time -- these make up one edge of the polygon.
2267 while (count--)
2269 CurrPt = pts++;
2272 * find out which point is above and which is below.
2274 if (PrevPt->y > CurrPt->y)
2276 bottom = PrevPt, top = CurrPt;
2277 pETEs->ClockWise = 0;
2279 else
2281 bottom = CurrPt, top = PrevPt;
2282 pETEs->ClockWise = 1;
2286 * don't add horizontal edges to the Edge table.
2288 if (bottom->y != top->y)
2290 pETEs->ymax = bottom->y-1;
2291 /* -1 so we don't get last scanline */
2294 * initialize integer edge algorithm
2296 dy = bottom->y - top->y;
2297 BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres);
2299 REGION_InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock,
2300 &iSLLBlock);
2302 if (PrevPt->y > ET->ymax)
2303 ET->ymax = PrevPt->y;
2304 if (PrevPt->y < ET->ymin)
2305 ET->ymin = PrevPt->y;
2306 pETEs++;
2309 PrevPt = CurrPt;
2314 /***********************************************************************
2315 * REGION_loadAET
2317 * This routine moves EdgeTableEntries from the
2318 * EdgeTable into the Active Edge Table,
2319 * leaving them sorted by smaller x coordinate.
2322 static void REGION_loadAET(EdgeTableEntry *AET, EdgeTableEntry *ETEs)
2324 EdgeTableEntry *pPrevAET;
2325 EdgeTableEntry *tmp;
2327 pPrevAET = AET;
2328 AET = AET->next;
2329 while (ETEs)
2331 while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis))
2333 pPrevAET = AET;
2334 AET = AET->next;
2336 tmp = ETEs->next;
2337 ETEs->next = AET;
2338 if (AET)
2339 AET->back = ETEs;
2340 ETEs->back = pPrevAET;
2341 pPrevAET->next = ETEs;
2342 pPrevAET = ETEs;
2344 ETEs = tmp;
2348 /***********************************************************************
2349 * REGION_computeWAET
2351 * This routine links the AET by the
2352 * nextWETE (winding EdgeTableEntry) link for
2353 * use by the winding number rule. The final
2354 * Active Edge Table (AET) might look something
2355 * like:
2357 * AET
2358 * ---------- --------- ---------
2359 * |ymax | |ymax | |ymax |
2360 * | ... | |... | |... |
2361 * |next |->|next |->|next |->...
2362 * |nextWETE| |nextWETE| |nextWETE|
2363 * --------- --------- ^--------
2364 * | | |
2365 * V-------------------> V---> ...
2368 static void REGION_computeWAET(EdgeTableEntry *AET)
2370 register EdgeTableEntry *pWETE;
2371 register int inside = 1;
2372 register int isInside = 0;
2374 AET->nextWETE = (EdgeTableEntry *)NULL;
2375 pWETE = AET;
2376 AET = AET->next;
2377 while (AET)
2379 if (AET->ClockWise)
2380 isInside++;
2381 else
2382 isInside--;
2384 if ((!inside && !isInside) ||
2385 ( inside && isInside))
2387 pWETE->nextWETE = AET;
2388 pWETE = AET;
2389 inside = !inside;
2391 AET = AET->next;
2393 pWETE->nextWETE = (EdgeTableEntry *)NULL;
2396 /***********************************************************************
2397 * REGION_InsertionSort
2399 * Just a simple insertion sort using
2400 * pointers and back pointers to sort the Active
2401 * Edge Table.
2404 static BOOL REGION_InsertionSort(EdgeTableEntry *AET)
2406 EdgeTableEntry *pETEchase;
2407 EdgeTableEntry *pETEinsert;
2408 EdgeTableEntry *pETEchaseBackTMP;
2409 BOOL changed = FALSE;
2411 AET = AET->next;
2412 while (AET)
2414 pETEinsert = AET;
2415 pETEchase = AET;
2416 while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
2417 pETEchase = pETEchase->back;
2419 AET = AET->next;
2420 if (pETEchase != pETEinsert)
2422 pETEchaseBackTMP = pETEchase->back;
2423 pETEinsert->back->next = AET;
2424 if (AET)
2425 AET->back = pETEinsert->back;
2426 pETEinsert->next = pETEchase;
2427 pETEchase->back->next = pETEinsert;
2428 pETEchase->back = pETEinsert;
2429 pETEinsert->back = pETEchaseBackTMP;
2430 changed = TRUE;
2433 return changed;
2436 /***********************************************************************
2437 * REGION_FreeStorage
2439 * Clean up our act.
2441 static void REGION_FreeStorage(ScanLineListBlock *pSLLBlock)
2443 ScanLineListBlock *tmpSLLBlock;
2445 while (pSLLBlock)
2447 tmpSLLBlock = pSLLBlock->next;
2448 HeapFree( GetProcessHeap(), 0, pSLLBlock );
2449 pSLLBlock = tmpSLLBlock;
2454 /***********************************************************************
2455 * REGION_PtsToRegion
2457 * Create an array of rectangles from a list of points.
2459 static int REGION_PtsToRegion(int numFullPtBlocks, int iCurPtBlock,
2460 POINTBLOCK *FirstPtBlock, WINEREGION *reg)
2462 RECT *rects;
2463 POINT *pts;
2464 POINTBLOCK *CurPtBlock;
2465 int i;
2466 RECT *extents;
2467 INT numRects;
2469 extents = &reg->extents;
2471 numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
2473 if (!(reg->rects = HeapReAlloc( GetProcessHeap(), 0, reg->rects,
2474 sizeof(RECT) * numRects )))
2475 return(0);
2477 reg->size = numRects;
2478 CurPtBlock = FirstPtBlock;
2479 rects = reg->rects - 1;
2480 numRects = 0;
2481 extents->left = LARGE_COORDINATE, extents->right = SMALL_COORDINATE;
2483 for ( ; numFullPtBlocks >= 0; numFullPtBlocks--) {
2484 /* the loop uses 2 points per iteration */
2485 i = NUMPTSTOBUFFER >> 1;
2486 if (!numFullPtBlocks)
2487 i = iCurPtBlock >> 1;
2488 for (pts = CurPtBlock->pts; i--; pts += 2) {
2489 if (pts->x == pts[1].x)
2490 continue;
2491 if (numRects && pts->x == rects->left && pts->y == rects->bottom &&
2492 pts[1].x == rects->right &&
2493 (numRects == 1 || rects[-1].top != rects->top) &&
2494 (i && pts[2].y > pts[1].y)) {
2495 rects->bottom = pts[1].y + 1;
2496 continue;
2498 numRects++;
2499 rects++;
2500 rects->left = pts->x; rects->top = pts->y;
2501 rects->right = pts[1].x; rects->bottom = pts[1].y + 1;
2502 if (rects->left < extents->left)
2503 extents->left = rects->left;
2504 if (rects->right > extents->right)
2505 extents->right = rects->right;
2507 CurPtBlock = CurPtBlock->next;
2510 if (numRects) {
2511 extents->top = reg->rects->top;
2512 extents->bottom = rects->bottom;
2513 } else {
2514 extents->left = 0;
2515 extents->top = 0;
2516 extents->right = 0;
2517 extents->bottom = 0;
2519 reg->numRects = numRects;
2521 return(TRUE);
2524 /***********************************************************************
2525 * CreatePolyPolygonRgn (GDI32.@)
2527 HRGN WINAPI CreatePolyPolygonRgn(const POINT *Pts, const INT *Count,
2528 INT nbpolygons, INT mode)
2530 HRGN hrgn;
2531 RGNOBJ *obj;
2532 WINEREGION *region;
2533 register EdgeTableEntry *pAET; /* Active Edge Table */
2534 register INT y; /* current scanline */
2535 register int iPts = 0; /* number of pts in buffer */
2536 register EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/
2537 register ScanLineList *pSLL; /* current scanLineList */
2538 register POINT *pts; /* output buffer */
2539 EdgeTableEntry *pPrevAET; /* ptr to previous AET */
2540 EdgeTable ET; /* header node for ET */
2541 EdgeTableEntry AET; /* header node for AET */
2542 EdgeTableEntry *pETEs; /* EdgeTableEntries pool */
2543 ScanLineListBlock SLLBlock; /* header for scanlinelist */
2544 int fixWAET = FALSE;
2545 POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
2546 POINTBLOCK *tmpPtBlock;
2547 int numFullPtBlocks = 0;
2548 INT poly, total;
2550 if(!(hrgn = REGION_CreateRegion(nbpolygons)))
2551 return 0;
2552 obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
2553 region = obj->rgn;
2555 /* special case a rectangle */
2557 if (((nbpolygons == 1) && ((*Count == 4) ||
2558 ((*Count == 5) && (Pts[4].x == Pts[0].x) && (Pts[4].y == Pts[0].y)))) &&
2559 (((Pts[0].y == Pts[1].y) &&
2560 (Pts[1].x == Pts[2].x) &&
2561 (Pts[2].y == Pts[3].y) &&
2562 (Pts[3].x == Pts[0].x)) ||
2563 ((Pts[0].x == Pts[1].x) &&
2564 (Pts[1].y == Pts[2].y) &&
2565 (Pts[2].x == Pts[3].x) &&
2566 (Pts[3].y == Pts[0].y))))
2568 SetRectRgn( hrgn, min(Pts[0].x, Pts[2].x), min(Pts[0].y, Pts[2].y),
2569 max(Pts[0].x, Pts[2].x), max(Pts[0].y, Pts[2].y) );
2570 GDI_ReleaseObj( hrgn );
2571 return hrgn;
2574 for(poly = total = 0; poly < nbpolygons; poly++)
2575 total += Count[poly];
2576 if (! (pETEs = HeapAlloc( GetProcessHeap(), 0, sizeof(EdgeTableEntry) * total )))
2578 REGION_DeleteObject( hrgn, obj );
2579 return 0;
2581 pts = FirstPtBlock.pts;
2582 REGION_CreateETandAET(Count, nbpolygons, Pts, &ET, &AET, pETEs, &SLLBlock);
2583 pSLL = ET.scanlines.next;
2584 curPtBlock = &FirstPtBlock;
2586 if (mode != WINDING) {
2588 * for each scanline
2590 for (y = ET.ymin; y < ET.ymax; y++) {
2592 * Add a new edge to the active edge table when we
2593 * get to the next edge.
2595 if (pSLL != NULL && y == pSLL->scanline) {
2596 REGION_loadAET(&AET, pSLL->edgelist);
2597 pSLL = pSLL->next;
2599 pPrevAET = &AET;
2600 pAET = AET.next;
2603 * for each active edge
2605 while (pAET) {
2606 pts->x = pAET->bres.minor_axis, pts->y = y;
2607 pts++, iPts++;
2610 * send out the buffer
2612 if (iPts == NUMPTSTOBUFFER) {
2613 tmpPtBlock = HeapAlloc( GetProcessHeap(), 0, sizeof(POINTBLOCK));
2614 if(!tmpPtBlock) {
2615 WARN("Can't alloc tPB\n");
2616 return 0;
2618 curPtBlock->next = tmpPtBlock;
2619 curPtBlock = tmpPtBlock;
2620 pts = curPtBlock->pts;
2621 numFullPtBlocks++;
2622 iPts = 0;
2624 EVALUATEEDGEEVENODD(pAET, pPrevAET, y);
2626 REGION_InsertionSort(&AET);
2629 else {
2631 * for each scanline
2633 for (y = ET.ymin; y < ET.ymax; y++) {
2635 * Add a new edge to the active edge table when we
2636 * get to the next edge.
2638 if (pSLL != NULL && y == pSLL->scanline) {
2639 REGION_loadAET(&AET, pSLL->edgelist);
2640 REGION_computeWAET(&AET);
2641 pSLL = pSLL->next;
2643 pPrevAET = &AET;
2644 pAET = AET.next;
2645 pWETE = pAET;
2648 * for each active edge
2650 while (pAET) {
2652 * add to the buffer only those edges that
2653 * are in the Winding active edge table.
2655 if (pWETE == pAET) {
2656 pts->x = pAET->bres.minor_axis, pts->y = y;
2657 pts++, iPts++;
2660 * send out the buffer
2662 if (iPts == NUMPTSTOBUFFER) {
2663 tmpPtBlock = HeapAlloc( GetProcessHeap(), 0,
2664 sizeof(POINTBLOCK) );
2665 if(!tmpPtBlock) {
2666 WARN("Can't alloc tPB\n");
2667 REGION_DeleteObject( hrgn, obj );
2668 return 0;
2670 curPtBlock->next = tmpPtBlock;
2671 curPtBlock = tmpPtBlock;
2672 pts = curPtBlock->pts;
2673 numFullPtBlocks++; iPts = 0;
2675 pWETE = pWETE->nextWETE;
2677 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
2681 * recompute the winding active edge table if
2682 * we just resorted or have exited an edge.
2684 if (REGION_InsertionSort(&AET) || fixWAET) {
2685 REGION_computeWAET(&AET);
2686 fixWAET = FALSE;
2690 REGION_FreeStorage(SLLBlock.next);
2691 REGION_PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
2693 for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) {
2694 tmpPtBlock = curPtBlock->next;
2695 HeapFree( GetProcessHeap(), 0, curPtBlock );
2696 curPtBlock = tmpPtBlock;
2698 HeapFree( GetProcessHeap(), 0, pETEs );
2699 GDI_ReleaseObj( hrgn );
2700 return hrgn;
2704 /***********************************************************************
2705 * CreatePolygonRgn (GDI32.@)
2707 HRGN WINAPI CreatePolygonRgn( const POINT *points, INT count,
2708 INT mode )
2710 return CreatePolyPolygonRgn( points, &count, 1, mode );