5e53b0b24b40dc44e97fd32aeabb2566cc7a0cec
[reactos.git] / win32ss / gdi / ntgdi / line.c
1 /*
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
6 * PROGRAMMERS: ...
7 */
8
9 #include <win32k.h>
10
11 #define NDEBUG
12 #include <debug.h>
13
14 // Some code from the WINE project source (www.winehq.com)
15
16 VOID FASTCALL
17 AddPenLinesBounds(PDC dc, int count, POINT *points)
18 {
19 DWORD join, endcap;
20 RECTL bounds, rect;
21 LONG lWidth;
22 PBRUSH pbrLine;
23
24 /* Get BRUSH from current pen. */
25 pbrLine = dc->dclevel.pbrLine;
26 ASSERT(pbrLine);
27
28 lWidth = pbrLine->lWidth;
29
30 // Setup bounds
31 bounds.left = bounds.top = INT_MAX;
32 bounds.right = bounds.bottom = INT_MIN;
33
34 if (((pbrLine->ulPenStyle & PS_TYPE_MASK) & PS_GEOMETRIC) || lWidth > 1)
35 {
36 /* Windows uses some heuristics to estimate the distance from the point that will be painted */
37 lWidth = lWidth + 2;
38 endcap = (PS_ENDCAP_MASK & pbrLine->ulPenStyle);
39 join = (PS_JOIN_MASK & pbrLine->ulPenStyle);
40 if (join == PS_JOIN_MITER)
41 {
42 lWidth *= 5;
43 if (endcap == PS_ENDCAP_SQUARE) lWidth = (lWidth * 3 + 1) / 2;
44 }
45 else
46 {
47 if (endcap == PS_ENDCAP_SQUARE) lWidth -= lWidth / 4;
48 else lWidth = (lWidth + 1) / 2;
49 }
50 }
51
52 while (count-- > 0)
53 {
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);
59 points++;
60 }
61
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);
64
65 {
66 RECTL rcRgn;
67 if (dc->fs & DC_FLAG_DIRTY_RAO) CLIPPING_UpdateGCRegion(dc);
68 if (REGION_GetRgnBox(dc->prgnRao, &rcRgn))
69 {
70 if (RECTL_bIntersectRect( &rcRgn, &rcRgn, &bounds )) IntUpdateBoundsRect(dc, &rcRgn);
71 }
72 else
73 IntUpdateBoundsRect(dc, &bounds);
74 }
75 }
76
77 // Should use Fx in Point
78 //
79 BOOL FASTCALL
80 IntGdiMoveToEx(DC *dc,
81 int X,
82 int Y,
83 LPPOINT Point)
84 {
85 PDC_ATTR pdcattr = dc->pdcattr;
86 if ( Point )
87 {
88 if ( pdcattr->ulDirty_ & DIRTY_PTLCURRENT ) // Double hit!
89 {
90 Point->x = pdcattr->ptfxCurrent.x; // ret prev before change.
91 Point->y = pdcattr->ptfxCurrent.y;
92 IntDPtoLP ( dc, Point, 1); // Reconvert back.
93 }
94 else
95 {
96 Point->x = pdcattr->ptlCurrent.x;
97 Point->y = pdcattr->ptlCurrent.y;
98 }
99 }
100 pdcattr->ptlCurrent.x = X;
101 pdcattr->ptlCurrent.y = Y;
102 pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
103 CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
104 pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
105
106 return TRUE;
107 }
108
109 BOOL FASTCALL
110 GreMoveTo( HDC hdc,
111 INT x,
112 INT y,
113 LPPOINT pptOut)
114 {
115 BOOL Ret;
116 PDC dc;
117 if (!(dc = DC_LockDc(hdc)))
118 {
119 EngSetLastError(ERROR_INVALID_HANDLE);
120 return FALSE;
121 }
122 Ret = IntGdiMoveToEx(dc, x, y, pptOut);
123 DC_UnlockDc(dc);
124 return Ret;
125 }
126
127 // Should use Fx in pt
128 //
129 VOID FASTCALL
130 IntGetCurrentPositionEx(PDC dc, LPPOINT pt)
131 {
132 PDC_ATTR pdcattr = dc->pdcattr;
133
134 if ( pt )
135 {
136 if (pdcattr->ulDirty_ & DIRTY_PTFXCURRENT)
137 {
138 pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
139 CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
140 pdcattr->ulDirty_ &= ~(DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
141 }
142 pt->x = pdcattr->ptlCurrent.x;
143 pt->y = pdcattr->ptlCurrent.y;
144 }
145 }
146
147 BOOL FASTCALL
148 IntGdiLineTo(DC *dc,
149 int XEnd,
150 int YEnd)
151 {
152 SURFACE *psurf;
153 BOOL Ret = TRUE;
154 PBRUSH pbrLine;
155 RECTL Bounds;
156 POINT Points[2];
157 PDC_ATTR pdcattr = dc->pdcattr;
158 ASSERT_DC_PREPARED(dc);
159
160 if (PATH_IsPathOpen(dc->dclevel))
161 {
162 Ret = PATH_LineTo(dc, XEnd, YEnd);
163 }
164 else
165 {
166 psurf = dc->dclevel.pSurface;
167 if (NULL == psurf)
168 {
169 EngSetLastError(ERROR_INVALID_HANDLE);
170 return FALSE;
171 }
172
173 Points[0].x = pdcattr->ptlCurrent.x;
174 Points[0].y = pdcattr->ptlCurrent.y;
175 Points[1].x = XEnd;
176 Points[1].y = YEnd;
177
178 IntLPtoDP(dc, Points, 2);
179
180 /* The DCOrg is in device coordinates */
181 Points[0].x += dc->ptlDCOrig.x;
182 Points[0].y += dc->ptlDCOrig.y;
183 Points[1].x += dc->ptlDCOrig.x;
184 Points[1].y += dc->ptlDCOrig.y;
185
186 Bounds.left = min(Points[0].x, Points[1].x);
187 Bounds.top = min(Points[0].y, Points[1].y);
188 Bounds.right = max(Points[0].x, Points[1].x);
189 Bounds.bottom = max(Points[0].y, Points[1].y);
190
191 /* Get BRUSH from current pen. */
192 pbrLine = dc->dclevel.pbrLine;
193 ASSERT(pbrLine);
194
195 if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
196 {
197 DPRINT("Bounds dc %p l %d t %d\n",dc,Bounds.left,Bounds.top);
198 DPRINT(" r %d b %d\n",Bounds.right,Bounds.bottom);
199 AddPenLinesBounds(dc, 2, Points);
200 }
201
202 if (!(pbrLine->flAttrs & BR_IS_NULL))
203 {
204 Ret = IntEngLineTo(&psurf->SurfObj,
205 (CLIPOBJ *)&dc->co,
206 &dc->eboLine.BrushObject,
207 Points[0].x, Points[0].y,
208 Points[1].x, Points[1].y,
209 &Bounds,
210 ROP2_TO_MIX(pdcattr->jROP2));
211 }
212
213 }
214
215 if (Ret)
216 {
217 pdcattr->ptlCurrent.x = XEnd;
218 pdcattr->ptlCurrent.y = YEnd;
219 pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
220 CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
221 pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
222 }
223
224 return Ret;
225 }
226
227 BOOL FASTCALL
228 IntGdiPolyBezier(DC *dc,
229 LPPOINT pt,
230 DWORD Count)
231 {
232 BOOL ret = FALSE; // Default to FAILURE
233
234 if ( PATH_IsPathOpen(dc->dclevel) )
235 {
236 return PATH_PolyBezier ( dc, pt, Count );
237 }
238
239 /* We'll convert it into line segments and draw them using Polyline */
240 {
241 POINT *Pts;
242 INT nOut;
243
244 Pts = GDI_Bezier ( pt, Count, &nOut );
245 if ( Pts )
246 {
247 ret = IntGdiPolyline(dc, Pts, nOut);
248 ExFreePoolWithTag(Pts, TAG_BEZIER);
249 }
250 }
251
252 return ret;
253 }
254
255 BOOL FASTCALL
256 IntGdiPolyBezierTo(DC *dc,
257 LPPOINT pt,
258 DWORD Count)
259 {
260 BOOL ret = FALSE; // Default to failure
261 PDC_ATTR pdcattr = dc->pdcattr;
262
263 if ( PATH_IsPathOpen(dc->dclevel) )
264 ret = PATH_PolyBezierTo ( dc, pt, Count );
265 else /* We'll do it using PolyBezier */
266 {
267 POINT *npt;
268 npt = ExAllocatePoolWithTag(PagedPool,
269 sizeof(POINT) * (Count + 1),
270 TAG_BEZIER);
271 if ( npt )
272 {
273 npt[0].x = pdcattr->ptlCurrent.x;
274 npt[0].y = pdcattr->ptlCurrent.y;
275 memcpy(npt + 1, pt, sizeof(POINT) * Count);
276 ret = IntGdiPolyBezier(dc, npt, Count+1);
277 ExFreePoolWithTag(npt, TAG_BEZIER);
278 }
279 }
280 if ( ret )
281 {
282 pdcattr->ptlCurrent.x = pt[Count-1].x;
283 pdcattr->ptlCurrent.y = pt[Count-1].y;
284 pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
285 CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
286 pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
287 }
288
289 return ret;
290 }
291
292 BOOL FASTCALL
293 IntGdiPolyline(DC *dc,
294 LPPOINT pt,
295 int Count)
296 {
297 SURFACE *psurf;
298 BRUSH *pbrLine;
299 LPPOINT Points;
300 BOOL Ret = TRUE;
301 LONG i;
302 PDC_ATTR pdcattr = dc->pdcattr;
303
304 if (!dc->dclevel.pSurface)
305 {
306 return TRUE;
307 }
308
309 DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL);
310 psurf = dc->dclevel.pSurface;
311
312 /* Get BRUSHOBJ from current pen. */
313 pbrLine = dc->dclevel.pbrLine;
314 ASSERT(pbrLine);
315
316 if (!(pbrLine->flAttrs & BR_IS_NULL))
317 {
318 Points = EngAllocMem(0, Count * sizeof(POINT), GDITAG_TEMP);
319 if (Points != NULL)
320 {
321 RtlCopyMemory(Points, pt, Count * sizeof(POINT));
322 IntLPtoDP(dc, Points, Count);
323
324 /* Offset the array of points by the DC origin */
325 for (i = 0; i < Count; i++)
326 {
327 Points[i].x += dc->ptlDCOrig.x;
328 Points[i].y += dc->ptlDCOrig.y;
329 }
330
331 if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
332 {
333 AddPenLinesBounds(dc, Count, Points);
334 }
335
336 Ret = IntEngPolyline(&psurf->SurfObj,
337 (CLIPOBJ *)&dc->co,
338 &dc->eboLine.BrushObject,
339 Points,
340 Count,
341 ROP2_TO_MIX(pdcattr->jROP2));
342
343 EngFreeMem(Points);
344 }
345 else
346 {
347 Ret = FALSE;
348 }
349 }
350
351 DC_vFinishBlit(dc, NULL);
352
353 return Ret;
354 }
355
356 BOOL FASTCALL
357 IntGdiPolylineTo(DC *dc,
358 LPPOINT pt,
359 DWORD Count)
360 {
361 BOOL ret = FALSE; // Default to failure
362 PDC_ATTR pdcattr = dc->pdcattr;
363
364 if (PATH_IsPathOpen(dc->dclevel))
365 {
366 ret = PATH_PolylineTo(dc, pt, Count);
367 }
368 else /* Do it using Polyline */
369 {
370 POINT *pts = ExAllocatePoolWithTag(PagedPool,
371 sizeof(POINT) * (Count + 1),
372 TAG_SHAPE);
373 if ( pts )
374 {
375 pts[0].x = pdcattr->ptlCurrent.x;
376 pts[0].y = pdcattr->ptlCurrent.y;
377 memcpy( pts + 1, pt, sizeof(POINT) * Count);
378 ret = IntGdiPolyline(dc, pts, Count + 1);
379 ExFreePoolWithTag(pts, TAG_SHAPE);
380 }
381 }
382 if ( ret )
383 {
384 pdcattr->ptlCurrent.x = pt[Count-1].x;
385 pdcattr->ptlCurrent.y = pt[Count-1].y;
386 pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
387 CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
388 pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
389 }
390
391 return ret;
392 }
393
394
395 BOOL FASTCALL
396 IntGdiPolyPolyline(DC *dc,
397 LPPOINT pt,
398 PULONG PolyPoints,
399 DWORD Count)
400 {
401 ULONG i;
402 LPPOINT pts;
403 PULONG pc;
404 BOOL ret = FALSE; // Default to failure
405 pts = pt;
406 pc = PolyPoints;
407
408 if (PATH_IsPathOpen(dc->dclevel))
409 {
410 return PATH_PolyPolyline( dc, pt, PolyPoints, Count );
411 }
412 for (i = 0; i < Count; i++)
413 {
414 ret = IntGdiPolyline ( dc, pts, *pc );
415 if (ret == FALSE)
416 {
417 return ret;
418 }
419 pts+=*pc++;
420 }
421
422 return ret;
423 }
424
425 /******************************************************************************/
426
427 BOOL
428 APIENTRY
429 NtGdiLineTo(HDC hDC,
430 int XEnd,
431 int YEnd)
432 {
433 DC *dc;
434 BOOL Ret;
435 RECT rcLockRect ;
436
437 dc = DC_LockDc(hDC);
438 if (!dc)
439 {
440 EngSetLastError(ERROR_INVALID_HANDLE);
441 return FALSE;
442 }
443
444 rcLockRect.left = dc->pdcattr->ptlCurrent.x;
445 rcLockRect.top = dc->pdcattr->ptlCurrent.y;
446 rcLockRect.right = XEnd;
447 rcLockRect.bottom = YEnd;
448
449 IntLPtoDP(dc, &rcLockRect, 2);
450
451 /* The DCOrg is in device coordinates */
452 rcLockRect.left += dc->ptlDCOrig.x;
453 rcLockRect.top += dc->ptlDCOrig.y;
454 rcLockRect.right += dc->ptlDCOrig.x;
455 rcLockRect.bottom += dc->ptlDCOrig.y;
456
457 DC_vPrepareDCsForBlit(dc, &rcLockRect, NULL, NULL);
458
459 Ret = IntGdiLineTo(dc, XEnd, YEnd);
460
461 DC_vFinishBlit(dc, NULL);
462
463 DC_UnlockDc(dc);
464 return Ret;
465 }
466
467 // FIXME: This function is completely broken
468 BOOL
469 APIENTRY
470 NtGdiPolyDraw(
471 IN HDC hdc,
472 IN LPPOINT lppt,
473 IN LPBYTE lpbTypes,
474 IN ULONG cCount)
475 {
476 PDC dc;
477 PDC_ATTR pdcattr;
478 POINT bzr[4];
479 volatile PPOINT line_pts, line_pts_old, bzr_pts;
480 INT num_pts, num_bzr_pts, space, space_old, size;
481 ULONG i;
482 BOOL result = FALSE;
483
484 dc = DC_LockDc(hdc);
485 if (!dc) return FALSE;
486 pdcattr = dc->pdcattr;
487
488 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
489 DC_vUpdateFillBrush(dc);
490
491 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
492 DC_vUpdateLineBrush(dc);
493
494 if (!cCount)
495 {
496 DC_UnlockDc(dc);
497 return TRUE;
498 }
499
500 line_pts = NULL;
501 line_pts_old = NULL;
502 bzr_pts = NULL;
503
504 _SEH2_TRY
505 {
506 ProbeArrayForRead(lppt, sizeof(POINT), cCount, sizeof(LONG));
507 ProbeArrayForRead(lpbTypes, sizeof(BYTE), cCount, sizeof(BYTE));
508
509 if (PATH_IsPathOpen(dc->dclevel))
510 {
511 result = PATH_PolyDraw(dc, (const POINT *)lppt, (const BYTE *)lpbTypes, cCount);
512 _SEH2_LEAVE;
513 }
514
515 /* Check for valid point types */
516 for (i = 0; i < cCount; i++)
517 {
518 switch (lpbTypes[i])
519 {
520 case PT_MOVETO:
521 case PT_LINETO | PT_CLOSEFIGURE:
522 case PT_LINETO:
523 break;
524 case PT_BEZIERTO:
525 if((i + 2 < cCount) && (lpbTypes[i + 1] == PT_BEZIERTO) &&
526 ((lpbTypes[i + 2] & ~PT_CLOSEFIGURE) == PT_BEZIERTO))
527 {
528 i += 2;
529 break;
530 }
531 default:
532 _SEH2_LEAVE;
533 }
534 }
535
536 space = cCount + 300;
537 line_pts = ExAllocatePoolWithTag(PagedPool, space * sizeof(POINT), TAG_SHAPE);
538 if (line_pts == NULL)
539 {
540 result = FALSE;
541 _SEH2_LEAVE;
542 }
543
544 num_pts = 1;
545
546 line_pts[0].x = pdcattr->ptlCurrent.x;
547 line_pts[0].y = pdcattr->ptlCurrent.y;
548
549 for ( i = 0; i < cCount; i++ )
550 {
551 switch (lpbTypes[i])
552 {
553 case PT_MOVETO:
554 if (num_pts >= 2) IntGdiPolyline( dc, line_pts, num_pts );
555 num_pts = 0;
556 line_pts[num_pts++] = lppt[i];
557 break;
558 case PT_LINETO:
559 case (PT_LINETO | PT_CLOSEFIGURE):
560 line_pts[num_pts++] = lppt[i];
561 break;
562 case PT_BEZIERTO:
563 bzr[0].x = line_pts[num_pts - 1].x;
564 bzr[0].y = line_pts[num_pts - 1].y;
565 RtlCopyMemory( &bzr[1], &lppt[i], 3 * sizeof(POINT) );
566
567 if ((bzr_pts = GDI_Bezier( bzr, 4, &num_bzr_pts )))
568 {
569 size = num_pts + (cCount - i) + num_bzr_pts;
570 if (space < size)
571 {
572 space_old = space;
573 space = size * 2;
574 line_pts_old = line_pts;
575 line_pts = ExAllocatePoolWithTag(PagedPool, space * sizeof(POINT), TAG_SHAPE);
576 if (!line_pts) _SEH2_LEAVE;
577 RtlCopyMemory(line_pts, line_pts_old, space_old * sizeof(POINT));
578 ExFreePoolWithTag(line_pts_old, TAG_SHAPE);
579 line_pts_old = NULL;
580 }
581 RtlCopyMemory( &line_pts[num_pts], &bzr_pts[1], (num_bzr_pts - 1) * sizeof(POINT) );
582 num_pts += num_bzr_pts - 1;
583 ExFreePoolWithTag(bzr_pts, TAG_BEZIER);
584 bzr_pts = NULL;
585 }
586 i += 2;
587 break;
588 }
589 if (lpbTypes[i] & PT_CLOSEFIGURE) line_pts[num_pts++] = line_pts[0];
590 }
591
592 if (num_pts >= 2) IntGdiPolyline( dc, line_pts, num_pts );
593 IntGdiMoveToEx( dc, line_pts[num_pts - 1].x, line_pts[num_pts - 1].y, NULL );
594 result = TRUE;
595 }
596 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
597 {
598 SetLastNtError(_SEH2_GetExceptionCode());
599 }
600 _SEH2_END;
601
602 if (line_pts != NULL)
603 {
604 ExFreePoolWithTag(line_pts, TAG_SHAPE);
605 }
606
607 if ((line_pts_old != NULL) && (line_pts_old != line_pts))
608 {
609 ExFreePoolWithTag(line_pts_old, TAG_SHAPE);
610 }
611
612 if (bzr_pts != NULL)
613 {
614 ExFreePoolWithTag(bzr_pts, TAG_BEZIER);
615 }
616
617 DC_UnlockDc(dc);
618
619 return result;
620 }
621
622 /*
623 * @implemented
624 */
625 _Success_(return != FALSE)
626 BOOL
627 APIENTRY
628 NtGdiMoveTo(
629 IN HDC hdc,
630 IN INT x,
631 IN INT y,
632 OUT OPTIONAL LPPOINT pptOut)
633 {
634 PDC pdc;
635 BOOL Ret;
636 POINT Point;
637
638 pdc = DC_LockDc(hdc);
639 if (!pdc) return FALSE;
640
641 Ret = IntGdiMoveToEx(pdc, x, y, &Point);
642
643 if (Ret && pptOut)
644 {
645 _SEH2_TRY
646 {
647 ProbeForWrite(pptOut, sizeof(POINT), 1);
648 RtlCopyMemory(pptOut, &Point, sizeof(POINT));
649 }
650 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
651 {
652 SetLastNtError(_SEH2_GetExceptionCode());
653 Ret = FALSE; // CHECKME: is this correct?
654 }
655 _SEH2_END;
656 }
657
658 DC_UnlockDc(pdc);
659
660 return Ret;
661 }
662
663 /* EOF */