xref: /reactos/win32ss/gdi/ntgdi/path.c (revision 26b5596a)
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 i = 0;
1604     INT nLinePts, nAlloc;
1605     POINT *pLinePts = NULL;
1606     POINT ptViewportOrg, ptWindowOrg;
1607     SIZE szViewportExt, szWindowExt;
1608     DWORD mapMode, graphicsMode;
1609     XFORM xform;
1610     PDC_ATTR pdcattr = dc->pdcattr;
1611 
1612     TRACE("Enter %s\n", __FUNCTION__);
1613 
1614     /* Save the mapping mode info */
1615     mapMode = pdcattr->iMapMode;
1616 
1617     szViewportExt = *DC_pszlViewportExt(dc);
1618     ptViewportOrg = dc->pdcattr->ptlViewportOrg;
1619     szWindowExt = dc->pdcattr->szlWindowExt;
1620     ptWindowOrg = dc->pdcattr->ptlWindowOrg;
1621 
1622     MatrixS2XForm(&xform, &dc->pdcattr->mxWorldToPage);
1623 
1624     /* Set MM_TEXT */
1625     pdcattr->iMapMode = MM_TEXT;
1626     pdcattr->ptlViewportOrg.x = 0;
1627     pdcattr->ptlViewportOrg.y = 0;
1628     pdcattr->ptlWindowOrg.x = 0;
1629     pdcattr->ptlWindowOrg.y = 0;
1630     graphicsMode = pdcattr->iGraphicsMode;
1631     pdcattr->iGraphicsMode = GM_ADVANCED;
1632     GreModifyWorldTransform(dc, (XFORML*)&xform, MWT_IDENTITY);
1633     pdcattr->iGraphicsMode = graphicsMode;
1634 
1635     /* Allocate enough memory for the worst case without beziers (one PT_MOVETO
1636      * and the rest PT_LINETO with PT_CLOSEFIGURE at the end) plus some buffer
1637      * space in case we get one to keep the number of reallocations small. */
1638     nAlloc = pPath->numEntriesUsed + 1 + 300;
1639     pLinePts = ExAllocatePoolWithTag(PagedPool, nAlloc * sizeof(POINT), TAG_PATH);
1640     if (!pLinePts)
1641     {
1642         ERR("Can't allocate pool!\n");
1643         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1644         goto end;
1645     }
1646     nLinePts = 0;
1647 
1648     for (i = 0; i < pPath->numEntriesUsed; i++)
1649     {
1650         if ((i == 0 || (pPath->pFlags[i - 1] & PT_CLOSEFIGURE))
1651                 && (pPath->pFlags[i] != PT_MOVETO))
1652         {
1653             ERR("Expected PT_MOVETO %s, got path flag %d\n",
1654                     i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
1655                     (INT)pPath->pFlags[i]);
1656             goto end;
1657         }
1658 
1659         switch(pPath->pFlags[i])
1660         {
1661             case PT_MOVETO:
1662                 TRACE("Got PT_MOVETO (%ld, %ld)\n",
1663                        pPath->pPoints[i].x, pPath->pPoints[i].y);
1664                 if (nLinePts >= 2) IntGdiPolyline(dc, pLinePts, nLinePts);
1665                 nLinePts = 0;
1666                 pLinePts[nLinePts++] = pPath->pPoints[i];
1667                 break;
1668             case PT_LINETO:
1669             case (PT_LINETO | PT_CLOSEFIGURE):
1670                 TRACE("Got PT_LINETO (%ld, %ld)\n",
1671                        pPath->pPoints[i].x, pPath->pPoints[i].y);
1672                 pLinePts[nLinePts++] = pPath->pPoints[i];
1673                 break;
1674             case PT_BEZIERTO:
1675                 TRACE("Got PT_BEZIERTO\n");
1676                 if (pPath->pFlags[i + 1] != PT_BEZIERTO ||
1677                         (pPath->pFlags[i + 2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO)
1678                 {
1679                     ERR("Path didn't contain 3 successive PT_BEZIERTOs\n");
1680                     ret = FALSE;
1681                     goto end;
1682                 }
1683                 else
1684                 {
1685                     INT nBzrPts, nMinAlloc;
1686                     POINT *pBzrPts = GDI_Bezier(&pPath->pPoints[i - 1], 4, &nBzrPts);
1687                     /* Make sure we have allocated enough memory for the lines of
1688                      * this bezier and the rest of the path, assuming we won't get
1689                      * another one (since we won't reallocate again then). */
1690                     nMinAlloc = nLinePts + (pPath->numEntriesUsed - i) + nBzrPts;
1691                     if (nAlloc < nMinAlloc)
1692                     {
1693                         // Reallocate memory
1694 
1695                         POINT *Realloc = NULL;
1696                         nAlloc = nMinAlloc * 2;
1697 
1698                         Realloc = ExAllocatePoolWithTag(PagedPool,
1699                                                         nAlloc * sizeof(POINT),
1700                                                         TAG_PATH);
1701 
1702                         if (!Realloc)
1703                         {
1704                             ERR("Can't allocate pool!\n");
1705                             ExFreePoolWithTag(pBzrPts, TAG_BEZIER);
1706                             goto end;
1707                         }
1708 
1709                         memcpy(Realloc, pLinePts, nLinePts * sizeof(POINT));
1710                         ExFreePoolWithTag(pLinePts, TAG_PATH);
1711                         pLinePts = Realloc;
1712                     }
1713                     memcpy(&pLinePts[nLinePts], &pBzrPts[1], (nBzrPts - 1) * sizeof(POINT));
1714                     nLinePts += nBzrPts - 1;
1715                     ExFreePoolWithTag(pBzrPts, TAG_BEZIER);
1716                     i += 2;
1717                 }
1718                 break;
1719             default:
1720                 ERR("Got path flag %d (not supported)\n", (INT)pPath->pFlags[i]);
1721                 goto end;
1722         }
1723 
1724         if (pPath->pFlags[i] & PT_CLOSEFIGURE)
1725         {
1726             pLinePts[nLinePts++] = pLinePts[0];
1727         }
1728     }
1729     if (nLinePts >= 2)
1730         IntGdiPolyline(dc, pLinePts, nLinePts);
1731 
1732     ret = TRUE;
1733 
1734 end:
1735     if (pLinePts) ExFreePoolWithTag(pLinePts, TAG_PATH);
1736 
1737     /* Restore the old mapping mode */
1738     pdcattr->iMapMode =  mapMode;
1739     pdcattr->szlWindowExt.cx = szWindowExt.cx;
1740     pdcattr->szlWindowExt.cy = szWindowExt.cy;
1741     pdcattr->ptlWindowOrg.x = ptWindowOrg.x;
1742     pdcattr->ptlWindowOrg.y = ptWindowOrg.y;
1743 
1744     pdcattr->szlViewportExt.cx = szViewportExt.cx;
1745     pdcattr->szlViewportExt.cy = szViewportExt.cy;
1746     pdcattr->ptlViewportOrg.x = ptViewportOrg.x;
1747     pdcattr->ptlViewportOrg.y = ptViewportOrg.y;
1748 
1749     /* Restore the world transform */
1750     XForm2MatrixS(&dc->pdcattr->mxWorldToPage, &xform);
1751 
1752     /* If we've moved the current point then get its new position
1753        which will be in device (MM_TEXT) co-ords, convert it to
1754        logical co-ords and re-set it.  This basically updates
1755        dc->CurPosX|Y so that their values are in the correct mapping
1756        mode.
1757     */
1758     if (i > 0)
1759     {
1760         POINT pt;
1761         IntGetCurrentPositionEx(dc, &pt);
1762         IntDPtoLP(dc, &pt, 1);
1763         IntGdiMoveToEx(dc, pt.x, pt.y, NULL);
1764     }
1765     TRACE("Leave %s, ret=%d\n", __FUNCTION__, ret);
1766     return ret;
1767 }
1768 
1769 #define round(x) ((int)((x)>0?(x)+0.5:(x)-0.5))
1770 
1771 PPATH FASTCALL
1772 IntGdiWidenPath(PPATH pPath, UINT penWidth, UINT penStyle, FLOAT eMiterLimit)
1773 {
1774     INT i, j, numStrokes, numOldStrokes, penWidthIn, penWidthOut;
1775     PPATH flat_path, pNewPath, *pStrokes = NULL, *pOldStrokes, pUpPath, pDownPath;
1776     BYTE *type;
1777     DWORD joint, endcap;
1778 
1779     endcap = (PS_ENDCAP_MASK & penStyle);
1780     joint = (PS_JOIN_MASK & penStyle);
1781 
1782     if (!(flat_path = PATH_FlattenPath(pPath)))
1783     {
1784         ERR("PATH_FlattenPath\n");
1785         return NULL;
1786     }
1787 
1788     penWidthIn = penWidth / 2;
1789     penWidthOut = penWidth / 2;
1790     if (penWidthIn + penWidthOut < penWidth)
1791         penWidthOut++;
1792 
1793     numStrokes = 0;
1794 
1795     for (i = 0, j = 0; i < flat_path->numEntriesUsed; i++, j++)
1796     {
1797         POINT point;
1798         if ((i == 0 || (flat_path->pFlags[i - 1] & PT_CLOSEFIGURE)) &&
1799                 (flat_path->pFlags[i] != PT_MOVETO))
1800         {
1801             ERR("Expected PT_MOVETO %s, got path flag %c\n",
1802                     i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
1803                     flat_path->pFlags[i]);
1804             if (pStrokes)
1805                 ExFreePoolWithTag(pStrokes, TAG_PATH);
1806             PATH_UnlockPath(flat_path);
1807             PATH_Delete(flat_path->BaseObject.hHmgr);
1808             return NULL;
1809         }
1810         switch(flat_path->pFlags[i])
1811         {
1812             case PT_MOVETO:
1813                 if (numStrokes > 0)
1814                 {
1815                     pStrokes[numStrokes - 1]->state = PATH_Closed;
1816                 }
1817                 numOldStrokes = numStrokes;
1818                 numStrokes++;
1819                 j = 0;
1820                 if (numStrokes == 1)
1821                     pStrokes = ExAllocatePoolWithTag(PagedPool, sizeof(*pStrokes), TAG_PATH);
1822                 else
1823                 {
1824                     pOldStrokes = pStrokes; // Save old pointer.
1825                     pStrokes = ExAllocatePoolWithTag(PagedPool, numStrokes * sizeof(*pStrokes), TAG_PATH);
1826                     if (!pStrokes)
1827                     {
1828                        ExFreePoolWithTag(pOldStrokes, TAG_PATH);
1829                        PATH_UnlockPath(flat_path);
1830                        PATH_Delete(flat_path->BaseObject.hHmgr);
1831                        return NULL;
1832                     }
1833                     RtlCopyMemory(pStrokes, pOldStrokes, numOldStrokes * sizeof(PPATH));
1834                     ExFreePoolWithTag(pOldStrokes, TAG_PATH); // Free old pointer.
1835                 }
1836                 if (!pStrokes)
1837                 {
1838                    PATH_UnlockPath(flat_path);
1839                    PATH_Delete(flat_path->BaseObject.hHmgr);
1840                    return NULL;
1841                 }
1842                 pStrokes[numStrokes - 1] = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1843                 if (!pStrokes[numStrokes - 1])
1844                 {
1845                     ASSERT(FALSE); // FIXME
1846                 }
1847                 PATH_InitGdiPath(pStrokes[numStrokes - 1]);
1848                 pStrokes[numStrokes - 1]->state = PATH_Open;
1849             case PT_LINETO:
1850             case (PT_LINETO | PT_CLOSEFIGURE):
1851                 point.x = flat_path->pPoints[i].x;
1852                 point.y = flat_path->pPoints[i].y;
1853                 PATH_AddEntry(pStrokes[numStrokes - 1], &point, flat_path->pFlags[i]);
1854                 break;
1855             case PT_BEZIERTO:
1856                 /* Should never happen because of the FlattenPath call */
1857                 ERR("Should never happen\n");
1858                 break;
1859             default:
1860                 ERR("Got path flag %c\n", flat_path->pFlags[i]);
1861                 if (pStrokes)
1862                     ExFreePoolWithTag(pStrokes, TAG_PATH);
1863                 PATH_UnlockPath(flat_path);
1864                 PATH_Delete(flat_path->BaseObject.hHmgr);
1865                 return NULL;
1866         }
1867     }
1868 
1869     pNewPath = PATH_CreatePath( flat_path->numEntriesUsed );
1870 
1871     for (i = 0; i < numStrokes; i++)
1872     {
1873         pUpPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1874         PATH_InitGdiPath(pUpPath);
1875         pUpPath->state = PATH_Open;
1876         pDownPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1877         PATH_InitGdiPath(pDownPath);
1878         pDownPath->state = PATH_Open;
1879 
1880         for (j = 0; j < pStrokes[i]->numEntriesUsed; j++)
1881         {
1882             /* Beginning or end of the path if not closed */
1883             if ((!(pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE)) && (j == 0 || j == pStrokes[i]->numEntriesUsed - 1))
1884             {
1885                 /* Compute segment angle */
1886                 INT xo, yo, xa, ya;
1887                 double theta;
1888                 POINT pt;
1889                 POINT corners[2];
1890                 if (j == 0)
1891                 {
1892                     xo = pStrokes[i]->pPoints[j].x;
1893                     yo = pStrokes[i]->pPoints[j].y;
1894                     xa = pStrokes[i]->pPoints[1].x;
1895                     ya = pStrokes[i]->pPoints[1].y;
1896                 }
1897                 else
1898                 {
1899                     xa = pStrokes[i]->pPoints[j - 1].x;
1900                     ya = pStrokes[i]->pPoints[j - 1].y;
1901                     xo = pStrokes[i]->pPoints[j].x;
1902                     yo = pStrokes[i]->pPoints[j].y;
1903                 }
1904                 theta = atan2(ya - yo, xa - xo);
1905                 switch(endcap)
1906                 {
1907                     case PS_ENDCAP_SQUARE :
1908                         pt.x = xo + round(sqrt(2) * penWidthOut * cos(M_PI_4 + theta));
1909                         pt.y = yo + round(sqrt(2) * penWidthOut * sin(M_PI_4 + theta));
1910                         PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
1911                         pt.x = xo + round(sqrt(2) * penWidthIn * cos(- M_PI_4 + theta));
1912                         pt.y = yo + round(sqrt(2) * penWidthIn * sin(- M_PI_4 + theta));
1913                         PATH_AddEntry(pUpPath, &pt, PT_LINETO);
1914                         break;
1915                     case PS_ENDCAP_FLAT :
1916                         pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
1917                         pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
1918                         PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
1919                         pt.x = xo - round(penWidthIn * cos(theta + M_PI_2));
1920                         pt.y = yo - round(penWidthIn * sin(theta + M_PI_2));
1921                         PATH_AddEntry(pUpPath, &pt, PT_LINETO);
1922                         break;
1923                     case PS_ENDCAP_ROUND :
1924                     default :
1925                         corners[0].x = xo - penWidthIn;
1926                         corners[0].y = yo - penWidthIn;
1927                         corners[1].x = xo + penWidthOut;
1928                         corners[1].y = yo + penWidthOut;
1929                         PATH_DoArcPart(pUpPath , corners, theta + M_PI_2 , theta + 3 * M_PI_4, (j == 0 ? PT_MOVETO : FALSE));
1930                         PATH_DoArcPart(pUpPath , corners, theta + 3 * M_PI_4 , theta + M_PI, FALSE);
1931                         PATH_DoArcPart(pUpPath , corners, theta + M_PI, theta +  5 * M_PI_4, FALSE);
1932                         PATH_DoArcPart(pUpPath , corners, theta + 5 * M_PI_4 , theta + 3 * M_PI_2, FALSE);
1933                         break;
1934                 }
1935             }
1936             /* Corpse of the path */
1937             else
1938             {
1939                 /* Compute angle */
1940                 INT previous, next;
1941                 double xa, ya, xb, yb, xo, yo;
1942                 double alpha, theta, miterWidth;
1943                 DWORD _joint = joint;
1944                 POINT pt;
1945                 PPATH pInsidePath, pOutsidePath;
1946                 if (j > 0 && j < pStrokes[i]->numEntriesUsed - 1)
1947                 {
1948                     previous = j - 1;
1949                     next = j + 1;
1950                 }
1951                 else if (j == 0)
1952                 {
1953                     previous = pStrokes[i]->numEntriesUsed - 1;
1954                     next = j + 1;
1955                 }
1956                 else
1957                 {
1958                     previous = j - 1;
1959                     next = 0;
1960                 }
1961                 xo = pStrokes[i]->pPoints[j].x;
1962                 yo = pStrokes[i]->pPoints[j].y;
1963                 xa = pStrokes[i]->pPoints[previous].x;
1964                 ya = pStrokes[i]->pPoints[previous].y;
1965                 xb = pStrokes[i]->pPoints[next].x;
1966                 yb = pStrokes[i]->pPoints[next].y;
1967                 theta = atan2(yo - ya, xo - xa);
1968                 alpha = atan2(yb - yo, xb - xo) - theta;
1969                 if (alpha > 0) alpha -= M_PI;
1970                 else alpha += M_PI;
1971                 if (_joint == PS_JOIN_MITER && eMiterLimit < fabs(1 / sin(alpha / 2)))
1972                 {
1973                     _joint = PS_JOIN_BEVEL;
1974                 }
1975                 if (alpha > 0)
1976                 {
1977                     pInsidePath = pUpPath;
1978                     pOutsidePath = pDownPath;
1979                 }
1980                 else if (alpha < 0)
1981                 {
1982                     pInsidePath = pDownPath;
1983                     pOutsidePath = pUpPath;
1984                 }
1985                 else
1986                 {
1987                     continue;
1988                 }
1989                 /* Inside angle points */
1990                 if (alpha > 0)
1991                 {
1992                     pt.x = xo - round(penWidthIn * cos(theta + M_PI_2));
1993                     pt.y = yo - round(penWidthIn * sin(theta + M_PI_2));
1994                 }
1995                 else
1996                 {
1997                     pt.x = xo + round(penWidthIn * cos(theta + M_PI_2));
1998                     pt.y = yo + round(penWidthIn * sin(theta + M_PI_2));
1999                 }
2000                 PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
2001                 if (alpha > 0)
2002                 {
2003                     pt.x = xo + round(penWidthIn * cos(M_PI_2 + alpha + theta));
2004                     pt.y = yo + round(penWidthIn * sin(M_PI_2 + alpha + theta));
2005                 }
2006                 else
2007                 {
2008                     pt.x = xo - round(penWidthIn * cos(M_PI_2 + alpha + theta));
2009                     pt.y = yo - round(penWidthIn * sin(M_PI_2 + alpha + theta));
2010                 }
2011                 PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
2012                 /* Outside angle point */
2013                 switch(_joint)
2014                 {
2015                     case PS_JOIN_MITER :
2016                         miterWidth = fabs(penWidthOut / cos(M_PI_2 - fabs(alpha) / 2));
2017                         pt.x = xo + round(miterWidth * cos(theta + alpha / 2));
2018                         pt.y = yo + round(miterWidth * sin(theta + alpha / 2));
2019                         PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2020                         break;
2021                     case PS_JOIN_BEVEL :
2022                         if (alpha > 0)
2023                         {
2024                             pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
2025                             pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
2026                         }
2027                         else
2028                         {
2029                             pt.x = xo - round(penWidthOut * cos(theta + M_PI_2));
2030                             pt.y = yo - round(penWidthOut * sin(theta + M_PI_2));
2031                         }
2032                         PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2033                         if (alpha > 0)
2034                         {
2035                             pt.x = xo - round(penWidthOut * cos(M_PI_2 + alpha + theta));
2036                             pt.y = yo - round(penWidthOut * sin(M_PI_2 + alpha + theta));
2037                         }
2038                         else
2039                         {
2040                             pt.x = xo + round(penWidthOut * cos(M_PI_2 + alpha + theta));
2041                             pt.y = yo + round(penWidthOut * sin(M_PI_2 + alpha + theta));
2042                         }
2043                         PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2044                         break;
2045                     case PS_JOIN_ROUND :
2046                     default :
2047                         if (alpha > 0)
2048                         {
2049                             pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
2050                             pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
2051                         }
2052                         else
2053                         {
2054                             pt.x = xo - round(penWidthOut * cos(theta + M_PI_2));
2055                             pt.y = yo - round(penWidthOut * sin(theta + M_PI_2));
2056                         }
2057                         PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2058                         pt.x = xo + round(penWidthOut * cos(theta + alpha / 2));
2059                         pt.y = yo + round(penWidthOut * sin(theta + alpha / 2));
2060                         PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2061                         if (alpha > 0)
2062                         {
2063                             pt.x = xo - round(penWidthOut * cos(M_PI_2 + alpha + theta));
2064                             pt.y = yo - round(penWidthOut * sin(M_PI_2 + alpha + theta));
2065                         }
2066                         else
2067                         {
2068                             pt.x = xo + round(penWidthOut * cos(M_PI_2 + alpha + theta));
2069                             pt.y = yo + round(penWidthOut * sin(M_PI_2 + alpha + theta));
2070                         }
2071                         PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2072                         break;
2073                 }
2074             }
2075         }
2076         type = add_points( pNewPath, pUpPath->pPoints, pUpPath->numEntriesUsed, PT_LINETO );
2077         type[0] = PT_MOVETO;
2078         reverse_points( pDownPath->pPoints, pDownPath->numEntriesUsed );
2079         type = add_points( pNewPath, pDownPath->pPoints, pDownPath->numEntriesUsed, PT_LINETO );
2080         if (pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE) type[0] = PT_MOVETO;
2081 
2082         PATH_DestroyGdiPath(pStrokes[i]);
2083         ExFreePoolWithTag(pStrokes[i], TAG_PATH);
2084         PATH_DestroyGdiPath(pUpPath);
2085         ExFreePoolWithTag(pUpPath, TAG_PATH);
2086         PATH_DestroyGdiPath(pDownPath);
2087         ExFreePoolWithTag(pDownPath, TAG_PATH);
2088     }
2089     if (pStrokes) ExFreePoolWithTag(pStrokes, TAG_PATH);
2090 
2091     PATH_UnlockPath(flat_path);
2092     PATH_Delete(flat_path->BaseObject.hHmgr);
2093     pNewPath->state = PATH_Closed;
2094     PATH_UnlockPath(pNewPath);
2095     return pNewPath;
2096 }
2097 
2098 static
2099 PPATH
2100 FASTCALL
2101 PATH_WidenPath(DC *dc)
2102 {
2103     INT size;
2104     UINT penWidth, penStyle;
2105     DWORD obj_type;
2106     PPATH pPath, pNewPath;
2107     LPEXTLOGPEN elp;
2108     PDC_ATTR pdcattr = dc->pdcattr;
2109 
2110     pPath = PATH_LockPath(dc->dclevel.hPath);
2111     if (!pPath)
2112     {
2113         EngSetLastError( ERROR_CAN_NOT_COMPLETE );
2114         return NULL;
2115     }
2116 
2117     if (pPath->state != PATH_Closed)
2118     {
2119         TRACE("PWP 1\n");
2120         PATH_UnlockPath(pPath);
2121         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2122         return NULL;
2123     }
2124 
2125     size = GreGetObject(pdcattr->hpen, 0, NULL);
2126     if (!size)
2127     {
2128         TRACE("PWP 2\n");
2129         PATH_UnlockPath(pPath);
2130         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2131         return NULL;
2132     }
2133 
2134     elp = ExAllocatePoolWithTag(PagedPool, size, TAG_PATH);
2135     if (elp == NULL)
2136     {
2137         TRACE("PWP 3\n");
2138         PATH_UnlockPath(pPath);
2139         EngSetLastError(ERROR_OUTOFMEMORY);
2140         return NULL;
2141     }
2142 
2143     GreGetObject(pdcattr->hpen, size, elp);
2144 
2145     obj_type = GDI_HANDLE_GET_TYPE(pdcattr->hpen);
2146     if (obj_type == GDI_OBJECT_TYPE_PEN)
2147     {
2148         penStyle = ((LOGPEN*)elp)->lopnStyle;
2149     }
2150     else if (obj_type == GDI_OBJECT_TYPE_EXTPEN)
2151     {
2152         penStyle = elp->elpPenStyle;
2153     }
2154     else
2155     {
2156         TRACE("PWP 4\n");
2157         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2158         ExFreePoolWithTag(elp, TAG_PATH);
2159         PATH_UnlockPath(pPath);
2160         return NULL;
2161     }
2162 
2163     penWidth = elp->elpWidth;
2164     ExFreePoolWithTag(elp, TAG_PATH);
2165 
2166     /* The function cannot apply to cosmetic pens */
2167     if (obj_type == GDI_OBJECT_TYPE_EXTPEN &&
2168         (PS_TYPE_MASK & penStyle) == PS_COSMETIC)
2169     {
2170         TRACE("PWP 5\n");
2171         PATH_UnlockPath(pPath);
2172         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2173         return FALSE;
2174     }
2175 
2176     pNewPath = IntGdiWidenPath(pPath, penWidth, penStyle, dc->dclevel.laPath.eMiterLimit);
2177     PATH_UnlockPath(pPath);
2178     return pNewPath;
2179 }
2180 
2181 static inline INT int_from_fixed(FIXED f)
2182 {
2183     return (f.fract >= 0x8000) ? (f.value + 1) : f.value;
2184 }
2185 
2186 /**********************************************************************
2187  *      PATH_BezierTo
2188  *
2189  * Internally used by PATH_add_outline
2190  */
2191 static
2192 VOID
2193 FASTCALL
2194 PATH_BezierTo(
2195     PPATH pPath,
2196     POINT *lppt,
2197     INT n)
2198 {
2199     if (n < 2) return;
2200 
2201     if (n == 2)
2202     {
2203         PATH_AddEntry(pPath, &lppt[1], PT_LINETO);
2204     }
2205     else if (n == 3)
2206     {
2207         add_points( pPath, lppt, 3, PT_BEZIERTO );
2208     }
2209     else
2210     {
2211         POINT pt[3];
2212         INT i = 0;
2213 
2214         pt[2] = lppt[0];
2215         n--;
2216 
2217         while (n > 2)
2218         {
2219             pt[0] = pt[2];
2220             pt[1] = lppt[i + 1];
2221             pt[2].x = (lppt[i + 2].x + lppt[i + 1].x) / 2;
2222             pt[2].y = (lppt[i + 2].y + lppt[i + 1].y) / 2;
2223             add_points( pPath, pt, 3, PT_BEZIERTO );
2224             n--;
2225             i++;
2226         }
2227 
2228         pt[0] = pt[2];
2229         pt[1] = lppt[i + 1];
2230         pt[2] = lppt[i + 2];
2231         add_points( pPath, pt, 3, PT_BEZIERTO );
2232     }
2233 }
2234 
2235 static
2236 BOOL
2237 FASTCALL
2238 PATH_add_outline(
2239     PDC dc,
2240     PPATH pPath,
2241     INT x,
2242     INT y,
2243     TTPOLYGONHEADER *header,
2244     DWORD size)
2245 {
2246     TTPOLYGONHEADER *start;
2247     POINT pt;
2248     BOOL bResult = FALSE;
2249 
2250     start = header;
2251 
2252     while ((char *)header < (char *)start + size)
2253     {
2254         TTPOLYCURVE *curve;
2255 
2256         if (header->dwType != TT_POLYGON_TYPE)
2257         {
2258             ERR("Unknown header type %lu\n", header->dwType);
2259             goto cleanup;
2260         }
2261 
2262         pt.x = x + int_from_fixed(header->pfxStart.x);
2263         pt.y = y - int_from_fixed(header->pfxStart.y);
2264         PATH_AddEntry(pPath, &pt, PT_MOVETO);
2265 
2266         curve = (TTPOLYCURVE *)(header + 1);
2267 
2268         while ((char *)curve < (char *)header + header->cb)
2269         {
2270             TRACE("curve->wType %d\n", curve->wType);
2271 
2272             switch(curve->wType)
2273             {
2274                 case TT_PRIM_LINE:
2275                 {
2276                     WORD i;
2277 
2278                     for (i = 0; i < curve->cpfx; i++)
2279                     {
2280                         pt.x = x + int_from_fixed(curve->apfx[i].x);
2281                         pt.y = y - int_from_fixed(curve->apfx[i].y);
2282                         PATH_AddEntry(pPath, &pt, PT_LINETO);
2283                     }
2284                     break;
2285                 }
2286 
2287                 case TT_PRIM_QSPLINE:
2288                 case TT_PRIM_CSPLINE:
2289                 {
2290                     WORD i;
2291                     POINTFX ptfx;
2292                     POINT *pts = ExAllocatePoolWithTag(PagedPool, (curve->cpfx + 1) * sizeof(POINT), TAG_PATH);
2293 
2294                     if (!pts) goto cleanup;
2295 
2296                     ptfx = *(POINTFX *)((char *)curve - sizeof(POINTFX));
2297 
2298                     pts[0].x = x + int_from_fixed(ptfx.x);
2299                     pts[0].y = y - int_from_fixed(ptfx.y);
2300 
2301                     for (i = 0; i < curve->cpfx; i++)
2302                     {
2303                         pts[i + 1].x = x + int_from_fixed(curve->apfx[i].x);
2304                         pts[i + 1].y = y - int_from_fixed(curve->apfx[i].y);
2305                     }
2306 
2307                     PATH_BezierTo(pPath, pts, curve->cpfx + 1);
2308 
2309                     ExFreePoolWithTag(pts, TAG_PATH);
2310                     break;
2311                 }
2312 
2313                 default:
2314                     ERR("Unknown curve type %04x\n", curve->wType);
2315                     goto cleanup;
2316             }
2317 
2318             curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
2319         }
2320         header = (TTPOLYGONHEADER *)((char *)header + header->cb);
2321     }
2322 
2323     bResult = TRUE;
2324 
2325 cleanup:
2326     IntGdiCloseFigure(pPath);
2327     return bResult;
2328 }
2329 
2330 /**********************************************************************
2331  *      PATH_ExtTextOut
2332  */
2333 BOOL
2334 FASTCALL
2335 PATH_ExtTextOut(
2336     PDC dc,
2337     INT x,
2338     INT y,
2339     UINT flags,
2340     const RECTL *lprc,
2341     LPCWSTR str,
2342     UINT count,
2343     const INT *dx)
2344 {
2345     PPATH pPath;
2346     unsigned int idx, ggo_flags = GGO_NATIVE;
2347     POINT offset = {0, 0};
2348 
2349     pPath = PATH_LockPath(dc->dclevel.hPath);
2350     if (!pPath)
2351     {
2352         return FALSE;
2353     }
2354 
2355     if (pPath->state != PATH_Open)
2356     {
2357         ERR("PATH_ExtTextOut not open\n");
2358         return FALSE;
2359     }
2360 
2361     if (!count) return TRUE;
2362     if (flags & ETO_GLYPH_INDEX) ggo_flags |= GGO_GLYPH_INDEX;
2363 
2364     for (idx = 0; idx < count; idx++)
2365     {
2366         MAT2 identity = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
2367         GLYPHMETRICS gm;
2368         DWORD dwSize;
2369         void *outline;
2370 
2371         dwSize = ftGdiGetGlyphOutline(dc,
2372                                       str[idx],
2373                                       ggo_flags,
2374                                       &gm,
2375                                       0,
2376                                       NULL,
2377                                       &identity,
2378                                       TRUE);
2379         if (dwSize == GDI_ERROR)
2380         {
2381            PATH_UnlockPath(pPath);
2382            return FALSE;
2383         }
2384 
2385         /* Add outline only if char is printable */
2386         if (dwSize)
2387         {
2388             outline = ExAllocatePoolWithTag(PagedPool, dwSize, TAG_PATH);
2389             if (!outline)
2390             {
2391                PATH_UnlockPath(pPath);
2392                return FALSE;
2393             }
2394 
2395             ftGdiGetGlyphOutline(dc,
2396                                  str[idx],
2397                                  ggo_flags,
2398                                  &gm,
2399                                  dwSize,
2400                                  outline,
2401                                  &identity,
2402                                  TRUE);
2403 
2404             PATH_add_outline(dc, pPath, x + offset.x, y + offset.y, outline, dwSize);
2405 
2406             ExFreePoolWithTag(outline, TAG_PATH);
2407         }
2408 
2409         if (dx)
2410         {
2411             if (flags & ETO_PDY)
2412             {
2413                 offset.x += dx[idx * 2];
2414                 offset.y += dx[idx * 2 + 1];
2415             }
2416             else
2417                 offset.x += dx[idx];
2418         }
2419         else
2420         {
2421             offset.x += gm.gmCellIncX;
2422             offset.y += gm.gmCellIncY;
2423         }
2424     }
2425     PATH_UnlockPath(pPath);
2426     return TRUE;
2427 }
2428 
2429 
2430 /***********************************************************************
2431  * Exported functions
2432  */
2433 
2434 BOOL
2435 APIENTRY
2436 NtGdiAbortPath(HDC hDC)
2437 {
2438     PDC dc = DC_LockDc(hDC);
2439     if (!dc)
2440     {
2441         EngSetLastError(ERROR_INVALID_HANDLE);
2442         return FALSE;
2443     }
2444 
2445     if (!dc->dclevel.hPath)
2446     {
2447        DC_UnlockDc(dc);
2448        return TRUE;
2449     }
2450 
2451     if (!PATH_Delete(dc->dclevel.hPath))
2452     {
2453        DC_UnlockDc(dc);
2454        return FALSE;
2455     }
2456 
2457     dc->dclevel.hPath = 0;
2458     dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2459 
2460     DC_UnlockDc(dc);
2461     return TRUE;
2462 }
2463 
2464 BOOL
2465 APIENTRY
2466 NtGdiBeginPath(HDC  hDC)
2467 {
2468     PPATH pPath;
2469     PDC dc;
2470 
2471     dc = DC_LockDc(hDC);
2472     if (!dc)
2473     {
2474         EngSetLastError(ERROR_INVALID_HANDLE);
2475         return FALSE;
2476     }
2477 
2478     /* If path is already open, do nothing. Check if not Save DC state */
2479     if ((dc->dclevel.flPath & DCPATH_ACTIVE) && !(dc->dclevel.flPath & DCPATH_SAVE))
2480     {
2481         DC_UnlockDc(dc);
2482         return TRUE;
2483     }
2484 
2485     if (dc->dclevel.hPath)
2486     {
2487         TRACE("BeginPath 1 0x%p\n", dc->dclevel.hPath);
2488         if (!(dc->dclevel.flPath & DCPATH_SAVE))
2489         {
2490             // Remove previous handle.
2491             if (!PATH_Delete(dc->dclevel.hPath))
2492             {
2493                 DC_UnlockDc(dc);
2494                 return FALSE;
2495             }
2496         }
2497         else
2498         {
2499             // Clear flags and Handle.
2500             dc->dclevel.flPath &= ~(DCPATH_SAVE | DCPATH_ACTIVE);
2501             dc->dclevel.hPath = NULL;
2502         }
2503     }
2504     pPath = PATH_CreatePath(NUM_ENTRIES_INITIAL);
2505     dc->dclevel.flPath |= DCPATH_ACTIVE; // Set active ASAP!
2506     dc->dclevel.hPath = pPath->BaseObject.hHmgr;
2507     IntGetCurrentPositionEx(dc, &pPath->pos);
2508     IntLPtoDP( dc, &pPath->pos, 1 );
2509     TRACE("BP : Current pos X %d Y %d\n",pPath->pos.x, pPath->pos.y);
2510     PATH_UnlockPath(pPath);
2511     DC_UnlockDc(dc);
2512 
2513     if (!pPath)
2514     {
2515         return FALSE;
2516     }
2517     return TRUE;
2518 }
2519 
2520 BOOL
2521 APIENTRY
2522 NtGdiCloseFigure(HDC hDC)
2523 {
2524     BOOL Ret = FALSE; // Default to failure
2525     PDC pDc;
2526     PPATH pPath;
2527 
2528     TRACE("Enter %s\n", __FUNCTION__);
2529 
2530     pDc = DC_LockDc(hDC);
2531     if (!pDc)
2532     {
2533         EngSetLastError(ERROR_INVALID_PARAMETER);
2534         return FALSE;
2535     }
2536 
2537     pPath = PATH_LockPath(pDc->dclevel.hPath);
2538     if (!pPath)
2539     {
2540         DC_UnlockDc(pDc);
2541         return FALSE;
2542     }
2543 
2544     if (pPath->state == PATH_Open)
2545     {
2546         IntGdiCloseFigure(pPath);
2547         Ret = TRUE;
2548     }
2549     else
2550     {
2551         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2552     }
2553 
2554     PATH_UnlockPath(pPath);
2555     DC_UnlockDc(pDc);
2556     return Ret;
2557 }
2558 
2559 BOOL
2560 APIENTRY
2561 NtGdiEndPath(HDC  hDC)
2562 {
2563     BOOL ret = TRUE;
2564     PPATH pPath;
2565     PDC dc;
2566 
2567     dc = DC_LockDc(hDC);
2568     if (!dc)
2569     {
2570         EngSetLastError(ERROR_INVALID_HANDLE);
2571         return FALSE;
2572     }
2573 
2574     pPath = PATH_LockPath(dc->dclevel.hPath);
2575     if (!pPath)
2576     {
2577         DC_UnlockDc(dc);
2578         return FALSE;
2579     }
2580 
2581     /* Check that path is currently being constructed */
2582     if ((pPath->state != PATH_Open) || !(dc->dclevel.flPath & DCPATH_ACTIVE))
2583     {
2584         TRACE("EndPath ERROR! 0x%p\n", dc->dclevel.hPath);
2585         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2586         ret = FALSE;
2587     }
2588     /* Set flag to indicate that path is finished */
2589     else
2590     {
2591         TRACE("EndPath 0x%p\n", dc->dclevel.hPath);
2592         pPath->state = PATH_Closed;
2593         dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2594     }
2595 
2596     PATH_UnlockPath(pPath);
2597     DC_UnlockDc(dc);
2598     return ret;
2599 }
2600 
2601 BOOL
2602 APIENTRY
2603 NtGdiFillPath(HDC  hDC)
2604 {
2605     BOOL ret = FALSE;
2606     PPATH pPath, pNewPath;
2607     PDC_ATTR pdcattr;
2608     PDC dc;
2609 
2610     dc = DC_LockDc(hDC);
2611     if (!dc)
2612     {
2613         EngSetLastError(ERROR_INVALID_PARAMETER);
2614         return FALSE;
2615     }
2616 
2617     pPath = PATH_LockPath(dc->dclevel.hPath);
2618     if (!pPath)
2619     {
2620         DC_UnlockDc(dc);
2621         return FALSE;
2622     }
2623 
2624     DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL);
2625 
2626     pdcattr = dc->pdcattr;
2627 
2628     if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2629         DC_vUpdateLineBrush(dc);
2630 
2631     if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
2632         DC_vUpdateFillBrush(dc);
2633 
2634     pNewPath = PATH_FlattenPath(pPath);
2635 
2636     if (pNewPath->state != PATH_Closed)
2637     {
2638         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2639     }
2640     else if (pNewPath->numEntriesUsed)
2641     {
2642        ret = PATH_FillPath(dc, pNewPath);
2643     }
2644     else ret = TRUE;
2645 
2646     PATH_UnlockPath(pNewPath);
2647     PATH_Delete(pNewPath->BaseObject.hHmgr);
2648 
2649     PATH_UnlockPath(pPath);
2650     PATH_Delete(pPath->BaseObject.hHmgr);
2651     dc->dclevel.hPath = 0;
2652     dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2653 
2654     DC_vFinishBlit(dc, NULL);
2655     DC_UnlockDc(dc);
2656     return ret;
2657 }
2658 
2659 BOOL
2660 APIENTRY
2661 NtGdiFlattenPath(HDC hDC)
2662 {
2663     BOOL Ret = FALSE;
2664     DC *pDc;
2665     PPATH pPath, pNewPath = NULL;
2666 
2667     TRACE("Enter %s\n", __FUNCTION__);
2668 
2669     pDc = DC_LockDc(hDC);
2670     if (!pDc)
2671     {
2672         EngSetLastError(ERROR_INVALID_HANDLE);
2673         return FALSE;
2674     }
2675 
2676     pPath = PATH_LockPath(pDc->dclevel.hPath);
2677     if (!pPath)
2678     {
2679         EngSetLastError( ERROR_CAN_NOT_COMPLETE );
2680         DC_UnlockDc(pDc);
2681         return FALSE;
2682     }
2683 
2684     if (pPath->state == PATH_Closed)
2685     {
2686         pNewPath = PATH_FlattenPath(pPath);
2687     }
2688 
2689     PATH_UnlockPath(pPath);
2690 
2691     if (pNewPath)
2692     {
2693        PATH_Delete(pDc->dclevel.hPath);
2694        pDc->dclevel.hPath = pNewPath->BaseObject.hHmgr;
2695        PATH_UnlockPath(pNewPath);
2696        Ret = TRUE;
2697     }
2698 
2699     DC_UnlockDc(pDc);
2700     return Ret;
2701 }
2702 
2703 _Success_(return != FALSE)
2704 BOOL
2705 APIENTRY
2706 NtGdiGetMiterLimit(
2707     _In_ HDC hdc,
2708     _Out_ PDWORD pdwOut)
2709 {
2710     DC *pDc;
2711     BOOL bResult = TRUE;
2712 
2713     if (!(pDc = DC_LockDc(hdc)))
2714     {
2715         EngSetLastError(ERROR_INVALID_PARAMETER);
2716         return FALSE;
2717     }
2718 
2719     _SEH2_TRY
2720     {
2721         ProbeForWrite(pdwOut, sizeof(DWORD), 1);
2722         *pdwOut = pDc->dclevel.laPath.eMiterLimit;
2723     }
2724     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2725     {
2726         SetLastNtError(_SEH2_GetExceptionCode());
2727         bResult = FALSE;
2728     }
2729     _SEH2_END;
2730 
2731     DC_UnlockDc(pDc);
2732     return bResult;
2733 
2734 }
2735 
2736 INT
2737 APIENTRY
2738 NtGdiGetPath(
2739     HDC hDC,
2740     LPPOINT Points,
2741     LPBYTE Types,
2742     INT nSize)
2743 {
2744     INT ret = -1;
2745     PPATH pPath;
2746     DC *dc;
2747 
2748     _SEH2_TRY
2749     {
2750         ProbeForWrite(Points, nSize * sizeof(*Points), sizeof(ULONG));
2751         ProbeForWrite(Types, nSize, 1);
2752     }
2753     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2754     {
2755         SetLastNtError(_SEH2_GetExceptionCode());
2756         _SEH2_YIELD(return -1);
2757     }
2758     _SEH2_END
2759 
2760     dc = DC_LockDc(hDC);
2761     TRACE("NtGdiGetPath start\n");
2762     if (!dc)
2763     {
2764         ERR("Can't lock dc!\n");
2765         EngSetLastError(ERROR_INVALID_PARAMETER);
2766         return -1;
2767     }
2768 
2769     pPath = PATH_LockPath(dc->dclevel.hPath);
2770     if (!pPath)
2771     {
2772         DC_UnlockDc(dc);
2773         return -1;
2774     }
2775 
2776     if (pPath->state != PATH_Closed)
2777     {
2778         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2779         goto done;
2780     }
2781 
2782     if (nSize == 0)
2783     {
2784         ret = pPath->numEntriesUsed;
2785     }
2786     else if (nSize < pPath->numEntriesUsed)
2787     {
2788         EngSetLastError(ERROR_INVALID_PARAMETER);
2789         goto done;
2790     }
2791     else
2792     {
2793         _SEH2_TRY
2794         {
2795             memcpy(Points, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
2796             memcpy(Types, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
2797 
2798             /* Convert the points to logical coordinates */
2799             if (!GdiPathDPtoLP(dc, Points, pPath->numEntriesUsed))
2800             {
2801                 EngSetLastError(ERROR_ARITHMETIC_OVERFLOW);
2802                 _SEH2_LEAVE;
2803             }
2804 
2805             ret = pPath->numEntriesUsed;
2806         }
2807         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2808         {
2809             SetLastNtError(_SEH2_GetExceptionCode());
2810         }
2811         _SEH2_END
2812     }
2813 
2814 done:
2815     TRACE("NtGdiGetPath exit %d\n",ret);
2816     PATH_UnlockPath(pPath);
2817     DC_UnlockDc(dc);
2818     return ret;
2819 }
2820 
2821 HRGN
2822 APIENTRY
2823 NtGdiPathToRegion(HDC  hDC)
2824 {
2825     PPATH pPath, pNewPath;
2826     HRGN  hrgnRval = 0;
2827     int Ret;
2828     PREGION Rgn;
2829     DC *pDc;
2830     PDC_ATTR pdcattr;
2831 
2832     TRACE("Enter %s\n", __FUNCTION__);
2833 
2834     pDc = DC_LockDc(hDC);
2835     if (!pDc)
2836     {
2837         ERR("Failed to lock DC %p\n", hDC);
2838         EngSetLastError(ERROR_INVALID_PARAMETER);
2839         return NULL;
2840     }
2841 
2842     pdcattr = pDc->pdcattr;
2843 
2844     pPath = PATH_LockPath(pDc->dclevel.hPath);
2845     if (!pPath)
2846     {
2847         ERR("Failed to lock DC path %p\n", pDc->dclevel.hPath);
2848         DC_UnlockDc(pDc);
2849         return NULL;
2850     }
2851 
2852     if (pPath->state != PATH_Closed)
2853     {
2854         // FIXME: Check that setlasterror is being called correctly
2855         ERR("Path is not closed!\n");
2856         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2857     }
2858     else
2859     {
2860         /* Create the region and fill it with the path strokes */
2861         Rgn = REGION_AllocUserRgnWithHandle(1);
2862         if (!Rgn)
2863         {
2864             ERR("Failed to allocate a region\n");
2865             PATH_UnlockPath(pPath);
2866             DC_UnlockDc(pDc);
2867             return NULL;
2868         }
2869         hrgnRval = Rgn->BaseObject.hHmgr;
2870 
2871         pNewPath = PATH_FlattenPath(pPath);
2872 
2873         Ret = PATH_PathToRegion(pNewPath, pdcattr->jFillMode, Rgn);
2874 
2875         PATH_UnlockPath(pNewPath);
2876         PATH_Delete(pNewPath->BaseObject.hHmgr);
2877 
2878         if (!Ret)
2879         {
2880             ERR("PATH_PathToRegion failed\n");
2881             REGION_Delete(Rgn);
2882             hrgnRval = NULL;
2883         }
2884         else
2885             REGION_UnlockRgn(Rgn);
2886     }
2887 
2888     PATH_UnlockPath(pPath);
2889     PATH_Delete(pDc->dclevel.hPath);
2890     pDc->dclevel.hPath = NULL;
2891     pDc->dclevel.flPath &= ~DCPATH_ACTIVE;
2892 
2893     DC_UnlockDc(pDc);
2894     return hrgnRval;
2895 }
2896 
2897 BOOL
2898 APIENTRY
2899 NtGdiSetMiterLimit(
2900     IN HDC hdc,
2901     IN DWORD dwNew,
2902     IN OUT OPTIONAL PDWORD pdwOut)
2903 {
2904     DC *pDc;
2905     gxf_long worker, worker1;
2906     BOOL bResult = TRUE;
2907 
2908     if (!(pDc = DC_LockDc(hdc)))
2909     {
2910         EngSetLastError(ERROR_INVALID_PARAMETER);
2911         return FALSE;
2912     }
2913 
2914     worker.l  = dwNew;
2915     worker1.f = pDc->dclevel.laPath.eMiterLimit;
2916     pDc->dclevel.laPath.eMiterLimit = worker.f;
2917 
2918     if (pdwOut)
2919     {
2920         _SEH2_TRY
2921         {
2922             ProbeForWrite(pdwOut, sizeof(DWORD), 1);
2923             *pdwOut = worker1.l;
2924         }
2925         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2926         {
2927             SetLastNtError(_SEH2_GetExceptionCode());
2928             bResult = FALSE;
2929         }
2930         _SEH2_END;
2931     }
2932 
2933     DC_UnlockDc(pDc);
2934     return bResult;
2935 }
2936 
2937 BOOL
2938 APIENTRY
2939 NtGdiStrokeAndFillPath(HDC hDC)
2940 {
2941     DC *pDc;
2942     PDC_ATTR pdcattr;
2943     PPATH pPath, pNewPath;
2944     BOOL bRet = FALSE;
2945 
2946     TRACE("Enter %s\n", __FUNCTION__);
2947 
2948     if (!(pDc = DC_LockDc(hDC)))
2949     {
2950         EngSetLastError(ERROR_INVALID_PARAMETER);
2951         return FALSE;
2952     }
2953     pPath = PATH_LockPath(pDc->dclevel.hPath);
2954     if (!pPath)
2955     {
2956         DC_UnlockDc(pDc);
2957         return FALSE;
2958     }
2959 
2960     DC_vPrepareDCsForBlit(pDc, NULL, NULL, NULL);
2961 
2962     pdcattr = pDc->pdcattr;
2963 
2964     if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
2965         DC_vUpdateFillBrush(pDc);
2966 
2967     if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2968         DC_vUpdateLineBrush(pDc);
2969 
2970     pNewPath = PATH_FlattenPath(pPath);
2971 
2972     if (pNewPath->state != PATH_Closed)
2973     {
2974         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2975     }
2976     else if (pNewPath->numEntriesUsed)
2977     {
2978        bRet = PATH_FillPath(pDc, pNewPath);
2979        if (bRet) bRet = PATH_StrokePath(pDc, pNewPath);
2980     }
2981     else bRet = TRUE;
2982 
2983     PATH_UnlockPath(pNewPath);
2984     PATH_Delete(pNewPath->BaseObject.hHmgr);
2985 
2986     PATH_UnlockPath(pPath);
2987     PATH_Delete(pPath->BaseObject.hHmgr);
2988     pDc->dclevel.hPath = 0;
2989     pDc->dclevel.flPath &= ~DCPATH_ACTIVE;
2990 
2991     DC_vFinishBlit(pDc, NULL);
2992     DC_UnlockDc(pDc);
2993     return bRet;
2994 }
2995 
2996 BOOL
2997 APIENTRY
2998 NtGdiStrokePath(HDC hDC)
2999 {
3000     DC *pDc;
3001     PDC_ATTR pdcattr;
3002     PPATH pPath, pNewPath;
3003     BOOL bRet = FALSE;
3004 
3005     TRACE("Enter %s\n", __FUNCTION__);
3006 
3007     if (!(pDc = DC_LockDc(hDC)))
3008     {
3009         EngSetLastError(ERROR_INVALID_PARAMETER);
3010         return FALSE;
3011     }
3012 
3013     pPath = PATH_LockPath(pDc->dclevel.hPath);
3014     if (!pPath)
3015     {
3016         DC_UnlockDc(pDc);
3017         return FALSE;
3018     }
3019 
3020     DC_vPrepareDCsForBlit(pDc, NULL, NULL, NULL);
3021 
3022     pdcattr = pDc->pdcattr;
3023 
3024     if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
3025         DC_vUpdateLineBrush(pDc);
3026 
3027     pNewPath = PATH_FlattenPath(pPath);
3028 
3029     if (pNewPath->state != PATH_Closed)
3030     {
3031         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
3032     }
3033     else bRet = PATH_StrokePath(pDc, pNewPath);
3034 
3035     PATH_UnlockPath(pNewPath);
3036     PATH_Delete(pNewPath->BaseObject.hHmgr);
3037 
3038     DC_vFinishBlit(pDc, NULL);
3039 
3040     PATH_UnlockPath(pPath);
3041     PATH_Delete(pPath->BaseObject.hHmgr);
3042     pDc->dclevel.hPath = 0;
3043     pDc->dclevel.flPath &= ~DCPATH_ACTIVE;
3044 
3045     DC_UnlockDc(pDc);
3046     return bRet;
3047 }
3048 
3049 BOOL
3050 APIENTRY
3051 NtGdiWidenPath(HDC  hDC)
3052 {
3053     PPATH pPath;
3054     BOOL Ret = FALSE;
3055     PDC pdc = DC_LockDc(hDC);
3056     TRACE("NtGdiWidenPat Enter\n");
3057     if (!pdc)
3058     {
3059         EngSetLastError(ERROR_INVALID_PARAMETER);
3060         return FALSE;
3061     }
3062 
3063     pPath = PATH_WidenPath(pdc);
3064     if (pPath)
3065     {
3066        TRACE("WindenPath New Path\n");
3067        PATH_Delete(pdc->dclevel.hPath);
3068        pdc->dclevel.hPath = pPath->BaseObject.hHmgr;
3069        Ret = TRUE;
3070     }
3071     DC_UnlockDc(pdc);
3072     TRACE("NtGdiWidenPat Ret %d\n",Ret);
3073     return Ret;
3074 }
3075 
3076 /* EOF */
3077