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