mshtml: Implement IHTMLDOMNode previousSibling.
[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_region
34  *
35  * Return the total clip region (if any).
36  */
37 static inline HRGN get_clip_region( DC * dc )
38 {
39     if (dc->hMetaClipRgn) return dc->hMetaClipRgn;
40     if (dc->hMetaRgn) return dc->hMetaRgn;
41     return dc->hClipRgn;
42 }
43
44
45 /***********************************************************************
46  *           get_clip_rect
47  *
48  * Compute a clip rectangle from its logical coordinates.
49  */
50 static inline RECT get_clip_rect( DC * dc, int left, int top, int right, int bottom )
51 {
52     RECT rect;
53
54     rect.left   = left;
55     rect.top    = top;
56     rect.right  = right;
57     rect.bottom = bottom;
58     LPtoDP( dc->hSelf, (POINT *)&rect, 2 );
59     if (dc->layout & LAYOUT_RTL)
60     {
61         int tmp = rect.left;
62         rect.left = rect.right + 1;
63         rect.right = tmp + 1;
64     }
65     return rect;
66 }
67
68 /***********************************************************************
69  *           CLIPPING_UpdateGCRegion
70  *
71  * Update the GC clip region when the ClipRgn or VisRgn have changed.
72  */
73 void CLIPPING_UpdateGCRegion( DC * dc )
74 {
75     HRGN clip_rgn;
76     PHYSDEV physdev = GET_DC_PHYSDEV( dc, pSetDeviceClipping );
77
78     /* update the intersection of meta and clip regions */
79     if (dc->hMetaRgn && dc->hClipRgn)
80     {
81         if (!dc->hMetaClipRgn) dc->hMetaClipRgn = CreateRectRgn( 0, 0, 0, 0 );
82         CombineRgn( dc->hMetaClipRgn, dc->hClipRgn, dc->hMetaRgn, RGN_AND );
83         clip_rgn = dc->hMetaClipRgn;
84     }
85     else  /* only one is set, no need for an intersection */
86     {
87         if (dc->hMetaClipRgn) DeleteObject( dc->hMetaClipRgn );
88         dc->hMetaClipRgn = 0;
89         clip_rgn = dc->hMetaRgn ? dc->hMetaRgn : dc->hClipRgn;
90     }
91     physdev->funcs->pSetDeviceClipping( physdev, dc->hVisRgn, clip_rgn );
92 }
93
94 /***********************************************************************
95  *           create_default_clip_region
96  *
97  * Create a default clipping region when none already exists.
98  */
99 static inline void create_default_clip_region( DC * dc )
100 {
101     UINT width, height;
102
103     if (dc->header.type == OBJ_MEMDC)
104     {
105         BITMAP bitmap;
106
107         GetObjectW( dc->hBitmap, sizeof(bitmap), &bitmap );
108         width = bitmap.bmWidth;
109         height = bitmap.bmHeight;
110     }
111     else
112     {
113         width = GetDeviceCaps( dc->hSelf, DESKTOPHORZRES );
114         height = GetDeviceCaps( dc->hSelf, DESKTOPVERTRES );
115     }
116     dc->hClipRgn = CreateRectRgn( 0, 0, width, height );
117 }
118
119
120 /***********************************************************************
121  *           null driver fallback implementations
122  */
123
124 INT CDECL nulldrv_ExtSelectClipRgn( PHYSDEV dev, HRGN rgn, INT mode )
125 {
126     DC *dc = get_nulldrv_dc( dev );
127     INT ret;
128
129     if (!rgn)
130     {
131         if (mode != RGN_COPY)
132         {
133             FIXME("Unimplemented: hrgn NULL in mode: %d\n", mode);
134             return ERROR;
135         }
136         if (dc->hClipRgn) DeleteObject( dc->hClipRgn );
137         dc->hClipRgn = 0;
138         ret = SIMPLEREGION;
139     }
140     else
141     {
142         HRGN mirrored = 0;
143
144         if (dc->layout & LAYOUT_RTL)
145         {
146             if (!(mirrored = CreateRectRgn( 0, 0, 0, 0 ))) return ERROR;
147             mirror_region( mirrored, rgn, dc->vis_rect.right - dc->vis_rect.left );
148             rgn = mirrored;
149         }
150
151         if (!dc->hClipRgn)
152             create_default_clip_region( dc );
153
154         if (mode == RGN_COPY)
155             ret = CombineRgn( dc->hClipRgn, rgn, 0, mode );
156         else
157             ret = CombineRgn( dc->hClipRgn, dc->hClipRgn, rgn, mode);
158
159         if (mirrored) DeleteObject( mirrored );
160     }
161     CLIPPING_UpdateGCRegion( dc );
162     return ret;
163 }
164
165 INT CDECL nulldrv_ExcludeClipRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
166 {
167     DC *dc = get_nulldrv_dc( dev );
168     RECT rect = get_clip_rect( dc, left, top, right, bottom );
169     INT ret;
170     HRGN rgn;
171
172     if (!(rgn = CreateRectRgnIndirect( &rect ))) return ERROR;
173     if (!dc->hClipRgn) create_default_clip_region( dc );
174     ret = CombineRgn( dc->hClipRgn, dc->hClipRgn, rgn, RGN_DIFF );
175     DeleteObject( rgn );
176     if (ret != ERROR) CLIPPING_UpdateGCRegion( dc );
177     return ret;
178 }
179
180 INT CDECL nulldrv_IntersectClipRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
181 {
182     DC *dc = get_nulldrv_dc( dev );
183     RECT rect = get_clip_rect( dc, left, top, right, bottom );
184     INT ret;
185     HRGN rgn;
186
187     if (!dc->hClipRgn)
188     {
189         dc->hClipRgn = CreateRectRgnIndirect( &rect );
190         ret = SIMPLEREGION;
191     }
192     else
193     {
194         if (!(rgn = CreateRectRgnIndirect( &rect ))) return ERROR;
195         ret = CombineRgn( dc->hClipRgn, dc->hClipRgn, rgn, RGN_AND );
196         DeleteObject( rgn );
197     }
198     if (ret != ERROR) CLIPPING_UpdateGCRegion( dc );
199     return ret;
200 }
201
202 INT CDECL nulldrv_OffsetClipRgn( PHYSDEV dev, INT x, INT y )
203 {
204     DC *dc = get_nulldrv_dc( dev );
205     INT ret = NULLREGION;
206
207     if (dc->hClipRgn)
208     {
209         x = MulDiv( x, dc->vportExtX, dc->wndExtX );
210         y = MulDiv( y, dc->vportExtY, dc->wndExtY );
211         if (dc->layout & LAYOUT_RTL) x = -x;
212         ret = OffsetRgn( dc->hClipRgn, x, y );
213         CLIPPING_UpdateGCRegion( dc );
214     }
215     return ret;
216 }
217
218
219 /***********************************************************************
220  *           SelectClipRgn    (GDI32.@)
221  */
222 INT WINAPI SelectClipRgn( HDC hdc, HRGN hrgn )
223 {
224     return ExtSelectClipRgn( hdc, hrgn, RGN_COPY );
225 }
226
227
228 /******************************************************************************
229  *              ExtSelectClipRgn        [GDI32.@]
230  */
231 INT WINAPI ExtSelectClipRgn( HDC hdc, HRGN hrgn, INT fnMode )
232 {
233     INT retval = ERROR;
234     DC * dc = get_dc_ptr( hdc );
235
236     TRACE("%p %p %d\n", hdc, hrgn, fnMode );
237
238     if (dc)
239     {
240         PHYSDEV physdev = GET_DC_PHYSDEV( dc, pExtSelectClipRgn );
241         update_dc( dc );
242         retval = physdev->funcs->pExtSelectClipRgn( physdev, hrgn, fnMode );
243         release_dc_ptr( dc );
244     }
245     return retval;
246 }
247
248 /***********************************************************************
249  *           __wine_set_visible_region   (GDI32.@)
250  */
251 void CDECL __wine_set_visible_region( HDC hdc, HRGN hrgn, const RECT *vis_rect )
252 {
253     DC * dc;
254
255     if (!(dc = get_dc_ptr( hdc ))) return;
256
257     TRACE( "%p %p %s\n", hdc, hrgn, wine_dbgstr_rect(vis_rect) );
258
259     /* map region to DC coordinates */
260     OffsetRgn( hrgn, -vis_rect->left, -vis_rect->top );
261
262     DeleteObject( dc->hVisRgn );
263     dc->dirty = 0;
264     dc->vis_rect = *vis_rect;
265     dc->hVisRgn = hrgn;
266     DC_UpdateXforms( dc );
267     CLIPPING_UpdateGCRegion( dc );
268     release_dc_ptr( dc );
269 }
270
271
272 /***********************************************************************
273  *           OffsetClipRgn    (GDI32.@)
274  */
275 INT WINAPI OffsetClipRgn( HDC hdc, INT x, INT y )
276 {
277     INT ret = ERROR;
278     DC *dc = get_dc_ptr( hdc );
279
280     TRACE("%p %d,%d\n", hdc, x, y );
281
282     if (dc)
283     {
284         PHYSDEV physdev = GET_DC_PHYSDEV( dc, pOffsetClipRgn );
285         update_dc( dc );
286         ret = physdev->funcs->pOffsetClipRgn( physdev, x, y );
287         release_dc_ptr( dc );
288     }
289     return ret;
290 }
291
292
293 /***********************************************************************
294  *           ExcludeClipRect    (GDI32.@)
295  */
296 INT WINAPI ExcludeClipRect( HDC hdc, INT left, INT top,
297                                 INT right, INT bottom )
298 {
299     INT ret = ERROR;
300     DC *dc = get_dc_ptr( hdc );
301
302     TRACE("%p %d,%d-%d,%d\n", hdc, left, top, right, bottom );
303
304     if (dc)
305     {
306         PHYSDEV physdev = GET_DC_PHYSDEV( dc, pExcludeClipRect );
307         update_dc( dc );
308         ret = physdev->funcs->pExcludeClipRect( physdev, left, top, right, bottom );
309         release_dc_ptr( dc );
310     }
311     return ret;
312 }
313
314
315 /***********************************************************************
316  *           IntersectClipRect    (GDI32.@)
317  */
318 INT WINAPI IntersectClipRect( HDC hdc, INT left, INT top, INT right, INT bottom )
319 {
320     INT ret = ERROR;
321     DC *dc = get_dc_ptr( hdc );
322
323     TRACE("%p %d,%d - %d,%d\n", hdc, left, top, right, bottom );
324
325     if (dc)
326     {
327         PHYSDEV physdev = GET_DC_PHYSDEV( dc, pIntersectClipRect );
328         update_dc( dc );
329         ret = physdev->funcs->pIntersectClipRect( physdev, left, top, right, bottom );
330         release_dc_ptr( dc );
331     }
332     return ret;
333 }
334
335
336 /***********************************************************************
337  *           PtVisible    (GDI32.@)
338  */
339 BOOL WINAPI PtVisible( HDC hdc, INT x, INT y )
340 {
341     POINT pt;
342     BOOL ret;
343     HRGN clip;
344     DC *dc = get_dc_ptr( hdc );
345
346     TRACE("%p %d,%d\n", hdc, x, y );
347     if (!dc) return FALSE;
348
349     pt.x = x;
350     pt.y = y;
351     LPtoDP( hdc, &pt, 1 );
352     update_dc( dc );
353     ret = PtInRegion( dc->hVisRgn, pt.x, pt.y );
354     if (ret && (clip = get_clip_region(dc))) ret = PtInRegion( clip, pt.x, pt.y );
355     release_dc_ptr( dc );
356     return ret;
357 }
358
359
360 /***********************************************************************
361  *           RectVisible    (GDI32.@)
362  */
363 BOOL WINAPI RectVisible( HDC hdc, const RECT* rect )
364 {
365     RECT tmpRect;
366     BOOL ret;
367     HRGN clip;
368     DC *dc = get_dc_ptr( hdc );
369     if (!dc) return FALSE;
370     TRACE("%p %s\n", hdc, wine_dbgstr_rect( rect ));
371
372     tmpRect = *rect;
373     LPtoDP( hdc, (POINT *)&tmpRect, 2 );
374
375     update_dc( dc );
376     if ((clip = get_clip_region(dc)))
377     {
378         HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
379         CombineRgn( hrgn, dc->hVisRgn, clip, RGN_AND );
380         ret = RectInRegion( hrgn, &tmpRect );
381         DeleteObject( hrgn );
382     }
383     else ret = RectInRegion( dc->hVisRgn, &tmpRect );
384     release_dc_ptr( dc );
385     return ret;
386 }
387
388
389 /***********************************************************************
390  *           GetClipBox    (GDI32.@)
391  */
392 INT WINAPI GetClipBox( HDC hdc, LPRECT rect )
393 {
394     INT ret;
395     HRGN clip;
396     DC *dc = get_dc_ptr( hdc );
397     if (!dc) return ERROR;
398
399     update_dc( dc );
400     if ((clip = get_clip_region(dc)))
401     {
402         HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
403         CombineRgn( hrgn, dc->hVisRgn, clip, RGN_AND );
404         ret = GetRgnBox( hrgn, rect );
405         DeleteObject( hrgn );
406     }
407     else ret = GetRgnBox( dc->hVisRgn, 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 }