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