xref: /reactos/win32ss/gdi/gdi32/objects/region.c (revision bd712186)
1 #include <precomp.h>
2 
3 #define NDEBUG
4 #include <debug.h>
5 
6 //
7 // "Windows Graphics Programming: Win32 GDI and DirectDraw",
8 //   Chp 9 Areas, Region, Set Operations on Regions, hard copy pg 560.
9 //   universal set's bounding box to be [-(1 << 27), -(1 << 27), (1 << 27) -1, (1 << 27) -1].
10 //
11 #define MIN_COORD (INT_MIN/16) // See also ntgdi/region.c
12 #define MAX_COORD (INT_MAX/16)
13 
14 #define INRECT(r, x, y) \
15       ( ( ((r).right >  x)) && \
16       ( ((r).left <= x)) && \
17       ( ((r).bottom >  y)) && \
18       ( ((r).top <= y)) )
19 
20 static
21 VOID
22 FASTCALL
23 SortRects(PRECT pRect, INT nCount)
24 {
25     INT i, a, b, c, s;
26     RECT sRect;
27 
28     if (nCount > 0)
29     {
30         i = 1; // set index point
31         c = nCount; // set inverse count
32         do
33         {
34             s = i; // set sort count
35             if ( i < nCount )
36             {
37                 a = i - 1; // [0]
38                 b = i;     // [1]
39                 do
40                 {
41                     if ( pRect[a].top != pRect[b].top ) break;
42                     if ( pRect[a].left > pRect[b].left )
43                     {
44                         sRect = pRect[a];
45                         pRect[a] = pRect[b];
46                         pRect[b] = sRect;
47                     }
48                     ++s;
49                     b++;
50                 }
51                 while ( s < nCount );
52             }
53             ++i;
54         }
55         while ( c-- != 1 );
56     }
57 }
58 
59 /*
60  * I thought it was okay to have this in DeleteObject but~ Speed. (jt)
61  */
62 BOOL
63 FASTCALL
64 DeleteRegion(
65     _In_ HRGN hrgn)
66 {
67     PRGN_ATTR Rgn_Attr = GdiGetRgnAttr(hrgn);
68 
69     if ( Rgn_Attr )
70     {
71         PGDIBSOBJECT pgO;
72 
73         pgO = GdiAllocBatchCommand(NULL, GdiBCDelRgn);
74         if (pgO)
75         {
76             pgO->hgdiobj = hrgn;
77             return TRUE;
78         }
79     }
80     return NtGdiDeleteObjectApp(hrgn);
81 }
82 
83 INT
84 FASTCALL
85 MirrorRgnByWidth(
86     _In_ HRGN hrgn,
87     _In_ INT Width,
88     _In_ HRGN *phrgn)
89 {
90     INT cRgnDSize, Ret = 0;
91     PRGNDATA pRgnData;
92 
93     cRgnDSize = NtGdiGetRegionData(hrgn, 0, NULL);
94 
95     if (cRgnDSize)
96     {
97         pRgnData = HeapAlloc(GetProcessHeap(), 0, cRgnDSize * sizeof(LONG));
98         if (pRgnData)
99         {
100             if ( GetRegionData(hrgn, cRgnDSize, pRgnData) )
101             {
102                 HRGN hRgnex;
103                 UINT i;
104                 INT SaveL = pRgnData->rdh.rcBound.left;
105                 pRgnData->rdh.rcBound.left = Width - pRgnData->rdh.rcBound.right;
106                 pRgnData->rdh.rcBound.right = Width - SaveL;
107                 if (pRgnData->rdh.nCount > 0)
108                 {
109                     PRECT pRect = (PRECT)&pRgnData->Buffer;
110                     for (i = 0; i < pRgnData->rdh.nCount; i++)
111                     {
112                         SaveL = pRect[i].left;
113                         pRect[i].left = Width - pRect[i].right;
114                         pRect[i].right = Width - SaveL;
115                     }
116                 }
117                 SortRects((PRECT)&pRgnData->Buffer, pRgnData->rdh.nCount);
118                 hRgnex = ExtCreateRegion(NULL, cRgnDSize , pRgnData);
119                 if (hRgnex)
120                 {
121                     if (phrgn) phrgn = (HRGN *)hRgnex;
122                     else
123                     {
124                         CombineRgn(hrgn, hRgnex, 0, RGN_COPY);
125                         DeleteObject(hRgnex);
126                     }
127                     Ret = 1;
128                 }
129             }
130             HeapFree( GetProcessHeap(), 0, pRgnData);
131         }
132     }
133     return Ret;
134 }
135 
136 INT
137 WINAPI
138 MirrorRgnDC(
139     _In_ HDC hdc,
140     _In_ HRGN hrgn,
141     _In_ HRGN *phrn)
142 {
143     if (!GdiValidateHandle((HGDIOBJ) hdc) ||
144         (GDI_HANDLE_GET_TYPE(hdc) != GDI_OBJECT_TYPE_DC))
145         return 0;
146 
147     return MirrorRgnByWidth(hrgn, NtGdiGetDeviceWidth(hdc), phrn);
148 }
149 
150 /* FUNCTIONS *****************************************************************/
151 
152 FORCEINLINE
153 ULONG
154 IntSetNullRgn(
155     _Inout_ PRGN_ATTR prgnattr)
156 {
157     prgnattr->iComplexity = NULLREGION;
158     prgnattr->AttrFlags |= ATTR_RGN_DIRTY;
159     prgnattr->Rect.left = prgnattr->Rect.top = prgnattr->Rect.right = prgnattr->Rect.bottom = 0;
160     return NULLREGION;
161 }
162 
163 FORCEINLINE
164 ULONG
165 IntSetRectRgn(
166     _Inout_ PRGN_ATTR prgnattr,
167     _In_ INT xLeft,
168     _In_ INT yTop,
169     _In_ INT xRight,
170     _In_ INT yBottom)
171 {
172     ASSERT(xLeft <= xRight);
173     ASSERT(yTop <= yBottom);
174 
175     if ((xLeft == xRight) || (yTop == yBottom))
176         return IntSetNullRgn(prgnattr);
177 
178     prgnattr->iComplexity = SIMPLEREGION;
179     prgnattr->Rect.left = xLeft;
180     prgnattr->Rect.top = yTop;
181     prgnattr->Rect.right = xRight;
182     prgnattr->Rect.bottom = yBottom;
183     prgnattr->AttrFlags |= ATTR_RGN_DIRTY;
184     return SIMPLEREGION;
185 }
186 
187 /*
188  * @implemented
189  */
190 INT
191 WINAPI
192 CombineRgn(
193     _In_ HRGN hrgnDest,
194     _In_ HRGN hrgnSrc1,
195     _In_ HRGN hrgnSrc2,
196     _In_ INT  iCombineMode)
197 {
198     PRGN_ATTR prngattrDest = NULL;
199     PRGN_ATTR prngattrSrc1 = NULL;
200     PRGN_ATTR prngattrSrc2 = NULL;
201     RECT rcTemp;
202 
203     /* Get the region attribute for dest and source 1 */
204     prngattrDest = GdiGetRgnAttr(hrgnDest);
205     prngattrSrc1 = GdiGetRgnAttr(hrgnSrc1);
206 
207     /* If that failed or if the source 1 region is complex, go to win32k */
208     if ((prngattrDest == NULL) || (prngattrSrc1 == NULL) ||
209         (prngattrSrc1->iComplexity > SIMPLEREGION))
210     {
211         return NtGdiCombineRgn(hrgnDest, hrgnSrc1, hrgnSrc2, iCombineMode);
212     }
213 
214     /* Handle RGN_COPY first, it needs only hrgnSrc1 */
215     if (iCombineMode == RGN_COPY)
216     {
217         /* Check if the source region is a NULLREGION */
218         if (prngattrSrc1->iComplexity == NULLREGION)
219         {
220             /* The dest region is a NULLREGION, too */
221             return IntSetNullRgn(prngattrDest);
222         }
223 
224         /* We already know that the source region cannot be complex, so
225            create a rect region from the bounds of the source rect */
226         return IntSetRectRgn(prngattrDest,
227                              prngattrSrc1->Rect.left,
228                              prngattrSrc1->Rect.top,
229                              prngattrSrc1->Rect.right,
230                              prngattrSrc1->Rect.bottom);
231     }
232 
233     /* For all other operations we need hrgnSrc2 */
234     prngattrSrc2 = GdiGetRgnAttr(hrgnSrc2);
235 
236     /* If we got no attribute or the region is complex, go to win32k */
237     if ((prngattrSrc2 == NULL) || (prngattrSrc2->iComplexity > SIMPLEREGION))
238     {
239         return NtGdiCombineRgn(hrgnDest, hrgnSrc1, hrgnSrc2, iCombineMode);
240     }
241 
242     /* Handle RGN_AND */
243     if (iCombineMode == RGN_AND)
244     {
245         /* Check if either of the regions is a NULLREGION */
246         if ((prngattrSrc1->iComplexity == NULLREGION) ||
247             (prngattrSrc2->iComplexity == NULLREGION))
248         {
249             /* Result is also a NULLREGION */
250             return IntSetNullRgn(prngattrDest);
251         }
252 
253         /* Get the intersection of the 2 rects */
254         if (!IntersectRect(&rcTemp, &prngattrSrc1->Rect, &prngattrSrc2->Rect))
255         {
256             /* The rects do not intersect, result is a NULLREGION */
257             return IntSetNullRgn(prngattrDest);
258         }
259 
260         /* Use the intersection of the rects */
261         return IntSetRectRgn(prngattrDest,
262                              rcTemp.left,
263                              rcTemp.top,
264                              rcTemp.right,
265                              rcTemp.bottom);
266     }
267 
268     /* Handle RGN_DIFF */
269     if (iCombineMode == RGN_DIFF)
270     {
271         /* Check if source 1 is a NULLREGION */
272         if (prngattrSrc1->iComplexity == NULLREGION)
273         {
274             /* The result is a NULLREGION as well */
275             return IntSetNullRgn(prngattrDest);
276         }
277 
278         /* Get the intersection of the 2 rects */
279         if ((prngattrSrc2->iComplexity == NULLREGION) ||
280             !IntersectRect(&rcTemp, &prngattrSrc1->Rect, &prngattrSrc2->Rect))
281         {
282             /* The rects do not intersect, dest equals source 1 */
283             return IntSetRectRgn(prngattrDest,
284                                  prngattrSrc1->Rect.left,
285                                  prngattrSrc1->Rect.top,
286                                  prngattrSrc1->Rect.right,
287                                  prngattrSrc1->Rect.bottom);
288         }
289 
290         /* We need to check is whether we can subtract the rects. For that
291            we call SubtractRect, which will give us the bounding box of the
292            subtraction. The function returns FALSE if the resulting rect is
293            empty */
294         if (!SubtractRect(&rcTemp, &prngattrSrc1->Rect, &rcTemp))
295         {
296             /* The result is a NULLREGION */
297             return IntSetNullRgn(prngattrDest);
298         }
299 
300         /* Now check if the result of SubtractRect matches the source 1 rect.
301            Since we already know that the rects intersect, the result can
302            only match the source 1 rect, if it could not be "cut" on either
303            side, but the overlapping was on a corner, so the new bounding box
304            equals the previous rect */
305         if (!EqualRect(&rcTemp, &prngattrSrc1->Rect))
306         {
307             /* We got a properly subtracted rect, so use it. */
308             return IntSetRectRgn(prngattrDest,
309                                  rcTemp.left,
310                                  rcTemp.top,
311                                  rcTemp.right,
312                                  rcTemp.bottom);
313         }
314 
315         /* The result would be a complex region, go to win32k */
316         return NtGdiCombineRgn(hrgnDest, hrgnSrc1, hrgnSrc2, iCombineMode);
317     }
318 
319     /* Handle OR and XOR */
320     if ((iCombineMode == RGN_OR) || (iCombineMode == RGN_XOR))
321     {
322         /* Check if source 1 is a NULLREGION */
323         if (prngattrSrc1->iComplexity == NULLREGION)
324         {
325             /* Check if source 2 is also a NULLREGION */
326             if (prngattrSrc2->iComplexity == NULLREGION)
327             {
328                 /* Both are NULLREGIONs, result is also a NULLREGION */
329                 return IntSetNullRgn(prngattrDest);
330             }
331 
332             /* The result is equal to source 2 */
333             return IntSetRectRgn(prngattrDest,
334                                  prngattrSrc2->Rect.left,
335                                  prngattrSrc2->Rect.top,
336                                  prngattrSrc2->Rect.right,
337                                  prngattrSrc2->Rect.bottom );
338         }
339 
340         /* Check if only source 2 is a NULLREGION */
341         if (prngattrSrc2->iComplexity == NULLREGION)
342         {
343             /* The result is equal to source 1 */
344             return IntSetRectRgn(prngattrDest,
345                                  prngattrSrc1->Rect.left,
346                                  prngattrSrc1->Rect.top,
347                                  prngattrSrc1->Rect.right,
348                                  prngattrSrc1->Rect.bottom);
349         }
350 
351         /* Do the rects have the same x extent */
352         if ((prngattrSrc1->Rect.left == prngattrSrc2->Rect.left) &&
353             (prngattrSrc1->Rect.right == prngattrSrc2->Rect.right))
354         {
355             /* Do the rects also have the same y extent */
356             if ((prngattrSrc1->Rect.top == prngattrSrc2->Rect.top) &&
357                 (prngattrSrc1->Rect.bottom == prngattrSrc2->Rect.bottom))
358             {
359                 /* Rects are equal, if this is RGN_OR, the result is source 1 */
360                 if (iCombineMode == RGN_OR)
361                 {
362                     /* The result is equal to source 1 */
363                     return IntSetRectRgn(prngattrDest,
364                                          prngattrSrc1->Rect.left,
365                                          prngattrSrc1->Rect.top,
366                                          prngattrSrc1->Rect.right,
367                                          prngattrSrc1->Rect.bottom );
368                 }
369                 else
370                 {
371                     /* XORing with itself yields an empty region */
372                     return IntSetNullRgn(prngattrDest);
373                 }
374             }
375 
376             /* Check if the rects are disjoint */
377             if ((prngattrSrc2->Rect.bottom < prngattrSrc1->Rect.top) ||
378                 (prngattrSrc2->Rect.top > prngattrSrc1->Rect.bottom))
379             {
380                 /* The result would be a complex region, go to win32k */
381                 return NtGdiCombineRgn(hrgnDest, hrgnSrc1, hrgnSrc2, iCombineMode);
382             }
383 
384             /* Check if this is OR */
385             if (iCombineMode == RGN_OR)
386             {
387                 /* Use the maximum extent of both rects combined */
388                 return IntSetRectRgn(prngattrDest,
389                                      prngattrSrc1->Rect.left,
390                                      min(prngattrSrc1->Rect.top, prngattrSrc2->Rect.top),
391                                      prngattrSrc1->Rect.right,
392                                      max(prngattrSrc1->Rect.bottom, prngattrSrc2->Rect.bottom));
393             }
394 
395             /* Check if the rects are adjacent */
396             if (prngattrSrc2->Rect.bottom == prngattrSrc1->Rect.top)
397             {
398                 /* The result is the combined rects */
399                 return IntSetRectRgn(prngattrDest,
400                                      prngattrSrc1->Rect.left,
401                                      prngattrSrc2->Rect.top,
402                                      prngattrSrc1->Rect.right,
403                                      prngattrSrc1->Rect.bottom );
404             }
405             else if (prngattrSrc2->Rect.top == prngattrSrc1->Rect.bottom)
406             {
407                 /* The result is the combined rects */
408                 return IntSetRectRgn(prngattrDest,
409                                      prngattrSrc1->Rect.left,
410                                      prngattrSrc1->Rect.top,
411                                      prngattrSrc1->Rect.right,
412                                      prngattrSrc2->Rect.bottom );
413             }
414 
415             /* When we are here, this is RGN_XOR and the rects overlap */
416             return NtGdiCombineRgn(hrgnDest, hrgnSrc1, hrgnSrc2, iCombineMode);
417         }
418 
419         /* Do the rects have the same y extent */
420         if ((prngattrSrc1->Rect.top == prngattrSrc2->Rect.top) &&
421             (prngattrSrc1->Rect.bottom == prngattrSrc2->Rect.bottom))
422         {
423             /* Check if the rects are disjoint */
424             if ((prngattrSrc2->Rect.right < prngattrSrc1->Rect.left) ||
425                 (prngattrSrc2->Rect.left > prngattrSrc1->Rect.right))
426             {
427                 /* The result would be a complex region, go to win32k */
428                 return NtGdiCombineRgn(hrgnDest, hrgnSrc1, hrgnSrc2, iCombineMode);
429             }
430 
431             /* Check if this is OR */
432             if (iCombineMode == RGN_OR)
433             {
434                 /* Use the maximum extent of both rects combined */
435                 return IntSetRectRgn(prngattrDest,
436                                      min(prngattrSrc1->Rect.left, prngattrSrc2->Rect.left),
437                                      prngattrSrc1->Rect.top,
438                                      max(prngattrSrc1->Rect.right, prngattrSrc2->Rect.right),
439                                      prngattrSrc1->Rect.bottom);
440             }
441 
442             /* Check if the rects are adjacent */
443             if (prngattrSrc2->Rect.right == prngattrSrc1->Rect.left)
444             {
445                 /* The result is the combined rects */
446                 return IntSetRectRgn(prngattrDest,
447                                      prngattrSrc2->Rect.left,
448                                      prngattrSrc1->Rect.top,
449                                      prngattrSrc1->Rect.right,
450                                      prngattrSrc1->Rect.bottom );
451             }
452             else if (prngattrSrc2->Rect.left == prngattrSrc1->Rect.right)
453             {
454                 /* The result is the combined rects */
455                 return IntSetRectRgn(prngattrDest,
456                                      prngattrSrc1->Rect.left,
457                                      prngattrSrc1->Rect.top,
458                                      prngattrSrc2->Rect.right,
459                                      prngattrSrc1->Rect.bottom );
460             }
461 
462             /* When we are here, this is RGN_XOR and the rects overlap */
463             return NtGdiCombineRgn(hrgnDest, hrgnSrc1, hrgnSrc2, iCombineMode);
464         }
465 
466         /* Last case: RGN_OR and one rect is completely within the other */
467         if (iCombineMode == RGN_OR)
468         {
469             /* Check if rect 1 can contain rect 2 */
470             if (prngattrSrc1->Rect.left <= prngattrSrc2->Rect.left)
471             {
472                 /* rect 1 might be the outer one, check of that is true */
473                 if ((prngattrSrc1->Rect.right >= prngattrSrc2->Rect.right) &&
474                     (prngattrSrc1->Rect.top <= prngattrSrc2->Rect.top) &&
475                     (prngattrSrc1->Rect.bottom >= prngattrSrc2->Rect.bottom))
476                 {
477                     /* Rect 1 contains rect 2, use it */
478                     return IntSetRectRgn(prngattrDest,
479                                          prngattrSrc1->Rect.left,
480                                          prngattrSrc1->Rect.top,
481                                          prngattrSrc1->Rect.right,
482                                          prngattrSrc1->Rect.bottom );
483                 }
484             }
485             else
486             {
487                 /* rect 2 might be the outer one, check of that is true */
488                 if ((prngattrSrc2->Rect.right >= prngattrSrc1->Rect.right) &&
489                     (prngattrSrc2->Rect.top <= prngattrSrc1->Rect.top) &&
490                     (prngattrSrc2->Rect.bottom >= prngattrSrc1->Rect.bottom))
491                 {
492                     /* Rect 2 contains rect 1, use it */
493                     return IntSetRectRgn(prngattrDest,
494                                          prngattrSrc2->Rect.left,
495                                          prngattrSrc2->Rect.top,
496                                          prngattrSrc2->Rect.right,
497                                          prngattrSrc2->Rect.bottom );
498                 }
499             }
500         }
501 
502         /* We couldn't handle the operation, go to win32k */
503         return NtGdiCombineRgn(hrgnDest, hrgnSrc1, hrgnSrc2, iCombineMode);
504     }
505 
506     DPRINT1("Invalid iCombineMode %d\n", iCombineMode);
507     SetLastError(ERROR_INVALID_PARAMETER);
508     return ERROR;
509 }
510 
511 
512 /*
513  * @implemented
514  */
515 HRGN
516 WINAPI
517 CreateEllipticRgnIndirect(
518     const RECT *prc
519 )
520 {
521     /* Notes if prc is NULL it will crash on All Windows NT I checked 2000/XP/VISTA */
522     return NtGdiCreateEllipticRgn(prc->left, prc->top, prc->right, prc->bottom);
523 
524 }
525 
526 /*
527  * @implemented
528  */
529 HRGN
530 WINAPI
531 CreatePolygonRgn( const POINT * lppt, int cPoints, int fnPolyFillMode)
532 {
533     return (HRGN) NtGdiPolyPolyDraw( (HDC)UlongToHandle(fnPolyFillMode), (PPOINT) lppt, (PULONG) &cPoints, 1, GdiPolyPolyRgn);
534 }
535 
536 /*
537  * @implemented
538  */
539 HRGN
540 WINAPI
541 CreatePolyPolygonRgn( const POINT* lppt,
542                       const INT* lpPolyCounts,
543                       int nCount,
544                       int fnPolyFillMode)
545 {
546     return (HRGN) NtGdiPolyPolyDraw(  (HDC)UlongToHandle(fnPolyFillMode), (PPOINT) lppt, (PULONG) lpPolyCounts, (ULONG) nCount, GdiPolyPolyRgn );
547 }
548 
549 /*
550  * @implemented
551  */
552 HRGN
553 WINAPI
554 CreateRectRgn(int x1, int y1, int x2, int y2)
555 {
556     PRGN_ATTR pRgn_Attr;
557     HRGN hrgn = NULL;
558     int tmp;
559 
560     /* Normalize points, REGION_SetRectRgn does this too. */
561     if ( x1 > x2 )
562     {
563         tmp = x1;
564         x1 = x2;
565         x2 = tmp;
566     }
567 
568     if ( y1 > y2 )
569     {
570         tmp = y1;
571         y1 = y2;
572         y2 = tmp;
573     }
574     /* Check outside 28 bit limit for universal set bound box. REGION_SetRectRgn doesn't do this! */
575     if ( x1 < MIN_COORD ||
576          y1 < MIN_COORD ||
577          x2 > MAX_COORD ||
578          y2 > MAX_COORD  )
579     {
580         SetLastError(ERROR_INVALID_PARAMETER);
581         return NULL;
582     }
583 
584     hrgn = hGetPEBHandle(hctRegionHandle, 0);
585     if (hrgn)
586     {
587        DPRINT1("PEB Handle Cache Test return hrgn %p, should be NULL!\n",hrgn);
588        hrgn = NULL;
589     }
590 
591     if (!hrgn)
592         hrgn = NtGdiCreateRectRgn(0, 0, 1, 1);
593 
594     if (!hrgn)
595         return hrgn;
596 
597     if (!(pRgn_Attr = GdiGetRgnAttr(hrgn)) )
598     {
599         DPRINT1("No Attr for Region handle!!!\n");
600         DeleteRegion(hrgn);
601         return NULL;
602     }
603 
604     pRgn_Attr->AttrFlags = ATTR_RGN_VALID;
605 
606     IntSetRectRgn( pRgn_Attr, x1, y1, x2, y2 );
607 
608     return hrgn;
609 }
610 
611 /*
612  * @implemented
613  */
614 HRGN
615 WINAPI
616 CreateRectRgnIndirect(
617     const RECT *prc
618 )
619 {
620     /* Notes if prc is NULL it will crash on All Windows NT I checked 2000/XP/VISTA */
621     return CreateRectRgn(prc->left, prc->top, prc->right, prc->bottom);
622 
623 }
624 
625 /*
626  * @implemented
627  */
628 INT
629 WINAPI
630 ExcludeClipRect(
631     _In_ HDC hdc,
632     _In_ INT xLeft,
633     _In_ INT yTop,
634     _In_ INT xRight,
635     _In_ INT yBottom)
636 {
637     HANDLE_METADC(INT, ExcludeClipRect, ERROR, hdc, xLeft, yTop, xRight, yBottom);
638 
639     return NtGdiExcludeClipRect(hdc, xLeft, yTop, xRight, yBottom);
640 }
641 
642 /*
643  * @implemented
644  */
645 HRGN
646 WINAPI
647 ExtCreateRegion(
648     CONST XFORM *	lpXform,
649     DWORD		nCount,
650     CONST RGNDATA *	lpRgnData
651 )
652 {
653     if (lpRgnData)
654     {
655         if ((!lpXform) && (lpRgnData->rdh.nCount == 1))
656         {
657             PRECT pRect = (PRECT)&lpRgnData->Buffer[0];
658             return CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom);
659         }
660         return NtGdiExtCreateRegion((LPXFORM) lpXform, nCount,(LPRGNDATA) lpRgnData);
661     }
662     SetLastError(ERROR_INVALID_PARAMETER);
663     return NULL;
664 }
665 
666 /*
667  * @implemented
668  */
669 INT
670 WINAPI
671 ExtSelectClipRgn(
672     _In_ HDC hdc,
673     _In_ HRGN hrgn,
674     _In_ INT iMode)
675 {
676     INT Ret;
677     HRGN NewRgn = NULL;
678 
679     HANDLE_METADC(INT, ExtSelectClipRgn, 0, hdc, hrgn, iMode);
680 
681 #if 0
682     if ( hrgn )
683     {
684         if ( GetLayout(hdc) & LAYOUT_RTL )
685         {
686             if ( MirrorRgnDC(hdc, hrgn, &NewRgn) )
687             {
688                 if ( NewRgn ) hrgn = NewRgn;
689             }
690         }
691     }
692 #endif
693     /* Batch handles RGN_COPY only! */
694     if (iMode == RGN_COPY)
695     {
696         PDC_ATTR pdcattr;
697         PRGN_ATTR pRgn_Attr = NULL;
698 
699         /* Get the DC attribute */
700         pdcattr = GdiGetDcAttr(hdc);
701         if ( pdcattr )
702         {
703             PGDI_TABLE_ENTRY pEntry = GdiHandleTable + GDI_HANDLE_GET_INDEX(hdc);
704 
705             /* hrgn can be NULL unless the RGN_COPY mode is specified. */
706             if (hrgn) pRgn_Attr = GdiGetRgnAttr(hrgn);
707 
708             if ( !(pdcattr->ulDirty_ & DC_DIBSECTION) &&
709                  !(pEntry->Flags & GDI_ENTRY_VALIDATE_VIS) )
710             {
711                 if (!hrgn || (hrgn && pRgn_Attr && pRgn_Attr->iComplexity <= SIMPLEREGION) )
712                 {
713                     PGDIBSEXTSELCLPRGN pgO = GdiAllocBatchCommand(hdc, GdiBCExtSelClipRgn);
714                     if (pgO)
715                     {
716                         pgO->fnMode = iMode;
717 
718                         if ( hrgn && pRgn_Attr )
719                         {
720                             Ret = pRgn_Attr->iComplexity;
721                             // Note from ntgdi/dcstate.c : "The VisRectRegion field needs to be set to a valid state."
722                             if ( pdcattr->VisRectRegion.Rect.left   >= pRgn_Attr->Rect.right  ||
723                                  pdcattr->VisRectRegion.Rect.top    >= pRgn_Attr->Rect.bottom ||
724                                  pdcattr->VisRectRegion.Rect.right  <= pRgn_Attr->Rect.left   ||
725                                  pdcattr->VisRectRegion.Rect.bottom <= pRgn_Attr->Rect.top )
726                                 Ret = NULLREGION;
727 
728                             // Pass the rect since this region will go away.
729                             pgO->rcl = pRgn_Attr->Rect;
730                         }
731                         else
732                         {
733                             Ret = pdcattr->VisRectRegion.iComplexity;
734                             pgO->fnMode |= GDIBS_NORECT; // Set no hrgn mode.
735                         }
736                         if ( NewRgn ) DeleteObject(NewRgn);
737                         return Ret;
738                     }
739                 }
740             }
741         }
742     }
743     Ret = NtGdiExtSelectClipRgn(hdc, hrgn, iMode);
744 
745     if ( NewRgn ) DeleteObject(NewRgn);
746 
747     return Ret;
748 }
749 
750 /*
751  * @implemented
752  */
753 int
754 WINAPI
755 GetClipRgn(
756     HDC     hdc,
757     HRGN    hrgn
758 )
759 {
760     INT Ret;
761 
762     /* Check if DC handle is valid */
763     if (!GdiGetDcAttr(hdc))
764     {
765         /* Last error code differs from what NtGdiGetRandomRgn returns */
766         SetLastError(ERROR_INVALID_PARAMETER);
767         return -1;
768     }
769 
770     Ret = NtGdiGetRandomRgn(hdc, hrgn, CLIPRGN);
771 
772 //  if (Ret)
773 //  {
774 //     if(GetLayout(hdc) & LAYOUT_RTL) MirrorRgnDC(hdc,(HRGN)Ret, NULL);
775 //  }
776     return Ret;
777 }
778 
779 /*
780  * @implemented
781  */
782 int
783 WINAPI
784 GetMetaRgn(HDC hdc,
785            HRGN hrgn)
786 {
787     return NtGdiGetRandomRgn(hdc, hrgn, METARGN);
788 }
789 
790 /*
791  * @implemented
792  *
793  */
794 DWORD
795 WINAPI
796 GetRegionData(HRGN hrgn,
797               DWORD nCount,
798               LPRGNDATA lpRgnData)
799 {
800     if (!lpRgnData)
801     {
802         nCount = 0;
803     }
804 
805     return NtGdiGetRegionData(hrgn,nCount,lpRgnData);
806 }
807 
808 /*
809  * @implemented
810  *
811  */
812 INT
813 WINAPI
814 GetRgnBox(HRGN hrgn,
815           LPRECT prcOut)
816 {
817     PRGN_ATTR Rgn_Attr;
818 
819     if (!(Rgn_Attr = GdiGetRgnAttr(hrgn)))
820         return NtGdiGetRgnBox(hrgn, prcOut);
821 
822     if (Rgn_Attr->iComplexity == NULLREGION)
823     {
824         prcOut->left   = 0;
825         prcOut->top    = 0;
826         prcOut->right  = 0;
827         prcOut->bottom = 0;
828     }
829     else
830     {
831         if (Rgn_Attr->iComplexity != SIMPLEREGION)
832             return NtGdiGetRgnBox(hrgn, prcOut);
833         /* WARNING! prcOut is never checked newbies! */
834         RtlCopyMemory( prcOut, &Rgn_Attr->Rect, sizeof(RECT));
835     }
836     return Rgn_Attr->iComplexity;
837 }
838 
839 /*
840  * @implemented
841  */
842 INT
843 WINAPI
844 IntersectClipRect(
845     _In_ HDC hdc,
846     _In_ INT nLeft,
847     _In_ INT nTop,
848     _In_ INT nRight,
849     _In_ INT nBottom)
850 {
851     HANDLE_METADC(INT, IntersectClipRect, ERROR, hdc, nLeft, nTop, nRight, nBottom);
852     return NtGdiIntersectClipRect(hdc, nLeft, nTop, nRight, nBottom);
853 }
854 
855 /*
856  * @implemented
857  */
858 BOOL
859 WINAPI
860 MirrorRgn(HWND hwnd, HRGN hrgn)
861 {
862     INT l;
863     RECT Rect;
864     GetWindowRect(hwnd, &Rect);
865     l = Rect.right - Rect.left;
866     Rect.right -= Rect.left;
867     return MirrorRgnByWidth(hrgn, l, NULL);
868 }
869 
870 /*
871  * @implemented
872  */
873 INT
874 WINAPI
875 OffsetClipRgn(
876     HDC hdc,
877     INT nXOffset,
878     INT nYOffset)
879 {
880     HANDLE_METADC(INT, OffsetClipRgn, ERROR, hdc, nXOffset, nYOffset);
881     return NtGdiOffsetClipRgn(hdc, nXOffset, nYOffset);
882 }
883 
884 /*
885  * @implemented
886  *
887  */
888 INT
889 WINAPI
890 OffsetRgn( HRGN hrgn,
891            int nXOffset,
892            int nYOffset)
893 {
894     PRGN_ATTR pRgn_Attr;
895     RECTL rc;
896 
897     if (!(pRgn_Attr = GdiGetRgnAttr(hrgn)))
898         return NtGdiOffsetRgn(hrgn,nXOffset,nYOffset);
899 
900     if ( pRgn_Attr->iComplexity == NULLREGION)
901         return pRgn_Attr->iComplexity;
902 
903     if ( pRgn_Attr->iComplexity != SIMPLEREGION)
904         return NtGdiOffsetRgn(hrgn,nXOffset,nYOffset);
905 
906     rc = pRgn_Attr->Rect;
907 
908     if (rc.left < rc.right)
909     {
910         if (rc.top < rc.bottom)
911         {
912             rc.left   += nXOffset;
913             rc.top    += nYOffset;
914             rc.right  += nXOffset;
915             rc.bottom += nYOffset;
916 
917             /* Make sure the offset is within the legal range */
918             if ( (rc.left   & MIN_COORD && ((rc.left   & MIN_COORD) != MIN_COORD)) ||
919                  (rc.top    & MIN_COORD && ((rc.top    & MIN_COORD) != MIN_COORD)) ||
920                  (rc.right  & MIN_COORD && ((rc.right  & MIN_COORD) != MIN_COORD)) ||
921                  (rc.bottom & MIN_COORD && ((rc.bottom & MIN_COORD) != MIN_COORD))  )
922             {
923                 DPRINT("OffsetRgn ERROR\n");
924                 return ERROR;
925             }
926 
927             pRgn_Attr->Rect = rc;
928             pRgn_Attr->AttrFlags |= ATTR_RGN_DIRTY;
929         }
930     }
931     return pRgn_Attr->iComplexity;
932 }
933 
934 /*
935  * @implemented
936  */
937 BOOL
938 WINAPI
939 PtInRegion(IN HRGN hrgn,
940            int x,
941            int y)
942 {
943     PRGN_ATTR pRgn_Attr;
944 
945     if (!(pRgn_Attr = GdiGetRgnAttr(hrgn)))
946         return NtGdiPtInRegion(hrgn,x,y);
947 
948     if ( pRgn_Attr->iComplexity == NULLREGION)
949         return FALSE;
950 
951     if ( pRgn_Attr->iComplexity != SIMPLEREGION)
952         return NtGdiPtInRegion(hrgn,x,y);
953 
954     return INRECT( pRgn_Attr->Rect, x, y);
955 }
956 
957 /*
958  * @implemented
959  */
960 BOOL
961 WINAPI
962 RectInRegion(HRGN hrgn,
963              LPCRECT prcl)
964 {
965     PRGN_ATTR pRgn_Attr;
966     RECTL rc;
967 
968     if (!(pRgn_Attr = GdiGetRgnAttr(hrgn)))
969         return NtGdiRectInRegion(hrgn, (LPRECT) prcl);
970 
971     if ( pRgn_Attr->iComplexity == NULLREGION)
972         return FALSE;
973 
974     if ( pRgn_Attr->iComplexity != SIMPLEREGION)
975         return NtGdiRectInRegion(hrgn, (LPRECT) prcl);
976 
977     /* swap the coordinates to make right >= left and bottom >= top */
978     /* (region building rectangles are normalized the same way) */
979     if ( prcl->top > prcl->bottom)
980     {
981         rc.top = prcl->bottom;
982         rc.bottom = prcl->top;
983     }
984     else
985     {
986         rc.top = prcl->top;
987         rc.bottom = prcl->bottom;
988     }
989     if ( prcl->right < prcl->left)
990     {
991         rc.right = prcl->left;
992         rc.left = prcl->right;
993     }
994     else
995     {
996         rc.right = prcl->right;
997         rc.left = prcl->left;
998     }
999 
1000     if ( ( pRgn_Attr->Rect.left   >= rc.right )  ||
1001          ( pRgn_Attr->Rect.right  <= rc.left )   ||
1002          ( pRgn_Attr->Rect.top    >= rc.bottom ) ||
1003          ( pRgn_Attr->Rect.bottom <= rc.top ) )
1004     {
1005         return FALSE;
1006     }
1007 
1008     return TRUE;
1009 }
1010 
1011 /*
1012  * @implemented
1013  */
1014 int
1015 WINAPI
1016 SelectClipRgn(
1017     _In_ HDC hdc,
1018     _In_ HRGN hrgn)
1019 {
1020     return ExtSelectClipRgn(hdc, hrgn, RGN_COPY);
1021 }
1022 
1023 /*
1024  * @implemented
1025  */
1026 BOOL
1027 WINAPI
1028 SetRectRgn(
1029     _In_ HRGN hrgn,
1030     _In_ INT xLeft,
1031     _In_ INT yTop,
1032     _In_ INT xRight,
1033     _In_ INT yBottom)
1034 {
1035     PRGN_ATTR prngattr;
1036 
1037     /* Try to get the region attribute */
1038     prngattr = GdiGetRgnAttr(hrgn);
1039     if (prngattr == NULL)
1040     {
1041         return NtGdiSetRectRgn(hrgn, xLeft, yTop, xRight, yBottom);
1042     }
1043 
1044     /* check for NULL region */
1045     if ((xLeft == xRight) || (yTop == yBottom))
1046     {
1047         IntSetNullRgn(prngattr);
1048         return TRUE;
1049     }
1050 
1051     if (xLeft > xRight)
1052     {
1053         prngattr->Rect.left   = xRight;
1054         prngattr->Rect.right  = xLeft;
1055     }
1056     else
1057     {
1058         prngattr->Rect.left   = xLeft;
1059         prngattr->Rect.right  = xRight;
1060     }
1061 
1062     if (yTop > yBottom)
1063     {
1064         prngattr->Rect.top    = yBottom;
1065         prngattr->Rect.bottom = yTop;
1066     }
1067     else
1068     {
1069         prngattr->Rect.top    = yTop;
1070         prngattr->Rect.bottom = yBottom;
1071     }
1072 
1073     prngattr->AttrFlags |= ATTR_RGN_DIRTY ;
1074     prngattr->iComplexity = SIMPLEREGION;
1075 
1076     return TRUE;
1077 }
1078 
1079 /*
1080  * @implemented
1081  */
1082 int
1083 WINAPI
1084 SetMetaRgn(HDC hDC)
1085 {
1086     if (GDI_HANDLE_GET_TYPE(hDC) == GDI_OBJECT_TYPE_DC)
1087         return NtGdiSetMetaRgn(hDC);
1088 #if 0
1089     PLDC pLDC = GdiGetLDC(hDC);
1090     if ( pLDC && GDI_HANDLE_GET_TYPE(hDC) != GDI_OBJECT_TYPE_METADC )
1091     {
1092         if (pLDC->iType == LDC_EMFLDC || EMFDRV_SetMetaRgn(hDC))
1093         {
1094             return NtGdiSetMetaRgn(hDC);
1095         }
1096         else
1097             SetLastError(ERROR_INVALID_HANDLE);
1098     }
1099 #endif
1100     return ERROR;
1101 }
1102 
1103