Jeffrey Morlan (mrnobo1024 at yahoo.com) - Fix ModifyWorldTransform multiplies. See...
[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 /* The DCOrg is in device coordinates */
133 Points[0].x += dc->ptlDCOrig.x;
134 Points[0].y += dc->ptlDCOrig.y;
135 Points[1].x += dc->ptlDCOrig.x;
136 Points[1].y += dc->ptlDCOrig.y;
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 if (!PenBrushObj)
146 {
147 /* default to BLACK_PEN */
148 PenBrushObj = PENOBJ_LockPen(NtGdiGetStockObject(BLACK_PEN));
149 ASSERT(PenBrushObj);
150 }
151
152 if (!(PenBrushObj->flAttrs & GDIBRUSH_IS_NULL))
153 {
154 IntGdiInitBrushInstance(&PenBrushInst, PenBrushObj, dc->XlatePen);
155 Ret = IntEngLineTo(&BitmapObj->SurfObj,
156 dc->CombinedClip,
157 &PenBrushInst.BrushObject,
158 Points[0].x, Points[0].y,
159 Points[1].x, Points[1].y,
160 &Bounds,
161 ROP2_TO_MIX(Dc_Attr->jROP2));
162 }
163
164 BITMAPOBJ_UnlockBitmap ( BitmapObj );
165 PENOBJ_UnlockPen( PenBrushObj );
166 }
167
168 if (Ret)
169 {
170 Dc_Attr->ptlCurrent.x = XEnd;
171 Dc_Attr->ptlCurrent.y = YEnd;
172 Dc_Attr->ptfxCurrent = Dc_Attr->ptlCurrent;
173 CoordLPtoDP(dc, &Dc_Attr->ptfxCurrent); // Update fx
174 Dc_Attr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
175 }
176
177 return Ret;
178 }
179
180 BOOL FASTCALL
181 IntGdiPolyBezier(DC *dc,
182 LPPOINT pt,
183 DWORD Count)
184 {
185 BOOL ret = FALSE; // default to FAILURE
186
187 if ( PATH_IsPathOpen(dc->w.path) )
188 {
189 return PATH_PolyBezier ( dc, pt, Count );
190 }
191
192 /* We'll convert it into line segments and draw them using Polyline */
193 {
194 POINT *Pts;
195 INT nOut;
196
197 Pts = GDI_Bezier ( pt, Count, &nOut );
198 if ( Pts )
199 {
200 ret = IntGdiPolyline(dc, Pts, nOut);
201 ExFreePool(Pts);
202 }
203 }
204
205 return ret;
206 }
207
208 BOOL FASTCALL
209 IntGdiPolyBezierTo(DC *dc,
210 LPPOINT pt,
211 DWORD Count)
212 {
213 BOOL ret = FALSE; // default to failure
214 PDC_ATTR Dc_Attr = dc->pDc_Attr;
215
216 if (!Dc_Attr) Dc_Attr = &dc->Dc_Attr;
217 if ( PATH_IsPathOpen(dc->w.path) )
218 ret = PATH_PolyBezierTo ( dc, pt, Count );
219 else /* We'll do it using PolyBezier */
220 {
221 POINT *npt;
222 npt = ExAllocatePoolWithTag(PagedPool,
223 sizeof(POINT) * (Count + 1),
224 TAG_BEZIER);
225 if ( npt )
226 {
227 npt[0].x = Dc_Attr->ptlCurrent.x;
228 npt[0].y = Dc_Attr->ptlCurrent.y;
229 memcpy(npt + 1, pt, sizeof(POINT) * Count);
230 ret = IntGdiPolyBezier(dc, npt, Count+1);
231 ExFreePool(npt);
232 }
233 }
234 if ( ret )
235 {
236 Dc_Attr->ptlCurrent.x = pt[Count-1].x;
237 Dc_Attr->ptlCurrent.y = pt[Count-1].y;
238 Dc_Attr->ptfxCurrent = Dc_Attr->ptlCurrent;
239 CoordLPtoDP(dc, &Dc_Attr->ptfxCurrent); // Update fx
240 Dc_Attr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
241 }
242
243 return ret;
244 }
245
246 BOOL FASTCALL
247 IntGdiPolyline(DC *dc,
248 LPPOINT pt,
249 int Count)
250 {
251 BITMAPOBJ *BitmapObj;
252 GDIBRUSHOBJ *PenBrushObj;
253 GDIBRUSHINST PenBrushInst;
254 LPPOINT Points;
255 BOOL Ret = TRUE;
256 LONG i;
257 PDC_ATTR Dc_Attr = dc->pDc_Attr;
258
259 if (!Dc_Attr) Dc_Attr = &dc->Dc_Attr;
260 if (PATH_IsPathOpen(dc->w.path))
261 return PATH_Polyline(dc, pt, Count);
262
263 /* Get BRUSHOBJ from current pen. */
264 PenBrushObj = PENOBJ_LockPen(Dc_Attr->hpen);
265 /* FIXME - PenBrushObj can be NULL! Don't assert here! */
266 ASSERT(PenBrushObj);
267
268 if (!(PenBrushObj->flAttrs & GDIBRUSH_IS_NULL))
269 {
270 Points = EngAllocMem(0, Count * sizeof(POINT), TAG_COORD);
271 if (Points != NULL)
272 {
273 BitmapObj = BITMAPOBJ_LockBitmap(dc->w.hBitmap);
274 /* FIXME - BitmapObj can be NULL!!!!
275 Don't assert but handle this case gracefully! */
276 ASSERT(BitmapObj);
277
278 RtlCopyMemory(Points, pt, Count * sizeof(POINT));
279 IntLPtoDP(dc, Points, Count);
280
281 /* Offset the array of point by the dc->w.DCOrg */
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 IntGdiInitBrushInstance(&PenBrushInst, PenBrushObj, dc->XlatePen);
289 Ret = IntEngPolyline(&BitmapObj->SurfObj,
290 dc->CombinedClip,
291 &PenBrushInst.BrushObject,
292 Points,
293 Count,
294 ROP2_TO_MIX(Dc_Attr->jROP2));
295
296 BITMAPOBJ_UnlockBitmap(BitmapObj);
297 EngFreeMem(Points);
298 }
299 else
300 {
301 Ret = FALSE;
302 }
303 }
304
305 PENOBJ_UnlockPen(PenBrushObj);
306
307 return Ret;
308 }
309
310 BOOL FASTCALL
311 IntGdiPolylineTo(DC *dc,
312 LPPOINT pt,
313 DWORD Count)
314 {
315 BOOL ret = FALSE; // default to failure
316 PDC_ATTR Dc_Attr = dc->pDc_Attr;
317
318 if (!Dc_Attr) Dc_Attr = &dc->Dc_Attr;
319 if (PATH_IsPathOpen(dc->w.path))
320 {
321 ret = PATH_PolylineTo(dc, pt, Count);
322 }
323 else /* do it using Polyline */
324 {
325 POINT *pts = ExAllocatePoolWithTag(PagedPool,
326 sizeof(POINT) * (Count + 1),
327 TAG_SHAPE);
328 if ( pts )
329 {
330 pts[0].x = Dc_Attr->ptlCurrent.x;
331 pts[0].y = Dc_Attr->ptlCurrent.y;
332 memcpy( pts + 1, pt, sizeof(POINT) * Count);
333 ret = IntGdiPolyline(dc, pts, Count + 1);
334 ExFreePool(pts);
335 }
336 }
337 if ( ret )
338 {
339 Dc_Attr->ptlCurrent.x = pt[Count-1].x;
340 Dc_Attr->ptlCurrent.y = pt[Count-1].y;
341 Dc_Attr->ptfxCurrent = Dc_Attr->ptlCurrent;
342 CoordLPtoDP(dc, &Dc_Attr->ptfxCurrent); // Update fx
343 Dc_Attr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
344 }
345
346 return ret;
347 }
348
349
350 BOOL FASTCALL
351 IntGdiPolyPolyline(DC *dc,
352 LPPOINT pt,
353 LPDWORD PolyPoints,
354 DWORD Count)
355 {
356 int i;
357 LPPOINT pts;
358 LPDWORD pc;
359 BOOL ret = FALSE; // default to failure
360 pts = pt;
361 pc = PolyPoints;
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 STDCALL
380 NtGdiLineTo(HDC hDC,
381 int XEnd,
382 int YEnd)
383 {
384 DC *dc;
385 BOOL Ret;
386
387 dc = DC_LockDc(hDC);
388 if (!dc)
389 {
390 SetLastWin32Error(ERROR_INVALID_HANDLE);
391 return FALSE;
392 }
393 if (dc->DC_Type == DC_TYPE_INFO)
394 {
395 DC_UnlockDc(dc);
396 /* Yes, Windows really returns TRUE in this case */
397 return TRUE;
398 }
399
400 Ret = IntGdiLineTo(dc, XEnd, YEnd);
401
402 DC_UnlockDc(dc);
403 return Ret;
404 }
405
406 BOOL
407 APIENTRY
408 NtGdiPolyDraw(
409 IN HDC hdc,
410 IN LPPOINT lppt,
411 IN LPBYTE lpbTypes,
412 IN ULONG cCount)
413 {
414 PDC dc;
415 BOOL result = FALSE;
416 POINT lastmove;
417 unsigned int i;
418 PDC_ATTR Dc_Attr = NULL;
419
420 dc = DC_LockDc(hdc);
421 if (!dc) return FALSE;
422 Dc_Attr = dc->pDc_Attr;
423 if (!Dc_Attr) Dc_Attr = &dc->Dc_Attr;
424
425 _SEH_TRY
426 {
427 ProbeArrayForRead(lppt, sizeof(POINT), cCount, sizeof(LONG));
428 ProbeArrayForRead(lpbTypes, sizeof(BYTE), cCount, sizeof(BYTE));
429
430 /* check for each bezierto if there are two more points */
431 for ( i = 0; i < cCount; i++ )
432 {
433 if ( lpbTypes[i] != PT_MOVETO &&
434 lpbTypes[i] & PT_BEZIERTO )
435 {
436 if ( cCount < i+3 ) _SEH_LEAVE;
437 else i += 2;
438 }
439 }
440
441 /* if no moveto occurs, we will close the figure here */
442 lastmove.x = Dc_Attr->ptlCurrent.x;
443 lastmove.y = Dc_Attr->ptlCurrent.y;
444
445 /* now let's draw */
446 for ( i = 0; i < cCount; i++ )
447 {
448 if ( lpbTypes[i] == PT_MOVETO )
449 {
450 IntGdiMoveToEx( dc, lppt[i].x, lppt[i].y, NULL );
451 lastmove.x = Dc_Attr->ptlCurrent.x;
452 lastmove.y = Dc_Attr->ptlCurrent.y;
453 }
454 else if ( lpbTypes[i] & PT_LINETO )
455 IntGdiLineTo( dc, lppt[i].x, lppt[i].y );
456 else if ( lpbTypes[i] & PT_BEZIERTO )
457 {
458 POINT pts[4];
459 pts[0].x = Dc_Attr->ptlCurrent.x;
460 pts[0].y = Dc_Attr->ptlCurrent.y;
461 RtlCopyMemory(pts + 1, &lppt[i], sizeof(POINT) * 3);
462 IntGdiPolyBezier(dc, pts, 4);
463 i += 2;
464 }
465 else _SEH_LEAVE;
466
467 if ( lpbTypes[i] & PT_CLOSEFIGURE )
468 {
469 if ( PATH_IsPathOpen(dc->w.path) )
470 {
471 IntGdiCloseFigure( dc );
472 }
473 else IntGdiLineTo( dc, lastmove.x, lastmove.y );
474 }
475 }
476
477 result = TRUE;
478 }
479 _SEH_HANDLE
480 {
481 SetLastNtError(_SEH_GetExceptionCode());
482 }
483 _SEH_END;
484
485 DC_UnlockDc(dc);
486
487 return result;
488 }
489
490
491 /* EOF */