xref: /reactos/win32ss/gdi/ntgdi/path.c (revision 0622ce17)
1 /*
2  * PROJECT:         ReactOS win32 kernel mode subsystem
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            win32ss/gdi/ntgdi/path.c
5  * PURPOSE:         Graphics paths (BeginPath, EndPath etc.)
6  * PROGRAMMER:      Copyright 1997, 1998 Martin Boehme
7  *                            1999 Huw D M Davies
8  *                            2005 Dmitry Timoshkov
9  */
10 
11 #include <win32k.h>
12 #include <suppress.h>
13 
14 #define NDEBUG
15 #include <debug.h>
16 
17 #ifdef _MSC_VER
18 #pragma warning(disable:4244)
19 #endif
20 
21 #define NUM_ENTRIES_INITIAL 16  /* Initial size of points / flags arrays  */
22 
23 #define GROW_FACTOR_NUMER    2  /* Numerator of grow factor for the array */
24 #define GROW_FACTOR_DENOM    1  /* Denominator of grow factor             */
25 
26 #if DBG
27 static int PathCount = 0;
28 #endif
29 
30 /***********************************************************************
31  * Internal functions
32  */
33 
34 PPATH FASTCALL
35 PATH_CreatePath(int count)
36 {
37     PPATH pPath = PATH_AllocPathWithHandle();
38 
39     if (!pPath)
40     {
41         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
42         return NULL;
43     }
44 
45     DPRINT("CreatePath p 0x%p\n", pPath);
46     // Path handles are shared. Also due to recursion with in the same thread.
47     GDIOBJ_vUnlockObject((POBJ)pPath);       // Unlock
48     pPath = PATH_LockPath(pPath->BaseObject.hHmgr); // Share Lock.
49 
50     /* Make sure that path is empty */
51     PATH_EmptyPath(pPath);
52 
53     count = max( NUM_ENTRIES_INITIAL, count );
54 
55     pPath->numEntriesAllocated = count;
56 
57     pPath->pPoints = (POINT *)ExAllocatePoolWithTag(PagedPool, count * sizeof(POINT), TAG_PATH);
58     RtlZeroMemory( pPath->pPoints, count * sizeof(POINT));
59     pPath->pFlags  =  (BYTE *)ExAllocatePoolWithTag(PagedPool, count * sizeof(BYTE),  TAG_PATH);
60     RtlZeroMemory( pPath->pFlags, count * sizeof(BYTE));
61 
62     /* Initialize variables for new path */
63     pPath->numEntriesUsed = 0;
64     pPath->newStroke = TRUE;
65     pPath->state     = PATH_Open;
66     pPath->pos.x = pPath->pos.y = 0;
67 #if DBG
68     PathCount++;
69     DPRINT("Create Path %d\n",PathCount);
70 #endif
71     return pPath;
72 }
73 
74 /* PATH_DestroyGdiPath
75  *
76  * Destroys a GdiPath structure (frees the memory in the arrays).
77  */
78 VOID
79 FASTCALL
80 PATH_DestroyGdiPath(PPATH pPath)
81 {
82     ASSERT(pPath != NULL);
83 
84     if (pPath->pPoints) ExFreePoolWithTag(pPath->pPoints, TAG_PATH);
85     if (pPath->pFlags) ExFreePoolWithTag(pPath->pFlags, TAG_PATH);
86 }
87 
88 BOOL
89 FASTCALL
90 PATH_Delete(HPATH hPath)
91 {
92     PPATH pPath;
93     if (!hPath) return FALSE;
94     pPath = PATH_LockPath(hPath);
95     if (!pPath) return FALSE;
96     PATH_DestroyGdiPath(pPath);
97     GDIOBJ_vDeleteObject(&pPath->BaseObject);
98 #if DBG
99     PathCount--;
100     DPRINT("Delete Path %d\n",PathCount);
101 #endif
102     return TRUE;
103 }
104 
105 
106 VOID
107 FASTCALL
108 IntGdiCloseFigure(PPATH pPath)
109 {
110     ASSERT(pPath->state == PATH_Open);
111 
112     // FIXME: Shouldn't we draw a line to the beginning of the figure?
113     // Set PT_CLOSEFIGURE on the last entry and start a new stroke
114     if (pPath->numEntriesUsed)
115     {
116         pPath->pFlags[pPath->numEntriesUsed - 1] |= PT_CLOSEFIGURE;
117         pPath->newStroke = TRUE;
118     }
119 }
120 
121 /* MSDN: This fails if the device coordinates exceed 27 bits, or if the converted
122          logical coordinates exceed 32 bits. */
123 BOOL
124 FASTCALL
125 GdiPathDPtoLP(
126     PDC pdc,
127     PPOINT ppt,
128     INT count)
129 {
130     XFORMOBJ xo;
131 
132     XFORMOBJ_vInit(&xo, &pdc->pdcattr->mxDeviceToWorld);
133     return XFORMOBJ_bApplyXform(&xo, XF_LTOL, count, (PPOINTL)ppt, (PPOINTL)ppt);
134 }
135 
136 /* PATH_InitGdiPath
137  *
138  * Initializes the GdiPath structure.
139  */
140 VOID
141 FASTCALL
142 PATH_InitGdiPath(
143     PPATH pPath)
144 {
145     ASSERT(pPath != NULL);
146 
147     pPath->state = PATH_Null;
148     pPath->pPoints = NULL;
149     pPath->pFlags = NULL;
150     pPath->numEntriesUsed = 0;
151     pPath->numEntriesAllocated = 0;
152 }
153 
154 /* PATH_AssignGdiPath
155  *
156  * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
157  * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
158  * not just the pointers. Since this means that the arrays in pPathDest may
159  * need to be resized, pPathDest should have been initialized using
160  * PATH_InitGdiPath (in C++, this function would be an assignment operator,
161  * not a copy constructor).
162  * Returns TRUE if successful, else FALSE.
163  */
164 BOOL
165 FASTCALL
166 PATH_AssignGdiPath(
167     PPATH pPathDest,
168     const PPATH pPathSrc)
169 {
170     ASSERT(pPathDest != NULL && pPathSrc != NULL);
171 
172     /* Make sure destination arrays are big enough */
173     if (!PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed))
174         return FALSE;
175 
176     /* Perform the copy operation */
177     memcpy(pPathDest->pPoints, pPathSrc->pPoints, sizeof(POINT)*pPathSrc->numEntriesUsed);
178     memcpy(pPathDest->pFlags,  pPathSrc->pFlags,  sizeof(BYTE)*pPathSrc->numEntriesUsed);
179 
180     pPathDest->pos = pPathSrc->pos;
181     pPathDest->state = pPathSrc->state;
182     pPathDest->numEntriesUsed = pPathSrc->numEntriesUsed;
183     pPathDest->newStroke = pPathSrc->newStroke;
184     return TRUE;
185 }
186 
187 BOOL PATH_SavePath( DC *dst, DC *src )
188 {
189     PPATH pdstPath, psrcPath = PATH_LockPath(src->dclevel.hPath);
190     DPRINT("PATH_SavePath\n");
191     if (psrcPath)
192     {
193        DPRINT("PATH_SavePath 1\n");
194 
195        pdstPath = PATH_CreatePath(psrcPath->numEntriesAllocated);
196 
197        dst->dclevel.flPath = src->dclevel.flPath;
198 
199        dst->dclevel.hPath = pdstPath->BaseObject.hHmgr;
200 
201        PATH_AssignGdiPath(pdstPath, psrcPath);
202 
203        PATH_UnlockPath(pdstPath);
204        PATH_UnlockPath(psrcPath);
205     }
206     return TRUE;
207 }
208 
209 BOOL PATH_RestorePath( DC *dst, DC *src )
210 {
211     DPRINT("PATH_RestorePath\n");
212 
213     if (dst->dclevel.hPath == NULL)
214     {
215        PPATH pdstPath, psrcPath = PATH_LockPath(src->dclevel.hPath);
216        DPRINT("PATH_RestorePath 1\n");
217        pdstPath = PATH_CreatePath(psrcPath->numEntriesAllocated);
218        dst->dclevel.flPath = src->dclevel.flPath;
219        dst->dclevel.hPath = pdstPath->BaseObject.hHmgr;
220 
221        PATH_AssignGdiPath(pdstPath, psrcPath);
222 
223        PATH_UnlockPath(pdstPath);
224        PATH_UnlockPath(psrcPath);
225     }
226     else
227     {
228        PPATH pdstPath, psrcPath = PATH_LockPath(src->dclevel.hPath);
229        pdstPath = PATH_LockPath(dst->dclevel.hPath);
230        DPRINT("PATH_RestorePath 2\n");
231        dst->dclevel.flPath = src->dclevel.flPath & (DCPATH_CLOCKWISE|DCPATH_ACTIVE);
232        PATH_AssignGdiPath(pdstPath, psrcPath);
233 
234        PATH_UnlockPath(pdstPath);
235        PATH_UnlockPath(psrcPath);
236     }
237     return TRUE;
238 }
239 
240 /* PATH_EmptyPath
241  *
242  * Removes all entries from the path and sets the path state to PATH_Null.
243  */
244 VOID
245 FASTCALL
246 PATH_EmptyPath(PPATH pPath)
247 {
248     ASSERT(pPath != NULL);
249 
250     pPath->state = PATH_Null;
251     pPath->numEntriesUsed = 0;
252 }
253 
254 /* PATH_AddEntry
255  *
256  * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
257  * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
258  * successful, FALSE otherwise (e.g. if not enough memory was available).
259  */
260 BOOL
261 FASTCALL
262 PATH_AddEntry(
263     PPATH pPath,
264     const POINT *pPoint,
265     BYTE flags)
266 {
267     ASSERT(pPath != NULL);
268 
269     /* FIXME: If newStroke is true, perhaps we want to check that we're
270      * getting a PT_MOVETO
271      */
272     DPRINT("(%d,%d) - %d\n", pPoint->x, pPoint->y, flags);
273 
274     /* Reserve enough memory for an extra path entry */
275     if (!PATH_ReserveEntries(pPath, pPath->numEntriesUsed + 1))
276         return FALSE;
277 
278     /* Store information in path entry */
279     pPath->pPoints[pPath->numEntriesUsed] = *pPoint;
280     pPath->pFlags[pPath->numEntriesUsed] = flags;
281 
282     /* Increment entry count */
283     pPath->numEntriesUsed++;
284 
285     return TRUE;
286 }
287 
288 /* PATH_ReserveEntries
289  *
290  * Ensures that at least "numEntries" entries (for points and flags) have
291  * been allocated; allocates larger arrays and copies the existing entries
292  * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
293  */
294 BOOL
295 FASTCALL
296 PATH_ReserveEntries(
297     PPATH pPath,
298     INT numEntries)
299 {
300     INT numEntriesToAllocate;
301     POINT *pPointsNew;
302     BYTE *pFlagsNew;
303 
304     ASSERT(pPath != NULL);
305     ASSERT(numEntries >= 0);
306 
307     /* Do we have to allocate more memory? */
308     if (numEntries > pPath->numEntriesAllocated)
309     {
310         /* Find number of entries to allocate. We let the size of the array
311          * grow exponentially, since that will guarantee linear time
312          * complexity. */
313         if (pPath->numEntriesAllocated)
314         {
315             numEntriesToAllocate = pPath->numEntriesAllocated;
316             while (numEntriesToAllocate < numEntries)
317                 numEntriesToAllocate = numEntriesToAllocate * GROW_FACTOR_NUMER / GROW_FACTOR_DENOM;
318         }
319         else
320             numEntriesToAllocate = numEntries;
321 
322         /* Allocate new arrays */
323         pPointsNew = (POINT *)ExAllocatePoolWithTag(PagedPool, numEntriesToAllocate * sizeof(POINT), TAG_PATH);
324         if (!pPointsNew)
325             return FALSE;
326 
327         pFlagsNew = (BYTE *)ExAllocatePoolWithTag(PagedPool, numEntriesToAllocate * sizeof(BYTE), TAG_PATH);
328         if (!pFlagsNew)
329         {
330             ExFreePoolWithTag(pPointsNew, TAG_PATH);
331             return FALSE;
332         }
333 
334         /* Copy old arrays to new arrays and discard old arrays */
335         if (pPath->pPoints)
336         {
337             ASSERT(pPath->pFlags);
338 
339             memcpy(pPointsNew, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
340             memcpy(pFlagsNew, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
341 
342             ExFreePoolWithTag(pPath->pPoints, TAG_PATH);
343             ExFreePoolWithTag(pPath->pFlags, TAG_PATH);
344         }
345 
346         pPath->pPoints = pPointsNew;
347         pPath->pFlags = pFlagsNew;
348         pPath->numEntriesAllocated = numEntriesToAllocate;
349     }
350 
351     return TRUE;
352 }
353 
354 /* PATH_ScaleNormalizedPoint
355  *
356  * Scales a normalized point (x, y) with respect to the box whose corners are
357  * passed in "corners". The point is stored in "*pPoint". The normalized
358  * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
359  * (1.0, 1.0) correspond to corners[1].
360  */
361 VOID
362 FASTCALL
363 PATH_ScaleNormalizedPoint(
364     FLOAT_POINT corners[],
365     double x,
366     double y,
367     POINT *pPoint)
368 {
369     ASSERT(corners);
370     ASSERT(pPoint);
371 
372     pPoint->x = GDI_ROUND((double)corners[0].x + (double)(corners[1].x - corners[0].x) * 0.5 * (x + 1.0));
373     pPoint->y = GDI_ROUND((double)corners[0].y + (double)(corners[1].y - corners[0].y) * 0.5 * (y + 1.0));
374 }
375 
376 /* PATH_NormalizePoint
377  *
378  * Normalizes a point with respect to the box whose corners are passed in
379  * corners. The normalized coordinates are stored in *pX and *pY.
380  */
381 VOID
382 FASTCALL
383 PATH_NormalizePoint(
384     FLOAT_POINT corners[],
385     const FLOAT_POINT *pPoint,
386     double *pX,
387     double *pY)
388 {
389     ASSERT(corners);
390     ASSERT(pPoint);
391     ASSERT(pX);
392     ASSERT(pY);
393 
394     *pX = (double)(pPoint->x - corners[0].x) / (double)(corners[1].x - corners[0].x) * 2.0 - 1.0;
395     *pY = (double)(pPoint->y - corners[0].y) / (double)(corners[1].y - corners[0].y) * 2.0 - 1.0;
396 }
397 
398 /* PATH_CheckCorners
399  *
400  * Helper function for PATH_RoundRect() and PATH_Rectangle()
401  */
402 BOOL
403 PATH_CheckCorners(
404     DC *dc,
405     POINT corners[],
406     INT x1,
407     INT y1,
408     INT x2,
409     INT y2)
410 {
411     INT temp;
412     PDC_ATTR pdcattr = dc->pdcattr;
413 
414     /* Convert points to device coordinates */
415     corners[0].x = x1;
416     corners[0].y = y1;
417     corners[1].x = x2;
418     corners[1].y = y2;
419     IntLPtoDP(dc, corners, 2);
420 
421     /* Make sure first corner is top left and second corner is bottom right */
422     if (corners[0].x > corners[1].x)
423     {
424         temp = corners[0].x;
425         corners[0].x = corners[1].x;
426         corners[1].x = temp;
427     }
428 
429     if (corners[0].y > corners[1].y)
430     {
431         temp = corners[0].y;
432         corners[0].y = corners[1].y;
433         corners[1].y = temp;
434     }
435 
436     /* In GM_COMPATIBLE, don't include bottom and right edges */
437     if (pdcattr->iGraphicsMode == GM_COMPATIBLE)
438     {
439         if (corners[0].x == corners[1].x) return FALSE;
440         if (corners[0].y == corners[1].y) return FALSE;
441         corners[1].x--;
442         corners[1].y--;
443     }
444     return TRUE;
445 }
446 
447 /* add a number of points, converting them to device coords */
448 /* return a pointer to the first type byte so it can be fixed up if necessary */
449 static BYTE *add_log_points( DC *dc, PPATH path, const POINT *points,
450                              DWORD count, BYTE type )
451 {
452     BYTE *ret;
453 
454     if (!PATH_ReserveEntries( path, path->numEntriesUsed + count )) return NULL;
455 
456     ret = &path->pFlags[path->numEntriesUsed];
457     memcpy( &path->pPoints[path->numEntriesUsed], points, count * sizeof(*points) );
458     IntLPtoDP( dc, &path->pPoints[path->numEntriesUsed], count );
459     memset( ret, type, count );
460     path->numEntriesUsed += count;
461     return ret;
462 }
463 
464 /* add a number of points that are already in device coords */
465 /* return a pointer to the first type byte so it can be fixed up if necessary */
466 static BYTE *add_points( PPATH path, const POINT *points, DWORD count, BYTE type )
467 {
468     BYTE *ret;
469 
470     if (!PATH_ReserveEntries( path, path->numEntriesUsed + count )) return NULL;
471 
472     ret = &path->pFlags[path->numEntriesUsed];
473     memcpy( &path->pPoints[path->numEntriesUsed], points, count * sizeof(*points) );
474     memset( ret, type, count );
475     path->numEntriesUsed += count;
476     return ret;
477 }
478 
479 /* reverse the order of an array of points */
480 static void reverse_points( POINT *points, UINT count )
481 {
482     UINT i;
483     for (i = 0; i < count / 2; i++)
484     {
485         POINT pt = points[i];
486         points[i] = points[count - i - 1];
487         points[count - i - 1] = pt;
488     }
489 }
490 
491 /* start a new path stroke if necessary */
492 static BOOL start_new_stroke( PPATH path )
493 {
494     if (!path->newStroke && path->numEntriesUsed &&
495         !(path->pFlags[path->numEntriesUsed - 1] & PT_CLOSEFIGURE) &&
496         path->pPoints[path->numEntriesUsed - 1].x == path->pos.x &&
497         path->pPoints[path->numEntriesUsed - 1].y == path->pos.y)
498         return TRUE;
499 
500     path->newStroke = FALSE;
501     return add_points( path, &path->pos, 1, PT_MOVETO ) != NULL;
502 }
503 
504 /* set current position to the last point that was added to the path */
505 static void update_current_pos( PPATH path )
506 {
507     assert( path->numEntriesUsed );
508     path->pos = path->pPoints[path->numEntriesUsed - 1];
509 }
510 
511 /* close the current figure */
512 static void close_figure( PPATH path )
513 {
514     assert( path->numEntriesUsed );
515     path->pFlags[path->numEntriesUsed - 1] |= PT_CLOSEFIGURE;
516 }
517 
518 /* add a number of points, starting a new stroke if necessary */
519 static BOOL add_log_points_new_stroke( DC *dc, PPATH path, const POINT *points,
520                                        DWORD count, BYTE type )
521 {
522     if (!start_new_stroke( path )) return FALSE;
523     if (!add_log_points( dc, path, points, count, type )) return FALSE;
524     update_current_pos( path );
525 
526     DPRINT("ALPNS : Pos X %d Y %d\n",path->pos.x, path->pos.y);
527     IntGdiMoveToEx(dc, path->pos.x, path->pos.y, NULL);
528 
529     return TRUE;
530 }
531 
532 /* PATH_MoveTo
533  *
534  * Should be called when a MoveTo is performed on a DC that has an
535  * open path. This starts a new stroke. Returns TRUE if successful, else
536  * FALSE.
537  */
538 BOOL
539 FASTCALL
540 PATH_MoveTo(
541     PDC dc,
542     PPATH pPath)
543 {
544     if (!pPath) return FALSE;
545 
546     // GDI32 : Signal from user space of a change in position.
547     if (dc->pdcattr->ulDirty_ & DIRTY_STYLESTATE)
548     {
549        DPRINT("MoveTo has changed\n");
550        pPath->newStroke = TRUE;
551        // Set position and clear the signal flag.
552        IntGetCurrentPositionEx(dc, &pPath->pos);
553        IntLPtoDP( dc, &pPath->pos, 1 );
554        return TRUE;
555     }
556 
557     return FALSE;
558 }
559 
560 /* PATH_LineTo
561  *
562  * Should be called when a LineTo is performed on a DC that has an
563  * open path. This adds a PT_LINETO entry to the path (and possibly
564  * a PT_MOVETO entry, if this is the first LineTo in a stroke).
565  * Returns TRUE if successful, else FALSE.
566  */
567 BOOL
568 FASTCALL
569 PATH_LineTo(
570     PDC dc,
571     INT x,
572     INT y)
573 {
574     BOOL Ret;
575     PPATH pPath;
576     POINT point, pointCurPos;
577 
578     pPath = PATH_LockPath(dc->dclevel.hPath);
579     if (!pPath) return FALSE;
580 
581     point.x = x;
582     point.y = y;
583 
584     // Coalesce a MoveTo point.
585     if ( !PATH_MoveTo(dc, pPath) )
586     {
587        /* Add a PT_MOVETO if necessary */
588        if (pPath->newStroke)
589        {
590            DPRINT("Line To : New Stroke\n");
591            pPath->newStroke = FALSE;
592            IntGetCurrentPositionEx(dc, &pointCurPos);
593            CoordLPtoDP(dc, &pointCurPos);
594            if (!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
595            {
596                PATH_UnlockPath(pPath);
597                return FALSE;
598            }
599        }
600     }
601     Ret = add_log_points_new_stroke( dc, pPath, &point, 1, PT_LINETO );
602     PATH_UnlockPath(pPath);
603     return Ret;
604 }
605 
606 /* PATH_Rectangle
607  *
608  * Should be called when a call to Rectangle is performed on a DC that has
609  * an open path. Returns TRUE if successful, else FALSE.
610  */
611 BOOL
612 FASTCALL
613 PATH_Rectangle(
614     PDC dc,
615     INT x1,
616     INT y1,
617     INT x2,
618     INT y2)
619 {
620     PPATH pPath;
621     POINT corners[2], points[4];
622     BYTE *type;
623 
624     pPath = PATH_LockPath(dc->dclevel.hPath);
625     if (!pPath) return FALSE;
626 
627     if (!PATH_CheckCorners(dc, corners, x1, y1, x2, y2))
628     {
629         PATH_UnlockPath(pPath);
630         return TRUE;
631     }
632 
633     points[0].x = corners[1].x;
634     points[0].y = corners[0].y;
635     points[1]   = corners[0];
636     points[2].x = corners[0].x;
637     points[2].y = corners[1].y;
638     points[3]   = corners[1];
639 
640     if (dc->dclevel.flPath & DCPATH_CLOCKWISE) reverse_points( points, 4 );
641 
642     if (!(type = add_points( pPath, points, 4, PT_LINETO )))
643     {
644        PATH_UnlockPath(pPath);
645        return FALSE;
646     }
647     type[0] = PT_MOVETO;
648 
649     /* Close the rectangle figure */
650     IntGdiCloseFigure(pPath) ;
651     PATH_UnlockPath(pPath);
652     return TRUE;
653 }
654 
655 /* PATH_RoundRect
656  *
657  * Should be called when a call to RoundRect is performed on a DC that has
658  * an open path. Returns TRUE if successful, else FALSE.
659  *
660  */
661 BOOL
662 FASTCALL
663 PATH_RoundRect(
664     DC *dc,
665     INT x1,
666     INT y1,
667     INT x2,
668     INT y2,
669     INT ell_width,
670     INT ell_height)
671 {
672     const double factor = 0.55428475; /* 4 / 3 * (sqrt(2) - 1) */
673     PPATH pPath;
674     POINT corners[2], ellipse[2], points[16];
675     BYTE *type;
676     double width, height;
677 
678     if (!ell_width || !ell_height) return PATH_Rectangle( dc, x1, y1, x2, y2 );
679 
680     pPath = PATH_LockPath(dc->dclevel.hPath);
681     if (!pPath) return FALSE;
682 
683     if (!PATH_CheckCorners(dc, corners, x1, y1, x2, y2))
684     {
685         PATH_UnlockPath(pPath);
686         return TRUE;
687     }
688 
689     ellipse[0].x = ellipse[0].y = 0;
690     ellipse[1].x = ell_width;
691     ellipse[1].y = ell_height;
692     IntLPtoDP( dc, &ellipse, 2 );
693     ell_width  = min( abs( ellipse[1].x - ellipse[0].x ), corners[1].x - corners[0].x );
694     ell_height = min( abs( ellipse[1].y - ellipse[0].y ), corners[1].y - corners[0].y );
695     width  = ell_width / 2.0;
696     height = ell_height / 2.0;
697 
698     /* starting point */
699     points[0].x  = corners[1].x;
700     points[0].y  = corners[0].y + GDI_ROUND( height );
701     /* first curve */
702     points[1].x  = corners[1].x;
703     points[1].y  = corners[0].y + GDI_ROUND( height * (1 - factor) );
704     points[2].x  = corners[1].x - GDI_ROUND( width * (1 - factor) );
705     points[2].y  = corners[0].y;
706     points[3].x  = corners[1].x - GDI_ROUND( width );
707     points[3].y  = corners[0].y;
708     /* horizontal line */
709     points[4].x  = corners[0].x + GDI_ROUND( width );
710     points[4].y  = corners[0].y;
711     /* second curve */
712     points[5].x  = corners[0].x + GDI_ROUND( width * (1 - factor) );
713     points[5].y  = corners[0].y;
714     points[6].x  = corners[0].x;
715     points[6].y  = corners[0].y + GDI_ROUND( height * (1 - factor) );
716     points[7].x  = corners[0].x;
717     points[7].y  = corners[0].y + GDI_ROUND( height );
718     /* vertical line */
719     points[8].x  = corners[0].x;
720     points[8].y  = corners[1].y - GDI_ROUND( height );
721     /* third curve */
722     points[9].x  = corners[0].x;
723     points[9].y  = corners[1].y - GDI_ROUND( height * (1 - factor) );
724     points[10].x = corners[0].x + GDI_ROUND( width * (1 - factor) );
725     points[10].y = corners[1].y;
726     points[11].x = corners[0].x + GDI_ROUND( width );
727     points[11].y = corners[1].y;
728     /* horizontal line */
729     points[12].x = corners[1].x - GDI_ROUND( width );
730     points[12].y = corners[1].y;
731     /* fourth curve */
732     points[13].x = corners[1].x - GDI_ROUND( width * (1 - factor) );
733     points[13].y = corners[1].y;
734     points[14].x = corners[1].x;
735     points[14].y = corners[1].y - GDI_ROUND( height * (1 - factor) );
736     points[15].x = corners[1].x;
737     points[15].y = corners[1].y - GDI_ROUND( height );
738 
739     if (dc->dclevel.flPath & DCPATH_CLOCKWISE) reverse_points( points, 16 );
740     if (!(type = add_points( pPath, points, 16, PT_BEZIERTO )))
741     {
742        PATH_UnlockPath(pPath);
743        return FALSE;
744     }
745     type[0] = PT_MOVETO;
746     type[4] = type[8] = type[12] = PT_LINETO;
747 
748     IntGdiCloseFigure(pPath);
749     PATH_UnlockPath(pPath);
750     return TRUE;
751 }
752 
753 /* PATH_Ellipse
754  *
755  */
756 BOOL
757 FASTCALL
758 PATH_Ellipse(
759     PDC dc,
760     INT x1,
761     INT y1,
762     INT x2,
763     INT y2)
764 {
765     const double factor = 0.55428475; /* 4 / 3 * (sqrt(2) - 1) */
766     PPATH pPath;
767     POINT corners[2], points[13];
768     BYTE *type;
769     double width, height;
770 
771     pPath = PATH_LockPath(dc->dclevel.hPath);
772     if (!pPath) return FALSE;
773 
774     if (!PATH_CheckCorners(dc, corners, x1, y1, x2, y2))
775     {
776         PATH_UnlockPath(pPath);
777         return TRUE;
778     }
779 
780     width  = (corners[1].x - corners[0].x) / 2.0;
781     height = (corners[1].y - corners[0].y) / 2.0;
782 
783     /* starting point */
784     points[0].x = corners[1].x;
785     points[0].y = corners[0].y + GDI_ROUND( height );
786     /* first curve */
787     points[1].x = corners[1].x;
788     points[1].y = corners[0].y + GDI_ROUND( height * (1 - factor) );
789     points[2].x = corners[1].x - GDI_ROUND( width * (1 - factor) );
790     points[2].y = corners[0].y;
791     points[3].x = corners[0].x + GDI_ROUND( width );
792     points[3].y = corners[0].y;
793     /* second curve */
794     points[4].x = corners[0].x + GDI_ROUND( width * (1 - factor) );
795     points[4].y = corners[0].y;
796     points[5].x = corners[0].x;
797     points[5].y = corners[0].y + GDI_ROUND( height * (1 - factor) );
798     points[6].x = corners[0].x;
799     points[6].y = corners[0].y + GDI_ROUND( height );
800     /* third curve */
801     points[7].x = corners[0].x;
802     points[7].y = corners[1].y - GDI_ROUND( height * (1 - factor) );
803     points[8].x = corners[0].x + GDI_ROUND( width * (1 - factor) );
804     points[8].y = corners[1].y;
805     points[9].x = corners[0].x + GDI_ROUND( width );
806     points[9].y = corners[1].y;
807     /* fourth curve */
808     points[10].x = corners[1].x - GDI_ROUND( width * (1 - factor) );
809     points[10].y = corners[1].y;
810     points[11].x = corners[1].x;
811     points[11].y = corners[1].y - GDI_ROUND( height * (1 - factor) );
812     points[12].x = corners[1].x;
813     points[12].y = corners[1].y - GDI_ROUND( height );
814 
815     if (dc->dclevel.flPath & DCPATH_CLOCKWISE) reverse_points( points, 13 );
816     if (!(type = add_points( pPath, points, 13, PT_BEZIERTO )))
817     {
818         DPRINT1("PATH_Ellipse No add\n");
819         PATH_UnlockPath(pPath);
820         return FALSE;
821     }
822     type[0] = PT_MOVETO;
823 
824     IntGdiCloseFigure(pPath);
825     PATH_UnlockPath(pPath);
826     return TRUE;
827 }
828 
829 /* PATH_DoArcPart
830  *
831  * Creates a Bezier spline that corresponds to part of an arc and appends the
832  * corresponding points to the path. The start and end angles are passed in
833  * "angleStart" and "angleEnd"; these angles should span a quarter circle
834  * at most. If "startEntryType" is non-zero, an entry of that type for the first
835  * control point is added to the path; otherwise, it is assumed that the current
836  * position is equal to the first control point.
837  */
838 BOOL
839 FASTCALL
840 PATH_DoArcPart(
841     PPATH pPath,
842     FLOAT_POINT corners[],
843     double angleStart,
844     double angleEnd,
845     BYTE startEntryType)
846 {
847     double  halfAngle, a;
848     double  xNorm[4], yNorm[4];
849     POINT points[4];
850     BYTE *type;
851     int i, start;
852 
853     ASSERT(fabs(angleEnd - angleStart) <= M_PI_2);
854 
855     /* FIXME: Is there an easier way of computing this? */
856 
857     /* Compute control points */
858     halfAngle = (angleEnd - angleStart) / 2.0;
859     if (fabs(halfAngle) > 1e-8)
860     {
861         a = 4.0 / 3.0 * (1 - cos(halfAngle)) / sin(halfAngle);
862         xNorm[0] = cos(angleStart);
863         yNorm[0] = sin(angleStart);
864         xNorm[1] = xNorm[0] - a * yNorm[0];
865         yNorm[1] = yNorm[0] + a * xNorm[0];
866         xNorm[3] = cos(angleEnd);
867         yNorm[3] = sin(angleEnd);
868         xNorm[2] = xNorm[3] + a * yNorm[3];
869         yNorm[2] = yNorm[3] - a * xNorm[3];
870     }
871     else
872         for (i = 0; i < 4; i++)
873         {
874             xNorm[i] = cos(angleStart);
875             yNorm[i] = sin(angleStart);
876         }
877 
878     /* Add starting point to path if desired */
879     start = !startEntryType;
880 
881     /* Add remaining control points */
882     for (i = start; i < 4; i++) PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &points[i]);
883     if (!(type = add_points( pPath, points + start, 4 - start, PT_BEZIERTO ))) return FALSE;
884     if (!start) type[0] = startEntryType;
885 
886     return TRUE;
887 }
888 
889 /* PATH_Arc
890  *
891  * Should be called when a call to Arc is performed on a DC that has
892  * an open path. This adds up to five Bezier splines representing the arc
893  * to the path. When 'lines' is 1, we add 1 extra line to get a chord,
894  * when 'lines' is 2, we add 2 extra lines to get a pie, and when 'lines' is
895  * -1 we add 1 extra line from the current DC position to the starting position
896  * of the arc before drawing the arc itself (arcto). Returns TRUE if successful,
897  * else FALSE.
898  */
899 BOOL
900 FASTCALL
901 PATH_Arc(
902     PDC dc,
903     INT x1,
904     INT y1,
905     INT x2,
906     INT y2,
907     INT xStart,
908     INT yStart,
909     INT xEnd,
910     INT yEnd,
911     INT direction,
912     INT lines)
913 {
914     double  angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant = 0.0;
915     /* Initialize angleEndQuadrant to silence gcc's warning */
916     double  x, y;
917     FLOAT_POINT corners[2], pointStart, pointEnd;
918     POINT   centre, pointCurPos;
919     BOOL    start, end, Ret = TRUE;
920     INT     temp;
921     BOOL    clockwise;
922     PPATH   pPath;
923 
924     /* FIXME: This function should check for all possible error returns */
925     /* FIXME: Do we have to respect newStroke? */
926 
927     ASSERT(dc);
928 
929     pPath = PATH_LockPath(dc->dclevel.hPath);
930     if (!pPath) return FALSE;
931 
932     if (direction)
933        clockwise = ((direction == AD_CLOCKWISE) !=0 );
934     else
935        clockwise = ((dc->dclevel.flPath & DCPATH_CLOCKWISE) != 0);
936 
937     /* Check for zero height / width */
938     /* FIXME: Only in GM_COMPATIBLE? */
939     if (x1 == x2 || y1 == y2)
940     {
941         Ret = TRUE;
942         goto ArcExit;
943     }
944     /* Convert points to device coordinates */
945     corners[0].x = (FLOAT)x1;
946     corners[0].y = (FLOAT)y1;
947     corners[1].x = (FLOAT)x2;
948     corners[1].y = (FLOAT)y2;
949     pointStart.x = (FLOAT)xStart;
950     pointStart.y = (FLOAT)yStart;
951     pointEnd.x = (FLOAT)xEnd;
952     pointEnd.y = (FLOAT)yEnd;
953     INTERNAL_LPTODP_FLOAT(dc, corners);
954     INTERNAL_LPTODP_FLOAT(dc, corners + 1);
955     INTERNAL_LPTODP_FLOAT(dc, &pointStart);
956     INTERNAL_LPTODP_FLOAT(dc, &pointEnd);
957 
958     /* Make sure first corner is top left and second corner is bottom right */
959     if (corners[0].x > corners[1].x)
960     {
961         temp = corners[0].x;
962         corners[0].x = corners[1].x;
963         corners[1].x = temp;
964     }
965     if (corners[0].y > corners[1].y)
966     {
967         temp = corners[0].y;
968         corners[0].y = corners[1].y;
969         corners[1].y = temp;
970     }
971 
972     /* Compute start and end angle */
973     PATH_NormalizePoint(corners, &pointStart, &x, &y);
974     angleStart = atan2(y, x);
975     PATH_NormalizePoint(corners, &pointEnd, &x, &y);
976     angleEnd = atan2(y, x);
977 
978     /* Make sure the end angle is "on the right side" of the start angle */
979     if (clockwise)
980     {
981         if (angleEnd <= angleStart)
982         {
983             angleEnd += 2 * M_PI;
984             ASSERT(angleEnd >= angleStart);
985         }
986     }
987     else
988     {
989         if (angleEnd >= angleStart)
990         {
991             angleEnd -= 2 * M_PI;
992             ASSERT(angleEnd <= angleStart);
993         }
994     }
995 
996     /* In GM_COMPATIBLE, don't include bottom and right edges */
997     if (dc->pdcattr->iGraphicsMode == GM_COMPATIBLE)
998     {
999         corners[1].x--;
1000         corners[1].y--;
1001     }
1002 
1003     /* arcto: Add a PT_MOVETO only if this is the first entry in a stroke */
1004     if (lines == GdiTypeArcTo && pPath->newStroke) // -1
1005     {
1006         pPath->newStroke = FALSE;
1007         IntGetCurrentPositionEx(dc, &pointCurPos);
1008         CoordLPtoDP(dc, &pointCurPos);
1009         if (!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
1010         {
1011             Ret = FALSE;
1012             goto ArcExit;
1013         }
1014     }
1015 
1016     /* Add the arc to the path with one Bezier spline per quadrant that the
1017      * arc spans */
1018     start = TRUE;
1019     end = FALSE;
1020     do
1021     {
1022         /* Determine the start and end angles for this quadrant */
1023         if (start)
1024         {
1025             angleStartQuadrant = angleStart;
1026             if (clockwise)
1027                 angleEndQuadrant = (floor(angleStart / M_PI_2) + 1.0) * M_PI_2;
1028             else
1029                 angleEndQuadrant = (ceil(angleStart / M_PI_2) - 1.0) * M_PI_2;
1030         }
1031         else
1032         {
1033             angleStartQuadrant = angleEndQuadrant;
1034             if (clockwise)
1035                 angleEndQuadrant += M_PI_2;
1036             else
1037                 angleEndQuadrant -= M_PI_2;
1038         }
1039 
1040         /* Have we reached the last part of the arc? */
1041         if ((clockwise && angleEnd < angleEndQuadrant) ||
1042             (!clockwise && angleEnd > angleEndQuadrant))
1043         {
1044             /* Adjust the end angle for this quadrant */
1045             angleEndQuadrant = angleEnd;
1046             end = TRUE;
1047         }
1048 
1049         /* Add the Bezier spline to the path */
1050         PATH_DoArcPart(pPath,
1051                        corners,
1052                        angleStartQuadrant,
1053                        angleEndQuadrant,
1054                        start ? (lines == GdiTypeArcTo ? PT_LINETO : PT_MOVETO) : FALSE); // -1
1055         start = FALSE;
1056     }
1057     while (!end);
1058 
1059     if (lines == GdiTypeArcTo)
1060     {
1061        update_current_pos( pPath );
1062     }
1063     else /* chord: close figure. pie: add line and close figure */
1064     if (lines == GdiTypeChord) // 1
1065     {
1066         IntGdiCloseFigure(pPath);
1067     }
1068     else if (lines == GdiTypePie) // 2
1069     {
1070         centre.x = (corners[0].x + corners[1].x) / 2;
1071         centre.y = (corners[0].y + corners[1].y) / 2;
1072         if (!PATH_AddEntry(pPath, &centre, PT_LINETO | PT_CLOSEFIGURE))
1073             Ret = FALSE;
1074     }
1075 ArcExit:
1076     PATH_UnlockPath(pPath);
1077     return Ret;
1078 }
1079 
1080 BOOL
1081 FASTCALL
1082 PATH_PolyBezierTo(
1083     PDC dc,
1084     const POINT *pts,
1085     DWORD cbPoints)
1086 {
1087     PPATH pPath;
1088     BOOL ret;
1089 
1090     ASSERT(dc);
1091     ASSERT(pts);
1092     ASSERT(cbPoints);
1093 
1094     pPath = PATH_LockPath(dc->dclevel.hPath);
1095     if (!pPath) return FALSE;
1096 
1097     ret = add_log_points_new_stroke( dc, pPath, pts, cbPoints, PT_BEZIERTO );
1098 
1099     PATH_UnlockPath(pPath);
1100     return ret;
1101 }
1102 
1103 BOOL
1104 FASTCALL
1105 PATH_PolyBezier(
1106     PDC dc,
1107     const POINT *pts,
1108     DWORD cbPoints)
1109 {
1110     PPATH pPath;
1111     BYTE *type;
1112 
1113     ASSERT(dc);
1114     ASSERT(pts);
1115     ASSERT(cbPoints);
1116 
1117     pPath = PATH_LockPath(dc->dclevel.hPath);
1118     if (!pPath) return FALSE;
1119 
1120     type = add_log_points( dc, pPath, pts, cbPoints, PT_BEZIERTO );
1121     if (!type) return FALSE;
1122 
1123     type[0] = PT_MOVETO;
1124 
1125     PATH_UnlockPath(pPath);
1126     return TRUE;
1127 }
1128 
1129 BOOL
1130 FASTCALL
1131 PATH_PolyDraw(
1132     PDC dc,
1133     const POINT *pts,
1134     const BYTE *types,
1135     DWORD cbPoints)
1136 {
1137     PPATH pPath;
1138     POINT orig_pos, cur_pos;
1139     ULONG i, lastmove = 0;
1140 
1141     pPath = PATH_LockPath(dc->dclevel.hPath);
1142     if (!pPath) return FALSE;
1143 
1144     if (pPath->state != PATH_Open)
1145     {
1146         PATH_UnlockPath(pPath);
1147         return FALSE;
1148     }
1149 
1150     for (i = 0; i < pPath->numEntriesUsed; i++) if (pPath->pFlags[i] == PT_MOVETO) lastmove = i;
1151     orig_pos = pPath->pos;
1152 
1153     IntGetCurrentPositionEx(dc, &cur_pos);
1154 
1155     DPRINT("PPD : Current pos X %d Y %d\n",pPath->pos.x, pPath->pos.y);
1156     DPRINT("PPD : last %d pos X %d Y %d\n",lastmove, pPath->pPoints[lastmove].x, pPath->pPoints[lastmove].y);
1157 
1158 
1159     for(i = 0; i < cbPoints; i++)
1160     {
1161         switch (types[i])
1162         {
1163         case PT_MOVETO:
1164             pPath->newStroke = TRUE;
1165             pPath->pos = pts[i];
1166             IntLPtoDP( dc, &pPath->pos, 1);
1167             lastmove = pPath->numEntriesUsed;
1168             break;
1169         case PT_LINETO:
1170         case PT_LINETO | PT_CLOSEFIGURE:
1171             if (!add_log_points_new_stroke( dc, pPath, &pts[i], 1, PT_LINETO ))
1172             {
1173                PATH_UnlockPath(pPath);
1174                return FALSE;
1175             }
1176             break;
1177         case PT_BEZIERTO:
1178             if ((i + 2 < cbPoints) && (types[i + 1] == PT_BEZIERTO) &&
1179                 (types[i + 2] & ~PT_CLOSEFIGURE) == PT_BEZIERTO)
1180             {
1181                 if (!add_log_points_new_stroke( dc, pPath, &pts[i], 3, PT_BEZIERTO ))
1182                 {
1183                    PATH_UnlockPath(pPath);
1184                    return FALSE;
1185                 }
1186                 i += 2;
1187                 break;
1188             }
1189             /* fall through */
1190         default:
1191             /* restore original position */
1192             pPath->pos = orig_pos;
1193 
1194             DPRINT("PPD Bad   : pos X %d Y %d\n",pPath->pos.x, pPath->pos.y);
1195 
1196             IntGdiMoveToEx(dc, cur_pos.x, cur_pos.y, NULL);
1197 
1198             PATH_UnlockPath(pPath);
1199             return FALSE;
1200         }
1201 
1202         if (types[i] & PT_CLOSEFIGURE)
1203         {
1204             close_figure( pPath );
1205             pPath->pos = pPath->pPoints[lastmove];
1206             DPRINT("PPD close : pos X %d Y %d\n",pPath->pos.x, pPath->pos.y);
1207         }
1208     }
1209     PATH_UnlockPath(pPath);
1210     return TRUE;
1211 }
1212 
1213 BOOL
1214 FASTCALL
1215 PATH_PolylineTo(
1216     PDC dc,
1217     const POINT *pts,
1218     DWORD cbPoints)
1219 {
1220     PPATH pPath;
1221     BOOL ret;
1222 
1223     ASSERT(dc);
1224     ASSERT(pts);
1225     ASSERT(cbPoints);
1226 
1227     if (cbPoints < 1) return FALSE;
1228 
1229     pPath = PATH_LockPath(dc->dclevel.hPath);
1230     if (!pPath) return FALSE;
1231 
1232     ret = add_log_points_new_stroke( dc, pPath, pts, cbPoints, PT_LINETO );
1233     PATH_UnlockPath(pPath);
1234     return ret;
1235 }
1236 
1237 BOOL
1238 FASTCALL
1239 PATH_PolyPolygon(
1240     PDC dc,
1241     const POINT* pts,
1242     const INT* counts,
1243     UINT polygons)
1244 {
1245     UINT poly, count;
1246     BYTE *type;
1247     PPATH pPath;
1248 
1249     ASSERT(dc);
1250     ASSERT(pts);
1251     ASSERT(counts);
1252     ASSERT(polygons);
1253 
1254     if (!polygons) return FALSE;
1255 
1256     pPath = PATH_LockPath(dc->dclevel.hPath);
1257     if (!pPath) return FALSE;
1258 
1259 
1260     for (poly = count = 0; poly < polygons; poly++)
1261     {
1262         if (counts[poly] < 2)
1263         {
1264            PATH_UnlockPath(pPath);
1265            return FALSE;
1266         }
1267         count += counts[poly];
1268     }
1269 
1270     type = add_log_points( dc, pPath, pts, count, PT_LINETO );
1271     if (!type)
1272     {
1273        PATH_UnlockPath(pPath);
1274        return FALSE;
1275     }
1276 
1277     /* make the first point of each polyline a PT_MOVETO, and close the last one */
1278     for (poly = 0; poly < polygons; type += counts[poly++])
1279     {
1280         type[0] = PT_MOVETO;
1281         type[counts[poly] - 1] = PT_LINETO | PT_CLOSEFIGURE;
1282     }
1283     PATH_UnlockPath(pPath);
1284     return TRUE;
1285 }
1286 
1287 BOOL
1288 FASTCALL
1289 PATH_PolyPolyline(
1290     PDC dc,
1291     const POINT* pts,
1292     const DWORD* counts,
1293     DWORD polylines)
1294 {
1295     POINT pt;
1296     ULONG poly, point, i;
1297     PPATH pPath;
1298 
1299     ASSERT(dc);
1300     ASSERT(pts);
1301     ASSERT(counts);
1302     ASSERT(polylines);
1303 
1304     pPath = PATH_LockPath(dc->dclevel.hPath);
1305     if (!pPath)
1306     {
1307        return FALSE;
1308     }
1309 
1310     for (i = 0, poly = 0; poly < polylines; poly++)
1311     {
1312         for (point = 0; point < counts[poly]; point++, i++)
1313         {
1314             pt = pts[i];
1315             CoordLPtoDP(dc, &pt);
1316             PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1317         }
1318     }
1319     DPRINT("PATH_PolyPolyline end count %d\n",pPath->numEntriesUsed);
1320     PATH_UnlockPath(pPath);
1321     return TRUE;
1322 }
1323 
1324 /* PATH_AddFlatBezier
1325  *
1326  */
1327 BOOL
1328 FASTCALL
1329 PATH_AddFlatBezier(
1330     PPATH pPath,
1331     POINT *pt,
1332     BOOL closed)
1333 {
1334     POINT *pts;
1335     BOOL ret = FALSE;
1336     INT no, i;
1337 
1338     pts = GDI_Bezier(pt, 4, &no);
1339     if (!pts) return FALSE;
1340 
1341     for (i = 1; i < no; i++)
1342     {
1343         if (!(ret = PATH_AddEntry(pPath, &pts[i],  (i == no - 1 && closed) ? PT_LINETO | PT_CLOSEFIGURE : PT_LINETO)))
1344            break;
1345     }
1346 
1347     ExFreePoolWithTag(pts, TAG_BEZIER);
1348     return ret;
1349 }
1350 
1351 /* PATH_FlattenPath
1352  *
1353  * Replaces Beziers with line segments
1354  *
1355  */
1356 PPATH
1357 FASTCALL
1358 PATH_FlattenPath(PPATH pPath)
1359 {
1360     PPATH newPath;
1361     INT srcpt;
1362     DPRINT("PATH_FlattenPath\n");
1363     if (!(newPath = PATH_CreatePath(pPath->numEntriesUsed))) return NULL;
1364 
1365     for (srcpt = 0; srcpt < pPath->numEntriesUsed; srcpt++)
1366     {
1367         switch(pPath->pFlags[srcpt] & ~PT_CLOSEFIGURE)
1368         {
1369             case PT_MOVETO:
1370             case PT_LINETO:
1371                 if (!PATH_AddEntry(newPath, &pPath->pPoints[srcpt], pPath->pFlags[srcpt]))
1372                 {
1373                    PATH_UnlockPath(newPath);
1374                    PATH_Delete(newPath->BaseObject.hHmgr);
1375                    return NULL;
1376                 }
1377                 break;
1378             case PT_BEZIERTO:
1379                 if(!PATH_AddFlatBezier(newPath, &pPath->pPoints[srcpt - 1], pPath->pFlags[srcpt + 2] & PT_CLOSEFIGURE))
1380                 {
1381                    PATH_UnlockPath(newPath);
1382                    PATH_Delete(newPath->BaseObject.hHmgr);
1383                    return NULL;
1384                 }
1385                 srcpt += 2;
1386                 break;
1387         }
1388     }
1389     DPRINT("PATH_FlattenPath good\n");
1390     newPath->state = pPath->state;
1391     return newPath;
1392 }
1393 
1394 /* PATH_PathToRegion
1395  *
1396  * Creates a region from the specified path using the specified polygon
1397  * filling mode. The path is left unchanged. A handle to the region that
1398  * was created is stored in *pHrgn.
1399  */
1400 BOOL
1401 FASTCALL
1402 PATH_PathToRegion(
1403     PPATH pPath,
1404     INT Mode,
1405     PREGION Rgn)
1406 {
1407     int i, pos, polygons;
1408     PULONG counts;
1409     int Ret;
1410 
1411     if (!pPath->numEntriesUsed) return FALSE;
1412 
1413     counts = ExAllocatePoolWithTag(PagedPool, (pPath->numEntriesUsed / 2) * sizeof(counts), TAG_PATH);
1414     if (!counts)
1415     {
1416         DPRINT1("Failed to allocate %lu strokes\n", (pPath->numEntriesUsed / 2) * sizeof(*counts));
1417         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1418         return FALSE;
1419     }
1420 
1421     pos = polygons = 0;
1422     ASSERT( pPath->pFlags[0] == PT_MOVETO );
1423     for (i = 1; i < pPath->numEntriesUsed; i++)
1424     {
1425         if (pPath->pFlags[i] != PT_MOVETO) continue;
1426         counts[polygons++] = i - pos;
1427         pos = i;
1428     }
1429     if (i > pos + 1) counts[polygons++] = i - pos;
1430 
1431     ASSERT( polygons <= pPath->numEntriesUsed / 2 );
1432 
1433     /* Fill the region with the strokes */
1434     Ret = REGION_SetPolyPolygonRgn(Rgn,
1435                                    pPath->pPoints,
1436                                    counts,
1437                                    polygons,
1438                                    Mode);
1439     if (!Ret)
1440     {
1441         DPRINT1("REGION_SetPolyPolygonRgn failed\n");
1442     }
1443 
1444     ExFreePoolWithTag(counts, TAG_PATH);
1445 
1446     /* Success! */
1447     return Ret;
1448 }
1449 
1450 /* PATH_FillPath
1451  *
1452  * You can play with this as long as you like, but if you break Area.exe the purge will Begain on Path!!!
1453  *
1454  */
1455 BOOL
1456 FASTCALL
1457 PATH_FillPath(
1458     PDC dc,
1459     PPATH pPath)
1460 {
1461     INT   mapMode, graphicsMode;
1462     SIZE  ptViewportExt, ptWindowExt;
1463     POINTL ptViewportOrg, ptWindowOrg;
1464     XFORML xform;
1465     PREGION  Rgn;
1466     PDC_ATTR pdcattr = dc->pdcattr;
1467 
1468     /* Allocate a temporary region */
1469     Rgn = IntSysCreateRectpRgn(0, 0, 0, 0);
1470     if (!Rgn)
1471     {
1472         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1473         return FALSE;
1474     }
1475 
1476     if (!PATH_PathToRegion(pPath, pdcattr->jFillMode, Rgn))
1477     {
1478         DPRINT("PFP : Fail P2R\n");
1479         /* EngSetLastError ? */
1480         REGION_Delete(Rgn);
1481         return FALSE;
1482     }
1483 
1484     /* Since PaintRgn interprets the region as being in logical coordinates
1485      * but the points we store for the path are already in device
1486      * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
1487      * Using SaveDC to save information about the mapping mode / world
1488      * transform would be easier but would require more overhead, especially
1489      * now that SaveDC saves the current path.
1490      */
1491 
1492     /* Save the information about the old mapping mode */
1493     mapMode = pdcattr->iMapMode;
1494     ptViewportExt = pdcattr->szlViewportExt;
1495     ptViewportOrg = pdcattr->ptlViewportOrg;
1496     ptWindowExt   = pdcattr->szlWindowExt;
1497     ptWindowOrg   = pdcattr->ptlWindowOrg;
1498 
1499     /* Save world transform
1500      * NB: The Windows documentation on world transforms would lead one to
1501      * believe that this has to be done only in GM_ADVANCED; however, my
1502      * tests show that resetting the graphics mode to GM_COMPATIBLE does
1503      * not reset the world transform.
1504      */
1505     MatrixS2XForm(&xform, &dc->pdcattr->mxWorldToPage);
1506 
1507     /* Set MM_TEXT */
1508     IntGdiSetMapMode(dc, MM_TEXT);
1509     pdcattr->ptlViewportOrg.x = 0;
1510     pdcattr->ptlViewportOrg.y = 0;
1511     pdcattr->ptlWindowOrg.x = 0;
1512     pdcattr->ptlWindowOrg.y = 0;
1513 
1514     graphicsMode = pdcattr->iGraphicsMode;
1515     pdcattr->iGraphicsMode = GM_ADVANCED;
1516     GreModifyWorldTransform(dc, &xform, MWT_IDENTITY);
1517     pdcattr->iGraphicsMode =  graphicsMode;
1518 
1519     /* Paint the region */
1520     IntGdiPaintRgn(dc, Rgn);
1521     REGION_Delete(Rgn);
1522     /* Restore the old mapping mode */
1523     IntGdiSetMapMode(dc, mapMode);
1524     pdcattr->szlViewportExt = ptViewportExt;
1525     pdcattr->ptlViewportOrg = ptViewportOrg;
1526     pdcattr->szlWindowExt   = ptWindowExt;
1527     pdcattr->ptlWindowOrg   = ptWindowOrg;
1528 
1529     /* Go to GM_ADVANCED temporarily to restore the world transform */
1530     graphicsMode = pdcattr->iGraphicsMode;
1531     pdcattr->iGraphicsMode = GM_ADVANCED;
1532     GreModifyWorldTransform(dc, &xform, MWT_SET);
1533     pdcattr->iGraphicsMode = graphicsMode;
1534     return TRUE;
1535 }
1536 
1537 BOOL
1538 FASTCALL
1539 PATH_StrokePath(
1540     DC *dc,
1541     PPATH pPath)
1542 {
1543     BOOL ret = FALSE;
1544     INT i = 0;
1545     INT nLinePts, nAlloc;
1546     POINT *pLinePts = NULL;
1547     POINT ptViewportOrg, ptWindowOrg;
1548     SIZE szViewportExt, szWindowExt;
1549     DWORD mapMode, graphicsMode;
1550     XFORM xform;
1551     PDC_ATTR pdcattr = dc->pdcattr;
1552 
1553     DPRINT("Enter %s\n", __FUNCTION__);
1554 
1555     /* Save the mapping mode info */
1556     mapMode = pdcattr->iMapMode;
1557 
1558     szViewportExt = *DC_pszlViewportExt(dc);
1559     ptViewportOrg = dc->pdcattr->ptlViewportOrg;
1560     szWindowExt = dc->pdcattr->szlWindowExt;
1561     ptWindowOrg = dc->pdcattr->ptlWindowOrg;
1562 
1563     MatrixS2XForm(&xform, &dc->pdcattr->mxWorldToPage);
1564 
1565     /* Set MM_TEXT */
1566     pdcattr->iMapMode = MM_TEXT;
1567     pdcattr->ptlViewportOrg.x = 0;
1568     pdcattr->ptlViewportOrg.y = 0;
1569     pdcattr->ptlWindowOrg.x = 0;
1570     pdcattr->ptlWindowOrg.y = 0;
1571     graphicsMode = pdcattr->iGraphicsMode;
1572     pdcattr->iGraphicsMode = GM_ADVANCED;
1573     GreModifyWorldTransform(dc, (XFORML*)&xform, MWT_IDENTITY);
1574     pdcattr->iGraphicsMode = graphicsMode;
1575 
1576     /* Allocate enough memory for the worst case without beziers (one PT_MOVETO
1577      * and the rest PT_LINETO with PT_CLOSEFIGURE at the end) plus some buffer
1578      * space in case we get one to keep the number of reallocations small. */
1579     nAlloc = pPath->numEntriesUsed + 1 + 300;
1580     pLinePts = ExAllocatePoolWithTag(PagedPool, nAlloc * sizeof(POINT), TAG_PATH);
1581     if (!pLinePts)
1582     {
1583         DPRINT1("Can't allocate pool!\n");
1584         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1585         goto end;
1586     }
1587     nLinePts = 0;
1588 
1589     for (i = 0; i < pPath->numEntriesUsed; i++)
1590     {
1591         if ((i == 0 || (pPath->pFlags[i - 1] & PT_CLOSEFIGURE))
1592                 && (pPath->pFlags[i] != PT_MOVETO))
1593         {
1594             DPRINT1("Expected PT_MOVETO %s, got path flag %d\n",
1595                     i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
1596                     (INT)pPath->pFlags[i]);
1597             goto end;
1598         }
1599 
1600         switch(pPath->pFlags[i])
1601         {
1602             case PT_MOVETO:
1603                 DPRINT("Got PT_MOVETO (%ld, %ld)\n",
1604                        pPath->pPoints[i].x, pPath->pPoints[i].y);
1605                 if (nLinePts >= 2) IntGdiPolyline(dc, pLinePts, nLinePts);
1606                 nLinePts = 0;
1607                 pLinePts[nLinePts++] = pPath->pPoints[i];
1608                 break;
1609             case PT_LINETO:
1610             case (PT_LINETO | PT_CLOSEFIGURE):
1611                 DPRINT("Got PT_LINETO (%ld, %ld)\n",
1612                        pPath->pPoints[i].x, pPath->pPoints[i].y);
1613                 pLinePts[nLinePts++] = pPath->pPoints[i];
1614                 break;
1615             case PT_BEZIERTO:
1616                 DPRINT("Got PT_BEZIERTO\n");
1617                 if (pPath->pFlags[i + 1] != PT_BEZIERTO ||
1618                         (pPath->pFlags[i + 2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO)
1619                 {
1620                     DPRINT1("Path didn't contain 3 successive PT_BEZIERTOs\n");
1621                     ret = FALSE;
1622                     goto end;
1623                 }
1624                 else
1625                 {
1626                     INT nBzrPts, nMinAlloc;
1627                     POINT *pBzrPts = GDI_Bezier(&pPath->pPoints[i - 1], 4, &nBzrPts);
1628                     /* Make sure we have allocated enough memory for the lines of
1629                      * this bezier and the rest of the path, assuming we won't get
1630                      * another one (since we won't reallocate again then). */
1631                     nMinAlloc = nLinePts + (pPath->numEntriesUsed - i) + nBzrPts;
1632                     if (nAlloc < nMinAlloc)
1633                     {
1634                         // Reallocate memory
1635 
1636                         POINT *Realloc = NULL;
1637                         nAlloc = nMinAlloc * 2;
1638 
1639                         Realloc = ExAllocatePoolWithTag(PagedPool,
1640                                                         nAlloc * sizeof(POINT),
1641                                                         TAG_PATH);
1642 
1643                         if (!Realloc)
1644                         {
1645                             DPRINT1("Can't allocate pool!\n");
1646                             ExFreePoolWithTag(pBzrPts, TAG_BEZIER);
1647                             goto end;
1648                         }
1649 
1650                         memcpy(Realloc, pLinePts, nLinePts * sizeof(POINT));
1651                         ExFreePoolWithTag(pLinePts, TAG_PATH);
1652                         pLinePts = Realloc;
1653                     }
1654                     memcpy(&pLinePts[nLinePts], &pBzrPts[1], (nBzrPts - 1) * sizeof(POINT));
1655                     nLinePts += nBzrPts - 1;
1656                     ExFreePoolWithTag(pBzrPts, TAG_BEZIER);
1657                     i += 2;
1658                 }
1659                 break;
1660             default:
1661                 DPRINT1("Got path flag %d (not supported)\n", (INT)pPath->pFlags[i]);
1662                 goto end;
1663         }
1664 
1665         if (pPath->pFlags[i] & PT_CLOSEFIGURE)
1666         {
1667             pLinePts[nLinePts++] = pLinePts[0];
1668         }
1669     }
1670     if (nLinePts >= 2)
1671         IntGdiPolyline(dc, pLinePts, nLinePts);
1672 
1673     ret = TRUE;
1674 
1675 end:
1676     if (pLinePts) ExFreePoolWithTag(pLinePts, TAG_PATH);
1677 
1678     /* Restore the old mapping mode */
1679     pdcattr->iMapMode =  mapMode;
1680     pdcattr->szlWindowExt.cx = szWindowExt.cx;
1681     pdcattr->szlWindowExt.cy = szWindowExt.cy;
1682     pdcattr->ptlWindowOrg.x = ptWindowOrg.x;
1683     pdcattr->ptlWindowOrg.y = ptWindowOrg.y;
1684 
1685     pdcattr->szlViewportExt.cx = szViewportExt.cx;
1686     pdcattr->szlViewportExt.cy = szViewportExt.cy;
1687     pdcattr->ptlViewportOrg.x = ptViewportOrg.x;
1688     pdcattr->ptlViewportOrg.y = ptViewportOrg.y;
1689 
1690     /* Restore the world transform */
1691     XForm2MatrixS(&dc->pdcattr->mxWorldToPage, &xform);
1692 
1693     /* If we've moved the current point then get its new position
1694        which will be in device (MM_TEXT) co-ords, convert it to
1695        logical co-ords and re-set it.  This basically updates
1696        dc->CurPosX|Y so that their values are in the correct mapping
1697        mode.
1698     */
1699     if (i > 0)
1700     {
1701         POINT pt;
1702         IntGetCurrentPositionEx(dc, &pt);
1703         IntDPtoLP(dc, &pt, 1);
1704         IntGdiMoveToEx(dc, pt.x, pt.y, NULL);
1705     }
1706     DPRINT("Leave %s, ret=%d\n", __FUNCTION__, ret);
1707     return ret;
1708 }
1709 
1710 #define round(x) ((int)((x)>0?(x)+0.5:(x)-0.5))
1711 
1712 static
1713 PPATH
1714 FASTCALL
1715 PATH_WidenPath(DC *dc)
1716 {
1717     INT i, j, numStrokes, numOldStrokes, penWidth, penWidthIn, penWidthOut, size, penStyle;
1718     PPATH pPath, flat_path, pNewPath, *pStrokes = NULL, *pOldStrokes, pUpPath, pDownPath;
1719     EXTLOGPEN *elp;
1720     BYTE *type;
1721     DWORD obj_type, joint, endcap, penType;
1722     PDC_ATTR pdcattr = dc->pdcattr;
1723 
1724     pPath = PATH_LockPath(dc->dclevel.hPath);
1725     if (!pPath)
1726     {
1727         EngSetLastError( ERROR_CAN_NOT_COMPLETE );
1728         return NULL;
1729     }
1730 
1731     if (pPath->state != PATH_Closed)
1732     {
1733         DPRINT("PWP 1\n");
1734         PATH_UnlockPath(pPath);
1735         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1736         return NULL;
1737     }
1738 
1739     size = GreGetObject(pdcattr->hpen, 0, NULL);
1740     if (!size)
1741     {
1742         DPRINT("PWP 2\n");
1743         PATH_UnlockPath(pPath);
1744         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1745         return FALSE;
1746     }
1747 
1748     elp = ExAllocatePoolWithTag(PagedPool, size, TAG_PATH);
1749     if (elp == NULL)
1750     {
1751         DPRINT("PWP 3\n");
1752         PATH_UnlockPath(pPath);
1753         EngSetLastError(ERROR_OUTOFMEMORY);
1754         return FALSE;
1755     }
1756 
1757     GreGetObject(pdcattr->hpen, size, elp);
1758 
1759     obj_type = GDI_HANDLE_GET_TYPE(pdcattr->hpen);
1760     if (obj_type == GDI_OBJECT_TYPE_PEN)
1761     {
1762         penStyle = ((LOGPEN*)elp)->lopnStyle;
1763     }
1764     else if (obj_type == GDI_OBJECT_TYPE_EXTPEN)
1765     {
1766         penStyle = elp->elpPenStyle;
1767     }
1768     else
1769     {
1770         DPRINT("PWP 4\n");
1771         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1772         ExFreePoolWithTag(elp, TAG_PATH);
1773         PATH_UnlockPath(pPath);
1774         return FALSE;
1775     }
1776 
1777     penWidth = elp->elpWidth;
1778     ExFreePoolWithTag(elp, TAG_PATH);
1779 
1780     endcap = (PS_ENDCAP_MASK & penStyle);
1781     joint = (PS_JOIN_MASK & penStyle);
1782     penType = (PS_TYPE_MASK & penStyle);
1783 
1784     /* The function cannot apply to cosmetic pens */
1785     if (obj_type == GDI_OBJECT_TYPE_EXTPEN && penType == PS_COSMETIC)
1786     {
1787         DPRINT("PWP 5\n");
1788         PATH_UnlockPath(pPath);
1789         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1790         return FALSE;
1791     }
1792 
1793     if (!(flat_path = PATH_FlattenPath(pPath)))
1794     {
1795        PATH_UnlockPath(pPath);
1796        return NULL;
1797     }
1798     PATH_UnlockPath(pPath);
1799 
1800     penWidthIn = penWidth / 2;
1801     penWidthOut = penWidth / 2;
1802     if (penWidthIn + penWidthOut < penWidth)
1803         penWidthOut++;
1804 
1805     numStrokes = 0;
1806 
1807     for (i = 0, j = 0; i < flat_path->numEntriesUsed; i++, j++)
1808     {
1809         POINT point;
1810         if ((i == 0 || (flat_path->pFlags[i - 1] & PT_CLOSEFIGURE)) &&
1811                 (flat_path->pFlags[i] != PT_MOVETO))
1812         {
1813             DPRINT1("Expected PT_MOVETO %s, got path flag %c\n",
1814                     i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
1815                     flat_path->pFlags[i]);
1816             if (pStrokes)
1817                 ExFreePoolWithTag(pStrokes, TAG_PATH);
1818             PATH_UnlockPath(flat_path);
1819             PATH_Delete(flat_path->BaseObject.hHmgr);
1820             return FALSE;
1821         }
1822         switch(flat_path->pFlags[i])
1823         {
1824             case PT_MOVETO:
1825                 if (numStrokes > 0)
1826                 {
1827                     pStrokes[numStrokes - 1]->state = PATH_Closed;
1828                 }
1829                 numOldStrokes = numStrokes;
1830                 numStrokes++;
1831                 j = 0;
1832                 if (numStrokes == 1)
1833                     pStrokes = ExAllocatePoolWithTag(PagedPool, sizeof(*pStrokes), TAG_PATH);
1834                 else
1835                 {
1836                     pOldStrokes = pStrokes; // Save old pointer.
1837                     pStrokes = ExAllocatePoolWithTag(PagedPool, numStrokes * sizeof(*pStrokes), TAG_PATH);
1838                     if (!pStrokes)
1839                     {
1840                        PATH_UnlockPath(flat_path);
1841                        PATH_Delete(flat_path->BaseObject.hHmgr);
1842                        return FALSE;
1843                     }
1844                     RtlCopyMemory(pStrokes, pOldStrokes, numOldStrokes * sizeof(PPATH));
1845                     ExFreePoolWithTag(pOldStrokes, TAG_PATH); // Free old pointer.
1846                 }
1847                 if (!pStrokes)
1848                 {
1849                    PATH_UnlockPath(flat_path);
1850                    PATH_Delete(flat_path->BaseObject.hHmgr);
1851                    return FALSE;
1852                 }
1853                 pStrokes[numStrokes - 1] = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1854                 if (!pStrokes[numStrokes - 1])
1855                 {
1856                     ASSERT(FALSE); // FIXME
1857                 }
1858                 PATH_InitGdiPath(pStrokes[numStrokes - 1]);
1859                 pStrokes[numStrokes - 1]->state = PATH_Open;
1860             case PT_LINETO:
1861             case (PT_LINETO | PT_CLOSEFIGURE):
1862                 point.x = flat_path->pPoints[i].x;
1863                 point.y = flat_path->pPoints[i].y;
1864                 PATH_AddEntry(pStrokes[numStrokes - 1], &point, flat_path->pFlags[i]);
1865                 break;
1866             case PT_BEZIERTO:
1867                 /* Should never happen because of the FlattenPath call */
1868                 DPRINT1("Should never happen\n");
1869                 break;
1870             default:
1871                 DPRINT1("Got path flag %c\n", flat_path->pFlags[i]);
1872                 if (pStrokes)
1873                     ExFreePoolWithTag(pStrokes, TAG_PATH);
1874                 PATH_UnlockPath(flat_path);
1875                 PATH_Delete(flat_path->BaseObject.hHmgr);
1876                 return FALSE;
1877         }
1878     }
1879 
1880     pNewPath = PATH_CreatePath( flat_path->numEntriesUsed );
1881 
1882     for (i = 0; i < numStrokes; i++)
1883     {
1884         pUpPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1885         PATH_InitGdiPath(pUpPath);
1886         pUpPath->state = PATH_Open;
1887         pDownPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1888         PATH_InitGdiPath(pDownPath);
1889         pDownPath->state = PATH_Open;
1890 
1891         for (j = 0; j < pStrokes[i]->numEntriesUsed; j++)
1892         {
1893             /* Beginning or end of the path if not closed */
1894             if ((!(pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE)) && (j == 0 || j == pStrokes[i]->numEntriesUsed - 1))
1895             {
1896                 /* Compute segment angle */
1897                 double xo, yo, xa, ya, theta;
1898                 POINT pt;
1899                 FLOAT_POINT corners[2];
1900                 if (j == 0)
1901                 {
1902                     xo = pStrokes[i]->pPoints[j].x;
1903                     yo = pStrokes[i]->pPoints[j].y;
1904                     xa = pStrokes[i]->pPoints[1].x;
1905                     ya = pStrokes[i]->pPoints[1].y;
1906                 }
1907                 else
1908                 {
1909                     xa = pStrokes[i]->pPoints[j - 1].x;
1910                     ya = pStrokes[i]->pPoints[j - 1].y;
1911                     xo = pStrokes[i]->pPoints[j].x;
1912                     yo = pStrokes[i]->pPoints[j].y;
1913                 }
1914                 theta = atan2(ya - yo, xa - xo);
1915                 switch(endcap)
1916                 {
1917                     case PS_ENDCAP_SQUARE :
1918                         pt.x = xo + round(sqrt(2) * penWidthOut * cos(M_PI_4 + theta));
1919                         pt.y = yo + round(sqrt(2) * penWidthOut * sin(M_PI_4 + theta));
1920                         PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
1921                         pt.x = xo + round(sqrt(2) * penWidthIn * cos(- M_PI_4 + theta));
1922                         pt.y = yo + round(sqrt(2) * penWidthIn * sin(- M_PI_4 + theta));
1923                         PATH_AddEntry(pUpPath, &pt, PT_LINETO);
1924                         break;
1925                     case PS_ENDCAP_FLAT :
1926                         pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
1927                         pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
1928                         PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
1929                         pt.x = xo - round(penWidthIn * cos(theta + M_PI_2));
1930                         pt.y = yo - round(penWidthIn * sin(theta + M_PI_2));
1931                         PATH_AddEntry(pUpPath, &pt, PT_LINETO);
1932                         break;
1933                     case PS_ENDCAP_ROUND :
1934                     default :
1935                         corners[0].x = xo - penWidthIn;
1936                         corners[0].y = yo - penWidthIn;
1937                         corners[1].x = xo + penWidthOut;
1938                         corners[1].y = yo + penWidthOut;
1939                         PATH_DoArcPart(pUpPath , corners, theta + M_PI_2 , theta + 3 * M_PI_4, (j == 0 ? PT_MOVETO : FALSE));
1940                         PATH_DoArcPart(pUpPath , corners, theta + 3 * M_PI_4 , theta + M_PI, FALSE);
1941                         PATH_DoArcPart(pUpPath , corners, theta + M_PI, theta +  5 * M_PI_4, FALSE);
1942                         PATH_DoArcPart(pUpPath , corners, theta + 5 * M_PI_4 , theta + 3 * M_PI_2, FALSE);
1943                         break;
1944                 }
1945             }
1946             /* Corpse of the path */
1947             else
1948             {
1949                 /* Compute angle */
1950                 INT previous, next;
1951                 double xa, ya, xb, yb, xo, yo;
1952                 double alpha, theta, miterWidth;
1953                 DWORD _joint = joint;
1954                 POINT pt;
1955                 PPATH pInsidePath, pOutsidePath;
1956                 if (j > 0 && j < pStrokes[i]->numEntriesUsed - 1)
1957                 {
1958                     previous = j - 1;
1959                     next = j + 1;
1960                 }
1961                 else if (j == 0)
1962                 {
1963                     previous = pStrokes[i]->numEntriesUsed - 1;
1964                     next = j + 1;
1965                 }
1966                 else
1967                 {
1968                     previous = j - 1;
1969                     next = 0;
1970                 }
1971                 xo = pStrokes[i]->pPoints[j].x;
1972                 yo = pStrokes[i]->pPoints[j].y;
1973                 xa = pStrokes[i]->pPoints[previous].x;
1974                 ya = pStrokes[i]->pPoints[previous].y;
1975                 xb = pStrokes[i]->pPoints[next].x;
1976                 yb = pStrokes[i]->pPoints[next].y;
1977                 theta = atan2(yo - ya, xo - xa);
1978                 alpha = atan2(yb - yo, xb - xo) - theta;
1979                 if (alpha > 0) alpha -= M_PI;
1980                 else alpha += M_PI;
1981                 if (_joint == PS_JOIN_MITER && dc->dclevel.laPath.eMiterLimit < fabs(1 / sin(alpha / 2)))
1982                 {
1983                     _joint = PS_JOIN_BEVEL;
1984                 }
1985                 if (alpha > 0)
1986                 {
1987                     pInsidePath = pUpPath;
1988                     pOutsidePath = pDownPath;
1989                 }
1990                 else if (alpha < 0)
1991                 {
1992                     pInsidePath = pDownPath;
1993                     pOutsidePath = pUpPath;
1994                 }
1995                 else
1996                 {
1997                     continue;
1998                 }
1999                 /* Inside angle points */
2000                 if (alpha > 0)
2001                 {
2002                     pt.x = xo - round(penWidthIn * cos(theta + M_PI_2));
2003                     pt.y = yo - round(penWidthIn * sin(theta + M_PI_2));
2004                 }
2005                 else
2006                 {
2007                     pt.x = xo + round(penWidthIn * cos(theta + M_PI_2));
2008                     pt.y = yo + round(penWidthIn * sin(theta + M_PI_2));
2009                 }
2010                 PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
2011                 if (alpha > 0)
2012                 {
2013                     pt.x = xo + round(penWidthIn * cos(M_PI_2 + alpha + theta));
2014                     pt.y = yo + round(penWidthIn * sin(M_PI_2 + alpha + theta));
2015                 }
2016                 else
2017                 {
2018                     pt.x = xo - round(penWidthIn * cos(M_PI_2 + alpha + theta));
2019                     pt.y = yo - round(penWidthIn * sin(M_PI_2 + alpha + theta));
2020                 }
2021                 PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
2022                 /* Outside angle point */
2023                 switch(_joint)
2024                 {
2025                     case PS_JOIN_MITER :
2026                         miterWidth = fabs(penWidthOut / cos(M_PI_2 - fabs(alpha) / 2));
2027                         pt.x = xo + round(miterWidth * cos(theta + alpha / 2));
2028                         pt.y = yo + round(miterWidth * sin(theta + alpha / 2));
2029                         PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2030                         break;
2031                     case PS_JOIN_BEVEL :
2032                         if (alpha > 0)
2033                         {
2034                             pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
2035                             pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
2036                         }
2037                         else
2038                         {
2039                             pt.x = xo - round(penWidthOut * cos(theta + M_PI_2));
2040                             pt.y = yo - round(penWidthOut * sin(theta + M_PI_2));
2041                         }
2042                         PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2043                         if (alpha > 0)
2044                         {
2045                             pt.x = xo - round(penWidthOut * cos(M_PI_2 + alpha + theta));
2046                             pt.y = yo - round(penWidthOut * sin(M_PI_2 + alpha + theta));
2047                         }
2048                         else
2049                         {
2050                             pt.x = xo + round(penWidthOut * cos(M_PI_2 + alpha + theta));
2051                             pt.y = yo + round(penWidthOut * sin(M_PI_2 + alpha + theta));
2052                         }
2053                         PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2054                         break;
2055                     case PS_JOIN_ROUND :
2056                     default :
2057                         if (alpha > 0)
2058                         {
2059                             pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
2060                             pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
2061                         }
2062                         else
2063                         {
2064                             pt.x = xo - round(penWidthOut * cos(theta + M_PI_2));
2065                             pt.y = yo - round(penWidthOut * sin(theta + M_PI_2));
2066                         }
2067                         PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2068                         pt.x = xo + round(penWidthOut * cos(theta + alpha / 2));
2069                         pt.y = yo + round(penWidthOut * sin(theta + alpha / 2));
2070                         PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2071                         if (alpha > 0)
2072                         {
2073                             pt.x = xo - round(penWidthOut * cos(M_PI_2 + alpha + theta));
2074                             pt.y = yo - round(penWidthOut * sin(M_PI_2 + alpha + theta));
2075                         }
2076                         else
2077                         {
2078                             pt.x = xo + round(penWidthOut * cos(M_PI_2 + alpha + theta));
2079                             pt.y = yo + round(penWidthOut * sin(M_PI_2 + alpha + theta));
2080                         }
2081                         PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2082                         break;
2083                 }
2084             }
2085         }
2086         type = add_points( pNewPath, pUpPath->pPoints, pUpPath->numEntriesUsed, PT_LINETO );
2087         type[0] = PT_MOVETO;
2088         reverse_points( pDownPath->pPoints, pDownPath->numEntriesUsed );
2089         type = add_points( pNewPath, pDownPath->pPoints, pDownPath->numEntriesUsed, PT_LINETO );
2090         if (pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE) type[0] = PT_MOVETO;
2091 
2092         PATH_DestroyGdiPath(pStrokes[i]);
2093         ExFreePoolWithTag(pStrokes[i], TAG_PATH);
2094         PATH_DestroyGdiPath(pUpPath);
2095         ExFreePoolWithTag(pUpPath, TAG_PATH);
2096         PATH_DestroyGdiPath(pDownPath);
2097         ExFreePoolWithTag(pDownPath, TAG_PATH);
2098     }
2099     if (pStrokes) ExFreePoolWithTag(pStrokes, TAG_PATH);
2100 
2101     PATH_UnlockPath(flat_path);
2102     PATH_Delete(flat_path->BaseObject.hHmgr);
2103     pNewPath->state = PATH_Closed;
2104     PATH_UnlockPath(pNewPath);
2105     return pNewPath;
2106 }
2107 
2108 static inline INT int_from_fixed(FIXED f)
2109 {
2110     return (f.fract >= 0x8000) ? (f.value + 1) : f.value;
2111 }
2112 
2113 /**********************************************************************
2114  *      PATH_BezierTo
2115  *
2116  * Internally used by PATH_add_outline
2117  */
2118 static
2119 VOID
2120 FASTCALL
2121 PATH_BezierTo(
2122     PPATH pPath,
2123     POINT *lppt,
2124     INT n)
2125 {
2126     if (n < 2) return;
2127 
2128     if (n == 2)
2129     {
2130         PATH_AddEntry(pPath, &lppt[1], PT_LINETO);
2131     }
2132     else if (n == 3)
2133     {
2134         add_points( pPath, lppt, 3, PT_BEZIERTO );
2135     }
2136     else
2137     {
2138         POINT pt[3];
2139         INT i = 0;
2140 
2141         pt[2] = lppt[0];
2142         n--;
2143 
2144         while (n > 2)
2145         {
2146             pt[0] = pt[2];
2147             pt[1] = lppt[i + 1];
2148             pt[2].x = (lppt[i + 2].x + lppt[i + 1].x) / 2;
2149             pt[2].y = (lppt[i + 2].y + lppt[i + 1].y) / 2;
2150             add_points( pPath, pt, 3, PT_BEZIERTO );
2151             n--;
2152             i++;
2153         }
2154 
2155         pt[0] = pt[2];
2156         pt[1] = lppt[i + 1];
2157         pt[2] = lppt[i + 2];
2158         add_points( pPath, pt, 3, PT_BEZIERTO );
2159     }
2160 }
2161 
2162 static
2163 BOOL
2164 FASTCALL
2165 PATH_add_outline(
2166     PDC dc,
2167     PPATH pPath,
2168     INT x,
2169     INT y,
2170     TTPOLYGONHEADER *header,
2171     DWORD size)
2172 {
2173     TTPOLYGONHEADER *start;
2174     POINT pt;
2175     BOOL bResult = FALSE;
2176 
2177     start = header;
2178 
2179     while ((char *)header < (char *)start + size)
2180     {
2181         TTPOLYCURVE *curve;
2182 
2183         if (header->dwType != TT_POLYGON_TYPE)
2184         {
2185             DPRINT1("Unknown header type %lu\n", header->dwType);
2186             goto cleanup;
2187         }
2188 
2189         pt.x = x + int_from_fixed(header->pfxStart.x);
2190         pt.y = y - int_from_fixed(header->pfxStart.y);
2191         PATH_AddEntry(pPath, &pt, PT_MOVETO);
2192 
2193         curve = (TTPOLYCURVE *)(header + 1);
2194 
2195         while ((char *)curve < (char *)header + header->cb)
2196         {
2197             /*DPRINT1("curve->wType %d\n", curve->wType);*/
2198 
2199             switch(curve->wType)
2200             {
2201                 case TT_PRIM_LINE:
2202                 {
2203                     WORD i;
2204 
2205                     for (i = 0; i < curve->cpfx; i++)
2206                     {
2207                         pt.x = x + int_from_fixed(curve->apfx[i].x);
2208                         pt.y = y - int_from_fixed(curve->apfx[i].y);
2209                         PATH_AddEntry(pPath, &pt, PT_LINETO);
2210                     }
2211                     break;
2212                 }
2213 
2214                 case TT_PRIM_QSPLINE:
2215                 case TT_PRIM_CSPLINE:
2216                 {
2217                     WORD i;
2218                     POINTFX ptfx;
2219                     POINT *pts = ExAllocatePoolWithTag(PagedPool, (curve->cpfx + 1) * sizeof(POINT), TAG_PATH);
2220 
2221                     if (!pts) goto cleanup;
2222 
2223                     ptfx = *(POINTFX *)((char *)curve - sizeof(POINTFX));
2224 
2225                     pts[0].x = x + int_from_fixed(ptfx.x);
2226                     pts[0].y = y - int_from_fixed(ptfx.y);
2227 
2228                     for (i = 0; i < curve->cpfx; i++)
2229                     {
2230                         pts[i + 1].x = x + int_from_fixed(curve->apfx[i].x);
2231                         pts[i + 1].y = y - int_from_fixed(curve->apfx[i].y);
2232                     }
2233 
2234                     PATH_BezierTo(pPath, pts, curve->cpfx + 1);
2235 
2236                     ExFreePoolWithTag(pts, TAG_PATH);
2237                     break;
2238                 }
2239 
2240                 default:
2241                     DPRINT1("Unknown curve type %04x\n", curve->wType);
2242                     goto cleanup;
2243             }
2244 
2245             curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
2246         }
2247         header = (TTPOLYGONHEADER *)((char *)header + header->cb);
2248     }
2249 
2250     bResult = TRUE;
2251 
2252 cleanup:
2253     IntGdiCloseFigure(pPath);
2254     return bResult;
2255 }
2256 
2257 /**********************************************************************
2258  *      PATH_ExtTextOut
2259  */
2260 BOOL
2261 FASTCALL
2262 PATH_ExtTextOut(
2263     PDC dc,
2264     INT x,
2265     INT y,
2266     UINT flags,
2267     const RECTL *lprc,
2268     LPCWSTR str,
2269     UINT count,
2270     const INT *dx)
2271 {
2272     PPATH pPath;
2273     unsigned int idx, ggo_flags = GGO_NATIVE;
2274     POINT offset = {0, 0};
2275 
2276     pPath = PATH_LockPath(dc->dclevel.hPath);
2277     if (!pPath)
2278     {
2279         return FALSE;
2280     }
2281 
2282     if (pPath->state != PATH_Open)
2283     {
2284         DPRINT1("PATH_ExtTextOut not open\n");
2285         return FALSE;
2286     }
2287 
2288     if (!count) return TRUE;
2289     if (flags & ETO_GLYPH_INDEX) ggo_flags |= GGO_GLYPH_INDEX;
2290 
2291     for (idx = 0; idx < count; idx++)
2292     {
2293         MAT2 identity = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
2294         GLYPHMETRICS gm;
2295         DWORD dwSize;
2296         void *outline;
2297 
2298         dwSize = ftGdiGetGlyphOutline(dc,
2299                                       str[idx],
2300                                       ggo_flags,
2301                                       &gm,
2302                                       0,
2303                                       NULL,
2304                                       &identity,
2305                                       TRUE);
2306         if (dwSize == GDI_ERROR)
2307         {
2308            PATH_UnlockPath(pPath);
2309            return FALSE;
2310         }
2311 
2312         /* Add outline only if char is printable */
2313         if (dwSize)
2314         {
2315             outline = ExAllocatePoolWithTag(PagedPool, dwSize, TAG_PATH);
2316             if (!outline)
2317             {
2318                PATH_UnlockPath(pPath);
2319                return FALSE;
2320             }
2321 
2322             ftGdiGetGlyphOutline(dc,
2323                                  str[idx],
2324                                  ggo_flags,
2325                                  &gm,
2326                                  dwSize,
2327                                  outline,
2328                                  &identity,
2329                                  TRUE);
2330 
2331             PATH_add_outline(dc, pPath, x + offset.x, y + offset.y, outline, dwSize);
2332 
2333             ExFreePoolWithTag(outline, TAG_PATH);
2334         }
2335 
2336         if (dx)
2337         {
2338             if (flags & ETO_PDY)
2339             {
2340                 offset.x += dx[idx * 2];
2341                 offset.y += dx[idx * 2 + 1];
2342             }
2343             else
2344                 offset.x += dx[idx];
2345         }
2346         else
2347         {
2348             offset.x += gm.gmCellIncX;
2349             offset.y += gm.gmCellIncY;
2350         }
2351     }
2352     PATH_UnlockPath(pPath);
2353     return TRUE;
2354 }
2355 
2356 
2357 /***********************************************************************
2358  * Exported functions
2359  */
2360 
2361 BOOL
2362 APIENTRY
2363 NtGdiAbortPath(HDC hDC)
2364 {
2365     PDC dc = DC_LockDc(hDC);
2366     if (!dc)
2367     {
2368         EngSetLastError(ERROR_INVALID_HANDLE);
2369         return FALSE;
2370     }
2371 
2372     if (!dc->dclevel.hPath)
2373     {
2374        DC_UnlockDc(dc);
2375        return TRUE;
2376     }
2377 
2378     if (!PATH_Delete(dc->dclevel.hPath))
2379     {
2380        DC_UnlockDc(dc);
2381        return FALSE;
2382     }
2383 
2384     dc->dclevel.hPath = 0;
2385     dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2386 
2387     DC_UnlockDc(dc);
2388     return TRUE;
2389 }
2390 
2391 BOOL
2392 APIENTRY
2393 NtGdiBeginPath(HDC  hDC)
2394 {
2395     PPATH pPath;
2396     PDC dc;
2397 
2398     dc = DC_LockDc(hDC);
2399     if (!dc)
2400     {
2401         EngSetLastError(ERROR_INVALID_HANDLE);
2402         return FALSE;
2403     }
2404 
2405     /* If path is already open, do nothing. Check if not Save DC state */
2406     if ((dc->dclevel.flPath & DCPATH_ACTIVE) && !(dc->dclevel.flPath & DCPATH_SAVE))
2407     {
2408         DC_UnlockDc(dc);
2409         return TRUE;
2410     }
2411 
2412     if (dc->dclevel.hPath)
2413     {
2414         DPRINT("BeginPath 1 0x%p\n", dc->dclevel.hPath);
2415         if (!(dc->dclevel.flPath & DCPATH_SAVE))
2416         {
2417             // Remove previous handle.
2418             if (!PATH_Delete(dc->dclevel.hPath))
2419             {
2420                 DC_UnlockDc(dc);
2421                 return FALSE;
2422             }
2423         }
2424         else
2425         {
2426             // Clear flags and Handle.
2427             dc->dclevel.flPath &= ~(DCPATH_SAVE | DCPATH_ACTIVE);
2428             dc->dclevel.hPath = NULL;
2429         }
2430     }
2431     pPath = PATH_CreatePath(NUM_ENTRIES_INITIAL);
2432     dc->dclevel.flPath |= DCPATH_ACTIVE; // Set active ASAP!
2433     dc->dclevel.hPath = pPath->BaseObject.hHmgr;
2434     IntGetCurrentPositionEx(dc, &pPath->pos);
2435     IntLPtoDP( dc, &pPath->pos, 1 );
2436     DPRINT("BP : Current pos X %d Y %d\n",pPath->pos.x, pPath->pos.y);
2437     PATH_UnlockPath(pPath);
2438     DC_UnlockDc(dc);
2439 
2440     if (!pPath)
2441     {
2442         return FALSE;
2443     }
2444     return TRUE;
2445 }
2446 
2447 BOOL
2448 APIENTRY
2449 NtGdiCloseFigure(HDC hDC)
2450 {
2451     BOOL Ret = FALSE; // Default to failure
2452     PDC pDc;
2453     PPATH pPath;
2454 
2455     DPRINT("Enter %s\n", __FUNCTION__);
2456 
2457     pDc = DC_LockDc(hDC);
2458     if (!pDc)
2459     {
2460         EngSetLastError(ERROR_INVALID_PARAMETER);
2461         return FALSE;
2462     }
2463 
2464     pPath = PATH_LockPath(pDc->dclevel.hPath);
2465     if (!pPath)
2466     {
2467         DC_UnlockDc(pDc);
2468         return FALSE;
2469     }
2470 
2471     if (pPath->state == PATH_Open)
2472     {
2473         IntGdiCloseFigure(pPath);
2474         Ret = TRUE;
2475     }
2476     else
2477     {
2478         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2479     }
2480 
2481     PATH_UnlockPath(pPath);
2482     DC_UnlockDc(pDc);
2483     return Ret;
2484 }
2485 
2486 BOOL
2487 APIENTRY
2488 NtGdiEndPath(HDC  hDC)
2489 {
2490     BOOL ret = TRUE;
2491     PPATH pPath;
2492     PDC dc;
2493 
2494     dc = DC_LockDc(hDC);
2495     if (!dc)
2496     {
2497         EngSetLastError(ERROR_INVALID_HANDLE);
2498         return FALSE;
2499     }
2500 
2501     pPath = PATH_LockPath(dc->dclevel.hPath);
2502     if (!pPath)
2503     {
2504         DC_UnlockDc(dc);
2505         return FALSE;
2506     }
2507 
2508     /* Check that path is currently being constructed */
2509     if ((pPath->state != PATH_Open) || !(dc->dclevel.flPath & DCPATH_ACTIVE))
2510     {
2511         DPRINT("EndPath ERROR! 0x%p\n", dc->dclevel.hPath);
2512         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2513         ret = FALSE;
2514     }
2515     /* Set flag to indicate that path is finished */
2516     else
2517     {
2518         DPRINT("EndPath 0x%p\n", dc->dclevel.hPath);
2519         pPath->state = PATH_Closed;
2520         dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2521     }
2522 
2523     PATH_UnlockPath(pPath);
2524     DC_UnlockDc(dc);
2525     return ret;
2526 }
2527 
2528 BOOL
2529 APIENTRY
2530 NtGdiFillPath(HDC  hDC)
2531 {
2532     BOOL ret = FALSE;
2533     PPATH pPath, pNewPath;
2534     PDC_ATTR pdcattr;
2535     PDC dc;
2536 
2537     dc = DC_LockDc(hDC);
2538     if (!dc)
2539     {
2540         EngSetLastError(ERROR_INVALID_PARAMETER);
2541         return FALSE;
2542     }
2543 
2544     pPath = PATH_LockPath(dc->dclevel.hPath);
2545     if (!pPath)
2546     {
2547         DC_UnlockDc(dc);
2548         return FALSE;
2549     }
2550 
2551     DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL);
2552 
2553     pdcattr = dc->pdcattr;
2554 
2555     if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2556         DC_vUpdateLineBrush(dc);
2557 
2558     if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
2559         DC_vUpdateFillBrush(dc);
2560 
2561     pNewPath = PATH_FlattenPath(pPath);
2562 
2563     if (pNewPath->state != PATH_Closed)
2564     {
2565         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2566     }
2567     else if (pNewPath->numEntriesUsed)
2568     {
2569        ret = PATH_FillPath(dc, pNewPath);
2570     }
2571     else ret = TRUE;
2572 
2573     PATH_UnlockPath(pNewPath);
2574     PATH_Delete(pNewPath->BaseObject.hHmgr);
2575 
2576     PATH_UnlockPath(pPath);
2577     PATH_Delete(pPath->BaseObject.hHmgr);
2578     dc->dclevel.hPath = 0;
2579     dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2580 
2581     DC_vFinishBlit(dc, NULL);
2582     DC_UnlockDc(dc);
2583     return ret;
2584 }
2585 
2586 BOOL
2587 APIENTRY
2588 NtGdiFlattenPath(HDC hDC)
2589 {
2590     BOOL Ret = FALSE;
2591     DC *pDc;
2592     PPATH pPath, pNewPath = NULL;
2593 
2594     DPRINT("Enter %s\n", __FUNCTION__);
2595 
2596     pDc = DC_LockDc(hDC);
2597     if (!pDc)
2598     {
2599         EngSetLastError(ERROR_INVALID_HANDLE);
2600         return FALSE;
2601     }
2602 
2603     pPath = PATH_LockPath(pDc->dclevel.hPath);
2604     if (!pPath)
2605     {
2606         EngSetLastError( ERROR_CAN_NOT_COMPLETE );
2607         DC_UnlockDc(pDc);
2608         return FALSE;
2609     }
2610 
2611     if (pPath->state == PATH_Closed)
2612     {
2613         pNewPath = PATH_FlattenPath(pPath);
2614     }
2615 
2616     PATH_UnlockPath(pPath);
2617 
2618     if (pNewPath)
2619     {
2620        PATH_Delete(pDc->dclevel.hPath);
2621        pDc->dclevel.hPath = pNewPath->BaseObject.hHmgr;
2622        PATH_UnlockPath(pNewPath);
2623        Ret = TRUE;
2624     }
2625 
2626     DC_UnlockDc(pDc);
2627     return Ret;
2628 }
2629 
2630 _Success_(return != FALSE)
2631 BOOL
2632 APIENTRY
2633 NtGdiGetMiterLimit(
2634     _In_ HDC hdc,
2635     _Out_ PDWORD pdwOut)
2636 {
2637     DC *pDc;
2638     BOOL bResult = TRUE;
2639 
2640     if (!(pDc = DC_LockDc(hdc)))
2641     {
2642         EngSetLastError(ERROR_INVALID_PARAMETER);
2643         return FALSE;
2644     }
2645 
2646     _SEH2_TRY
2647     {
2648         ProbeForWrite(pdwOut, sizeof(DWORD), 1);
2649         *pdwOut = pDc->dclevel.laPath.eMiterLimit;
2650     }
2651     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2652     {
2653         SetLastNtError(_SEH2_GetExceptionCode());
2654         bResult = FALSE;
2655     }
2656     _SEH2_END;
2657 
2658     DC_UnlockDc(pDc);
2659     return bResult;
2660 
2661 }
2662 
2663 INT
2664 APIENTRY
2665 NtGdiGetPath(
2666     HDC hDC,
2667     LPPOINT Points,
2668     LPBYTE Types,
2669     INT nSize)
2670 {
2671     INT ret = -1;
2672     PPATH pPath;
2673 
2674     DC *dc = DC_LockDc(hDC);
2675     DPRINT("NtGdiGetPath start\n");
2676     if (!dc)
2677     {
2678         DPRINT1("Can't lock dc!\n");
2679         EngSetLastError(ERROR_INVALID_PARAMETER);
2680         return -1;
2681     }
2682 
2683     pPath = PATH_LockPath(dc->dclevel.hPath);
2684     if (!pPath)
2685     {
2686         DC_UnlockDc(dc);
2687         return -1;
2688     }
2689 
2690     if (pPath->state != PATH_Closed)
2691     {
2692         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2693         goto done;
2694     }
2695 
2696     if (nSize == 0)
2697     {
2698         ret = pPath->numEntriesUsed;
2699     }
2700     else if (nSize < pPath->numEntriesUsed)
2701     {
2702         EngSetLastError(ERROR_INVALID_PARAMETER);
2703         goto done;
2704     }
2705     else
2706     {
2707         _SEH2_TRY
2708         {
2709             memcpy(Points, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
2710             memcpy(Types, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
2711 
2712             /* Convert the points to logical coordinates */
2713             if (!GdiPathDPtoLP(dc, Points, pPath->numEntriesUsed))
2714             {
2715                 EngSetLastError(ERROR_ARITHMETIC_OVERFLOW);
2716                 _SEH2_LEAVE;
2717             }
2718 
2719             ret = pPath->numEntriesUsed;
2720         }
2721         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2722         {
2723             SetLastNtError(_SEH2_GetExceptionCode());
2724         }
2725         _SEH2_END
2726     }
2727 
2728 done:
2729     DPRINT("NtGdiGetPath exit %d\n",ret);
2730     PATH_UnlockPath(pPath);
2731     DC_UnlockDc(dc);
2732     return ret;
2733 }
2734 
2735 HRGN
2736 APIENTRY
2737 NtGdiPathToRegion(HDC  hDC)
2738 {
2739     PPATH pPath, pNewPath;
2740     HRGN  hrgnRval = 0;
2741     int Ret;
2742     PREGION Rgn;
2743     DC *pDc;
2744     PDC_ATTR pdcattr;
2745 
2746     DPRINT("Enter %s\n", __FUNCTION__);
2747 
2748     pDc = DC_LockDc(hDC);
2749     if (!pDc)
2750     {
2751         DPRINT("Failed to lock DC %p\n", hDC);
2752         EngSetLastError(ERROR_INVALID_PARAMETER);
2753         return NULL;
2754     }
2755 
2756     pdcattr = pDc->pdcattr;
2757 
2758     pPath = PATH_LockPath(pDc->dclevel.hPath);
2759     if (!pPath)
2760     {
2761         DPRINT("Failed to lock DC path %p\n", pDc->dclevel.hPath);
2762         DC_UnlockDc(pDc);
2763         return NULL;
2764     }
2765 
2766     if (pPath->state != PATH_Closed)
2767     {
2768         // FIXME: Check that setlasterror is being called correctly
2769         DPRINT("Path is not closed!\n");
2770         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2771     }
2772     else
2773     {
2774         /* Create the region and fill it with the path strokes */
2775         Rgn = REGION_AllocUserRgnWithHandle(1);
2776         if (!Rgn)
2777         {
2778             DPRINT("Failed to allocate a region\n");
2779             PATH_UnlockPath(pPath);
2780             DC_UnlockDc(pDc);
2781             return NULL;
2782         }
2783         hrgnRval = Rgn->BaseObject.hHmgr;
2784 
2785         pNewPath = PATH_FlattenPath(pPath);
2786 
2787         Ret = PATH_PathToRegion(pNewPath, pdcattr->jFillMode, Rgn);
2788 
2789         PATH_UnlockPath(pNewPath);
2790         PATH_Delete(pNewPath->BaseObject.hHmgr);
2791 
2792         if (!Ret)
2793         {
2794             DPRINT("PATH_PathToRegion failed\n");
2795             REGION_Delete(Rgn);
2796             hrgnRval = NULL;
2797         }
2798         else
2799             REGION_UnlockRgn(Rgn);
2800     }
2801 
2802     PATH_UnlockPath(pPath);
2803     PATH_Delete(pDc->dclevel.hPath);
2804     pDc->dclevel.hPath = NULL;
2805     pDc->dclevel.flPath &= ~DCPATH_ACTIVE;
2806 
2807     DC_UnlockDc(pDc);
2808     return hrgnRval;
2809 }
2810 
2811 BOOL
2812 APIENTRY
2813 NtGdiSetMiterLimit(
2814     IN HDC hdc,
2815     IN DWORD dwNew,
2816     IN OUT OPTIONAL PDWORD pdwOut)
2817 {
2818     DC *pDc;
2819     gxf_long worker, worker1;
2820     BOOL bResult = TRUE;
2821 
2822     if (!(pDc = DC_LockDc(hdc)))
2823     {
2824         EngSetLastError(ERROR_INVALID_PARAMETER);
2825         return FALSE;
2826     }
2827 
2828     worker.l  = dwNew;
2829     worker1.f = pDc->dclevel.laPath.eMiterLimit;
2830     pDc->dclevel.laPath.eMiterLimit = worker.f;
2831 
2832     if (pdwOut)
2833     {
2834         _SEH2_TRY
2835         {
2836             ProbeForWrite(pdwOut, sizeof(DWORD), 1);
2837             *pdwOut = worker1.l;
2838         }
2839         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2840         {
2841             SetLastNtError(_SEH2_GetExceptionCode());
2842             bResult = FALSE;
2843         }
2844         _SEH2_END;
2845     }
2846 
2847     DC_UnlockDc(pDc);
2848     return bResult;
2849 }
2850 
2851 BOOL
2852 APIENTRY
2853 NtGdiStrokeAndFillPath(HDC hDC)
2854 {
2855     DC *pDc;
2856     PDC_ATTR pdcattr;
2857     PPATH pPath, pNewPath;
2858     BOOL bRet = FALSE;
2859 
2860     DPRINT("Enter %s\n", __FUNCTION__);
2861 
2862     if (!(pDc = DC_LockDc(hDC)))
2863     {
2864         EngSetLastError(ERROR_INVALID_PARAMETER);
2865         return FALSE;
2866     }
2867     pPath = PATH_LockPath(pDc->dclevel.hPath);
2868     if (!pPath)
2869     {
2870         DC_UnlockDc(pDc);
2871         return FALSE;
2872     }
2873 
2874     DC_vPrepareDCsForBlit(pDc, NULL, NULL, NULL);
2875 
2876     pdcattr = pDc->pdcattr;
2877 
2878     if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
2879         DC_vUpdateFillBrush(pDc);
2880 
2881     if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2882         DC_vUpdateLineBrush(pDc);
2883 
2884     pNewPath = PATH_FlattenPath(pPath);
2885 
2886     if (pNewPath->state != PATH_Closed)
2887     {
2888         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2889     }
2890     else if (pNewPath->numEntriesUsed)
2891     {
2892        bRet = PATH_FillPath(pDc, pNewPath);
2893        if (bRet) bRet = PATH_StrokePath(pDc, pNewPath);
2894     }
2895     else bRet = TRUE;
2896 
2897     PATH_UnlockPath(pNewPath);
2898     PATH_Delete(pNewPath->BaseObject.hHmgr);
2899 
2900     PATH_UnlockPath(pPath);
2901     PATH_Delete(pPath->BaseObject.hHmgr);
2902     pDc->dclevel.hPath = 0;
2903     pDc->dclevel.flPath &= ~DCPATH_ACTIVE;
2904 
2905     DC_vFinishBlit(pDc, NULL);
2906     DC_UnlockDc(pDc);
2907     return bRet;
2908 }
2909 
2910 BOOL
2911 APIENTRY
2912 NtGdiStrokePath(HDC hDC)
2913 {
2914     DC *pDc;
2915     PDC_ATTR pdcattr;
2916     PPATH pPath, pNewPath;
2917     BOOL bRet = FALSE;
2918 
2919     DPRINT("Enter %s\n", __FUNCTION__);
2920 
2921     if (!(pDc = DC_LockDc(hDC)))
2922     {
2923         EngSetLastError(ERROR_INVALID_PARAMETER);
2924         return FALSE;
2925     }
2926 
2927     pPath = PATH_LockPath(pDc->dclevel.hPath);
2928     if (!pPath)
2929     {
2930         DC_UnlockDc(pDc);
2931         return FALSE;
2932     }
2933 
2934     DC_vPrepareDCsForBlit(pDc, NULL, NULL, NULL);
2935 
2936     pdcattr = pDc->pdcattr;
2937 
2938     if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2939         DC_vUpdateLineBrush(pDc);
2940 
2941     pNewPath = PATH_FlattenPath(pPath);
2942 
2943     if (pNewPath->state != PATH_Closed)
2944     {
2945         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2946     }
2947     else bRet = PATH_StrokePath(pDc, pNewPath);
2948 
2949     PATH_UnlockPath(pNewPath);
2950     PATH_Delete(pNewPath->BaseObject.hHmgr);
2951 
2952     DC_vFinishBlit(pDc, NULL);
2953 
2954     PATH_UnlockPath(pPath);
2955     PATH_Delete(pPath->BaseObject.hHmgr);
2956     pDc->dclevel.hPath = 0;
2957     pDc->dclevel.flPath &= ~DCPATH_ACTIVE;
2958 
2959     DC_UnlockDc(pDc);
2960     return bRet;
2961 }
2962 
2963 BOOL
2964 APIENTRY
2965 NtGdiWidenPath(HDC  hDC)
2966 {
2967     PPATH pPath;
2968     BOOL Ret = FALSE;
2969     PDC pdc = DC_LockDc(hDC);
2970     DPRINT("NtGdiWidenPat Enter\n");
2971     if (!pdc)
2972     {
2973         EngSetLastError(ERROR_INVALID_PARAMETER);
2974         return FALSE;
2975     }
2976 
2977     pPath = PATH_WidenPath(pdc);
2978     if (pPath)
2979     {
2980        DPRINT("WindenPath New Path\n");
2981        PATH_Delete(pdc->dclevel.hPath);
2982        pdc->dclevel.hPath = pPath->BaseObject.hHmgr;
2983        Ret = TRUE;
2984     }
2985     DC_UnlockDc(pdc);
2986     DPRINT("NtGdiWidenPat Ret %d\n",Ret);
2987     return Ret;
2988 }
2989 
2990 /* EOF */
2991