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