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