[WIN32K] Fix probing and parameter validation in NtGdiPolyPolyDraw
[reactos.git] / reactos / win32ss / gdi / ntgdi / fillshap.c
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;
387 BOOL Ret = TRUE;
388 ULONG nPoints = 0, nMaxPoints = 0, i;
389
390 /* Validate parameters */
391 if ((UnsafePoints == NULL) ||
392 (UnsafeCounts == NULL) ||
393 (Count == 0) ||
394 (Count > ULONG_MAX / sizeof(ULONG)) ||
395 (iFunc == 0) ||
396 (iFunc > GdiPolyPolyRgn))
397 {
398 DPRINT1("NtGdiPolyPolyDraw - Invalid parameter!\n");
399 /* Windows doesn't set last error */
400 return FALSE;
401 }
402
403 _SEH2_TRY
404 {
405 /* Probe the buffer of counts for each polygon */
406 ProbeForRead(UnsafeCounts, Count * sizeof(ULONG), 1);
407
408 /* Count points. Note: We are not copying the buffer, so it can be
409 changed by usermode. This is ok, since the content is validated
410 again later. */
411 for (i = 0; i < Count; i++)
412 {
413 Status = RtlULongAdd(nMaxPoints, UnsafeCounts[i], &nMaxPoints);
414 if (!NT_SUCCESS(Status))
415 {
416 DPRINT1("Overflow when counting points!\n");
417 return FALSE;
418 }
419 }
420
421 ProbeForRead(UnsafePoints, nMaxPoints * sizeof(POINT), 1);
422 }
423 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
424 {
425 DPRINT1("Got exception!\n");
426 /* Windows doesn't set last error */
427 return FALSE;
428 }
429 _SEH2_END;
430
431 if (nMaxPoints == 0)
432 {
433 /* If all polygon counts are zero, return FALSE
434 without setting a last error code. */
435 DPRINT1("nMaxPoints == 0!\n");
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 DPRINT1("Failed to allocate %lu bytes (Count = %lu, nPoints = %u).\n",
446 Count * sizeof(ULONG) + nPoints * sizeof(POINT),
447 Count,
448 nPoints);
449 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
450 return FALSE;
451 }
452
453 SafeCounts = pTemp;
454 SafePoints = (PPOINT)&SafeCounts[Count];
455
456 _SEH2_TRY
457 {
458 /* Pointers already probed! */
459 RtlCopyMemory(SafeCounts, UnsafeCounts, Count * sizeof(ULONG));
460 RtlCopyMemory(SafePoints, UnsafePoints, nPoints * sizeof(POINT));
461 }
462 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
463 {
464 DPRINT1("Got exception!\n");
465 ExFreePoolWithTag(pTemp, TAG_SHAPE);
466 return FALSE;
467 }
468 _SEH2_END;
469
470 /* Now that the buffers are copied, validate them again */
471 for (i = 0; i < Count; i++)
472 {
473 /* If any poly count is 0 or 1, fail */
474 if (SafeCounts[i] < 2)
475 {
476 DPRINT1("Invalid: UnsafeCounts[%lu] == %lu\n", i, SafeCounts[i]);
477 ExFreePoolWithTag(pTemp, TAG_SHAPE);
478 EngSetLastError(ERROR_INVALID_PARAMETER);
479 return FALSE;
480 }
481
482 Status = RtlULongAdd(nPoints, SafeCounts[i], &nPoints);
483 if (!NT_SUCCESS(Status))
484 {
485 DPRINT1("Overflow when counting points!\n");
486 return FALSE;
487 }
488 }
489
490 /* If the 2nd count does not match the 1st, someone changed the buffer
491 behind our back! */
492 if (nPoints != nMaxPoints)
493 {
494 DPRINT1("Polygon count mismatch: %lu != %lu\n", nPoints, nMaxPoints);
495 ExFreePoolWithTag(pTemp, TAG_SHAPE);
496 return FALSE;
497 }
498
499 /* Special handling for GdiPolyPolyRgn */
500 if (iFunc == GdiPolyPolyRgn)
501 {
502 INT iMode = (INT)(UINT_PTR)hDC;
503 HRGN hrgn;
504
505 hrgn = GreCreatePolyPolygonRgn(SafePoints, SafeCounts, Count, iMode);
506
507 ExFreePoolWithTag(pTemp, TAG_SHAPE);
508 return (ULONG_PTR)hrgn;
509 }
510
511 dc = DC_LockDc(hDC);
512 if (!dc)
513 {
514 EngSetLastError(ERROR_INVALID_HANDLE);
515 ExFreePoolWithTag(pTemp, TAG_SHAPE);
516 return FALSE;
517 }
518
519 DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL);
520
521 if (dc->pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
522 DC_vUpdateFillBrush(dc);
523
524 if (dc->pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
525 DC_vUpdateLineBrush(dc);
526
527 /* Perform the actual work */
528 switch (iFunc)
529 {
530 case GdiPolyPolygon:
531 Ret = IntGdiPolyPolygon(dc, SafePoints, SafeCounts, Count);
532 break;
533 case GdiPolyPolyLine:
534 Ret = IntGdiPolyPolyline(dc, SafePoints, SafeCounts, Count);
535 break;
536 case GdiPolyBezier:
537 Ret = IntGdiPolyBezier(dc, SafePoints, *SafeCounts);
538 break;
539 case GdiPolyLineTo:
540 Ret = IntGdiPolylineTo(dc, SafePoints, *SafeCounts);
541 break;
542 case GdiPolyBezierTo:
543 Ret = IntGdiPolyBezierTo(dc, SafePoints, *SafeCounts);
544 break;
545 default:
546 EngSetLastError(ERROR_INVALID_PARAMETER);
547 Ret = FALSE;
548 }
549
550 /* Cleanup and return */
551 DC_vFinishBlit(dc, NULL);
552 DC_UnlockDc(dc);
553 ExFreePoolWithTag(pTemp, TAG_SHAPE);
554
555 return (ULONG_PTR)Ret;
556 }
557
558
559 BOOL
560 FASTCALL
561 IntRectangle(PDC dc,
562 int LeftRect,
563 int TopRect,
564 int RightRect,
565 int BottomRect)
566 {
567 SURFACE *psurf = NULL;
568 PBRUSH pbrLine, pbrFill;
569 BOOL ret = FALSE; // Default to failure
570 RECTL DestRect;
571 MIX Mix;
572 PDC_ATTR pdcattr;
573 POINTL BrushOrigin;
574
575 ASSERT ( dc ); // Caller's responsibility to set this up
576
577 pdcattr = dc->pdcattr;
578
579 // Rectangle Path only.
580 if ( PATH_IsPathOpen(dc->dclevel) )
581 {
582 return PATH_Rectangle ( dc, LeftRect, TopRect, RightRect, BottomRect );
583 }
584
585 /* Make sure rectangle is not inverted */
586 DestRect.left = min(LeftRect, RightRect);
587 DestRect.right = max(LeftRect, RightRect);
588 DestRect.top = min(TopRect, BottomRect);
589 DestRect.bottom = max(TopRect, BottomRect);
590
591 IntLPtoDP(dc, (LPPOINT)&DestRect, 2);
592
593 DestRect.left += dc->ptlDCOrig.x;
594 DestRect.right += dc->ptlDCOrig.x;
595 DestRect.top += dc->ptlDCOrig.y;
596 DestRect.bottom += dc->ptlDCOrig.y;
597
598 if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
599 {
600 IntUpdateBoundsRect(dc, &DestRect);
601 }
602
603 /* In GM_COMPATIBLE, don't include bottom and right edges */
604 if (pdcattr->iGraphicsMode == GM_COMPATIBLE)
605 {
606 DestRect.right--;
607 DestRect.bottom--;
608 }
609
610 DC_vPrepareDCsForBlit(dc, &DestRect, NULL, NULL);
611
612 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
613 DC_vUpdateFillBrush(dc);
614
615 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
616 DC_vUpdateLineBrush(dc);
617
618 pbrFill = dc->dclevel.pbrFill;
619 pbrLine = dc->dclevel.pbrLine;
620 if (!pbrLine)
621 {
622 ret = FALSE;
623 goto cleanup;
624 }
625
626 psurf = dc->dclevel.pSurface;
627 if (!psurf)
628 {
629 ret = TRUE;
630 goto cleanup;
631 }
632
633 if (pbrFill)
634 {
635 if (!(pbrFill->flAttrs & BR_IS_NULL))
636 {
637 BrushOrigin = *((PPOINTL)&pbrFill->ptOrigin);
638 BrushOrigin.x += dc->ptlDCOrig.x;
639 BrushOrigin.y += dc->ptlDCOrig.y;
640 ret = IntEngBitBlt(&psurf->SurfObj,
641 NULL,
642 NULL,
643 (CLIPOBJ *)&dc->co,
644 NULL,
645 &DestRect,
646 NULL,
647 NULL,
648 &dc->eboFill.BrushObject,
649 &BrushOrigin,
650 ROP4_FROM_INDEX(R3_OPINDEX_PATCOPY));
651 }
652 }
653
654 // Draw the rectangle with the current pen
655
656 ret = TRUE; // Change default to success
657
658 if (!(pbrLine->flAttrs & BR_IS_NULL))
659 {
660 Mix = ROP2_TO_MIX(pdcattr->jROP2);
661 ret = ret && IntEngLineTo(&psurf->SurfObj,
662 (CLIPOBJ *)&dc->co,
663 &dc->eboLine.BrushObject,
664 DestRect.left, DestRect.top, DestRect.right, DestRect.top,
665 &DestRect, // Bounding rectangle
666 Mix);
667
668 ret = ret && IntEngLineTo(&psurf->SurfObj,
669 (CLIPOBJ *)&dc->co,
670 &dc->eboLine.BrushObject,
671 DestRect.right, DestRect.top, DestRect.right, DestRect.bottom,
672 &DestRect, // Bounding rectangle
673 Mix);
674
675 ret = ret && IntEngLineTo(&psurf->SurfObj,
676 (CLIPOBJ *)&dc->co,
677 &dc->eboLine.BrushObject,
678 DestRect.right, DestRect.bottom, DestRect.left, DestRect.bottom,
679 &DestRect, // Bounding rectangle
680 Mix);
681
682 ret = ret && IntEngLineTo(&psurf->SurfObj,
683 (CLIPOBJ *)&dc->co,
684 &dc->eboLine.BrushObject,
685 DestRect.left, DestRect.bottom, DestRect.left, DestRect.top,
686 &DestRect, // Bounding rectangle
687 Mix);
688 }
689
690 cleanup:
691 DC_vFinishBlit(dc, NULL);
692
693 /* Move current position in DC?
694 MSDN: The current position is neither used nor updated by Rectangle. */
695
696 return ret;
697 }
698
699 BOOL
700 APIENTRY
701 NtGdiRectangle(HDC hDC,
702 int LeftRect,
703 int TopRect,
704 int RightRect,
705 int BottomRect)
706 {
707 DC *dc;
708 BOOL ret; // Default to failure
709
710 dc = DC_LockDc(hDC);
711 if (!dc)
712 {
713 EngSetLastError(ERROR_INVALID_HANDLE);
714 return FALSE;
715 }
716
717 /* Do we rotate or shear? */
718 if (!(dc->pdcattr->mxWorldToDevice.flAccel & XFORM_SCALE))
719 {
720 POINTL DestCoords[4];
721 ULONG PolyCounts = 4;
722
723 DestCoords[0].x = DestCoords[3].x = LeftRect;
724 DestCoords[0].y = DestCoords[1].y = TopRect;
725 DestCoords[1].x = DestCoords[2].x = RightRect;
726 DestCoords[2].y = DestCoords[3].y = BottomRect;
727 // Use IntGdiPolyPolygon so to support PATH.
728 ret = IntGdiPolyPolygon(dc, DestCoords, &PolyCounts, 1);
729 }
730 else
731 {
732 ret = IntRectangle(dc, LeftRect, TopRect, RightRect, BottomRect );
733 }
734
735 DC_UnlockDc(dc);
736
737 return ret;
738 }
739
740
741 BOOL
742 FASTCALL
743 IntRoundRect(
744 PDC dc,
745 int Left,
746 int Top,
747 int Right,
748 int Bottom,
749 int xCurveDiameter,
750 int yCurveDiameter)
751 {
752 PDC_ATTR pdcattr;
753 PBRUSH pbrLine, pbrFill;
754 RECTL RectBounds;
755 LONG PenWidth, PenOrigWidth;
756 BOOL ret = TRUE; // Default to success
757 BRUSH brushTemp;
758
759 ASSERT ( dc ); // Caller's responsibility to set this up
760
761 if ( PATH_IsPathOpen(dc->dclevel) )
762 return PATH_RoundRect ( dc, Left, Top, Right, Bottom,
763 xCurveDiameter, yCurveDiameter );
764
765 if ((Left == Right) || (Top == Bottom)) return TRUE;
766
767 xCurveDiameter = max(abs( xCurveDiameter ), 1);
768 yCurveDiameter = max(abs( yCurveDiameter ), 1);
769
770 if (Right < Left)
771 {
772 INT tmp = Right; Right = Left; Left = tmp;
773 }
774 if (Bottom < Top)
775 {
776 INT tmp = Bottom; Bottom = Top; Top = tmp;
777 }
778
779 pdcattr = dc->pdcattr;
780
781 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
782 DC_vUpdateFillBrush(dc);
783
784 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
785 DC_vUpdateLineBrush(dc);
786
787 pbrLine = PEN_ShareLockPen(pdcattr->hpen);
788 if (!pbrLine)
789 {
790 /* Nothing to do, as we don't have a bitmap */
791 EngSetLastError(ERROR_INTERNAL_ERROR);
792 return FALSE;
793 }
794
795 PenOrigWidth = PenWidth = pbrLine->lWidth;
796 if (pbrLine->ulPenStyle == PS_NULL) PenWidth = 0;
797
798 if (pbrLine->ulPenStyle == PS_INSIDEFRAME)
799 {
800 if (2*PenWidth > (Right - Left)) PenWidth = (Right -Left + 1)/2;
801 if (2*PenWidth > (Bottom - Top)) PenWidth = (Bottom -Top + 1)/2;
802 Left += PenWidth / 2;
803 Right -= (PenWidth - 1) / 2;
804 Top += PenWidth / 2;
805 Bottom -= (PenWidth - 1) / 2;
806 }
807
808 if (!PenWidth) PenWidth = 1;
809 pbrLine->lWidth = PenWidth;
810
811 RectBounds.left = Left;
812 RectBounds.top = Top;
813 RectBounds.right = Right;
814 RectBounds.bottom = Bottom;
815
816 IntLPtoDP(dc, (LPPOINT)&RectBounds, 2);
817
818 RectBounds.left += dc->ptlDCOrig.x;
819 RectBounds.top += dc->ptlDCOrig.y;
820 RectBounds.right += dc->ptlDCOrig.x;
821 RectBounds.bottom += dc->ptlDCOrig.y;
822
823 pbrFill = BRUSH_ShareLockBrush(pdcattr->hbrush);
824 if (!pbrFill)
825 {
826 DPRINT1("FillRound Fail\n");
827 EngSetLastError(ERROR_INTERNAL_ERROR);
828 ret = FALSE;
829 }
830 else
831 {
832
833 DC_vPrepareDCsForBlit(dc, &RectBounds, NULL, NULL);
834
835 RtlCopyMemory(&brushTemp, pbrFill, sizeof(brushTemp));
836 brushTemp.ptOrigin.x += RectBounds.left - Left;
837 brushTemp.ptOrigin.y += RectBounds.top - Top;
838 ret = IntFillRoundRect( dc,
839 RectBounds.left,
840 RectBounds.top,
841 RectBounds.right,
842 RectBounds.bottom,
843 xCurveDiameter,
844 yCurveDiameter,
845 &brushTemp);
846 BRUSH_ShareUnlockBrush(pbrFill);
847
848 if (ret)
849 {
850 ret = IntDrawRoundRect( dc,
851 RectBounds.left,
852 RectBounds.top,
853 RectBounds.right,
854 RectBounds.bottom,
855 xCurveDiameter,
856 yCurveDiameter,
857 pbrLine);
858 }
859
860 DC_vFinishBlit(dc, NULL);
861 }
862
863
864 pbrLine->lWidth = PenOrigWidth;
865 PEN_ShareUnlockPen(pbrLine);
866 return ret;
867 }
868
869 BOOL
870 APIENTRY
871 NtGdiRoundRect(
872 HDC hDC,
873 int LeftRect,
874 int TopRect,
875 int RightRect,
876 int BottomRect,
877 int Width,
878 int Height)
879 {
880 DC *dc = DC_LockDc(hDC);
881 BOOL ret = FALSE; /* Default to failure */
882
883 DPRINT("NtGdiRoundRect(0x%p,%i,%i,%i,%i,%i,%i)\n",hDC,LeftRect,TopRect,RightRect,BottomRect,Width,Height);
884 if ( !dc )
885 {
886 DPRINT1("NtGdiRoundRect() - hDC is invalid\n");
887 EngSetLastError(ERROR_INVALID_HANDLE);
888 }
889 else
890 {
891 ret = IntRoundRect ( dc, LeftRect, TopRect, RightRect, BottomRect, Width, Height );
892 DC_UnlockDc ( dc );
893 }
894
895 return ret;
896 }
897
898 BOOL
899 NTAPI
900 GreGradientFill(
901 HDC hdc,
902 PTRIVERTEX pVertex,
903 ULONG nVertex,
904 PVOID pMesh,
905 ULONG nMesh,
906 ULONG ulMode)
907 {
908 PDC pdc;
909 SURFACE *psurf;
910 EXLATEOBJ exlo;
911 RECTL rclExtent;
912 POINTL ptlDitherOrg;
913 ULONG i;
914 BOOL bRet;
915
916 /* Check parameters */
917 if (ulMode & GRADIENT_FILL_TRIANGLE)
918 {
919 PGRADIENT_TRIANGLE pTriangle = (PGRADIENT_TRIANGLE)pMesh;
920
921 for (i = 0; i < nMesh; i++, pTriangle++)
922 {
923 if (pTriangle->Vertex1 >= nVertex ||
924 pTriangle->Vertex2 >= nVertex ||
925 pTriangle->Vertex3 >= nVertex)
926 {
927 EngSetLastError(ERROR_INVALID_PARAMETER);
928 return FALSE;
929 }
930 }
931 }
932 else
933 {
934 PGRADIENT_RECT pRect = (PGRADIENT_RECT)pMesh;
935 for (i = 0; i < nMesh; i++, pRect++)
936 {
937 if (pRect->UpperLeft >= nVertex || pRect->LowerRight >= nVertex)
938 {
939 EngSetLastError(ERROR_INVALID_PARAMETER);
940 return FALSE;
941 }
942 }
943 }
944
945 /* Lock the output DC */
946 pdc = DC_LockDc(hdc);
947 if(!pdc)
948 {
949 EngSetLastError(ERROR_INVALID_HANDLE);
950 return FALSE;
951 }
952
953 if (!pdc->dclevel.pSurface)
954 {
955 /* Memory DC with no surface selected */
956 DC_UnlockDc(pdc);
957 return TRUE; // CHECKME
958 }
959
960 /* Calculate extent */
961 rclExtent.left = rclExtent.right = pVertex->x;
962 rclExtent.top = rclExtent.bottom = pVertex->y;
963 for (i = 0; i < nVertex; i++)
964 {
965 rclExtent.left = min(rclExtent.left, (pVertex + i)->x);
966 rclExtent.right = max(rclExtent.right, (pVertex + i)->x);
967 rclExtent.top = min(rclExtent.top, (pVertex + i)->y);
968 rclExtent.bottom = max(rclExtent.bottom, (pVertex + i)->y);
969 }
970 IntLPtoDP(pdc, (LPPOINT)&rclExtent, 2);
971
972 rclExtent.left += pdc->ptlDCOrig.x;
973 rclExtent.right += pdc->ptlDCOrig.x;
974 rclExtent.top += pdc->ptlDCOrig.y;
975 rclExtent.bottom += pdc->ptlDCOrig.y;
976
977 ptlDitherOrg.x = ptlDitherOrg.y = 0;
978 IntLPtoDP(pdc, (LPPOINT)&ptlDitherOrg, 1);
979
980 ptlDitherOrg.x += pdc->ptlDCOrig.x;
981 ptlDitherOrg.y += pdc->ptlDCOrig.y;
982
983 if (pdc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
984 {
985 IntUpdateBoundsRect(pdc, &rclExtent);
986 }
987
988 DC_vPrepareDCsForBlit(pdc, &rclExtent, NULL, NULL);
989
990 psurf = pdc->dclevel.pSurface;
991
992 EXLATEOBJ_vInitialize(&exlo, &gpalRGB, psurf->ppal, 0, 0, 0);
993
994 bRet = IntEngGradientFill(&psurf->SurfObj,
995 (CLIPOBJ *)&pdc->co,
996 &exlo.xlo,
997 pVertex,
998 nVertex,
999 pMesh,
1000 nMesh,
1001 &rclExtent,
1002 &ptlDitherOrg,
1003 ulMode);
1004
1005 EXLATEOBJ_vCleanup(&exlo);
1006 DC_vFinishBlit(pdc, NULL);
1007 DC_UnlockDc(pdc);
1008
1009 return bRet;
1010 }
1011
1012 BOOL
1013 APIENTRY
1014 NtGdiGradientFill(
1015 HDC hdc,
1016 PTRIVERTEX pVertex,
1017 ULONG nVertex,
1018 PVOID pMesh,
1019 ULONG nMesh,
1020 ULONG ulMode)
1021 {
1022 BOOL bRet;
1023 PTRIVERTEX SafeVertex;
1024 PVOID SafeMesh;
1025 ULONG cbVertex, cbMesh;
1026
1027 /* Validate parameters */
1028 if (!pVertex || !nVertex || !pMesh || !nMesh)
1029 {
1030 EngSetLastError(ERROR_INVALID_PARAMETER);
1031 return FALSE;
1032 }
1033
1034 switch (ulMode)
1035 {
1036 case GRADIENT_FILL_RECT_H:
1037 case GRADIENT_FILL_RECT_V:
1038 cbMesh = nMesh * sizeof(GRADIENT_RECT);
1039 break;
1040 case GRADIENT_FILL_TRIANGLE:
1041 cbMesh = nMesh * sizeof(GRADIENT_TRIANGLE);
1042 break;
1043 default:
1044 EngSetLastError(ERROR_INVALID_PARAMETER);
1045 return FALSE;
1046 }
1047
1048 cbVertex = nVertex * sizeof(TRIVERTEX) ;
1049 if(cbVertex + cbMesh <= cbVertex)
1050 {
1051 /* Overflow */
1052 return FALSE ;
1053 }
1054
1055 /* Allocate a kernel mode buffer */
1056 SafeVertex = ExAllocatePoolWithTag(PagedPool, cbVertex + cbMesh, TAG_SHAPE);
1057 if(!SafeVertex)
1058 {
1059 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1060 return FALSE;
1061 }
1062
1063 SafeMesh = (PVOID)((ULONG_PTR)SafeVertex + cbVertex);
1064
1065 /* Copy the parameters to kernel mode */
1066 _SEH2_TRY
1067 {
1068 ProbeForRead(pVertex, cbVertex, 1);
1069 ProbeForRead(pMesh, cbMesh, 1);
1070 RtlCopyMemory(SafeVertex, pVertex, cbVertex);
1071 RtlCopyMemory(SafeMesh, pMesh, cbMesh);
1072 }
1073 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1074 {
1075 ExFreePoolWithTag(SafeVertex, TAG_SHAPE);
1076 SetLastNtError(_SEH2_GetExceptionCode());
1077 _SEH2_YIELD(return FALSE;)
1078 }
1079 _SEH2_END;
1080
1081 /* Call the internal function */
1082 bRet = GreGradientFill(hdc, SafeVertex, nVertex, SafeMesh, nMesh, ulMode);
1083
1084 /* Cleanup and return result */
1085 ExFreePoolWithTag(SafeVertex, TAG_SHAPE);
1086 return bRet;
1087 }
1088
1089 BOOL APIENTRY
1090 NtGdiExtFloodFill(
1091 HDC hDC,
1092 INT XStart,
1093 INT YStart,
1094 COLORREF Color,
1095 UINT FillType)
1096 {
1097 PDC dc;
1098 #if 0
1099 PDC_ATTR pdcattr;
1100 #endif
1101 SURFACE *psurf;
1102 EXLATEOBJ exlo;
1103 BOOL Ret = FALSE;
1104 RECTL DestRect;
1105 POINTL Pt;
1106 ULONG ConvColor;
1107
1108 dc = DC_LockDc(hDC);
1109 if (!dc)
1110 {
1111 EngSetLastError(ERROR_INVALID_HANDLE);
1112 return FALSE;
1113 }
1114
1115 if (!dc->dclevel.pSurface)
1116 {
1117 Ret = TRUE;
1118 goto cleanup;
1119 }
1120
1121 #if 0
1122 pdcattr = dc->pdcattr;
1123 #endif
1124
1125 Pt.x = XStart;
1126 Pt.y = YStart;
1127 IntLPtoDP(dc, (LPPOINT)&Pt, 1);
1128
1129 DC_vPrepareDCsForBlit(dc, &DestRect, NULL, NULL);
1130
1131 /// FIXME: what about prgnVIS? And what about REAL clipping?
1132 psurf = dc->dclevel.pSurface;
1133 if (dc->prgnRao)
1134 {
1135 Ret = REGION_PtInRegion(dc->prgnRao, Pt.x, Pt.y);
1136 if (Ret)
1137 REGION_GetRgnBox(dc->prgnRao, (LPRECT)&DestRect);
1138 else
1139 {
1140 DC_vFinishBlit(dc, NULL);
1141 goto cleanup;
1142 }
1143 }
1144 else
1145 {
1146 RECTL_vSetRect(&DestRect, 0, 0, psurf->SurfObj.sizlBitmap.cx, psurf->SurfObj.sizlBitmap.cy);
1147 }
1148
1149 if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
1150 {
1151 IntUpdateBoundsRect(dc, &DestRect);
1152 }
1153
1154 EXLATEOBJ_vInitialize(&exlo, &gpalRGB, psurf->ppal, 0, 0xffffff, 0);
1155
1156 /* Only solid fills supported for now
1157 * How to support pattern brushes and non standard surfaces (not offering dib functions):
1158 * Version a (most likely slow): call DrvPatBlt for every pixel
1159 * Version b: create a flood mask and let MaskBlt blit a masked brush */
1160 ConvColor = XLATEOBJ_iXlate(&exlo.xlo, Color);
1161 Ret = DIB_XXBPP_FloodFillSolid(&psurf->SurfObj, &dc->eboFill.BrushObject, &DestRect, &Pt, ConvColor, FillType);
1162
1163 DC_vFinishBlit(dc, NULL);
1164
1165 EXLATEOBJ_vCleanup(&exlo);
1166
1167 cleanup:
1168 DC_UnlockDc(dc);
1169 return Ret;
1170 }
1171
1172 /* EOF */