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