2 * PROJECT: ReactOS Win32k Subsystem
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: win32ss/gdi/ntgdi/line.c
5 * PURPOSE: Line functions
14 // Some code from the WINE project source (www.winehq.com)
17 AddPenLinesBounds(PDC dc
, int count
, POINT
*points
)
24 /* Get BRUSH from current pen. */
25 pbrLine
= dc
->dclevel
.pbrLine
;
31 bounds
.left
= bounds
.top
= INT_MAX
;
32 bounds
.right
= bounds
.bottom
= INT_MIN
;
34 if (((pbrLine
->ulPenStyle
& PS_TYPE_MASK
) & PS_GEOMETRIC
) || pbrLine
->lWidth
> 1)
36 /* Windows uses some heuristics to estimate the distance from the point that will be painted */
37 lWidth
= pbrLine
->lWidth
+ 2;
38 endcap
= (PS_ENDCAP_MASK
& pbrLine
->ulPenStyle
);
39 join
= (PS_JOIN_MASK
& pbrLine
->ulPenStyle
);
40 if (join
== PS_JOIN_MITER
)
43 if (endcap
== PS_ENDCAP_SQUARE
) lWidth
= (lWidth
* 3 + 1) / 2;
47 if (endcap
== PS_ENDCAP_SQUARE
) lWidth
-= lWidth
/ 4;
48 else lWidth
= (lWidth
+ 1) / 2;
54 rect
.left
= points
->x
- lWidth
;
55 rect
.top
= points
->y
- lWidth
;
56 rect
.right
= points
->x
+ lWidth
+ 1;
57 rect
.bottom
= points
->y
+ lWidth
+ 1;
58 RECTL_bUnionRect(&bounds
, &bounds
, &rect
);
62 DPRINT("APLB dc %p l %d t %d\n",dc
,rect
.left
,rect
.top
);
63 DPRINT(" r %d b %d\n",rect
.right
,rect
.bottom
);
66 RECTL rcRgn
= dc
->erclClip
; // Use the clip box for now.
68 if (RECTL_bIntersectRect( &rcRgn
, &rcRgn
, &bounds
))
69 IntUpdateBoundsRect(dc
, &rcRgn
);
71 IntUpdateBoundsRect(dc
, &bounds
);
75 // Should use Fx in Point
78 IntGdiMoveToEx(DC
*dc
,
83 PDC_ATTR pdcattr
= dc
->pdcattr
;
86 if ( pdcattr
->ulDirty_
& DIRTY_PTLCURRENT
) // Double hit!
88 Point
->x
= pdcattr
->ptfxCurrent
.x
; // ret prev before change.
89 Point
->y
= pdcattr
->ptfxCurrent
.y
;
90 IntDPtoLP ( dc
, Point
, 1); // Reconvert back.
94 Point
->x
= pdcattr
->ptlCurrent
.x
;
95 Point
->y
= pdcattr
->ptlCurrent
.y
;
98 pdcattr
->ptlCurrent
.x
= X
;
99 pdcattr
->ptlCurrent
.y
= Y
;
100 pdcattr
->ptfxCurrent
= pdcattr
->ptlCurrent
;
101 CoordLPtoDP(dc
, &pdcattr
->ptfxCurrent
); // Update fx
102 pdcattr
->ulDirty_
&= ~(DIRTY_PTLCURRENT
|DIRTY_PTFXCURRENT
|DIRTY_STYLESTATE
);
115 if (!(dc
= DC_LockDc(hdc
)))
117 EngSetLastError(ERROR_INVALID_HANDLE
);
120 Ret
= IntGdiMoveToEx(dc
, x
, y
, pptOut
);
125 // Should use Fx in pt
128 IntGetCurrentPositionEx(PDC dc
, LPPOINT pt
)
130 PDC_ATTR pdcattr
= dc
->pdcattr
;
134 if (pdcattr
->ulDirty_
& DIRTY_PTFXCURRENT
)
136 pdcattr
->ptfxCurrent
= pdcattr
->ptlCurrent
;
137 CoordLPtoDP(dc
, &pdcattr
->ptfxCurrent
); // Update fx
138 pdcattr
->ulDirty_
&= ~(DIRTY_PTFXCURRENT
|DIRTY_STYLESTATE
);
140 pt
->x
= pdcattr
->ptlCurrent
.x
;
141 pt
->y
= pdcattr
->ptlCurrent
.y
;
158 ASSERT_DC_PREPARED(dc
);
160 pdcattr
= dc
->pdcattr
;
162 if (PATH_IsPathOpen(dc
->dclevel
))
164 Ret
= PATH_LineTo(dc
, XEnd
, YEnd
);
168 psurf
= dc
->dclevel
.pSurface
;
171 EngSetLastError(ERROR_INVALID_HANDLE
);
175 Points
[0].x
= pdcattr
->ptlCurrent
.x
;
176 Points
[0].y
= pdcattr
->ptlCurrent
.y
;
180 IntLPtoDP(dc
, Points
, 2);
182 /* The DCOrg is in device coordinates */
183 Points
[0].x
+= dc
->ptlDCOrig
.x
;
184 Points
[0].y
+= dc
->ptlDCOrig
.y
;
185 Points
[1].x
+= dc
->ptlDCOrig
.x
;
186 Points
[1].y
+= dc
->ptlDCOrig
.y
;
188 Bounds
.left
= min(Points
[0].x
, Points
[1].x
);
189 Bounds
.top
= min(Points
[0].y
, Points
[1].y
);
190 Bounds
.right
= max(Points
[0].x
, Points
[1].x
);
191 Bounds
.bottom
= max(Points
[0].y
, Points
[1].y
);
193 /* Get BRUSH from current pen. */
194 pbrLine
= dc
->dclevel
.pbrLine
;
197 if (dc
->fs
& (DC_ACCUM_APP
|DC_ACCUM_WMGR
))
199 DPRINT("Bounds dc %p l %d t %d\n",dc
,Bounds
.left
,Bounds
.top
);
200 DPRINT(" r %d b %d\n",Bounds
.right
,Bounds
.bottom
);
201 AddPenLinesBounds(dc
, 2, Points
);
204 if (!(pbrLine
->flAttrs
& BR_IS_NULL
))
206 if (IntIsEffectiveWidePen(pbrLine
))
209 PATH_Delete(dc
->dclevel
.hPath
);
210 dc
->dclevel
.hPath
= NULL
;
213 pPath
= PATH_CreatePath(2);
214 dc
->dclevel
.flPath
|= DCPATH_ACTIVE
;
215 dc
->dclevel
.hPath
= pPath
->BaseObject
.hHmgr
;
216 IntGetCurrentPositionEx(dc
, &pPath
->pos
);
217 IntLPtoDP(dc
, &pPath
->pos
, 1);
219 PATH_MoveTo(dc
, pPath
);
220 PATH_LineTo(dc
, XEnd
, YEnd
);
223 pPath
->state
= PATH_Closed
;
224 dc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
226 /* Actually stroke a path */
227 Ret
= PATH_StrokePath(dc
, pPath
);
230 PATH_UnlockPath(pPath
);
231 PATH_Delete(dc
->dclevel
.hPath
);
232 dc
->dclevel
.hPath
= NULL
;
236 Ret
= IntEngLineTo(&psurf
->SurfObj
,
238 &dc
->eboLine
.BrushObject
,
239 Points
[0].x
, Points
[0].y
,
240 Points
[1].x
, Points
[1].y
,
242 ROP2_TO_MIX(pdcattr
->jROP2
));
249 pdcattr
->ptlCurrent
.x
= XEnd
;
250 pdcattr
->ptlCurrent
.y
= YEnd
;
251 pdcattr
->ptfxCurrent
= pdcattr
->ptlCurrent
;
252 CoordLPtoDP(dc
, &pdcattr
->ptfxCurrent
); // Update fx
253 pdcattr
->ulDirty_
&= ~(DIRTY_PTLCURRENT
|DIRTY_PTFXCURRENT
|DIRTY_STYLESTATE
);
260 IntGdiPolyBezier(DC
*dc
,
264 BOOL ret
= FALSE
; // Default to FAILURE
266 if ( PATH_IsPathOpen(dc
->dclevel
) )
268 return PATH_PolyBezier ( dc
, pt
, Count
);
271 /* We'll convert it into line segments and draw them using Polyline */
276 Pts
= GDI_Bezier ( pt
, Count
, &nOut
);
279 ret
= IntGdiPolyline(dc
, Pts
, nOut
);
280 ExFreePoolWithTag(Pts
, TAG_BEZIER
);
288 IntGdiPolyBezierTo(DC
*dc
,
292 BOOL ret
= FALSE
; // Default to failure
293 PDC_ATTR pdcattr
= dc
->pdcattr
;
295 if ( PATH_IsPathOpen(dc
->dclevel
) )
296 ret
= PATH_PolyBezierTo ( dc
, pt
, Count
);
297 else /* We'll do it using PolyBezier */
300 npt
= ExAllocatePoolWithTag(PagedPool
,
301 sizeof(POINT
) * (Count
+ 1),
305 npt
[0].x
= pdcattr
->ptlCurrent
.x
;
306 npt
[0].y
= pdcattr
->ptlCurrent
.y
;
307 memcpy(npt
+ 1, pt
, sizeof(POINT
) * Count
);
308 ret
= IntGdiPolyBezier(dc
, npt
, Count
+1);
309 ExFreePoolWithTag(npt
, TAG_BEZIER
);
314 pdcattr
->ptlCurrent
.x
= pt
[Count
-1].x
;
315 pdcattr
->ptlCurrent
.y
= pt
[Count
-1].y
;
316 pdcattr
->ptfxCurrent
= pdcattr
->ptlCurrent
;
317 CoordLPtoDP(dc
, &pdcattr
->ptfxCurrent
); // Update fx
318 pdcattr
->ulDirty_
&= ~(DIRTY_PTLCURRENT
|DIRTY_PTFXCURRENT
|DIRTY_STYLESTATE
);
325 IntGdiPolyline(DC
*dc
,
334 PDC_ATTR pdcattr
= dc
->pdcattr
;
337 if (!dc
->dclevel
.pSurface
)
342 DC_vPrepareDCsForBlit(dc
, NULL
, NULL
, NULL
);
343 psurf
= dc
->dclevel
.pSurface
;
345 /* Get BRUSHOBJ from current pen. */
346 pbrLine
= dc
->dclevel
.pbrLine
;
349 if (!(pbrLine
->flAttrs
& BR_IS_NULL
))
351 Points
= EngAllocMem(0, Count
* sizeof(POINT
), GDITAG_TEMP
);
354 RtlCopyMemory(Points
, pt
, Count
* sizeof(POINT
));
355 IntLPtoDP(dc
, Points
, Count
);
357 /* Offset the array of points by the DC origin */
358 for (i
= 0; i
< Count
; i
++)
360 Points
[i
].x
+= dc
->ptlDCOrig
.x
;
361 Points
[i
].y
+= dc
->ptlDCOrig
.y
;
364 if (dc
->fs
& (DC_ACCUM_APP
|DC_ACCUM_WMGR
))
366 AddPenLinesBounds(dc
, Count
, Points
);
369 if (IntIsEffectiveWidePen(pbrLine
))
372 PATH_Delete(dc
->dclevel
.hPath
);
373 dc
->dclevel
.hPath
= NULL
;
376 pPath
= PATH_CreatePath(Count
);
377 dc
->dclevel
.flPath
|= DCPATH_ACTIVE
;
378 dc
->dclevel
.hPath
= pPath
->BaseObject
.hHmgr
;
380 IntLPtoDP(dc
, &pPath
->pos
, 1);
382 PATH_MoveTo(dc
, pPath
);
383 for (i
= 1; i
< Count
; ++i
)
385 PATH_LineTo(dc
, pt
[i
].x
, pt
[i
].y
);
389 pPath
->state
= PATH_Closed
;
390 dc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
392 /* Actually stroke a path */
393 Ret
= PATH_StrokePath(dc
, pPath
);
396 PATH_UnlockPath(pPath
);
397 PATH_Delete(dc
->dclevel
.hPath
);
398 dc
->dclevel
.hPath
= NULL
;
402 Ret
= IntEngPolyline(&psurf
->SurfObj
,
404 &dc
->eboLine
.BrushObject
,
407 ROP2_TO_MIX(pdcattr
->jROP2
));
417 DC_vFinishBlit(dc
, NULL
);
423 IntGdiPolylineTo(DC
*dc
,
427 BOOL ret
= FALSE
; // Default to failure
428 PDC_ATTR pdcattr
= dc
->pdcattr
;
430 if (PATH_IsPathOpen(dc
->dclevel
))
432 ret
= PATH_PolylineTo(dc
, pt
, Count
);
434 else /* Do it using Polyline */
436 POINT
*pts
= ExAllocatePoolWithTag(PagedPool
,
437 sizeof(POINT
) * (Count
+ 1),
441 pts
[0].x
= pdcattr
->ptlCurrent
.x
;
442 pts
[0].y
= pdcattr
->ptlCurrent
.y
;
443 memcpy( pts
+ 1, pt
, sizeof(POINT
) * Count
);
444 ret
= IntGdiPolyline(dc
, pts
, Count
+ 1);
445 ExFreePoolWithTag(pts
, TAG_SHAPE
);
450 pdcattr
->ptlCurrent
.x
= pt
[Count
-1].x
;
451 pdcattr
->ptlCurrent
.y
= pt
[Count
-1].y
;
452 pdcattr
->ptfxCurrent
= pdcattr
->ptlCurrent
;
453 CoordLPtoDP(dc
, &pdcattr
->ptfxCurrent
); // Update fx
454 pdcattr
->ulDirty_
&= ~(DIRTY_PTLCURRENT
|DIRTY_PTFXCURRENT
|DIRTY_STYLESTATE
);
462 IntGdiPolyPolyline(DC
*dc
,
470 BOOL ret
= FALSE
; // Default to failure
474 if (PATH_IsPathOpen(dc
->dclevel
))
476 return PATH_PolyPolyline( dc
, pt
, PolyPoints
, Count
);
478 for (i
= 0; i
< Count
; i
++)
480 ret
= IntGdiPolyline ( dc
, pts
, *pc
);
491 /******************************************************************************/
506 EngSetLastError(ERROR_INVALID_HANDLE
);
510 rcLockRect
.left
= dc
->pdcattr
->ptlCurrent
.x
;
511 rcLockRect
.top
= dc
->pdcattr
->ptlCurrent
.y
;
512 rcLockRect
.right
= XEnd
;
513 rcLockRect
.bottom
= YEnd
;
515 IntLPtoDP(dc
, (PPOINT
)&rcLockRect
, 2);
517 /* The DCOrg is in device coordinates */
518 rcLockRect
.left
+= dc
->ptlDCOrig
.x
;
519 rcLockRect
.top
+= dc
->ptlDCOrig
.y
;
520 rcLockRect
.right
+= dc
->ptlDCOrig
.x
;
521 rcLockRect
.bottom
+= dc
->ptlDCOrig
.y
;
523 DC_vPrepareDCsForBlit(dc
, &rcLockRect
, NULL
, NULL
);
525 Ret
= IntGdiLineTo(dc
, XEnd
, YEnd
);
527 DC_vFinishBlit(dc
, NULL
);
533 // FIXME: This function is completely broken
545 volatile PPOINT line_pts
, line_pts_old
, bzr_pts
;
546 INT num_pts
, num_bzr_pts
, space
, space_old
, size
;
551 if (!dc
) return FALSE
;
552 pdcattr
= dc
->pdcattr
;
554 if (pdcattr
->ulDirty_
& (DIRTY_FILL
| DC_BRUSH_DIRTY
))
555 DC_vUpdateFillBrush(dc
);
557 if (pdcattr
->ulDirty_
& (DIRTY_LINE
| DC_PEN_DIRTY
))
558 DC_vUpdateLineBrush(dc
);
572 ProbeArrayForRead(lppt
, sizeof(POINT
), cCount
, sizeof(LONG
));
573 ProbeArrayForRead(lpbTypes
, sizeof(BYTE
), cCount
, sizeof(BYTE
));
575 if (PATH_IsPathOpen(dc
->dclevel
))
577 result
= PATH_PolyDraw(dc
, (const POINT
*)lppt
, (const BYTE
*)lpbTypes
, cCount
);
581 /* Check for valid point types */
582 for (i
= 0; i
< cCount
; i
++)
587 case PT_LINETO
| PT_CLOSEFIGURE
:
591 if((i
+ 2 < cCount
) && (lpbTypes
[i
+ 1] == PT_BEZIERTO
) &&
592 ((lpbTypes
[i
+ 2] & ~PT_CLOSEFIGURE
) == PT_BEZIERTO
))
602 space
= cCount
+ 300;
603 line_pts
= ExAllocatePoolWithTag(PagedPool
, space
* sizeof(POINT
), TAG_SHAPE
);
604 if (line_pts
== NULL
)
612 line_pts
[0].x
= pdcattr
->ptlCurrent
.x
;
613 line_pts
[0].y
= pdcattr
->ptlCurrent
.y
;
615 for ( i
= 0; i
< cCount
; i
++ )
620 if (num_pts
>= 2) IntGdiPolyline( dc
, line_pts
, num_pts
);
622 line_pts
[num_pts
++] = lppt
[i
];
625 case (PT_LINETO
| PT_CLOSEFIGURE
):
626 line_pts
[num_pts
++] = lppt
[i
];
629 bzr
[0].x
= line_pts
[num_pts
- 1].x
;
630 bzr
[0].y
= line_pts
[num_pts
- 1].y
;
631 RtlCopyMemory( &bzr
[1], &lppt
[i
], 3 * sizeof(POINT
) );
633 if ((bzr_pts
= GDI_Bezier( bzr
, 4, &num_bzr_pts
)))
635 size
= num_pts
+ (cCount
- i
) + num_bzr_pts
;
640 line_pts_old
= line_pts
;
641 line_pts
= ExAllocatePoolWithTag(PagedPool
, space
* sizeof(POINT
), TAG_SHAPE
);
642 if (!line_pts
) _SEH2_LEAVE
;
643 RtlCopyMemory(line_pts
, line_pts_old
, space_old
* sizeof(POINT
));
644 ExFreePoolWithTag(line_pts_old
, TAG_SHAPE
);
647 RtlCopyMemory( &line_pts
[num_pts
], &bzr_pts
[1], (num_bzr_pts
- 1) * sizeof(POINT
) );
648 num_pts
+= num_bzr_pts
- 1;
649 ExFreePoolWithTag(bzr_pts
, TAG_BEZIER
);
655 if (lpbTypes
[i
] & PT_CLOSEFIGURE
) line_pts
[num_pts
++] = line_pts
[0];
658 if (num_pts
>= 2) IntGdiPolyline( dc
, line_pts
, num_pts
);
659 IntGdiMoveToEx( dc
, line_pts
[num_pts
- 1].x
, line_pts
[num_pts
- 1].y
, NULL
);
662 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
664 SetLastNtError(_SEH2_GetExceptionCode());
668 if (line_pts
!= NULL
)
670 ExFreePoolWithTag(line_pts
, TAG_SHAPE
);
673 if ((line_pts_old
!= NULL
) && (line_pts_old
!= line_pts
))
675 ExFreePoolWithTag(line_pts_old
, TAG_SHAPE
);
680 ExFreePoolWithTag(bzr_pts
, TAG_BEZIER
);
691 _Success_(return != FALSE
)
698 OUT OPTIONAL LPPOINT pptOut
)
704 pdc
= DC_LockDc(hdc
);
705 if (!pdc
) return FALSE
;
707 Ret
= IntGdiMoveToEx(pdc
, x
, y
, &Point
);
713 ProbeForWrite(pptOut
, sizeof(POINT
), 1);
714 RtlCopyMemory(pptOut
, &Point
, sizeof(POINT
));
716 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
718 SetLastNtError(_SEH2_GetExceptionCode());
719 Ret
= FALSE
; // CHECKME: is this correct?