gdi32: Maintain a region inside gdi32 to keep track of the total visible region.
[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 /***********************************************************************
33  *           get_clip_rect
34  *
35  * Compute a clip rectangle from its logical coordinates.
36  */
37 static inline RECT get_clip_rect( DC * dc, int left, int top, int right, int bottom )
38 {
39     RECT rect;
40
41     rect.left   = left;
42     rect.top    = top;
43     rect.right  = right;
44     rect.bottom = bottom;
45     LPtoDP( dc->hSelf, (POINT *)&rect, 2 );
46     if (dc->layout & LAYOUT_RTL)
47     {
48         int tmp = rect.left;
49         rect.left = rect.right + 1;
50         rect.right = tmp + 1;
51     }
52     return rect;
53 }
54
55 /***********************************************************************
56  *           clip_visrect
57  *
58  * Clip a rectangle to the DC visible rect.
59  */
60 BOOL clip_visrect( DC *dc, RECT *dst, const RECT *src )
61 {
62     RECT clip;
63
64     if (!GetRgnBox( get_dc_region(dc), &clip ))
65     {
66         *dst = *src;
67         return !is_rect_empty( dst );
68     }
69     return intersect_rect( dst, src, &clip );
70 }
71
72 /***********************************************************************
73  *           CLIPPING_UpdateGCRegion
74  *
75  * Update the GC clip region when the ClipRgn or VisRgn have changed.
76  */
77 void CLIPPING_UpdateGCRegion( DC * dc )
78 {
79     HRGN clip_rgn;
80     PHYSDEV physdev = GET_DC_PHYSDEV( dc, pSetDeviceClipping );
81
82     /* update the intersection of meta and clip regions */
83     if (dc->hMetaRgn && dc->hClipRgn)
84     {
85         if (!dc->hMetaClipRgn) dc->hMetaClipRgn = CreateRectRgn( 0, 0, 0, 0 );
86         CombineRgn( dc->hMetaClipRgn, dc->hClipRgn, dc->hMetaRgn, RGN_AND );
87     }
88     else  /* only one is set, no need for an intersection */
89     {
90         if (dc->hMetaClipRgn) DeleteObject( dc->hMetaClipRgn );
91         dc->hMetaClipRgn = 0;
92     }
93     clip_rgn = get_clip_region( dc );
94     if (clip_rgn || dc->hVisRgn)
95     {
96         if (!dc->region) dc->region = CreateRectRgn( 0, 0, 0, 0 );
97         CombineRgn( dc->region, dc->hVisRgn, clip_rgn, clip_rgn ? RGN_AND : RGN_COPY );
98     }
99     else
100     {
101         if (dc->region) DeleteObject( dc->region );
102         dc->region = 0;
103     }
104     physdev->funcs->pSetDeviceClipping( physdev, dc->hVisRgn, clip_rgn );
105 }
106
107 /***********************************************************************
108  *           create_default_clip_region
109  *
110  * Create a default clipping region when none already exists.
111  */
112 static inline void create_default_clip_region( DC * dc )
113 {
114     UINT width, height;
115
116     if (dc->header.type == OBJ_MEMDC)
117     {
118         BITMAP bitmap;
119
120         GetObjectW( dc->hBitmap, sizeof(bitmap), &bitmap );
121         width = bitmap.bmWidth;
122         height = bitmap.bmHeight;
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     CLIPPING_UpdateGCRegion( 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) CLIPPING_UpdateGCRegion( 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) CLIPPING_UpdateGCRegion( 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         CLIPPING_UpdateGCRegion( 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     DeleteObject( dc->hVisRgn );
276     dc->dirty = 0;
277     dc->vis_rect = *vis_rect;
278     dc->hVisRgn = hrgn;
279     DC_UpdateXforms( dc );
280     CLIPPING_UpdateGCRegion( 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     BOOL ret;
356     DC *dc = get_dc_ptr( hdc );
357
358     TRACE("%p %d,%d\n", hdc, x, y );
359     if (!dc) return FALSE;
360
361     pt.x = x;
362     pt.y = y;
363     LPtoDP( hdc, &pt, 1 );
364     update_dc( dc );
365     ret = PtInRegion( get_dc_region(dc), pt.x, pt.y );
366     release_dc_ptr( dc );
367     return ret;
368 }
369
370
371 /***********************************************************************
372  *           RectVisible    (GDI32.@)
373  */
374 BOOL WINAPI RectVisible( HDC hdc, const RECT* rect )
375 {
376     RECT tmpRect;
377     BOOL ret;
378     DC *dc = get_dc_ptr( hdc );
379     if (!dc) return FALSE;
380     TRACE("%p %s\n", hdc, wine_dbgstr_rect( rect ));
381
382     tmpRect = *rect;
383     LPtoDP( hdc, (POINT *)&tmpRect, 2 );
384
385     update_dc( dc );
386     ret = RectInRegion( get_dc_region(dc), &tmpRect );
387     release_dc_ptr( dc );
388     return ret;
389 }
390
391
392 /***********************************************************************
393  *           GetClipBox    (GDI32.@)
394  */
395 INT WINAPI GetClipBox( HDC hdc, LPRECT rect )
396 {
397     INT ret;
398     DC *dc = get_dc_ptr( hdc );
399     if (!dc) return ERROR;
400
401     update_dc( dc );
402     ret = GetRgnBox( get_dc_region(dc), rect );
403     if (dc->layout & LAYOUT_RTL)
404     {
405         int tmp = rect->left;
406         rect->left = rect->right - 1;
407         rect->right = tmp - 1;
408     }
409     DPtoLP( hdc, (LPPOINT)rect, 2 );
410     release_dc_ptr( dc );
411     TRACE("%p => %d %s\n", hdc, ret, wine_dbgstr_rect( rect ));
412     return ret;
413 }
414
415
416 /***********************************************************************
417  *           GetClipRgn  (GDI32.@)
418  */
419 INT WINAPI GetClipRgn( HDC hdc, HRGN hRgn )
420 {
421     INT ret = -1;
422     DC * dc;
423     if ((dc = get_dc_ptr( hdc )))
424     {
425       if( dc->hClipRgn )
426       {
427           if( CombineRgn(hRgn, dc->hClipRgn, 0, RGN_COPY) != ERROR )
428           {
429               ret = 1;
430               if (dc->layout & LAYOUT_RTL)
431                   mirror_region( hRgn, hRgn, dc->vis_rect.right - dc->vis_rect.left );
432           }
433       }
434       else ret = 0;
435       release_dc_ptr( dc );
436     }
437     return ret;
438 }
439
440
441 /***********************************************************************
442  *           GetMetaRgn    (GDI32.@)
443  */
444 INT WINAPI GetMetaRgn( HDC hdc, HRGN hRgn )
445 {
446     INT ret = 0;
447     DC * dc = get_dc_ptr( hdc );
448
449     if (dc)
450     {
451         if (dc->hMetaRgn && CombineRgn( hRgn, dc->hMetaRgn, 0, RGN_COPY ) != ERROR)
452         {
453             ret = 1;
454             if (dc->layout & LAYOUT_RTL)
455                 mirror_region( hRgn, hRgn, dc->vis_rect.right - dc->vis_rect.left );
456         }
457         release_dc_ptr( dc );
458     }
459     return ret;
460 }
461
462
463 /***********************************************************************
464  * GetRandomRgn [GDI32.@]
465  *
466  * NOTES
467  *     This function is documented in MSDN online for the case of
468  *     iCode == SYSRGN (4).
469  *
470  *     For iCode == 1 it should return the clip region
471  *                  2 "    "       "   the meta region
472  *                  3 "    "       "   the intersection of the clip with
473  *                                     the meta region (== 'Rao' region).
474  *
475  *     See http://www.codeproject.com/gdi/cliprgnguide.asp
476  */
477 INT WINAPI GetRandomRgn(HDC hDC, HRGN hRgn, INT iCode)
478 {
479     HRGN rgn;
480     DC *dc = get_dc_ptr( hDC );
481
482     if (!dc) return -1;
483
484     switch (iCode)
485     {
486     case 1:
487         rgn = dc->hClipRgn;
488         break;
489     case 2:
490         rgn = dc->hMetaRgn;
491         break;
492     case 3:
493         rgn = dc->hMetaClipRgn;
494         if(!rgn) rgn = dc->hClipRgn;
495         if(!rgn) rgn = dc->hMetaRgn;
496         break;
497     case SYSRGN: /* == 4 */
498         update_dc( dc );
499         rgn = dc->hVisRgn;
500         break;
501     default:
502         WARN("Unknown code %d\n", iCode);
503         release_dc_ptr( dc );
504         return -1;
505     }
506     if (rgn) CombineRgn( hRgn, rgn, 0, RGN_COPY );
507     release_dc_ptr( dc );
508
509     /* On Windows NT/2000, the SYSRGN returned is in screen coordinates */
510     if (iCode == SYSRGN && !(GetVersion() & 0x80000000))
511         OffsetRgn( hRgn, dc->vis_rect.left, dc->vis_rect.top );
512
513     return (rgn != 0);
514 }
515
516
517 /***********************************************************************
518  *           SetMetaRgn    (GDI32.@)
519  */
520 INT WINAPI SetMetaRgn( HDC hdc )
521 {
522     INT ret;
523     RECT dummy;
524     DC *dc = get_dc_ptr( hdc );
525
526     if (!dc) return ERROR;
527
528     if (dc->hMetaClipRgn)
529     {
530         /* the intersection becomes the new meta region */
531         DeleteObject( dc->hMetaRgn );
532         DeleteObject( dc->hClipRgn );
533         dc->hMetaRgn = dc->hMetaClipRgn;
534         dc->hClipRgn = 0;
535         dc->hMetaClipRgn = 0;
536     }
537     else if (dc->hClipRgn)
538     {
539         dc->hMetaRgn = dc->hClipRgn;
540         dc->hClipRgn = 0;
541     }
542     /* else nothing to do */
543
544     /* Note: no need to call CLIPPING_UpdateGCRegion, the overall clip region hasn't changed */
545
546     ret = GetRgnBox( dc->hMetaRgn, &dummy );
547     release_dc_ptr( dc );
548     return ret;
549 }