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