mshtml: Don't crash in QueryInterface if uri is NULL.
[wine] / dlls / gdi / 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 "wownt32.h"
27 #include "wine/winuser16.h"
28 #include "gdi.h"
29 #include "gdi_private.h"
30 #include "wine/debug.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(clipping);
33
34
35 /***********************************************************************
36  *           get_clip_region
37  *
38  * Return the total clip region (if any).
39  */
40 static inline HRGN get_clip_region( DC * dc )
41 {
42     if (dc->hMetaClipRgn) return dc->hMetaClipRgn;
43     if (dc->hMetaRgn) return dc->hMetaRgn;
44     return dc->hClipRgn;
45 }
46
47
48 /***********************************************************************
49  *           CLIPPING_UpdateGCRegion
50  *
51  * Update the GC clip region when the ClipRgn or VisRgn have changed.
52  */
53 void CLIPPING_UpdateGCRegion( DC * dc )
54 {
55     HRGN clip_rgn;
56
57     if (!dc->hVisRgn)
58     {
59         ERR("hVisRgn is zero. Please report this.\n" );
60         exit(1);
61     }
62
63     if (dc->flags & DC_DIRTY) ERR( "DC is dirty. Please report this.\n" );
64
65     /* update the intersection of meta and clip regions */
66     if (dc->hMetaRgn && dc->hClipRgn)
67     {
68         if (!dc->hMetaClipRgn) dc->hMetaClipRgn = CreateRectRgn( 0, 0, 0, 0 );
69         CombineRgn( dc->hMetaClipRgn, dc->hClipRgn, dc->hMetaRgn, RGN_AND );
70         clip_rgn = dc->hMetaClipRgn;
71     }
72     else  /* only one is set, no need for an intersection */
73     {
74         if (dc->hMetaClipRgn) DeleteObject( dc->hMetaClipRgn );
75         dc->hMetaClipRgn = 0;
76         clip_rgn = dc->hMetaRgn ? dc->hMetaRgn : dc->hClipRgn;
77     }
78
79     if (dc->funcs->pSetDeviceClipping)
80         dc->funcs->pSetDeviceClipping( dc->physDev, dc->hVisRgn, clip_rgn );
81 }
82
83 /***********************************************************************
84  *           create_default_clip_region
85  *
86  * Create a default clipping region when none already exists.
87  */
88 static inline void create_default_clip_region( DC * dc )
89 {
90     dc->hClipRgn = CreateRectRgn(0, 0,
91         GetDeviceCaps( dc->hSelf, HORZRES ),
92         GetDeviceCaps( dc->hSelf, VERTRES ));
93 }
94
95
96 /***********************************************************************
97  *           SelectClipRgn    (GDI32.@)
98  */
99 INT WINAPI SelectClipRgn( HDC hdc, HRGN hrgn )
100 {
101     return ExtSelectClipRgn( hdc, hrgn, RGN_COPY );
102 }
103
104
105 /******************************************************************************
106  *              ExtSelectClipRgn        [GDI32.@]
107  */
108 INT WINAPI ExtSelectClipRgn( HDC hdc, HRGN hrgn, INT fnMode )
109 {
110     INT retval;
111     RECT rect;
112     DC * dc = DC_GetDCUpdate( hdc );
113     if (!dc) return ERROR;
114
115     TRACE("%p %p %d\n", hdc, hrgn, fnMode );
116
117     if (dc->funcs->pExtSelectClipRgn)
118     {
119         retval = dc->funcs->pExtSelectClipRgn( dc->physDev, hrgn, fnMode );
120         GDI_ReleaseObj( hdc );
121         return retval;
122     }
123
124     if (!hrgn)
125     {
126         if (fnMode == RGN_COPY)
127         {
128             if (dc->hClipRgn) DeleteObject( dc->hClipRgn );
129             dc->hClipRgn = 0;
130         }
131         else
132         {
133             FIXME("Unimplemented: hrgn NULL in mode: %d\n", fnMode);
134             GDI_ReleaseObj( hdc );
135             return ERROR;
136         }
137     }
138     else
139     {
140         if (!dc->hClipRgn)
141             create_default_clip_region( dc );
142
143         if(fnMode == RGN_COPY)
144             CombineRgn( dc->hClipRgn, hrgn, 0, fnMode );
145         else
146             CombineRgn( dc->hClipRgn, dc->hClipRgn, hrgn, fnMode);
147     }
148
149     CLIPPING_UpdateGCRegion( dc );
150     GDI_ReleaseObj( hdc );
151
152     return GetClipBox(hdc, &rect);
153 }
154
155 /***********************************************************************
156  *           SelectVisRgn   (GDI.105)
157  */
158 INT16 WINAPI SelectVisRgn16( HDC16 hdc16, HRGN16 hrgn )
159 {
160     int retval;
161     HDC hdc = HDC_32( hdc16 );
162     DC * dc;
163
164     if (!hrgn) return ERROR;
165     if (!(dc = DC_GetDCPtr( hdc ))) return ERROR;
166
167     TRACE("%p %04x\n", hdc, hrgn );
168
169     dc->flags &= ~DC_DIRTY;
170
171     retval = CombineRgn( dc->hVisRgn, HRGN_32(hrgn), 0, RGN_COPY );
172     CLIPPING_UpdateGCRegion( dc );
173     GDI_ReleaseObj( hdc );
174     return retval;
175 }
176
177
178 /***********************************************************************
179  *           OffsetClipRgn    (GDI32.@)
180  */
181 INT WINAPI OffsetClipRgn( HDC hdc, INT x, INT y )
182 {
183     INT ret = SIMPLEREGION;
184     DC *dc = DC_GetDCUpdate( hdc );
185     if (!dc) return ERROR;
186
187     TRACE("%p %d,%d\n", hdc, x, y );
188
189     if(dc->funcs->pOffsetClipRgn)
190     {
191         ret = dc->funcs->pOffsetClipRgn( dc->physDev, x, y );
192         /* FIXME: ret is just a success flag, we should return a proper value */
193     }
194     else if (dc->hClipRgn) {
195         ret = OffsetRgn( dc->hClipRgn, MulDiv( x, dc->vportExtX, dc->wndExtX ),
196                          MulDiv( y, dc->vportExtY, dc->wndExtY ) );
197         CLIPPING_UpdateGCRegion( dc );
198     }
199     GDI_ReleaseObj( hdc );
200     return ret;
201 }
202
203
204 /***********************************************************************
205  *           OffsetVisRgn    (GDI.102)
206  */
207 INT16 WINAPI OffsetVisRgn16( HDC16 hdc16, INT16 x, INT16 y )
208 {
209     INT16 retval;
210     HDC hdc = HDC_32( hdc16 );
211     DC * dc = DC_GetDCUpdate( hdc );
212     if (!dc) return ERROR;
213     TRACE("%p %d,%d\n", hdc, x, y );
214     retval = OffsetRgn( dc->hVisRgn, x, y );
215     CLIPPING_UpdateGCRegion( dc );
216     GDI_ReleaseObj( hdc );
217     return retval;
218 }
219
220
221 /***********************************************************************
222  *           ExcludeClipRect    (GDI32.@)
223  */
224 INT WINAPI ExcludeClipRect( HDC hdc, INT left, INT top,
225                                 INT right, INT bottom )
226 {
227     HRGN newRgn;
228     INT ret;
229     DC *dc = DC_GetDCUpdate( hdc );
230     if (!dc) return ERROR;
231
232     TRACE("%p %dx%d,%dx%d\n", hdc, left, top, right, bottom );
233
234     if(dc->funcs->pExcludeClipRect)
235     {
236         ret = dc->funcs->pExcludeClipRect( dc->physDev, left, top, right, bottom );
237         /* FIXME: ret is just a success flag, we should return a proper value */
238     }
239     else
240     {
241         POINT pt[2];
242
243         pt[0].x = left;
244         pt[0].y = top;
245         pt[1].x = right;
246         pt[1].y = bottom;
247         LPtoDP( hdc, pt, 2 );
248         if (!(newRgn = CreateRectRgn( pt[0].x, pt[0].y, pt[1].x, pt[1].y ))) ret = ERROR;
249         else
250         {
251             if (!dc->hClipRgn)
252                 create_default_clip_region( dc );
253             ret = CombineRgn( dc->hClipRgn, dc->hClipRgn, newRgn, RGN_DIFF );
254             DeleteObject( newRgn );
255         }
256         if (ret != ERROR) CLIPPING_UpdateGCRegion( dc );
257     }
258     GDI_ReleaseObj( hdc );
259     return ret;
260 }
261
262
263 /***********************************************************************
264  *           IntersectClipRect    (GDI32.@)
265  */
266 INT WINAPI IntersectClipRect( HDC hdc, INT left, INT top, INT right, INT bottom )
267 {
268     INT ret;
269     DC *dc = DC_GetDCUpdate( hdc );
270     if (!dc) return ERROR;
271
272     TRACE("%p %d,%d - %d,%d\n", hdc, left, top, right, bottom );
273
274     if(dc->funcs->pIntersectClipRect)
275     {
276         ret = dc->funcs->pIntersectClipRect( dc->physDev, left, top, right, bottom );
277         /* FIXME: ret is just a success flag, we should return a proper value */
278     }
279     else
280     {
281         POINT pt[2];
282
283         pt[0].x = left;
284         pt[0].y = top;
285         pt[1].x = right;
286         pt[1].y = bottom;
287
288         LPtoDP( hdc, pt, 2 );
289
290         if (!dc->hClipRgn)
291         {
292             dc->hClipRgn = CreateRectRgn( pt[0].x, pt[0].y, pt[1].x, pt[1].y );
293             ret = SIMPLEREGION;
294         }
295         else
296         {
297             HRGN newRgn;
298
299             if (!(newRgn = CreateRectRgn( pt[0].x, pt[0].y, pt[1].x, pt[1].y ))) ret = ERROR;
300             else
301             {
302                 ret = CombineRgn( dc->hClipRgn, dc->hClipRgn, newRgn, RGN_AND );
303                 DeleteObject( newRgn );
304             }
305         }
306         if (ret != ERROR) CLIPPING_UpdateGCRegion( dc );
307     }
308     GDI_ReleaseObj( hdc );
309     return ret;
310 }
311
312
313 /***********************************************************************
314  *           ExcludeVisRect   (GDI.73)
315  */
316 INT16 WINAPI ExcludeVisRect16( HDC16 hdc16, INT16 left, INT16 top, INT16 right, INT16 bottom )
317 {
318     HRGN tempRgn;
319     INT16 ret;
320     POINT pt[2];
321     HDC hdc = HDC_32( hdc16 );
322     DC * dc = DC_GetDCUpdate( hdc );
323     if (!dc) return ERROR;
324
325     pt[0].x = left;
326     pt[0].y = top;
327     pt[1].x = right;
328     pt[1].y = bottom;
329
330     LPtoDP( hdc, pt, 2 );
331
332     TRACE("%p %ld,%ld - %ld,%ld\n", hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
333
334     if (!(tempRgn = CreateRectRgn( pt[0].x, pt[0].y, pt[1].x, pt[1].y ))) ret = ERROR;
335     else
336     {
337         ret = CombineRgn( dc->hVisRgn, dc->hVisRgn, tempRgn, RGN_DIFF );
338         DeleteObject( tempRgn );
339     }
340     if (ret != ERROR) CLIPPING_UpdateGCRegion( dc );
341     GDI_ReleaseObj( hdc );
342     return ret;
343 }
344
345
346 /***********************************************************************
347  *           IntersectVisRect   (GDI.98)
348  */
349 INT16 WINAPI IntersectVisRect16( HDC16 hdc16, INT16 left, INT16 top, INT16 right, INT16 bottom )
350 {
351     HRGN tempRgn;
352     INT16 ret;
353     POINT pt[2];
354     HDC hdc = HDC_32( hdc16 );
355     DC * dc = DC_GetDCUpdate( hdc );
356     if (!dc) return ERROR;
357
358     pt[0].x = left;
359     pt[0].y = top;
360     pt[1].x = right;
361     pt[1].y = bottom;
362
363     LPtoDP( hdc, pt, 2 );
364
365     TRACE("%p %ld,%ld - %ld,%ld\n", hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
366
367
368     if (!(tempRgn = CreateRectRgn( pt[0].x, pt[0].y, pt[1].x, pt[1].y ))) ret = ERROR;
369     else
370     {
371         ret = CombineRgn( dc->hVisRgn, dc->hVisRgn, tempRgn, RGN_AND );
372         DeleteObject( tempRgn );
373     }
374     if (ret != ERROR) CLIPPING_UpdateGCRegion( dc );
375     GDI_ReleaseObj( hdc );
376     return ret;
377 }
378
379
380 /***********************************************************************
381  *           PtVisible    (GDI32.@)
382  */
383 BOOL WINAPI PtVisible( HDC hdc, INT x, INT y )
384 {
385     POINT pt;
386     BOOL ret;
387     HRGN clip;
388     DC *dc = DC_GetDCUpdate( hdc );
389
390     TRACE("%p %d,%d\n", hdc, x, y );
391     if (!dc) return FALSE;
392
393     pt.x = x;
394     pt.y = y;
395     LPtoDP( hdc, &pt, 1 );
396     ret = PtInRegion( dc->hVisRgn, pt.x, pt.y );
397     if (ret && (clip = get_clip_region(dc))) ret = PtInRegion( clip, pt.x, pt.y );
398     GDI_ReleaseObj( hdc );
399     return ret;
400 }
401
402
403 /***********************************************************************
404  *           RectVisible    (GDI32.@)
405  */
406 BOOL WINAPI RectVisible( HDC hdc, const RECT* rect )
407 {
408     RECT tmpRect;
409     BOOL ret;
410     HRGN clip;
411     DC *dc = DC_GetDCUpdate( hdc );
412     if (!dc) return FALSE;
413     TRACE("%p %ld,%ldx%ld,%ld\n", hdc, rect->left, rect->top, rect->right, rect->bottom );
414
415     tmpRect = *rect;
416     LPtoDP( hdc, (POINT *)&tmpRect, 2 );
417
418     if ((clip = get_clip_region(dc)))
419     {
420         HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
421         CombineRgn( hrgn, dc->hVisRgn, clip, RGN_AND );
422         ret = RectInRegion( hrgn, &tmpRect );
423         DeleteObject( hrgn );
424     }
425     else ret = RectInRegion( dc->hVisRgn, &tmpRect );
426     GDI_ReleaseObj( hdc );
427     return ret;
428 }
429
430
431 /***********************************************************************
432  *           GetClipBox    (GDI32.@)
433  */
434 INT WINAPI GetClipBox( HDC hdc, LPRECT rect )
435 {
436     INT ret;
437     HRGN clip;
438     DC *dc = DC_GetDCUpdate( hdc );
439     if (!dc) return ERROR;
440     if ((clip = get_clip_region(dc)))
441     {
442         HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
443         CombineRgn( hrgn, dc->hVisRgn, clip, RGN_AND );
444         ret = GetRgnBox( hrgn, rect );
445         DeleteObject( hrgn );
446     }
447     else ret = GetRgnBox( dc->hVisRgn, rect );
448     DPtoLP( hdc, (LPPOINT)rect, 2 );
449     GDI_ReleaseObj( hdc );
450     return ret;
451 }
452
453
454 /***********************************************************************
455  *           GetClipRgn  (GDI32.@)
456  */
457 INT WINAPI GetClipRgn( HDC hdc, HRGN hRgn )
458 {
459     INT ret = -1;
460     DC * dc;
461     if (hRgn && (dc = DC_GetDCPtr( hdc )))
462     {
463       if( dc->hClipRgn )
464       {
465           if( CombineRgn(hRgn, dc->hClipRgn, 0, RGN_COPY) != ERROR ) ret = 1;
466       }
467       else ret = 0;
468       GDI_ReleaseObj( hdc );
469     }
470     return ret;
471 }
472
473
474 /***********************************************************************
475  *           GetMetaRgn    (GDI32.@)
476  */
477 INT WINAPI GetMetaRgn( HDC hdc, HRGN hRgn )
478 {
479     INT ret = 0;
480     DC * dc = DC_GetDCPtr( hdc );
481
482     if (dc)
483     {
484         if (dc->hMetaRgn && CombineRgn( hRgn, dc->hMetaRgn, 0, RGN_COPY ) != ERROR)
485             ret = 1;
486         GDI_ReleaseObj( hdc );
487     }
488     return ret;
489 }
490
491
492 /***********************************************************************
493  *           SaveVisRgn   (GDI.129)
494  */
495 HRGN16 WINAPI SaveVisRgn16( HDC16 hdc16 )
496 {
497     struct saved_visrgn *saved;
498     HDC hdc = HDC_32( hdc16 );
499     DC *dc = DC_GetDCUpdate( hdc );
500
501     if (!dc) return 0;
502     TRACE("%p\n", hdc );
503
504     if (!(saved = HeapAlloc( GetProcessHeap(), 0, sizeof(*saved) ))) goto error;
505     if (!(saved->hrgn = CreateRectRgn( 0, 0, 0, 0 ))) goto error;
506     CombineRgn( saved->hrgn, dc->hVisRgn, 0, RGN_COPY );
507     saved->next = dc->saved_visrgn;
508     dc->saved_visrgn = saved;
509     GDI_ReleaseObj( hdc );
510     return HRGN_16(saved->hrgn);
511
512 error:
513     GDI_ReleaseObj( hdc );
514     HeapFree( GetProcessHeap(), 0, saved );
515     return 0;
516 }
517
518
519 /***********************************************************************
520  *           RestoreVisRgn   (GDI.130)
521  */
522 INT16 WINAPI RestoreVisRgn16( HDC16 hdc16 )
523 {
524     struct saved_visrgn *saved;
525     HDC hdc = HDC_32( hdc16 );
526     DC *dc = DC_GetDCPtr( hdc );
527     INT16 ret = ERROR;
528
529     if (!dc) return ERROR;
530
531     TRACE("%p\n", hdc );
532
533     if (!(saved = dc->saved_visrgn)) goto done;
534
535     ret = CombineRgn( dc->hVisRgn, saved->hrgn, 0, RGN_COPY );
536     dc->saved_visrgn = saved->next;
537     DeleteObject( saved->hrgn );
538     HeapFree( GetProcessHeap(), 0, saved );
539     dc->flags &= ~DC_DIRTY;
540     CLIPPING_UpdateGCRegion( dc );
541  done:
542     GDI_ReleaseObj( hdc );
543     return ret;
544 }
545
546
547 /***********************************************************************
548  * GetRandomRgn [GDI32.@]
549  *
550  * NOTES
551  *     This function is documented in MSDN online for the case of
552  *     iCode == SYSRGN (4).
553  *
554  *     For iCode == 1 it should return the clip region
555  *                  2 "    "       "   the meta region
556  *                  3 "    "       "   the intersection of the clip with
557  *                                     the meta region (== 'Rao' region).
558  *
559  *     See http://www.codeproject.com/gdi/cliprgnguide.asp
560  */
561 INT WINAPI GetRandomRgn(HDC hDC, HRGN hRgn, INT iCode)
562 {
563     HRGN rgn;
564     DC *dc = DC_GetDCPtr( hDC );
565
566     if (!dc) return -1;
567
568     switch (iCode)
569     {
570     case 1:
571         rgn = dc->hClipRgn;
572         break;
573     case 2:
574         rgn = dc->hMetaRgn;
575         break;
576     case 3:
577         rgn = dc->hMetaClipRgn;
578         if(!rgn) rgn = dc->hClipRgn;
579         if(!rgn) rgn = dc->hMetaRgn;
580         break;
581     case SYSRGN: /* == 4 */
582         rgn = dc->hVisRgn;
583         break;
584     default:
585         WARN("Unknown code %d\n", iCode);
586         GDI_ReleaseObj( hDC );
587         return -1;
588     }
589     if (rgn) CombineRgn( hRgn, rgn, 0, RGN_COPY );
590     GDI_ReleaseObj( hDC );
591
592     /* On Windows NT/2000, the SYSRGN returned is in screen coordinates */
593     if (iCode == SYSRGN && !(GetVersion() & 0x80000000))
594     {
595         POINT org;
596         GetDCOrgEx( hDC, &org );
597         OffsetRgn( hRgn, org.x, org.y );
598     }
599     return (rgn != 0);
600 }
601
602
603 /***********************************************************************
604  *           SetMetaRgn    (GDI32.@)
605  */
606 INT WINAPI SetMetaRgn( HDC hdc )
607 {
608     INT ret;
609     RECT dummy;
610     DC *dc = DC_GetDCPtr( hdc );
611
612     if (!dc) return ERROR;
613
614     if (dc->hMetaClipRgn)
615     {
616         /* the intersection becomes the new meta region */
617         DeleteObject( dc->hMetaRgn );
618         DeleteObject( dc->hClipRgn );
619         dc->hMetaRgn = dc->hMetaClipRgn;
620         dc->hClipRgn = 0;
621         dc->hMetaClipRgn = 0;
622     }
623     else if (dc->hClipRgn)
624     {
625         dc->hMetaRgn = dc->hClipRgn;
626         dc->hClipRgn = 0;
627     }
628     /* else nothing to do */
629
630     /* Note: no need to call CLIPPING_UpdateGCRegion, the overall clip region hasn't changed */
631
632     ret = GetRgnBox( dc->hMetaRgn, &dummy );
633     GDI_ReleaseObj( hdc );
634     return ret;
635 }