[BRANCHES]
[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: 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->rosdc.CombinedClip,
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, dc->rosdc.CombinedClip->rclBounds,
261 NULL, dc->rosdc.CombinedClip->rclBounds);
262
263 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
264 DC_vUpdateFillBrush(dc);
265
266 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
267 DC_vUpdateLineBrush(dc);
268
269 /* Get BRUSHOBJ from current pen. */
270 pbrLine = dc->dclevel.pbrLine;
271 ASSERT(pbrLine);
272
273 if (!(pbrLine->flAttrs & BR_IS_NULL))
274 {
275 Points = EngAllocMem(0, Count * sizeof(POINT), GDITAG_TEMP);
276 if (Points != NULL)
277 {
278 RtlCopyMemory(Points, pt, Count * sizeof(POINT));
279 IntLPtoDP(dc, Points, Count);
280
281 /* Offset the array of points by the DC origin */
282 for (i = 0; i < Count; i++)
283 {
284 Points[i].x += dc->ptlDCOrig.x;
285 Points[i].y += dc->ptlDCOrig.y;
286 }
287
288 Ret = IntEngPolyline(&psurf->SurfObj,
289 dc->rosdc.CombinedClip,
290 &dc->eboLine.BrushObject,
291 Points,
292 Count,
293 ROP2_TO_MIX(pdcattr->jROP2));
294
295 EngFreeMem(Points);
296 }
297 else
298 {
299 Ret = FALSE;
300 }
301 }
302
303 DC_vFinishBlit(dc, NULL);
304
305 return Ret;
306 }
307
308 BOOL FASTCALL
309 IntGdiPolylineTo(DC *dc,
310 LPPOINT pt,
311 DWORD Count)
312 {
313 BOOL ret = FALSE; // Default to failure
314 PDC_ATTR pdcattr = dc->pdcattr;
315
316 if (PATH_IsPathOpen(dc->dclevel))
317 {
318 ret = PATH_PolylineTo(dc, pt, Count);
319 }
320 else /* Do it using Polyline */
321 {
322 POINT *pts = ExAllocatePoolWithTag(PagedPool,
323 sizeof(POINT) * (Count + 1),
324 TAG_SHAPE);
325 if ( pts )
326 {
327 pts[0].x = pdcattr->ptlCurrent.x;
328 pts[0].y = pdcattr->ptlCurrent.y;
329 memcpy( pts + 1, pt, sizeof(POINT) * Count);
330 ret = IntGdiPolyline(dc, pts, Count + 1);
331 ExFreePoolWithTag(pts, TAG_SHAPE);
332 }
333 }
334 if ( ret )
335 {
336 pdcattr->ptlCurrent.x = pt[Count-1].x;
337 pdcattr->ptlCurrent.y = pt[Count-1].y;
338 pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
339 CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
340 pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
341 }
342
343 return ret;
344 }
345
346
347 BOOL FASTCALL
348 IntGdiPolyPolyline(DC *dc,
349 LPPOINT pt,
350 PULONG PolyPoints,
351 DWORD Count)
352 {
353 ULONG i;
354 LPPOINT pts;
355 PULONG pc;
356 BOOL ret = FALSE; // Default to failure
357 pts = pt;
358 pc = PolyPoints;
359
360 if (PATH_IsPathOpen(dc->dclevel))
361 return PATH_PolyPolyline( dc, pt, PolyPoints, Count );
362
363 for (i = 0; i < Count; i++)
364 {
365 ret = IntGdiPolyline ( dc, pts, *pc );
366 if (ret == FALSE)
367 {
368 return ret;
369 }
370 pts+=*pc++;
371 }
372
373 return ret;
374 }
375
376 /******************************************************************************/
377
378 BOOL
379 APIENTRY
380 NtGdiLineTo(HDC hDC,
381 int XEnd,
382 int YEnd)
383 {
384 DC *dc;
385 BOOL Ret;
386 RECT rcLockRect ;
387
388 dc = DC_LockDc(hDC);
389 if (!dc)
390 {
391 EngSetLastError(ERROR_INVALID_HANDLE);
392 return FALSE;
393 }
394 if (dc->dctype == DC_TYPE_INFO)
395 {
396 DC_UnlockDc(dc);
397 /* Yes, Windows really returns TRUE in this case */
398 return TRUE;
399 }
400
401 rcLockRect.left = dc->pdcattr->ptlCurrent.x;
402 rcLockRect.top = dc->pdcattr->ptlCurrent.y;
403 rcLockRect.right = XEnd;
404 rcLockRect.bottom = YEnd;
405
406 IntLPtoDP(dc, &rcLockRect, 2);
407
408 /* The DCOrg is in device coordinates */
409 rcLockRect.left += dc->ptlDCOrig.x;
410 rcLockRect.top += dc->ptlDCOrig.y;
411 rcLockRect.right += dc->ptlDCOrig.x;
412 rcLockRect.bottom += dc->ptlDCOrig.y;
413
414 DC_vPrepareDCsForBlit(dc, rcLockRect, NULL, rcLockRect);
415
416 if (dc->pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
417 DC_vUpdateLineBrush(dc);
418
419 Ret = IntGdiLineTo(dc, XEnd, YEnd);
420
421 DC_vFinishBlit(dc, NULL);
422
423 DC_UnlockDc(dc);
424 return Ret;
425 }
426
427 // FIXME: This function is completely broken
428 BOOL
429 APIENTRY
430 NtGdiPolyDraw(
431 IN HDC hdc,
432 IN LPPOINT lppt,
433 IN LPBYTE lpbTypes,
434 IN ULONG cCount)
435 {
436 PDC dc;
437 PDC_ATTR pdcattr;
438 POINT bzr[4];
439 volatile PPOINT line_pts, line_pts_old, bzr_pts;
440 INT num_pts, num_bzr_pts, space, space_old, size;
441 ULONG i;
442 BOOL result = FALSE;
443
444 dc = DC_LockDc(hdc);
445 if (!dc) return FALSE;
446 pdcattr = dc->pdcattr;
447
448 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
449 DC_vUpdateFillBrush(dc);
450
451 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
452 DC_vUpdateLineBrush(dc);
453
454 if (!cCount)
455 {
456 DC_UnlockDc(dc);
457 return TRUE;
458 }
459
460 line_pts = NULL;
461 line_pts_old = NULL;
462 bzr_pts = NULL;
463
464 _SEH2_TRY
465 {
466 ProbeArrayForRead(lppt, sizeof(POINT), cCount, sizeof(LONG));
467 ProbeArrayForRead(lpbTypes, sizeof(BYTE), cCount, sizeof(BYTE));
468
469 if (PATH_IsPathOpen(dc->dclevel))
470 {
471 result = PATH_PolyDraw(dc, (const POINT *)lppt, (const BYTE *)lpbTypes, cCount);
472 _SEH2_LEAVE;
473 }
474
475 /* Check for valid point types */
476 for (i = 0; i < cCount; i++)
477 {
478 switch (lpbTypes[i])
479 {
480 case PT_MOVETO:
481 case PT_LINETO | PT_CLOSEFIGURE:
482 case PT_LINETO:
483 break;
484 case PT_BEZIERTO:
485 if((i + 2 < cCount) && (lpbTypes[i + 1] == PT_BEZIERTO) &&
486 ((lpbTypes[i + 2] & ~PT_CLOSEFIGURE) == PT_BEZIERTO))
487 {
488 i += 2;
489 break;
490 }
491 default:
492 _SEH2_LEAVE;
493 }
494 }
495
496 space = cCount + 300;
497 line_pts = ExAllocatePoolWithTag(PagedPool, space * sizeof(POINT), TAG_SHAPE);
498 if (line_pts == NULL)
499 {
500 result = FALSE;
501 _SEH2_LEAVE;
502 }
503
504 num_pts = 1;
505
506 line_pts[0].x = pdcattr->ptlCurrent.x;
507 line_pts[0].y = pdcattr->ptlCurrent.y;
508
509 for ( i = 0; i < cCount; i++ )
510 {
511 switch (lpbTypes[i])
512 {
513 case PT_MOVETO:
514 if (num_pts >= 2) IntGdiPolyline( dc, line_pts, num_pts );
515 num_pts = 0;
516 line_pts[num_pts++] = lppt[i];
517 break;
518 case PT_LINETO:
519 case (PT_LINETO | PT_CLOSEFIGURE):
520 line_pts[num_pts++] = lppt[i];
521 break;
522 case PT_BEZIERTO:
523 bzr[0].x = line_pts[num_pts - 1].x;
524 bzr[0].y = line_pts[num_pts - 1].y;
525 RtlCopyMemory( &bzr[1], &lppt[i], 3 * sizeof(POINT) );
526
527 if ((bzr_pts = GDI_Bezier( bzr, 4, &num_bzr_pts )))
528 {
529 size = num_pts + (cCount - i) + num_bzr_pts;
530 if (space < size)
531 {
532 space_old = space;
533 space = size * 2;
534 line_pts_old = line_pts;
535 line_pts = ExAllocatePoolWithTag(PagedPool, space * sizeof(POINT), TAG_SHAPE);
536 if (!line_pts) _SEH2_LEAVE;
537 RtlCopyMemory(line_pts, line_pts_old, space_old * sizeof(POINT));
538 ExFreePoolWithTag(line_pts_old, TAG_SHAPE);
539 line_pts_old = NULL;
540 }
541 RtlCopyMemory( &line_pts[num_pts], &bzr_pts[1], (num_bzr_pts - 1) * sizeof(POINT) );
542 num_pts += num_bzr_pts - 1;
543 ExFreePoolWithTag(bzr_pts, TAG_BEZIER);
544 bzr_pts = NULL;
545 }
546 i += 2;
547 break;
548 }
549 if (lpbTypes[i] & PT_CLOSEFIGURE) line_pts[num_pts++] = line_pts[0];
550 }
551
552 if (num_pts >= 2) IntGdiPolyline( dc, line_pts, num_pts );
553 IntGdiMoveToEx( dc, line_pts[num_pts - 1].x, line_pts[num_pts - 1].y, NULL, TRUE );
554 result = TRUE;
555 }
556 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
557 {
558 SetLastNtError(_SEH2_GetExceptionCode());
559 }
560 _SEH2_END;
561
562 if (line_pts != NULL)
563 {
564 ExFreePoolWithTag(line_pts, TAG_SHAPE);
565 }
566
567 if ((line_pts_old != NULL) && (line_pts_old != line_pts))
568 {
569 ExFreePoolWithTag(line_pts_old, TAG_SHAPE);
570 }
571
572 if (bzr_pts != NULL)
573 {
574 ExFreePoolWithTag(bzr_pts, TAG_BEZIER);
575 }
576
577 DC_UnlockDc(dc);
578
579 return result;
580 }
581
582 /*
583 * @implemented
584 */
585 _Success_(return != FALSE)
586 BOOL
587 APIENTRY
588 NtGdiMoveTo(
589 IN HDC hdc,
590 IN INT x,
591 IN INT y,
592 OUT OPTIONAL LPPOINT pptOut)
593 {
594 PDC pdc;
595 BOOL Ret;
596 POINT Point;
597
598 pdc = DC_LockDc(hdc);
599 if (!pdc) return FALSE;
600
601 Ret = IntGdiMoveToEx(pdc, x, y, &Point, TRUE);
602
603 if (Ret && pptOut)
604 {
605 _SEH2_TRY
606 {
607 ProbeForWrite(pptOut, sizeof(POINT), 1);
608 RtlCopyMemory(pptOut, &Point, sizeof(POINT));
609 }
610 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
611 {
612 SetLastNtError(_SEH2_GetExceptionCode());
613 Ret = FALSE; // CHECKME: is this correct?
614 }
615 _SEH2_END;
616 }
617
618 DC_UnlockDc(pdc);
619
620 return Ret;
621 }
622
623 /* EOF */