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