xref: /reactos/win32ss/gdi/ntgdi/cliprgn.c (revision 50cf16b3)
1 /*
2  * COPYRIGHT:        GNU GPL, See COPYING in the top level directory
3  * PROJECT:          ReactOS Win32k subsystem
4  * PURPOSE:          Clip region functions
5  * FILE:             win32ss/gdi/ntgdi/cliprgn.c
6  * PROGRAMER:        Unknown
7  */
8 
9 #include <win32k.h>
10 
11 #define NDEBUG
12 #include <debug.h>
13 
14 VOID
15 FASTCALL
16 IntGdiReleaseRaoRgn(PDC pDC)
17 {
18     INT Index = GDI_HANDLE_GET_INDEX(pDC->BaseObject.hHmgr);
19     PGDI_TABLE_ENTRY Entry = &GdiHandleTable->Entries[Index];
20     pDC->fs |= DC_FLAG_DIRTY_RAO;
21     Entry->Flags |= GDI_ENTRY_VALIDATE_VIS;
22     RECTL_vSetEmptyRect(&pDC->erclClip);
23     REGION_Delete(pDC->prgnRao);
24     pDC->prgnRao = NULL;
25 }
26 
27 VOID
28 FASTCALL
29 IntGdiReleaseVisRgn(PDC pDC)
30 {
31     INT Index = GDI_HANDLE_GET_INDEX(pDC->BaseObject.hHmgr);
32     PGDI_TABLE_ENTRY Entry = &GdiHandleTable->Entries[Index];
33     pDC->fs |= DC_FLAG_DIRTY_RAO;
34     Entry->Flags |= GDI_ENTRY_VALIDATE_VIS;
35     RECTL_vSetEmptyRect(&pDC->erclClip);
36     REGION_Delete(pDC->prgnVis);
37     pDC->prgnVis = prgnDefault;
38 }
39 
40 VOID
41 FASTCALL
42 GdiSelectVisRgn(
43     HDC hdc,
44     PREGION prgn)
45 {
46     DC *dc;
47 
48     if (!(dc = DC_LockDc(hdc)))
49     {
50         EngSetLastError(ERROR_INVALID_HANDLE);
51         return;
52     }
53 
54     dc->fs |= DC_FLAG_DIRTY_RAO;
55 
56     ASSERT(dc->prgnVis != NULL);
57     ASSERT(prgn != NULL);
58 
59     IntGdiCombineRgn(dc->prgnVis, prgn, NULL, RGN_COPY);
60     REGION_bOffsetRgn(dc->prgnVis, -dc->ptlDCOrig.x, -dc->ptlDCOrig.y);
61 
62     DC_UnlockDc(dc);
63 }
64 
65 
66 int
67 FASTCALL
68 IntGdiExtSelectClipRgn(
69     PDC dc,
70     PREGION prgn,
71     int fnMode)
72 {
73     if (fnMode == RGN_COPY)
74     {
75         if (!prgn)
76         {
77             if (dc->dclevel.prgnClip != NULL)
78             {
79                 REGION_Delete(dc->dclevel.prgnClip);
80                 dc->dclevel.prgnClip = NULL;
81                 dc->fs |= DC_FLAG_DIRTY_RAO;
82             }
83             return SIMPLEREGION;
84         }
85 
86         if (!dc->dclevel.prgnClip)
87             dc->dclevel.prgnClip = IntSysCreateRectpRgn(0, 0, 0, 0);
88 
89         dc->fs |= DC_FLAG_DIRTY_RAO;
90 
91         return IntGdiCombineRgn(dc->dclevel.prgnClip, prgn, NULL, RGN_COPY);
92     }
93 
94     ASSERT(prgn != NULL);
95 
96     if (!dc->dclevel.prgnClip)
97     {
98         RECTL rect;
99 
100         REGION_GetRgnBox(dc->prgnVis, &rect);
101         dc->dclevel.prgnClip = IntSysCreateRectpRgnIndirect(&rect);
102     }
103 
104     dc->fs |= DC_FLAG_DIRTY_RAO;
105 
106     return IntGdiCombineRgn(dc->dclevel.prgnClip, dc->dclevel.prgnClip, prgn, fnMode);
107 }
108 
109 
110 int
111 APIENTRY
112 NtGdiExtSelectClipRgn(
113     HDC  hDC,
114     HRGN  hrgn,
115     int  fnMode)
116 {
117     int retval;
118     DC *dc;
119     PREGION prgn;
120 
121     if (!(dc = DC_LockDc(hDC)))
122     {
123         EngSetLastError(ERROR_INVALID_HANDLE);
124         return ERROR;
125     }
126 
127     prgn = REGION_LockRgn(hrgn);
128 
129     if ((prgn == NULL) && (fnMode != RGN_COPY))
130     {
131         EngSetLastError(ERROR_INVALID_HANDLE);
132         retval = ERROR;
133     }
134     else
135     {
136         retval = IntGdiExtSelectClipRgn(dc, prgn, fnMode);
137     }
138 
139     if (prgn)
140         REGION_UnlockRgn(prgn);
141 
142     DC_UnlockDc(dc);
143     return retval;
144 }
145 
146 _Success_(return!=ERROR)
147 INT
148 FASTCALL
149 GdiGetClipBox(
150     _In_ HDC hdc,
151     _Out_ LPRECT prc)
152 {
153     PDC pdc;
154     INT iComplexity;
155 
156     /* Lock the DC */
157     pdc = DC_LockDc(hdc);
158     if (!pdc)
159     {
160         return ERROR;
161     }
162 
163     /* Update RAO region if necessary */
164     if (pdc->fs & DC_FLAG_DIRTY_RAO)
165         CLIPPING_UpdateGCRegion(pdc);
166 
167     /* Check if we have a RAO region (intersection of API and VIS region) */
168     if (pdc->prgnRao)
169     {
170         /* We have a RAO region, use it */
171         iComplexity = REGION_GetRgnBox(pdc->prgnRao, prc);
172     }
173     else
174     {
175         /* No RAO region means no API region, so use the VIS region */
176         ASSERT(pdc->prgnVis);
177         iComplexity = REGION_GetRgnBox(pdc->prgnVis, prc);
178     }
179 
180     /* Unlock the DC */
181     DC_UnlockDc(pdc);
182 
183     /* Convert the rect to logical coordinates */
184     IntDPtoLP(pdc, (LPPOINT)prc, 2);
185 
186     /* Return the complexity */
187     return iComplexity;
188 }
189 
190 _Success_(return!=ERROR)
191 INT
192 APIENTRY
193 NtGdiGetAppClipBox(
194     _In_ HDC hdc,
195     _Out_ LPRECT prc)
196 {
197     RECT rect;
198     INT iComplexity;
199 
200     /* Call the internal function */
201     iComplexity = GdiGetClipBox(hdc, &rect);
202 
203     if (iComplexity != ERROR)
204     {
205         _SEH2_TRY
206         {
207             ProbeForWrite(prc, sizeof(RECT), 1);
208             *prc = rect;
209         }
210         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
211         {
212             iComplexity = ERROR;
213         }
214         _SEH2_END
215     }
216 
217     /* Return the complexity */
218     return iComplexity;
219 }
220 
221 INT
222 APIENTRY
223 NtGdiExcludeClipRect(
224     _In_ HDC hdc,
225     _In_ INT xLeft,
226     _In_ INT yTop,
227     _In_ INT xRight,
228     _In_ INT yBottom)
229 {
230     INT iComplexity;
231     RECTL rect;
232     PDC pdc;
233 
234     /* Lock the DC */
235     pdc = DC_LockDc(hdc);
236     if (pdc == NULL)
237     {
238         EngSetLastError(ERROR_INVALID_HANDLE);
239         return ERROR;
240     }
241 
242     /* Convert coordinates to device space */
243     rect.left = xLeft;
244     rect.top = yTop;
245     rect.right = xRight;
246     rect.bottom = yBottom;
247     RECTL_vMakeWellOrdered(&rect);
248     IntLPtoDP(pdc, (LPPOINT)&rect, 2);
249 
250     /* Check if we already have a clip region */
251     if (pdc->dclevel.prgnClip != NULL)
252     {
253         /* We have a region, subtract the rect */
254         iComplexity = REGION_SubtractRectFromRgn(pdc->dclevel.prgnClip,
255                                                  pdc->dclevel.prgnClip,
256                                                  &rect);
257     }
258     else
259     {
260         /* We don't have a clip region yet, create an empty region */
261         pdc->dclevel.prgnClip = IntSysCreateRectpRgn(0, 0, 0, 0);
262         if (pdc->dclevel.prgnClip == NULL)
263         {
264             iComplexity = ERROR;
265         }
266         else
267         {
268             /* Subtract the rect from the VIS region */
269             iComplexity = REGION_SubtractRectFromRgn(pdc->dclevel.prgnClip,
270                                                      pdc->prgnVis,
271                                                      &rect);
272         }
273     }
274 
275     /* Emulate Windows behavior */
276     if (iComplexity == SIMPLEREGION)
277         iComplexity = COMPLEXREGION;
278 
279     /* If we succeeded, mark the RAO region as dirty */
280     if (iComplexity != ERROR)
281         pdc->fs |= DC_FLAG_DIRTY_RAO;
282 
283     /* Unlock the DC */
284     DC_UnlockDc(pdc);
285 
286     return iComplexity;
287 }
288 
289 INT
290 APIENTRY
291 NtGdiIntersectClipRect(
292     _In_ HDC hdc,
293     _In_ INT xLeft,
294     _In_ INT yTop,
295     _In_ INT xRight,
296     _In_ INT yBottom)
297 {
298     INT iComplexity;
299     RECTL rect;
300     PREGION prgnNew;
301     PDC pdc;
302 
303     DPRINT("NtGdiIntersectClipRect(%p, %d,%d-%d,%d)\n",
304             hdc, xLeft, yTop, xRight, yBottom);
305 
306     /* Lock the DC */
307     pdc = DC_LockDc(hdc);
308     if (!pdc)
309     {
310         EngSetLastError(ERROR_INVALID_HANDLE);
311         return ERROR;
312     }
313 
314     /* Convert coordinates to device space */
315     rect.left = xLeft;
316     rect.top = yTop;
317     rect.right = xRight;
318     rect.bottom = yBottom;
319     IntLPtoDP(pdc, (LPPOINT)&rect, 2);
320 
321     /* Check if we already have a clip region */
322     if (pdc->dclevel.prgnClip != NULL)
323     {
324         /* We have a region, crop it */
325         iComplexity = REGION_CropRegion(pdc->dclevel.prgnClip,
326                                         pdc->dclevel.prgnClip,
327                                         &rect);
328     }
329     else
330     {
331         /* We don't have a region yet, allocate a new one */
332         prgnNew = IntSysCreateRectpRgnIndirect(&rect);
333         if (prgnNew == NULL)
334         {
335             iComplexity = ERROR;
336         }
337         else
338         {
339             /* Set the new region */
340             pdc->dclevel.prgnClip = prgnNew;
341             iComplexity = SIMPLEREGION;
342         }
343     }
344 
345     /* If we succeeded, mark the RAO region as dirty */
346     if (iComplexity != ERROR)
347         pdc->fs |= DC_FLAG_DIRTY_RAO;
348 
349     /* Unlock the DC */
350     DC_UnlockDc(pdc);
351 
352     return iComplexity;
353 }
354 
355 INT
356 APIENTRY
357 NtGdiOffsetClipRgn(
358     _In_ HDC hdc,
359     _In_ INT xOffset,
360     _In_ INT yOffset)
361 {
362     INT iComplexity;
363     PDC pdc;
364     POINTL apt[2];
365 
366     /* Lock the DC */
367     pdc = DC_LockDc(hdc);
368     if (pdc == NULL)
369     {
370         return ERROR;
371     }
372 
373     /* Check if we have a clip region */
374     if (pdc->dclevel.prgnClip != NULL)
375     {
376         /* Convert coordinates into device space. Note that we need to convert
377            2 coordinates to account for rotation / shear / offset */
378         apt[0].x = 0;
379         apt[0].y = 0;
380         apt[1].x = xOffset;
381         apt[1].y = yOffset;
382         IntLPtoDP(pdc, &apt, 2);
383 
384         /* Offset the clip region */
385         if (!REGION_bOffsetRgn(pdc->dclevel.prgnClip,
386                                apt[1].x - apt[0].x,
387                                apt[1].y - apt[0].y))
388         {
389             iComplexity = ERROR;
390         }
391         else
392         {
393             iComplexity = REGION_Complexity(pdc->dclevel.prgnClip);
394         }
395 
396         /* Mark the RAO region as dirty */
397         pdc->fs |= DC_FLAG_DIRTY_RAO;
398     }
399     else
400     {
401         /* NULL means no clipping, i.e. the "whole" region */
402         iComplexity = SIMPLEREGION;
403     }
404 
405     /* Unlock the DC and return the complexity */
406     DC_UnlockDc(pdc);
407     return iComplexity;
408 }
409 
410 BOOL APIENTRY NtGdiPtVisible(HDC  hDC,
411                     int  X,
412                     int  Y)
413 {
414     BOOL ret = FALSE;
415     PDC dc;
416 
417     if(!(dc = DC_LockDc(hDC)))
418     {
419         EngSetLastError(ERROR_INVALID_HANDLE);
420         return FALSE;
421     }
422 
423     if (dc->prgnRao)
424     {
425         POINT pt = {X, Y};
426         IntLPtoDP(dc, &pt, 1);
427         ret = REGION_PtInRegion(dc->prgnRao, pt.x, pt.y);
428     }
429 
430     DC_UnlockDc(dc);
431 
432     return ret;
433 }
434 
435 BOOL
436 APIENTRY
437 NtGdiRectVisible(
438     HDC hDC,
439     LPRECT UnsafeRect)
440 {
441     NTSTATUS Status = STATUS_SUCCESS;
442     PDC dc = DC_LockDc(hDC);
443     BOOL Result = FALSE;
444     RECTL Rect;
445 
446     if (!dc)
447     {
448         EngSetLastError(ERROR_INVALID_HANDLE);
449         return FALSE;
450     }
451 
452     _SEH2_TRY
453     {
454         ProbeForRead(UnsafeRect,
455                    sizeof(RECT),
456                    1);
457         Rect = *UnsafeRect;
458     }
459     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
460     {
461         Status = _SEH2_GetExceptionCode();
462     }
463     _SEH2_END;
464 
465     if(!NT_SUCCESS(Status))
466     {
467         DC_UnlockDc(dc);
468         SetLastNtError(Status);
469         return FALSE;
470     }
471 
472     if (dc->fs & DC_FLAG_DIRTY_RAO)
473         CLIPPING_UpdateGCRegion(dc);
474 
475     if (dc->prgnRao)
476     {
477          IntLPtoDP(dc, (LPPOINT)&Rect, 2);
478          Result = REGION_RectInRegion(dc->prgnRao, &Rect);
479     }
480     DC_UnlockDc(dc);
481 
482     return Result;
483 }
484 
485 int
486 FASTCALL
487 IntGdiSetMetaRgn(PDC pDC)
488 {
489     INT Ret = ERROR;
490 
491     if ( pDC->dclevel.prgnMeta )
492     {
493         if ( pDC->dclevel.prgnClip )
494         {
495             // preferably REGION_IntersectRegion
496             Ret = IntGdiCombineRgn(pDC->dclevel.prgnMeta, pDC->dclevel.prgnMeta, pDC->dclevel.prgnClip, RGN_AND);
497             if (Ret != ERROR)
498             {
499                 REGION_Delete(pDC->dclevel.prgnClip);
500                 pDC->dclevel.prgnClip = NULL;
501                 IntGdiReleaseRaoRgn(pDC);
502             }
503         }
504         else
505             Ret = REGION_Complexity(pDC->dclevel.prgnMeta);
506     }
507     else
508     {
509         if ( pDC->dclevel.prgnClip )
510         {
511             Ret = REGION_Complexity(pDC->dclevel.prgnClip);
512             pDC->dclevel.prgnMeta = pDC->dclevel.prgnClip;
513             pDC->dclevel.prgnClip = NULL;
514         }
515         else
516             Ret = SIMPLEREGION;
517     }
518 
519     if (Ret != ERROR)
520         pDC->fs |= DC_FLAG_DIRTY_RAO;
521 
522     return Ret;
523 }
524 
525 
526 int APIENTRY NtGdiSetMetaRgn(HDC  hDC)
527 {
528   INT Ret;
529   PDC pDC = DC_LockDc(hDC);
530 
531   if (!pDC)
532   {
533      EngSetLastError(ERROR_INVALID_PARAMETER);
534      return ERROR;
535   }
536   Ret = IntGdiSetMetaRgn(pDC);
537 
538   DC_UnlockDc(pDC);
539   return Ret;
540 }
541 
542 VOID
543 FASTCALL
544 CLIPPING_UpdateGCRegion(PDC pDC)
545 {
546     /* Must have VisRgn set to a valid state! */
547     ASSERT (pDC->prgnVis);
548 
549     if (pDC->prgnAPI)
550     {
551         REGION_Delete(pDC->prgnAPI);
552         pDC->prgnAPI = NULL;
553     }
554 
555     if (pDC->prgnRao)
556         REGION_Delete(pDC->prgnRao);
557 
558     pDC->prgnRao = IntSysCreateRectpRgn(0,0,0,0);
559 
560     ASSERT(pDC->prgnRao);
561 
562     if (pDC->dclevel.prgnMeta || pDC->dclevel.prgnClip)
563     {
564         pDC->prgnAPI = IntSysCreateRectpRgn(0,0,0,0);
565         if (!pDC->dclevel.prgnMeta)
566         {
567             IntGdiCombineRgn(pDC->prgnAPI,
568                              pDC->dclevel.prgnClip,
569                              NULL,
570                              RGN_COPY);
571         }
572         else if (!pDC->dclevel.prgnClip)
573         {
574             IntGdiCombineRgn(pDC->prgnAPI,
575                              pDC->dclevel.prgnMeta,
576                              NULL,
577                              RGN_COPY);
578         }
579         else
580         {
581             IntGdiCombineRgn(pDC->prgnAPI,
582                              pDC->dclevel.prgnClip,
583                              pDC->dclevel.prgnMeta,
584                              RGN_AND);
585         }
586     }
587 
588     if (pDC->prgnAPI)
589     {
590         IntGdiCombineRgn(pDC->prgnRao,
591                          pDC->prgnVis,
592                          pDC->prgnAPI,
593                          RGN_AND);
594     }
595     else
596     {
597         IntGdiCombineRgn(pDC->prgnRao,
598                          pDC->prgnVis,
599                          NULL,
600                          RGN_COPY);
601     }
602 
603 
604     REGION_bOffsetRgn(pDC->prgnRao, pDC->ptlDCOrig.x, pDC->ptlDCOrig.y);
605 
606     RtlCopyMemory(&pDC->erclClip,
607                 &pDC->prgnRao->rdh.rcBound,
608                 sizeof(RECTL));
609 
610     pDC->fs &= ~DC_FLAG_DIRTY_RAO;
611 
612     // pDC->co should be used. Example, CLIPOBJ_cEnumStart uses XCLIPOBJ to build
613     // the rects from region objects rects in pClipRgn->Buffer.
614     // With pDC->co.pClipRgn->Buffer,
615     // pDC->co.pClipRgn = pDC->prgnRao ? pDC->prgnRao : pDC->prgnVis;
616 
617     IntEngUpdateClipRegion(&pDC->co,
618                            pDC->prgnRao->rdh.nCount,
619                            pDC->prgnRao->Buffer,
620                            &pDC->erclClip);
621 
622     REGION_bOffsetRgn(pDC->prgnRao, -pDC->ptlDCOrig.x, -pDC->ptlDCOrig.y);
623 }
624 
625 /* EOF */
626