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