Release 950727
[wine] / objects / region.c
1 /*
2  * GDI region objects
3  *
4  * Copyright 1993, 1994, 1995 Alexandre Julliard
5  *
6  */
7
8 #include <stdlib.h>
9 #include <stdio.h>
10
11 #include "region.h"
12 #include "stddebug.h"
13 /* #define DEBUG_REGION */
14 #include "debug.h"
15
16
17 /***********************************************************************
18  *           REGION_DeleteObject
19  */
20 BOOL REGION_DeleteObject( HRGN hrgn, RGNOBJ * obj )
21 {
22     dprintf_region(stddeb, "DeleteRegion: %x\n", hrgn );
23     if (obj->xrgn) XDestroyRegion( obj->xrgn );
24     return GDI_FreeObject( hrgn );
25 }
26
27
28 /***********************************************************************
29  *           OffsetRgn    (GDI.101)
30  */
31 int OffsetRgn( HRGN hrgn, short x, short y )
32 {
33     RGNOBJ * obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
34     if (!obj) return ERROR;
35     dprintf_region(stddeb, "OffsetRgn: %d %d,%d\n", hrgn, x, y );
36     if (!obj->xrgn) return NULLREGION;
37     XOffsetRegion( obj->xrgn, x, y );
38     return COMPLEXREGION;
39 }
40
41
42 /***********************************************************************
43  *           GetRgnBox    (GDI.134)
44  */
45 int GetRgnBox( HRGN hrgn, LPRECT rect )
46 {
47     RGNOBJ * obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC );
48     if (!obj) return ERROR;
49     dprintf_region(stddeb, "GetRgnBox: %d\n", hrgn );
50     if (!obj->xrgn)
51     {
52         SetRectEmpty( rect );
53         return NULLREGION;
54     }
55     else
56     {
57         XRectangle xrect;
58         XClipBox( obj->xrgn, &xrect );
59         SetRect( rect, xrect.x, xrect.y,
60                  xrect.x + xrect.width, xrect.y + xrect.height);
61         return COMPLEXREGION;
62     }
63 }
64
65
66 /***********************************************************************
67  *           CreateRectRgn    (GDI.64)
68  */
69 HRGN CreateRectRgn( short left, short top, short right, short bottom )
70 {
71     HRGN hrgn;
72     RGNOBJ *obj;
73
74     if (!(hrgn = GDI_AllocObject( sizeof(RGNOBJ), REGION_MAGIC ))) return 0;
75     obj = (RGNOBJ *) GDI_HEAP_LIN_ADDR( hrgn );
76     if ((right > left) && (bottom > top))
77     {
78         XRectangle rect = { left, top, right - left, bottom - top };
79         if (!(obj->xrgn = XCreateRegion()))
80         {
81             GDI_FreeObject( hrgn );
82             return 0;
83         }
84         XUnionRectWithRegion( &rect, obj->xrgn, obj->xrgn );
85     }
86     else obj->xrgn = 0;
87     dprintf_region( stddeb, "CreateRectRgn(%d,%d-%d,%d): returning %x\n",
88                     left, top, right, bottom, hrgn );
89     return hrgn;
90 }
91
92
93 /***********************************************************************
94  *           CreateRectRgnIndirect    (GDI.65)
95  */
96 HRGN CreateRectRgnIndirect( LPRECT rect )
97 {
98     return CreateRectRgn( rect->left, rect->top, rect->right, rect->bottom );
99 }
100
101
102 /***********************************************************************
103  *           SetRectRgn    (GDI.172)
104  */
105 void SetRectRgn( HRGN hrgn, short left, short top, short right, short bottom )
106 {
107     RGNOBJ * obj;
108
109     dprintf_region(stddeb, "SetRectRgn: %x %d,%d-%d,%d\n", 
110                    hrgn, left, top, right, bottom );
111     
112     if (!(obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC ))) return;
113     if (obj->xrgn) XDestroyRegion( obj->xrgn );
114     if ((right > left) && (bottom > top))
115     {
116         XRectangle rect = { left, top, right - left, bottom - top };
117         if ((obj->xrgn = XCreateRegion()) != 0)
118             XUnionRectWithRegion( &rect, obj->xrgn, obj->xrgn );
119     }
120     else obj->xrgn = 0;
121 }
122
123
124 /***********************************************************************
125  *           CreateRoundRectRgn    (GDI.444)
126  */
127 HRGN CreateRoundRectRgn( short left, short top, short right, short bottom,
128                          short ellipse_width, short ellipse_height )
129 {
130     RGNOBJ * obj;
131     HRGN hrgn;
132     XRectangle rect;
133     int asq, bsq, d, xd, yd;
134
135       /* Check if we can do a normal rectangle instead */
136
137     if ((right <= left) || (bottom <= top) ||
138         (ellipse_width <= 0) || (ellipse_height <= 0))
139         return CreateRectRgn( left, top, right, bottom );
140
141       /* Create region */
142
143     if (!(hrgn = GDI_AllocObject( sizeof(RGNOBJ), REGION_MAGIC ))) return 0;
144     obj = (RGNOBJ *) GDI_HEAP_LIN_ADDR( hrgn );
145     obj->xrgn = XCreateRegion();
146     dprintf_region(stddeb,"CreateRoundRectRgn(%d,%d-%d,%d %dx%d): return=%x\n",
147                left, top, right, bottom, ellipse_width, ellipse_height, hrgn );
148
149       /* Check parameters */
150
151     if (ellipse_width > right-left) ellipse_width = right-left;
152     if (ellipse_height > bottom-top) ellipse_height = bottom-top;
153
154       /* Ellipse algorithm, based on an article by K. Porter */
155       /* in DDJ Graphics Programming Column, 8/89 */
156
157     asq = ellipse_width * ellipse_width / 4;        /* a^2 */
158     bsq = ellipse_height * ellipse_height / 4;      /* b^2 */
159     d = bsq - asq * ellipse_height / 2 + asq / 4;   /* b^2 - a^2b + a^2/4 */
160     xd = 0;
161     yd = asq * ellipse_height;                      /* 2a^2b */
162
163     rect.x      = left + ellipse_width / 2;
164     rect.width  = right - left - ellipse_width;
165     rect.height = 1;
166
167       /* Loop to draw first half of quadrant */
168
169     while (xd < yd)
170     {
171         if (d > 0)  /* if nearest pixel is toward the center */
172         {
173               /* move toward center */
174             rect.y = top++;
175             XUnionRectWithRegion( &rect, obj->xrgn, obj->xrgn );
176             rect.y = --bottom;
177             XUnionRectWithRegion( &rect, obj->xrgn, obj->xrgn );
178             yd -= 2*asq;
179             d  -= yd;
180         }
181         rect.x--;        /* next horiz point */
182         rect.width += 2;
183         xd += 2*bsq;
184         d  += bsq + xd;
185     }
186
187       /* Loop to draw second half of quadrant */
188
189     d += (3 * (asq-bsq) / 2 - (xd+yd)) / 2;
190     while (yd >= 0)
191     {
192           /* next vertical point */
193         rect.y = top++;
194         XUnionRectWithRegion( &rect, obj->xrgn, obj->xrgn );
195         rect.y = --bottom;
196         XUnionRectWithRegion( &rect, obj->xrgn, obj->xrgn );
197         if (d < 0)   /* if nearest pixel is outside ellipse */
198         {
199             rect.x--;     /* move away from center */
200             rect.width += 2;
201             xd += 2*bsq;
202             d  += xd;
203         }
204         yd -= 2*asq;
205         d  += asq - yd;
206     }
207
208       /* Add the inside rectangle */
209
210     if (top <= bottom)
211     {
212         rect.y = top;
213         rect.height = bottom - top + 1;
214         XUnionRectWithRegion( &rect, obj->xrgn, obj->xrgn );
215     }
216     return hrgn;
217 }
218
219
220 /***********************************************************************
221  *           CreateEllipticRgn    (GDI.54)
222  */
223 HRGN CreateEllipticRgn( short left, short top, short right, short bottom )
224 {
225     return CreateRoundRectRgn( left, top, right, bottom,
226                                right-left, bottom-top );
227 }
228
229
230 /***********************************************************************
231  *           CreateEllipticRgnIndirect    (GDI.55)
232  */
233 HRGN CreateEllipticRgnIndirect( LPRECT rect )
234 {
235     return CreateRoundRectRgn(rect->left, rect->top, rect->right, rect->bottom,
236                               rect->right-rect->left, rect->bottom-rect->top );
237 }
238
239
240 /***********************************************************************
241  *           CreatePolygonRgn    (GDI.63)
242  */
243 HRGN CreatePolygonRgn( POINT * points, short count, short mode )
244 {
245     return CreatePolyPolygonRgn( points, &count, 1, mode );
246 }
247
248
249 /***********************************************************************
250  *           CreatePolyPolygonRgn    (GDI.451)
251  */
252 HRGN CreatePolyPolygonRgn( POINT * points, short * count,
253                            short nbpolygons, short mode )
254 {
255     RGNOBJ * obj;
256     HRGN hrgn;
257     int i, j, maxPoints;
258     XPoint *xpoints, *pt;
259     Region xrgn;
260
261       /* Allocate points array */
262
263     if (!nbpolygons) return 0;
264     for (i = maxPoints = 0; i < nbpolygons; i++)
265         if (maxPoints < count[i]) maxPoints = count[i];
266     if (!maxPoints) return 0;
267     if (!(xpoints = (XPoint *) malloc( sizeof(XPoint) * maxPoints )))
268         return 0;
269
270       /* Allocate region */
271
272     if (!(hrgn = GDI_AllocObject( sizeof(RGNOBJ), REGION_MAGIC )))
273     {
274         free( xpoints );
275         return 0;
276     }
277     obj = (RGNOBJ *) GDI_HEAP_LIN_ADDR( hrgn );
278     obj->xrgn = 0;
279     dprintf_region(stddeb, "CreatePolyPolygonRgn: %d polygons, returning %x\n",
280                    nbpolygons, hrgn );
281
282       /* Create X region */
283
284     for (i = 0; i < nbpolygons; i++, count++)
285     {
286         for (j = *count, pt = xpoints; j > 0; j--, points++, pt++)
287         {
288             pt->x = points->x;
289             pt->y = points->y;
290         }
291         xrgn = XPolygonRegion( xpoints, *count,
292                                (mode == WINDING) ? WindingRule : EvenOddRule );
293         if (!xrgn)
294         {
295             if (obj->xrgn) XDestroyRegion( obj->xrgn );
296             free( xpoints );
297             GDI_FreeObject( hrgn );
298             return 0;
299         }
300         if (i > 0)
301         {
302             Region tmprgn = XCreateRegion();
303             if (mode == WINDING) XUnionRegion( xrgn, obj->xrgn, tmprgn );
304             else XXorRegion( xrgn, obj->xrgn, tmprgn );
305             XDestroyRegion( obj->xrgn );
306             obj->xrgn = tmprgn;
307         }
308         else obj->xrgn = xrgn;
309     }
310
311     free( xpoints );
312     return hrgn;
313 }
314
315
316 /***********************************************************************
317  *           PtInRegion    (GDI.161)
318  */
319 BOOL PtInRegion( HRGN hrgn, short x, short y )
320 {
321     RGNOBJ * obj;
322     
323     if (!(obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC ))) return FALSE;
324     if (!obj->xrgn) return FALSE;
325     return XPointInRegion( obj->xrgn, x, y );
326 }
327
328
329 /***********************************************************************
330  *           RectInRegion    (GDI.181)
331  */
332 BOOL RectInRegion( HRGN hrgn, LPRECT rect )
333 {
334     RGNOBJ * obj;
335     
336     if (!(obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC ))) return FALSE;
337     if (!obj->xrgn) return FALSE;
338     return (XRectInRegion( obj->xrgn, rect->left, rect->top,
339                            rect->right-rect->left,
340                            rect->bottom-rect->top ) != RectangleOut);
341 }
342
343
344 /***********************************************************************
345  *           EqualRgn    (GDI.72)
346  */
347 BOOL EqualRgn( HRGN rgn1, HRGN rgn2 )
348 {
349     RGNOBJ *obj1, *obj2;
350     if (!(obj1 = (RGNOBJ *) GDI_GetObjPtr( rgn1, REGION_MAGIC ))) return FALSE;
351     if (!(obj2 = (RGNOBJ *) GDI_GetObjPtr( rgn2, REGION_MAGIC ))) return FALSE;
352     if (!obj1->xrgn || !obj2->xrgn) return (!obj1->xrgn && !obj2->xrgn);
353     return XEqualRegion( obj1->xrgn, obj2->xrgn );
354 }
355
356
357 /***********************************************************************
358  *           REGION_CopyRegion
359  *
360  * Copy region src into dest.
361  */
362 static int REGION_CopyRegion( RGNOBJ *src, RGNOBJ *dest )
363 {
364     if (src->xrgn)
365     {
366         Region tmprgn = XCreateRegion();
367         if (!dest->xrgn) dest->xrgn = XCreateRegion();
368         XUnionRegion( tmprgn, src->xrgn, dest->xrgn );
369         XDestroyRegion( tmprgn );
370         return COMPLEXREGION;
371     }
372     else
373     {
374         if (dest->xrgn) XDestroyRegion( dest->xrgn );
375         dest->xrgn = 0;
376         return NULLREGION;
377     }
378 }
379
380 /***********************************************************************
381  *           REGION_CreateFrameRgn
382  *
383  * Create a region that is a frame around another region
384  */
385 BOOL REGION_FrameRgn( HRGN hDest, HRGN hSrc, int x, int y )
386 {
387     RGNOBJ *destObj,*srcObj;
388     Region result;
389
390     destObj = (RGNOBJ*) GDI_GetObjPtr( hDest, REGION_MAGIC );
391     srcObj  = (RGNOBJ*) GDI_GetObjPtr( hSrc, REGION_MAGIC );
392     if (!srcObj->xrgn) return 0;
393     REGION_CopyRegion( srcObj, destObj );
394     XShrinkRegion( destObj->xrgn, -x, -y );
395     result = XCreateRegion();
396     XSubtractRegion( destObj->xrgn, srcObj->xrgn, result );
397     XDestroyRegion( destObj->xrgn );
398     destObj->xrgn = result;
399     return 1;
400 }
401
402 /***********************************************************************
403  *           CombineRgn    (GDI.451)
404  */
405 int CombineRgn( HRGN hDest, HRGN hSrc1, HRGN hSrc2, short mode )
406 {
407     RGNOBJ *destObj, *src1Obj, *src2Obj;
408     
409     dprintf_region(stddeb, "CombineRgn: %x,%x -> %x mode=%x\n", 
410                    hSrc1, hSrc2, hDest, mode );
411     
412     if (!(destObj = (RGNOBJ *) GDI_GetObjPtr( hDest, REGION_MAGIC )))
413         return ERROR;
414     if (!(src1Obj = (RGNOBJ *) GDI_GetObjPtr( hSrc1, REGION_MAGIC )))
415         return ERROR;
416     if (mode == RGN_COPY) return REGION_CopyRegion( src1Obj, destObj );
417
418     if (!(src2Obj = (RGNOBJ *) GDI_GetObjPtr( hSrc2, REGION_MAGIC )))
419         return ERROR;
420
421       /* Some optimizations for null regions */
422
423     if (!src1Obj->xrgn || !src2Obj->xrgn)
424     {
425         switch(mode)
426         {
427         case RGN_DIFF:
428             if (src1Obj->xrgn)
429                 return REGION_CopyRegion( src1Obj, destObj );
430             /* else fall through */
431         case RGN_AND:
432             if (destObj->xrgn) XDestroyRegion( destObj->xrgn );
433             destObj->xrgn = 0;
434             return NULLREGION;
435         case RGN_OR:
436         case RGN_XOR:
437             if (src1Obj->xrgn)
438                 return REGION_CopyRegion( src1Obj, destObj );
439             else
440                 return REGION_CopyRegion( src2Obj, destObj );
441         default:
442             return ERROR;
443         }
444     }
445
446       /* Perform the operation with the two X regions */
447
448     if (!destObj->xrgn) destObj->xrgn = XCreateRegion();
449     switch(mode)
450     {
451     case RGN_AND:
452         XIntersectRegion( src1Obj->xrgn, src2Obj->xrgn, destObj->xrgn );
453         break;
454     case RGN_OR:
455         XUnionRegion( src1Obj->xrgn, src2Obj->xrgn, destObj->xrgn );
456         break;
457     case RGN_XOR:
458         XXorRegion( src1Obj->xrgn, src2Obj->xrgn, destObj->xrgn );
459         break;
460     case RGN_DIFF:
461         XSubtractRegion( src1Obj->xrgn, src2Obj->xrgn, destObj->xrgn );
462         break;
463     default:
464         return ERROR;
465     }
466     if (XEmptyRegion(destObj->xrgn))
467     {
468         XDestroyRegion( destObj->xrgn );
469         destObj->xrgn = 0;
470         return NULLREGION;
471     }
472     else return COMPLEXREGION;
473 }