[NtGDI]
[reactos.git] / reactos / 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 DPRINT1("Bounds dc %p l %d t %d\n",dc,Bounds.left,Bounds.top);
198 DPRINT1(" 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 &dc->co.ClipObj,
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 FALSE;
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 &dc->co.ClipObj,
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 if (dc->dctype == DC_TYPE_INFO)
444 {
445 DC_UnlockDc(dc);
446 /* Yes, Windows really returns TRUE in this case */
447 return TRUE;
448 }
449
450 rcLockRect.left = dc->pdcattr->ptlCurrent.x;
451 rcLockRect.top = dc->pdcattr->ptlCurrent.y;
452 rcLockRect.right = XEnd;
453 rcLockRect.bottom = YEnd;
454
455 IntLPtoDP(dc, &rcLockRect, 2);
456
457 /* The DCOrg is in device coordinates */
458 rcLockRect.left += dc->ptlDCOrig.x;
459 rcLockRect.top += dc->ptlDCOrig.y;
460 rcLockRect.right += dc->ptlDCOrig.x;
461 rcLockRect.bottom += dc->ptlDCOrig.y;
462
463 DC_vPrepareDCsForBlit(dc, &rcLockRect, NULL, NULL);
464
465 Ret = IntGdiLineTo(dc, XEnd, YEnd);
466
467 DC_vFinishBlit(dc, NULL);
468
469 DC_UnlockDc(dc);
470 return Ret;
471 }
472
473 // FIXME: This function is completely broken
474 BOOL
475 APIENTRY
476 NtGdiPolyDraw(
477 IN HDC hdc,
478 IN LPPOINT lppt,
479 IN LPBYTE lpbTypes,
480 IN ULONG cCount)
481 {
482 PDC dc;
483 PDC_ATTR pdcattr;
484 POINT bzr[4];
485 volatile PPOINT line_pts, line_pts_old, bzr_pts;
486 INT num_pts, num_bzr_pts, space, space_old, size;
487 ULONG i;
488 BOOL result = FALSE;
489
490 dc = DC_LockDc(hdc);
491 if (!dc) return FALSE;
492 pdcattr = dc->pdcattr;
493
494 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
495 DC_vUpdateFillBrush(dc);
496
497 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
498 DC_vUpdateLineBrush(dc);
499
500 if (!cCount)
501 {
502 DC_UnlockDc(dc);
503 return TRUE;
504 }
505
506 line_pts = NULL;
507 line_pts_old = NULL;
508 bzr_pts = NULL;
509
510 _SEH2_TRY
511 {
512 ProbeArrayForRead(lppt, sizeof(POINT), cCount, sizeof(LONG));
513 ProbeArrayForRead(lpbTypes, sizeof(BYTE), cCount, sizeof(BYTE));
514
515 if (PATH_IsPathOpen(dc->dclevel))
516 {
517 result = PATH_PolyDraw(dc, (const POINT *)lppt, (const BYTE *)lpbTypes, cCount);
518 _SEH2_LEAVE;
519 }
520
521 /* Check for valid point types */
522 for (i = 0; i < cCount; i++)
523 {
524 switch (lpbTypes[i])
525 {
526 case PT_MOVETO:
527 case PT_LINETO | PT_CLOSEFIGURE:
528 case PT_LINETO:
529 break;
530 case PT_BEZIERTO:
531 if((i + 2 < cCount) && (lpbTypes[i + 1] == PT_BEZIERTO) &&
532 ((lpbTypes[i + 2] & ~PT_CLOSEFIGURE) == PT_BEZIERTO))
533 {
534 i += 2;
535 break;
536 }
537 default:
538 _SEH2_LEAVE;
539 }
540 }
541
542 space = cCount + 300;
543 line_pts = ExAllocatePoolWithTag(PagedPool, space * sizeof(POINT), TAG_SHAPE);
544 if (line_pts == NULL)
545 {
546 result = FALSE;
547 _SEH2_LEAVE;
548 }
549
550 num_pts = 1;
551
552 line_pts[0].x = pdcattr->ptlCurrent.x;
553 line_pts[0].y = pdcattr->ptlCurrent.y;
554
555 for ( i = 0; i < cCount; i++ )
556 {
557 switch (lpbTypes[i])
558 {
559 case PT_MOVETO:
560 if (num_pts >= 2) IntGdiPolyline( dc, line_pts, num_pts );
561 num_pts = 0;
562 line_pts[num_pts++] = lppt[i];
563 break;
564 case PT_LINETO:
565 case (PT_LINETO | PT_CLOSEFIGURE):
566 line_pts[num_pts++] = lppt[i];
567 break;
568 case PT_BEZIERTO:
569 bzr[0].x = line_pts[num_pts - 1].x;
570 bzr[0].y = line_pts[num_pts - 1].y;
571 RtlCopyMemory( &bzr[1], &lppt[i], 3 * sizeof(POINT) );
572
573 if ((bzr_pts = GDI_Bezier( bzr, 4, &num_bzr_pts )))
574 {
575 size = num_pts + (cCount - i) + num_bzr_pts;
576 if (space < size)
577 {
578 space_old = space;
579 space = size * 2;
580 line_pts_old = line_pts;
581 line_pts = ExAllocatePoolWithTag(PagedPool, space * sizeof(POINT), TAG_SHAPE);
582 if (!line_pts) _SEH2_LEAVE;
583 RtlCopyMemory(line_pts, line_pts_old, space_old * sizeof(POINT));
584 ExFreePoolWithTag(line_pts_old, TAG_SHAPE);
585 line_pts_old = NULL;
586 }
587 RtlCopyMemory( &line_pts[num_pts], &bzr_pts[1], (num_bzr_pts - 1) * sizeof(POINT) );
588 num_pts += num_bzr_pts - 1;
589 ExFreePoolWithTag(bzr_pts, TAG_BEZIER);
590 bzr_pts = NULL;
591 }
592 i += 2;
593 break;
594 }
595 if (lpbTypes[i] & PT_CLOSEFIGURE) line_pts[num_pts++] = line_pts[0];
596 }
597
598 if (num_pts >= 2) IntGdiPolyline( dc, line_pts, num_pts );
599 IntGdiMoveToEx( dc, line_pts[num_pts - 1].x, line_pts[num_pts - 1].y, NULL );
600 result = TRUE;
601 }
602 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
603 {
604 SetLastNtError(_SEH2_GetExceptionCode());
605 }
606 _SEH2_END;
607
608 if (line_pts != NULL)
609 {
610 ExFreePoolWithTag(line_pts, TAG_SHAPE);
611 }
612
613 if ((line_pts_old != NULL) && (line_pts_old != line_pts))
614 {
615 ExFreePoolWithTag(line_pts_old, TAG_SHAPE);
616 }
617
618 if (bzr_pts != NULL)
619 {
620 ExFreePoolWithTag(bzr_pts, TAG_BEZIER);
621 }
622
623 DC_UnlockDc(dc);
624
625 return result;
626 }
627
628 /*
629 * @implemented
630 */
631 _Success_(return != FALSE)
632 BOOL
633 APIENTRY
634 NtGdiMoveTo(
635 IN HDC hdc,
636 IN INT x,
637 IN INT y,
638 OUT OPTIONAL LPPOINT pptOut)
639 {
640 PDC pdc;
641 BOOL Ret;
642 POINT Point;
643
644 pdc = DC_LockDc(hdc);
645 if (!pdc) return FALSE;
646
647 Ret = IntGdiMoveToEx(pdc, x, y, &Point);
648
649 if (Ret && pptOut)
650 {
651 _SEH2_TRY
652 {
653 ProbeForWrite(pptOut, sizeof(POINT), 1);
654 RtlCopyMemory(pptOut, &Point, sizeof(POINT));
655 }
656 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
657 {
658 SetLastNtError(_SEH2_GetExceptionCode());
659 Ret = FALSE; // CHECKME: is this correct?
660 }
661 _SEH2_END;
662 }
663
664 DC_UnlockDc(pdc);
665
666 return Ret;
667 }
668
669 /* EOF */