Jeffrey Morlan (mrnobo1024 at yahoo.com) - Fix ModifyWorldTransform multiplies. See...
[reactos.git] / reactos / subsystems / win32 / win32k / objects / path.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 /* $Id$ */
20
21 #include <w32k.h>
22 #include "math.h"
23
24 #define NDEBUG
25 #include <debug.h>
26
27 #define NUM_ENTRIES_INITIAL 16 /* Initial size of points / flags arrays */
28 #define GROW_FACTOR_NUMER 2 /* Numerator of grow factor for the array */
29 #define GROW_FACTOR_DENOM 1 /* Denominator of grow factor */
30
31 BOOL FASTCALL PATH_AddEntry (GdiPath *pPath, const POINT *pPoint, BYTE flags);
32 BOOL FASTCALL PATH_AddFlatBezier (GdiPath *pPath, POINT *pt, BOOL closed);
33 BOOL FASTCALL PATH_DoArcPart (GdiPath *pPath, FLOAT_POINT corners[], double angleStart, double angleEnd, BOOL addMoveTo);
34 BOOL FASTCALL PATH_FillPath( PDC dc, GdiPath *pPath );
35 BOOL FASTCALL PATH_FlattenPath (GdiPath *pPath);
36 VOID FASTCALL PATH_NormalizePoint (FLOAT_POINT corners[], const FLOAT_POINT *pPoint, double *pX, double *pY);
37 BOOL FASTCALL PATH_PathToRegion (GdiPath *pPath, INT nPolyFillMode, HRGN *pHrgn);
38 BOOL FASTCALL PATH_ReserveEntries (GdiPath *pPath, INT numEntries);
39 VOID FASTCALL PATH_ScaleNormalizedPoint (FLOAT_POINT corners[], double x, double y, POINT *pPoint);
40 BOOL FASTCALL PATH_StrokePath(DC *dc, GdiPath *pPath);
41 BOOL PATH_CheckCorners(DC *dc, POINT corners[], INT x1, INT y1, INT x2, INT y2);
42
43 VOID FASTCALL
44 IntGetCurrentPositionEx(PDC dc, LPPOINT pt);
45
46
47 BOOL
48 STDCALL
49 NtGdiAbortPath(HDC hDC)
50 {
51 BOOL ret = TRUE;
52 PDC dc = DC_LockDc ( hDC );
53
54 if( !dc ) return FALSE;
55
56 PATH_EmptyPath(&dc->w.path);
57
58 DC_UnlockDc ( dc );
59 return ret;
60 }
61
62 BOOL
63 STDCALL
64 NtGdiBeginPath( HDC hDC )
65 {
66 BOOL ret = TRUE;
67 PDC dc = DC_LockDc ( hDC );
68
69 if( !dc ) return FALSE;
70
71 /* If path is already open, do nothing */
72 if ( dc->w.path.state != PATH_Open )
73 {
74 /* Make sure that path is empty */
75 PATH_EmptyPath( &dc->w.path );
76
77 /* Initialize variables for new path */
78 dc->w.path.newStroke = TRUE;
79 dc->w.path.state = PATH_Open;
80 }
81
82 DC_UnlockDc ( dc );
83 return ret;
84 }
85
86 VOID
87 FASTCALL
88 IntGdiCloseFigure(PDC pDc)
89 {
90 ASSERT(pDc);
91 ASSERT(pDc->w.path.state == PATH_Open);
92
93 // FIXME: Shouldn't we draw a line to the beginning of the figure?
94 // Set PT_CLOSEFIGURE on the last entry and start a new stroke
95 if(pDc->w.path.numEntriesUsed)
96 {
97 pDc->w.path.pFlags[pDc->w.path.numEntriesUsed-1]|=PT_CLOSEFIGURE;
98 pDc->w.path.newStroke=TRUE;
99 }
100 }
101
102 BOOL
103 STDCALL
104 NtGdiCloseFigure(HDC hDC)
105 {
106 BOOL Ret = FALSE; // default to failure
107 PDC pDc;
108
109 DPRINT("Enter %s\n", __FUNCTION__);
110
111 pDc = DC_LockDc(hDC);
112 if(!pDc) return FALSE;
113
114 if(pDc->w.path.state==PATH_Open)
115 {
116 IntGdiCloseFigure(pDc);
117 Ret = TRUE;
118 }
119 else
120 {
121 // FIXME: check if lasterror is set correctly
122 SetLastWin32Error(ERROR_CAN_NOT_COMPLETE);
123 }
124
125 DC_UnlockDc(pDc);
126
127 return Ret;
128 }
129
130 BOOL
131 STDCALL
132 NtGdiEndPath(HDC hDC)
133 {
134 BOOL ret = TRUE;
135 PDC dc = DC_LockDc ( hDC );
136
137 if ( !dc ) return FALSE;
138
139 /* Check that path is currently being constructed */
140 if( dc->w.path.state != PATH_Open )
141 {
142 ret = FALSE;
143 }
144 /* Set flag to indicate that path is finished */
145 else dc->w.path.state = PATH_Closed;
146
147 DC_UnlockDc ( dc );
148 return ret;
149 }
150
151 BOOL
152 STDCALL
153 NtGdiFillPath(HDC hDC)
154 {
155 BOOL ret = TRUE;
156 PDC dc = DC_LockDc ( hDC );
157
158 if ( !dc ) return FALSE;
159
160 ret = PATH_FillPath( dc, &dc->w.path );
161 if( ret )
162 {
163 /* FIXME: Should the path be emptied even if conversion
164 failed? */
165 PATH_EmptyPath( &dc->w.path );
166 }
167
168 DC_UnlockDc ( dc );
169 return ret;
170 }
171
172 BOOL
173 STDCALL
174 NtGdiFlattenPath(HDC hDC)
175 {
176 BOOL Ret = FALSE;
177 DC *pDc;
178
179 DPRINT("Enter %s\n", __FUNCTION__);
180
181 pDc = DC_LockDc(hDC);
182 if(!pDc) return FALSE;
183
184 if(pDc->w.path.state == PATH_Open)
185 Ret = PATH_FlattenPath(&pDc->w.path);
186
187 DC_UnlockDc(pDc);
188 return Ret;
189 }
190
191
192 BOOL
193 APIENTRY
194 NtGdiGetMiterLimit(
195 IN HDC hdc,
196 OUT PDWORD pdwOut)
197 {
198 DC *pDc;
199 gxf_long worker;
200 NTSTATUS Status = STATUS_SUCCESS;
201
202 if(!(pDc = DC_LockDc(hdc))) return FALSE;
203
204 worker.f = pDc->DcLevel.laPath.eMiterLimit;
205
206 if (pdwOut)
207 {
208 _SEH_TRY
209 {
210 ProbeForWrite(pdwOut,
211 sizeof(DWORD),
212 1);
213 *pdwOut = worker.l;
214 }
215 _SEH_HANDLE
216 {
217 Status = _SEH_GetExceptionCode();
218 }
219 _SEH_END;
220 if (!NT_SUCCESS(Status))
221 {
222 SetLastNtError(Status);
223 DC_UnlockDc(pDc);
224 return FALSE;
225 }
226 }
227
228 DC_UnlockDc(pDc);
229 return TRUE;
230
231 }
232
233 INT
234 STDCALL
235 NtGdiGetPath(
236 HDC hDC,
237 LPPOINT Points,
238 LPBYTE Types,
239 INT nSize)
240 {
241 INT ret = -1;
242 GdiPath *pPath;
243
244 DC *dc = DC_LockDc(hDC);
245 if(!dc)
246 {
247 DPRINT1("Can't lock dc!\n");
248 return -1;
249 }
250
251 pPath = &dc->w.path;
252
253 if(pPath->state != PATH_Closed)
254 {
255 SetLastWin32Error(ERROR_CAN_NOT_COMPLETE);
256 goto done;
257 }
258
259 if(nSize==0)
260 {
261 ret = pPath->numEntriesUsed;
262 }
263 else if(nSize<pPath->numEntriesUsed)
264 {
265 SetLastWin32Error(ERROR_INVALID_PARAMETER);
266 goto done;
267 }
268 else
269 {
270 _SEH_TRY
271 {
272 memcpy(Points, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
273 memcpy(Types, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
274
275 /* Convert the points to logical coordinates */
276 IntDPtoLP(dc, Points, pPath->numEntriesUsed);
277
278 ret = pPath->numEntriesUsed;
279 }
280 _SEH_HANDLE
281 {
282 SetLastNtError(_SEH_GetExceptionCode());
283 }
284 _SEH_END
285 }
286
287 done:
288 DC_UnlockDc(dc);
289 return ret;
290 }
291
292 HRGN
293 STDCALL
294 NtGdiPathToRegion(HDC hDC)
295 {
296 GdiPath *pPath;
297 HRGN hrgnRval = 0;
298 DC *pDc;
299 PDC_ATTR Dc_Attr;
300
301 DPRINT("Enter %s\n", __FUNCTION__);
302
303 pDc = DC_LockDc(hDC);
304 if(!pDc) return NULL;
305 Dc_Attr = pDc->pDc_Attr;
306 if(!Dc_Attr) Dc_Attr = &pDc->Dc_Attr;
307 pPath = &pDc->w.path;
308
309 if(pPath->state!=PATH_Closed)
310 {
311 //FIXME: check that setlasterror is being called correctly
312 SetLastWin32Error(ERROR_CAN_NOT_COMPLETE);
313 }
314 else
315 {
316 /* FIXME: Should we empty the path even if conversion failed? */
317 if(PATH_PathToRegion(pPath, Dc_Attr->jFillMode, &hrgnRval))
318 PATH_EmptyPath(pPath);
319 }
320
321 DC_UnlockDc(pDc);
322 return hrgnRval;
323 }
324
325 BOOL
326 APIENTRY
327 NtGdiSetMiterLimit(
328 IN HDC hdc,
329 IN DWORD dwNew,
330 IN OUT OPTIONAL PDWORD pdwOut)
331 {
332 DC *pDc;
333 gxf_long worker, worker1;
334 NTSTATUS Status = STATUS_SUCCESS;
335
336 if(!(pDc = DC_LockDc(hdc))) return FALSE;
337
338 worker.l = dwNew;
339 worker1.f = pDc->DcLevel.laPath.eMiterLimit;
340 pDc->DcLevel.laPath.eMiterLimit = worker.f;
341
342 if (pdwOut)
343 {
344 _SEH_TRY
345 {
346 ProbeForWrite(pdwOut,
347 sizeof(DWORD),
348 1);
349 *pdwOut = worker1.l;
350 }
351 _SEH_HANDLE
352 {
353 Status = _SEH_GetExceptionCode();
354 }
355 _SEH_END;
356 if (!NT_SUCCESS(Status))
357 {
358 SetLastNtError(Status);
359 DC_UnlockDc(pDc);
360 return FALSE;
361 }
362 }
363
364 DC_UnlockDc(pDc);
365 return TRUE;
366 }
367
368 BOOL
369 STDCALL
370 NtGdiStrokeAndFillPath(HDC hDC)
371 {
372 DC *pDc;
373 BOOL bRet = FALSE;
374
375 DPRINT("Enter %s\n", __FUNCTION__);
376
377 if(!(pDc = DC_LockDc(hDC))) return FALSE;
378
379 bRet = PATH_FillPath(pDc, &pDc->w.path);
380 if(bRet) bRet = PATH_StrokePath(pDc, &pDc->w.path);
381 if(bRet) PATH_EmptyPath(&pDc->w.path);
382
383 DC_UnlockDc(pDc);
384 return bRet;
385 }
386
387 BOOL
388 STDCALL
389 NtGdiStrokePath(HDC hDC)
390 {
391 DC *pDc;
392 BOOL bRet = FALSE;
393
394 DPRINT("Enter %s\n", __FUNCTION__);
395
396 if(!(pDc = DC_LockDc(hDC))) return FALSE;
397
398 bRet = PATH_StrokePath(pDc, &pDc->w.path);
399 PATH_EmptyPath(&pDc->w.path);
400
401 DC_UnlockDc(pDc);
402 return bRet;
403 }
404
405 BOOL
406 STDCALL
407 NtGdiWidenPath(HDC hDC)
408 {
409 UNIMPLEMENTED;
410 return FALSE;
411 }
412
413 BOOL STDCALL NtGdiSelectClipPath(HDC hDC,
414 int Mode)
415 {
416 HRGN hrgnPath;
417 BOOL success = FALSE;
418 PDC dc = DC_LockDc ( hDC );
419 PDC_ATTR Dc_Attr;
420
421 if( !dc ) return FALSE;
422 Dc_Attr = dc->pDc_Attr;
423 if(!Dc_Attr) Dc_Attr = &dc->Dc_Attr;
424 /* Check that path is closed */
425 if( dc->w.path.state != PATH_Closed )
426 {
427 SetLastWin32Error(ERROR_CAN_NOT_COMPLETE);
428 return FALSE;
429 }
430 /* Construct a region from the path */
431 else if( PATH_PathToRegion( &dc->w.path, Dc_Attr->jFillMode, &hrgnPath ) )
432 {
433 success = GdiExtSelectClipRgn( dc, hrgnPath, Mode ) != ERROR;
434 NtGdiDeleteObject( hrgnPath );
435
436 /* Empty the path */
437 if( success )
438 PATH_EmptyPath( &dc->w.path);
439 /* FIXME: Should this function delete the path even if it failed? */
440 }
441
442 DC_UnlockDc ( dc );
443 return success;
444 }
445
446 /***********************************************************************
447 * Exported functions
448 */
449
450
451 /* PATH_FillPath
452 *
453 *
454 */
455 BOOL
456 FASTCALL
457 PATH_FillPath( PDC dc, GdiPath *pPath )
458 {
459 INT mapMode, graphicsMode;
460 SIZE ptViewportExt, ptWindowExt;
461 POINTL ptViewportOrg, ptWindowOrg;
462 XFORM xform;
463 HRGN hrgn;
464 PDC_ATTR Dc_Attr = dc->pDc_Attr;
465
466 if(!Dc_Attr) Dc_Attr = &dc->Dc_Attr;
467
468 if( pPath->state != PATH_Closed )
469 {
470 SetLastWin32Error(ERROR_CAN_NOT_COMPLETE);
471 return FALSE;
472 }
473
474 if( PATH_PathToRegion( pPath, Dc_Attr->jFillMode, &hrgn ))
475 {
476 /* Since PaintRgn interprets the region as being in logical coordinates
477 * but the points we store for the path are already in device
478 * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
479 * Using SaveDC to save information about the mapping mode / world
480 * transform would be easier but would require more overhead, especially
481 * now that SaveDC saves the current path.
482 */
483
484 /* Save the information about the old mapping mode */
485 mapMode = Dc_Attr->iMapMode;
486 ptViewportExt = Dc_Attr->szlViewportExt;
487 ptViewportOrg = Dc_Attr->ptlViewportOrg;
488 ptWindowExt = Dc_Attr->szlWindowExt;
489 ptWindowOrg = Dc_Attr->ptlWindowOrg;
490
491 /* Save world transform
492 * NB: The Windows documentation on world transforms would lead one to
493 * believe that this has to be done only in GM_ADVANCED; however, my
494 * tests show that resetting the graphics mode to GM_COMPATIBLE does
495 * not reset the world transform.
496 */
497 xform = dc->DcLevel.xformWorld2Wnd;
498
499 /* Set MM_TEXT */
500 IntGdiSetMapMode( dc, MM_TEXT );
501 Dc_Attr->ptlViewportOrg.x = 0;
502 Dc_Attr->ptlViewportOrg.y = 0;
503 Dc_Attr->ptlWindowOrg.x = 0;
504 Dc_Attr->ptlWindowOrg.y = 0;
505
506 graphicsMode = Dc_Attr->iGraphicsMode;
507 Dc_Attr->iGraphicsMode = GM_ADVANCED;
508 IntGdiModifyWorldTransform( dc, &xform, MWT_IDENTITY );
509 Dc_Attr->iGraphicsMode = graphicsMode;
510
511 /* Paint the region */
512 IntGdiPaintRgn( dc, hrgn );
513 NtGdiDeleteObject( hrgn );
514 /* Restore the old mapping mode */
515 IntGdiSetMapMode( dc, mapMode );
516 Dc_Attr->szlViewportExt = ptViewportExt;
517 Dc_Attr->ptlViewportOrg = ptViewportOrg;
518 Dc_Attr->szlWindowExt = ptWindowExt;
519 Dc_Attr->ptlWindowOrg = ptWindowOrg;
520
521 /* Go to GM_ADVANCED temporarily to restore the world transform */
522 graphicsMode = Dc_Attr->iGraphicsMode;
523 Dc_Attr->iGraphicsMode = GM_ADVANCED;
524 IntGdiModifyWorldTransform( dc, &xform, MWT_MAX+1 );
525 Dc_Attr->iGraphicsMode = graphicsMode;
526 return TRUE;
527 }
528 return FALSE;
529 }
530
531 /* PATH_InitGdiPath
532 *
533 * Initializes the GdiPath structure.
534 */
535 VOID
536 FASTCALL
537 PATH_InitGdiPath ( GdiPath *pPath )
538 {
539 ASSERT(pPath!=NULL);
540
541 pPath->state=PATH_Null;
542 pPath->pPoints=NULL;
543 pPath->pFlags=NULL;
544 pPath->numEntriesUsed=0;
545 pPath->numEntriesAllocated=0;
546 }
547
548 /* PATH_DestroyGdiPath
549 *
550 * Destroys a GdiPath structure (frees the memory in the arrays).
551 */
552 VOID
553 FASTCALL
554 PATH_DestroyGdiPath ( GdiPath *pPath )
555 {
556 ASSERT(pPath!=NULL);
557
558 if (pPath->pPoints) ExFreePool(pPath->pPoints);
559 if (pPath->pFlags) ExFreePool(pPath->pFlags);
560 }
561
562 /* PATH_AssignGdiPath
563 *
564 * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
565 * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
566 * not just the pointers. Since this means that the arrays in pPathDest may
567 * need to be resized, pPathDest should have been initialized using
568 * PATH_InitGdiPath (in C++, this function would be an assignment operator,
569 * not a copy constructor).
570 * Returns TRUE if successful, else FALSE.
571 */
572 BOOL
573 FASTCALL
574 PATH_AssignGdiPath ( GdiPath *pPathDest, const GdiPath *pPathSrc )
575 {
576 ASSERT(pPathDest!=NULL && pPathSrc!=NULL);
577
578 /* Make sure destination arrays are big enough */
579 if ( !PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed) )
580 return FALSE;
581
582 /* Perform the copy operation */
583 memcpy(pPathDest->pPoints, pPathSrc->pPoints,
584 sizeof(POINT)*pPathSrc->numEntriesUsed);
585 memcpy(pPathDest->pFlags, pPathSrc->pFlags,
586 sizeof(BYTE)*pPathSrc->numEntriesUsed);
587
588 pPathDest->state=pPathSrc->state;
589 pPathDest->numEntriesUsed=pPathSrc->numEntriesUsed;
590 pPathDest->newStroke=pPathSrc->newStroke;
591
592 return TRUE;
593 }
594
595 /* PATH_MoveTo
596 *
597 * Should be called when a MoveTo is performed on a DC that has an
598 * open path. This starts a new stroke. Returns TRUE if successful, else
599 * FALSE.
600 */
601 BOOL
602 FASTCALL
603 PATH_MoveTo ( PDC dc )
604 {
605
606 /* Check that path is open */
607 if ( dc->w.path.state != PATH_Open )
608 /* FIXME: Do we have to call SetLastError? */
609 return FALSE;
610
611 /* Start a new stroke */
612 dc->w.path.newStroke = TRUE;
613
614 return TRUE;
615 }
616
617 /* PATH_LineTo
618 *
619 * Should be called when a LineTo is performed on a DC that has an
620 * open path. This adds a PT_LINETO entry to the path (and possibly
621 * a PT_MOVETO entry, if this is the first LineTo in a stroke).
622 * Returns TRUE if successful, else FALSE.
623 */
624 BOOL
625 FASTCALL
626 PATH_LineTo ( PDC dc, INT x, INT y )
627 {
628 POINT point, pointCurPos;
629
630 /* Check that path is open */
631 if ( dc->w.path.state != PATH_Open )
632 return FALSE;
633
634 /* Convert point to device coordinates */
635 point.x=x;
636 point.y=y;
637 CoordLPtoDP ( dc, &point );
638
639 /* Add a PT_MOVETO if necessary */
640 if ( dc->w.path.newStroke )
641 {
642 dc->w.path.newStroke = FALSE;
643 IntGetCurrentPositionEx ( dc, &pointCurPos );
644 CoordLPtoDP ( dc, &pointCurPos );
645 if ( !PATH_AddEntry(&dc->w.path, &pointCurPos, PT_MOVETO) )
646 return FALSE;
647 }
648
649 /* Add a PT_LINETO entry */
650 return PATH_AddEntry(&dc->w.path, &point, PT_LINETO);
651 }
652
653 /* PATH_Rectangle
654 *
655 * Should be called when a call to Rectangle is performed on a DC that has
656 * an open path. Returns TRUE if successful, else FALSE.
657 */
658 BOOL
659 FASTCALL
660 PATH_Rectangle ( PDC dc, INT x1, INT y1, INT x2, INT y2 )
661 {
662 POINT corners[2], pointTemp;
663 INT temp;
664
665 /* Check that path is open */
666 if ( dc->w.path.state != PATH_Open )
667 return FALSE;
668
669 /* Convert points to device coordinates */
670 corners[0].x=x1;
671 corners[0].y=y1;
672 corners[1].x=x2;
673 corners[1].y=y2;
674 IntLPtoDP ( dc, corners, 2 );
675
676 /* Make sure first corner is top left and second corner is bottom right */
677 if ( corners[0].x > corners[1].x )
678 {
679 temp=corners[0].x;
680 corners[0].x=corners[1].x;
681 corners[1].x=temp;
682 }
683 if ( corners[0].y > corners[1].y )
684 {
685 temp=corners[0].y;
686 corners[0].y=corners[1].y;
687 corners[1].y=temp;
688 }
689
690 /* In GM_COMPATIBLE, don't include bottom and right edges */
691 if ( IntGetGraphicsMode(dc) == GM_COMPATIBLE )
692 {
693 corners[1].x--;
694 corners[1].y--;
695 }
696
697 /* Close any previous figure */
698 IntGdiCloseFigure(dc);
699
700 /* Add four points to the path */
701 pointTemp.x=corners[1].x;
702 pointTemp.y=corners[0].y;
703 if ( !PATH_AddEntry(&dc->w.path, &pointTemp, PT_MOVETO) )
704 return FALSE;
705 if ( !PATH_AddEntry(&dc->w.path, corners, PT_LINETO) )
706 return FALSE;
707 pointTemp.x=corners[0].x;
708 pointTemp.y=corners[1].y;
709 if ( !PATH_AddEntry(&dc->w.path, &pointTemp, PT_LINETO) )
710 return FALSE;
711 if ( !PATH_AddEntry(&dc->w.path, corners+1, PT_LINETO) )
712 return FALSE;
713
714 /* Close the rectangle figure */
715 IntGdiCloseFigure(dc) ;
716
717 return TRUE;
718 }
719
720 /* PATH_RoundRect
721 *
722 * Should be called when a call to RoundRect is performed on a DC that has
723 * an open path. Returns TRUE if successful, else FALSE.
724 *
725 * FIXME: it adds the same entries to the path as windows does, but there
726 * is an error in the bezier drawing code so that there are small pixel-size
727 * gaps when the resulting path is drawn by StrokePath()
728 */
729 BOOL FASTCALL PATH_RoundRect(DC *dc, INT x1, INT y1, INT x2, INT y2, INT ell_width, INT ell_height)
730 {
731 GdiPath *pPath = &dc->w.path;
732 POINT corners[2], pointTemp;
733 FLOAT_POINT ellCorners[2];
734
735 /* Check that path is open */
736 if(pPath->state!=PATH_Open)
737 return FALSE;
738
739 if(!PATH_CheckCorners(dc,corners,x1,y1,x2,y2))
740 return FALSE;
741
742 /* Add points to the roundrect path */
743 ellCorners[0].x = corners[1].x-ell_width;
744 ellCorners[0].y = corners[0].y;
745 ellCorners[1].x = corners[1].x;
746 ellCorners[1].y = corners[0].y+ell_height;
747 if(!PATH_DoArcPart(pPath, ellCorners, 0, -M_PI_2, TRUE))
748 return FALSE;
749 pointTemp.x = corners[0].x+ell_width/2;
750 pointTemp.y = corners[0].y;
751 if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
752 return FALSE;
753 ellCorners[0].x = corners[0].x;
754 ellCorners[1].x = corners[0].x+ell_width;
755 if(!PATH_DoArcPart(pPath, ellCorners, -M_PI_2, -M_PI, FALSE))
756 return FALSE;
757 pointTemp.x = corners[0].x;
758 pointTemp.y = corners[1].y-ell_height/2;
759 if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
760 return FALSE;
761 ellCorners[0].y = corners[1].y-ell_height;
762 ellCorners[1].y = corners[1].y;
763 if(!PATH_DoArcPart(pPath, ellCorners, M_PI, M_PI_2, FALSE))
764 return FALSE;
765 pointTemp.x = corners[1].x-ell_width/2;
766 pointTemp.y = corners[1].y;
767 if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
768 return FALSE;
769 ellCorners[0].x = corners[1].x-ell_width;
770 ellCorners[1].x = corners[1].x;
771 if(!PATH_DoArcPart(pPath, ellCorners, M_PI_2, 0, FALSE))
772 return FALSE;
773
774 IntGdiCloseFigure(dc);
775
776 return TRUE;
777 }
778
779 /* PATH_Ellipse
780 *
781 * Should be called when a call to Ellipse is performed on a DC that has
782 * an open path. This adds four Bezier splines representing the ellipse
783 * to the path. Returns TRUE if successful, else FALSE.
784 */
785 BOOL
786 FASTCALL
787 PATH_Ellipse ( PDC dc, INT x1, INT y1, INT x2, INT y2 )
788 {
789 /* TODO: This should probably be revised to call PATH_AngleArc */
790 /* (once it exists) */
791 BOOL Ret = PATH_Arc ( dc, x1, y1, x2, y2, x1, (y1+y2)/2, x1, (y1+y2)/2, GdiTypeArc );
792 if (Ret) IntGdiCloseFigure(dc);
793 return Ret;
794 }
795
796 /* PATH_Arc
797 *
798 * Should be called when a call to Arc is performed on a DC that has
799 * an open path. This adds up to five Bezier splines representing the arc
800 * to the path. When 'lines' is 1, we add 1 extra line to get a chord,
801 * and when 'lines' is 2, we add 2 extra lines to get a pie.
802 * Returns TRUE if successful, else FALSE.
803 */
804 BOOL
805 FASTCALL
806 PATH_Arc ( PDC dc, INT x1, INT y1, INT x2, INT y2,
807 INT xStart, INT yStart, INT xEnd, INT yEnd, INT lines)
808 {
809 double angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
810 /* Initialize angleEndQuadrant to silence gcc's warning */
811 double x, y;
812 FLOAT_POINT corners[2], pointStart, pointEnd;
813 POINT centre;
814 BOOL start, end;
815 INT temp;
816 BOOL clockwise;
817
818 /* FIXME: This function should check for all possible error returns */
819 /* FIXME: Do we have to respect newStroke? */
820
821 ASSERT ( dc );
822
823 clockwise = ((dc->DcLevel.flPath & DCPATH_CLOCKWISE) != 0);
824
825 /* Check that path is open */
826 if ( dc->w.path.state != PATH_Open )
827 return FALSE;
828
829 /* FIXME: Do we have to close the current figure? */
830
831 /* Check for zero height / width */
832 /* FIXME: Only in GM_COMPATIBLE? */
833 if ( x1==x2 || y1==y2 )
834 return TRUE;
835
836 /* Convert points to device coordinates */
837 corners[0].x=(FLOAT)x1;
838 corners[0].y=(FLOAT)y1;
839 corners[1].x=(FLOAT)x2;
840 corners[1].y=(FLOAT)y2;
841 pointStart.x=(FLOAT)xStart;
842 pointStart.y=(FLOAT)yStart;
843 pointEnd.x=(FLOAT)xEnd;
844 pointEnd.y=(FLOAT)yEnd;
845 INTERNAL_LPTODP_FLOAT(dc, corners);
846 INTERNAL_LPTODP_FLOAT(dc, corners+1);
847 INTERNAL_LPTODP_FLOAT(dc, &pointStart);
848 INTERNAL_LPTODP_FLOAT(dc, &pointEnd);
849
850 /* Make sure first corner is top left and second corner is bottom right */
851 if ( corners[0].x > corners[1].x )
852 {
853 temp=corners[0].x;
854 corners[0].x=corners[1].x;
855 corners[1].x=temp;
856 }
857 if ( corners[0].y > corners[1].y )
858 {
859 temp=corners[0].y;
860 corners[0].y=corners[1].y;
861 corners[1].y=temp;
862 }
863
864 /* Compute start and end angle */
865 PATH_NormalizePoint(corners, &pointStart, &x, &y);
866 angleStart=atan2(y, x);
867 PATH_NormalizePoint(corners, &pointEnd, &x, &y);
868 angleEnd=atan2(y, x);
869
870 /* Make sure the end angle is "on the right side" of the start angle */
871 if ( clockwise )
872 {
873 if ( angleEnd <= angleStart )
874 {
875 angleEnd+=2*M_PI;
876 ASSERT(angleEnd>=angleStart);
877 }
878 }
879 else
880 {
881 if(angleEnd>=angleStart)
882 {
883 angleEnd-=2*M_PI;
884 ASSERT(angleEnd<=angleStart);
885 }
886 }
887
888 /* In GM_COMPATIBLE, don't include bottom and right edges */
889 if ( IntGetGraphicsMode(dc) == GM_COMPATIBLE )
890 {
891 corners[1].x--;
892 corners[1].y--;
893 }
894
895 /* Add the arc to the path with one Bezier spline per quadrant that the
896 * arc spans */
897 start=TRUE;
898 end=FALSE;
899 do
900 {
901 /* Determine the start and end angles for this quadrant */
902 if(start)
903 {
904 angleStartQuadrant=angleStart;
905 if ( clockwise )
906 angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
907 else
908 angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
909 }
910 else
911 {
912 angleStartQuadrant=angleEndQuadrant;
913 if ( clockwise )
914 angleEndQuadrant+=M_PI_2;
915 else
916 angleEndQuadrant-=M_PI_2;
917 }
918
919 /* Have we reached the last part of the arc? */
920 if ( (clockwise && angleEnd<angleEndQuadrant)
921 || (!clockwise && angleEnd>angleEndQuadrant)
922 )
923 {
924 /* Adjust the end angle for this quadrant */
925 angleEndQuadrant = angleEnd;
926 end = TRUE;
927 }
928
929 /* Add the Bezier spline to the path */
930 PATH_DoArcPart ( &dc->w.path, corners, angleStartQuadrant, angleEndQuadrant, start );
931 start = FALSE;
932 } while(!end);
933
934 /* chord: close figure. pie: add line and close figure */
935 if(lines==GdiTypeChord) // 1
936 {
937 IntGdiCloseFigure(dc);
938 }
939 else if(lines==GdiTypePie) // 2
940 {
941 centre.x = (corners[0].x+corners[1].x)/2;
942 centre.y = (corners[0].y+corners[1].y)/2;
943 if(!PATH_AddEntry(&dc->w.path, &centre, PT_LINETO | PT_CLOSEFIGURE))
944 return FALSE;
945 }
946
947 return TRUE;
948 }
949
950 BOOL
951 FASTCALL
952 PATH_PolyBezierTo ( PDC dc, const POINT *pts, DWORD cbPoints )
953 {
954 POINT pt;
955 ULONG i;
956
957 ASSERT ( dc );
958 ASSERT ( pts );
959 ASSERT ( cbPoints );
960
961 /* Check that path is open */
962 if ( dc->w.path.state != PATH_Open )
963 return FALSE;
964
965 /* Add a PT_MOVETO if necessary */
966 if ( dc->w.path.newStroke )
967 {
968 dc->w.path.newStroke=FALSE;
969 IntGetCurrentPositionEx ( dc, &pt );
970 CoordLPtoDP ( dc, &pt );
971 if ( !PATH_AddEntry(&dc->w.path, &pt, PT_MOVETO) )
972 return FALSE;
973 }
974
975 for(i = 0; i < cbPoints; i++)
976 {
977 pt = pts[i];
978 CoordLPtoDP ( dc, &pt );
979 PATH_AddEntry(&dc->w.path, &pt, PT_BEZIERTO);
980 }
981 return TRUE;
982 }
983
984 BOOL
985 FASTCALL
986 PATH_PolyBezier ( PDC dc, const POINT *pts, DWORD cbPoints )
987 {
988 POINT pt;
989 ULONG i;
990
991 ASSERT ( dc );
992 ASSERT ( pts );
993 ASSERT ( cbPoints );
994
995 /* Check that path is open */
996 if ( dc->w.path.state != PATH_Open )
997 return FALSE;
998
999 for ( i = 0; i < cbPoints; i++ )
1000 {
1001 pt = pts[i];
1002 CoordLPtoDP ( dc, &pt );
1003 PATH_AddEntry ( &dc->w.path, &pt, (i == 0) ? PT_MOVETO : PT_BEZIERTO );
1004 }
1005
1006 return TRUE;
1007 }
1008
1009 BOOL
1010 FASTCALL
1011 PATH_Polyline ( PDC dc, const POINT *pts, DWORD cbPoints )
1012 {
1013 POINT pt;
1014 ULONG i;
1015
1016 ASSERT ( dc );
1017 ASSERT ( pts );
1018 ASSERT ( cbPoints );
1019
1020 /* Check that path is open */
1021 if ( dc->w.path.state != PATH_Open )
1022 return FALSE;
1023
1024 for ( i = 0; i < cbPoints; i++ )
1025 {
1026 pt = pts[i];
1027 CoordLPtoDP ( dc, &pt );
1028 PATH_AddEntry(&dc->w.path, &pt, (i == 0) ? PT_MOVETO : PT_LINETO);
1029 }
1030 return TRUE;
1031 }
1032
1033 BOOL
1034 FASTCALL
1035 PATH_PolylineTo ( PDC dc, const POINT *pts, DWORD cbPoints )
1036 {
1037 POINT pt;
1038 ULONG i;
1039
1040 ASSERT ( dc );
1041 ASSERT ( pts );
1042 ASSERT ( cbPoints );
1043
1044 /* Check that path is open */
1045 if ( dc->w.path.state != PATH_Open )
1046 return FALSE;
1047
1048 /* Add a PT_MOVETO if necessary */
1049 if ( dc->w.path.newStroke )
1050 {
1051 dc->w.path.newStroke = FALSE;
1052 IntGetCurrentPositionEx ( dc, &pt );
1053 CoordLPtoDP ( dc, &pt );
1054 if ( !PATH_AddEntry(&dc->w.path, &pt, PT_MOVETO) )
1055 return FALSE;
1056 }
1057
1058 for(i = 0; i < cbPoints; i++)
1059 {
1060 pt = pts[i];
1061 CoordLPtoDP ( dc, &pt );
1062 PATH_AddEntry(&dc->w.path, &pt, PT_LINETO);
1063 }
1064
1065 return TRUE;
1066 }
1067
1068
1069 BOOL
1070 FASTCALL
1071 PATH_Polygon ( PDC dc, const POINT *pts, DWORD cbPoints )
1072 {
1073 POINT pt;
1074 ULONG i;
1075
1076 ASSERT ( dc );
1077 ASSERT ( pts );
1078
1079 /* Check that path is open */
1080 if ( dc->w.path.state != PATH_Open )
1081 return FALSE;
1082
1083 for(i = 0; i < cbPoints; i++)
1084 {
1085 pt = pts[i];
1086 CoordLPtoDP ( dc, &pt );
1087 PATH_AddEntry(&dc->w.path, &pt, (i == 0) ? PT_MOVETO :
1088 ((i == cbPoints-1) ? PT_LINETO | PT_CLOSEFIGURE :
1089 PT_LINETO));
1090 }
1091 return TRUE;
1092 }
1093
1094 BOOL
1095 FASTCALL
1096 PATH_PolyPolygon ( PDC dc, const POINT* pts, const INT* counts, UINT polygons )
1097 {
1098 POINT pt, startpt;
1099 ULONG poly, point, i;
1100
1101 ASSERT ( dc );
1102 ASSERT ( pts );
1103 ASSERT ( counts );
1104 ASSERT ( polygons );
1105
1106 /* Check that path is open */
1107 if ( dc->w.path.state != PATH_Open );
1108 return FALSE;
1109
1110 for(i = 0, poly = 0; poly < polygons; poly++)
1111 {
1112 for(point = 0; point < (ULONG) counts[poly]; point++, i++)
1113 {
1114 pt = pts[i];
1115 CoordLPtoDP ( dc, &pt );
1116 if(point == 0) startpt = pt;
1117 PATH_AddEntry(&dc->w.path, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1118 }
1119 /* win98 adds an extra line to close the figure for some reason */
1120 PATH_AddEntry(&dc->w.path, &startpt, PT_LINETO | PT_CLOSEFIGURE);
1121 }
1122 return TRUE;
1123 }
1124
1125 BOOL
1126 FASTCALL
1127 PATH_PolyPolyline ( PDC dc, const POINT* pts, const DWORD* counts, DWORD polylines )
1128 {
1129 POINT pt;
1130 ULONG poly, point, i;
1131
1132 ASSERT ( dc );
1133 ASSERT ( pts );
1134 ASSERT ( counts );
1135 ASSERT ( polylines );
1136
1137 /* Check that path is open */
1138 if ( dc->w.path.state != PATH_Open )
1139 return FALSE;
1140
1141 for(i = 0, poly = 0; poly < polylines; poly++)
1142 {
1143 for(point = 0; point < counts[poly]; point++, i++)
1144 {
1145 pt = pts[i];
1146 CoordLPtoDP ( dc, &pt );
1147 PATH_AddEntry(&dc->w.path, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1148 }
1149 }
1150 return TRUE;
1151 }
1152
1153 /***********************************************************************
1154 * Internal functions
1155 */
1156
1157 /* PATH_CheckCorners
1158 *
1159 * Helper function for PATH_RoundRect() and PATH_Rectangle()
1160 */
1161 BOOL PATH_CheckCorners(DC *dc, POINT corners[], INT x1, INT y1, INT x2, INT y2)
1162 {
1163 INT temp;
1164 PDC_ATTR Dc_Attr = dc->pDc_Attr;
1165 if(!Dc_Attr) Dc_Attr = &dc->Dc_Attr;
1166
1167 /* Convert points to device coordinates */
1168 corners[0].x=x1;
1169 corners[0].y=y1;
1170 corners[1].x=x2;
1171 corners[1].y=y2;
1172 CoordLPtoDP(dc, &corners[0]);
1173 CoordLPtoDP(dc, &corners[1]);
1174
1175 /* Make sure first corner is top left and second corner is bottom right */
1176 if(corners[0].x>corners[1].x)
1177 {
1178 temp=corners[0].x;
1179 corners[0].x=corners[1].x;
1180 corners[1].x=temp;
1181 }
1182 if(corners[0].y>corners[1].y)
1183 {
1184 temp=corners[0].y;
1185 corners[0].y=corners[1].y;
1186 corners[1].y=temp;
1187 }
1188
1189 /* In GM_COMPATIBLE, don't include bottom and right edges */
1190 if(Dc_Attr->iGraphicsMode==GM_COMPATIBLE)
1191 {
1192 corners[1].x--;
1193 corners[1].y--;
1194 }
1195
1196 return TRUE;
1197 }
1198
1199
1200 /* PATH_AddFlatBezier
1201 *
1202 */
1203 BOOL
1204 FASTCALL
1205 PATH_AddFlatBezier ( GdiPath *pPath, POINT *pt, BOOL closed )
1206 {
1207 POINT *pts;
1208 INT no, i;
1209
1210 pts = GDI_Bezier( pt, 4, &no );
1211 if ( !pts ) return FALSE;
1212
1213 for(i = 1; i < no; i++)
1214 PATH_AddEntry(pPath, &pts[i], (i == no-1 && closed) ? PT_LINETO | PT_CLOSEFIGURE : PT_LINETO);
1215
1216 ExFreePool(pts);
1217 return TRUE;
1218 }
1219
1220 /* PATH_FlattenPath
1221 *
1222 * Replaces Beziers with line segments
1223 *
1224 */
1225 BOOL
1226 FASTCALL
1227 PATH_FlattenPath(GdiPath *pPath)
1228 {
1229 GdiPath newPath;
1230 INT srcpt;
1231
1232 RtlZeroMemory(&newPath, sizeof(newPath));
1233 newPath.state = PATH_Open;
1234 for(srcpt = 0; srcpt < pPath->numEntriesUsed; srcpt++) {
1235 switch(pPath->pFlags[srcpt] & ~PT_CLOSEFIGURE) {
1236 case PT_MOVETO:
1237 case PT_LINETO:
1238 PATH_AddEntry(&newPath, &pPath->pPoints[srcpt], pPath->pFlags[srcpt]);
1239 break;
1240 case PT_BEZIERTO:
1241 PATH_AddFlatBezier(&newPath, &pPath->pPoints[srcpt-1], pPath->pFlags[srcpt+2] & PT_CLOSEFIGURE);
1242 srcpt += 2;
1243 break;
1244 }
1245 }
1246 newPath.state = PATH_Closed;
1247 PATH_AssignGdiPath(pPath, &newPath);
1248 PATH_EmptyPath(&newPath);
1249 return TRUE;
1250 }
1251
1252 /* PATH_PathToRegion
1253 *
1254 * Creates a region from the specified path using the specified polygon
1255 * filling mode. The path is left unchanged. A handle to the region that
1256 * was created is stored in *pHrgn. If successful, TRUE is returned; if an
1257 * error occurs, SetLastError is called with the appropriate value and
1258 * FALSE is returned.
1259 */
1260
1261
1262 BOOL
1263 FASTCALL
1264 PATH_PathToRegion ( GdiPath *pPath, INT nPolyFillMode, HRGN *pHrgn )
1265 {
1266 int numStrokes, iStroke, i;
1267 INT *pNumPointsInStroke;
1268 HRGN hrgn = 0;
1269
1270 ASSERT(pPath!=NULL);
1271 ASSERT(pHrgn!=NULL);
1272
1273 PATH_FlattenPath ( pPath );
1274
1275 /* FIXME: What happens when number of points is zero? */
1276
1277 /* First pass: Find out how many strokes there are in the path */
1278 /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
1279 numStrokes=0;
1280 for(i=0; i<pPath->numEntriesUsed; i++)
1281 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
1282 numStrokes++;
1283
1284 /* Allocate memory for number-of-points-in-stroke array */
1285 pNumPointsInStroke=(int *)ExAllocatePoolWithTag(PagedPool, sizeof(int) * numStrokes, TAG_PATH);
1286 if(!pNumPointsInStroke)
1287 {
1288 // SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1289 return FALSE;
1290 }
1291
1292 /* Second pass: remember number of points in each polygon */
1293 iStroke=-1; /* Will get incremented to 0 at beginning of first stroke */
1294 for(i=0; i<pPath->numEntriesUsed; i++)
1295 {
1296 /* Is this the beginning of a new stroke? */
1297 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
1298 {
1299 iStroke++;
1300 pNumPointsInStroke[iStroke]=0;
1301 }
1302
1303 pNumPointsInStroke[iStroke]++;
1304 }
1305
1306 /* Create a region from the strokes */
1307 /* hrgn=CreatePolyPolygonRgn(pPath->pPoints, pNumPointsInStroke,
1308 numStrokes, nPolyFillMode); FIXME: reinclude when region code implemented */
1309 if(hrgn==(HRGN)0)
1310 {
1311 // SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1312 return FALSE;
1313 }
1314
1315 /* Free memory for number-of-points-in-stroke array */
1316 ExFreePool(pNumPointsInStroke);
1317
1318 /* Success! */
1319 *pHrgn=hrgn;
1320 return TRUE;
1321 }
1322
1323 /* PATH_EmptyPath
1324 *
1325 * Removes all entries from the path and sets the path state to PATH_Null.
1326 */
1327 VOID
1328 FASTCALL
1329 PATH_EmptyPath ( GdiPath *pPath )
1330 {
1331 ASSERT(pPath!=NULL);
1332
1333 pPath->state=PATH_Null;
1334 pPath->numEntriesUsed=0;
1335 }
1336
1337 /* PATH_AddEntry
1338 *
1339 * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
1340 * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
1341 * successful, FALSE otherwise (e.g. if not enough memory was available).
1342 */
1343 BOOL
1344 FASTCALL
1345 PATH_AddEntry ( GdiPath *pPath, const POINT *pPoint, BYTE flags )
1346 {
1347 ASSERT(pPath!=NULL);
1348
1349 /* FIXME: If newStroke is true, perhaps we want to check that we're
1350 * getting a PT_MOVETO
1351 */
1352
1353 /* Check that path is open */
1354 if ( pPath->state != PATH_Open )
1355 return FALSE;
1356
1357 /* Reserve enough memory for an extra path entry */
1358 if ( !PATH_ReserveEntries(pPath, pPath->numEntriesUsed+1) )
1359 return FALSE;
1360
1361 /* Store information in path entry */
1362 pPath->pPoints[pPath->numEntriesUsed]=*pPoint;
1363 pPath->pFlags[pPath->numEntriesUsed]=flags;
1364
1365 /* If this is PT_CLOSEFIGURE, we have to start a new stroke next time */
1366 if((flags & PT_CLOSEFIGURE) == PT_CLOSEFIGURE)
1367 pPath->newStroke=TRUE;
1368
1369 /* Increment entry count */
1370 pPath->numEntriesUsed++;
1371
1372 return TRUE;
1373 }
1374
1375 /* PATH_ReserveEntries
1376 *
1377 * Ensures that at least "numEntries" entries (for points and flags) have
1378 * been allocated; allocates larger arrays and copies the existing entries
1379 * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
1380 */
1381 BOOL
1382 FASTCALL
1383 PATH_ReserveEntries ( GdiPath *pPath, INT numEntries )
1384 {
1385 INT numEntriesToAllocate;
1386 POINT *pPointsNew;
1387 BYTE *pFlagsNew;
1388
1389 ASSERT(pPath!=NULL);
1390 ASSERT(numEntries>=0);
1391
1392 /* Do we have to allocate more memory? */
1393 if(numEntries > pPath->numEntriesAllocated)
1394 {
1395 /* Find number of entries to allocate. We let the size of the array
1396 * grow exponentially, since that will guarantee linear time
1397 * complexity. */
1398 if(pPath->numEntriesAllocated)
1399 {
1400 numEntriesToAllocate=pPath->numEntriesAllocated;
1401 while(numEntriesToAllocate<numEntries)
1402 numEntriesToAllocate=numEntriesToAllocate*GROW_FACTOR_NUMER/GROW_FACTOR_DENOM;
1403 } else
1404 numEntriesToAllocate=numEntries;
1405
1406 /* Allocate new arrays */
1407 pPointsNew=(POINT *)ExAllocatePoolWithTag(PagedPool, numEntriesToAllocate * sizeof(POINT), TAG_PATH);
1408 if(!pPointsNew)
1409 return FALSE;
1410 pFlagsNew=(BYTE *)ExAllocatePoolWithTag(PagedPool, numEntriesToAllocate * sizeof(BYTE), TAG_PATH);
1411 if(!pFlagsNew)
1412 {
1413 ExFreePool(pPointsNew);
1414 return FALSE;
1415 }
1416
1417 /* Copy old arrays to new arrays and discard old arrays */
1418 if(pPath->pPoints)
1419 {
1420 ASSERT(pPath->pFlags);
1421
1422 memcpy(pPointsNew, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
1423 memcpy(pFlagsNew, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
1424
1425 ExFreePool(pPath->pPoints);
1426 ExFreePool(pPath->pFlags);
1427 }
1428 pPath->pPoints=pPointsNew;
1429 pPath->pFlags=pFlagsNew;
1430 pPath->numEntriesAllocated=numEntriesToAllocate;
1431 }
1432
1433 return TRUE;
1434 }
1435
1436 /* PATH_DoArcPart
1437 *
1438 * Creates a Bezier spline that corresponds to part of an arc and appends the
1439 * corresponding points to the path. The start and end angles are passed in
1440 * "angleStart" and "angleEnd"; these angles should span a quarter circle
1441 * at most. If "addMoveTo" is true, a PT_MOVETO entry for the first control
1442 * point is added to the path; otherwise, it is assumed that the current
1443 * position is equal to the first control point.
1444 */
1445 BOOL
1446 FASTCALL
1447 PATH_DoArcPart ( GdiPath *pPath, FLOAT_POINT corners[],
1448 double angleStart, double angleEnd, BOOL addMoveTo )
1449 {
1450 double halfAngle, a;
1451 double xNorm[4], yNorm[4];
1452 POINT point;
1453 int i;
1454
1455 ASSERT(fabs(angleEnd-angleStart)<=M_PI_2);
1456
1457 /* FIXME: Is there an easier way of computing this? */
1458
1459 /* Compute control points */
1460 halfAngle=(angleEnd-angleStart)/2.0;
1461 if(fabs(halfAngle)>1e-8)
1462 {
1463 a=4.0/3.0*(1-cos(halfAngle))/sin(halfAngle);
1464 xNorm[0]=cos(angleStart);
1465 yNorm[0]=sin(angleStart);
1466 xNorm[1]=xNorm[0] - a*yNorm[0];
1467 yNorm[1]=yNorm[0] + a*xNorm[0];
1468 xNorm[3]=cos(angleEnd);
1469 yNorm[3]=sin(angleEnd);
1470 xNorm[2]=xNorm[3] + a*yNorm[3];
1471 yNorm[2]=yNorm[3] - a*xNorm[3];
1472 } else
1473 for(i=0; i<4; i++)
1474 {
1475 xNorm[i]=cos(angleStart);
1476 yNorm[i]=sin(angleStart);
1477 }
1478
1479 /* Add starting point to path if desired */
1480 if(addMoveTo)
1481 {
1482 PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
1483 if(!PATH_AddEntry(pPath, &point, PT_MOVETO))
1484 return FALSE;
1485 }
1486
1487 /* Add remaining control points */
1488 for(i=1; i<4; i++)
1489 {
1490 PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &point);
1491 if(!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
1492 return FALSE;
1493 }
1494
1495 return TRUE;
1496 }
1497
1498 /* PATH_ScaleNormalizedPoint
1499 *
1500 * Scales a normalized point (x, y) with respect to the box whose corners are
1501 * passed in "corners". The point is stored in "*pPoint". The normalized
1502 * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
1503 * (1.0, 1.0) correspond to corners[1].
1504 */
1505 VOID
1506 FASTCALL
1507 PATH_ScaleNormalizedPoint ( FLOAT_POINT corners[], double x,
1508 double y, POINT *pPoint )
1509 {
1510 ASSERT ( corners );
1511 ASSERT ( pPoint );
1512 pPoint->x=GDI_ROUND( (double)corners[0].x + (double)(corners[1].x-corners[0].x)*0.5*(x+1.0) );
1513 pPoint->y=GDI_ROUND( (double)corners[0].y + (double)(corners[1].y-corners[0].y)*0.5*(y+1.0) );
1514 }
1515
1516 /* PATH_NormalizePoint
1517 *
1518 * Normalizes a point with respect to the box whose corners are passed in
1519 * corners. The normalized coordinates are stored in *pX and *pY.
1520 */
1521 VOID
1522 FASTCALL
1523 PATH_NormalizePoint ( FLOAT_POINT corners[],
1524 const FLOAT_POINT *pPoint,
1525 double *pX, double *pY)
1526 {
1527 ASSERT ( corners );
1528 ASSERT ( pPoint );
1529 ASSERT ( pX );
1530 ASSERT ( pY );
1531 *pX=(double)(pPoint->x-corners[0].x)/(double)(corners[1].x-corners[0].x) * 2.0 - 1.0;
1532 *pY=(double)(pPoint->y-corners[0].y)/(double)(corners[1].y-corners[0].y) * 2.0 - 1.0;
1533 }
1534
1535
1536 BOOL FASTCALL PATH_StrokePath(DC *dc, GdiPath *pPath)
1537 {
1538 BOOL ret = FALSE;
1539 INT i=0;
1540 INT nLinePts, nAlloc;
1541 POINT *pLinePts = NULL;
1542 POINT ptViewportOrg, ptWindowOrg;
1543 SIZE szViewportExt, szWindowExt;
1544 DWORD mapMode, graphicsMode;
1545 XFORM xform;
1546 PDC_ATTR Dc_Attr = dc->pDc_Attr;
1547
1548 DPRINT("Enter %s\n", __FUNCTION__);
1549
1550 if(pPath->state != PATH_Closed)
1551 return FALSE;
1552 if(!Dc_Attr) Dc_Attr = &dc->Dc_Attr;
1553 /* Save the mapping mode info */
1554 mapMode = Dc_Attr->iMapMode;
1555 IntGetViewportExtEx(dc, &szViewportExt);
1556 IntGetViewportOrgEx(dc, &ptViewportOrg);
1557 IntGetWindowExtEx(dc, &szWindowExt);
1558 IntGetWindowOrgEx(dc, &ptWindowOrg);
1559 xform = dc->DcLevel.xformWorld2Wnd;
1560
1561 /* Set MM_TEXT */
1562 Dc_Attr->iMapMode = MM_TEXT;
1563 Dc_Attr->ptlViewportOrg.x = 0;
1564 Dc_Attr->ptlViewportOrg.y = 0;
1565 Dc_Attr->ptlWindowOrg.x = 0;
1566 Dc_Attr->ptlWindowOrg.y = 0;
1567 graphicsMode = Dc_Attr->iGraphicsMode;
1568 Dc_Attr->iGraphicsMode = GM_ADVANCED;
1569 IntGdiModifyWorldTransform(dc, &xform, MWT_IDENTITY);
1570 Dc_Attr->iGraphicsMode = graphicsMode;
1571
1572 /* Allocate enough memory for the worst case without beziers (one PT_MOVETO
1573 * and the rest PT_LINETO with PT_CLOSEFIGURE at the end) plus some buffer
1574 * space in case we get one to keep the number of reallocations small. */
1575 nAlloc = pPath->numEntriesUsed + 1 + 300;
1576 pLinePts = ExAllocatePoolWithTag(PagedPool, nAlloc * sizeof(POINT), TAG_PATH);
1577 if(!pLinePts)
1578 {
1579 DPRINT1("Can't allocate pool!\n");
1580 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
1581 goto end;
1582 }
1583 nLinePts = 0;
1584
1585 for(i = 0; i < pPath->numEntriesUsed; i++)
1586 {
1587 if((i == 0 || (pPath->pFlags[i-1] & PT_CLOSEFIGURE))
1588 && (pPath->pFlags[i] != PT_MOVETO))
1589 {
1590 DPRINT1("Expected PT_MOVETO %s, got path flag %d\n",
1591 i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
1592 (INT)pPath->pFlags[i]);
1593 goto end;
1594 }
1595
1596 switch(pPath->pFlags[i])
1597 {
1598 case PT_MOVETO:
1599 DPRINT("Got PT_MOVETO (%ld, %ld)\n",
1600 pPath->pPoints[i].x, pPath->pPoints[i].y);
1601 if(nLinePts >= 2) IntGdiPolyline(dc, pLinePts, nLinePts);
1602 nLinePts = 0;
1603 pLinePts[nLinePts++] = pPath->pPoints[i];
1604 break;
1605 case PT_LINETO:
1606 case (PT_LINETO | PT_CLOSEFIGURE):
1607 DPRINT("Got PT_LINETO (%ld, %ld)\n",
1608 pPath->pPoints[i].x, pPath->pPoints[i].y);
1609 pLinePts[nLinePts++] = pPath->pPoints[i];
1610 break;
1611 case PT_BEZIERTO:
1612 DPRINT("Got PT_BEZIERTO\n");
1613 if(pPath->pFlags[i+1] != PT_BEZIERTO ||
1614 (pPath->pFlags[i+2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO)
1615 {
1616 DPRINT1("Path didn't contain 3 successive PT_BEZIERTOs\n");
1617 ret = FALSE;
1618 goto end;
1619 }
1620 else
1621 {
1622 INT nBzrPts, nMinAlloc;
1623 POINT *pBzrPts = GDI_Bezier(&pPath->pPoints[i-1], 4, &nBzrPts);
1624 /* Make sure we have allocated enough memory for the lines of
1625 * this bezier and the rest of the path, assuming we won't get
1626 * another one (since we won't reallocate again then). */
1627 nMinAlloc = nLinePts + (pPath->numEntriesUsed - i) + nBzrPts;
1628 if(nAlloc < nMinAlloc)
1629 {
1630 // Reallocate memory
1631
1632 POINT *Realloc = NULL;
1633 nAlloc = nMinAlloc * 2;
1634
1635 Realloc = ExAllocatePoolWithTag(PagedPool,
1636 nAlloc * sizeof(POINT),
1637 TAG_PATH);
1638
1639 if(!Realloc)
1640 {
1641 DPRINT1("Can't allocate pool!\n");
1642 goto end;
1643 }
1644
1645 memcpy(Realloc, pLinePts, nLinePts*sizeof(POINT));
1646 ExFreePool(pLinePts);
1647 pLinePts = Realloc;
1648 }
1649 memcpy(&pLinePts[nLinePts], &pBzrPts[1], (nBzrPts - 1) * sizeof(POINT));
1650 nLinePts += nBzrPts - 1;
1651 ExFreePool(pBzrPts);
1652 i += 2;
1653 }
1654 break;
1655 default:
1656 DPRINT1("Got path flag %d (not supported)\n", (INT)pPath->pFlags[i]);
1657 goto end;
1658 }
1659
1660 if(pPath->pFlags[i] & PT_CLOSEFIGURE)
1661 {
1662 pLinePts[nLinePts++] = pLinePts[0];
1663 }
1664 }//for
1665
1666 if(nLinePts >= 2)
1667 IntGdiPolyline(dc, pLinePts, nLinePts);
1668
1669 ret = TRUE;
1670
1671 end:
1672 if(pLinePts)ExFreePool(pLinePts);
1673
1674 /* Restore the old mapping mode */
1675 Dc_Attr->iMapMode = mapMode;
1676 Dc_Attr->szlWindowExt.cx = szWindowExt.cx;
1677 Dc_Attr->szlWindowExt.cy = szWindowExt.cy;
1678 Dc_Attr->ptlWindowOrg.x = ptWindowOrg.x;
1679 Dc_Attr->ptlWindowOrg.y = ptWindowOrg.y;
1680
1681 Dc_Attr->szlViewportExt.cx = szViewportExt.cx;
1682 Dc_Attr->szlViewportExt.cy = szViewportExt.cy;
1683 Dc_Attr->ptlViewportOrg.x = ptViewportOrg.x;
1684 Dc_Attr->ptlViewportOrg.y = ptViewportOrg.y;
1685
1686 /* Restore the world transform */
1687 dc->DcLevel.xformWorld2Wnd = xform;
1688
1689 /* If we've moved the current point then get its new position
1690 which will be in device (MM_TEXT) co-ords, convert it to
1691 logical co-ords and re-set it. This basically updates
1692 dc->CurPosX|Y so that their values are in the correct mapping
1693 mode.
1694 */
1695 if(i > 0)
1696 {
1697 POINT pt;
1698 IntGetCurrentPositionEx(dc, &pt);
1699 IntDPtoLP(dc, &pt, 1);
1700 IntGdiMoveToEx(dc, pt.x, pt.y, NULL);
1701 }
1702 DPRINT("Leave %s, ret=%d\n", __FUNCTION__, ret);
1703 return ret;
1704 }
1705
1706 /* EOF */