[win32k]
[reactos.git] / reactos / subsystems / win32 / win32k / objects / 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 & GDIBRUSH_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 if (PATH_IsPathOpen(dc->dclevel))
237 return PATH_Polyline(dc, pt, Count);
238
239 DC_vPrepareDCsForBlit(dc, dc->rosdc.CombinedClip->rclBounds,
240 NULL, dc->rosdc.CombinedClip->rclBounds);
241
242 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
243 DC_vUpdateFillBrush(dc);
244
245 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
246 DC_vUpdateLineBrush(dc);
247
248 /* Get BRUSHOBJ from current pen. */
249 pbrLine = dc->dclevel.pbrLine;
250 ASSERT(pbrLine);
251
252 if (!(pbrLine->flAttrs & GDIBRUSH_IS_NULL))
253 {
254 Points = EngAllocMem(0, Count * sizeof(POINT), GDITAG_TEMP);
255 if (Points != NULL)
256 {
257 psurf = dc->dclevel.pSurface;
258 /* FIXME - psurf can be NULL!!!!
259 Don't assert but handle this case gracefully! */
260 ASSERT(psurf);
261
262 RtlCopyMemory(Points, pt, Count * sizeof(POINT));
263 IntLPtoDP(dc, Points, Count);
264
265 /* Offset the array of points by the DC origin */
266 for (i = 0; i < Count; i++)
267 {
268 Points[i].x += dc->ptlDCOrig.x;
269 Points[i].y += dc->ptlDCOrig.y;
270 }
271
272 Ret = IntEngPolyline(&psurf->SurfObj,
273 dc->rosdc.CombinedClip,
274 &dc->eboLine.BrushObject,
275 Points,
276 Count,
277 ROP2_TO_MIX(pdcattr->jROP2));
278
279 EngFreeMem(Points);
280 }
281 else
282 {
283 Ret = FALSE;
284 }
285 }
286
287 DC_vFinishBlit(dc, NULL);
288
289 return Ret;
290 }
291
292 BOOL FASTCALL
293 IntGdiPolylineTo(DC *dc,
294 LPPOINT pt,
295 DWORD Count)
296 {
297 BOOL ret = FALSE; // default to failure
298 PDC_ATTR pdcattr = dc->pdcattr;
299
300 if (PATH_IsPathOpen(dc->dclevel))
301 {
302 ret = PATH_PolylineTo(dc, pt, Count);
303 }
304 else /* do it using Polyline */
305 {
306 POINT *pts = ExAllocatePoolWithTag(PagedPool,
307 sizeof(POINT) * (Count + 1),
308 TAG_SHAPE);
309 if ( pts )
310 {
311 pts[0].x = pdcattr->ptlCurrent.x;
312 pts[0].y = pdcattr->ptlCurrent.y;
313 memcpy( pts + 1, pt, sizeof(POINT) * Count);
314 ret = IntGdiPolyline(dc, pts, Count + 1);
315 ExFreePoolWithTag(pts, TAG_SHAPE);
316 }
317 }
318 if ( ret )
319 {
320 pdcattr->ptlCurrent.x = pt[Count-1].x;
321 pdcattr->ptlCurrent.y = pt[Count-1].y;
322 pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
323 CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
324 pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
325 }
326
327 return ret;
328 }
329
330
331 BOOL FASTCALL
332 IntGdiPolyPolyline(DC *dc,
333 LPPOINT pt,
334 PULONG PolyPoints,
335 DWORD Count)
336 {
337 int i;
338 LPPOINT pts;
339 PULONG pc;
340 BOOL ret = FALSE; // default to failure
341 pts = pt;
342 pc = PolyPoints;
343
344 if (PATH_IsPathOpen(dc->dclevel))
345 return PATH_PolyPolyline( dc, pt, PolyPoints, Count );
346
347 for (i = 0; i < Count; i++)
348 {
349 ret = IntGdiPolyline ( dc, pts, *pc );
350 if (ret == FALSE)
351 {
352 return ret;
353 }
354 pts+=*pc++;
355 }
356
357 return ret;
358 }
359
360 /******************************************************************************/
361
362 BOOL
363 APIENTRY
364 NtGdiLineTo(HDC hDC,
365 int XEnd,
366 int YEnd)
367 {
368 DC *dc;
369 BOOL Ret;
370 RECT rcLockRect ;
371
372 dc = DC_LockDc(hDC);
373 if (!dc)
374 {
375 EngSetLastError(ERROR_INVALID_HANDLE);
376 return FALSE;
377 }
378 if (dc->dctype == DC_TYPE_INFO)
379 {
380 DC_UnlockDc(dc);
381 /* Yes, Windows really returns TRUE in this case */
382 return TRUE;
383 }
384
385 rcLockRect.left = dc->pdcattr->ptlCurrent.x;
386 rcLockRect.top = dc->pdcattr->ptlCurrent.y;
387 rcLockRect.right = XEnd;
388 rcLockRect.bottom = YEnd;
389
390 IntLPtoDP(dc, &rcLockRect, 2);
391
392 /* The DCOrg is in device coordinates */
393 rcLockRect.left += dc->ptlDCOrig.x;
394 rcLockRect.top += dc->ptlDCOrig.y;
395 rcLockRect.right += dc->ptlDCOrig.x;
396 rcLockRect.bottom += dc->ptlDCOrig.y;
397
398 DC_vPrepareDCsForBlit(dc, rcLockRect, NULL, rcLockRect);
399
400 if (dc->pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
401 DC_vUpdateLineBrush(dc);
402
403 Ret = IntGdiLineTo(dc, XEnd, YEnd);
404
405 DC_vFinishBlit(dc, NULL);
406
407 DC_UnlockDc(dc);
408 return Ret;
409 }
410
411 BOOL
412 APIENTRY
413 NtGdiPolyDraw(
414 IN HDC hdc,
415 IN LPPOINT lppt,
416 IN LPBYTE lpbTypes,
417 IN ULONG cCount)
418 {
419 PDC dc;
420 PPATH pPath;
421 BOOL result = FALSE;
422 POINT lastmove;
423 unsigned int i;
424 PDC_ATTR pdcattr;
425
426 dc = DC_LockDc(hdc);
427 if (!dc) return FALSE;
428 pdcattr = dc->pdcattr;
429
430 _SEH2_TRY
431 {
432 ProbeArrayForRead(lppt, sizeof(POINT), cCount, sizeof(LONG));
433 ProbeArrayForRead(lpbTypes, sizeof(BYTE), cCount, sizeof(BYTE));
434
435 /* check for each bezierto if there are two more points */
436 for ( i = 0; i < cCount; i++ )
437 {
438 if ( lpbTypes[i] != PT_MOVETO &&
439 lpbTypes[i] & PT_BEZIERTO )
440 {
441 if ( cCount < i+3 ) _SEH2_LEAVE;
442 else i += 2;
443 }
444 }
445
446 /* if no moveto occurs, we will close the figure here */
447 lastmove.x = pdcattr->ptlCurrent.x;
448 lastmove.y = pdcattr->ptlCurrent.y;
449
450 /* now let's draw */
451 for ( i = 0; i < cCount; i++ )
452 {
453 if ( lpbTypes[i] == PT_MOVETO )
454 {
455 IntGdiMoveToEx( dc, lppt[i].x, lppt[i].y, NULL, FALSE );
456 lastmove.x = pdcattr->ptlCurrent.x;
457 lastmove.y = pdcattr->ptlCurrent.y;
458 }
459 else if ( lpbTypes[i] & PT_LINETO )
460 IntGdiLineTo( dc, lppt[i].x, lppt[i].y );
461 else if ( lpbTypes[i] & PT_BEZIERTO )
462 {
463 POINT pts[4];
464 pts[0].x = pdcattr->ptlCurrent.x;
465 pts[0].y = pdcattr->ptlCurrent.y;
466 RtlCopyMemory(pts + 1, &lppt[i], sizeof(POINT) * 3);
467 IntGdiPolyBezier(dc, pts, 4);
468 i += 2;
469 }
470 else _SEH2_LEAVE;
471
472 if ( lpbTypes[i] & PT_CLOSEFIGURE )
473 {
474 if ( PATH_IsPathOpen(dc->dclevel) )
475 {
476 pPath = PATH_LockPath( dc->dclevel.hPath );
477 if (pPath)
478 {
479 IntGdiCloseFigure( pPath );
480 PATH_UnlockPath( pPath );
481 }
482 }
483 else IntGdiLineTo( dc, lastmove.x, lastmove.y );
484 }
485 }
486
487 result = TRUE;
488 }
489 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
490 {
491 SetLastNtError(_SEH2_GetExceptionCode());
492 }
493 _SEH2_END;
494
495 DC_UnlockDc(dc);
496
497 return result;
498 }
499
500 /*
501 * @unimplemented
502 */
503 BOOL
504 APIENTRY
505 NtGdiMoveTo(
506 IN HDC hdc,
507 IN INT x,
508 IN INT y,
509 OUT OPTIONAL LPPOINT pptOut)
510 {
511 UNIMPLEMENTED;
512 return FALSE;
513 }
514
515 /* EOF */