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