gdi32: Handle ArcTo in paths as native.
[wine] / dlls / gdi32 / clipping.c
1 /*
2  * DC clipping functions
3  *
4  * Copyright 1993 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22 #include <stdlib.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "wingdi.h"
26 #include "wownt32.h"
27 #include "wine/winuser16.h"
28 #include "gdi_private.h"
29 #include "wine/debug.h"
30
31 WINE_DEFAULT_DEBUG_CHANNEL(clipping);
32
33
34 /***********************************************************************
35  *           get_clip_region
36  *
37  * Return the total clip region (if any).
38  */
39 static inline HRGN get_clip_region( DC * dc )
40 {
41     if (dc->hMetaClipRgn) return dc->hMetaClipRgn;
42     if (dc->hMetaRgn) return dc->hMetaRgn;
43     return dc->hClipRgn;
44 }
45
46
47 /***********************************************************************
48  *           CLIPPING_UpdateGCRegion
49  *
50  * Update the GC clip region when the ClipRgn or VisRgn have changed.
51  */
52 void CLIPPING_UpdateGCRegion( DC * dc )
53 {
54     HRGN clip_rgn;
55
56     if (!dc->hVisRgn)
57     {
58         ERR("hVisRgn is zero. Please report this.\n" );
59         exit(1);
60     }
61
62     if (dc->flags & DC_DIRTY) ERR( "DC is dirty. Please report this.\n" );
63
64     /* update the intersection of meta and clip regions */
65     if (dc->hMetaRgn && dc->hClipRgn)
66     {
67         if (!dc->hMetaClipRgn) dc->hMetaClipRgn = CreateRectRgn( 0, 0, 0, 0 );
68         CombineRgn( dc->hMetaClipRgn, dc->hClipRgn, dc->hMetaRgn, RGN_AND );
69         clip_rgn = dc->hMetaClipRgn;
70     }
71     else  /* only one is set, no need for an intersection */
72     {
73         if (dc->hMetaClipRgn) DeleteObject( dc->hMetaClipRgn );
74         dc->hMetaClipRgn = 0;
75         clip_rgn = dc->hMetaRgn ? dc->hMetaRgn : dc->hClipRgn;
76     }
77
78     if (dc->funcs->pSetDeviceClipping)
79         dc->funcs->pSetDeviceClipping( dc->physDev, dc->hVisRgn, clip_rgn );
80 }
81
82 /***********************************************************************
83  *           create_default_clip_region
84  *
85  * Create a default clipping region when none already exists.
86  */
87 static inline void create_default_clip_region( DC * dc )
88 {
89     UINT width, height;
90
91     if (GDIMAGIC( dc->header.wMagic ) == MEMORY_DC_MAGIC)
92     {
93         BITMAP bitmap;
94
95         GetObjectW( dc->hBitmap, sizeof(bitmap), &bitmap );
96         width = bitmap.bmWidth;
97         height = bitmap.bmHeight;
98     }
99     else
100     {
101         width = GetDeviceCaps( dc->hSelf, DESKTOPHORZRES );
102         height = GetDeviceCaps( dc->hSelf, DESKTOPVERTRES );
103     }
104     dc->hClipRgn = CreateRectRgn( 0, 0, width, height );
105 }
106
107
108 /***********************************************************************
109  *           SelectClipRgn    (GDI32.@)
110  */
111 INT WINAPI SelectClipRgn( HDC hdc, HRGN hrgn )
112 {
113     return ExtSelectClipRgn( hdc, hrgn, RGN_COPY );
114 }
115
116
117 /******************************************************************************
118  *              ExtSelectClipRgn        [GDI32.@]
119  */
120 INT WINAPI ExtSelectClipRgn( HDC hdc, HRGN hrgn, INT fnMode )
121 {
122     INT retval;
123     RECT rect;
124     DC * dc = DC_GetDCUpdate( hdc );
125     if (!dc) return ERROR;
126
127     TRACE("%p %p %d\n", hdc, hrgn, fnMode );
128
129     if (dc->funcs->pExtSelectClipRgn)
130     {
131         retval = dc->funcs->pExtSelectClipRgn( dc->physDev, hrgn, fnMode );
132         GDI_ReleaseObj( hdc );
133         return retval;
134     }
135
136     if (!hrgn)
137     {
138         if (fnMode == RGN_COPY)
139         {
140             if (dc->hClipRgn) DeleteObject( dc->hClipRgn );
141             dc->hClipRgn = 0;
142         }
143         else
144         {
145             FIXME("Unimplemented: hrgn NULL in mode: %d\n", fnMode);
146             GDI_ReleaseObj( hdc );
147             return ERROR;
148         }
149     }
150     else
151     {
152         if (!dc->hClipRgn)
153             create_default_clip_region( dc );
154
155         if(fnMode == RGN_COPY)
156             CombineRgn( dc->hClipRgn, hrgn, 0, fnMode );
157         else
158             CombineRgn( dc->hClipRgn, dc->hClipRgn, hrgn, fnMode);
159     }
160
161     CLIPPING_UpdateGCRegion( dc );
162     GDI_ReleaseObj( hdc );
163
164     return GetClipBox(hdc, &rect);
165 }
166
167 /***********************************************************************
168  *           SelectVisRgn   (GDI.105)
169  */
170 INT16 WINAPI SelectVisRgn16( HDC16 hdc16, HRGN16 hrgn )
171 {
172     int retval;
173     HDC hdc = HDC_32( hdc16 );
174     DC * dc;
175
176     if (!hrgn) return ERROR;
177     if (!(dc = DC_GetDCPtr( hdc ))) return ERROR;
178
179     TRACE("%p %04x\n", hdc, hrgn );
180
181     dc->flags &= ~DC_DIRTY;
182
183     retval = CombineRgn( dc->hVisRgn, HRGN_32(hrgn), 0, RGN_COPY );
184     CLIPPING_UpdateGCRegion( dc );
185     GDI_ReleaseObj( hdc );
186     return retval;
187 }
188
189
190 /***********************************************************************
191  *           OffsetClipRgn    (GDI32.@)
192  */
193 INT WINAPI OffsetClipRgn( HDC hdc, INT x, INT y )
194 {
195     INT ret = SIMPLEREGION;
196     DC *dc = DC_GetDCUpdate( hdc );
197     if (!dc) return ERROR;
198
199     TRACE("%p %d,%d\n", hdc, x, y );
200
201     if(dc->funcs->pOffsetClipRgn)
202     {
203         ret = dc->funcs->pOffsetClipRgn( dc->physDev, x, y );
204         /* FIXME: ret is just a success flag, we should return a proper value */
205     }
206     else if (dc->hClipRgn) {
207         ret = OffsetRgn( dc->hClipRgn, MulDiv( x, dc->vportExtX, dc->wndExtX ),
208                          MulDiv( y, dc->vportExtY, dc->wndExtY ) );
209         CLIPPING_UpdateGCRegion( dc );
210     }
211     GDI_ReleaseObj( hdc );
212     return ret;
213 }
214
215
216 /***********************************************************************
217  *           OffsetVisRgn    (GDI.102)
218  */
219 INT16 WINAPI OffsetVisRgn16( HDC16 hdc16, INT16 x, INT16 y )
220 {
221     INT16 retval;
222     HDC hdc = HDC_32( hdc16 );
223     DC * dc = DC_GetDCUpdate( hdc );
224     if (!dc) return ERROR;
225     TRACE("%p %d,%d\n", hdc, x, y );
226     retval = OffsetRgn( dc->hVisRgn, x, y );
227     CLIPPING_UpdateGCRegion( dc );
228     GDI_ReleaseObj( hdc );
229     return retval;
230 }
231
232
233 /***********************************************************************
234  *           ExcludeClipRect    (GDI32.@)
235  */
236 INT WINAPI ExcludeClipRect( HDC hdc, INT left, INT top,
237                                 INT right, INT bottom )
238 {
239     HRGN newRgn;
240     INT ret;
241     DC *dc = DC_GetDCUpdate( hdc );
242     if (!dc) return ERROR;
243
244     TRACE("%p %dx%d,%dx%d\n", hdc, left, top, right, bottom );
245
246     if(dc->funcs->pExcludeClipRect)
247     {
248         ret = dc->funcs->pExcludeClipRect( dc->physDev, left, top, right, bottom );
249         /* FIXME: ret is just a success flag, we should return a proper value */
250     }
251     else
252     {
253         POINT pt[2];
254
255         pt[0].x = left;
256         pt[0].y = top;
257         pt[1].x = right;
258         pt[1].y = bottom;
259         LPtoDP( hdc, pt, 2 );
260         if (!(newRgn = CreateRectRgn( pt[0].x, pt[0].y, pt[1].x, pt[1].y ))) ret = ERROR;
261         else
262         {
263             if (!dc->hClipRgn)
264                 create_default_clip_region( dc );
265             ret = CombineRgn( dc->hClipRgn, dc->hClipRgn, newRgn, RGN_DIFF );
266             DeleteObject( newRgn );
267         }
268         if (ret != ERROR) CLIPPING_UpdateGCRegion( dc );
269     }
270     GDI_ReleaseObj( hdc );
271     return ret;
272 }
273
274
275 /***********************************************************************
276  *           IntersectClipRect    (GDI32.@)
277  */
278 INT WINAPI IntersectClipRect( HDC hdc, INT left, INT top, INT right, INT bottom )
279 {
280     INT ret;
281     DC *dc = DC_GetDCUpdate( hdc );
282     if (!dc) return ERROR;
283
284     TRACE("%p %d,%d - %d,%d\n", hdc, left, top, right, bottom );
285
286     if(dc->funcs->pIntersectClipRect)
287     {
288         ret = dc->funcs->pIntersectClipRect( dc->physDev, left, top, right, bottom );
289         /* FIXME: ret is just a success flag, we should return a proper value */
290     }
291     else
292     {
293         POINT pt[2];
294
295         pt[0].x = left;
296         pt[0].y = top;
297         pt[1].x = right;
298         pt[1].y = bottom;
299
300         LPtoDP( hdc, pt, 2 );
301
302         if (!dc->hClipRgn)
303         {
304             dc->hClipRgn = CreateRectRgn( pt[0].x, pt[0].y, pt[1].x, pt[1].y );
305             ret = SIMPLEREGION;
306         }
307         else
308         {
309             HRGN newRgn;
310
311             if (!(newRgn = CreateRectRgn( pt[0].x, pt[0].y, pt[1].x, pt[1].y ))) ret = ERROR;
312             else
313             {
314                 ret = CombineRgn( dc->hClipRgn, dc->hClipRgn, newRgn, RGN_AND );
315                 DeleteObject( newRgn );
316             }
317         }
318         if (ret != ERROR) CLIPPING_UpdateGCRegion( dc );
319     }
320     GDI_ReleaseObj( hdc );
321     return ret;
322 }
323
324
325 /***********************************************************************
326  *           ExcludeVisRect   (GDI.73)
327  */
328 INT16 WINAPI ExcludeVisRect16( HDC16 hdc16, INT16 left, INT16 top, INT16 right, INT16 bottom )
329 {
330     HRGN tempRgn;
331     INT16 ret;
332     POINT pt[2];
333     HDC hdc = HDC_32( hdc16 );
334     DC * dc = DC_GetDCUpdate( hdc );
335     if (!dc) return ERROR;
336
337     pt[0].x = left;
338     pt[0].y = top;
339     pt[1].x = right;
340     pt[1].y = bottom;
341
342     LPtoDP( hdc, pt, 2 );
343
344     TRACE("%p %d,%d - %d,%d\n", hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
345
346     if (!(tempRgn = CreateRectRgn( pt[0].x, pt[0].y, pt[1].x, pt[1].y ))) ret = ERROR;
347     else
348     {
349         ret = CombineRgn( dc->hVisRgn, dc->hVisRgn, tempRgn, RGN_DIFF );
350         DeleteObject( tempRgn );
351     }
352     if (ret != ERROR) CLIPPING_UpdateGCRegion( dc );
353     GDI_ReleaseObj( hdc );
354     return ret;
355 }
356
357
358 /***********************************************************************
359  *           IntersectVisRect   (GDI.98)
360  */
361 INT16 WINAPI IntersectVisRect16( HDC16 hdc16, INT16 left, INT16 top, INT16 right, INT16 bottom )
362 {
363     HRGN tempRgn;
364     INT16 ret;
365     POINT pt[2];
366     HDC hdc = HDC_32( hdc16 );
367     DC * dc = DC_GetDCUpdate( hdc );
368     if (!dc) return ERROR;
369
370     pt[0].x = left;
371     pt[0].y = top;
372     pt[1].x = right;
373     pt[1].y = bottom;
374
375     LPtoDP( hdc, pt, 2 );
376
377     TRACE("%p %d,%d - %d,%d\n", hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
378
379
380     if (!(tempRgn = CreateRectRgn( pt[0].x, pt[0].y, pt[1].x, pt[1].y ))) ret = ERROR;
381     else
382     {
383         ret = CombineRgn( dc->hVisRgn, dc->hVisRgn, tempRgn, RGN_AND );
384         DeleteObject( tempRgn );
385     }
386     if (ret != ERROR) CLIPPING_UpdateGCRegion( dc );
387     GDI_ReleaseObj( hdc );
388     return ret;
389 }
390
391
392 /***********************************************************************
393  *           PtVisible    (GDI32.@)
394  */
395 BOOL WINAPI PtVisible( HDC hdc, INT x, INT y )
396 {
397     POINT pt;
398     BOOL ret;
399     HRGN clip;
400     DC *dc = DC_GetDCUpdate( hdc );
401
402     TRACE("%p %d,%d\n", hdc, x, y );
403     if (!dc) return FALSE;
404
405     pt.x = x;
406     pt.y = y;
407     LPtoDP( hdc, &pt, 1 );
408     ret = PtInRegion( dc->hVisRgn, pt.x, pt.y );
409     if (ret && (clip = get_clip_region(dc))) ret = PtInRegion( clip, pt.x, pt.y );
410     GDI_ReleaseObj( hdc );
411     return ret;
412 }
413
414
415 /***********************************************************************
416  *           RectVisible    (GDI32.@)
417  */
418 BOOL WINAPI RectVisible( HDC hdc, const RECT* rect )
419 {
420     RECT tmpRect;
421     BOOL ret;
422     HRGN clip;
423     DC *dc = DC_GetDCUpdate( hdc );
424     if (!dc) return FALSE;
425     TRACE("%p %d,%dx%d,%d\n", hdc, rect->left, rect->top, rect->right, rect->bottom );
426
427     tmpRect = *rect;
428     LPtoDP( hdc, (POINT *)&tmpRect, 2 );
429
430     if ((clip = get_clip_region(dc)))
431     {
432         HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
433         CombineRgn( hrgn, dc->hVisRgn, clip, RGN_AND );
434         ret = RectInRegion( hrgn, &tmpRect );
435         DeleteObject( hrgn );
436     }
437     else ret = RectInRegion( dc->hVisRgn, &tmpRect );
438     GDI_ReleaseObj( hdc );
439     return ret;
440 }
441
442
443 /***********************************************************************
444  *           GetClipBox    (GDI32.@)
445  */
446 INT WINAPI GetClipBox( HDC hdc, LPRECT rect )
447 {
448     INT ret;
449     HRGN clip;
450     DC *dc = DC_GetDCUpdate( hdc );
451     if (!dc) return ERROR;
452     if ((clip = get_clip_region(dc)))
453     {
454         HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
455         CombineRgn( hrgn, dc->hVisRgn, clip, RGN_AND );
456         ret = GetRgnBox( hrgn, rect );
457         DeleteObject( hrgn );
458     }
459     else ret = GetRgnBox( dc->hVisRgn, rect );
460     DPtoLP( hdc, (LPPOINT)rect, 2 );
461     GDI_ReleaseObj( hdc );
462     return ret;
463 }
464
465
466 /***********************************************************************
467  *           GetClipRgn  (GDI32.@)
468  */
469 INT WINAPI GetClipRgn( HDC hdc, HRGN hRgn )
470 {
471     INT ret = -1;
472     DC * dc;
473     if (hRgn && (dc = DC_GetDCPtr( hdc )))
474     {
475       if( dc->hClipRgn )
476       {
477           if( CombineRgn(hRgn, dc->hClipRgn, 0, RGN_COPY) != ERROR ) ret = 1;
478       }
479       else ret = 0;
480       GDI_ReleaseObj( hdc );
481     }
482     return ret;
483 }
484
485
486 /***********************************************************************
487  *           GetMetaRgn    (GDI32.@)
488  */
489 INT WINAPI GetMetaRgn( HDC hdc, HRGN hRgn )
490 {
491     INT ret = 0;
492     DC * dc = DC_GetDCPtr( hdc );
493
494     if (dc)
495     {
496         if (dc->hMetaRgn && CombineRgn( hRgn, dc->hMetaRgn, 0, RGN_COPY ) != ERROR)
497             ret = 1;
498         GDI_ReleaseObj( hdc );
499     }
500     return ret;
501 }
502
503
504 /***********************************************************************
505  *           SaveVisRgn   (GDI.129)
506  */
507 HRGN16 WINAPI SaveVisRgn16( HDC16 hdc16 )
508 {
509     struct saved_visrgn *saved;
510     HDC hdc = HDC_32( hdc16 );
511     DC *dc = DC_GetDCUpdate( hdc );
512
513     if (!dc) return 0;
514     TRACE("%p\n", hdc );
515
516     if (!(saved = HeapAlloc( GetProcessHeap(), 0, sizeof(*saved) ))) goto error;
517     if (!(saved->hrgn = CreateRectRgn( 0, 0, 0, 0 ))) goto error;
518     CombineRgn( saved->hrgn, dc->hVisRgn, 0, RGN_COPY );
519     saved->next = dc->saved_visrgn;
520     dc->saved_visrgn = saved;
521     GDI_ReleaseObj( hdc );
522     return HRGN_16(saved->hrgn);
523
524 error:
525     GDI_ReleaseObj( hdc );
526     HeapFree( GetProcessHeap(), 0, saved );
527     return 0;
528 }
529
530
531 /***********************************************************************
532  *           RestoreVisRgn   (GDI.130)
533  */
534 INT16 WINAPI RestoreVisRgn16( HDC16 hdc16 )
535 {
536     struct saved_visrgn *saved;
537     HDC hdc = HDC_32( hdc16 );
538     DC *dc = DC_GetDCPtr( hdc );
539     INT16 ret = ERROR;
540
541     if (!dc) return ERROR;
542
543     TRACE("%p\n", hdc );
544
545     if (!(saved = dc->saved_visrgn)) goto done;
546
547     ret = CombineRgn( dc->hVisRgn, saved->hrgn, 0, RGN_COPY );
548     dc->saved_visrgn = saved->next;
549     DeleteObject( saved->hrgn );
550     HeapFree( GetProcessHeap(), 0, saved );
551     dc->flags &= ~DC_DIRTY;
552     CLIPPING_UpdateGCRegion( dc );
553  done:
554     GDI_ReleaseObj( hdc );
555     return ret;
556 }
557
558
559 /***********************************************************************
560  * GetRandomRgn [GDI32.@]
561  *
562  * NOTES
563  *     This function is documented in MSDN online for the case of
564  *     iCode == SYSRGN (4).
565  *
566  *     For iCode == 1 it should return the clip region
567  *                  2 "    "       "   the meta region
568  *                  3 "    "       "   the intersection of the clip with
569  *                                     the meta region (== 'Rao' region).
570  *
571  *     See http://www.codeproject.com/gdi/cliprgnguide.asp
572  */
573 INT WINAPI GetRandomRgn(HDC hDC, HRGN hRgn, INT iCode)
574 {
575     HRGN rgn;
576     DC *dc = DC_GetDCPtr( hDC );
577
578     if (!dc) return -1;
579
580     switch (iCode)
581     {
582     case 1:
583         rgn = dc->hClipRgn;
584         break;
585     case 2:
586         rgn = dc->hMetaRgn;
587         break;
588     case 3:
589         rgn = dc->hMetaClipRgn;
590         if(!rgn) rgn = dc->hClipRgn;
591         if(!rgn) rgn = dc->hMetaRgn;
592         break;
593     case SYSRGN: /* == 4 */
594         rgn = dc->hVisRgn;
595         break;
596     default:
597         WARN("Unknown code %d\n", iCode);
598         GDI_ReleaseObj( hDC );
599         return -1;
600     }
601     if (rgn) CombineRgn( hRgn, rgn, 0, RGN_COPY );
602     GDI_ReleaseObj( hDC );
603
604     /* On Windows NT/2000, the SYSRGN returned is in screen coordinates */
605     if (iCode == SYSRGN && !(GetVersion() & 0x80000000))
606     {
607         POINT org;
608         GetDCOrgEx( hDC, &org );
609         OffsetRgn( hRgn, org.x, org.y );
610     }
611     return (rgn != 0);
612 }
613
614
615 /***********************************************************************
616  *           SetMetaRgn    (GDI32.@)
617  */
618 INT WINAPI SetMetaRgn( HDC hdc )
619 {
620     INT ret;
621     RECT dummy;
622     DC *dc = DC_GetDCPtr( hdc );
623
624     if (!dc) return ERROR;
625
626     if (dc->hMetaClipRgn)
627     {
628         /* the intersection becomes the new meta region */
629         DeleteObject( dc->hMetaRgn );
630         DeleteObject( dc->hClipRgn );
631         dc->hMetaRgn = dc->hMetaClipRgn;
632         dc->hClipRgn = 0;
633         dc->hMetaClipRgn = 0;
634     }
635     else if (dc->hClipRgn)
636     {
637         dc->hMetaRgn = dc->hClipRgn;
638         dc->hClipRgn = 0;
639     }
640     /* else nothing to do */
641
642     /* Note: no need to call CLIPPING_UpdateGCRegion, the overall clip region hasn't changed */
643
644     ret = GetRgnBox( dc->hMetaRgn, &dummy );
645     GDI_ReleaseObj( hdc );
646     return ret;
647 }