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