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