ddraw: Don't leak the window DC.
[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 "gdi_private.h"
27 #include "wine/debug.h"
28
29 WINE_DEFAULT_DEBUG_CHANNEL(clipping);
30
31
32 /* return the DC visible rectangle if not empty */
33 static inline BOOL get_dc_visrect( DC *dc, RECT *rect )
34 {
35     rect->left = 0;
36     rect->top = 0;
37     rect->right = dc->vis_rect.right - dc->vis_rect.left;
38     rect->bottom = dc->vis_rect.bottom - dc->vis_rect.top;
39     return !is_rect_empty( rect );
40 }
41
42 /***********************************************************************
43  *           get_clip_rect
44  *
45  * Compute a clip rectangle from its logical coordinates.
46  */
47 static inline RECT get_clip_rect( DC * dc, int left, int top, int right, int bottom )
48 {
49     RECT rect;
50
51     rect.left   = left;
52     rect.top    = top;
53     rect.right  = right;
54     rect.bottom = bottom;
55     LPtoDP( dc->hSelf, (POINT *)&rect, 2 );
56     if (dc->layout & LAYOUT_RTL)
57     {
58         int tmp = rect.left;
59         rect.left = rect.right + 1;
60         rect.right = tmp + 1;
61     }
62     return rect;
63 }
64
65 /***********************************************************************
66  *           clip_visrect
67  *
68  * Clip a rectangle to the DC visible rect.
69  */
70 BOOL clip_visrect( DC *dc, RECT *dst, const RECT *src )
71 {
72     RECT clip;
73
74     if (get_dc_visrect( dc, &clip )) intersect_rect( dst, src, &clip );
75     else *dst = *src;
76
77     if (GetRgnBox( get_dc_region(dc), &clip )) intersect_rect( dst, dst, &clip );
78     return !is_rect_empty( dst );
79 }
80
81 /***********************************************************************
82  *           update_dc_clipping
83  *
84  * Update the DC and device clip regions when the ClipRgn or VisRgn have changed.
85  */
86 void update_dc_clipping( DC * dc )
87 {
88     PHYSDEV physdev = GET_DC_PHYSDEV( dc, pSetDeviceClipping );
89     HRGN regions[3];
90     int count = 0;
91
92     if (dc->hVisRgn)  regions[count++] = dc->hVisRgn;
93     if (dc->hClipRgn) regions[count++] = dc->hClipRgn;
94     if (dc->hMetaRgn) regions[count++] = dc->hMetaRgn;
95
96     if (count > 1)
97     {
98         if (!dc->region) dc->region = CreateRectRgn( 0, 0, 0, 0 );
99         CombineRgn( dc->region, regions[0], regions[1], RGN_AND );
100         if (count > 2) CombineRgn( dc->region, dc->region, regions[2], RGN_AND );
101     }
102     else  /* only one region, we don't need the total region */
103     {
104         if (dc->region) DeleteObject( dc->region );
105         dc->region = 0;
106     }
107     physdev->funcs->pSetDeviceClipping( physdev, get_dc_region( dc ));
108 }
109
110 /***********************************************************************
111  *           create_default_clip_region
112  *
113  * Create a default clipping region when none already exists.
114  */
115 static inline void create_default_clip_region( DC * dc )
116 {
117     UINT width, height;
118
119     if (dc->header.type == OBJ_MEMDC)
120     {
121         width = dc->vis_rect.right - dc->vis_rect.left;
122         height = dc->vis_rect.bottom - dc->vis_rect.top;
123     }
124     else
125     {
126         width = GetDeviceCaps( dc->hSelf, DESKTOPHORZRES );
127         height = GetDeviceCaps( dc->hSelf, DESKTOPVERTRES );
128     }
129     dc->hClipRgn = CreateRectRgn( 0, 0, width, height );
130 }
131
132
133 /***********************************************************************
134  *           null driver fallback implementations
135  */
136
137 INT nulldrv_ExtSelectClipRgn( PHYSDEV dev, HRGN rgn, INT mode )
138 {
139     DC *dc = get_nulldrv_dc( dev );
140     INT ret;
141
142     if (!rgn)
143     {
144         if (mode != RGN_COPY)
145         {
146             FIXME("Unimplemented: hrgn NULL in mode: %d\n", mode);
147             return ERROR;
148         }
149         if (dc->hClipRgn) DeleteObject( dc->hClipRgn );
150         dc->hClipRgn = 0;
151         ret = SIMPLEREGION;
152     }
153     else
154     {
155         HRGN mirrored = 0;
156
157         if (dc->layout & LAYOUT_RTL)
158         {
159             if (!(mirrored = CreateRectRgn( 0, 0, 0, 0 ))) return ERROR;
160             mirror_region( mirrored, rgn, dc->vis_rect.right - dc->vis_rect.left );
161             rgn = mirrored;
162         }
163
164         if (!dc->hClipRgn)
165             create_default_clip_region( dc );
166
167         if (mode == RGN_COPY)
168             ret = CombineRgn( dc->hClipRgn, rgn, 0, mode );
169         else
170             ret = CombineRgn( dc->hClipRgn, dc->hClipRgn, rgn, mode);
171
172         if (mirrored) DeleteObject( mirrored );
173     }
174     update_dc_clipping( dc );
175     return ret;
176 }
177
178 INT nulldrv_ExcludeClipRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
179 {
180     DC *dc = get_nulldrv_dc( dev );
181     RECT rect = get_clip_rect( dc, left, top, right, bottom );
182     INT ret;
183     HRGN rgn;
184
185     if (!(rgn = CreateRectRgnIndirect( &rect ))) return ERROR;
186     if (!dc->hClipRgn) create_default_clip_region( dc );
187     ret = CombineRgn( dc->hClipRgn, dc->hClipRgn, rgn, RGN_DIFF );
188     DeleteObject( rgn );
189     if (ret != ERROR) update_dc_clipping( dc );
190     return ret;
191 }
192
193 INT nulldrv_IntersectClipRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
194 {
195     DC *dc = get_nulldrv_dc( dev );
196     RECT rect = get_clip_rect( dc, left, top, right, bottom );
197     INT ret;
198     HRGN rgn;
199
200     if (!dc->hClipRgn)
201     {
202         dc->hClipRgn = CreateRectRgnIndirect( &rect );
203         ret = SIMPLEREGION;
204     }
205     else
206     {
207         if (!(rgn = CreateRectRgnIndirect( &rect ))) return ERROR;
208         ret = CombineRgn( dc->hClipRgn, dc->hClipRgn, rgn, RGN_AND );
209         DeleteObject( rgn );
210     }
211     if (ret != ERROR) update_dc_clipping( dc );
212     return ret;
213 }
214
215 INT nulldrv_OffsetClipRgn( PHYSDEV dev, INT x, INT y )
216 {
217     DC *dc = get_nulldrv_dc( dev );
218     INT ret = NULLREGION;
219
220     if (dc->hClipRgn)
221     {
222         x = MulDiv( x, dc->vportExtX, dc->wndExtX );
223         y = MulDiv( y, dc->vportExtY, dc->wndExtY );
224         if (dc->layout & LAYOUT_RTL) x = -x;
225         ret = OffsetRgn( dc->hClipRgn, x, y );
226         update_dc_clipping( dc );
227     }
228     return ret;
229 }
230
231
232 /***********************************************************************
233  *           SelectClipRgn    (GDI32.@)
234  */
235 INT WINAPI SelectClipRgn( HDC hdc, HRGN hrgn )
236 {
237     return ExtSelectClipRgn( hdc, hrgn, RGN_COPY );
238 }
239
240
241 /******************************************************************************
242  *              ExtSelectClipRgn        [GDI32.@]
243  */
244 INT WINAPI ExtSelectClipRgn( HDC hdc, HRGN hrgn, INT fnMode )
245 {
246     INT retval = ERROR;
247     DC * dc = get_dc_ptr( hdc );
248
249     TRACE("%p %p %d\n", hdc, hrgn, fnMode );
250
251     if (dc)
252     {
253         PHYSDEV physdev = GET_DC_PHYSDEV( dc, pExtSelectClipRgn );
254         update_dc( dc );
255         retval = physdev->funcs->pExtSelectClipRgn( physdev, hrgn, fnMode );
256         release_dc_ptr( dc );
257     }
258     return retval;
259 }
260
261 /***********************************************************************
262  *           __wine_set_visible_region   (GDI32.@)
263  */
264 void CDECL __wine_set_visible_region( HDC hdc, HRGN hrgn, const RECT *vis_rect )
265 {
266     DC * dc;
267
268     if (!(dc = get_dc_ptr( hdc ))) return;
269
270     TRACE( "%p %p %s\n", hdc, hrgn, wine_dbgstr_rect(vis_rect) );
271
272     /* map region to DC coordinates */
273     OffsetRgn( hrgn, -vis_rect->left, -vis_rect->top );
274
275     if (dc->hVisRgn) DeleteObject( dc->hVisRgn );
276     dc->dirty = 0;
277     dc->vis_rect = *vis_rect;
278     dc->hVisRgn = hrgn;
279     DC_UpdateXforms( dc );
280     update_dc_clipping( dc );
281     release_dc_ptr( dc );
282 }
283
284
285 /***********************************************************************
286  *           OffsetClipRgn    (GDI32.@)
287  */
288 INT WINAPI OffsetClipRgn( HDC hdc, INT x, INT y )
289 {
290     INT ret = ERROR;
291     DC *dc = get_dc_ptr( hdc );
292
293     TRACE("%p %d,%d\n", hdc, x, y );
294
295     if (dc)
296     {
297         PHYSDEV physdev = GET_DC_PHYSDEV( dc, pOffsetClipRgn );
298         update_dc( dc );
299         ret = physdev->funcs->pOffsetClipRgn( physdev, x, y );
300         release_dc_ptr( dc );
301     }
302     return ret;
303 }
304
305
306 /***********************************************************************
307  *           ExcludeClipRect    (GDI32.@)
308  */
309 INT WINAPI ExcludeClipRect( HDC hdc, INT left, INT top,
310                                 INT right, INT bottom )
311 {
312     INT ret = ERROR;
313     DC *dc = get_dc_ptr( hdc );
314
315     TRACE("%p %d,%d-%d,%d\n", hdc, left, top, right, bottom );
316
317     if (dc)
318     {
319         PHYSDEV physdev = GET_DC_PHYSDEV( dc, pExcludeClipRect );
320         update_dc( dc );
321         ret = physdev->funcs->pExcludeClipRect( physdev, left, top, right, bottom );
322         release_dc_ptr( dc );
323     }
324     return ret;
325 }
326
327
328 /***********************************************************************
329  *           IntersectClipRect    (GDI32.@)
330  */
331 INT WINAPI IntersectClipRect( HDC hdc, INT left, INT top, INT right, INT bottom )
332 {
333     INT ret = ERROR;
334     DC *dc = get_dc_ptr( hdc );
335
336     TRACE("%p %d,%d - %d,%d\n", hdc, left, top, right, bottom );
337
338     if (dc)
339     {
340         PHYSDEV physdev = GET_DC_PHYSDEV( dc, pIntersectClipRect );
341         update_dc( dc );
342         ret = physdev->funcs->pIntersectClipRect( physdev, left, top, right, bottom );
343         release_dc_ptr( dc );
344     }
345     return ret;
346 }
347
348
349 /***********************************************************************
350  *           PtVisible    (GDI32.@)
351  */
352 BOOL WINAPI PtVisible( HDC hdc, INT x, INT y )
353 {
354     POINT pt;
355     RECT visrect;
356     BOOL ret;
357     DC *dc = get_dc_ptr( hdc );
358
359     TRACE("%p %d,%d\n", hdc, x, y );
360     if (!dc) return FALSE;
361
362     pt.x = x;
363     pt.y = y;
364     LPtoDP( hdc, &pt, 1 );
365     update_dc( dc );
366     ret = (get_dc_visrect( dc, &visrect ) &&
367            pt.x >= visrect.left && pt.x < visrect.right &&
368            pt.y >= visrect.top && pt.y < visrect.bottom);
369     if (ret && get_dc_region( dc )) ret = PtInRegion( get_dc_region( dc ), pt.x, pt.y );
370     release_dc_ptr( dc );
371     return ret;
372 }
373
374
375 /***********************************************************************
376  *           RectVisible    (GDI32.@)
377  */
378 BOOL WINAPI RectVisible( HDC hdc, const RECT* rect )
379 {
380     RECT tmpRect, visrect;
381     BOOL ret;
382     DC *dc = get_dc_ptr( hdc );
383     if (!dc) return FALSE;
384     TRACE("%p %s\n", hdc, wine_dbgstr_rect( rect ));
385
386     tmpRect = *rect;
387     LPtoDP( hdc, (POINT *)&tmpRect, 2 );
388
389     update_dc( dc );
390     ret = (get_dc_visrect( dc, &visrect ) && intersect_rect( &visrect, &visrect, &tmpRect ));
391     if (ret && get_dc_region( dc )) ret = RectInRegion( get_dc_region( dc ), &tmpRect );
392     release_dc_ptr( dc );
393     return ret;
394 }
395
396
397 /***********************************************************************
398  *           GetClipBox    (GDI32.@)
399  */
400 INT WINAPI GetClipBox( HDC hdc, LPRECT rect )
401 {
402     RECT visrect;
403     INT ret;
404     DC *dc = get_dc_ptr( hdc );
405     if (!dc) return ERROR;
406
407     update_dc( dc );
408     if (get_dc_region( dc ))
409     {
410         ret = GetRgnBox( get_dc_region( dc ), rect );
411         if (get_dc_visrect( dc, &visrect ) && !intersect_rect( rect, rect, &visrect ))
412             ret = NULLREGION;
413     }
414     else ret = get_dc_visrect( dc, rect ) ? SIMPLEREGION : NULLREGION;
415
416     if (dc->layout & LAYOUT_RTL)
417     {
418         int tmp = rect->left;
419         rect->left = rect->right - 1;
420         rect->right = tmp - 1;
421     }
422     DPtoLP( hdc, (LPPOINT)rect, 2 );
423     release_dc_ptr( dc );
424     TRACE("%p => %d %s\n", hdc, ret, wine_dbgstr_rect( rect ));
425     return ret;
426 }
427
428
429 /***********************************************************************
430  *           GetClipRgn  (GDI32.@)
431  */
432 INT WINAPI GetClipRgn( HDC hdc, HRGN hRgn )
433 {
434     INT ret = -1;
435     DC * dc;
436     if ((dc = get_dc_ptr( hdc )))
437     {
438       if( dc->hClipRgn )
439       {
440           if( CombineRgn(hRgn, dc->hClipRgn, 0, RGN_COPY) != ERROR )
441           {
442               ret = 1;
443               if (dc->layout & LAYOUT_RTL)
444                   mirror_region( hRgn, hRgn, dc->vis_rect.right - dc->vis_rect.left );
445           }
446       }
447       else ret = 0;
448       release_dc_ptr( dc );
449     }
450     return ret;
451 }
452
453
454 /***********************************************************************
455  *           GetMetaRgn    (GDI32.@)
456  */
457 INT WINAPI GetMetaRgn( HDC hdc, HRGN hRgn )
458 {
459     INT ret = 0;
460     DC * dc = get_dc_ptr( hdc );
461
462     if (dc)
463     {
464         if (dc->hMetaRgn && CombineRgn( hRgn, dc->hMetaRgn, 0, RGN_COPY ) != ERROR)
465         {
466             ret = 1;
467             if (dc->layout & LAYOUT_RTL)
468                 mirror_region( hRgn, hRgn, dc->vis_rect.right - dc->vis_rect.left );
469         }
470         release_dc_ptr( dc );
471     }
472     return ret;
473 }
474
475
476 /***********************************************************************
477  * GetRandomRgn [GDI32.@]
478  *
479  * NOTES
480  *     This function is documented in MSDN online for the case of
481  *     iCode == SYSRGN (4).
482  *
483  *     For iCode == 1 it should return the clip region
484  *                  2 "    "       "   the meta region
485  *                  3 "    "       "   the intersection of the clip with
486  *                                     the meta region (== 'Rao' region).
487  *
488  *     See http://www.codeproject.com/gdi/cliprgnguide.asp
489  */
490 INT WINAPI GetRandomRgn(HDC hDC, HRGN hRgn, INT iCode)
491 {
492     INT ret = 1;
493     RECT visrect;
494     DC *dc = get_dc_ptr( hDC );
495
496     if (!dc) return -1;
497
498     switch (iCode)
499     {
500     case 1:
501         if (dc->hClipRgn) CombineRgn( hRgn, dc->hClipRgn, 0, RGN_COPY );
502         else ret = 0;
503         break;
504     case 2:
505         if (dc->hMetaRgn) CombineRgn( hRgn, dc->hMetaRgn, 0, RGN_COPY );
506         else ret = 0;
507         break;
508     case 3:
509         if (dc->hClipRgn && dc->hMetaRgn) CombineRgn( hRgn, dc->hClipRgn, dc->hMetaRgn, RGN_AND );
510         else if (dc->hClipRgn) CombineRgn( hRgn, dc->hClipRgn, 0, RGN_COPY );
511         else if (dc->hMetaRgn) CombineRgn( hRgn, dc->hMetaRgn, 0, RGN_COPY );
512         else ret = 0;
513         break;
514     case SYSRGN: /* == 4 */
515         update_dc( dc );
516         if (dc->hVisRgn)
517             CombineRgn( hRgn, dc->hVisRgn, 0, RGN_COPY );
518         else if (get_dc_visrect( dc, &visrect ))
519             SetRectRgn( hRgn, visrect.left, visrect.top, visrect.right, visrect.bottom );
520         else
521             ret = 0;
522         /* On Windows NT/2000, the SYSRGN returned is in screen coordinates */
523         if (ret && !(GetVersion() & 0x80000000)) OffsetRgn( hRgn, dc->vis_rect.left, dc->vis_rect.top );
524         break;
525     default:
526         WARN("Unknown code %d\n", iCode);
527         ret = -1;
528         break;
529     }
530     release_dc_ptr( dc );
531     return ret;
532 }
533
534
535 /***********************************************************************
536  *           SetMetaRgn    (GDI32.@)
537  */
538 INT WINAPI SetMetaRgn( HDC hdc )
539 {
540     INT ret;
541     RECT dummy;
542     DC *dc = get_dc_ptr( hdc );
543
544     if (!dc) return ERROR;
545
546     if (dc->hClipRgn)
547     {
548         if (dc->hMetaRgn)
549         {
550             /* the intersection becomes the new meta region */
551             CombineRgn( dc->hMetaRgn, dc->hMetaRgn, dc->hClipRgn, RGN_AND );
552             DeleteObject( dc->hClipRgn );
553             dc->hClipRgn = 0;
554         }
555         else
556         {
557             dc->hMetaRgn = dc->hClipRgn;
558             dc->hClipRgn = 0;
559         }
560     }
561     /* else nothing to do */
562
563     /* Note: no need to call update_dc_clipping, the overall clip region hasn't changed */
564
565     ret = GetRgnBox( dc->hMetaRgn, &dummy );
566     release_dc_ptr( dc );
567     return ret;
568 }