Added a few more files to the install list.
[wine/gsoc_dplay.git] / objects / region.c
blob1c980f0cf9721dbf31e0c2055bc8d95fe76f159b
1 /*
2 * GDI region objects. Shamelessly ripped out from the X11 distribution
3 * Thanks for the nice licence.
5 * Copyright 1993, 1994, 1995 Alexandre Julliard
6 * Modifications and additions: Copyright 1998 Huw Davies
7 * 1999 Alex Korobka
9 */
11 /************************************************************************
13 Copyright (c) 1987, 1988 X Consortium
15 Permission is hereby granted, free of charge, to any person obtaining a copy
16 of this software and associated documentation files (the "Software"), to deal
17 in the Software without restriction, including without limitation the rights
18 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19 copies of the Software, and to permit persons to whom the Software is
20 furnished to do so, subject to the following conditions:
22 The above copyright notice and this permission notice shall be included in
23 all copies or substantial portions of the Software.
25 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
29 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 Except as contained in this notice, the name of the X Consortium shall not be
33 used in advertising or otherwise to promote the sale, use or other dealings
34 in this Software without prior written authorization from the X Consortium.
37 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
39 All Rights Reserved
41 Permission to use, copy, modify, and distribute this software and its
42 documentation for any purpose and without fee is hereby granted,
43 provided that the above copyright notice appear in all copies and that
44 both that copyright notice and this permission notice appear in
45 supporting documentation, and that the name of Digital not be
46 used in advertising or publicity pertaining to distribution of the
47 software without specific, written prior permission.
49 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
50 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
51 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
52 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
53 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
54 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
55 SOFTWARE.
57 ************************************************************************/
59 * The functions in this file implement the Region abstraction, similar to one
60 * used in the X11 sample server. A Region is simply an area, as the name
61 * implies, and is implemented as a "y-x-banded" array of rectangles. To
62 * explain: Each Region is made up of a certain number of rectangles sorted
63 * by y coordinate first, and then by x coordinate.
65 * Furthermore, the rectangles are banded such that every rectangle with a
66 * given upper-left y coordinate (y1) will have the same lower-right y
67 * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
68 * will span the entire vertical distance of the band. This means that some
69 * areas that could be merged into a taller rectangle will be represented as
70 * several shorter rectangles to account for shorter rectangles to its left
71 * or right but within its "vertical scope".
73 * An added constraint on the rectangles is that they must cover as much
74 * horizontal area as possible. E.g. no two rectangles in a band are allowed
75 * to touch.
77 * Whenever possible, bands will be merged together to cover a greater vertical
78 * distance (and thus reduce the number of rectangles). Two bands can be merged
79 * only if the bottom of one touches the top of the other and they have
80 * rectangles in the same places (of the same width, of course). This maintains
81 * the y-x-banding that's so nice to have...
84 #include <stdlib.h>
85 #include <string.h>
86 #include "windef.h"
87 #include "wingdi.h"
88 #include "debugtools.h"
89 #include "region.h"
90 #include "heap.h"
91 #include "gdi.h"
93 DEFAULT_DEBUG_CHANNEL(region);
95 /* 1 if two RECTs overlap.
96 * 0 if two RECTs do not overlap.
98 #define EXTENTCHECK(r1, r2) \
99 ((r1)->right > (r2)->left && \
100 (r1)->left < (r2)->right && \
101 (r1)->bottom > (r2)->top && \
102 (r1)->top < (r2)->bottom)
105 * Check to see if there is enough memory in the present region.
108 static inline int xmemcheck(WINEREGION *reg, LPRECT *rect, LPRECT *firstrect ) {
109 if (reg->numRects >= (reg->size - 1)) {
110 *firstrect = HeapReAlloc( GetProcessHeap(), 0, *firstrect, (2 * (sizeof(RECT)) * (reg->size)));
111 if (*firstrect == 0)
112 return 0;
113 reg->size *= 2;
114 *rect = (*firstrect)+reg->numRects;
116 return 1;
119 #define MEMCHECK(reg, rect, firstrect) xmemcheck(reg,&(rect),&(firstrect))
121 #define EMPTY_REGION(pReg) { \
122 (pReg)->numRects = 0; \
123 (pReg)->extents.left = (pReg)->extents.top = 0; \
124 (pReg)->extents.right = (pReg)->extents.bottom = 0; \
125 (pReg)->type = NULLREGION; \
128 #define REGION_NOT_EMPTY(pReg) pReg->numRects
130 #define INRECT(r, x, y) \
131 ( ( ((r).right > x)) && \
132 ( ((r).left <= x)) && \
133 ( ((r).bottom > y)) && \
134 ( ((r).top <= y)) )
138 * number of points to buffer before sending them off
139 * to scanlines() : Must be an even number
141 #define NUMPTSTOBUFFER 200
144 * used to allocate buffers for points and link
145 * the buffers together
148 typedef struct _POINTBLOCK {
149 POINT pts[NUMPTSTOBUFFER];
150 struct _POINTBLOCK *next;
151 } POINTBLOCK;
156 * This file contains a few macros to help track
157 * the edge of a filled object. The object is assumed
158 * to be filled in scanline order, and thus the
159 * algorithm used is an extension of Bresenham's line
160 * drawing algorithm which assumes that y is always the
161 * major axis.
162 * Since these pieces of code are the same for any filled shape,
163 * it is more convenient to gather the library in one
164 * place, but since these pieces of code are also in
165 * the inner loops of output primitives, procedure call
166 * overhead is out of the question.
167 * See the author for a derivation if needed.
172 * In scan converting polygons, we want to choose those pixels
173 * which are inside the polygon. Thus, we add .5 to the starting
174 * x coordinate for both left and right edges. Now we choose the
175 * first pixel which is inside the pgon for the left edge and the
176 * first pixel which is outside the pgon for the right edge.
177 * Draw the left pixel, but not the right.
179 * How to add .5 to the starting x coordinate:
180 * If the edge is moving to the right, then subtract dy from the
181 * error term from the general form of the algorithm.
182 * If the edge is moving to the left, then add dy to the error term.
184 * The reason for the difference between edges moving to the left
185 * and edges moving to the right is simple: If an edge is moving
186 * to the right, then we want the algorithm to flip immediately.
187 * If it is moving to the left, then we don't want it to flip until
188 * we traverse an entire pixel.
190 #define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \
191 int dx; /* local storage */ \
193 /* \
194 * if the edge is horizontal, then it is ignored \
195 * and assumed not to be processed. Otherwise, do this stuff. \
196 */ \
197 if ((dy) != 0) { \
198 xStart = (x1); \
199 dx = (x2) - xStart; \
200 if (dx < 0) { \
201 m = dx / (dy); \
202 m1 = m - 1; \
203 incr1 = -2 * dx + 2 * (dy) * m1; \
204 incr2 = -2 * dx + 2 * (dy) * m; \
205 d = 2 * m * (dy) - 2 * dx - 2 * (dy); \
206 } else { \
207 m = dx / (dy); \
208 m1 = m + 1; \
209 incr1 = 2 * dx - 2 * (dy) * m1; \
210 incr2 = 2 * dx - 2 * (dy) * m; \
211 d = -2 * m * (dy) + 2 * dx; \
216 #define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \
217 if (m1 > 0) { \
218 if (d > 0) { \
219 minval += m1; \
220 d += incr1; \
222 else { \
223 minval += m; \
224 d += incr2; \
226 } else {\
227 if (d >= 0) { \
228 minval += m1; \
229 d += incr1; \
231 else { \
232 minval += m; \
233 d += incr2; \
239 * This structure contains all of the information needed
240 * to run the bresenham algorithm.
241 * The variables may be hardcoded into the declarations
242 * instead of using this structure to make use of
243 * register declarations.
245 typedef struct {
246 INT minor_axis; /* minor axis */
247 INT d; /* decision variable */
248 INT m, m1; /* slope and slope+1 */
249 INT incr1, incr2; /* error increments */
250 } BRESINFO;
253 #define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \
254 BRESINITPGON(dmaj, min1, min2, bres.minor_axis, bres.d, \
255 bres.m, bres.m1, bres.incr1, bres.incr2)
257 #define BRESINCRPGONSTRUCT(bres) \
258 BRESINCRPGON(bres.d, bres.minor_axis, bres.m, bres.m1, bres.incr1, bres.incr2)
263 * These are the data structures needed to scan
264 * convert regions. Two different scan conversion
265 * methods are available -- the even-odd method, and
266 * the winding number method.
267 * The even-odd rule states that a point is inside
268 * the polygon if a ray drawn from that point in any
269 * direction will pass through an odd number of
270 * path segments.
271 * By the winding number rule, a point is decided
272 * to be inside the polygon if a ray drawn from that
273 * point in any direction passes through a different
274 * number of clockwise and counter-clockwise path
275 * segments.
277 * These data structures are adapted somewhat from
278 * the algorithm in (Foley/Van Dam) for scan converting
279 * polygons.
280 * The basic algorithm is to start at the top (smallest y)
281 * of the polygon, stepping down to the bottom of
282 * the polygon by incrementing the y coordinate. We
283 * keep a list of edges which the current scanline crosses,
284 * sorted by x. This list is called the Active Edge Table (AET)
285 * As we change the y-coordinate, we update each entry in
286 * in the active edge table to reflect the edges new xcoord.
287 * This list must be sorted at each scanline in case
288 * two edges intersect.
289 * We also keep a data structure known as the Edge Table (ET),
290 * which keeps track of all the edges which the current
291 * scanline has not yet reached. The ET is basically a
292 * list of ScanLineList structures containing a list of
293 * edges which are entered at a given scanline. There is one
294 * ScanLineList per scanline at which an edge is entered.
295 * When we enter a new edge, we move it from the ET to the AET.
297 * From the AET, we can implement the even-odd rule as in
298 * (Foley/Van Dam).
299 * The winding number rule is a little trickier. We also
300 * keep the EdgeTableEntries in the AET linked by the
301 * nextWETE (winding EdgeTableEntry) link. This allows
302 * the edges to be linked just as before for updating
303 * purposes, but only uses the edges linked by the nextWETE
304 * link as edges representing spans of the polygon to
305 * drawn (as with the even-odd rule).
309 * for the winding number rule
311 #define CLOCKWISE 1
312 #define COUNTERCLOCKWISE -1
314 typedef struct _EdgeTableEntry {
315 INT ymax; /* ycoord at which we exit this edge. */
316 BRESINFO bres; /* Bresenham info to run the edge */
317 struct _EdgeTableEntry *next; /* next in the list */
318 struct _EdgeTableEntry *back; /* for insertion sort */
319 struct _EdgeTableEntry *nextWETE; /* for winding num rule */
320 int ClockWise; /* flag for winding number rule */
321 } EdgeTableEntry;
324 typedef struct _ScanLineList{
325 INT scanline; /* the scanline represented */
326 EdgeTableEntry *edgelist; /* header node */
327 struct _ScanLineList *next; /* next in the list */
328 } ScanLineList;
331 typedef struct {
332 INT ymax; /* ymax for the polygon */
333 INT ymin; /* ymin for the polygon */
334 ScanLineList scanlines; /* header node */
335 } EdgeTable;
339 * Here is a struct to help with storage allocation
340 * so we can allocate a big chunk at a time, and then take
341 * pieces from this heap when we need to.
343 #define SLLSPERBLOCK 25
345 typedef struct _ScanLineListBlock {
346 ScanLineList SLLs[SLLSPERBLOCK];
347 struct _ScanLineListBlock *next;
348 } ScanLineListBlock;
353 * a few macros for the inner loops of the fill code where
354 * performance considerations don't allow a procedure call.
356 * Evaluate the given edge at the given scanline.
357 * If the edge has expired, then we leave it and fix up
358 * the active edge table; otherwise, we increment the
359 * x value to be ready for the next scanline.
360 * The winding number rule is in effect, so we must notify
361 * the caller when the edge has been removed so he
362 * can reorder the Winding Active Edge Table.
364 #define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \
365 if (pAET->ymax == y) { /* leaving this edge */ \
366 pPrevAET->next = pAET->next; \
367 pAET = pPrevAET->next; \
368 fixWAET = 1; \
369 if (pAET) \
370 pAET->back = pPrevAET; \
372 else { \
373 BRESINCRPGONSTRUCT(pAET->bres); \
374 pPrevAET = pAET; \
375 pAET = pAET->next; \
381 * Evaluate the given edge at the given scanline.
382 * If the edge has expired, then we leave it and fix up
383 * the active edge table; otherwise, we increment the
384 * x value to be ready for the next scanline.
385 * The even-odd rule is in effect.
387 #define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \
388 if (pAET->ymax == y) { /* leaving this edge */ \
389 pPrevAET->next = pAET->next; \
390 pAET = pPrevAET->next; \
391 if (pAET) \
392 pAET->back = pPrevAET; \
394 else { \
395 BRESINCRPGONSTRUCT(pAET->bres); \
396 pPrevAET = pAET; \
397 pAET = pAET->next; \
401 typedef void (*voidProcp)();
403 /* Note the parameter order is different from the X11 equivalents */
405 static void REGION_CopyRegion(WINEREGION *d, WINEREGION *s);
406 static void REGION_IntersectRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
407 static void REGION_UnionRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
408 static void REGION_SubtractRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
409 static void REGION_XorRegion(WINEREGION *d, WINEREGION *s1, WINEREGION *s2);
410 static void REGION_UnionRectWithRegion(const RECT *rect, WINEREGION *rgn);
412 #define RGN_DEFAULT_RECTS 2
414 /***********************************************************************
415 * REGION_DumpRegion
416 * Outputs the contents of a WINEREGION
418 static void REGION_DumpRegion(WINEREGION *pReg)
420 RECT *pRect, *pRectEnd = pReg->rects + pReg->numRects;
422 TRACE("Region %p: %d,%d - %d,%d %d rects\n", pReg,
423 pReg->extents.left, pReg->extents.top,
424 pReg->extents.right, pReg->extents.bottom, pReg->numRects);
425 for(pRect = pReg->rects; pRect < pRectEnd; pRect++)
426 TRACE("\t%d,%d - %d,%d\n", pRect->left, pRect->top,
427 pRect->right, pRect->bottom);
428 return;
432 /***********************************************************************
433 * REGION_AllocWineRegion
434 * Create a new empty WINEREGION.
436 static WINEREGION *REGION_AllocWineRegion( INT n )
438 WINEREGION *pReg;
440 if ((pReg = HeapAlloc(GetProcessHeap(), 0, sizeof( WINEREGION ))))
442 if ((pReg->rects = HeapAlloc(GetProcessHeap(), 0, n * sizeof( RECT ))))
444 pReg->size = n;
445 EMPTY_REGION(pReg);
446 return pReg;
448 HeapFree(GetProcessHeap(), 0, pReg);
450 return NULL;
454 /***********************************************************************
455 * REGION_CreateRegion
456 * Create a new empty region.
458 static HRGN REGION_CreateRegion( INT n )
460 HRGN hrgn;
461 RGNOBJ *obj;
463 if(!(obj = GDI_AllocObject( sizeof(RGNOBJ), REGION_MAGIC, &hrgn ))) return 0;
464 if(!(obj->rgn = REGION_AllocWineRegion(n))) {
465 GDI_FreeObject( hrgn, obj );
466 return 0;
468 GDI_ReleaseObj( hrgn );
469 return hrgn;
473 /***********************************************************************
474 * REGION_DestroyWineRegion
476 static void REGION_DestroyWineRegion( WINEREGION* pReg )
478 HeapFree( GetProcessHeap(), 0, pReg->rects );
479 HeapFree( GetProcessHeap(), 0, pReg );
480 return;
483 /***********************************************************************
484 * REGION_DeleteObject
486 BOOL REGION_DeleteObject( HRGN hrgn, RGNOBJ * obj )
488 TRACE(" %04x\n", hrgn );
490 REGION_DestroyWineRegion( obj->rgn );
491 return GDI_FreeObject( hrgn, obj );
494 /***********************************************************************
495 * OffsetRgn (GDI.101)
497 INT16 WINAPI OffsetRgn16( HRGN16 hrgn, INT16 x, INT16 y )
499 return OffsetRgn( hrgn, x, y );
502 /***********************************************************************
503 * OffsetRgn (GDI32.@)
505 INT WINAPI OffsetRgn( HRGN hrgn, INT x, INT y )
507 RGNOBJ * obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
508 INT ret;
510 TRACE("%04x %d,%d\n", hrgn, x, y);
512 if (!obj)
513 return ERROR;
515 if(x || y) {
516 int nbox = obj->rgn->numRects;
517 RECT *pbox = obj->rgn->rects;
519 if(nbox) {
520 while(nbox--) {
521 pbox->left += x;
522 pbox->right += x;
523 pbox->top += y;
524 pbox->bottom += y;
525 pbox++;
527 obj->rgn->extents.left += x;
528 obj->rgn->extents.right += x;
529 obj->rgn->extents.top += y;
530 obj->rgn->extents.bottom += y;
533 ret = obj->rgn->type;
534 GDI_ReleaseObj( hrgn );
535 return ret;
539 /***********************************************************************
540 * GetRgnBox (GDI.134)
542 INT16 WINAPI GetRgnBox16( HRGN16 hrgn, LPRECT16 rect )
544 RECT r;
545 INT16 ret = (INT16)GetRgnBox( hrgn, &r );
546 CONV_RECT32TO16( &r, rect );
547 return ret;
550 /***********************************************************************
551 * GetRgnBox (GDI32.@)
553 INT WINAPI GetRgnBox( HRGN hrgn, LPRECT rect )
555 RGNOBJ * obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
556 if (obj)
558 INT ret;
559 TRACE(" %04x\n", hrgn );
560 rect->left = obj->rgn->extents.left;
561 rect->top = obj->rgn->extents.top;
562 rect->right = obj->rgn->extents.right;
563 rect->bottom = obj->rgn->extents.bottom;
564 ret = obj->rgn->type;
565 GDI_ReleaseObj(hrgn);
566 return ret;
568 return ERROR;
572 /***********************************************************************
573 * CreateRectRgn (GDI.64)
575 * NOTE: Doesn't call CreateRectRgn because of differences in SetRectRgn16/32
577 HRGN16 WINAPI CreateRectRgn16(INT16 left, INT16 top, INT16 right, INT16 bottom)
579 HRGN16 hrgn;
581 if (!(hrgn = (HRGN16)REGION_CreateRegion(RGN_DEFAULT_RECTS)))
582 return 0;
583 TRACE("\n");
584 SetRectRgn16(hrgn, left, top, right, bottom);
585 return hrgn;
589 /***********************************************************************
590 * CreateRectRgn (GDI32.@)
592 HRGN WINAPI CreateRectRgn(INT left, INT top, INT right, INT bottom)
594 HRGN hrgn;
596 /* Allocate 2 rects by default to reduce the number of reallocs */
598 if (!(hrgn = REGION_CreateRegion(RGN_DEFAULT_RECTS)))
599 return 0;
600 TRACE("\n");
601 SetRectRgn(hrgn, left, top, right, bottom);
602 return hrgn;
605 /***********************************************************************
606 * CreateRectRgnIndirect (GDI.65)
608 HRGN16 WINAPI CreateRectRgnIndirect16( const RECT16* rect )
610 return CreateRectRgn16( rect->left, rect->top, rect->right, rect->bottom );
614 /***********************************************************************
615 * CreateRectRgnIndirect (GDI32.@)
617 HRGN WINAPI CreateRectRgnIndirect( const RECT* rect )
619 return CreateRectRgn( rect->left, rect->top, rect->right, rect->bottom );
623 /***********************************************************************
624 * SetRectRgn (GDI.172)
626 * NOTE: Win 3.1 sets region to empty if left > right
628 VOID WINAPI SetRectRgn16( HRGN16 hrgn, INT16 left, INT16 top,
629 INT16 right, INT16 bottom )
631 if(left < right)
632 SetRectRgn( hrgn, left, top, right, bottom );
633 else
634 SetRectRgn( hrgn, 0, 0, 0, 0 );
638 /***********************************************************************
639 * SetRectRgn (GDI32.@)
641 * Allows either or both left and top to be greater than right or bottom.
643 BOOL WINAPI SetRectRgn( HRGN hrgn, INT left, INT top,
644 INT right, INT bottom )
646 RGNOBJ * obj;
648 TRACE(" %04x %d,%d-%d,%d\n",
649 hrgn, left, top, right, bottom );
651 if (!(obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC ))) return FALSE;
653 if (left > right) { INT tmp = left; left = right; right = tmp; }
654 if (top > bottom) { INT tmp = top; top = bottom; bottom = tmp; }
656 if((left != right) && (top != bottom))
658 obj->rgn->rects->left = obj->rgn->extents.left = left;
659 obj->rgn->rects->top = obj->rgn->extents.top = top;
660 obj->rgn->rects->right = obj->rgn->extents.right = right;
661 obj->rgn->rects->bottom = obj->rgn->extents.bottom = bottom;
662 obj->rgn->numRects = 1;
663 obj->rgn->type = SIMPLEREGION;
665 else
666 EMPTY_REGION(obj->rgn);
668 GDI_ReleaseObj( hrgn );
669 return TRUE;
673 /***********************************************************************
674 * CreateRoundRectRgn (GDI.444)
676 * If either ellipse dimension is zero we call CreateRectRgn16 for its
677 * `special' behaviour. -ve ellipse dimensions can result in GPFs under win3.1
678 * we just let CreateRoundRectRgn convert them to +ve values.
681 HRGN16 WINAPI CreateRoundRectRgn16( INT16 left, INT16 top,
682 INT16 right, INT16 bottom,
683 INT16 ellipse_width, INT16 ellipse_height )
685 if( ellipse_width == 0 || ellipse_height == 0 )
686 return CreateRectRgn16( left, top, right, bottom );
687 else
688 return (HRGN16)CreateRoundRectRgn( left, top, right, bottom,
689 ellipse_width, ellipse_height );
692 /***********************************************************************
693 * CreateRoundRectRgn (GDI32.@)
695 HRGN WINAPI CreateRoundRectRgn( INT left, INT top,
696 INT right, INT bottom,
697 INT ellipse_width, INT ellipse_height )
699 RGNOBJ * obj;
700 HRGN hrgn;
701 int asq, bsq, d, xd, yd;
702 RECT rect;
704 /* Make the dimensions sensible */
706 if (left > right) { INT tmp = left; left = right; right = tmp; }
707 if (top > bottom) { INT tmp = top; top = bottom; bottom = tmp; }
709 ellipse_width = abs(ellipse_width);
710 ellipse_height = abs(ellipse_height);
712 /* Check parameters */
714 if (ellipse_width > right-left) ellipse_width = right-left;
715 if (ellipse_height > bottom-top) ellipse_height = bottom-top;
717 /* Check if we can do a normal rectangle instead */
719 if ((ellipse_width < 2) || (ellipse_height < 2))
720 return CreateRectRgn( left, top, right, bottom );
722 /* Create region */
724 d = (ellipse_height < 128) ? ((3 * ellipse_height) >> 2) : 64;
725 if (!(hrgn = REGION_CreateRegion(d))) return 0;
726 if (!(obj = GDI_GetObjPtr( hrgn, REGION_MAGIC ))) return 0;
727 TRACE("(%d,%d-%d,%d %dx%d): ret=%04x\n",
728 left, top, right, bottom, ellipse_width, ellipse_height, hrgn );
730 /* Ellipse algorithm, based on an article by K. Porter */
731 /* in DDJ Graphics Programming Column, 8/89 */
733 asq = ellipse_width * ellipse_width / 4; /* a^2 */
734 bsq = ellipse_height * ellipse_height / 4; /* b^2 */
735 d = bsq - asq * ellipse_height / 2 + asq / 4; /* b^2 - a^2b + a^2/4 */
736 xd = 0;
737 yd = asq * ellipse_height; /* 2a^2b */
739 rect.left = left + ellipse_width / 2;
740 rect.right = right - ellipse_width / 2;
742 /* Loop to draw first half of quadrant */
744 while (xd < yd)
746 if (d > 0) /* if nearest pixel is toward the center */
748 /* move toward center */
749 rect.top = top++;
750 rect.bottom = rect.top + 1;
751 REGION_UnionRectWithRegion( &rect, obj->rgn );
752 rect.top = --bottom;
753 rect.bottom = rect.top + 1;
754 REGION_UnionRectWithRegion( &rect, obj->rgn );
755 yd -= 2*asq;
756 d -= yd;
758 rect.left--; /* next horiz point */
759 rect.right++;
760 xd += 2*bsq;
761 d += bsq + xd;
764 /* Loop to draw second half of quadrant */
766 d += (3 * (asq-bsq) / 2 - (xd+yd)) / 2;
767 while (yd >= 0)
769 /* next vertical point */
770 rect.top = top++;
771 rect.bottom = rect.top + 1;
772 REGION_UnionRectWithRegion( &rect, obj->rgn );
773 rect.top = --bottom;
774 rect.bottom = rect.top + 1;
775 REGION_UnionRectWithRegion( &rect, obj->rgn );
776 if (d < 0) /* if nearest pixel is outside ellipse */
778 rect.left--; /* move away from center */
779 rect.right++;
780 xd += 2*bsq;
781 d += xd;
783 yd -= 2*asq;
784 d += asq - yd;
787 /* Add the inside rectangle */
789 if (top <= bottom)
791 rect.top = top;
792 rect.bottom = bottom;
793 REGION_UnionRectWithRegion( &rect, obj->rgn );
795 obj->rgn->type = SIMPLEREGION; /* FIXME? */
796 GDI_ReleaseObj( hrgn );
797 return hrgn;
801 /***********************************************************************
802 * CreateEllipticRgn (GDI.54)
804 HRGN16 WINAPI CreateEllipticRgn16( INT16 left, INT16 top,
805 INT16 right, INT16 bottom )
807 return (HRGN16)CreateRoundRectRgn( left, top, right, bottom,
808 right-left, bottom-top );
812 /***********************************************************************
813 * CreateEllipticRgn (GDI32.@)
815 HRGN WINAPI CreateEllipticRgn( INT left, INT top,
816 INT right, INT bottom )
818 return CreateRoundRectRgn( left, top, right, bottom,
819 right-left, bottom-top );
823 /***********************************************************************
824 * CreateEllipticRgnIndirect (GDI.55)
826 HRGN16 WINAPI CreateEllipticRgnIndirect16( const RECT16 *rect )
828 return CreateRoundRectRgn( rect->left, rect->top, rect->right,
829 rect->bottom, rect->right - rect->left,
830 rect->bottom - rect->top );
834 /***********************************************************************
835 * CreateEllipticRgnIndirect (GDI32.@)
837 HRGN WINAPI CreateEllipticRgnIndirect( const RECT *rect )
839 return CreateRoundRectRgn( rect->left, rect->top, rect->right,
840 rect->bottom, rect->right - rect->left,
841 rect->bottom - rect->top );
844 /***********************************************************************
845 * GetRegionData (GDI32.@)
847 * MSDN: GetRegionData, Return Values:
849 * "If the function succeeds and dwCount specifies an adequate number of bytes,
850 * the return value is always dwCount. If dwCount is too small or the function
851 * fails, the return value is 0. If lpRgnData is NULL, the return value is the
852 * required number of bytes.
854 * If the function fails, the return value is zero."
856 DWORD WINAPI GetRegionData(HRGN hrgn, DWORD count, LPRGNDATA rgndata)
858 DWORD size;
859 RGNOBJ *obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
861 TRACE(" %04x count = %ld, rgndata = %p\n",
862 hrgn, count, rgndata);
864 if(!obj) return 0;
866 size = obj->rgn->numRects * sizeof(RECT);
867 if(count < (size + sizeof(RGNDATAHEADER)) || rgndata == NULL)
869 GDI_ReleaseObj( hrgn );
870 if (rgndata) /* buffer is too small, signal it by return 0 */
871 return 0;
872 else /* user requested buffer size with rgndata NULL */
873 return size + sizeof(RGNDATAHEADER);
876 rgndata->rdh.dwSize = sizeof(RGNDATAHEADER);
877 rgndata->rdh.iType = RDH_RECTANGLES;
878 rgndata->rdh.nCount = obj->rgn->numRects;
879 rgndata->rdh.nRgnSize = size;
880 rgndata->rdh.rcBound.left = obj->rgn->extents.left;
881 rgndata->rdh.rcBound.top = obj->rgn->extents.top;
882 rgndata->rdh.rcBound.right = obj->rgn->extents.right;
883 rgndata->rdh.rcBound.bottom = obj->rgn->extents.bottom;
885 memcpy( rgndata->Buffer, obj->rgn->rects, size );
887 GDI_ReleaseObj( hrgn );
888 return size + sizeof(RGNDATAHEADER);
891 /***********************************************************************
892 * GetRegionData (GDI.607)
893 * FIXME: is LPRGNDATA the same in Win16 and Win32 ?
895 DWORD WINAPI GetRegionData16(HRGN16 hrgn, DWORD count, LPRGNDATA rgndata)
897 return GetRegionData((HRGN)hrgn, count, rgndata);
900 /***********************************************************************
901 * ExtCreateRegion (GDI32.@)
904 HRGN WINAPI ExtCreateRegion( const XFORM* lpXform, DWORD dwCount, const RGNDATA* rgndata)
906 HRGN hrgn;
908 TRACE(" %p %ld %p = ", lpXform, dwCount, rgndata );
910 if( lpXform )
911 WARN("(Xform not implemented - ignored) ");
913 if( rgndata->rdh.iType != RDH_RECTANGLES )
915 /* FIXME: We can use CreatePolyPolygonRgn() here
916 * for trapezoidal data */
918 WARN("(Unsupported region data) ");
919 goto fail;
922 if( (hrgn = REGION_CreateRegion( rgndata->rdh.nCount )) )
924 RECT *pCurRect, *pEndRect;
925 RGNOBJ *obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
927 if (obj) {
928 pEndRect = (RECT *)rgndata->Buffer + rgndata->rdh.nCount;
929 for(pCurRect = (RECT *)rgndata->Buffer; pCurRect < pEndRect; pCurRect++)
930 REGION_UnionRectWithRegion( pCurRect, obj->rgn );
931 GDI_ReleaseObj( hrgn );
933 TRACE("%04x\n", hrgn );
934 return hrgn;
936 else ERR("Could not get pointer to newborn Region!");
938 fail:
939 WARN("Failed\n");
940 return 0;
943 /***********************************************************************
944 * PtInRegion (GDI.161)
946 BOOL16 WINAPI PtInRegion16( HRGN16 hrgn, INT16 x, INT16 y )
948 return PtInRegion( hrgn, x, y );
952 /***********************************************************************
953 * PtInRegion (GDI32.@)
955 BOOL WINAPI PtInRegion( HRGN hrgn, INT x, INT y )
957 RGNOBJ * obj;
958 BOOL ret = FALSE;
960 if ((obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC )))
962 int i;
964 if (obj->rgn->numRects > 0 && INRECT(obj->rgn->extents, x, y))
965 for (i = 0; i < obj->rgn->numRects; i++)
966 if (INRECT (obj->rgn->rects[i], x, y))
968 ret = TRUE;
969 break;
971 GDI_ReleaseObj( hrgn );
973 return ret;
977 /***********************************************************************
978 * RectInRegion (GDI.466)
979 * RectInRegionOld (GDI.181)
981 BOOL16 WINAPI RectInRegion16( HRGN16 hrgn, const RECT16 *rect )
983 RECT r32;
985 CONV_RECT16TO32(rect, &r32);
986 return (BOOL16)RectInRegion(hrgn, &r32);
990 /***********************************************************************
991 * RectInRegion (GDI32.@)
993 * Returns TRUE if rect is at least partly inside hrgn
995 BOOL WINAPI RectInRegion( HRGN hrgn, const RECT *rect )
997 RGNOBJ * obj;
998 BOOL ret = FALSE;
1000 if ((obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC )))
1002 RECT *pCurRect, *pRectEnd;
1004 /* this is (just) a useful optimization */
1005 if ((obj->rgn->numRects > 0) && EXTENTCHECK(&obj->rgn->extents,
1006 rect))
1008 for (pCurRect = obj->rgn->rects, pRectEnd = pCurRect +
1009 obj->rgn->numRects; pCurRect < pRectEnd; pCurRect++)
1011 if (pCurRect->bottom <= rect->top)
1012 continue; /* not far enough down yet */
1014 if (pCurRect->top >= rect->bottom)
1015 break; /* too far down */
1017 if (pCurRect->right <= rect->left)
1018 continue; /* not far enough over yet */
1020 if (pCurRect->left >= rect->right) {
1021 continue;
1024 ret = TRUE;
1025 break;
1028 GDI_ReleaseObj(hrgn);
1030 return ret;
1033 /***********************************************************************
1034 * EqualRgn (GDI.72)
1036 BOOL16 WINAPI EqualRgn16( HRGN16 rgn1, HRGN16 rgn2 )
1038 return EqualRgn( rgn1, rgn2 );
1042 /***********************************************************************
1043 * EqualRgn (GDI32.@)
1045 BOOL WINAPI EqualRgn( HRGN hrgn1, HRGN hrgn2 )
1047 RGNOBJ *obj1, *obj2;
1048 BOOL ret = FALSE;
1050 if ((obj1 = (RGNOBJ *) GDI_GetObjPtr( hrgn1, REGION_MAGIC )))
1052 if ((obj2 = (RGNOBJ *) GDI_GetObjPtr( hrgn2, REGION_MAGIC )))
1054 int i;
1056 if ( obj1->rgn->numRects != obj2->rgn->numRects ) goto done;
1057 if ( obj1->rgn->numRects == 0 )
1059 ret = TRUE;
1060 goto done;
1063 if (obj1->rgn->extents.left != obj2->rgn->extents.left) goto done;
1064 if (obj1->rgn->extents.right != obj2->rgn->extents.right) goto done;
1065 if (obj1->rgn->extents.top != obj2->rgn->extents.top) goto done;
1066 if (obj1->rgn->extents.bottom != obj2->rgn->extents.bottom) goto done;
1067 for( i = 0; i < obj1->rgn->numRects; i++ )
1069 if (obj1->rgn->rects[i].left != obj2->rgn->rects[i].left) goto done;
1070 if (obj1->rgn->rects[i].right != obj2->rgn->rects[i].right) goto done;
1071 if (obj1->rgn->rects[i].top != obj2->rgn->rects[i].top) goto done;
1072 if (obj1->rgn->rects[i].bottom != obj2->rgn->rects[i].bottom) goto done;
1074 ret = TRUE;
1075 done:
1076 GDI_ReleaseObj(hrgn2);
1078 GDI_ReleaseObj(hrgn1);
1080 return ret;
1082 /***********************************************************************
1083 * REGION_UnionRectWithRegion
1084 * Adds a rectangle to a WINEREGION
1085 * See below for REGION_UnionRectWithRgn
1087 static void REGION_UnionRectWithRegion(const RECT *rect, WINEREGION *rgn)
1089 WINEREGION region;
1091 region.rects = &region.extents;
1092 region.numRects = 1;
1093 region.size = 1;
1094 region.type = SIMPLEREGION;
1095 region.extents = *rect;
1096 REGION_UnionRegion(rgn, rgn, &region);
1097 return;
1100 /***********************************************************************
1101 * REGION_UnionRectWithRgn
1102 * Adds a rectangle to a HRGN
1103 * A helper used by scroll.c
1105 BOOL REGION_UnionRectWithRgn( HRGN hrgn, const RECT *lpRect )
1107 RGNOBJ *obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
1109 if(!obj) return FALSE;
1110 REGION_UnionRectWithRegion( lpRect, obj->rgn );
1111 GDI_ReleaseObj(hrgn);
1112 return TRUE;
1115 /***********************************************************************
1116 * REGION_CreateFrameRgn
1118 * Create a region that is a frame around another region.
1119 * Expand all rectangles by +/- x and y, then subtract original region.
1121 BOOL REGION_FrameRgn( HRGN hDest, HRGN hSrc, INT x, INT y )
1123 BOOL bRet;
1124 RGNOBJ *srcObj = (RGNOBJ*) GDI_GetObjPtr( hSrc, REGION_MAGIC );
1126 if (!srcObj) return FALSE;
1127 if (srcObj->rgn->numRects != 0)
1129 RGNOBJ* destObj = (RGNOBJ*) GDI_GetObjPtr( hDest, REGION_MAGIC );
1130 RECT *pRect, *pEndRect;
1131 RECT tempRect;
1133 EMPTY_REGION( destObj->rgn );
1135 pEndRect = srcObj->rgn->rects + srcObj->rgn->numRects;
1136 for(pRect = srcObj->rgn->rects; pRect < pEndRect; pRect++)
1138 tempRect.left = pRect->left - x;
1139 tempRect.top = pRect->top - y;
1140 tempRect.right = pRect->right + x;
1141 tempRect.bottom = pRect->bottom + y;
1142 REGION_UnionRectWithRegion( &tempRect, destObj->rgn );
1144 REGION_SubtractRegion( destObj->rgn, destObj->rgn, srcObj->rgn );
1145 GDI_ReleaseObj ( hDest );
1146 bRet = TRUE;
1148 else
1149 bRet = FALSE;
1150 GDI_ReleaseObj( hSrc );
1151 return bRet;
1154 /***********************************************************************
1155 * REGION_LPTODP
1157 * Convert region to device co-ords for the supplied dc.
1159 BOOL REGION_LPTODP( HDC hdc, HRGN hDest, HRGN hSrc )
1161 RECT *pCurRect, *pEndRect;
1162 RGNOBJ *srcObj, *destObj;
1163 DC * dc = DC_GetDCPtr( hdc );
1164 RECT tmpRect;
1165 BOOL ret = FALSE;
1167 TRACE(" hdc=%04x dest=%04x src=%04x\n",
1168 hdc, hDest, hSrc) ;
1169 if (!dc) return ret;
1171 if (dc->MapMode == MM_TEXT) /* Requires only a translation */
1173 if( CombineRgn( hDest, hSrc, 0, RGN_COPY ) == ERROR ) goto done;
1174 OffsetRgn( hDest, dc->vportOrgX - dc->wndOrgX,
1175 dc->vportOrgY - dc->wndOrgY );
1176 ret = TRUE;
1177 goto done;
1180 if(!( srcObj = (RGNOBJ *) GDI_GetObjPtr( hSrc, REGION_MAGIC) ))
1181 goto done;
1182 if(!( destObj = (RGNOBJ *) GDI_GetObjPtr( hDest, REGION_MAGIC) ))
1184 GDI_ReleaseObj( hSrc );
1185 goto done;
1187 EMPTY_REGION( destObj->rgn );
1189 pEndRect = srcObj->rgn->rects + srcObj->rgn->numRects;
1190 for(pCurRect = srcObj->rgn->rects; pCurRect < pEndRect; pCurRect++)
1192 tmpRect = *pCurRect;
1193 tmpRect.left = XLPTODP( dc, tmpRect.left );
1194 tmpRect.top = YLPTODP( dc, tmpRect.top );
1195 tmpRect.right = XLPTODP( dc, tmpRect.right );
1196 tmpRect.bottom = YLPTODP( dc, tmpRect.bottom );
1198 if (tmpRect.left > tmpRect.right)
1199 { INT tmp = tmpRect.left; tmpRect.left = tmpRect.right; tmpRect.right = tmp; }
1200 if (tmpRect.top > tmpRect.bottom)
1201 { INT tmp = tmpRect.top; tmpRect.top = tmpRect.bottom; tmpRect.bottom = tmp; }
1203 REGION_UnionRectWithRegion( &tmpRect, destObj->rgn );
1205 ret = TRUE;
1207 GDI_ReleaseObj( hDest );
1208 GDI_ReleaseObj( hSrc );
1209 done:
1210 GDI_ReleaseObj( hdc );
1211 return ret;
1214 /***********************************************************************
1215 * CombineRgn (GDI.47)
1217 INT16 WINAPI CombineRgn16(HRGN16 hDest, HRGN16 hSrc1, HRGN16 hSrc2, INT16 mode)
1219 return (INT16)CombineRgn( hDest, hSrc1, hSrc2, mode );
1223 /***********************************************************************
1224 * CombineRgn (GDI32.@)
1226 * Note: The behavior is correct even if src and dest regions are the same.
1228 INT WINAPI CombineRgn(HRGN hDest, HRGN hSrc1, HRGN hSrc2, INT mode)
1230 RGNOBJ *destObj = (RGNOBJ *) GDI_GetObjPtr( hDest, REGION_MAGIC);
1231 INT result = ERROR;
1233 TRACE(" %04x,%04x -> %04x mode=%x\n",
1234 hSrc1, hSrc2, hDest, mode );
1235 if (destObj)
1237 RGNOBJ *src1Obj = (RGNOBJ *) GDI_GetObjPtr( hSrc1, REGION_MAGIC);
1239 if (src1Obj)
1241 TRACE("dump src1Obj:\n");
1242 if(TRACE_ON(region))
1243 REGION_DumpRegion(src1Obj->rgn);
1244 if (mode == RGN_COPY)
1246 REGION_CopyRegion( destObj->rgn, src1Obj->rgn );
1247 result = destObj->rgn->type;
1249 else
1251 RGNOBJ *src2Obj = (RGNOBJ *) GDI_GetObjPtr( hSrc2, REGION_MAGIC);
1253 if (src2Obj)
1255 TRACE("dump src2Obj:\n");
1256 if(TRACE_ON(region))
1257 REGION_DumpRegion(src2Obj->rgn);
1258 switch (mode)
1260 case RGN_AND:
1261 REGION_IntersectRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn);
1262 break;
1263 case RGN_OR:
1264 REGION_UnionRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn );
1265 break;
1266 case RGN_XOR:
1267 REGION_XorRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn );
1268 break;
1269 case RGN_DIFF:
1270 REGION_SubtractRegion( destObj->rgn, src1Obj->rgn, src2Obj->rgn );
1271 break;
1273 result = destObj->rgn->type;
1274 GDI_ReleaseObj( hSrc2 );
1277 GDI_ReleaseObj( hSrc1 );
1279 TRACE("dump destObj:\n");
1280 if(TRACE_ON(region))
1281 REGION_DumpRegion(destObj->rgn);
1283 GDI_ReleaseObj( hDest );
1284 } else {
1285 ERR("Invalid rgn=%04x\n", hDest);
1287 return result;
1290 /***********************************************************************
1291 * REGION_SetExtents
1292 * Re-calculate the extents of a region
1294 static void REGION_SetExtents (WINEREGION *pReg)
1296 RECT *pRect, *pRectEnd, *pExtents;
1298 if (pReg->numRects == 0)
1300 pReg->extents.left = 0;
1301 pReg->extents.top = 0;
1302 pReg->extents.right = 0;
1303 pReg->extents.bottom = 0;
1304 return;
1307 pExtents = &pReg->extents;
1308 pRect = pReg->rects;
1309 pRectEnd = &pRect[pReg->numRects - 1];
1312 * Since pRect is the first rectangle in the region, it must have the
1313 * smallest top and since pRectEnd is the last rectangle in the region,
1314 * it must have the largest bottom, because of banding. Initialize left and
1315 * right from pRect and pRectEnd, resp., as good things to initialize them
1316 * to...
1318 pExtents->left = pRect->left;
1319 pExtents->top = pRect->top;
1320 pExtents->right = pRectEnd->right;
1321 pExtents->bottom = pRectEnd->bottom;
1323 while (pRect <= pRectEnd)
1325 if (pRect->left < pExtents->left)
1326 pExtents->left = pRect->left;
1327 if (pRect->right > pExtents->right)
1328 pExtents->right = pRect->right;
1329 pRect++;
1333 /***********************************************************************
1334 * REGION_CopyRegion
1336 static void REGION_CopyRegion(WINEREGION *dst, WINEREGION *src)
1338 if (dst != src) /* don't want to copy to itself */
1340 if (dst->size < src->numRects)
1342 if (! (dst->rects = HeapReAlloc( GetProcessHeap(), 0, dst->rects,
1343 src->numRects * sizeof(RECT) )))
1344 return;
1345 dst->size = src->numRects;
1347 dst->numRects = src->numRects;
1348 dst->extents.left = src->extents.left;
1349 dst->extents.top = src->extents.top;
1350 dst->extents.right = src->extents.right;
1351 dst->extents.bottom = src->extents.bottom;
1352 dst->type = src->type;
1354 memcpy((char *) dst->rects, (char *) src->rects,
1355 (int) (src->numRects * sizeof(RECT)));
1357 return;
1360 /***********************************************************************
1361 * REGION_Coalesce
1363 * Attempt to merge the rects in the current band with those in the
1364 * previous one. Used only by REGION_RegionOp.
1366 * Results:
1367 * The new index for the previous band.
1369 * Side Effects:
1370 * If coalescing takes place:
1371 * - rectangles in the previous band will have their bottom fields
1372 * altered.
1373 * - pReg->numRects will be decreased.
1376 static INT REGION_Coalesce (
1377 WINEREGION *pReg, /* Region to coalesce */
1378 INT prevStart, /* Index of start of previous band */
1379 INT curStart /* Index of start of current band */
1381 RECT *pPrevRect; /* Current rect in previous band */
1382 RECT *pCurRect; /* Current rect in current band */
1383 RECT *pRegEnd; /* End of region */
1384 INT curNumRects; /* Number of rectangles in current band */
1385 INT prevNumRects; /* Number of rectangles in previous band */
1386 INT bandtop; /* top coordinate for current band */
1388 pRegEnd = &pReg->rects[pReg->numRects];
1390 pPrevRect = &pReg->rects[prevStart];
1391 prevNumRects = curStart - prevStart;
1394 * Figure out how many rectangles are in the current band. Have to do
1395 * this because multiple bands could have been added in REGION_RegionOp
1396 * at the end when one region has been exhausted.
1398 pCurRect = &pReg->rects[curStart];
1399 bandtop = pCurRect->top;
1400 for (curNumRects = 0;
1401 (pCurRect != pRegEnd) && (pCurRect->top == bandtop);
1402 curNumRects++)
1404 pCurRect++;
1407 if (pCurRect != pRegEnd)
1410 * If more than one band was added, we have to find the start
1411 * of the last band added so the next coalescing job can start
1412 * at the right place... (given when multiple bands are added,
1413 * this may be pointless -- see above).
1415 pRegEnd--;
1416 while (pRegEnd[-1].top == pRegEnd->top)
1418 pRegEnd--;
1420 curStart = pRegEnd - pReg->rects;
1421 pRegEnd = pReg->rects + pReg->numRects;
1424 if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
1425 pCurRect -= curNumRects;
1427 * The bands may only be coalesced if the bottom of the previous
1428 * matches the top scanline of the current.
1430 if (pPrevRect->bottom == pCurRect->top)
1433 * Make sure the bands have rects in the same places. This
1434 * assumes that rects have been added in such a way that they
1435 * cover the most area possible. I.e. two rects in a band must
1436 * have some horizontal space between them.
1440 if ((pPrevRect->left != pCurRect->left) ||
1441 (pPrevRect->right != pCurRect->right))
1444 * The bands don't line up so they can't be coalesced.
1446 return (curStart);
1448 pPrevRect++;
1449 pCurRect++;
1450 prevNumRects -= 1;
1451 } while (prevNumRects != 0);
1453 pReg->numRects -= curNumRects;
1454 pCurRect -= curNumRects;
1455 pPrevRect -= curNumRects;
1458 * The bands may be merged, so set the bottom of each rect
1459 * in the previous band to that of the corresponding rect in
1460 * the current band.
1464 pPrevRect->bottom = pCurRect->bottom;
1465 pPrevRect++;
1466 pCurRect++;
1467 curNumRects -= 1;
1468 } while (curNumRects != 0);
1471 * If only one band was added to the region, we have to backup
1472 * curStart to the start of the previous band.
1474 * If more than one band was added to the region, copy the
1475 * other bands down. The assumption here is that the other bands
1476 * came from the same region as the current one and no further
1477 * coalescing can be done on them since it's all been done
1478 * already... curStart is already in the right place.
1480 if (pCurRect == pRegEnd)
1482 curStart = prevStart;
1484 else
1488 *pPrevRect++ = *pCurRect++;
1489 } while (pCurRect != pRegEnd);
1494 return (curStart);
1497 /***********************************************************************
1498 * REGION_RegionOp
1500 * Apply an operation to two regions. Called by REGION_Union,
1501 * REGION_Inverse, REGION_Subtract, REGION_Intersect...
1503 * Results:
1504 * None.
1506 * Side Effects:
1507 * The new region is overwritten.
1509 * Notes:
1510 * The idea behind this function is to view the two regions as sets.
1511 * Together they cover a rectangle of area that this function divides
1512 * into horizontal bands where points are covered only by one region
1513 * or by both. For the first case, the nonOverlapFunc is called with
1514 * each the band and the band's upper and lower extents. For the
1515 * second, the overlapFunc is called to process the entire band. It
1516 * is responsible for clipping the rectangles in the band, though
1517 * this function provides the boundaries.
1518 * At the end of each band, the new region is coalesced, if possible,
1519 * to reduce the number of rectangles in the region.
1522 static void REGION_RegionOp(
1523 WINEREGION *newReg, /* Place to store result */
1524 WINEREGION *reg1, /* First region in operation */
1525 WINEREGION *reg2, /* 2nd region in operation */
1526 void (*overlapFunc)(), /* Function to call for over-lapping bands */
1527 void (*nonOverlap1Func)(), /* Function to call for non-overlapping bands in region 1 */
1528 void (*nonOverlap2Func)() /* Function to call for non-overlapping bands in region 2 */
1530 RECT *r1; /* Pointer into first region */
1531 RECT *r2; /* Pointer into 2d region */
1532 RECT *r1End; /* End of 1st region */
1533 RECT *r2End; /* End of 2d region */
1534 INT ybot; /* Bottom of intersection */
1535 INT ytop; /* Top of intersection */
1536 RECT *oldRects; /* Old rects for newReg */
1537 INT prevBand; /* Index of start of
1538 * previous band in newReg */
1539 INT curBand; /* Index of start of current
1540 * band in newReg */
1541 RECT *r1BandEnd; /* End of current band in r1 */
1542 RECT *r2BandEnd; /* End of current band in r2 */
1543 INT top; /* Top of non-overlapping band */
1544 INT bot; /* Bottom of non-overlapping band */
1547 * Initialization:
1548 * set r1, r2, r1End and r2End appropriately, preserve the important
1549 * parts of the destination region until the end in case it's one of
1550 * the two source regions, then mark the "new" region empty, allocating
1551 * another array of rectangles for it to use.
1553 r1 = reg1->rects;
1554 r2 = reg2->rects;
1555 r1End = r1 + reg1->numRects;
1556 r2End = r2 + reg2->numRects;
1560 * newReg may be one of the src regions so we can't empty it. We keep a
1561 * note of its rects pointer (so that we can free them later), preserve its
1562 * extents and simply set numRects to zero.
1565 oldRects = newReg->rects;
1566 newReg->numRects = 0;
1569 * Allocate a reasonable number of rectangles for the new region. The idea
1570 * is to allocate enough so the individual functions don't need to
1571 * reallocate and copy the array, which is time consuming, yet we don't
1572 * have to worry about using too much memory. I hope to be able to
1573 * nuke the Xrealloc() at the end of this function eventually.
1575 newReg->size = max(reg1->numRects,reg2->numRects) * 2;
1577 if (! (newReg->rects = HeapAlloc( GetProcessHeap(), 0,
1578 sizeof(RECT) * newReg->size )))
1580 newReg->size = 0;
1581 return;
1585 * Initialize ybot and ytop.
1586 * In the upcoming loop, ybot and ytop serve different functions depending
1587 * on whether the band being handled is an overlapping or non-overlapping
1588 * band.
1589 * In the case of a non-overlapping band (only one of the regions
1590 * has points in the band), ybot is the bottom of the most recent
1591 * intersection and thus clips the top of the rectangles in that band.
1592 * ytop is the top of the next intersection between the two regions and
1593 * serves to clip the bottom of the rectangles in the current band.
1594 * For an overlapping band (where the two regions intersect), ytop clips
1595 * the top of the rectangles of both regions and ybot clips the bottoms.
1597 if (reg1->extents.top < reg2->extents.top)
1598 ybot = reg1->extents.top;
1599 else
1600 ybot = reg2->extents.top;
1603 * prevBand serves to mark the start of the previous band so rectangles
1604 * can be coalesced into larger rectangles. qv. miCoalesce, above.
1605 * In the beginning, there is no previous band, so prevBand == curBand
1606 * (curBand is set later on, of course, but the first band will always
1607 * start at index 0). prevBand and curBand must be indices because of
1608 * the possible expansion, and resultant moving, of the new region's
1609 * array of rectangles.
1611 prevBand = 0;
1615 curBand = newReg->numRects;
1618 * This algorithm proceeds one source-band (as opposed to a
1619 * destination band, which is determined by where the two regions
1620 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
1621 * rectangle after the last one in the current band for their
1622 * respective regions.
1624 r1BandEnd = r1;
1625 while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top))
1627 r1BandEnd++;
1630 r2BandEnd = r2;
1631 while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top))
1633 r2BandEnd++;
1637 * First handle the band that doesn't intersect, if any.
1639 * Note that attention is restricted to one band in the
1640 * non-intersecting region at once, so if a region has n
1641 * bands between the current position and the next place it overlaps
1642 * the other, this entire loop will be passed through n times.
1644 if (r1->top < r2->top)
1646 top = max(r1->top,ybot);
1647 bot = min(r1->bottom,r2->top);
1649 if ((top != bot) && (nonOverlap1Func != (void (*)())NULL))
1651 (* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot);
1654 ytop = r2->top;
1656 else if (r2->top < r1->top)
1658 top = max(r2->top,ybot);
1659 bot = min(r2->bottom,r1->top);
1661 if ((top != bot) && (nonOverlap2Func != (void (*)())NULL))
1663 (* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot);
1666 ytop = r1->top;
1668 else
1670 ytop = r1->top;
1674 * If any rectangles got added to the region, try and coalesce them
1675 * with rectangles from the previous band. Note we could just do
1676 * this test in miCoalesce, but some machines incur a not
1677 * inconsiderable cost for function calls, so...
1679 if (newReg->numRects != curBand)
1681 prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1685 * Now see if we've hit an intersecting band. The two bands only
1686 * intersect if ybot > ytop
1688 ybot = min(r1->bottom, r2->bottom);
1689 curBand = newReg->numRects;
1690 if (ybot > ytop)
1692 (* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
1696 if (newReg->numRects != curBand)
1698 prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1702 * If we've finished with a band (bottom == ybot) we skip forward
1703 * in the region to the next band.
1705 if (r1->bottom == ybot)
1707 r1 = r1BandEnd;
1709 if (r2->bottom == ybot)
1711 r2 = r2BandEnd;
1713 } while ((r1 != r1End) && (r2 != r2End));
1716 * Deal with whichever region still has rectangles left.
1718 curBand = newReg->numRects;
1719 if (r1 != r1End)
1721 if (nonOverlap1Func != (void (*)())NULL)
1725 r1BandEnd = r1;
1726 while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top))
1728 r1BandEnd++;
1730 (* nonOverlap1Func) (newReg, r1, r1BandEnd,
1731 max(r1->top,ybot), r1->bottom);
1732 r1 = r1BandEnd;
1733 } while (r1 != r1End);
1736 else if ((r2 != r2End) && (nonOverlap2Func != (void (*)())NULL))
1740 r2BandEnd = r2;
1741 while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top))
1743 r2BandEnd++;
1745 (* nonOverlap2Func) (newReg, r2, r2BandEnd,
1746 max(r2->top,ybot), r2->bottom);
1747 r2 = r2BandEnd;
1748 } while (r2 != r2End);
1751 if (newReg->numRects != curBand)
1753 (void) REGION_Coalesce (newReg, prevBand, curBand);
1757 * A bit of cleanup. To keep regions from growing without bound,
1758 * we shrink the array of rectangles to match the new number of
1759 * rectangles in the region. This never goes to 0, however...
1761 * Only do this stuff if the number of rectangles allocated is more than
1762 * twice the number of rectangles in the region (a simple optimization...).
1764 if ((newReg->numRects < (newReg->size >> 1)) && (newReg->numRects > 2))
1766 if (REGION_NOT_EMPTY(newReg))
1768 RECT *prev_rects = newReg->rects;
1769 newReg->size = newReg->numRects;
1770 newReg->rects = HeapReAlloc( GetProcessHeap(), 0, newReg->rects,
1771 sizeof(RECT) * newReg->size );
1772 if (! newReg->rects)
1773 newReg->rects = prev_rects;
1775 else
1778 * No point in doing the extra work involved in an Xrealloc if
1779 * the region is empty
1781 newReg->size = 1;
1782 HeapFree( GetProcessHeap(), 0, newReg->rects );
1783 newReg->rects = HeapAlloc( GetProcessHeap(), 0, sizeof(RECT) );
1786 HeapFree( GetProcessHeap(), 0, oldRects );
1787 return;
1790 /***********************************************************************
1791 * Region Intersection
1792 ***********************************************************************/
1795 /***********************************************************************
1796 * REGION_IntersectO
1798 * Handle an overlapping band for REGION_Intersect.
1800 * Results:
1801 * None.
1803 * Side Effects:
1804 * Rectangles may be added to the region.
1807 static void REGION_IntersectO(WINEREGION *pReg, RECT *r1, RECT *r1End,
1808 RECT *r2, RECT *r2End, INT top, INT bottom)
1811 INT left, right;
1812 RECT *pNextRect;
1814 pNextRect = &pReg->rects[pReg->numRects];
1816 while ((r1 != r1End) && (r2 != r2End))
1818 left = max(r1->left, r2->left);
1819 right = min(r1->right, r2->right);
1822 * If there's any overlap between the two rectangles, add that
1823 * overlap to the new region.
1824 * There's no need to check for subsumption because the only way
1825 * such a need could arise is if some region has two rectangles
1826 * right next to each other. Since that should never happen...
1828 if (left < right)
1830 MEMCHECK(pReg, pNextRect, pReg->rects);
1831 pNextRect->left = left;
1832 pNextRect->top = top;
1833 pNextRect->right = right;
1834 pNextRect->bottom = bottom;
1835 pReg->numRects += 1;
1836 pNextRect++;
1840 * Need to advance the pointers. Shift the one that extends
1841 * to the right the least, since the other still has a chance to
1842 * overlap with that region's next rectangle, if you see what I mean.
1844 if (r1->right < r2->right)
1846 r1++;
1848 else if (r2->right < r1->right)
1850 r2++;
1852 else
1854 r1++;
1855 r2++;
1858 return;
1861 /***********************************************************************
1862 * REGION_IntersectRegion
1864 static void REGION_IntersectRegion(WINEREGION *newReg, WINEREGION *reg1,
1865 WINEREGION *reg2)
1867 /* check for trivial reject */
1868 if ( (!(reg1->numRects)) || (!(reg2->numRects)) ||
1869 (!EXTENTCHECK(&reg1->extents, &reg2->extents)))
1870 newReg->numRects = 0;
1871 else
1872 REGION_RegionOp (newReg, reg1, reg2,
1873 (voidProcp) REGION_IntersectO, (voidProcp) NULL, (voidProcp) NULL);
1876 * Can't alter newReg's extents before we call miRegionOp because
1877 * it might be one of the source regions and miRegionOp depends
1878 * on the extents of those regions being the same. Besides, this
1879 * way there's no checking against rectangles that will be nuked
1880 * due to coalescing, so we have to examine fewer rectangles.
1882 REGION_SetExtents(newReg);
1883 newReg->type = (newReg->numRects) ?
1884 ((newReg->numRects > 1) ? COMPLEXREGION : SIMPLEREGION)
1885 : NULLREGION ;
1886 return;
1889 /***********************************************************************
1890 * Region Union
1891 ***********************************************************************/
1893 /***********************************************************************
1894 * REGION_UnionNonO
1896 * Handle a non-overlapping band for the union operation. Just
1897 * Adds the rectangles into the region. Doesn't have to check for
1898 * subsumption or anything.
1900 * Results:
1901 * None.
1903 * Side Effects:
1904 * pReg->numRects is incremented and the final rectangles overwritten
1905 * with the rectangles we're passed.
1908 static void REGION_UnionNonO (WINEREGION *pReg, RECT *r, RECT *rEnd,
1909 INT top, INT bottom)
1911 RECT *pNextRect;
1913 pNextRect = &pReg->rects[pReg->numRects];
1915 while (r != rEnd)
1917 MEMCHECK(pReg, pNextRect, pReg->rects);
1918 pNextRect->left = r->left;
1919 pNextRect->top = top;
1920 pNextRect->right = r->right;
1921 pNextRect->bottom = bottom;
1922 pReg->numRects += 1;
1923 pNextRect++;
1924 r++;
1926 return;
1929 /***********************************************************************
1930 * REGION_UnionO
1932 * Handle an overlapping band for the union operation. Picks the
1933 * left-most rectangle each time and merges it into the region.
1935 * Results:
1936 * None.
1938 * Side Effects:
1939 * Rectangles are overwritten in pReg->rects and pReg->numRects will
1940 * be changed.
1943 static void REGION_UnionO (WINEREGION *pReg, RECT *r1, RECT *r1End,
1944 RECT *r2, RECT *r2End, INT top, INT bottom)
1946 RECT *pNextRect;
1948 pNextRect = &pReg->rects[pReg->numRects];
1950 #define MERGERECT(r) \
1951 if ((pReg->numRects != 0) && \
1952 (pNextRect[-1].top == top) && \
1953 (pNextRect[-1].bottom == bottom) && \
1954 (pNextRect[-1].right >= r->left)) \
1956 if (pNextRect[-1].right < r->right) \
1958 pNextRect[-1].right = r->right; \
1961 else \
1963 MEMCHECK(pReg, pNextRect, pReg->rects); \
1964 pNextRect->top = top; \
1965 pNextRect->bottom = bottom; \
1966 pNextRect->left = r->left; \
1967 pNextRect->right = r->right; \
1968 pReg->numRects += 1; \
1969 pNextRect += 1; \
1971 r++;
1973 while ((r1 != r1End) && (r2 != r2End))
1975 if (r1->left < r2->left)
1977 MERGERECT(r1);
1979 else
1981 MERGERECT(r2);
1985 if (r1 != r1End)
1989 MERGERECT(r1);
1990 } while (r1 != r1End);
1992 else while (r2 != r2End)
1994 MERGERECT(r2);
1996 return;
1999 /***********************************************************************
2000 * REGION_UnionRegion
2002 static void REGION_UnionRegion(WINEREGION *newReg, WINEREGION *reg1,
2003 WINEREGION *reg2)
2005 /* checks all the simple cases */
2008 * Region 1 and 2 are the same or region 1 is empty
2010 if ( (reg1 == reg2) || (!(reg1->numRects)) )
2012 if (newReg != reg2)
2013 REGION_CopyRegion(newReg, reg2);
2014 return;
2018 * if nothing to union (region 2 empty)
2020 if (!(reg2->numRects))
2022 if (newReg != reg1)
2023 REGION_CopyRegion(newReg, reg1);
2024 return;
2028 * Region 1 completely subsumes region 2
2030 if ((reg1->numRects == 1) &&
2031 (reg1->extents.left <= reg2->extents.left) &&
2032 (reg1->extents.top <= reg2->extents.top) &&
2033 (reg1->extents.right >= reg2->extents.right) &&
2034 (reg1->extents.bottom >= reg2->extents.bottom))
2036 if (newReg != reg1)
2037 REGION_CopyRegion(newReg, reg1);
2038 return;
2042 * Region 2 completely subsumes region 1
2044 if ((reg2->numRects == 1) &&
2045 (reg2->extents.left <= reg1->extents.left) &&
2046 (reg2->extents.top <= reg1->extents.top) &&
2047 (reg2->extents.right >= reg1->extents.right) &&
2048 (reg2->extents.bottom >= reg1->extents.bottom))
2050 if (newReg != reg2)
2051 REGION_CopyRegion(newReg, reg2);
2052 return;
2055 REGION_RegionOp (newReg, reg1, reg2, (voidProcp) REGION_UnionO,
2056 (voidProcp) REGION_UnionNonO, (voidProcp) REGION_UnionNonO);
2058 newReg->extents.left = min(reg1->extents.left, reg2->extents.left);
2059 newReg->extents.top = min(reg1->extents.top, reg2->extents.top);
2060 newReg->extents.right = max(reg1->extents.right, reg2->extents.right);
2061 newReg->extents.bottom = max(reg1->extents.bottom, reg2->extents.bottom);
2062 newReg->type = (newReg->numRects) ?
2063 ((newReg->numRects > 1) ? COMPLEXREGION : SIMPLEREGION)
2064 : NULLREGION ;
2065 return;
2068 /***********************************************************************
2069 * Region Subtraction
2070 ***********************************************************************/
2072 /***********************************************************************
2073 * REGION_SubtractNonO1
2075 * Deal with non-overlapping band for subtraction. Any parts from
2076 * region 2 we discard. Anything from region 1 we add to the region.
2078 * Results:
2079 * None.
2081 * Side Effects:
2082 * pReg may be affected.
2085 static void REGION_SubtractNonO1 (WINEREGION *pReg, RECT *r, RECT *rEnd,
2086 INT top, INT bottom)
2088 RECT *pNextRect;
2090 pNextRect = &pReg->rects[pReg->numRects];
2092 while (r != rEnd)
2094 MEMCHECK(pReg, pNextRect, pReg->rects);
2095 pNextRect->left = r->left;
2096 pNextRect->top = top;
2097 pNextRect->right = r->right;
2098 pNextRect->bottom = bottom;
2099 pReg->numRects += 1;
2100 pNextRect++;
2101 r++;
2103 return;
2107 /***********************************************************************
2108 * REGION_SubtractO
2110 * Overlapping band subtraction. x1 is the left-most point not yet
2111 * checked.
2113 * Results:
2114 * None.
2116 * Side Effects:
2117 * pReg may have rectangles added to it.
2120 static void REGION_SubtractO (WINEREGION *pReg, RECT *r1, RECT *r1End,
2121 RECT *r2, RECT *r2End, INT top, INT bottom)
2123 RECT *pNextRect;
2124 INT left;
2126 left = r1->left;
2127 pNextRect = &pReg->rects[pReg->numRects];
2129 while ((r1 != r1End) && (r2 != r2End))
2131 if (r2->right <= left)
2134 * Subtrahend missed the boat: go to next subtrahend.
2136 r2++;
2138 else if (r2->left <= left)
2141 * Subtrahend preceeds minuend: nuke left edge of minuend.
2143 left = r2->right;
2144 if (left >= r1->right)
2147 * Minuend completely covered: advance to next minuend and
2148 * reset left fence to edge of new minuend.
2150 r1++;
2151 if (r1 != r1End)
2152 left = r1->left;
2154 else
2157 * Subtrahend now used up since it doesn't extend beyond
2158 * minuend
2160 r2++;
2163 else if (r2->left < r1->right)
2166 * Left part of subtrahend covers part of minuend: add uncovered
2167 * part of minuend to region and skip to next subtrahend.
2169 MEMCHECK(pReg, pNextRect, pReg->rects);
2170 pNextRect->left = left;
2171 pNextRect->top = top;
2172 pNextRect->right = r2->left;
2173 pNextRect->bottom = bottom;
2174 pReg->numRects += 1;
2175 pNextRect++;
2176 left = r2->right;
2177 if (left >= r1->right)
2180 * Minuend used up: advance to new...
2182 r1++;
2183 if (r1 != r1End)
2184 left = r1->left;
2186 else
2189 * Subtrahend used up
2191 r2++;
2194 else
2197 * Minuend used up: add any remaining piece before advancing.
2199 if (r1->right > left)
2201 MEMCHECK(pReg, pNextRect, pReg->rects);
2202 pNextRect->left = left;
2203 pNextRect->top = top;
2204 pNextRect->right = r1->right;
2205 pNextRect->bottom = bottom;
2206 pReg->numRects += 1;
2207 pNextRect++;
2209 r1++;
2210 left = r1->left;
2215 * Add remaining minuend rectangles to region.
2217 while (r1 != r1End)
2219 MEMCHECK(pReg, pNextRect, pReg->rects);
2220 pNextRect->left = left;
2221 pNextRect->top = top;
2222 pNextRect->right = r1->right;
2223 pNextRect->bottom = bottom;
2224 pReg->numRects += 1;
2225 pNextRect++;
2226 r1++;
2227 if (r1 != r1End)
2229 left = r1->left;
2232 return;
2235 /***********************************************************************
2236 * REGION_SubtractRegion
2238 * Subtract regS from regM and leave the result in regD.
2239 * S stands for subtrahend, M for minuend and D for difference.
2241 * Results:
2242 * TRUE.
2244 * Side Effects:
2245 * regD is overwritten.
2248 static void REGION_SubtractRegion(WINEREGION *regD, WINEREGION *regM,
2249 WINEREGION *regS )
2251 /* check for trivial reject */
2252 if ( (!(regM->numRects)) || (!(regS->numRects)) ||
2253 (!EXTENTCHECK(&regM->extents, &regS->extents)) )
2255 REGION_CopyRegion(regD, regM);
2256 return;
2259 REGION_RegionOp (regD, regM, regS, (voidProcp) REGION_SubtractO,
2260 (voidProcp) REGION_SubtractNonO1, (voidProcp) NULL);
2263 * Can't alter newReg's extents before we call miRegionOp because
2264 * it might be one of the source regions and miRegionOp depends
2265 * on the extents of those regions being the unaltered. Besides, this
2266 * way there's no checking against rectangles that will be nuked
2267 * due to coalescing, so we have to examine fewer rectangles.
2269 REGION_SetExtents (regD);
2270 regD->type = (regD->numRects) ?
2271 ((regD->numRects > 1) ? COMPLEXREGION : SIMPLEREGION)
2272 : NULLREGION ;
2273 return;
2276 /***********************************************************************
2277 * REGION_XorRegion
2279 static void REGION_XorRegion(WINEREGION *dr, WINEREGION *sra,
2280 WINEREGION *srb)
2282 WINEREGION *tra, *trb;
2284 if ((! (tra = REGION_AllocWineRegion(sra->numRects + 1))) ||
2285 (! (trb = REGION_AllocWineRegion(srb->numRects + 1))))
2286 return;
2287 REGION_SubtractRegion(tra,sra,srb);
2288 REGION_SubtractRegion(trb,srb,sra);
2289 REGION_UnionRegion(dr,tra,trb);
2290 REGION_DestroyWineRegion(tra);
2291 REGION_DestroyWineRegion(trb);
2292 return;
2295 /**************************************************************************
2297 * Poly Regions
2299 *************************************************************************/
2301 #define LARGE_COORDINATE 0x7fffffff /* FIXME */
2302 #define SMALL_COORDINATE 0x80000000
2304 /***********************************************************************
2305 * REGION_InsertEdgeInET
2307 * Insert the given edge into the edge table.
2308 * First we must find the correct bucket in the
2309 * Edge table, then find the right slot in the
2310 * bucket. Finally, we can insert it.
2313 static void REGION_InsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE,
2314 INT scanline, ScanLineListBlock **SLLBlock, INT *iSLLBlock)
2317 EdgeTableEntry *start, *prev;
2318 ScanLineList *pSLL, *pPrevSLL;
2319 ScanLineListBlock *tmpSLLBlock;
2322 * find the right bucket to put the edge into
2324 pPrevSLL = &ET->scanlines;
2325 pSLL = pPrevSLL->next;
2326 while (pSLL && (pSLL->scanline < scanline))
2328 pPrevSLL = pSLL;
2329 pSLL = pSLL->next;
2333 * reassign pSLL (pointer to ScanLineList) if necessary
2335 if ((!pSLL) || (pSLL->scanline > scanline))
2337 if (*iSLLBlock > SLLSPERBLOCK-1)
2339 tmpSLLBlock = HeapAlloc( GetProcessHeap(), 0, sizeof(ScanLineListBlock));
2340 if(!tmpSLLBlock)
2342 WARN("Can't alloc SLLB\n");
2343 return;
2345 (*SLLBlock)->next = tmpSLLBlock;
2346 tmpSLLBlock->next = (ScanLineListBlock *)NULL;
2347 *SLLBlock = tmpSLLBlock;
2348 *iSLLBlock = 0;
2350 pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
2352 pSLL->next = pPrevSLL->next;
2353 pSLL->edgelist = (EdgeTableEntry *)NULL;
2354 pPrevSLL->next = pSLL;
2356 pSLL->scanline = scanline;
2359 * now insert the edge in the right bucket
2361 prev = (EdgeTableEntry *)NULL;
2362 start = pSLL->edgelist;
2363 while (start && (start->bres.minor_axis < ETE->bres.minor_axis))
2365 prev = start;
2366 start = start->next;
2368 ETE->next = start;
2370 if (prev)
2371 prev->next = ETE;
2372 else
2373 pSLL->edgelist = ETE;
2376 /***********************************************************************
2377 * REGION_CreateEdgeTable
2379 * This routine creates the edge table for
2380 * scan converting polygons.
2381 * The Edge Table (ET) looks like:
2383 * EdgeTable
2384 * --------
2385 * | ymax | ScanLineLists
2386 * |scanline|-->------------>-------------->...
2387 * -------- |scanline| |scanline|
2388 * |edgelist| |edgelist|
2389 * --------- ---------
2390 * | |
2391 * | |
2392 * V V
2393 * list of ETEs list of ETEs
2395 * where ETE is an EdgeTableEntry data structure,
2396 * and there is one ScanLineList per scanline at
2397 * which an edge is initially entered.
2400 static void REGION_CreateETandAET(const INT *Count, INT nbpolygons,
2401 const POINT *pts, EdgeTable *ET, EdgeTableEntry *AET,
2402 EdgeTableEntry *pETEs, ScanLineListBlock *pSLLBlock)
2404 const POINT *top, *bottom;
2405 const POINT *PrevPt, *CurrPt, *EndPt;
2406 INT poly, count;
2407 int iSLLBlock = 0;
2408 int dy;
2412 * initialize the Active Edge Table
2414 AET->next = (EdgeTableEntry *)NULL;
2415 AET->back = (EdgeTableEntry *)NULL;
2416 AET->nextWETE = (EdgeTableEntry *)NULL;
2417 AET->bres.minor_axis = SMALL_COORDINATE;
2420 * initialize the Edge Table.
2422 ET->scanlines.next = (ScanLineList *)NULL;
2423 ET->ymax = SMALL_COORDINATE;
2424 ET->ymin = LARGE_COORDINATE;
2425 pSLLBlock->next = (ScanLineListBlock *)NULL;
2427 EndPt = pts - 1;
2428 for(poly = 0; poly < nbpolygons; poly++)
2430 count = Count[poly];
2431 EndPt += count;
2432 if(count < 2)
2433 continue;
2435 PrevPt = EndPt;
2438 * for each vertex in the array of points.
2439 * In this loop we are dealing with two vertices at
2440 * a time -- these make up one edge of the polygon.
2442 while (count--)
2444 CurrPt = pts++;
2447 * find out which point is above and which is below.
2449 if (PrevPt->y > CurrPt->y)
2451 bottom = PrevPt, top = CurrPt;
2452 pETEs->ClockWise = 0;
2454 else
2456 bottom = CurrPt, top = PrevPt;
2457 pETEs->ClockWise = 1;
2461 * don't add horizontal edges to the Edge table.
2463 if (bottom->y != top->y)
2465 pETEs->ymax = bottom->y-1;
2466 /* -1 so we don't get last scanline */
2469 * initialize integer edge algorithm
2471 dy = bottom->y - top->y;
2472 BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres);
2474 REGION_InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock,
2475 &iSLLBlock);
2477 if (PrevPt->y > ET->ymax)
2478 ET->ymax = PrevPt->y;
2479 if (PrevPt->y < ET->ymin)
2480 ET->ymin = PrevPt->y;
2481 pETEs++;
2484 PrevPt = CurrPt;
2489 /***********************************************************************
2490 * REGION_loadAET
2492 * This routine moves EdgeTableEntries from the
2493 * EdgeTable into the Active Edge Table,
2494 * leaving them sorted by smaller x coordinate.
2497 static void REGION_loadAET(EdgeTableEntry *AET, EdgeTableEntry *ETEs)
2499 EdgeTableEntry *pPrevAET;
2500 EdgeTableEntry *tmp;
2502 pPrevAET = AET;
2503 AET = AET->next;
2504 while (ETEs)
2506 while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis))
2508 pPrevAET = AET;
2509 AET = AET->next;
2511 tmp = ETEs->next;
2512 ETEs->next = AET;
2513 if (AET)
2514 AET->back = ETEs;
2515 ETEs->back = pPrevAET;
2516 pPrevAET->next = ETEs;
2517 pPrevAET = ETEs;
2519 ETEs = tmp;
2523 /***********************************************************************
2524 * REGION_computeWAET
2526 * This routine links the AET by the
2527 * nextWETE (winding EdgeTableEntry) link for
2528 * use by the winding number rule. The final
2529 * Active Edge Table (AET) might look something
2530 * like:
2532 * AET
2533 * ---------- --------- ---------
2534 * |ymax | |ymax | |ymax |
2535 * | ... | |... | |... |
2536 * |next |->|next |->|next |->...
2537 * |nextWETE| |nextWETE| |nextWETE|
2538 * --------- --------- ^--------
2539 * | | |
2540 * V-------------------> V---> ...
2543 static void REGION_computeWAET(EdgeTableEntry *AET)
2545 register EdgeTableEntry *pWETE;
2546 register int inside = 1;
2547 register int isInside = 0;
2549 AET->nextWETE = (EdgeTableEntry *)NULL;
2550 pWETE = AET;
2551 AET = AET->next;
2552 while (AET)
2554 if (AET->ClockWise)
2555 isInside++;
2556 else
2557 isInside--;
2559 if ((!inside && !isInside) ||
2560 ( inside && isInside))
2562 pWETE->nextWETE = AET;
2563 pWETE = AET;
2564 inside = !inside;
2566 AET = AET->next;
2568 pWETE->nextWETE = (EdgeTableEntry *)NULL;
2571 /***********************************************************************
2572 * REGION_InsertionSort
2574 * Just a simple insertion sort using
2575 * pointers and back pointers to sort the Active
2576 * Edge Table.
2579 static BOOL REGION_InsertionSort(EdgeTableEntry *AET)
2581 EdgeTableEntry *pETEchase;
2582 EdgeTableEntry *pETEinsert;
2583 EdgeTableEntry *pETEchaseBackTMP;
2584 BOOL changed = FALSE;
2586 AET = AET->next;
2587 while (AET)
2589 pETEinsert = AET;
2590 pETEchase = AET;
2591 while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
2592 pETEchase = pETEchase->back;
2594 AET = AET->next;
2595 if (pETEchase != pETEinsert)
2597 pETEchaseBackTMP = pETEchase->back;
2598 pETEinsert->back->next = AET;
2599 if (AET)
2600 AET->back = pETEinsert->back;
2601 pETEinsert->next = pETEchase;
2602 pETEchase->back->next = pETEinsert;
2603 pETEchase->back = pETEinsert;
2604 pETEinsert->back = pETEchaseBackTMP;
2605 changed = TRUE;
2608 return changed;
2611 /***********************************************************************
2612 * REGION_FreeStorage
2614 * Clean up our act.
2616 static void REGION_FreeStorage(ScanLineListBlock *pSLLBlock)
2618 ScanLineListBlock *tmpSLLBlock;
2620 while (pSLLBlock)
2622 tmpSLLBlock = pSLLBlock->next;
2623 HeapFree( GetProcessHeap(), 0, pSLLBlock );
2624 pSLLBlock = tmpSLLBlock;
2629 /***********************************************************************
2630 * REGION_PtsToRegion
2632 * Create an array of rectangles from a list of points.
2634 static int REGION_PtsToRegion(int numFullPtBlocks, int iCurPtBlock,
2635 POINTBLOCK *FirstPtBlock, WINEREGION *reg)
2637 RECT *rects;
2638 POINT *pts;
2639 POINTBLOCK *CurPtBlock;
2640 int i;
2641 RECT *extents;
2642 INT numRects;
2644 extents = &reg->extents;
2646 numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
2648 if (!(reg->rects = HeapReAlloc( GetProcessHeap(), 0, reg->rects,
2649 sizeof(RECT) * numRects )))
2650 return(0);
2652 reg->size = numRects;
2653 CurPtBlock = FirstPtBlock;
2654 rects = reg->rects - 1;
2655 numRects = 0;
2656 extents->left = LARGE_COORDINATE, extents->right = SMALL_COORDINATE;
2658 for ( ; numFullPtBlocks >= 0; numFullPtBlocks--) {
2659 /* the loop uses 2 points per iteration */
2660 i = NUMPTSTOBUFFER >> 1;
2661 if (!numFullPtBlocks)
2662 i = iCurPtBlock >> 1;
2663 for (pts = CurPtBlock->pts; i--; pts += 2) {
2664 if (pts->x == pts[1].x)
2665 continue;
2666 if (numRects && pts->x == rects->left && pts->y == rects->bottom &&
2667 pts[1].x == rects->right &&
2668 (numRects == 1 || rects[-1].top != rects->top) &&
2669 (i && pts[2].y > pts[1].y)) {
2670 rects->bottom = pts[1].y + 1;
2671 continue;
2673 numRects++;
2674 rects++;
2675 rects->left = pts->x; rects->top = pts->y;
2676 rects->right = pts[1].x; rects->bottom = pts[1].y + 1;
2677 if (rects->left < extents->left)
2678 extents->left = rects->left;
2679 if (rects->right > extents->right)
2680 extents->right = rects->right;
2682 CurPtBlock = CurPtBlock->next;
2685 if (numRects) {
2686 extents->top = reg->rects->top;
2687 extents->bottom = rects->bottom;
2688 } else {
2689 extents->left = 0;
2690 extents->top = 0;
2691 extents->right = 0;
2692 extents->bottom = 0;
2694 reg->numRects = numRects;
2696 return(TRUE);
2699 /***********************************************************************
2700 * CreatePolyPolygonRgn (GDI32.@)
2702 HRGN WINAPI CreatePolyPolygonRgn(const POINT *Pts, const INT *Count,
2703 INT nbpolygons, INT mode)
2705 HRGN hrgn;
2706 RGNOBJ *obj;
2707 WINEREGION *region;
2708 register EdgeTableEntry *pAET; /* Active Edge Table */
2709 register INT y; /* current scanline */
2710 register int iPts = 0; /* number of pts in buffer */
2711 register EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/
2712 register ScanLineList *pSLL; /* current scanLineList */
2713 register POINT *pts; /* output buffer */
2714 EdgeTableEntry *pPrevAET; /* ptr to previous AET */
2715 EdgeTable ET; /* header node for ET */
2716 EdgeTableEntry AET; /* header node for AET */
2717 EdgeTableEntry *pETEs; /* EdgeTableEntries pool */
2718 ScanLineListBlock SLLBlock; /* header for scanlinelist */
2719 int fixWAET = FALSE;
2720 POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
2721 POINTBLOCK *tmpPtBlock;
2722 int numFullPtBlocks = 0;
2723 INT poly, total;
2725 if(!(hrgn = REGION_CreateRegion(nbpolygons)))
2726 return 0;
2727 obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
2728 region = obj->rgn;
2730 /* special case a rectangle */
2732 if (((nbpolygons == 1) && ((*Count == 4) ||
2733 ((*Count == 5) && (Pts[4].x == Pts[0].x) && (Pts[4].y == Pts[0].y)))) &&
2734 (((Pts[0].y == Pts[1].y) &&
2735 (Pts[1].x == Pts[2].x) &&
2736 (Pts[2].y == Pts[3].y) &&
2737 (Pts[3].x == Pts[0].x)) ||
2738 ((Pts[0].x == Pts[1].x) &&
2739 (Pts[1].y == Pts[2].y) &&
2740 (Pts[2].x == Pts[3].x) &&
2741 (Pts[3].y == Pts[0].y))))
2743 SetRectRgn( hrgn, min(Pts[0].x, Pts[2].x), min(Pts[0].y, Pts[2].y),
2744 max(Pts[0].x, Pts[2].x), max(Pts[0].y, Pts[2].y) );
2745 GDI_ReleaseObj( hrgn );
2746 return hrgn;
2749 for(poly = total = 0; poly < nbpolygons; poly++)
2750 total += Count[poly];
2751 if (! (pETEs = HeapAlloc( GetProcessHeap(), 0, sizeof(EdgeTableEntry) * total )))
2753 REGION_DeleteObject( hrgn, obj );
2754 return 0;
2756 pts = FirstPtBlock.pts;
2757 REGION_CreateETandAET(Count, nbpolygons, Pts, &ET, &AET, pETEs, &SLLBlock);
2758 pSLL = ET.scanlines.next;
2759 curPtBlock = &FirstPtBlock;
2761 if (mode != WINDING) {
2763 * for each scanline
2765 for (y = ET.ymin; y < ET.ymax; y++) {
2767 * Add a new edge to the active edge table when we
2768 * get to the next edge.
2770 if (pSLL != NULL && y == pSLL->scanline) {
2771 REGION_loadAET(&AET, pSLL->edgelist);
2772 pSLL = pSLL->next;
2774 pPrevAET = &AET;
2775 pAET = AET.next;
2778 * for each active edge
2780 while (pAET) {
2781 pts->x = pAET->bres.minor_axis, pts->y = y;
2782 pts++, iPts++;
2785 * send out the buffer
2787 if (iPts == NUMPTSTOBUFFER) {
2788 tmpPtBlock = HeapAlloc( GetProcessHeap(), 0, sizeof(POINTBLOCK));
2789 if(!tmpPtBlock) {
2790 WARN("Can't alloc tPB\n");
2791 return 0;
2793 curPtBlock->next = tmpPtBlock;
2794 curPtBlock = tmpPtBlock;
2795 pts = curPtBlock->pts;
2796 numFullPtBlocks++;
2797 iPts = 0;
2799 EVALUATEEDGEEVENODD(pAET, pPrevAET, y);
2801 REGION_InsertionSort(&AET);
2804 else {
2806 * for each scanline
2808 for (y = ET.ymin; y < ET.ymax; y++) {
2810 * Add a new edge to the active edge table when we
2811 * get to the next edge.
2813 if (pSLL != NULL && y == pSLL->scanline) {
2814 REGION_loadAET(&AET, pSLL->edgelist);
2815 REGION_computeWAET(&AET);
2816 pSLL = pSLL->next;
2818 pPrevAET = &AET;
2819 pAET = AET.next;
2820 pWETE = pAET;
2823 * for each active edge
2825 while (pAET) {
2827 * add to the buffer only those edges that
2828 * are in the Winding active edge table.
2830 if (pWETE == pAET) {
2831 pts->x = pAET->bres.minor_axis, pts->y = y;
2832 pts++, iPts++;
2835 * send out the buffer
2837 if (iPts == NUMPTSTOBUFFER) {
2838 tmpPtBlock = HeapAlloc( GetProcessHeap(), 0,
2839 sizeof(POINTBLOCK) );
2840 if(!tmpPtBlock) {
2841 WARN("Can't alloc tPB\n");
2842 REGION_DeleteObject( hrgn, obj );
2843 return 0;
2845 curPtBlock->next = tmpPtBlock;
2846 curPtBlock = tmpPtBlock;
2847 pts = curPtBlock->pts;
2848 numFullPtBlocks++; iPts = 0;
2850 pWETE = pWETE->nextWETE;
2852 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
2856 * recompute the winding active edge table if
2857 * we just resorted or have exited an edge.
2859 if (REGION_InsertionSort(&AET) || fixWAET) {
2860 REGION_computeWAET(&AET);
2861 fixWAET = FALSE;
2865 REGION_FreeStorage(SLLBlock.next);
2866 REGION_PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
2867 region->type = (region->numRects) ?
2868 ((region->numRects > 1) ? COMPLEXREGION : SIMPLEREGION)
2869 : NULLREGION;
2871 for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) {
2872 tmpPtBlock = curPtBlock->next;
2873 HeapFree( GetProcessHeap(), 0, curPtBlock );
2874 curPtBlock = tmpPtBlock;
2876 HeapFree( GetProcessHeap(), 0, pETEs );
2877 GDI_ReleaseObj( hrgn );
2878 return hrgn;
2882 /***********************************************************************
2883 * CreatePolygonRgn (GDI.63)
2885 HRGN16 WINAPI CreatePolygonRgn16( const POINT16 * points, INT16 count,
2886 INT16 mode )
2888 return CreatePolyPolygonRgn16( points, &count, 1, mode );
2891 /***********************************************************************
2892 * CreatePolyPolygonRgn (GDI.451)
2894 HRGN16 WINAPI CreatePolyPolygonRgn16( const POINT16 *points,
2895 const INT16 *count, INT16 nbpolygons, INT16 mode )
2897 HRGN hrgn;
2898 int i, npts = 0;
2899 INT *count32;
2900 POINT *points32;
2902 for (i = 0; i < nbpolygons; i++)
2903 npts += count[i];
2904 points32 = HeapAlloc( GetProcessHeap(), 0, npts * sizeof(POINT) );
2905 for (i = 0; i < npts; i++)
2906 CONV_POINT16TO32( &(points[i]), &(points32[i]) );
2908 count32 = HeapAlloc( GetProcessHeap(), 0, nbpolygons * sizeof(INT) );
2909 for (i = 0; i < nbpolygons; i++)
2910 count32[i] = count[i];
2911 hrgn = CreatePolyPolygonRgn( points32, count32, nbpolygons, mode );
2912 HeapFree( GetProcessHeap(), 0, count32 );
2913 HeapFree( GetProcessHeap(), 0, points32 );
2914 return hrgn;
2917 /***********************************************************************
2918 * CreatePolygonRgn (GDI32.@)
2920 HRGN WINAPI CreatePolygonRgn( const POINT *points, INT count,
2921 INT mode )
2923 return CreatePolyPolygonRgn( points, &count, 1, mode );
2927 /***********************************************************************
2928 * GetRandomRgn [GDI32.@]
2930 * NOTES
2931 * This function is documented in MSDN online
2933 INT WINAPI GetRandomRgn(HDC hDC, HRGN hRgn, DWORD dwCode)
2935 switch (dwCode)
2937 case 4: /* == SYSRGN ? */
2939 DC *dc = DC_GetDCPtr (hDC);
2940 OSVERSIONINFOA vi;
2941 POINT org;
2943 if (!dc) return -1;
2944 CombineRgn (hRgn, dc->hVisRgn, 0, RGN_COPY);
2946 * On Windows NT/2000,
2947 * the region returned is in screen coordinates.
2948 * On Windows 95/98,
2949 * the region returned is in window coordinates
2951 vi.dwOSVersionInfoSize = sizeof(vi);
2952 if (GetVersionExA( &vi ) && vi.dwPlatformId == VER_PLATFORM_WIN32_NT)
2953 GetDCOrgEx(hDC, &org);
2954 else
2955 org.x = org.y = 0;
2956 org.x -= dc->DCOrgX;
2957 org.y -= dc->DCOrgY;
2958 OffsetRgn (hRgn, org.x, org.y);
2959 GDI_ReleaseObj( hDC );
2960 return 1;
2962 /* case 1:
2963 return GetClipRgn (hDC, hRgn);
2965 default:
2966 WARN("Unknown dwCode %ld\n", dwCode);
2967 return -1;
2970 return -1;
2973 /***********************************************************************
2974 * REGION_CropAndOffsetRegion
2976 static BOOL REGION_CropAndOffsetRegion(const POINT* off, const RECT *rect, WINEREGION *rgnSrc, WINEREGION* rgnDst)
2979 if( !rect ) /* just copy and offset */
2981 RECT *xrect;
2982 if( rgnDst == rgnSrc )
2984 if( off->x || off->y )
2985 xrect = rgnDst->rects;
2986 else
2987 return TRUE;
2989 else
2990 xrect = HeapReAlloc( GetProcessHeap(), 0, rgnDst->rects,
2991 rgnSrc->size * sizeof( RECT ));
2992 if( xrect )
2994 INT i;
2996 if( rgnDst != rgnSrc )
2997 memcpy( rgnDst, rgnSrc, sizeof( WINEREGION ));
2999 if( off->x || off->y )
3001 for( i = 0; i < rgnDst->numRects; i++ )
3003 xrect[i].left = rgnSrc->rects[i].left + off->x;
3004 xrect[i].right = rgnSrc->rects[i].right + off->x;
3005 xrect[i].top = rgnSrc->rects[i].top + off->y;
3006 xrect[i].bottom = rgnSrc->rects[i].bottom + off->y;
3008 rgnDst->extents.left += off->x;
3009 rgnDst->extents.right += off->x;
3010 rgnDst->extents.top += off->y;
3011 rgnDst->extents.bottom += off->y;
3013 else
3014 memcpy( xrect, rgnSrc->rects, rgnDst->numRects * sizeof(RECT));
3015 rgnDst->rects = xrect;
3016 } else
3017 return FALSE;
3019 else if ((rect->left >= rect->right) ||
3020 (rect->top >= rect->bottom) ||
3021 !EXTENTCHECK(rect, &rgnSrc->extents))
3023 empty:
3024 if( !rgnDst->rects )
3026 rgnDst->rects = HeapAlloc(GetProcessHeap(), 0, RGN_DEFAULT_RECTS * sizeof( RECT ));
3027 if( rgnDst->rects )
3028 rgnDst->size = RGN_DEFAULT_RECTS;
3029 else
3030 return FALSE;
3033 TRACE("cropped to empty!\n");
3034 EMPTY_REGION(rgnDst);
3036 else /* region box and clipping rect appear to intersect */
3038 RECT *lpr;
3039 INT i, j, clipa, clipb;
3040 INT left = rgnSrc->extents.right + off->x;
3041 INT right = rgnSrc->extents.left + off->x;
3043 for( clipa = 0; rgnSrc->rects[clipa].bottom <= rect->top; clipa++ )
3044 ; /* skip bands above the clipping rectangle */
3046 for( clipb = clipa; clipb < rgnSrc->numRects; clipb++ )
3047 if( rgnSrc->rects[clipb].top >= rect->bottom )
3048 break; /* and below it */
3050 /* clipa - index of the first rect in the first intersecting band
3051 * clipb - index of the last rect in the last intersecting band
3054 if((rgnDst != rgnSrc) && (rgnDst->size < (i = (clipb - clipa))))
3056 rgnDst->rects = HeapReAlloc( GetProcessHeap(), 0,
3057 rgnDst->rects, i * sizeof(RECT));
3058 if( !rgnDst->rects ) return FALSE;
3059 rgnDst->size = i;
3062 if( TRACE_ON(region) )
3064 REGION_DumpRegion( rgnSrc );
3065 TRACE("\tclipa = %i, clipb = %i\n", clipa, clipb );
3068 for( i = clipa, j = 0; i < clipb ; i++ )
3070 /* i - src index, j - dst index, j is always <= i for obvious reasons */
3072 lpr = rgnSrc->rects + i;
3073 if( lpr->left < rect->right && lpr->right > rect->left )
3075 rgnDst->rects[j].top = lpr->top + off->y;
3076 rgnDst->rects[j].bottom = lpr->bottom + off->y;
3077 rgnDst->rects[j].left = ((lpr->left > rect->left) ? lpr->left : rect->left) + off->x;
3078 rgnDst->rects[j].right = ((lpr->right < rect->right) ? lpr->right : rect->right) + off->x;
3080 if( rgnDst->rects[j].left < left ) left = rgnDst->rects[j].left;
3081 if( rgnDst->rects[j].right > right ) right = rgnDst->rects[j].right;
3083 j++;
3087 if( j == 0 ) goto empty;
3089 rgnDst->extents.left = left;
3090 rgnDst->extents.right = right;
3092 left = rect->top + off->y;
3093 right = rect->bottom + off->y;
3095 rgnDst->numRects = j--;
3096 for( i = 0; i <= j; i++ ) /* fixup top band */
3097 if( rgnDst->rects[i].top < left )
3098 rgnDst->rects[i].top = left;
3099 else
3100 break;
3102 for( i = j; i >= 0; i-- ) /* fixup bottom band */
3103 if( rgnDst->rects[i].bottom > right )
3104 rgnDst->rects[i].bottom = right;
3105 else
3106 break;
3108 rgnDst->extents.top = rgnDst->rects[0].top;
3109 rgnDst->extents.bottom = rgnDst->rects[j].bottom;
3111 rgnDst->type = (j >= 1) ? COMPLEXREGION : SIMPLEREGION;
3113 if( TRACE_ON(region) )
3115 TRACE("result:\n");
3116 REGION_DumpRegion( rgnDst );
3120 return TRUE;
3123 /***********************************************************************
3124 * REGION_CropRgn
3127 * hSrc: Region to crop and offset.
3128 * lpRect: Clipping rectangle. Can be NULL (no clipping).
3129 * lpPt: Points to offset the cropped region. Can be NULL (no offset).
3131 * hDst: Region to hold the result (a new region is created if it's 0).
3132 * Allowed to be the same region as hSrc in which case everything
3133 * will be done in place, with no memory reallocations.
3135 * Returns: hDst if success, 0 otherwise.
3137 HRGN REGION_CropRgn( HRGN hDst, HRGN hSrc, const RECT *lpRect, const POINT *lpPt )
3139 /* Optimization of the following generic code:
3141 HRGN h;
3143 if( lpRect )
3144 h = CreateRectRgn( lpRect->left, lpRect->top, lpRect->right, lpRect->bottom );
3145 else
3146 h = CreateRectRgn( 0, 0, 0, 0 );
3147 if( hDst == 0 ) hDst = h;
3148 if( lpRect )
3149 CombineRgn( hDst, hSrc, h, RGN_AND );
3150 else
3151 CombineRgn( hDst, hSrc, 0, RGN_COPY );
3152 if( lpPt )
3153 OffsetRgn( hDst, lpPt->x, lpPt->y );
3154 if( hDst != h )
3155 DeleteObject( h );
3156 return hDst;
3160 RGNOBJ *objSrc = (RGNOBJ *) GDI_GetObjPtr( hSrc, REGION_MAGIC );
3162 if(objSrc)
3164 RGNOBJ *objDst;
3165 WINEREGION *rgnDst;
3167 if( hDst )
3169 if (!(objDst = (RGNOBJ *) GDI_GetObjPtr( hDst, REGION_MAGIC )))
3171 hDst = 0;
3172 goto done;
3174 rgnDst = objDst->rgn;
3176 else
3178 if ((rgnDst = HeapAlloc(GetProcessHeap(), 0, sizeof( WINEREGION ))))
3180 rgnDst->size = rgnDst->numRects = 0;
3181 rgnDst->rects = NULL; /* back end will allocate exact number */
3185 if( rgnDst )
3187 POINT pt = { 0, 0 };
3189 if( !lpPt ) lpPt = &pt;
3191 if( lpRect )
3192 TRACE("src %p -> dst %p (%i,%i)-(%i,%i) by (%li,%li)\n", objSrc->rgn, rgnDst,
3193 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom, lpPt->x, lpPt->y );
3194 else
3195 TRACE("src %p -> dst %p by (%li,%li)\n", objSrc->rgn, rgnDst, lpPt->x, lpPt->y );
3197 if( REGION_CropAndOffsetRegion( lpPt, lpRect, objSrc->rgn, rgnDst ) == FALSE )
3199 if( hDst ) /* existing rgn */
3201 GDI_ReleaseObj(hDst);
3202 hDst = 0;
3203 goto done;
3205 goto fail;
3207 else if( hDst == 0 )
3209 if (!(objDst = GDI_AllocObject( sizeof(RGNOBJ), REGION_MAGIC, &hDst )))
3211 fail:
3212 if( rgnDst->rects )
3213 HeapFree( GetProcessHeap(), 0, rgnDst->rects );
3214 HeapFree( GetProcessHeap(), 0, rgnDst );
3215 goto done;
3217 objDst->rgn = rgnDst;
3220 GDI_ReleaseObj(hDst);
3222 else hDst = 0;
3223 done:
3224 GDI_ReleaseObj(hSrc);
3225 return hDst;
3227 return 0;
3230 /***********************************************************************
3231 * GetMetaRgn (GDI32.@)
3233 INT WINAPI GetMetaRgn( HDC hdc, HRGN hRgn )
3235 FIXME( "stub\n" );
3237 return 0;
3241 /***********************************************************************
3242 * SetMetaRgn (GDI32.@)
3244 INT WINAPI SetMetaRgn( HDC hdc )
3246 FIXME( "stub\n" );
3248 return ERROR;