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