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