[FORMATTING]
[reactos.git] / reactos / subsystems / win32 / win32k / objects / line.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <w32k.h>
21
22 #define NDEBUG
23 #include <debug.h>
24
25 // Some code from the WINE project source (www.winehq.com)
26
27 // Should use Fx in Point
28 //
29 BOOL FASTCALL
30 IntGdiMoveToEx(DC *dc,
31 int X,
32 int Y,
33 LPPOINT Point)
34 {
35 BOOL PathIsOpen;
36 PDC_ATTR Dc_Attr = dc->pDc_Attr;
37 if (!Dc_Attr) Dc_Attr = &dc->Dc_Attr;
38 if ( Point )
39 {
40 if ( Dc_Attr->ulDirty_ & DIRTY_PTLCURRENT ) // Double hit!
41 {
42 Point->x = Dc_Attr->ptfxCurrent.x; // ret prev before change.
43 Point->y = Dc_Attr->ptfxCurrent.y;
44 IntDPtoLP ( dc, Point, 1); // reconvert back.
45 }
46 else
47 {
48 Point->x = Dc_Attr->ptlCurrent.x;
49 Point->y = Dc_Attr->ptlCurrent.y;
50 }
51 }
52 Dc_Attr->ptlCurrent.x = X;
53 Dc_Attr->ptlCurrent.y = Y;
54 Dc_Attr->ptfxCurrent = Dc_Attr->ptlCurrent;
55 CoordLPtoDP(dc, &Dc_Attr->ptfxCurrent); // Update fx
56 Dc_Attr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
57
58 PathIsOpen = PATH_IsPathOpen(dc->w.path);
59
60 if ( PathIsOpen )
61 return PATH_MoveTo ( dc );
62
63 return TRUE;
64 }
65
66 // Should use Fx in pt
67 //
68 VOID FASTCALL
69 IntGetCurrentPositionEx(PDC dc, LPPOINT pt)
70 {
71 PDC_ATTR Dc_Attr = dc->pDc_Attr;
72 if (!Dc_Attr) Dc_Attr = &dc->Dc_Attr;
73
74 if ( pt )
75 {
76 if (Dc_Attr->ulDirty_ & DIRTY_PTFXCURRENT)
77 {
78 Dc_Attr->ptfxCurrent = Dc_Attr->ptlCurrent;
79 CoordLPtoDP(dc, &Dc_Attr->ptfxCurrent); // Update fx
80 Dc_Attr->ulDirty_ &= ~(DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
81 }
82 pt->x = Dc_Attr->ptlCurrent.x;
83 pt->y = Dc_Attr->ptlCurrent.y;
84 }
85 }
86
87 BOOL FASTCALL
88 IntGdiLineTo(DC *dc,
89 int XEnd,
90 int YEnd)
91 {
92 BITMAPOBJ *BitmapObj;
93 BOOL Ret = TRUE;
94 PGDIBRUSHOBJ PenBrushObj;
95 GDIBRUSHINST PenBrushInst;
96 RECTL Bounds;
97 POINT Points[2];
98 PDC_ATTR Dc_Attr = dc->pDc_Attr;
99
100 if (!Dc_Attr) Dc_Attr = &dc->Dc_Attr;
101
102 if (PATH_IsPathOpen(dc->w.path))
103 {
104 Ret = PATH_LineTo(dc, XEnd, YEnd);
105 if (Ret)
106 {
107 // FIXME - PATH_LineTo should maybe do this...
108 Dc_Attr->ptlCurrent.x = XEnd;
109 Dc_Attr->ptlCurrent.y = YEnd;
110 Dc_Attr->ptfxCurrent = Dc_Attr->ptlCurrent;
111 CoordLPtoDP(dc, &Dc_Attr->ptfxCurrent); // Update fx
112 Dc_Attr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
113 }
114 return Ret;
115 }
116 else
117 {
118 BitmapObj = BITMAPOBJ_LockBitmap ( dc->w.hBitmap );
119 if (NULL == BitmapObj)
120 {
121 SetLastWin32Error(ERROR_INVALID_HANDLE);
122 return FALSE;
123 }
124
125 Points[0].x = Dc_Attr->ptlCurrent.x;
126 Points[0].y = Dc_Attr->ptlCurrent.y;
127 Points[1].x = XEnd;
128 Points[1].y = YEnd;
129
130 IntLPtoDP(dc, Points, 2);
131
132 /* FIXME: Is it correct to do this after the transformation? */
133 Points[0].x += dc->w.DCOrgX;
134 Points[0].y += dc->w.DCOrgY;
135 Points[1].x += dc->w.DCOrgX;
136 Points[1].y += dc->w.DCOrgY;
137
138 Bounds.left = min(Points[0].x, Points[1].x);
139 Bounds.top = min(Points[0].y, Points[1].y);
140 Bounds.right = max(Points[0].x, Points[1].x);
141 Bounds.bottom = max(Points[0].y, Points[1].y);
142
143 /* get BRUSHOBJ from current pen. */
144 PenBrushObj = PENOBJ_LockPen( Dc_Attr->hpen );
145 /* FIXME - PenBrushObj can be NULL!!!! Don't assert here! */
146 ASSERT(PenBrushObj);
147
148 if (!(PenBrushObj->flAttrs & GDIBRUSH_IS_NULL))
149 {
150 IntGdiInitBrushInstance(&PenBrushInst, PenBrushObj, dc->XlatePen);
151 Ret = IntEngLineTo(&BitmapObj->SurfObj,
152 dc->CombinedClip,
153 &PenBrushInst.BrushObject,
154 Points[0].x, Points[0].y,
155 Points[1].x, Points[1].y,
156 &Bounds,
157 ROP2_TO_MIX(Dc_Attr->jROP2));
158 }
159
160 BITMAPOBJ_UnlockBitmap ( BitmapObj );
161 PENOBJ_UnlockPen( PenBrushObj );
162 }
163
164 if (Ret)
165 {
166 Dc_Attr->ptlCurrent.x = XEnd;
167 Dc_Attr->ptlCurrent.y = YEnd;
168 Dc_Attr->ptfxCurrent = Dc_Attr->ptlCurrent;
169 CoordLPtoDP(dc, &Dc_Attr->ptfxCurrent); // Update fx
170 Dc_Attr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
171 }
172
173 return Ret;
174 }
175
176 BOOL FASTCALL
177 IntGdiPolyBezier(DC *dc,
178 LPPOINT pt,
179 DWORD Count)
180 {
181 BOOL ret = FALSE; // default to FAILURE
182
183 if ( PATH_IsPathOpen(dc->w.path) )
184 {
185 return PATH_PolyBezier ( dc, pt, Count );
186 }
187
188 /* We'll convert it into line segments and draw them using Polyline */
189 {
190 POINT *Pts;
191 INT nOut;
192
193 Pts = GDI_Bezier ( pt, Count, &nOut );
194 if ( Pts )
195 {
196 ret = IntGdiPolyline(dc, Pts, nOut);
197 ExFreePool(Pts);
198 }
199 }
200
201 return ret;
202 }
203
204 BOOL FASTCALL
205 IntGdiPolyBezierTo(DC *dc,
206 LPPOINT pt,
207 DWORD Count)
208 {
209 BOOL ret = FALSE; // default to failure
210 PDC_ATTR Dc_Attr = dc->pDc_Attr;
211
212 if (!Dc_Attr) Dc_Attr = &dc->Dc_Attr;
213 if ( PATH_IsPathOpen(dc->w.path) )
214 ret = PATH_PolyBezierTo ( dc, pt, Count );
215 else /* We'll do it using PolyBezier */
216 {
217 POINT *npt;
218 npt = ExAllocatePoolWithTag(PagedPool,
219 sizeof(POINT) * (Count + 1),
220 TAG_BEZIER);
221 if ( npt )
222 {
223 npt[0].x = Dc_Attr->ptlCurrent.x;
224 npt[0].y = Dc_Attr->ptlCurrent.y;
225 memcpy(npt + 1, pt, sizeof(POINT) * Count);
226 ret = IntGdiPolyBezier(dc, npt, Count+1);
227 ExFreePool(npt);
228 }
229 }
230 if ( ret )
231 {
232 Dc_Attr->ptlCurrent.x = pt[Count-1].x;
233 Dc_Attr->ptlCurrent.y = pt[Count-1].y;
234 Dc_Attr->ptfxCurrent = Dc_Attr->ptlCurrent;
235 CoordLPtoDP(dc, &Dc_Attr->ptfxCurrent); // Update fx
236 Dc_Attr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
237 }
238
239 return ret;
240 }
241
242 BOOL FASTCALL
243 IntGdiPolyline(DC *dc,
244 LPPOINT pt,
245 int Count)
246 {
247 BITMAPOBJ *BitmapObj;
248 GDIBRUSHOBJ *PenBrushObj;
249 GDIBRUSHINST PenBrushInst;
250 LPPOINT Points;
251 BOOL Ret = TRUE;
252 LONG i;
253 PDC_ATTR Dc_Attr = dc->pDc_Attr;
254
255 if (!Dc_Attr) Dc_Attr = &dc->Dc_Attr;
256 if (PATH_IsPathOpen(dc->w.path))
257 return PATH_Polyline(dc, pt, Count);
258
259 /* Get BRUSHOBJ from current pen. */
260 PenBrushObj = PENOBJ_LockPen(Dc_Attr->hpen);
261 /* FIXME - PenBrushObj can be NULL! Don't assert here! */
262 ASSERT(PenBrushObj);
263
264 if (!(PenBrushObj->flAttrs & GDIBRUSH_IS_NULL))
265 {
266 Points = EngAllocMem(0, Count * sizeof(POINT), TAG_COORD);
267 if (Points != NULL)
268 {
269 BitmapObj = BITMAPOBJ_LockBitmap(dc->w.hBitmap);
270 /* FIXME - BitmapObj can be NULL!!!!
271 Don't assert but handle this case gracefully! */
272 ASSERT(BitmapObj);
273
274 RtlCopyMemory(Points, pt, Count * sizeof(POINT));
275 IntLPtoDP(dc, Points, Count);
276
277 /* Offset the array of point by the dc->w.DCOrg */
278 for (i = 0; i < Count; i++)
279 {
280 Points[i].x += dc->w.DCOrgX;
281 Points[i].y += dc->w.DCOrgY;
282 }
283
284 IntGdiInitBrushInstance(&PenBrushInst, PenBrushObj, dc->XlatePen);
285 Ret = IntEngPolyline(&BitmapObj->SurfObj,
286 dc->CombinedClip,
287 &PenBrushInst.BrushObject,
288 Points,
289 Count,
290 ROP2_TO_MIX(Dc_Attr->jROP2));
291
292 BITMAPOBJ_UnlockBitmap(BitmapObj);
293 EngFreeMem(Points);
294 }
295 else
296 {
297 Ret = FALSE;
298 }
299 }
300
301 PENOBJ_UnlockPen(PenBrushObj);
302
303 return Ret;
304 }
305
306 BOOL FASTCALL
307 IntGdiPolylineTo(DC *dc,
308 LPPOINT pt,
309 DWORD Count)
310 {
311 BOOL ret = FALSE; // default to failure
312 PDC_ATTR Dc_Attr = dc->pDc_Attr;
313
314 if (!Dc_Attr) Dc_Attr = &dc->Dc_Attr;
315 if (PATH_IsPathOpen(dc->w.path))
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 = Dc_Attr->ptlCurrent.x;
327 pts[0].y = Dc_Attr->ptlCurrent.y;
328 memcpy( pts + 1, pt, sizeof(POINT) * Count);
329 ret = IntGdiPolyline(dc, pts, Count + 1);
330 ExFreePool(pts);
331 }
332 }
333 if ( ret )
334 {
335 Dc_Attr->ptlCurrent.x = pt[Count-1].x;
336 Dc_Attr->ptlCurrent.y = pt[Count-1].y;
337 Dc_Attr->ptfxCurrent = Dc_Attr->ptlCurrent;
338 CoordLPtoDP(dc, &Dc_Attr->ptfxCurrent); // Update fx
339 Dc_Attr->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 LPDWORD PolyPoints,
350 DWORD Count)
351 {
352 int i;
353 LPPOINT pts;
354 LPDWORD pc;
355 BOOL ret = FALSE; // default to failure
356 pts = pt;
357 pc = PolyPoints;
358
359 for (i = 0; i < Count; i++)
360 {
361 ret = IntGdiPolyline ( dc, pts, *pc );
362 if (ret == FALSE)
363 {
364 return ret;
365 }
366 pts+=*pc++;
367 }
368
369 return ret;
370 }
371
372 /******************************************************************************/
373
374 BOOL
375 APIENTRY
376 NtGdiAngleArc(
377 IN HDC hdc,
378 IN INT x,
379 IN INT y,
380 IN DWORD dwRadius,
381 IN DWORD dwStartAngle,
382 IN DWORD dwSweepAngle)
383 {
384 UNIMPLEMENTED;
385 return FALSE;
386 }
387
388
389 BOOL
390 STDCALL
391 NtGdiLineTo(HDC hDC,
392 int XEnd,
393 int YEnd)
394 {
395 DC *dc;
396 BOOL Ret;
397
398 dc = DC_LockDc(hDC);
399 if (!dc)
400 {
401 SetLastWin32Error(ERROR_INVALID_HANDLE);
402 return FALSE;
403 }
404 if (dc->DC_Type == DC_TYPE_INFO)
405 {
406 DC_UnlockDc(dc);
407 /* Yes, Windows really returns TRUE in this case */
408 return TRUE;
409 }
410
411 Ret = IntGdiLineTo(dc, XEnd, YEnd);
412
413 DC_UnlockDc(dc);
414 return Ret;
415 }
416
417 BOOL
418 APIENTRY
419 NtGdiPolyDraw(
420 IN HDC hdc,
421 IN LPPOINT lppt,
422 IN LPBYTE lpbTypes,
423 IN ULONG cCount)
424 {
425 PDC dc;
426 BOOL result = FALSE;
427 POINT lastmove;
428 unsigned int i;
429 PDC_ATTR Dc_Attr = NULL;
430
431 dc = DC_LockDc(hdc);
432 if (!dc) return FALSE;
433 Dc_Attr = dc->pDc_Attr;
434 if (!Dc_Attr) Dc_Attr = &dc->Dc_Attr;
435
436 _SEH_TRY
437 {
438 ProbeArrayForRead(lppt, sizeof(POINT), cCount, sizeof(LONG));
439 ProbeArrayForRead(lpbTypes, sizeof(BYTE), cCount, sizeof(BYTE));
440
441 /* check for each bezierto if there are two more points */
442 for ( i = 0; i < cCount; i++ )
443 {
444 if ( lpbTypes[i] != PT_MOVETO &&
445 lpbTypes[i] & PT_BEZIERTO )
446 {
447 if ( cCount < i+3 ) _SEH_LEAVE;
448 else i += 2;
449 }
450 }
451
452 /* if no moveto occurs, we will close the figure here */
453 lastmove.x = Dc_Attr->ptlCurrent.x;
454 lastmove.y = Dc_Attr->ptlCurrent.y;
455
456 /* now let's draw */
457 for ( i = 0; i < cCount; i++ )
458 {
459 if ( lpbTypes[i] == PT_MOVETO )
460 {
461 IntGdiMoveToEx( dc, lppt[i].x, lppt[i].y, NULL );
462 lastmove.x = Dc_Attr->ptlCurrent.x;
463 lastmove.y = Dc_Attr->ptlCurrent.y;
464 }
465 else if ( lpbTypes[i] & PT_LINETO )
466 IntGdiLineTo( dc, lppt[i].x, lppt[i].y );
467 else if ( lpbTypes[i] & PT_BEZIERTO )
468 {
469 POINT pts[4];
470 pts[0].x = Dc_Attr->ptlCurrent.x;
471 pts[0].y = Dc_Attr->ptlCurrent.y;
472 RtlCopyMemory(pts + 1, &lppt[i], sizeof(POINT) * 3);
473 IntGdiPolyBezier(dc, pts, 4);
474 i += 2;
475 }
476 else _SEH_LEAVE;
477
478 if ( lpbTypes[i] & PT_CLOSEFIGURE )
479 {
480 if ( PATH_IsPathOpen(dc->w.path) )
481 {
482 IntGdiCloseFigure( dc );
483 }
484 else IntGdiLineTo( dc, lastmove.x, lastmove.y );
485 }
486 }
487
488 result = TRUE;
489 }
490 _SEH_HANDLE
491 {
492 SetLastNtError(_SEH_GetExceptionCode());
493 }
494 _SEH_END;
495
496 DC_UnlockDc(dc);
497
498 return result;
499 }
500
501
502 /* EOF */