xref: /reactos/win32ss/gdi/ntgdi/fillshap.c (revision 4561998a)
1 /*
2  * PROJECT:         ReactOS win32 kernel mode subsystem
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            win32ss/gdi/ntgdi/fillshap.c
5  * PURPOSE:         fillshap
6  * PROGRAMMER:
7  */
8 
9 #include <win32k.h>
10 
11 #define NDEBUG
12 #include <debug.h>
13 
14 #define Rsin(d) ((d) == 0.0 ? 0.0 : ((d) == 90.0 ? 1.0 : sin(d*M_PI/180.0)))
15 #define Rcos(d) ((d) == 0.0 ? 1.0 : ((d) == 90.0 ? 0.0 : cos(d*M_PI/180.0)))
16 
17 BOOL FASTCALL
18 IntGdiPolygon(PDC    dc,
19               PPOINT Points,
20               int    Count)
21 {
22     SURFACE *psurf;
23     PBRUSH pbrLine, pbrFill;
24     BOOL ret = FALSE; // Default to failure
25     RECTL DestRect;
26     int CurrentPoint;
27     PDC_ATTR pdcattr;
28     POINTL BrushOrigin;
29 //    int Left;
30 //    int Top;
31 
32     ASSERT(dc); // Caller's responsibility to pass a valid dc
33 
34     if (!Points || Count < 2 )
35     {
36         EngSetLastError(ERROR_INVALID_PARAMETER);
37         return FALSE;
38     }
39 
40 /*
41     // Find start x, y
42     Left = Points[0].x;
43     Top  = Points[0].y;
44     for (CurrentPoint = 1; CurrentPoint < Count; ++CurrentPoint) {
45       Left = min(Left, Points[CurrentPoint].x);
46       Top  = min(Top, Points[CurrentPoint].y);
47     }
48 */
49 
50     pdcattr = dc->pdcattr;
51 
52     /* Convert to screen coordinates */
53     IntLPtoDP(dc, Points, Count);
54     for (CurrentPoint = 0; CurrentPoint < Count; CurrentPoint++)
55     {
56         Points[CurrentPoint].x += dc->ptlDCOrig.x;
57         Points[CurrentPoint].y += dc->ptlDCOrig.y;
58     }
59     // No need to have path here.
60     {
61         DestRect.left   = Points[0].x;
62         DestRect.right  = Points[0].x;
63         DestRect.top    = Points[0].y;
64         DestRect.bottom = Points[0].y;
65 
66         for (CurrentPoint = 1; CurrentPoint < Count; ++CurrentPoint)
67         {
68             DestRect.left     = min(DestRect.left, Points[CurrentPoint].x);
69             DestRect.right    = max(DestRect.right, Points[CurrentPoint].x);
70             DestRect.top      = min(DestRect.top, Points[CurrentPoint].y);
71             DestRect.bottom   = max(DestRect.bottom, Points[CurrentPoint].y);
72         }
73 
74         if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
75             DC_vUpdateFillBrush(dc);
76 
77         if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
78             DC_vUpdateLineBrush(dc);
79 
80         /* Special locking order to avoid lock-ups */
81         pbrFill = dc->dclevel.pbrFill;
82         pbrLine = dc->dclevel.pbrLine;
83         psurf = dc->dclevel.pSurface;
84         if (psurf == NULL)
85         {
86             /* Memory DC without a bitmap selected, nothing to do. */
87             return TRUE;
88         }
89 
90         /* Now fill the polygon with the current fill brush. */
91         if (!(pbrFill->flAttrs & BR_IS_NULL))
92         {
93             BrushOrigin = *((PPOINTL)&pbrFill->ptOrigin);
94             BrushOrigin.x += dc->ptlDCOrig.x;
95             BrushOrigin.y += dc->ptlDCOrig.y;
96             ret = IntFillPolygon (dc,
97                                   psurf,
98                                   &dc->eboFill.BrushObject,
99                                   Points,
100                                   Count,
101                                   DestRect,
102                                   &BrushOrigin);
103         }
104 
105         // Draw the Polygon Edges with the current pen ( if not a NULL pen )
106         if (!(pbrLine->flAttrs & BR_IS_NULL))
107         {
108             int i;
109 
110             for (i = 0; i < Count-1; i++)
111             {
112 
113 // DPRINT1("Polygon Making line from (%d,%d) to (%d,%d)\n",
114 //                                 Points[0].x, Points[0].y,
115 //                                 Points[1].x, Points[1].y );
116 
117                 ret = IntEngLineTo(&psurf->SurfObj,
118                                    (CLIPOBJ *)&dc->co,
119                                    &dc->eboLine.BrushObject,
120                                    Points[i].x,          /* From */
121                                    Points[i].y,
122                                    Points[i+1].x,        /* To */
123                                    Points[i+1].y,
124                                    &DestRect,
125                                    ROP2_TO_MIX(pdcattr->jROP2)); /* MIX */
126                 if (!ret) break;
127             }
128             /* Close the polygon */
129             if (ret)
130             {
131                 ret = IntEngLineTo(&psurf->SurfObj,
132                                    (CLIPOBJ *)&dc->co,
133                                    &dc->eboLine.BrushObject,
134                                    Points[Count-1].x, /* From */
135                                    Points[Count-1].y,
136                                    Points[0].x,       /* To */
137                                    Points[0].y,
138                                    &DestRect,
139                                    ROP2_TO_MIX(pdcattr->jROP2)); /* MIX */
140             }
141         }
142     }
143 
144     return ret;
145 }
146 
147 BOOL FASTCALL
148 IntGdiPolyPolygon(DC      *dc,
149                   LPPOINT Points,
150                   PULONG  PolyCounts,
151                   int     Count)
152 {
153     if (PATH_IsPathOpen(dc->dclevel))
154         return PATH_PolyPolygon ( dc, Points, (PINT)PolyCounts, Count);
155 
156     while (--Count >=0)
157     {
158         if (!IntGdiPolygon ( dc, Points, *PolyCounts ))
159             return FALSE;
160         Points+=*PolyCounts++;
161     }
162     return TRUE;
163 }
164 
165 BOOL FASTCALL
166 IntPolygon(HDC hdc, POINT *Point, int Count)
167 {
168     BOOL bResult;
169     PDC pdc;
170 
171     pdc = DC_LockDc(hdc);
172     if (pdc == NULL)
173     {
174         EngSetLastError(ERROR_INVALID_HANDLE);
175         return FALSE;
176     }
177 
178     bResult = IntGdiPolygon(pdc, Point, Count);
179 
180     DC_UnlockDc(pdc);
181     return bResult;
182 }
183 
184 
185 /******************************************************************************/
186 
187 /*
188  * NtGdiEllipse
189  *
190  * Author
191  *    Filip Navara
192  *
193  * Remarks
194  *    This function uses optimized Bresenham's ellipse algorithm. It draws
195  *    four lines of the ellipse in one pass.
196  *
197  */
198 
199 BOOL APIENTRY
200 NtGdiEllipse(
201     HDC hDC,
202     int Left,
203     int Top,
204     int Right,
205     int Bottom)
206 {
207     PDC dc;
208     PDC_ATTR pdcattr;
209     RECTL RectBounds;
210     PBRUSH pbrush;
211     BOOL ret = TRUE;
212     LONG PenWidth, PenOrigWidth;
213     LONG RadiusX, RadiusY, CenterX, CenterY;
214     PBRUSH pFillBrushObj;
215     BRUSH tmpFillBrushObj;
216 
217     dc = DC_LockDc(hDC);
218     if (dc == NULL)
219     {
220        EngSetLastError(ERROR_INVALID_HANDLE);
221        return FALSE;
222     }
223 
224     if (PATH_IsPathOpen(dc->dclevel))
225     {
226         ret = PATH_Ellipse(dc, Left, Top, Right, Bottom);
227         DC_UnlockDc(dc);
228         return ret;
229     }
230 
231     ////
232     //// Could this use PATH_CheckCorners ?
233     ////
234     if ((Left == Right) || (Top == Bottom))
235     {
236        DC_UnlockDc(dc);
237        return TRUE;
238     }
239 
240     if (Right < Left)
241     {
242        INT tmp = Right; Right = Left; Left = tmp;
243     }
244     if (Bottom < Top)
245     {
246        INT tmp = Bottom; Bottom = Top; Top = tmp;
247     }
248     ////
249 
250     pdcattr = dc->pdcattr;
251 
252     if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
253         DC_vUpdateFillBrush(dc);
254 
255     if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
256         DC_vUpdateLineBrush(dc);
257 
258     pbrush = PEN_ShareLockPen(pdcattr->hpen);
259     if (!pbrush)
260     {
261         DPRINT1("Ellipse Fail 1\n");
262         DC_UnlockDc(dc);
263         EngSetLastError(ERROR_INTERNAL_ERROR);
264         return FALSE;
265     }
266 
267     PenOrigWidth = PenWidth = pbrush->lWidth;
268     if (pbrush->ulPenStyle == PS_NULL) PenWidth = 0;
269 
270     if (pbrush->ulPenStyle == PS_INSIDEFRAME)
271     {
272        if (2*PenWidth > (Right - Left)) PenWidth = (Right -Left + 1)/2;
273        if (2*PenWidth > (Bottom - Top)) PenWidth = (Bottom -Top + 1)/2;
274        Left   += PenWidth / 2;
275        Right  -= (PenWidth - 1) / 2;
276        Top    += PenWidth / 2;
277        Bottom -= (PenWidth - 1) / 2;
278     }
279 
280     if (!PenWidth) PenWidth = 1;
281     pbrush->lWidth = PenWidth;
282 
283     RectBounds.left   = Left;
284     RectBounds.right  = Right;
285     RectBounds.top    = Top;
286     RectBounds.bottom = Bottom;
287 
288     IntLPtoDP(dc, (LPPOINT)&RectBounds, 2);
289 
290     RectBounds.left += dc->ptlDCOrig.x;
291     RectBounds.right += dc->ptlDCOrig.x;
292     RectBounds.top += dc->ptlDCOrig.y;
293     RectBounds.bottom += dc->ptlDCOrig.y;
294 
295     // Setup for dynamic width and height.
296     RadiusX = max((RectBounds.right - RectBounds.left) / 2, 2); // Needs room
297     RadiusY = max((RectBounds.bottom - RectBounds.top) / 2, 2);
298     CenterX = (RectBounds.right + RectBounds.left) / 2;
299     CenterY = (RectBounds.bottom + RectBounds.top) / 2;
300 
301     DPRINT("Ellipse 1: Left: %d, Top: %d, Right: %d, Bottom: %d\n",
302                RectBounds.left,RectBounds.top,RectBounds.right,RectBounds.bottom);
303 
304     DPRINT("Ellipse 2: XLeft: %d, YLeft: %d, Width: %d, Height: %d\n",
305                CenterX - RadiusX, CenterY + RadiusY, RadiusX*2, RadiusY*2);
306 
307     pFillBrushObj = BRUSH_ShareLockBrush(pdcattr->hbrush);
308     if (NULL == pFillBrushObj)
309     {
310         DPRINT1("FillEllipse Fail\n");
311         EngSetLastError(ERROR_INTERNAL_ERROR);
312         ret = FALSE;
313     }
314     else
315     {
316         RtlCopyMemory(&tmpFillBrushObj, pFillBrushObj, sizeof(tmpFillBrushObj));
317         //tmpFillBrushObj.ptOrigin.x += RectBounds.left - Left;
318         //tmpFillBrushObj.ptOrigin.y += RectBounds.top - Top;
319         tmpFillBrushObj.ptOrigin.x += dc->ptlDCOrig.x;
320         tmpFillBrushObj.ptOrigin.y += dc->ptlDCOrig.y;
321 
322         DC_vPrepareDCsForBlit(dc, &RectBounds, NULL, NULL);
323 
324         ret = IntFillEllipse( dc,
325                               CenterX - RadiusX,
326                               CenterY - RadiusY,
327                               RadiusX*2, // Width
328                               RadiusY*2, // Height
329                               &tmpFillBrushObj);
330         BRUSH_ShareUnlockBrush(pFillBrushObj);
331 
332         if (ret)
333         {
334            ret = IntDrawEllipse( dc,
335                                  CenterX - RadiusX,
336                                  CenterY - RadiusY,
337                                  RadiusX*2, // Width
338                                  RadiusY*2, // Height
339                                  pbrush);
340         }
341 
342         DC_vFinishBlit(dc, NULL);
343     }
344 
345     pbrush->lWidth = PenOrigWidth;
346     PEN_ShareUnlockPen(pbrush);
347     DC_UnlockDc(dc);
348     DPRINT("Ellipse Exit.\n");
349     return ret;
350 }
351 
352 #if 0
353 
354 // When the fill mode is ALTERNATE, GDI fills the area between odd-numbered and
355 // even-numbered polygon sides on each scan line. That is, GDI fills the area between the
356 // first and second side, between the third and fourth side, and so on.
357 
358 // WINDING Selects winding mode (fills any region with a nonzero winding value).
359 // When the fill mode is WINDING, GDI fills any region that has a nonzero winding value.
360 // This value is defined as the number of times a pen used to draw the polygon would go around the region.
361 // The direction of each edge of the polygon is important.
362 
363 extern BOOL FillPolygon(PDC dc,
364                             SURFOBJ *SurfObj,
365                             PBRUSHOBJ BrushObj,
366                             MIX RopMode,
367                             CONST PPOINT Points,
368                             int Count,
369                             RECTL BoundRect);
370 
371 #endif
372 
373 
374 ULONG_PTR
375 APIENTRY
376 NtGdiPolyPolyDraw( IN HDC hDC,
377                    IN PPOINT UnsafePoints,
378                    IN PULONG UnsafeCounts,
379                    IN ULONG Count,
380                    IN INT iFunc )
381 {
382     DC *dc;
383     PVOID pTemp;
384     LPPOINT SafePoints;
385     PULONG SafeCounts;
386     NTSTATUS Status = STATUS_SUCCESS;
387     BOOL Ret = TRUE;
388     ULONG nPoints = 0, nMaxPoints = 0, nInvalid = 0, i;
389 
390     if (!UnsafePoints || !UnsafeCounts ||
391         Count == 0 || iFunc == 0 || iFunc > GdiPolyPolyRgn)
392     {
393         /* Windows doesn't set last error */
394         return FALSE;
395     }
396 
397     _SEH2_TRY
398     {
399         ProbeForRead(UnsafePoints, Count * sizeof(POINT), 1);
400         ProbeForRead(UnsafeCounts, Count * sizeof(ULONG), 1);
401 
402         /* Count points and validate poligons */
403         for (i = 0; i < Count; i++)
404         {
405             if (UnsafeCounts[i] < 2)
406             {
407                 nInvalid++;
408             }
409             nPoints += UnsafeCounts[i];
410             nMaxPoints = max(nMaxPoints, UnsafeCounts[i]);
411         }
412     }
413     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
414     {
415         Status = _SEH2_GetExceptionCode();
416     }
417     _SEH2_END;
418 
419     if (!NT_SUCCESS(Status))
420     {
421         /* Windows doesn't set last error */
422         return FALSE;
423     }
424 
425     if (nPoints == 0 || nPoints < nMaxPoints)
426     {
427         /* If all polygon counts are zero, or we have overflow,
428            return without setting a last error code. */
429         return FALSE;
430     }
431 
432     if (nInvalid != 0)
433     {
434         /* If at least one poly count is 0 or 1, fail */
435         EngSetLastError(ERROR_INVALID_PARAMETER);
436         return FALSE;
437     }
438 
439     /* Allocate one buffer for both counts and points */
440     pTemp = ExAllocatePoolWithTag(PagedPool,
441                                   Count * sizeof(ULONG) + nPoints * sizeof(POINT),
442                                   TAG_SHAPE);
443     if (!pTemp)
444     {
445         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
446         return FALSE;
447     }
448 
449     SafeCounts = pTemp;
450     SafePoints = (PVOID)(SafeCounts + Count);
451 
452     _SEH2_TRY
453     {
454         /* Pointers already probed! */
455         RtlCopyMemory(SafeCounts, UnsafeCounts, Count * sizeof(ULONG));
456         RtlCopyMemory(SafePoints, UnsafePoints, nPoints * sizeof(POINT));
457     }
458     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
459     {
460         Status = _SEH2_GetExceptionCode();
461     }
462     _SEH2_END;
463 
464     if (!NT_SUCCESS(Status))
465     {
466         ExFreePoolWithTag(pTemp, TAG_SHAPE);
467         return FALSE;
468     }
469 
470     /* Special handling for GdiPolyPolyRgn */
471     if (iFunc == GdiPolyPolyRgn)
472     {
473         INT iMode = (INT)(UINT_PTR)hDC;
474         HRGN hrgn;
475 
476         hrgn = GreCreatePolyPolygonRgn(SafePoints, SafeCounts, Count, iMode);
477 
478         ExFreePoolWithTag(pTemp, TAG_SHAPE);
479         return (ULONG_PTR)hrgn;
480     }
481 
482     dc = DC_LockDc(hDC);
483     if (!dc)
484     {
485         EngSetLastError(ERROR_INVALID_HANDLE);
486         ExFreePoolWithTag(pTemp, TAG_SHAPE);
487         return FALSE;
488     }
489 
490     DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL);
491 
492     if (dc->pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
493         DC_vUpdateFillBrush(dc);
494 
495     if (dc->pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
496         DC_vUpdateLineBrush(dc);
497 
498     /* Perform the actual work */
499     switch (iFunc)
500     {
501         case GdiPolyPolygon:
502             Ret = IntGdiPolyPolygon(dc, SafePoints, SafeCounts, Count);
503             break;
504         case GdiPolyPolyLine:
505             Ret = IntGdiPolyPolyline(dc, SafePoints, SafeCounts, Count);
506             break;
507         case GdiPolyBezier:
508             Ret = IntGdiPolyBezier(dc, SafePoints, *SafeCounts);
509             break;
510         case GdiPolyLineTo:
511             Ret = IntGdiPolylineTo(dc, SafePoints, *SafeCounts);
512             break;
513         case GdiPolyBezierTo:
514             Ret = IntGdiPolyBezierTo(dc, SafePoints, *SafeCounts);
515             break;
516         default:
517             EngSetLastError(ERROR_INVALID_PARAMETER);
518             Ret = FALSE;
519     }
520 
521     /* Cleanup and return */
522     DC_vFinishBlit(dc, NULL);
523     DC_UnlockDc(dc);
524     ExFreePoolWithTag(pTemp, TAG_SHAPE);
525 
526     return (ULONG_PTR)Ret;
527 }
528 
529 
530 BOOL
531 FASTCALL
532 IntRectangle(PDC dc,
533              int LeftRect,
534              int TopRect,
535              int RightRect,
536              int BottomRect)
537 {
538     SURFACE *psurf = NULL;
539     PBRUSH pbrLine, pbrFill;
540     BOOL       ret = FALSE; // Default to failure
541     RECTL      DestRect;
542     MIX        Mix;
543     PDC_ATTR pdcattr;
544     POINTL BrushOrigin;
545 
546     ASSERT ( dc ); // Caller's responsibility to set this up
547 
548     pdcattr = dc->pdcattr;
549 
550     // Rectangle Path only.
551     if ( PATH_IsPathOpen(dc->dclevel) )
552     {
553         return PATH_Rectangle ( dc, LeftRect, TopRect, RightRect, BottomRect );
554     }
555 
556 	/* Make sure rectangle is not inverted */
557     DestRect.left   = min(LeftRect, RightRect);
558     DestRect.right  = max(LeftRect, RightRect);
559     DestRect.top    = min(TopRect,  BottomRect);
560     DestRect.bottom = max(TopRect,  BottomRect);
561 
562     IntLPtoDP(dc, (LPPOINT)&DestRect, 2);
563 
564     DestRect.left   += dc->ptlDCOrig.x;
565     DestRect.right  += dc->ptlDCOrig.x;
566     DestRect.top    += dc->ptlDCOrig.y;
567     DestRect.bottom += dc->ptlDCOrig.y;
568 
569     if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
570     {
571        IntUpdateBoundsRect(dc, &DestRect);
572     }
573 
574     /* In GM_COMPATIBLE, don't include bottom and right edges */
575     if (pdcattr->iGraphicsMode == GM_COMPATIBLE)
576     {
577         DestRect.right--;
578         DestRect.bottom--;
579     }
580 
581     DC_vPrepareDCsForBlit(dc, &DestRect, NULL, NULL);
582 
583     if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
584         DC_vUpdateFillBrush(dc);
585 
586     if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
587         DC_vUpdateLineBrush(dc);
588 
589     pbrFill = dc->dclevel.pbrFill;
590     pbrLine = dc->dclevel.pbrLine;
591     if (!pbrLine)
592     {
593         ret = FALSE;
594         goto cleanup;
595     }
596 
597     psurf = dc->dclevel.pSurface;
598     if (!psurf)
599     {
600         ret = TRUE;
601         goto cleanup;
602     }
603 
604     if (pbrFill)
605     {
606         if (!(pbrFill->flAttrs & BR_IS_NULL))
607         {
608             BrushOrigin = *((PPOINTL)&pbrFill->ptOrigin);
609             BrushOrigin.x += dc->ptlDCOrig.x;
610             BrushOrigin.y += dc->ptlDCOrig.y;
611             ret = IntEngBitBlt(&psurf->SurfObj,
612                                NULL,
613                                NULL,
614                                (CLIPOBJ *)&dc->co,
615                                NULL,
616                                &DestRect,
617                                NULL,
618                                NULL,
619                                &dc->eboFill.BrushObject,
620                                &BrushOrigin,
621                                ROP4_FROM_INDEX(R3_OPINDEX_PATCOPY));
622         }
623     }
624 
625     // Draw the rectangle with the current pen
626 
627     ret = TRUE; // Change default to success
628 
629     if (!(pbrLine->flAttrs & BR_IS_NULL))
630     {
631         Mix = ROP2_TO_MIX(pdcattr->jROP2);
632         ret = ret && IntEngLineTo(&psurf->SurfObj,
633                                   (CLIPOBJ *)&dc->co,
634                                   &dc->eboLine.BrushObject,
635                                   DestRect.left, DestRect.top, DestRect.right, DestRect.top,
636                                   &DestRect, // Bounding rectangle
637                                   Mix);
638 
639         ret = ret && IntEngLineTo(&psurf->SurfObj,
640                                   (CLIPOBJ *)&dc->co,
641                                   &dc->eboLine.BrushObject,
642                                   DestRect.right, DestRect.top, DestRect.right, DestRect.bottom,
643                                   &DestRect, // Bounding rectangle
644                                   Mix);
645 
646         ret = ret && IntEngLineTo(&psurf->SurfObj,
647                                   (CLIPOBJ *)&dc->co,
648                                   &dc->eboLine.BrushObject,
649                                   DestRect.right, DestRect.bottom, DestRect.left, DestRect.bottom,
650                                   &DestRect, // Bounding rectangle
651                                   Mix);
652 
653         ret = ret && IntEngLineTo(&psurf->SurfObj,
654                                   (CLIPOBJ *)&dc->co,
655                                   &dc->eboLine.BrushObject,
656                                   DestRect.left, DestRect.bottom, DestRect.left, DestRect.top,
657                                   &DestRect, // Bounding rectangle
658                                   Mix);
659     }
660 
661 cleanup:
662     DC_vFinishBlit(dc, NULL);
663 
664     /* Move current position in DC?
665        MSDN: The current position is neither used nor updated by Rectangle. */
666 
667     return ret;
668 }
669 
670 BOOL
671 APIENTRY
672 NtGdiRectangle(HDC  hDC,
673                int  LeftRect,
674                int  TopRect,
675                int  RightRect,
676                int  BottomRect)
677 {
678     DC   *dc;
679     BOOL ret; // Default to failure
680 
681     dc = DC_LockDc(hDC);
682     if (!dc)
683     {
684         EngSetLastError(ERROR_INVALID_HANDLE);
685         return FALSE;
686     }
687 
688     /* Do we rotate or shear? */
689     if (!(dc->pdcattr->mxWorldToDevice.flAccel & XFORM_SCALE))
690     {
691         POINTL DestCoords[4];
692         ULONG PolyCounts = 4;
693 
694         DestCoords[0].x = DestCoords[3].x = LeftRect;
695         DestCoords[0].y = DestCoords[1].y = TopRect;
696         DestCoords[1].x = DestCoords[2].x = RightRect;
697         DestCoords[2].y = DestCoords[3].y = BottomRect;
698         // Use IntGdiPolyPolygon so to support PATH.
699         ret = IntGdiPolyPolygon(dc, DestCoords, &PolyCounts, 1);
700     }
701     else
702     {
703         ret = IntRectangle(dc, LeftRect, TopRect, RightRect, BottomRect );
704     }
705 
706     DC_UnlockDc(dc);
707 
708     return ret;
709 }
710 
711 
712 BOOL
713 FASTCALL
714 IntRoundRect(
715     PDC  dc,
716     int  Left,
717     int  Top,
718     int  Right,
719     int  Bottom,
720     int  xCurveDiameter,
721     int  yCurveDiameter)
722 {
723     PDC_ATTR pdcattr;
724     PBRUSH   pbrLine, pbrFill;
725     RECTL RectBounds;
726     LONG PenWidth, PenOrigWidth;
727     BOOL ret = TRUE; // Default to success
728     BRUSH brushTemp;
729 
730     ASSERT ( dc ); // Caller's responsibility to set this up
731 
732     if ( PATH_IsPathOpen(dc->dclevel) )
733         return PATH_RoundRect ( dc, Left, Top, Right, Bottom,
734                                 xCurveDiameter, yCurveDiameter );
735 
736     if ((Left == Right) || (Top == Bottom)) return TRUE;
737 
738     xCurveDiameter = max(abs( xCurveDiameter ), 1);
739     yCurveDiameter = max(abs( yCurveDiameter ), 1);
740 
741     if (Right < Left)
742     {
743        INT tmp = Right; Right = Left; Left = tmp;
744     }
745     if (Bottom < Top)
746     {
747        INT tmp = Bottom; Bottom = Top; Top = tmp;
748     }
749 
750     pdcattr = dc->pdcattr;
751 
752     if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
753         DC_vUpdateFillBrush(dc);
754 
755     if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
756         DC_vUpdateLineBrush(dc);
757 
758     pbrLine = PEN_ShareLockPen(pdcattr->hpen);
759     if (!pbrLine)
760     {
761         /* Nothing to do, as we don't have a bitmap */
762         EngSetLastError(ERROR_INTERNAL_ERROR);
763         return FALSE;
764     }
765 
766     PenOrigWidth = PenWidth = pbrLine->lWidth;
767     if (pbrLine->ulPenStyle == PS_NULL) PenWidth = 0;
768 
769     if (pbrLine->ulPenStyle == PS_INSIDEFRAME)
770     {
771        if (2*PenWidth > (Right - Left)) PenWidth = (Right -Left + 1)/2;
772        if (2*PenWidth > (Bottom - Top)) PenWidth = (Bottom -Top + 1)/2;
773        Left   += PenWidth / 2;
774        Right  -= (PenWidth - 1) / 2;
775        Top    += PenWidth / 2;
776        Bottom -= (PenWidth - 1) / 2;
777     }
778 
779     if (!PenWidth) PenWidth = 1;
780     pbrLine->lWidth = PenWidth;
781 
782     RectBounds.left = Left;
783     RectBounds.top = Top;
784     RectBounds.right = Right;
785     RectBounds.bottom = Bottom;
786 
787     IntLPtoDP(dc, (LPPOINT)&RectBounds, 2);
788 
789     RectBounds.left   += dc->ptlDCOrig.x;
790     RectBounds.top    += dc->ptlDCOrig.y;
791     RectBounds.right  += dc->ptlDCOrig.x;
792     RectBounds.bottom += dc->ptlDCOrig.y;
793 
794     pbrFill = BRUSH_ShareLockBrush(pdcattr->hbrush);
795     if (!pbrFill)
796     {
797         DPRINT1("FillRound Fail\n");
798         EngSetLastError(ERROR_INTERNAL_ERROR);
799         ret = FALSE;
800     }
801     else
802     {
803 
804         DC_vPrepareDCsForBlit(dc, &RectBounds, NULL, NULL);
805 
806         RtlCopyMemory(&brushTemp, pbrFill, sizeof(brushTemp));
807         brushTemp.ptOrigin.x += RectBounds.left - Left;
808         brushTemp.ptOrigin.y += RectBounds.top - Top;
809         ret = IntFillRoundRect( dc,
810                                 RectBounds.left,
811                                 RectBounds.top,
812                                 RectBounds.right,
813                                 RectBounds.bottom,
814                                 xCurveDiameter,
815                                 yCurveDiameter,
816                                 &brushTemp);
817         BRUSH_ShareUnlockBrush(pbrFill);
818 
819         if (ret)
820         {
821            ret = IntDrawRoundRect( dc,
822                       RectBounds.left,
823                        RectBounds.top,
824                      RectBounds.right,
825                     RectBounds.bottom,
826                        xCurveDiameter,
827                        yCurveDiameter,
828                        pbrLine);
829         }
830 
831         DC_vFinishBlit(dc, NULL);
832     }
833 
834 
835     pbrLine->lWidth = PenOrigWidth;
836     PEN_ShareUnlockPen(pbrLine);
837     return ret;
838 }
839 
840 BOOL
841 APIENTRY
842 NtGdiRoundRect(
843     HDC  hDC,
844     int  LeftRect,
845     int  TopRect,
846     int  RightRect,
847     int  BottomRect,
848     int  Width,
849     int  Height)
850 {
851     DC   *dc = DC_LockDc(hDC);
852     BOOL  ret = FALSE; /* Default to failure */
853 
854     DPRINT("NtGdiRoundRect(0x%p,%i,%i,%i,%i,%i,%i)\n",hDC,LeftRect,TopRect,RightRect,BottomRect,Width,Height);
855     if ( !dc )
856     {
857         DPRINT1("NtGdiRoundRect() - hDC is invalid\n");
858         EngSetLastError(ERROR_INVALID_HANDLE);
859     }
860     else
861     {
862         ret = IntRoundRect ( dc, LeftRect, TopRect, RightRect, BottomRect, Width, Height );
863         DC_UnlockDc ( dc );
864     }
865 
866     return ret;
867 }
868 
869 BOOL
870 NTAPI
871 GreGradientFill(
872     HDC hdc,
873     PTRIVERTEX pVertex,
874     ULONG nVertex,
875     PVOID pMesh,
876     ULONG nMesh,
877     ULONG ulMode)
878 {
879     PDC pdc;
880     SURFACE *psurf;
881     EXLATEOBJ exlo;
882     RECTL rclExtent;
883     POINTL ptlDitherOrg;
884     ULONG i;
885     BOOL bRet;
886 
887     /* Check parameters */
888     if (ulMode & GRADIENT_FILL_TRIANGLE)
889     {
890         PGRADIENT_TRIANGLE pTriangle = (PGRADIENT_TRIANGLE)pMesh;
891 
892         for (i = 0; i < nMesh; i++, pTriangle++)
893         {
894             if (pTriangle->Vertex1 >= nVertex ||
895                 pTriangle->Vertex2 >= nVertex ||
896                 pTriangle->Vertex3 >= nVertex)
897             {
898                 EngSetLastError(ERROR_INVALID_PARAMETER);
899                 return FALSE;
900             }
901         }
902     }
903     else
904     {
905         PGRADIENT_RECT pRect = (PGRADIENT_RECT)pMesh;
906         for (i = 0; i < nMesh; i++, pRect++)
907         {
908             if (pRect->UpperLeft >= nVertex || pRect->LowerRight >= nVertex)
909             {
910                 EngSetLastError(ERROR_INVALID_PARAMETER);
911                 return FALSE;
912             }
913         }
914     }
915 
916     /* Lock the output DC */
917     pdc = DC_LockDc(hdc);
918     if(!pdc)
919     {
920         EngSetLastError(ERROR_INVALID_HANDLE);
921         return FALSE;
922     }
923 
924     if (!pdc->dclevel.pSurface)
925     {
926         /* Memory DC with no surface selected */
927         DC_UnlockDc(pdc);
928         return TRUE; // CHECKME
929     }
930 
931     /* Calculate extent */
932     rclExtent.left = rclExtent.right = pVertex->x;
933     rclExtent.top = rclExtent.bottom = pVertex->y;
934     for (i = 0; i < nVertex; i++)
935     {
936         rclExtent.left = min(rclExtent.left, (pVertex + i)->x);
937         rclExtent.right = max(rclExtent.right, (pVertex + i)->x);
938         rclExtent.top = min(rclExtent.top, (pVertex + i)->y);
939         rclExtent.bottom = max(rclExtent.bottom, (pVertex + i)->y);
940     }
941     IntLPtoDP(pdc, (LPPOINT)&rclExtent, 2);
942 
943     rclExtent.left   += pdc->ptlDCOrig.x;
944     rclExtent.right  += pdc->ptlDCOrig.x;
945     rclExtent.top    += pdc->ptlDCOrig.y;
946     rclExtent.bottom += pdc->ptlDCOrig.y;
947 
948     if (RECTL_bIsEmptyRect(&rclExtent))
949     {
950         DC_UnlockDc(pdc);
951         return TRUE;
952     }
953 
954     ptlDitherOrg.x = ptlDitherOrg.y = 0;
955     IntLPtoDP(pdc, (LPPOINT)&ptlDitherOrg, 1);
956 
957     ptlDitherOrg.x += pdc->ptlDCOrig.x;
958     ptlDitherOrg.y += pdc->ptlDCOrig.y;
959 
960    if (pdc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
961    {
962       IntUpdateBoundsRect(pdc, &rclExtent);
963    }
964 
965     DC_vPrepareDCsForBlit(pdc, &rclExtent, NULL, NULL);
966 
967     psurf = pdc->dclevel.pSurface;
968 
969     EXLATEOBJ_vInitialize(&exlo, &gpalRGB, psurf->ppal, 0, 0, 0);
970 
971     bRet = IntEngGradientFill(&psurf->SurfObj,
972                              (CLIPOBJ *)&pdc->co,
973                              &exlo.xlo,
974                              pVertex,
975                              nVertex,
976                              pMesh,
977                              nMesh,
978                              &rclExtent,
979                              &ptlDitherOrg,
980                              ulMode);
981 
982     EXLATEOBJ_vCleanup(&exlo);
983     DC_vFinishBlit(pdc, NULL);
984     DC_UnlockDc(pdc);
985 
986     return bRet;
987 }
988 
989 BOOL
990 APIENTRY
991 NtGdiGradientFill(
992     HDC hdc,
993     PTRIVERTEX pVertex,
994     ULONG nVertex,
995     PVOID pMesh,
996     ULONG nMesh,
997     ULONG ulMode)
998 {
999     BOOL bRet;
1000     PTRIVERTEX SafeVertex;
1001     PVOID SafeMesh;
1002     ULONG cbVertex, cbMesh;
1003 
1004     /* Validate parameters */
1005     if (!pVertex || !nVertex || !pMesh || !nMesh)
1006     {
1007         EngSetLastError(ERROR_INVALID_PARAMETER);
1008         return FALSE;
1009     }
1010 
1011     switch (ulMode)
1012     {
1013         case GRADIENT_FILL_RECT_H:
1014         case GRADIENT_FILL_RECT_V:
1015             cbMesh = nMesh * sizeof(GRADIENT_RECT);
1016             break;
1017         case GRADIENT_FILL_TRIANGLE:
1018             cbMesh = nMesh * sizeof(GRADIENT_TRIANGLE);
1019             break;
1020         default:
1021             EngSetLastError(ERROR_INVALID_PARAMETER);
1022             return FALSE;
1023     }
1024 
1025     cbVertex = nVertex * sizeof(TRIVERTEX) ;
1026     if(cbVertex + cbMesh <= cbVertex)
1027     {
1028         /* Overflow */
1029         return FALSE ;
1030     }
1031 
1032     /* Allocate a kernel mode buffer */
1033     SafeVertex = ExAllocatePoolWithTag(PagedPool, cbVertex + cbMesh, TAG_SHAPE);
1034     if(!SafeVertex)
1035     {
1036         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1037         return FALSE;
1038     }
1039 
1040     SafeMesh = (PVOID)((ULONG_PTR)SafeVertex + cbVertex);
1041 
1042     /* Copy the parameters to kernel mode */
1043     _SEH2_TRY
1044     {
1045         ProbeForRead(pVertex, cbVertex, 1);
1046         ProbeForRead(pMesh, cbMesh, 1);
1047         RtlCopyMemory(SafeVertex, pVertex, cbVertex);
1048         RtlCopyMemory(SafeMesh, pMesh, cbMesh);
1049     }
1050     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1051     {
1052         ExFreePoolWithTag(SafeVertex, TAG_SHAPE);
1053         SetLastNtError(_SEH2_GetExceptionCode());
1054         _SEH2_YIELD(return FALSE;)
1055     }
1056     _SEH2_END;
1057 
1058     /* Call the internal function */
1059     bRet = GreGradientFill(hdc, SafeVertex, nVertex, SafeMesh, nMesh, ulMode);
1060 
1061     /* Cleanup and return result */
1062     ExFreePoolWithTag(SafeVertex, TAG_SHAPE);
1063     return bRet;
1064 }
1065 
1066 BOOL APIENTRY
1067 NtGdiExtFloodFill(
1068     HDC  hDC,
1069     INT  XStart,
1070     INT  YStart,
1071     COLORREF  Color,
1072     UINT  FillType)
1073 {
1074     PDC dc;
1075 #if 0
1076     PDC_ATTR   pdcattr;
1077 #endif
1078     SURFACE    *psurf;
1079     EXLATEOBJ  exlo;
1080     BOOL       Ret = FALSE;
1081     RECTL      DestRect;
1082     POINTL     Pt;
1083     ULONG      ConvColor;
1084 
1085     dc = DC_LockDc(hDC);
1086     if (!dc)
1087     {
1088         EngSetLastError(ERROR_INVALID_HANDLE);
1089         return FALSE;
1090     }
1091 
1092     if (!dc->dclevel.pSurface)
1093     {
1094         Ret = TRUE;
1095         goto cleanup;
1096     }
1097 
1098 #if 0
1099     pdcattr = dc->pdcattr;
1100 #endif
1101 
1102     Pt.x = XStart;
1103     Pt.y = YStart;
1104     IntLPtoDP(dc, (LPPOINT)&Pt, 1);
1105 
1106     DC_vPrepareDCsForBlit(dc, &DestRect, NULL, NULL);
1107 
1108     /// FIXME: what about prgnVIS? And what about REAL clipping?
1109     psurf = dc->dclevel.pSurface;
1110     if (dc->prgnRao)
1111     {
1112         Ret = REGION_PtInRegion(dc->prgnRao, Pt.x, Pt.y);
1113         if (Ret)
1114             REGION_GetRgnBox(dc->prgnRao, (LPRECT)&DestRect);
1115         else
1116         {
1117             DC_vFinishBlit(dc, NULL);
1118             goto cleanup;
1119         }
1120     }
1121     else
1122     {
1123         RECTL_vSetRect(&DestRect, 0, 0, psurf->SurfObj.sizlBitmap.cx, psurf->SurfObj.sizlBitmap.cy);
1124     }
1125 
1126     if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
1127     {
1128        IntUpdateBoundsRect(dc, &DestRect);
1129     }
1130 
1131     EXLATEOBJ_vInitialize(&exlo, &gpalRGB, psurf->ppal, 0, 0xffffff, 0);
1132 
1133     /* Only solid fills supported for now
1134      * How to support pattern brushes and non standard surfaces (not offering dib functions):
1135      * Version a (most likely slow): call DrvPatBlt for every pixel
1136      * Version b: create a flood mask and let MaskBlt blit a masked brush */
1137     ConvColor = XLATEOBJ_iXlate(&exlo.xlo, Color);
1138     Ret = DIB_XXBPP_FloodFillSolid(&psurf->SurfObj, &dc->eboFill.BrushObject, &DestRect, &Pt, ConvColor, FillType);
1139 
1140     DC_vFinishBlit(dc, NULL);
1141 
1142     EXLATEOBJ_vCleanup(&exlo);
1143 
1144 cleanup:
1145     DC_UnlockDc(dc);
1146     return Ret;
1147 }
1148 
1149 /* EOF */
1150