msxml3: Avoid BAD_CAST in schemasInit.
[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 ret = get_dc_visrect( dc, rect ) ? SIMPLEREGION : NULLREGION;
413
414     if (dc->layout & LAYOUT_RTL)
415     {
416         int tmp = rect->left;
417         rect->left = rect->right - 1;
418         rect->right = tmp - 1;
419     }
420     DPtoLP( hdc, (LPPOINT)rect, 2 );
421     release_dc_ptr( dc );
422     TRACE("%p => %d %s\n", hdc, ret, wine_dbgstr_rect( rect ));
423     return ret;
424 }
425
426
427 /***********************************************************************
428  *           GetClipRgn  (GDI32.@)
429  */
430 INT WINAPI GetClipRgn( HDC hdc, HRGN hRgn )
431 {
432     INT ret = -1;
433     DC * dc;
434     if ((dc = get_dc_ptr( hdc )))
435     {
436       if( dc->hClipRgn )
437       {
438           if( CombineRgn(hRgn, dc->hClipRgn, 0, RGN_COPY) != ERROR )
439           {
440               ret = 1;
441               if (dc->layout & LAYOUT_RTL)
442                   mirror_region( hRgn, hRgn, dc->vis_rect.right - dc->vis_rect.left );
443           }
444       }
445       else ret = 0;
446       release_dc_ptr( dc );
447     }
448     return ret;
449 }
450
451
452 /***********************************************************************
453  *           GetMetaRgn    (GDI32.@)
454  */
455 INT WINAPI GetMetaRgn( HDC hdc, HRGN hRgn )
456 {
457     INT ret = 0;
458     DC * dc = get_dc_ptr( hdc );
459
460     if (dc)
461     {
462         if (dc->hMetaRgn && CombineRgn( hRgn, dc->hMetaRgn, 0, RGN_COPY ) != ERROR)
463         {
464             ret = 1;
465             if (dc->layout & LAYOUT_RTL)
466                 mirror_region( hRgn, hRgn, dc->vis_rect.right - dc->vis_rect.left );
467         }
468         release_dc_ptr( dc );
469     }
470     return ret;
471 }
472
473
474 /***********************************************************************
475  * GetRandomRgn [GDI32.@]
476  *
477  * NOTES
478  *     This function is documented in MSDN online for the case of
479  *     iCode == SYSRGN (4).
480  *
481  *     For iCode == 1 it should return the clip region
482  *                  2 "    "       "   the meta region
483  *                  3 "    "       "   the intersection of the clip with
484  *                                     the meta region (== 'Rao' region).
485  *
486  *     See http://www.codeproject.com/gdi/cliprgnguide.asp
487  */
488 INT WINAPI GetRandomRgn(HDC hDC, HRGN hRgn, INT iCode)
489 {
490     INT ret = 1;
491     RECT visrect;
492     DC *dc = get_dc_ptr( hDC );
493
494     if (!dc) return -1;
495
496     switch (iCode)
497     {
498     case 1:
499         if (dc->hClipRgn) CombineRgn( hRgn, dc->hClipRgn, 0, RGN_COPY );
500         else ret = 0;
501         break;
502     case 2:
503         if (dc->hMetaRgn) CombineRgn( hRgn, dc->hMetaRgn, 0, RGN_COPY );
504         else ret = 0;
505         break;
506     case 3:
507         if (dc->hClipRgn && dc->hMetaRgn) CombineRgn( hRgn, dc->hClipRgn, dc->hMetaRgn, RGN_AND );
508         else if (dc->hClipRgn) CombineRgn( hRgn, dc->hClipRgn, 0, RGN_COPY );
509         else if (dc->hMetaRgn) CombineRgn( hRgn, dc->hMetaRgn, 0, RGN_COPY );
510         else ret = 0;
511         break;
512     case SYSRGN: /* == 4 */
513         update_dc( dc );
514         if (dc->hVisRgn)
515             CombineRgn( hRgn, dc->hVisRgn, 0, RGN_COPY );
516         else if (get_dc_visrect( dc, &visrect ))
517             SetRectRgn( hRgn, visrect.left, visrect.top, visrect.right, visrect.bottom );
518         else
519             ret = 0;
520         /* On Windows NT/2000, the SYSRGN returned is in screen coordinates */
521         if (ret && !(GetVersion() & 0x80000000)) OffsetRgn( hRgn, dc->vis_rect.left, dc->vis_rect.top );
522         break;
523     default:
524         WARN("Unknown code %d\n", iCode);
525         ret = -1;
526         break;
527     }
528     release_dc_ptr( dc );
529     return ret;
530 }
531
532
533 /***********************************************************************
534  *           SetMetaRgn    (GDI32.@)
535  */
536 INT WINAPI SetMetaRgn( HDC hdc )
537 {
538     INT ret;
539     RECT dummy;
540     DC *dc = get_dc_ptr( hdc );
541
542     if (!dc) return ERROR;
543
544     if (dc->hClipRgn)
545     {
546         if (dc->hMetaRgn)
547         {
548             /* the intersection becomes the new meta region */
549             CombineRgn( dc->hMetaRgn, dc->hMetaRgn, dc->hClipRgn, RGN_AND );
550             DeleteObject( dc->hClipRgn );
551             dc->hClipRgn = 0;
552         }
553         else
554         {
555             dc->hMetaRgn = dc->hClipRgn;
556             dc->hClipRgn = 0;
557         }
558     }
559     /* else nothing to do */
560
561     /* Note: no need to call update_dc_clipping, the overall clip region hasn't changed */
562
563     ret = GetRgnBox( dc->hMetaRgn, &dummy );
564     release_dc_ptr( dc );
565     return ret;
566 }