xref: /reactos/win32ss/gdi/ntgdi/path.c (revision bbabe248)
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            // With default DC font,,, bitmap font?
2382            // ExtTextOut on a path with bitmap font selected shouldn't fail.
2383            // This just leads to empty path generated.
2384            // Ref : test_emf_ExtTextOut_on_path
2385            continue;
2386         }
2387 
2388         /* Add outline only if char is printable */
2389         if (dwSize)
2390         {
2391             outline = ExAllocatePoolWithTag(PagedPool, dwSize, TAG_PATH);
2392             if (!outline)
2393             {
2394                PATH_UnlockPath(pPath);
2395                return FALSE;
2396             }
2397 
2398             ftGdiGetGlyphOutline(dc,
2399                                  str[idx],
2400                                  ggo_flags,
2401                                  &gm,
2402                                  dwSize,
2403                                  outline,
2404                                  &identity,
2405                                  TRUE);
2406 
2407             PATH_add_outline(dc, pPath, x + offset.x, y + offset.y, outline, dwSize);
2408 
2409             ExFreePoolWithTag(outline, TAG_PATH);
2410         }
2411 
2412         if (dx)
2413         {
2414             if (flags & ETO_PDY)
2415             {
2416                 offset.x += dx[idx * 2];
2417                 offset.y += dx[idx * 2 + 1];
2418             }
2419             else
2420                 offset.x += dx[idx];
2421         }
2422         else
2423         {
2424             offset.x += gm.gmCellIncX;
2425             offset.y += gm.gmCellIncY;
2426         }
2427     }
2428     PATH_UnlockPath(pPath);
2429     return TRUE;
2430 }
2431 
2432 
2433 /***********************************************************************
2434  * Exported functions
2435  */
2436 
2437 BOOL
2438 APIENTRY
2439 NtGdiAbortPath(HDC hDC)
2440 {
2441     PDC dc = DC_LockDc(hDC);
2442     if (!dc)
2443     {
2444         EngSetLastError(ERROR_INVALID_HANDLE);
2445         return FALSE;
2446     }
2447 
2448     if (!dc->dclevel.hPath)
2449     {
2450        DC_UnlockDc(dc);
2451        return TRUE;
2452     }
2453 
2454     if (!PATH_Delete(dc->dclevel.hPath))
2455     {
2456        DC_UnlockDc(dc);
2457        return FALSE;
2458     }
2459 
2460     dc->dclevel.hPath = 0;
2461     dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2462 
2463     DC_UnlockDc(dc);
2464     return TRUE;
2465 }
2466 
2467 BOOL
2468 APIENTRY
2469 NtGdiBeginPath(HDC  hDC)
2470 {
2471     PPATH pPath;
2472     PDC dc;
2473 
2474     dc = DC_LockDc(hDC);
2475     if (!dc)
2476     {
2477         EngSetLastError(ERROR_INVALID_HANDLE);
2478         return FALSE;
2479     }
2480 
2481     /* If path is already open, do nothing. Check if not Save DC state */
2482     if ((dc->dclevel.flPath & DCPATH_ACTIVE) && !(dc->dclevel.flPath & DCPATH_SAVE))
2483     {
2484         DC_UnlockDc(dc);
2485         return TRUE;
2486     }
2487 
2488     if (dc->dclevel.hPath)
2489     {
2490         TRACE("BeginPath 1 0x%p\n", dc->dclevel.hPath);
2491         if (!(dc->dclevel.flPath & DCPATH_SAVE))
2492         {
2493             // Remove previous handle.
2494             if (!PATH_Delete(dc->dclevel.hPath))
2495             {
2496                 DC_UnlockDc(dc);
2497                 return FALSE;
2498             }
2499         }
2500         else
2501         {
2502             // Clear flags and Handle.
2503             dc->dclevel.flPath &= ~(DCPATH_SAVE | DCPATH_ACTIVE);
2504             dc->dclevel.hPath = NULL;
2505         }
2506     }
2507     pPath = PATH_CreatePath(NUM_ENTRIES_INITIAL);
2508     dc->dclevel.flPath |= DCPATH_ACTIVE; // Set active ASAP!
2509     dc->dclevel.hPath = pPath->BaseObject.hHmgr;
2510     IntGetCurrentPositionEx(dc, &pPath->pos);
2511     IntLPtoDP( dc, &pPath->pos, 1 );
2512     TRACE("BP : Current pos X %d Y %d\n",pPath->pos.x, pPath->pos.y);
2513     PATH_UnlockPath(pPath);
2514     DC_UnlockDc(dc);
2515 
2516     if (!pPath)
2517     {
2518         return FALSE;
2519     }
2520     return TRUE;
2521 }
2522 
2523 BOOL
2524 APIENTRY
2525 NtGdiCloseFigure(HDC hDC)
2526 {
2527     BOOL Ret = FALSE; // Default to failure
2528     PDC pDc;
2529     PPATH pPath;
2530 
2531     TRACE("Enter %s\n", __FUNCTION__);
2532 
2533     pDc = DC_LockDc(hDC);
2534     if (!pDc)
2535     {
2536         EngSetLastError(ERROR_INVALID_PARAMETER);
2537         return FALSE;
2538     }
2539 
2540     pPath = PATH_LockPath(pDc->dclevel.hPath);
2541     if (!pPath)
2542     {
2543         DC_UnlockDc(pDc);
2544         return FALSE;
2545     }
2546 
2547     if (pPath->state == PATH_Open)
2548     {
2549         IntGdiCloseFigure(pPath);
2550         Ret = TRUE;
2551     }
2552     else
2553     {
2554         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2555     }
2556 
2557     PATH_UnlockPath(pPath);
2558     DC_UnlockDc(pDc);
2559     return Ret;
2560 }
2561 
2562 BOOL
2563 APIENTRY
2564 NtGdiEndPath(HDC  hDC)
2565 {
2566     BOOL ret = TRUE;
2567     PPATH pPath;
2568     PDC dc;
2569 
2570     dc = DC_LockDc(hDC);
2571     if (!dc)
2572     {
2573         EngSetLastError(ERROR_INVALID_HANDLE);
2574         return FALSE;
2575     }
2576 
2577     pPath = PATH_LockPath(dc->dclevel.hPath);
2578     if (!pPath)
2579     {
2580         DC_UnlockDc(dc);
2581         return FALSE;
2582     }
2583 
2584     /* Check that path is currently being constructed */
2585     if ((pPath->state != PATH_Open) || !(dc->dclevel.flPath & DCPATH_ACTIVE))
2586     {
2587         TRACE("EndPath ERROR! 0x%p\n", dc->dclevel.hPath);
2588         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2589         ret = FALSE;
2590     }
2591     /* Set flag to indicate that path is finished */
2592     else
2593     {
2594         TRACE("EndPath 0x%p\n", dc->dclevel.hPath);
2595         pPath->state = PATH_Closed;
2596         dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2597     }
2598 
2599     PATH_UnlockPath(pPath);
2600     DC_UnlockDc(dc);
2601     return ret;
2602 }
2603 
2604 BOOL
2605 APIENTRY
2606 NtGdiFillPath(HDC  hDC)
2607 {
2608     BOOL ret = FALSE;
2609     PPATH pPath, pNewPath;
2610     PDC_ATTR pdcattr;
2611     PDC dc;
2612 
2613     dc = DC_LockDc(hDC);
2614     if (!dc)
2615     {
2616         EngSetLastError(ERROR_INVALID_PARAMETER);
2617         return FALSE;
2618     }
2619 
2620     pPath = PATH_LockPath(dc->dclevel.hPath);
2621     if (!pPath)
2622     {
2623         DC_UnlockDc(dc);
2624         return FALSE;
2625     }
2626 
2627     DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL);
2628 
2629     pdcattr = dc->pdcattr;
2630 
2631     if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2632         DC_vUpdateLineBrush(dc);
2633 
2634     if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
2635         DC_vUpdateFillBrush(dc);
2636 
2637     pNewPath = PATH_FlattenPath(pPath);
2638 
2639     if (pNewPath->state != PATH_Closed)
2640     {
2641         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2642     }
2643     else if (pNewPath->numEntriesUsed)
2644     {
2645        ret = PATH_FillPath(dc, pNewPath);
2646     }
2647     else ret = TRUE;
2648 
2649     PATH_UnlockPath(pNewPath);
2650     PATH_Delete(pNewPath->BaseObject.hHmgr);
2651 
2652     PATH_UnlockPath(pPath);
2653     PATH_Delete(pPath->BaseObject.hHmgr);
2654     dc->dclevel.hPath = 0;
2655     dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2656 
2657     DC_vFinishBlit(dc, NULL);
2658     DC_UnlockDc(dc);
2659     return ret;
2660 }
2661 
2662 BOOL
2663 APIENTRY
2664 NtGdiFlattenPath(HDC hDC)
2665 {
2666     BOOL Ret = FALSE;
2667     DC *pDc;
2668     PPATH pPath, pNewPath = NULL;
2669 
2670     TRACE("Enter %s\n", __FUNCTION__);
2671 
2672     pDc = DC_LockDc(hDC);
2673     if (!pDc)
2674     {
2675         EngSetLastError(ERROR_INVALID_HANDLE);
2676         return FALSE;
2677     }
2678 
2679     pPath = PATH_LockPath(pDc->dclevel.hPath);
2680     if (!pPath)
2681     {
2682         EngSetLastError( ERROR_CAN_NOT_COMPLETE );
2683         DC_UnlockDc(pDc);
2684         return FALSE;
2685     }
2686 
2687     if (pPath->state == PATH_Closed)
2688     {
2689         pNewPath = PATH_FlattenPath(pPath);
2690     }
2691 
2692     PATH_UnlockPath(pPath);
2693 
2694     if (pNewPath)
2695     {
2696        PATH_Delete(pDc->dclevel.hPath);
2697        pDc->dclevel.hPath = pNewPath->BaseObject.hHmgr;
2698        PATH_UnlockPath(pNewPath);
2699        Ret = TRUE;
2700     }
2701 
2702     DC_UnlockDc(pDc);
2703     return Ret;
2704 }
2705 
2706 _Success_(return != FALSE)
2707 BOOL
2708 APIENTRY
2709 NtGdiGetMiterLimit(
2710     _In_ HDC hdc,
2711     _Out_ PDWORD pdwOut)
2712 {
2713     DC *pDc;
2714     BOOL bResult = TRUE;
2715 
2716     if (!(pDc = DC_LockDc(hdc)))
2717     {
2718         EngSetLastError(ERROR_INVALID_PARAMETER);
2719         return FALSE;
2720     }
2721 
2722     _SEH2_TRY
2723     {
2724         ProbeForWrite(pdwOut, sizeof(DWORD), 1);
2725         *pdwOut = pDc->dclevel.laPath.eMiterLimit;
2726     }
2727     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2728     {
2729         SetLastNtError(_SEH2_GetExceptionCode());
2730         bResult = FALSE;
2731     }
2732     _SEH2_END;
2733 
2734     DC_UnlockDc(pDc);
2735     return bResult;
2736 
2737 }
2738 
2739 INT
2740 APIENTRY
2741 NtGdiGetPath(
2742     HDC hDC,
2743     LPPOINT Points,
2744     LPBYTE Types,
2745     INT nSize)
2746 {
2747     INT ret = -1;
2748     PPATH pPath;
2749     DC *dc;
2750 
2751     _SEH2_TRY
2752     {
2753         ProbeForWrite(Points, nSize * sizeof(*Points), sizeof(ULONG));
2754         ProbeForWrite(Types, nSize, 1);
2755     }
2756     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2757     {
2758         SetLastNtError(_SEH2_GetExceptionCode());
2759         _SEH2_YIELD(return -1);
2760     }
2761     _SEH2_END
2762 
2763     dc = DC_LockDc(hDC);
2764     TRACE("NtGdiGetPath start\n");
2765     if (!dc)
2766     {
2767         ERR("Can't lock dc!\n");
2768         EngSetLastError(ERROR_INVALID_PARAMETER);
2769         return -1;
2770     }
2771 
2772     pPath = PATH_LockPath(dc->dclevel.hPath);
2773     if (!pPath)
2774     {
2775         DC_UnlockDc(dc);
2776         return -1;
2777     }
2778 
2779     if (pPath->state != PATH_Closed)
2780     {
2781         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2782         goto done;
2783     }
2784 
2785     if (nSize == 0)
2786     {
2787         ret = pPath->numEntriesUsed;
2788     }
2789     else if (nSize < pPath->numEntriesUsed)
2790     {
2791         EngSetLastError(ERROR_INVALID_PARAMETER);
2792         goto done;
2793     }
2794     else
2795     {
2796         _SEH2_TRY
2797         {
2798             memcpy(Points, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
2799             memcpy(Types, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
2800 
2801             /* Convert the points to logical coordinates */
2802             if (!GdiPathDPtoLP(dc, Points, pPath->numEntriesUsed))
2803             {
2804                 EngSetLastError(ERROR_ARITHMETIC_OVERFLOW);
2805                 _SEH2_LEAVE;
2806             }
2807 
2808             ret = pPath->numEntriesUsed;
2809         }
2810         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2811         {
2812             SetLastNtError(_SEH2_GetExceptionCode());
2813         }
2814         _SEH2_END
2815     }
2816 
2817 done:
2818     TRACE("NtGdiGetPath exit %d\n",ret);
2819     PATH_UnlockPath(pPath);
2820     DC_UnlockDc(dc);
2821     return ret;
2822 }
2823 
2824 HRGN
2825 APIENTRY
2826 NtGdiPathToRegion(HDC  hDC)
2827 {
2828     PPATH pPath, pNewPath;
2829     HRGN  hrgnRval = 0;
2830     int Ret;
2831     PREGION Rgn;
2832     DC *pDc;
2833     PDC_ATTR pdcattr;
2834 
2835     TRACE("Enter %s\n", __FUNCTION__);
2836 
2837     pDc = DC_LockDc(hDC);
2838     if (!pDc)
2839     {
2840         ERR("Failed to lock DC %p\n", hDC);
2841         EngSetLastError(ERROR_INVALID_PARAMETER);
2842         return NULL;
2843     }
2844 
2845     pdcattr = pDc->pdcattr;
2846 
2847     pPath = PATH_LockPath(pDc->dclevel.hPath);
2848     if (!pPath)
2849     {
2850         ERR("Failed to lock DC path %p\n", pDc->dclevel.hPath);
2851         DC_UnlockDc(pDc);
2852         return NULL;
2853     }
2854 
2855     if (pPath->state != PATH_Closed)
2856     {
2857         // FIXME: Check that setlasterror is being called correctly
2858         ERR("Path is not closed!\n");
2859         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2860     }
2861     else
2862     {
2863         /* Create the region and fill it with the path strokes */
2864         Rgn = REGION_AllocUserRgnWithHandle(1);
2865         if (!Rgn)
2866         {
2867             ERR("Failed to allocate a region\n");
2868             PATH_UnlockPath(pPath);
2869             DC_UnlockDc(pDc);
2870             return NULL;
2871         }
2872         hrgnRval = Rgn->BaseObject.hHmgr;
2873 
2874         pNewPath = PATH_FlattenPath(pPath);
2875 
2876         Ret = PATH_PathToRegion(pNewPath, pdcattr->jFillMode, Rgn);
2877 
2878         PATH_UnlockPath(pNewPath);
2879         PATH_Delete(pNewPath->BaseObject.hHmgr);
2880 
2881         if (!Ret)
2882         {
2883             ERR("PATH_PathToRegion failed\n");
2884             REGION_Delete(Rgn);
2885             hrgnRval = NULL;
2886         }
2887         else
2888             REGION_UnlockRgn(Rgn);
2889     }
2890 
2891     PATH_UnlockPath(pPath);
2892     PATH_Delete(pDc->dclevel.hPath);
2893     pDc->dclevel.hPath = NULL;
2894     pDc->dclevel.flPath &= ~DCPATH_ACTIVE;
2895 
2896     DC_UnlockDc(pDc);
2897     return hrgnRval;
2898 }
2899 
2900 BOOL
2901 APIENTRY
2902 NtGdiSetMiterLimit(
2903     IN HDC hdc,
2904     IN DWORD dwNew,
2905     IN OUT OPTIONAL PDWORD pdwOut)
2906 {
2907     DC *pDc;
2908     gxf_long worker, worker1;
2909     BOOL bResult = TRUE;
2910 
2911     if (!(pDc = DC_LockDc(hdc)))
2912     {
2913         EngSetLastError(ERROR_INVALID_PARAMETER);
2914         return FALSE;
2915     }
2916 
2917     worker.l  = dwNew;
2918     worker1.f = pDc->dclevel.laPath.eMiterLimit;
2919     pDc->dclevel.laPath.eMiterLimit = worker.f;
2920 
2921     if (pdwOut)
2922     {
2923         _SEH2_TRY
2924         {
2925             ProbeForWrite(pdwOut, sizeof(DWORD), 1);
2926             *pdwOut = worker1.l;
2927         }
2928         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2929         {
2930             SetLastNtError(_SEH2_GetExceptionCode());
2931             bResult = FALSE;
2932         }
2933         _SEH2_END;
2934     }
2935 
2936     DC_UnlockDc(pDc);
2937     return bResult;
2938 }
2939 
2940 BOOL
2941 APIENTRY
2942 NtGdiStrokeAndFillPath(HDC hDC)
2943 {
2944     DC *pDc;
2945     PDC_ATTR pdcattr;
2946     PPATH pPath, pNewPath;
2947     BOOL bRet = FALSE;
2948 
2949     TRACE("Enter %s\n", __FUNCTION__);
2950 
2951     if (!(pDc = DC_LockDc(hDC)))
2952     {
2953         EngSetLastError(ERROR_INVALID_PARAMETER);
2954         return FALSE;
2955     }
2956     pPath = PATH_LockPath(pDc->dclevel.hPath);
2957     if (!pPath)
2958     {
2959         DC_UnlockDc(pDc);
2960         return FALSE;
2961     }
2962 
2963     DC_vPrepareDCsForBlit(pDc, NULL, NULL, NULL);
2964 
2965     pdcattr = pDc->pdcattr;
2966 
2967     if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
2968         DC_vUpdateFillBrush(pDc);
2969 
2970     if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2971         DC_vUpdateLineBrush(pDc);
2972 
2973     pNewPath = PATH_FlattenPath(pPath);
2974 
2975     if (pNewPath->state != PATH_Closed)
2976     {
2977         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2978     }
2979     else if (pNewPath->numEntriesUsed)
2980     {
2981        bRet = PATH_FillPath(pDc, pNewPath);
2982        if (bRet) bRet = PATH_StrokePath(pDc, pNewPath);
2983     }
2984     else bRet = TRUE;
2985 
2986     PATH_UnlockPath(pNewPath);
2987     PATH_Delete(pNewPath->BaseObject.hHmgr);
2988 
2989     PATH_UnlockPath(pPath);
2990     PATH_Delete(pPath->BaseObject.hHmgr);
2991     pDc->dclevel.hPath = 0;
2992     pDc->dclevel.flPath &= ~DCPATH_ACTIVE;
2993 
2994     DC_vFinishBlit(pDc, NULL);
2995     DC_UnlockDc(pDc);
2996     return bRet;
2997 }
2998 
2999 BOOL
3000 APIENTRY
3001 NtGdiStrokePath(HDC hDC)
3002 {
3003     DC *pDc;
3004     PDC_ATTR pdcattr;
3005     PPATH pPath, pNewPath;
3006     BOOL bRet = FALSE;
3007 
3008     TRACE("Enter %s\n", __FUNCTION__);
3009 
3010     if (!(pDc = DC_LockDc(hDC)))
3011     {
3012         EngSetLastError(ERROR_INVALID_PARAMETER);
3013         return FALSE;
3014     }
3015 
3016     pPath = PATH_LockPath(pDc->dclevel.hPath);
3017     if (!pPath)
3018     {
3019         DC_UnlockDc(pDc);
3020         return FALSE;
3021     }
3022 
3023     DC_vPrepareDCsForBlit(pDc, NULL, NULL, NULL);
3024 
3025     pdcattr = pDc->pdcattr;
3026 
3027     if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
3028         DC_vUpdateLineBrush(pDc);
3029 
3030     pNewPath = PATH_FlattenPath(pPath);
3031 
3032     if (pNewPath->state != PATH_Closed)
3033     {
3034         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
3035     }
3036     else bRet = PATH_StrokePath(pDc, pNewPath);
3037 
3038     PATH_UnlockPath(pNewPath);
3039     PATH_Delete(pNewPath->BaseObject.hHmgr);
3040 
3041     DC_vFinishBlit(pDc, NULL);
3042 
3043     PATH_UnlockPath(pPath);
3044     PATH_Delete(pPath->BaseObject.hHmgr);
3045     pDc->dclevel.hPath = 0;
3046     pDc->dclevel.flPath &= ~DCPATH_ACTIVE;
3047 
3048     DC_UnlockDc(pDc);
3049     return bRet;
3050 }
3051 
3052 BOOL
3053 APIENTRY
3054 NtGdiWidenPath(HDC  hDC)
3055 {
3056     PPATH pPath;
3057     BOOL Ret = FALSE;
3058     PDC pdc = DC_LockDc(hDC);
3059     TRACE("NtGdiWidenPat Enter\n");
3060     if (!pdc)
3061     {
3062         EngSetLastError(ERROR_INVALID_PARAMETER);
3063         return FALSE;
3064     }
3065 
3066     pPath = PATH_WidenPath(pdc);
3067     if (pPath)
3068     {
3069        TRACE("WindenPath New Path\n");
3070        PATH_Delete(pdc->dclevel.hPath);
3071        pdc->dclevel.hPath = pPath->BaseObject.hHmgr;
3072        Ret = TRUE;
3073     }
3074     DC_UnlockDc(pdc);
3075     TRACE("NtGdiWidenPat Ret %d\n",Ret);
3076     return Ret;
3077 }
3078 
3079 /* EOF */
3080