gdi32: Implement the LineTo entry point in the path driver.
[wine.git] / dlls / gdi32 / path.c
blob11be17c2d1efd6847de196c8d94734da280b7e17
1 /*
2 * Graphics paths (BeginPath, EndPath etc.)
4 * Copyright 1997, 1998 Martin Boehme
5 * 1999 Huw D M Davies
6 * Copyright 2005 Dmitry Timoshkov
7 * Copyright 2011 Alexandre Julliard
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "config.h"
25 #include "wine/port.h"
27 #include <assert.h>
28 #include <math.h>
29 #include <stdarg.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #if defined(HAVE_FLOAT_H)
33 #include <float.h>
34 #endif
36 #include "windef.h"
37 #include "winbase.h"
38 #include "wingdi.h"
39 #include "winerror.h"
41 #include "gdi_private.h"
42 #include "wine/debug.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(gdi);
46 /* Notes on the implementation
48 * The implementation is based on dynamically resizable arrays of points and
49 * flags. I dithered for a bit before deciding on this implementation, and
50 * I had even done a bit of work on a linked list version before switching
51 * to arrays. It's a bit of a tradeoff. When you use linked lists, the
52 * implementation of FlattenPath is easier, because you can rip the
53 * PT_BEZIERTO entries out of the middle of the list and link the
54 * corresponding PT_LINETO entries in. However, when you use arrays,
55 * PathToRegion becomes easier, since you can essentially just pass your array
56 * of points to CreatePolyPolygonRgn. Also, if I'd used linked lists, I would
57 * have had the extra effort of creating a chunk-based allocation scheme
58 * in order to use memory effectively. That's why I finally decided to use
59 * arrays. Note by the way that the array based implementation has the same
60 * linear time complexity that linked lists would have since the arrays grow
61 * exponentially.
63 * The points are stored in the path in device coordinates. This is
64 * consistent with the way Windows does things (for instance, see the Win32
65 * SDK documentation for GetPath).
67 * The word "stroke" appears in several places (e.g. in the flag
68 * GdiPath.newStroke). A stroke consists of a PT_MOVETO followed by one or
69 * more PT_LINETOs or PT_BEZIERTOs, up to, but not including, the next
70 * PT_MOVETO. Note that this is not the same as the definition of a figure;
71 * a figure can contain several strokes.
73 * Martin Boehme
76 #define NUM_ENTRIES_INITIAL 16 /* Initial size of points / flags arrays */
77 #define GROW_FACTOR_NUMER 2 /* Numerator of grow factor for the array */
78 #define GROW_FACTOR_DENOM 1 /* Denominator of grow factor */
80 /* A floating point version of the POINT structure */
81 typedef struct tagFLOAT_POINT
83 double x, y;
84 } FLOAT_POINT;
87 struct path_physdev
89 struct gdi_physdev dev;
90 GdiPath *path;
93 static inline struct path_physdev *get_path_physdev( PHYSDEV dev )
95 return (struct path_physdev *)dev;
98 static inline void pop_path_driver( DC *dc )
100 PHYSDEV dev = pop_dc_driver( &dc->physDev );
101 assert( dev->funcs == &path_driver );
102 HeapFree( GetProcessHeap(), 0, dev );
106 /* Performs a world-to-viewport transformation on the specified point (which
107 * is in floating point format).
109 static inline void INTERNAL_LPTODP_FLOAT(DC *dc, FLOAT_POINT *point)
111 double x, y;
113 /* Perform the transformation */
114 x = point->x;
115 y = point->y;
116 point->x = x * dc->xformWorld2Vport.eM11 + y * dc->xformWorld2Vport.eM21 + dc->xformWorld2Vport.eDx;
117 point->y = x * dc->xformWorld2Vport.eM12 + y * dc->xformWorld2Vport.eM22 + dc->xformWorld2Vport.eDy;
120 static inline INT int_from_fixed(FIXED f)
122 return (f.fract >= 0x8000) ? (f.value + 1) : f.value;
126 /* PATH_EmptyPath
128 * Removes all entries from the path and sets the path state to PATH_Null.
130 static void PATH_EmptyPath(GdiPath *pPath)
132 pPath->state=PATH_Null;
133 pPath->numEntriesUsed=0;
136 /* PATH_ReserveEntries
138 * Ensures that at least "numEntries" entries (for points and flags) have
139 * been allocated; allocates larger arrays and copies the existing entries
140 * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
142 static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries)
144 INT numEntriesToAllocate;
145 POINT *pPointsNew;
146 BYTE *pFlagsNew;
148 assert(numEntries>=0);
150 /* Do we have to allocate more memory? */
151 if(numEntries > pPath->numEntriesAllocated)
153 /* Find number of entries to allocate. We let the size of the array
154 * grow exponentially, since that will guarantee linear time
155 * complexity. */
156 if(pPath->numEntriesAllocated)
158 numEntriesToAllocate=pPath->numEntriesAllocated;
159 while(numEntriesToAllocate<numEntries)
160 numEntriesToAllocate=numEntriesToAllocate*GROW_FACTOR_NUMER/
161 GROW_FACTOR_DENOM;
163 else
164 numEntriesToAllocate=numEntries;
166 /* Allocate new arrays */
167 pPointsNew=HeapAlloc( GetProcessHeap(), 0, numEntriesToAllocate * sizeof(POINT) );
168 if(!pPointsNew)
169 return FALSE;
170 pFlagsNew=HeapAlloc( GetProcessHeap(), 0, numEntriesToAllocate * sizeof(BYTE) );
171 if(!pFlagsNew)
173 HeapFree( GetProcessHeap(), 0, pPointsNew );
174 return FALSE;
177 /* Copy old arrays to new arrays and discard old arrays */
178 if(pPath->pPoints)
180 assert(pPath->pFlags);
182 memcpy(pPointsNew, pPath->pPoints,
183 sizeof(POINT)*pPath->numEntriesUsed);
184 memcpy(pFlagsNew, pPath->pFlags,
185 sizeof(BYTE)*pPath->numEntriesUsed);
187 HeapFree( GetProcessHeap(), 0, pPath->pPoints );
188 HeapFree( GetProcessHeap(), 0, pPath->pFlags );
190 pPath->pPoints=pPointsNew;
191 pPath->pFlags=pFlagsNew;
192 pPath->numEntriesAllocated=numEntriesToAllocate;
195 return TRUE;
198 /* PATH_AddEntry
200 * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
201 * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
202 * successful, FALSE otherwise (e.g. if not enough memory was available).
204 static BOOL PATH_AddEntry(GdiPath *pPath, const POINT *pPoint, BYTE flags)
206 /* FIXME: If newStroke is true, perhaps we want to check that we're
207 * getting a PT_MOVETO
209 TRACE("(%d,%d) - %d\n", pPoint->x, pPoint->y, flags);
211 /* Check that path is open */
212 if(pPath->state!=PATH_Open)
213 return FALSE;
215 /* Reserve enough memory for an extra path entry */
216 if(!PATH_ReserveEntries(pPath, pPath->numEntriesUsed+1))
217 return FALSE;
219 /* Store information in path entry */
220 pPath->pPoints[pPath->numEntriesUsed]=*pPoint;
221 pPath->pFlags[pPath->numEntriesUsed]=flags;
223 /* If this is PT_CLOSEFIGURE, we have to start a new stroke next time */
224 if((flags & PT_CLOSEFIGURE) == PT_CLOSEFIGURE)
225 pPath->newStroke=TRUE;
227 /* Increment entry count */
228 pPath->numEntriesUsed++;
230 return TRUE;
233 /* PATH_AssignGdiPath
235 * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
236 * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
237 * not just the pointers. Since this means that the arrays in pPathDest may
238 * need to be resized, pPathDest should have been initialized using
239 * PATH_InitGdiPath (in C++, this function would be an assignment operator,
240 * not a copy constructor).
241 * Returns TRUE if successful, else FALSE.
243 static BOOL PATH_AssignGdiPath(GdiPath *pPathDest, const GdiPath *pPathSrc)
245 /* Make sure destination arrays are big enough */
246 if(!PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed))
247 return FALSE;
249 /* Perform the copy operation */
250 memcpy(pPathDest->pPoints, pPathSrc->pPoints,
251 sizeof(POINT)*pPathSrc->numEntriesUsed);
252 memcpy(pPathDest->pFlags, pPathSrc->pFlags,
253 sizeof(BYTE)*pPathSrc->numEntriesUsed);
255 pPathDest->state=pPathSrc->state;
256 pPathDest->numEntriesUsed=pPathSrc->numEntriesUsed;
257 pPathDest->newStroke=pPathSrc->newStroke;
259 return TRUE;
262 /* PATH_CheckCorners
264 * Helper function for PATH_RoundRect() and PATH_Rectangle()
266 static BOOL PATH_CheckCorners(DC *dc, POINT corners[], INT x1, INT y1, INT x2, INT y2)
268 INT temp;
270 /* Convert points to device coordinates */
271 corners[0].x=x1;
272 corners[0].y=y1;
273 corners[1].x=x2;
274 corners[1].y=y2;
275 if(!LPtoDP(dc->hSelf, corners, 2))
276 return FALSE;
278 /* Make sure first corner is top left and second corner is bottom right */
279 if(corners[0].x>corners[1].x)
281 temp=corners[0].x;
282 corners[0].x=corners[1].x;
283 corners[1].x=temp;
285 if(corners[0].y>corners[1].y)
287 temp=corners[0].y;
288 corners[0].y=corners[1].y;
289 corners[1].y=temp;
292 /* In GM_COMPATIBLE, don't include bottom and right edges */
293 if(dc->GraphicsMode==GM_COMPATIBLE)
295 corners[1].x--;
296 corners[1].y--;
299 return TRUE;
302 /* PATH_AddFlatBezier
304 static BOOL PATH_AddFlatBezier(GdiPath *pPath, POINT *pt, BOOL closed)
306 POINT *pts;
307 INT no, i;
309 pts = GDI_Bezier( pt, 4, &no );
310 if(!pts) return FALSE;
312 for(i = 1; i < no; i++)
313 PATH_AddEntry(pPath, &pts[i], (i == no-1 && closed) ? PT_LINETO | PT_CLOSEFIGURE : PT_LINETO);
314 HeapFree( GetProcessHeap(), 0, pts );
315 return TRUE;
318 /* PATH_FlattenPath
320 * Replaces Beziers with line segments
323 static BOOL PATH_FlattenPath(GdiPath *pPath)
325 GdiPath newPath;
326 INT srcpt;
328 memset(&newPath, 0, sizeof(newPath));
329 newPath.state = PATH_Open;
330 for(srcpt = 0; srcpt < pPath->numEntriesUsed; srcpt++) {
331 switch(pPath->pFlags[srcpt] & ~PT_CLOSEFIGURE) {
332 case PT_MOVETO:
333 case PT_LINETO:
334 PATH_AddEntry(&newPath, &pPath->pPoints[srcpt],
335 pPath->pFlags[srcpt]);
336 break;
337 case PT_BEZIERTO:
338 PATH_AddFlatBezier(&newPath, &pPath->pPoints[srcpt-1],
339 pPath->pFlags[srcpt+2] & PT_CLOSEFIGURE);
340 srcpt += 2;
341 break;
344 newPath.state = PATH_Closed;
345 PATH_AssignGdiPath(pPath, &newPath);
346 PATH_DestroyGdiPath(&newPath);
347 return TRUE;
350 /* PATH_PathToRegion
352 * Creates a region from the specified path using the specified polygon
353 * filling mode. The path is left unchanged. A handle to the region that
354 * was created is stored in *pHrgn. If successful, TRUE is returned; if an
355 * error occurs, SetLastError is called with the appropriate value and
356 * FALSE is returned.
358 static BOOL PATH_PathToRegion(GdiPath *pPath, INT nPolyFillMode,
359 HRGN *pHrgn)
361 int numStrokes, iStroke, i;
362 INT *pNumPointsInStroke;
363 HRGN hrgn;
365 PATH_FlattenPath(pPath);
367 /* FIXME: What happens when number of points is zero? */
369 /* First pass: Find out how many strokes there are in the path */
370 /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
371 numStrokes=0;
372 for(i=0; i<pPath->numEntriesUsed; i++)
373 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
374 numStrokes++;
376 /* Allocate memory for number-of-points-in-stroke array */
377 pNumPointsInStroke=HeapAlloc( GetProcessHeap(), 0, sizeof(int) * numStrokes );
378 if(!pNumPointsInStroke)
380 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
381 return FALSE;
384 /* Second pass: remember number of points in each polygon */
385 iStroke=-1; /* Will get incremented to 0 at beginning of first stroke */
386 for(i=0; i<pPath->numEntriesUsed; i++)
388 /* Is this the beginning of a new stroke? */
389 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
391 iStroke++;
392 pNumPointsInStroke[iStroke]=0;
395 pNumPointsInStroke[iStroke]++;
398 /* Create a region from the strokes */
399 hrgn=CreatePolyPolygonRgn(pPath->pPoints, pNumPointsInStroke,
400 numStrokes, nPolyFillMode);
402 /* Free memory for number-of-points-in-stroke array */
403 HeapFree( GetProcessHeap(), 0, pNumPointsInStroke );
405 if(hrgn==NULL)
407 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
408 return FALSE;
411 /* Success! */
412 *pHrgn=hrgn;
413 return TRUE;
416 /* PATH_ScaleNormalizedPoint
418 * Scales a normalized point (x, y) with respect to the box whose corners are
419 * passed in "corners". The point is stored in "*pPoint". The normalized
420 * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
421 * (1.0, 1.0) correspond to corners[1].
423 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
424 double y, POINT *pPoint)
426 pPoint->x=GDI_ROUND( (double)corners[0].x + (double)(corners[1].x-corners[0].x)*0.5*(x+1.0) );
427 pPoint->y=GDI_ROUND( (double)corners[0].y + (double)(corners[1].y-corners[0].y)*0.5*(y+1.0) );
430 /* PATH_NormalizePoint
432 * Normalizes a point with respect to the box whose corners are passed in
433 * "corners". The normalized coordinates are stored in "*pX" and "*pY".
435 static void PATH_NormalizePoint(FLOAT_POINT corners[],
436 const FLOAT_POINT *pPoint,
437 double *pX, double *pY)
439 *pX=(double)(pPoint->x-corners[0].x)/(double)(corners[1].x-corners[0].x) * 2.0 - 1.0;
440 *pY=(double)(pPoint->y-corners[0].y)/(double)(corners[1].y-corners[0].y) * 2.0 - 1.0;
443 /* PATH_DoArcPart
445 * Creates a Bezier spline that corresponds to part of an arc and appends the
446 * corresponding points to the path. The start and end angles are passed in
447 * "angleStart" and "angleEnd"; these angles should span a quarter circle
448 * at most. If "startEntryType" is non-zero, an entry of that type for the first
449 * control point is added to the path; otherwise, it is assumed that the current
450 * position is equal to the first control point.
452 static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
453 double angleStart, double angleEnd, BYTE startEntryType)
455 double halfAngle, a;
456 double xNorm[4], yNorm[4];
457 POINT point;
458 int i;
460 assert(fabs(angleEnd-angleStart)<=M_PI_2);
462 /* FIXME: Is there an easier way of computing this? */
464 /* Compute control points */
465 halfAngle=(angleEnd-angleStart)/2.0;
466 if(fabs(halfAngle)>1e-8)
468 a=4.0/3.0*(1-cos(halfAngle))/sin(halfAngle);
469 xNorm[0]=cos(angleStart);
470 yNorm[0]=sin(angleStart);
471 xNorm[1]=xNorm[0] - a*yNorm[0];
472 yNorm[1]=yNorm[0] + a*xNorm[0];
473 xNorm[3]=cos(angleEnd);
474 yNorm[3]=sin(angleEnd);
475 xNorm[2]=xNorm[3] + a*yNorm[3];
476 yNorm[2]=yNorm[3] - a*xNorm[3];
478 else
479 for(i=0; i<4; i++)
481 xNorm[i]=cos(angleStart);
482 yNorm[i]=sin(angleStart);
485 /* Add starting point to path if desired */
486 if(startEntryType)
488 PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
489 if(!PATH_AddEntry(pPath, &point, startEntryType))
490 return FALSE;
493 /* Add remaining control points */
494 for(i=1; i<4; i++)
496 PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &point);
497 if(!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
498 return FALSE;
501 return TRUE;
505 /***********************************************************************
506 * BeginPath (GDI32.@)
508 BOOL WINAPI BeginPath(HDC hdc)
510 BOOL ret = FALSE;
511 DC *dc = get_dc_ptr( hdc );
513 if (dc)
515 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pBeginPath );
516 ret = physdev->funcs->pBeginPath( physdev );
517 release_dc_ptr( dc );
519 return ret;
523 /***********************************************************************
524 * EndPath (GDI32.@)
526 BOOL WINAPI EndPath(HDC hdc)
528 BOOL ret = FALSE;
529 DC *dc = get_dc_ptr( hdc );
531 if (dc)
533 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pEndPath );
534 ret = physdev->funcs->pEndPath( physdev );
535 release_dc_ptr( dc );
537 return ret;
541 /******************************************************************************
542 * AbortPath [GDI32.@]
543 * Closes and discards paths from device context
545 * NOTES
546 * Check that SetLastError is being called correctly
548 * PARAMS
549 * hdc [I] Handle to device context
551 * RETURNS
552 * Success: TRUE
553 * Failure: FALSE
555 BOOL WINAPI AbortPath( HDC hdc )
557 BOOL ret = FALSE;
558 DC *dc = get_dc_ptr( hdc );
560 if (dc)
562 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pAbortPath );
563 ret = physdev->funcs->pAbortPath( physdev );
564 release_dc_ptr( dc );
566 return ret;
570 /***********************************************************************
571 * CloseFigure (GDI32.@)
573 * FIXME: Check that SetLastError is being called correctly
575 BOOL WINAPI CloseFigure(HDC hdc)
577 BOOL ret = FALSE;
578 DC *dc = get_dc_ptr( hdc );
580 if (dc)
582 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pCloseFigure );
583 ret = physdev->funcs->pCloseFigure( physdev );
584 release_dc_ptr( dc );
586 return ret;
590 /***********************************************************************
591 * GetPath (GDI32.@)
593 INT WINAPI GetPath(HDC hdc, LPPOINT pPoints, LPBYTE pTypes,
594 INT nSize)
596 INT ret = -1;
597 GdiPath *pPath;
598 DC *dc = get_dc_ptr( hdc );
600 if(!dc) return -1;
602 pPath = &dc->path;
604 /* Check that path is closed */
605 if(pPath->state!=PATH_Closed)
607 SetLastError(ERROR_CAN_NOT_COMPLETE);
608 goto done;
611 if(nSize==0)
612 ret = pPath->numEntriesUsed;
613 else if(nSize<pPath->numEntriesUsed)
615 SetLastError(ERROR_INVALID_PARAMETER);
616 goto done;
618 else
620 memcpy(pPoints, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
621 memcpy(pTypes, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
623 /* Convert the points to logical coordinates */
624 if(!DPtoLP(hdc, pPoints, pPath->numEntriesUsed))
626 /* FIXME: Is this the correct value? */
627 SetLastError(ERROR_CAN_NOT_COMPLETE);
628 goto done;
630 else ret = pPath->numEntriesUsed;
632 done:
633 release_dc_ptr( dc );
634 return ret;
638 /***********************************************************************
639 * PathToRegion (GDI32.@)
641 * FIXME
642 * Check that SetLastError is being called correctly
644 * The documentation does not state this explicitly, but a test under Windows
645 * shows that the region which is returned should be in device coordinates.
647 HRGN WINAPI PathToRegion(HDC hdc)
649 GdiPath *pPath;
650 HRGN hrgnRval = 0;
651 DC *dc = get_dc_ptr( hdc );
653 /* Get pointer to path */
654 if(!dc) return 0;
656 pPath = &dc->path;
658 /* Check that path is closed */
659 if(pPath->state!=PATH_Closed) SetLastError(ERROR_CAN_NOT_COMPLETE);
660 else
662 /* FIXME: Should we empty the path even if conversion failed? */
663 if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnRval))
664 PATH_EmptyPath(pPath);
665 else
666 hrgnRval=0;
668 release_dc_ptr( dc );
669 return hrgnRval;
672 static BOOL PATH_FillPath(DC *dc, GdiPath *pPath)
674 INT mapMode, graphicsMode;
675 SIZE ptViewportExt, ptWindowExt;
676 POINT ptViewportOrg, ptWindowOrg;
677 XFORM xform;
678 HRGN hrgn;
680 /* Construct a region from the path and fill it */
681 if(PATH_PathToRegion(pPath, dc->polyFillMode, &hrgn))
683 /* Since PaintRgn interprets the region as being in logical coordinates
684 * but the points we store for the path are already in device
685 * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
686 * Using SaveDC to save information about the mapping mode / world
687 * transform would be easier but would require more overhead, especially
688 * now that SaveDC saves the current path.
691 /* Save the information about the old mapping mode */
692 mapMode=GetMapMode(dc->hSelf);
693 GetViewportExtEx(dc->hSelf, &ptViewportExt);
694 GetViewportOrgEx(dc->hSelf, &ptViewportOrg);
695 GetWindowExtEx(dc->hSelf, &ptWindowExt);
696 GetWindowOrgEx(dc->hSelf, &ptWindowOrg);
698 /* Save world transform
699 * NB: The Windows documentation on world transforms would lead one to
700 * believe that this has to be done only in GM_ADVANCED; however, my
701 * tests show that resetting the graphics mode to GM_COMPATIBLE does
702 * not reset the world transform.
704 GetWorldTransform(dc->hSelf, &xform);
706 /* Set MM_TEXT */
707 SetMapMode(dc->hSelf, MM_TEXT);
708 SetViewportOrgEx(dc->hSelf, 0, 0, NULL);
709 SetWindowOrgEx(dc->hSelf, 0, 0, NULL);
710 graphicsMode=GetGraphicsMode(dc->hSelf);
711 SetGraphicsMode(dc->hSelf, GM_ADVANCED);
712 ModifyWorldTransform(dc->hSelf, &xform, MWT_IDENTITY);
713 SetGraphicsMode(dc->hSelf, graphicsMode);
715 /* Paint the region */
716 PaintRgn(dc->hSelf, hrgn);
717 DeleteObject(hrgn);
718 /* Restore the old mapping mode */
719 SetMapMode(dc->hSelf, mapMode);
720 SetViewportExtEx(dc->hSelf, ptViewportExt.cx, ptViewportExt.cy, NULL);
721 SetViewportOrgEx(dc->hSelf, ptViewportOrg.x, ptViewportOrg.y, NULL);
722 SetWindowExtEx(dc->hSelf, ptWindowExt.cx, ptWindowExt.cy, NULL);
723 SetWindowOrgEx(dc->hSelf, ptWindowOrg.x, ptWindowOrg.y, NULL);
725 /* Go to GM_ADVANCED temporarily to restore the world transform */
726 graphicsMode=GetGraphicsMode(dc->hSelf);
727 SetGraphicsMode(dc->hSelf, GM_ADVANCED);
728 SetWorldTransform(dc->hSelf, &xform);
729 SetGraphicsMode(dc->hSelf, graphicsMode);
730 return TRUE;
732 return FALSE;
736 /***********************************************************************
737 * FillPath (GDI32.@)
739 * FIXME
740 * Check that SetLastError is being called correctly
742 BOOL WINAPI FillPath(HDC hdc)
744 BOOL ret = FALSE;
745 DC *dc = get_dc_ptr( hdc );
747 if (dc)
749 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pFillPath );
750 ret = physdev->funcs->pFillPath( physdev );
751 release_dc_ptr( dc );
753 return ret;
757 /***********************************************************************
758 * SelectClipPath (GDI32.@)
759 * FIXME
760 * Check that SetLastError is being called correctly
762 BOOL WINAPI SelectClipPath(HDC hdc, INT iMode)
764 BOOL ret = FALSE;
765 DC *dc = get_dc_ptr( hdc );
767 if (dc)
769 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pSelectClipPath );
770 ret = physdev->funcs->pSelectClipPath( physdev, iMode );
771 release_dc_ptr( dc );
773 return ret;
777 /***********************************************************************
778 * pathdrv_AbortPath
780 static BOOL pathdrv_AbortPath( PHYSDEV dev )
782 DC *dc = get_dc_ptr( dev->hdc );
784 if (!dc) return FALSE;
785 PATH_EmptyPath( &dc->path );
786 pop_path_driver( dc );
787 release_dc_ptr( dc );
788 return TRUE;
792 /***********************************************************************
793 * pathdrv_EndPath
795 static BOOL pathdrv_EndPath( PHYSDEV dev )
797 DC *dc = get_dc_ptr( dev->hdc );
799 if (!dc) return FALSE;
800 dc->path.state = PATH_Closed;
801 pop_path_driver( dc );
802 release_dc_ptr( dc );
803 return TRUE;
807 /***********************************************************************
808 * pathdrv_CreateDC
810 static BOOL pathdrv_CreateDC( PHYSDEV *dev, LPCWSTR driver, LPCWSTR device,
811 LPCWSTR output, const DEVMODEW *devmode )
813 struct path_physdev *physdev = HeapAlloc( GetProcessHeap(), 0, sizeof(*physdev) );
814 DC *dc;
816 if (!physdev) return FALSE;
817 dc = get_dc_ptr( (*dev)->hdc );
818 physdev->path = &dc->path;
819 push_dc_driver( dev, &physdev->dev, &path_driver );
820 release_dc_ptr( dc );
821 return TRUE;
825 /*************************************************************
826 * pathdrv_DeleteDC
828 static BOOL pathdrv_DeleteDC( PHYSDEV dev )
830 assert( 0 ); /* should never be called */
831 return TRUE;
835 /* PATH_InitGdiPath
837 * Initializes the GdiPath structure.
839 void PATH_InitGdiPath(GdiPath *pPath)
841 assert(pPath!=NULL);
843 pPath->state=PATH_Null;
844 pPath->pPoints=NULL;
845 pPath->pFlags=NULL;
846 pPath->numEntriesUsed=0;
847 pPath->numEntriesAllocated=0;
850 /* PATH_DestroyGdiPath
852 * Destroys a GdiPath structure (frees the memory in the arrays).
854 void PATH_DestroyGdiPath(GdiPath *pPath)
856 assert(pPath!=NULL);
858 HeapFree( GetProcessHeap(), 0, pPath->pPoints );
859 HeapFree( GetProcessHeap(), 0, pPath->pFlags );
862 BOOL PATH_SavePath( DC *dst, DC *src )
864 PATH_InitGdiPath( &dst->path );
865 return PATH_AssignGdiPath( &dst->path, &src->path );
868 BOOL PATH_RestorePath( DC *dst, DC *src )
870 BOOL ret;
872 if (src->path.state == PATH_Open && dst->path.state != PATH_Open)
874 if (!path_driver.pCreateDC( &dst->physDev, NULL, NULL, NULL, NULL )) return FALSE;
875 ret = PATH_AssignGdiPath( &dst->path, &src->path );
876 if (!ret) pop_path_driver( dst );
878 else if (src->path.state != PATH_Open && dst->path.state == PATH_Open)
880 ret = PATH_AssignGdiPath( &dst->path, &src->path );
881 if (ret) pop_path_driver( dst );
883 else ret = PATH_AssignGdiPath( &dst->path, &src->path );
884 return ret;
888 /*************************************************************
889 * pathdrv_MoveTo
891 static BOOL pathdrv_MoveTo( PHYSDEV dev, INT x, INT y )
893 struct path_physdev *physdev = get_path_physdev( dev );
894 physdev->path->newStroke = TRUE;
895 return TRUE;
899 /*************************************************************
900 * pathdrv_LineTo
902 static BOOL pathdrv_LineTo( PHYSDEV dev, INT x, INT y )
904 struct path_physdev *physdev = get_path_physdev( dev );
905 POINT point, pointCurPos;
907 /* Convert point to device coordinates */
908 point.x = x;
909 point.y = y;
910 LPtoDP( dev->hdc, &point, 1 );
912 /* Add a PT_MOVETO if necessary */
913 if(physdev->path->newStroke)
915 physdev->path->newStroke = FALSE;
916 GetCurrentPositionEx( dev->hdc, &pointCurPos );
917 LPtoDP( dev->hdc, &pointCurPos, 1 );
918 if(!PATH_AddEntry(physdev->path, &pointCurPos, PT_MOVETO))
919 return FALSE;
922 /* Add a PT_LINETO entry */
923 return PATH_AddEntry(physdev->path, &point, PT_LINETO);
926 /* PATH_RoundRect
928 * Should be called when a call to RoundRect is performed on a DC that has
929 * an open path. Returns TRUE if successful, else FALSE.
931 * FIXME: it adds the same entries to the path as windows does, but there
932 * is an error in the bezier drawing code so that there are small pixel-size
933 * gaps when the resulting path is drawn by StrokePath()
935 BOOL PATH_RoundRect(DC *dc, INT x1, INT y1, INT x2, INT y2, INT ell_width, INT ell_height)
937 GdiPath *pPath = &dc->path;
938 POINT corners[2], pointTemp;
939 FLOAT_POINT ellCorners[2];
941 /* Check that path is open */
942 if(pPath->state!=PATH_Open)
943 return FALSE;
945 if(!PATH_CheckCorners(dc,corners,x1,y1,x2,y2))
946 return FALSE;
948 /* Add points to the roundrect path */
949 ellCorners[0].x = corners[1].x-ell_width;
950 ellCorners[0].y = corners[0].y;
951 ellCorners[1].x = corners[1].x;
952 ellCorners[1].y = corners[0].y+ell_height;
953 if(!PATH_DoArcPart(pPath, ellCorners, 0, -M_PI_2, PT_MOVETO))
954 return FALSE;
955 pointTemp.x = corners[0].x+ell_width/2;
956 pointTemp.y = corners[0].y;
957 if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
958 return FALSE;
959 ellCorners[0].x = corners[0].x;
960 ellCorners[1].x = corners[0].x+ell_width;
961 if(!PATH_DoArcPart(pPath, ellCorners, -M_PI_2, -M_PI, FALSE))
962 return FALSE;
963 pointTemp.x = corners[0].x;
964 pointTemp.y = corners[1].y-ell_height/2;
965 if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
966 return FALSE;
967 ellCorners[0].y = corners[1].y-ell_height;
968 ellCorners[1].y = corners[1].y;
969 if(!PATH_DoArcPart(pPath, ellCorners, M_PI, M_PI_2, FALSE))
970 return FALSE;
971 pointTemp.x = corners[1].x-ell_width/2;
972 pointTemp.y = corners[1].y;
973 if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
974 return FALSE;
975 ellCorners[0].x = corners[1].x-ell_width;
976 ellCorners[1].x = corners[1].x;
977 if(!PATH_DoArcPart(pPath, ellCorners, M_PI_2, 0, FALSE))
978 return FALSE;
980 /* Close the roundrect figure */
981 if(!CloseFigure(dc->hSelf))
982 return FALSE;
984 return TRUE;
987 /* PATH_Rectangle
989 * Should be called when a call to Rectangle is performed on a DC that has
990 * an open path. Returns TRUE if successful, else FALSE.
992 BOOL PATH_Rectangle(DC *dc, INT x1, INT y1, INT x2, INT y2)
994 GdiPath *pPath = &dc->path;
995 POINT corners[2], pointTemp;
997 /* Check that path is open */
998 if(pPath->state!=PATH_Open)
999 return FALSE;
1001 if(!PATH_CheckCorners(dc,corners,x1,y1,x2,y2))
1002 return FALSE;
1004 /* Close any previous figure */
1005 if(!CloseFigure(dc->hSelf))
1007 /* The CloseFigure call shouldn't have failed */
1008 assert(FALSE);
1009 return FALSE;
1012 /* Add four points to the path */
1013 pointTemp.x=corners[1].x;
1014 pointTemp.y=corners[0].y;
1015 if(!PATH_AddEntry(pPath, &pointTemp, PT_MOVETO))
1016 return FALSE;
1017 if(!PATH_AddEntry(pPath, corners, PT_LINETO))
1018 return FALSE;
1019 pointTemp.x=corners[0].x;
1020 pointTemp.y=corners[1].y;
1021 if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
1022 return FALSE;
1023 if(!PATH_AddEntry(pPath, corners+1, PT_LINETO))
1024 return FALSE;
1026 /* Close the rectangle figure */
1027 if(!CloseFigure(dc->hSelf))
1029 /* The CloseFigure call shouldn't have failed */
1030 assert(FALSE);
1031 return FALSE;
1034 return TRUE;
1037 /* PATH_Ellipse
1039 * Should be called when a call to Ellipse is performed on a DC that has
1040 * an open path. This adds four Bezier splines representing the ellipse
1041 * to the path. Returns TRUE if successful, else FALSE.
1043 BOOL PATH_Ellipse(DC *dc, INT x1, INT y1, INT x2, INT y2)
1045 return( PATH_Arc(dc, x1, y1, x2, y2, x1, (y1+y2)/2, x1, (y1+y2)/2,0) &&
1046 CloseFigure(dc->hSelf) );
1049 /* PATH_Arc
1051 * Should be called when a call to Arc is performed on a DC that has
1052 * an open path. This adds up to five Bezier splines representing the arc
1053 * to the path. When 'lines' is 1, we add 1 extra line to get a chord,
1054 * when 'lines' is 2, we add 2 extra lines to get a pie, and when 'lines' is
1055 * -1 we add 1 extra line from the current DC position to the starting position
1056 * of the arc before drawing the arc itself (arcto). Returns TRUE if successful,
1057 * else FALSE.
1059 BOOL PATH_Arc(DC *dc, INT x1, INT y1, INT x2, INT y2,
1060 INT xStart, INT yStart, INT xEnd, INT yEnd, INT lines)
1062 GdiPath *pPath = &dc->path;
1063 double angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
1064 /* Initialize angleEndQuadrant to silence gcc's warning */
1065 double x, y;
1066 FLOAT_POINT corners[2], pointStart, pointEnd;
1067 POINT centre, pointCurPos;
1068 BOOL start, end;
1069 INT temp;
1071 /* FIXME: This function should check for all possible error returns */
1072 /* FIXME: Do we have to respect newStroke? */
1074 /* Check that path is open */
1075 if(pPath->state!=PATH_Open)
1076 return FALSE;
1078 /* Check for zero height / width */
1079 /* FIXME: Only in GM_COMPATIBLE? */
1080 if(x1==x2 || y1==y2)
1081 return TRUE;
1083 /* Convert points to device coordinates */
1084 corners[0].x = x1;
1085 corners[0].y = y1;
1086 corners[1].x = x2;
1087 corners[1].y = y2;
1088 pointStart.x = xStart;
1089 pointStart.y = yStart;
1090 pointEnd.x = xEnd;
1091 pointEnd.y = yEnd;
1092 INTERNAL_LPTODP_FLOAT(dc, corners);
1093 INTERNAL_LPTODP_FLOAT(dc, corners+1);
1094 INTERNAL_LPTODP_FLOAT(dc, &pointStart);
1095 INTERNAL_LPTODP_FLOAT(dc, &pointEnd);
1097 /* Make sure first corner is top left and second corner is bottom right */
1098 if(corners[0].x>corners[1].x)
1100 temp=corners[0].x;
1101 corners[0].x=corners[1].x;
1102 corners[1].x=temp;
1104 if(corners[0].y>corners[1].y)
1106 temp=corners[0].y;
1107 corners[0].y=corners[1].y;
1108 corners[1].y=temp;
1111 /* Compute start and end angle */
1112 PATH_NormalizePoint(corners, &pointStart, &x, &y);
1113 angleStart=atan2(y, x);
1114 PATH_NormalizePoint(corners, &pointEnd, &x, &y);
1115 angleEnd=atan2(y, x);
1117 /* Make sure the end angle is "on the right side" of the start angle */
1118 if(dc->ArcDirection==AD_CLOCKWISE)
1120 if(angleEnd<=angleStart)
1122 angleEnd+=2*M_PI;
1123 assert(angleEnd>=angleStart);
1126 else
1128 if(angleEnd>=angleStart)
1130 angleEnd-=2*M_PI;
1131 assert(angleEnd<=angleStart);
1135 /* In GM_COMPATIBLE, don't include bottom and right edges */
1136 if(dc->GraphicsMode==GM_COMPATIBLE)
1138 corners[1].x--;
1139 corners[1].y--;
1142 /* arcto: Add a PT_MOVETO only if this is the first entry in a stroke */
1143 if(lines==-1 && pPath->newStroke)
1145 pPath->newStroke=FALSE;
1146 pointCurPos.x = dc->CursPosX;
1147 pointCurPos.y = dc->CursPosY;
1148 if(!LPtoDP(dc->hSelf, &pointCurPos, 1))
1149 return FALSE;
1150 if(!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
1151 return FALSE;
1154 /* Add the arc to the path with one Bezier spline per quadrant that the
1155 * arc spans */
1156 start=TRUE;
1157 end=FALSE;
1160 /* Determine the start and end angles for this quadrant */
1161 if(start)
1163 angleStartQuadrant=angleStart;
1164 if(dc->ArcDirection==AD_CLOCKWISE)
1165 angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
1166 else
1167 angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
1169 else
1171 angleStartQuadrant=angleEndQuadrant;
1172 if(dc->ArcDirection==AD_CLOCKWISE)
1173 angleEndQuadrant+=M_PI_2;
1174 else
1175 angleEndQuadrant-=M_PI_2;
1178 /* Have we reached the last part of the arc? */
1179 if((dc->ArcDirection==AD_CLOCKWISE &&
1180 angleEnd<angleEndQuadrant) ||
1181 (dc->ArcDirection==AD_COUNTERCLOCKWISE &&
1182 angleEnd>angleEndQuadrant))
1184 /* Adjust the end angle for this quadrant */
1185 angleEndQuadrant=angleEnd;
1186 end=TRUE;
1189 /* Add the Bezier spline to the path */
1190 PATH_DoArcPart(pPath, corners, angleStartQuadrant, angleEndQuadrant,
1191 start ? (lines==-1 ? PT_LINETO : PT_MOVETO) : FALSE);
1192 start=FALSE;
1193 } while(!end);
1195 /* chord: close figure. pie: add line and close figure */
1196 if(lines==1)
1198 if(!CloseFigure(dc->hSelf))
1199 return FALSE;
1201 else if(lines==2)
1203 centre.x = (corners[0].x+corners[1].x)/2;
1204 centre.y = (corners[0].y+corners[1].y)/2;
1205 if(!PATH_AddEntry(pPath, &centre, PT_LINETO | PT_CLOSEFIGURE))
1206 return FALSE;
1209 return TRUE;
1212 BOOL PATH_PolyBezierTo(DC *dc, const POINT *pts, DWORD cbPoints)
1214 GdiPath *pPath = &dc->path;
1215 POINT pt;
1216 UINT i;
1218 /* Check that path is open */
1219 if(pPath->state!=PATH_Open)
1220 return FALSE;
1222 /* Add a PT_MOVETO if necessary */
1223 if(pPath->newStroke)
1225 pPath->newStroke=FALSE;
1226 pt.x = dc->CursPosX;
1227 pt.y = dc->CursPosY;
1228 if(!LPtoDP(dc->hSelf, &pt, 1))
1229 return FALSE;
1230 if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
1231 return FALSE;
1234 for(i = 0; i < cbPoints; i++) {
1235 pt = pts[i];
1236 if(!LPtoDP(dc->hSelf, &pt, 1))
1237 return FALSE;
1238 PATH_AddEntry(pPath, &pt, PT_BEZIERTO);
1240 return TRUE;
1243 BOOL PATH_PolyBezier(DC *dc, const POINT *pts, DWORD cbPoints)
1245 GdiPath *pPath = &dc->path;
1246 POINT pt;
1247 UINT i;
1249 /* Check that path is open */
1250 if(pPath->state!=PATH_Open)
1251 return FALSE;
1253 for(i = 0; i < cbPoints; i++) {
1254 pt = pts[i];
1255 if(!LPtoDP(dc->hSelf, &pt, 1))
1256 return FALSE;
1257 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_BEZIERTO);
1259 return TRUE;
1262 /* PATH_PolyDraw
1264 * Should be called when a call to PolyDraw is performed on a DC that has
1265 * an open path. Returns TRUE if successful, else FALSE.
1267 BOOL PATH_PolyDraw(DC *dc, const POINT *pts, const BYTE *types,
1268 DWORD cbPoints)
1270 GdiPath *pPath = &dc->path;
1271 POINT lastmove, orig_pos;
1272 INT i;
1274 GetCurrentPositionEx( dc->hSelf, &orig_pos );
1275 lastmove = orig_pos;
1277 for(i = pPath->numEntriesUsed - 1; i >= 0; i--){
1278 if(pPath->pFlags[i] == PT_MOVETO){
1279 lastmove = pPath->pPoints[i];
1280 DPtoLP(dc->hSelf, &lastmove, 1);
1281 break;
1285 for(i = 0; i < cbPoints; i++)
1287 switch (types[i])
1289 case PT_MOVETO:
1290 MoveToEx( dc->hSelf, pts[i].x, pts[i].y, NULL );
1291 break;
1292 case PT_LINETO:
1293 case PT_LINETO | PT_CLOSEFIGURE:
1294 LineTo( dc->hSelf, pts[i].x, pts[i].y );
1295 break;
1296 case PT_BEZIERTO:
1297 if ((i + 2 < cbPoints) && (types[i + 1] == PT_BEZIERTO) &&
1298 (types[i + 2] & ~PT_CLOSEFIGURE) == PT_BEZIERTO)
1300 PolyBezierTo( dc->hSelf, &pts[i], 3 );
1301 i += 2;
1302 break;
1304 /* fall through */
1305 default:
1306 if (i) /* restore original position */
1308 if (!(types[i - 1] & PT_CLOSEFIGURE)) lastmove = pts[i - 1];
1309 if (lastmove.x != orig_pos.x || lastmove.y != orig_pos.y)
1310 MoveToEx( dc->hSelf, orig_pos.x, orig_pos.y, NULL );
1312 return FALSE;
1315 if(types[i] & PT_CLOSEFIGURE){
1316 pPath->pFlags[pPath->numEntriesUsed-1] |= PT_CLOSEFIGURE;
1317 MoveToEx( dc->hSelf, lastmove.x, lastmove.y, NULL );
1321 return TRUE;
1324 BOOL PATH_Polyline(DC *dc, const POINT *pts, DWORD cbPoints)
1326 GdiPath *pPath = &dc->path;
1327 POINT pt;
1328 UINT i;
1330 /* Check that path is open */
1331 if(pPath->state!=PATH_Open)
1332 return FALSE;
1334 for(i = 0; i < cbPoints; i++) {
1335 pt = pts[i];
1336 if(!LPtoDP(dc->hSelf, &pt, 1))
1337 return FALSE;
1338 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_LINETO);
1340 return TRUE;
1343 BOOL PATH_PolylineTo(DC *dc, const POINT *pts, DWORD cbPoints)
1345 GdiPath *pPath = &dc->path;
1346 POINT pt;
1347 UINT i;
1349 /* Check that path is open */
1350 if(pPath->state!=PATH_Open)
1351 return FALSE;
1353 /* Add a PT_MOVETO if necessary */
1354 if(pPath->newStroke)
1356 pPath->newStroke=FALSE;
1357 pt.x = dc->CursPosX;
1358 pt.y = dc->CursPosY;
1359 if(!LPtoDP(dc->hSelf, &pt, 1))
1360 return FALSE;
1361 if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
1362 return FALSE;
1365 for(i = 0; i < cbPoints; i++) {
1366 pt = pts[i];
1367 if(!LPtoDP(dc->hSelf, &pt, 1))
1368 return FALSE;
1369 PATH_AddEntry(pPath, &pt, PT_LINETO);
1372 return TRUE;
1376 BOOL PATH_Polygon(DC *dc, const POINT *pts, DWORD cbPoints)
1378 GdiPath *pPath = &dc->path;
1379 POINT pt;
1380 UINT i;
1382 /* Check that path is open */
1383 if(pPath->state!=PATH_Open)
1384 return FALSE;
1386 for(i = 0; i < cbPoints; i++) {
1387 pt = pts[i];
1388 if(!LPtoDP(dc->hSelf, &pt, 1))
1389 return FALSE;
1390 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO :
1391 ((i == cbPoints-1) ? PT_LINETO | PT_CLOSEFIGURE :
1392 PT_LINETO));
1394 return TRUE;
1397 BOOL PATH_PolyPolygon( DC *dc, const POINT* pts, const INT* counts,
1398 UINT polygons )
1400 GdiPath *pPath = &dc->path;
1401 POINT pt, startpt;
1402 UINT poly, i;
1403 INT point;
1405 /* Check that path is open */
1406 if(pPath->state!=PATH_Open)
1407 return FALSE;
1409 for(i = 0, poly = 0; poly < polygons; poly++) {
1410 for(point = 0; point < counts[poly]; point++, i++) {
1411 pt = pts[i];
1412 if(!LPtoDP(dc->hSelf, &pt, 1))
1413 return FALSE;
1414 if(point == 0) startpt = pt;
1415 PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1417 /* win98 adds an extra line to close the figure for some reason */
1418 PATH_AddEntry(pPath, &startpt, PT_LINETO | PT_CLOSEFIGURE);
1420 return TRUE;
1423 BOOL PATH_PolyPolyline( DC *dc, const POINT* pts, const DWORD* counts,
1424 DWORD polylines )
1426 GdiPath *pPath = &dc->path;
1427 POINT pt;
1428 UINT poly, point, i;
1430 /* Check that path is open */
1431 if(pPath->state!=PATH_Open)
1432 return FALSE;
1434 for(i = 0, poly = 0; poly < polylines; poly++) {
1435 for(point = 0; point < counts[poly]; point++, i++) {
1436 pt = pts[i];
1437 if(!LPtoDP(dc->hSelf, &pt, 1))
1438 return FALSE;
1439 PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1442 return TRUE;
1446 /**********************************************************************
1447 * PATH_BezierTo
1449 * internally used by PATH_add_outline
1451 static void PATH_BezierTo(GdiPath *pPath, POINT *lppt, INT n)
1453 if (n < 2) return;
1455 if (n == 2)
1457 PATH_AddEntry(pPath, &lppt[1], PT_LINETO);
1459 else if (n == 3)
1461 PATH_AddEntry(pPath, &lppt[0], PT_BEZIERTO);
1462 PATH_AddEntry(pPath, &lppt[1], PT_BEZIERTO);
1463 PATH_AddEntry(pPath, &lppt[2], PT_BEZIERTO);
1465 else
1467 POINT pt[3];
1468 INT i = 0;
1470 pt[2] = lppt[0];
1471 n--;
1473 while (n > 2)
1475 pt[0] = pt[2];
1476 pt[1] = lppt[i+1];
1477 pt[2].x = (lppt[i+2].x + lppt[i+1].x) / 2;
1478 pt[2].y = (lppt[i+2].y + lppt[i+1].y) / 2;
1479 PATH_BezierTo(pPath, pt, 3);
1480 n--;
1481 i++;
1484 pt[0] = pt[2];
1485 pt[1] = lppt[i+1];
1486 pt[2] = lppt[i+2];
1487 PATH_BezierTo(pPath, pt, 3);
1491 static BOOL PATH_add_outline(DC *dc, INT x, INT y, TTPOLYGONHEADER *header, DWORD size)
1493 GdiPath *pPath = &dc->path;
1494 TTPOLYGONHEADER *start;
1495 POINT pt;
1497 start = header;
1499 while ((char *)header < (char *)start + size)
1501 TTPOLYCURVE *curve;
1503 if (header->dwType != TT_POLYGON_TYPE)
1505 FIXME("Unknown header type %d\n", header->dwType);
1506 return FALSE;
1509 pt.x = x + int_from_fixed(header->pfxStart.x);
1510 pt.y = y - int_from_fixed(header->pfxStart.y);
1511 PATH_AddEntry(pPath, &pt, PT_MOVETO);
1513 curve = (TTPOLYCURVE *)(header + 1);
1515 while ((char *)curve < (char *)header + header->cb)
1517 /*TRACE("curve->wType %d\n", curve->wType);*/
1519 switch(curve->wType)
1521 case TT_PRIM_LINE:
1523 WORD i;
1525 for (i = 0; i < curve->cpfx; i++)
1527 pt.x = x + int_from_fixed(curve->apfx[i].x);
1528 pt.y = y - int_from_fixed(curve->apfx[i].y);
1529 PATH_AddEntry(pPath, &pt, PT_LINETO);
1531 break;
1534 case TT_PRIM_QSPLINE:
1535 case TT_PRIM_CSPLINE:
1537 WORD i;
1538 POINTFX ptfx;
1539 POINT *pts = HeapAlloc(GetProcessHeap(), 0, (curve->cpfx + 1) * sizeof(POINT));
1541 if (!pts) return FALSE;
1543 ptfx = *(POINTFX *)((char *)curve - sizeof(POINTFX));
1545 pts[0].x = x + int_from_fixed(ptfx.x);
1546 pts[0].y = y - int_from_fixed(ptfx.y);
1548 for(i = 0; i < curve->cpfx; i++)
1550 pts[i + 1].x = x + int_from_fixed(curve->apfx[i].x);
1551 pts[i + 1].y = y - int_from_fixed(curve->apfx[i].y);
1554 PATH_BezierTo(pPath, pts, curve->cpfx + 1);
1556 HeapFree(GetProcessHeap(), 0, pts);
1557 break;
1560 default:
1561 FIXME("Unknown curve type %04x\n", curve->wType);
1562 return FALSE;
1565 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
1568 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
1571 return CloseFigure(dc->hSelf);
1574 /**********************************************************************
1575 * PATH_ExtTextOut
1577 BOOL PATH_ExtTextOut(DC *dc, INT x, INT y, UINT flags, const RECT *lprc,
1578 LPCWSTR str, UINT count, const INT *dx)
1580 unsigned int idx;
1581 HDC hdc = dc->hSelf;
1582 POINT offset = {0, 0};
1584 TRACE("%p, %d, %d, %08x, %s, %s, %d, %p)\n", hdc, x, y, flags,
1585 wine_dbgstr_rect(lprc), debugstr_wn(str, count), count, dx);
1587 if (!count) return TRUE;
1589 for (idx = 0; idx < count; idx++)
1591 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
1592 GLYPHMETRICS gm;
1593 DWORD dwSize;
1594 void *outline;
1596 dwSize = GetGlyphOutlineW(hdc, str[idx], GGO_GLYPH_INDEX | GGO_NATIVE, &gm, 0, NULL, &identity);
1597 if (dwSize == GDI_ERROR) return FALSE;
1599 /* add outline only if char is printable */
1600 if(dwSize)
1602 outline = HeapAlloc(GetProcessHeap(), 0, dwSize);
1603 if (!outline) return FALSE;
1605 GetGlyphOutlineW(hdc, str[idx], GGO_GLYPH_INDEX | GGO_NATIVE, &gm, dwSize, outline, &identity);
1607 PATH_add_outline(dc, x + offset.x, y + offset.y, outline, dwSize);
1609 HeapFree(GetProcessHeap(), 0, outline);
1612 if (dx)
1614 if(flags & ETO_PDY)
1616 offset.x += dx[idx * 2];
1617 offset.y += dx[idx * 2 + 1];
1619 else
1620 offset.x += dx[idx];
1622 else
1624 offset.x += gm.gmCellIncX;
1625 offset.y += gm.gmCellIncY;
1628 return TRUE;
1632 /*******************************************************************
1633 * FlattenPath [GDI32.@]
1637 BOOL WINAPI FlattenPath(HDC hdc)
1639 BOOL ret = FALSE;
1640 DC *dc = get_dc_ptr( hdc );
1642 if (dc)
1644 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pFlattenPath );
1645 ret = physdev->funcs->pFlattenPath( physdev );
1646 release_dc_ptr( dc );
1648 return ret;
1652 static BOOL PATH_StrokePath(DC *dc, GdiPath *pPath)
1654 INT i, nLinePts, nAlloc;
1655 POINT *pLinePts;
1656 POINT ptViewportOrg, ptWindowOrg;
1657 SIZE szViewportExt, szWindowExt;
1658 DWORD mapMode, graphicsMode;
1659 XFORM xform;
1660 BOOL ret = TRUE;
1662 /* Save the mapping mode info */
1663 mapMode=GetMapMode(dc->hSelf);
1664 GetViewportExtEx(dc->hSelf, &szViewportExt);
1665 GetViewportOrgEx(dc->hSelf, &ptViewportOrg);
1666 GetWindowExtEx(dc->hSelf, &szWindowExt);
1667 GetWindowOrgEx(dc->hSelf, &ptWindowOrg);
1668 GetWorldTransform(dc->hSelf, &xform);
1670 /* Set MM_TEXT */
1671 SetMapMode(dc->hSelf, MM_TEXT);
1672 SetViewportOrgEx(dc->hSelf, 0, 0, NULL);
1673 SetWindowOrgEx(dc->hSelf, 0, 0, NULL);
1674 graphicsMode=GetGraphicsMode(dc->hSelf);
1675 SetGraphicsMode(dc->hSelf, GM_ADVANCED);
1676 ModifyWorldTransform(dc->hSelf, &xform, MWT_IDENTITY);
1677 SetGraphicsMode(dc->hSelf, graphicsMode);
1679 /* Allocate enough memory for the worst case without beziers (one PT_MOVETO
1680 * and the rest PT_LINETO with PT_CLOSEFIGURE at the end) plus some buffer
1681 * space in case we get one to keep the number of reallocations small. */
1682 nAlloc = pPath->numEntriesUsed + 1 + 300;
1683 pLinePts = HeapAlloc(GetProcessHeap(), 0, nAlloc * sizeof(POINT));
1684 nLinePts = 0;
1686 for(i = 0; i < pPath->numEntriesUsed; i++) {
1687 if((i == 0 || (pPath->pFlags[i-1] & PT_CLOSEFIGURE)) &&
1688 (pPath->pFlags[i] != PT_MOVETO)) {
1689 ERR("Expected PT_MOVETO %s, got path flag %d\n",
1690 i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
1691 (INT)pPath->pFlags[i]);
1692 ret = FALSE;
1693 goto end;
1695 switch(pPath->pFlags[i]) {
1696 case PT_MOVETO:
1697 TRACE("Got PT_MOVETO (%d, %d)\n",
1698 pPath->pPoints[i].x, pPath->pPoints[i].y);
1699 if(nLinePts >= 2)
1700 Polyline(dc->hSelf, pLinePts, nLinePts);
1701 nLinePts = 0;
1702 pLinePts[nLinePts++] = pPath->pPoints[i];
1703 break;
1704 case PT_LINETO:
1705 case (PT_LINETO | PT_CLOSEFIGURE):
1706 TRACE("Got PT_LINETO (%d, %d)\n",
1707 pPath->pPoints[i].x, pPath->pPoints[i].y);
1708 pLinePts[nLinePts++] = pPath->pPoints[i];
1709 break;
1710 case PT_BEZIERTO:
1711 TRACE("Got PT_BEZIERTO\n");
1712 if(pPath->pFlags[i+1] != PT_BEZIERTO ||
1713 (pPath->pFlags[i+2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO) {
1714 ERR("Path didn't contain 3 successive PT_BEZIERTOs\n");
1715 ret = FALSE;
1716 goto end;
1717 } else {
1718 INT nBzrPts, nMinAlloc;
1719 POINT *pBzrPts = GDI_Bezier(&pPath->pPoints[i-1], 4, &nBzrPts);
1720 /* Make sure we have allocated enough memory for the lines of
1721 * this bezier and the rest of the path, assuming we won't get
1722 * another one (since we won't reallocate again then). */
1723 nMinAlloc = nLinePts + (pPath->numEntriesUsed - i) + nBzrPts;
1724 if(nAlloc < nMinAlloc)
1726 nAlloc = nMinAlloc * 2;
1727 pLinePts = HeapReAlloc(GetProcessHeap(), 0, pLinePts,
1728 nAlloc * sizeof(POINT));
1730 memcpy(&pLinePts[nLinePts], &pBzrPts[1],
1731 (nBzrPts - 1) * sizeof(POINT));
1732 nLinePts += nBzrPts - 1;
1733 HeapFree(GetProcessHeap(), 0, pBzrPts);
1734 i += 2;
1736 break;
1737 default:
1738 ERR("Got path flag %d\n", (INT)pPath->pFlags[i]);
1739 ret = FALSE;
1740 goto end;
1742 if(pPath->pFlags[i] & PT_CLOSEFIGURE)
1743 pLinePts[nLinePts++] = pLinePts[0];
1745 if(nLinePts >= 2)
1746 Polyline(dc->hSelf, pLinePts, nLinePts);
1748 end:
1749 HeapFree(GetProcessHeap(), 0, pLinePts);
1751 /* Restore the old mapping mode */
1752 SetMapMode(dc->hSelf, mapMode);
1753 SetWindowExtEx(dc->hSelf, szWindowExt.cx, szWindowExt.cy, NULL);
1754 SetWindowOrgEx(dc->hSelf, ptWindowOrg.x, ptWindowOrg.y, NULL);
1755 SetViewportExtEx(dc->hSelf, szViewportExt.cx, szViewportExt.cy, NULL);
1756 SetViewportOrgEx(dc->hSelf, ptViewportOrg.x, ptViewportOrg.y, NULL);
1758 /* Go to GM_ADVANCED temporarily to restore the world transform */
1759 graphicsMode=GetGraphicsMode(dc->hSelf);
1760 SetGraphicsMode(dc->hSelf, GM_ADVANCED);
1761 SetWorldTransform(dc->hSelf, &xform);
1762 SetGraphicsMode(dc->hSelf, graphicsMode);
1764 /* If we've moved the current point then get its new position
1765 which will be in device (MM_TEXT) co-ords, convert it to
1766 logical co-ords and re-set it. This basically updates
1767 dc->CurPosX|Y so that their values are in the correct mapping
1768 mode.
1770 if(i > 0) {
1771 POINT pt;
1772 GetCurrentPositionEx(dc->hSelf, &pt);
1773 DPtoLP(dc->hSelf, &pt, 1);
1774 MoveToEx(dc->hSelf, pt.x, pt.y, NULL);
1777 return ret;
1780 #define round(x) ((int)((x)>0?(x)+0.5:(x)-0.5))
1782 static BOOL PATH_WidenPath(DC *dc)
1784 INT i, j, numStrokes, penWidth, penWidthIn, penWidthOut, size, penStyle;
1785 BOOL ret = FALSE;
1786 GdiPath *pPath, *pNewPath, **pStrokes = NULL, *pUpPath, *pDownPath;
1787 EXTLOGPEN *elp;
1788 DWORD obj_type, joint, endcap, penType;
1790 pPath = &dc->path;
1792 PATH_FlattenPath(pPath);
1794 size = GetObjectW( dc->hPen, 0, NULL );
1795 if (!size) {
1796 SetLastError(ERROR_CAN_NOT_COMPLETE);
1797 return FALSE;
1800 elp = HeapAlloc( GetProcessHeap(), 0, size );
1801 GetObjectW( dc->hPen, size, elp );
1803 obj_type = GetObjectType(dc->hPen);
1804 if(obj_type == OBJ_PEN) {
1805 penStyle = ((LOGPEN*)elp)->lopnStyle;
1807 else if(obj_type == OBJ_EXTPEN) {
1808 penStyle = elp->elpPenStyle;
1810 else {
1811 SetLastError(ERROR_CAN_NOT_COMPLETE);
1812 HeapFree( GetProcessHeap(), 0, elp );
1813 return FALSE;
1816 penWidth = elp->elpWidth;
1817 HeapFree( GetProcessHeap(), 0, elp );
1819 endcap = (PS_ENDCAP_MASK & penStyle);
1820 joint = (PS_JOIN_MASK & penStyle);
1821 penType = (PS_TYPE_MASK & penStyle);
1823 /* The function cannot apply to cosmetic pens */
1824 if(obj_type == OBJ_EXTPEN && penType == PS_COSMETIC) {
1825 SetLastError(ERROR_CAN_NOT_COMPLETE);
1826 return FALSE;
1829 penWidthIn = penWidth / 2;
1830 penWidthOut = penWidth / 2;
1831 if(penWidthIn + penWidthOut < penWidth)
1832 penWidthOut++;
1834 numStrokes = 0;
1836 for(i = 0, j = 0; i < pPath->numEntriesUsed; i++, j++) {
1837 POINT point;
1838 if((i == 0 || (pPath->pFlags[i-1] & PT_CLOSEFIGURE)) &&
1839 (pPath->pFlags[i] != PT_MOVETO)) {
1840 ERR("Expected PT_MOVETO %s, got path flag %c\n",
1841 i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
1842 pPath->pFlags[i]);
1843 return FALSE;
1845 switch(pPath->pFlags[i]) {
1846 case PT_MOVETO:
1847 if(numStrokes > 0) {
1848 pStrokes[numStrokes - 1]->state = PATH_Closed;
1850 numStrokes++;
1851 j = 0;
1852 if(numStrokes == 1)
1853 pStrokes = HeapAlloc(GetProcessHeap(), 0, sizeof(GdiPath*));
1854 else
1855 pStrokes = HeapReAlloc(GetProcessHeap(), 0, pStrokes, numStrokes * sizeof(GdiPath*));
1856 if(!pStrokes) return FALSE;
1857 pStrokes[numStrokes - 1] = HeapAlloc(GetProcessHeap(), 0, sizeof(GdiPath));
1858 PATH_InitGdiPath(pStrokes[numStrokes - 1]);
1859 pStrokes[numStrokes - 1]->state = PATH_Open;
1860 /* fall through */
1861 case PT_LINETO:
1862 case (PT_LINETO | PT_CLOSEFIGURE):
1863 point.x = pPath->pPoints[i].x;
1864 point.y = pPath->pPoints[i].y;
1865 PATH_AddEntry(pStrokes[numStrokes - 1], &point, pPath->pFlags[i]);
1866 break;
1867 case PT_BEZIERTO:
1868 /* should never happen because of the FlattenPath call */
1869 ERR("Should never happen\n");
1870 break;
1871 default:
1872 ERR("Got path flag %c\n", pPath->pFlags[i]);
1873 return FALSE;
1877 pNewPath = HeapAlloc(GetProcessHeap(), 0, sizeof(GdiPath));
1878 PATH_InitGdiPath(pNewPath);
1879 pNewPath->state = PATH_Open;
1881 for(i = 0; i < numStrokes; i++) {
1882 pUpPath = HeapAlloc(GetProcessHeap(), 0, sizeof(GdiPath));
1883 PATH_InitGdiPath(pUpPath);
1884 pUpPath->state = PATH_Open;
1885 pDownPath = HeapAlloc(GetProcessHeap(), 0, sizeof(GdiPath));
1886 PATH_InitGdiPath(pDownPath);
1887 pDownPath->state = PATH_Open;
1889 for(j = 0; j < pStrokes[i]->numEntriesUsed; j++) {
1890 /* Beginning or end of the path if not closed */
1891 if((!(pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE)) && (j == 0 || j == pStrokes[i]->numEntriesUsed - 1) ) {
1892 /* Compute segment angle */
1893 double xo, yo, xa, ya, theta;
1894 POINT pt;
1895 FLOAT_POINT corners[2];
1896 if(j == 0) {
1897 xo = pStrokes[i]->pPoints[j].x;
1898 yo = pStrokes[i]->pPoints[j].y;
1899 xa = pStrokes[i]->pPoints[1].x;
1900 ya = pStrokes[i]->pPoints[1].y;
1902 else {
1903 xa = pStrokes[i]->pPoints[j - 1].x;
1904 ya = pStrokes[i]->pPoints[j - 1].y;
1905 xo = pStrokes[i]->pPoints[j].x;
1906 yo = pStrokes[i]->pPoints[j].y;
1908 theta = atan2( ya - yo, xa - xo );
1909 switch(endcap) {
1910 case PS_ENDCAP_SQUARE :
1911 pt.x = xo + round(sqrt(2) * penWidthOut * cos(M_PI_4 + theta));
1912 pt.y = yo + round(sqrt(2) * penWidthOut * sin(M_PI_4 + theta));
1913 PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO) );
1914 pt.x = xo + round(sqrt(2) * penWidthIn * cos(- M_PI_4 + theta));
1915 pt.y = yo + round(sqrt(2) * penWidthIn * sin(- M_PI_4 + theta));
1916 PATH_AddEntry(pUpPath, &pt, PT_LINETO);
1917 break;
1918 case PS_ENDCAP_FLAT :
1919 pt.x = xo + round( penWidthOut * cos(theta + M_PI_2) );
1920 pt.y = yo + round( penWidthOut * sin(theta + M_PI_2) );
1921 PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
1922 pt.x = xo - round( penWidthIn * cos(theta + M_PI_2) );
1923 pt.y = yo - round( penWidthIn * sin(theta + M_PI_2) );
1924 PATH_AddEntry(pUpPath, &pt, PT_LINETO);
1925 break;
1926 case PS_ENDCAP_ROUND :
1927 default :
1928 corners[0].x = xo - penWidthIn;
1929 corners[0].y = yo - penWidthIn;
1930 corners[1].x = xo + penWidthOut;
1931 corners[1].y = yo + penWidthOut;
1932 PATH_DoArcPart(pUpPath ,corners, theta + M_PI_2 , theta + 3 * M_PI_4, (j == 0 ? PT_MOVETO : FALSE));
1933 PATH_DoArcPart(pUpPath ,corners, theta + 3 * M_PI_4 , theta + M_PI, FALSE);
1934 PATH_DoArcPart(pUpPath ,corners, theta + M_PI, theta + 5 * M_PI_4, FALSE);
1935 PATH_DoArcPart(pUpPath ,corners, theta + 5 * M_PI_4 , theta + 3 * M_PI_2, FALSE);
1936 break;
1939 /* Corpse of the path */
1940 else {
1941 /* Compute angle */
1942 INT previous, next;
1943 double xa, ya, xb, yb, xo, yo;
1944 double alpha, theta, miterWidth;
1945 DWORD _joint = joint;
1946 POINT pt;
1947 GdiPath *pInsidePath, *pOutsidePath;
1948 if(j > 0 && j < pStrokes[i]->numEntriesUsed - 1) {
1949 previous = j - 1;
1950 next = j + 1;
1952 else if (j == 0) {
1953 previous = pStrokes[i]->numEntriesUsed - 1;
1954 next = j + 1;
1956 else {
1957 previous = j - 1;
1958 next = 0;
1960 xo = pStrokes[i]->pPoints[j].x;
1961 yo = pStrokes[i]->pPoints[j].y;
1962 xa = pStrokes[i]->pPoints[previous].x;
1963 ya = pStrokes[i]->pPoints[previous].y;
1964 xb = pStrokes[i]->pPoints[next].x;
1965 yb = pStrokes[i]->pPoints[next].y;
1966 theta = atan2( yo - ya, xo - xa );
1967 alpha = atan2( yb - yo, xb - xo ) - theta;
1968 if (alpha > 0) alpha -= M_PI;
1969 else alpha += M_PI;
1970 if(_joint == PS_JOIN_MITER && dc->miterLimit < fabs(1 / sin(alpha/2))) {
1971 _joint = PS_JOIN_BEVEL;
1973 if(alpha > 0) {
1974 pInsidePath = pUpPath;
1975 pOutsidePath = pDownPath;
1977 else if(alpha < 0) {
1978 pInsidePath = pDownPath;
1979 pOutsidePath = pUpPath;
1981 else {
1982 continue;
1984 /* Inside angle points */
1985 if(alpha > 0) {
1986 pt.x = xo - round( penWidthIn * cos(theta + M_PI_2) );
1987 pt.y = yo - round( penWidthIn * sin(theta + M_PI_2) );
1989 else {
1990 pt.x = xo + round( penWidthIn * cos(theta + M_PI_2) );
1991 pt.y = yo + round( penWidthIn * sin(theta + M_PI_2) );
1993 PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
1994 if(alpha > 0) {
1995 pt.x = xo + round( penWidthIn * cos(M_PI_2 + alpha + theta) );
1996 pt.y = yo + round( penWidthIn * sin(M_PI_2 + alpha + theta) );
1998 else {
1999 pt.x = xo - round( penWidthIn * cos(M_PI_2 + alpha + theta) );
2000 pt.y = yo - round( penWidthIn * sin(M_PI_2 + alpha + theta) );
2002 PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
2003 /* Outside angle point */
2004 switch(_joint) {
2005 case PS_JOIN_MITER :
2006 miterWidth = fabs(penWidthOut / cos(M_PI_2 - fabs(alpha) / 2));
2007 pt.x = xo + round( miterWidth * cos(theta + alpha / 2) );
2008 pt.y = yo + round( miterWidth * sin(theta + alpha / 2) );
2009 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2010 break;
2011 case PS_JOIN_BEVEL :
2012 if(alpha > 0) {
2013 pt.x = xo + round( penWidthOut * cos(theta + M_PI_2) );
2014 pt.y = yo + round( penWidthOut * sin(theta + M_PI_2) );
2016 else {
2017 pt.x = xo - round( penWidthOut * cos(theta + M_PI_2) );
2018 pt.y = yo - round( penWidthOut * sin(theta + M_PI_2) );
2020 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2021 if(alpha > 0) {
2022 pt.x = xo - round( penWidthOut * cos(M_PI_2 + alpha + theta) );
2023 pt.y = yo - round( penWidthOut * sin(M_PI_2 + alpha + theta) );
2025 else {
2026 pt.x = xo + round( penWidthOut * cos(M_PI_2 + alpha + theta) );
2027 pt.y = yo + round( penWidthOut * sin(M_PI_2 + alpha + theta) );
2029 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2030 break;
2031 case PS_JOIN_ROUND :
2032 default :
2033 if(alpha > 0) {
2034 pt.x = xo + round( penWidthOut * cos(theta + M_PI_2) );
2035 pt.y = yo + round( penWidthOut * sin(theta + M_PI_2) );
2037 else {
2038 pt.x = xo - round( penWidthOut * cos(theta + M_PI_2) );
2039 pt.y = yo - round( penWidthOut * sin(theta + M_PI_2) );
2041 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2042 pt.x = xo + round( penWidthOut * cos(theta + alpha / 2) );
2043 pt.y = yo + round( penWidthOut * sin(theta + alpha / 2) );
2044 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2045 if(alpha > 0) {
2046 pt.x = xo - round( penWidthOut * cos(M_PI_2 + alpha + theta) );
2047 pt.y = yo - round( penWidthOut * sin(M_PI_2 + alpha + theta) );
2049 else {
2050 pt.x = xo + round( penWidthOut * cos(M_PI_2 + alpha + theta) );
2051 pt.y = yo + round( penWidthOut * sin(M_PI_2 + alpha + theta) );
2053 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2054 break;
2058 for(j = 0; j < pUpPath->numEntriesUsed; j++) {
2059 POINT pt;
2060 pt.x = pUpPath->pPoints[j].x;
2061 pt.y = pUpPath->pPoints[j].y;
2062 PATH_AddEntry(pNewPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
2064 for(j = 0; j < pDownPath->numEntriesUsed; j++) {
2065 POINT pt;
2066 pt.x = pDownPath->pPoints[pDownPath->numEntriesUsed - j - 1].x;
2067 pt.y = pDownPath->pPoints[pDownPath->numEntriesUsed - j - 1].y;
2068 PATH_AddEntry(pNewPath, &pt, ( (j == 0 && (pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE)) ? PT_MOVETO : PT_LINETO));
2071 PATH_DestroyGdiPath(pStrokes[i]);
2072 HeapFree(GetProcessHeap(), 0, pStrokes[i]);
2073 PATH_DestroyGdiPath(pUpPath);
2074 HeapFree(GetProcessHeap(), 0, pUpPath);
2075 PATH_DestroyGdiPath(pDownPath);
2076 HeapFree(GetProcessHeap(), 0, pDownPath);
2078 HeapFree(GetProcessHeap(), 0, pStrokes);
2080 pNewPath->state = PATH_Closed;
2081 if (!(ret = PATH_AssignGdiPath(pPath, pNewPath)))
2082 ERR("Assign path failed\n");
2083 PATH_DestroyGdiPath(pNewPath);
2084 HeapFree(GetProcessHeap(), 0, pNewPath);
2085 return ret;
2089 /*******************************************************************
2090 * StrokeAndFillPath [GDI32.@]
2094 BOOL WINAPI StrokeAndFillPath(HDC hdc)
2096 BOOL ret = FALSE;
2097 DC *dc = get_dc_ptr( hdc );
2099 if (dc)
2101 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pStrokeAndFillPath );
2102 ret = physdev->funcs->pStrokeAndFillPath( physdev );
2103 release_dc_ptr( dc );
2105 return ret;
2109 /*******************************************************************
2110 * StrokePath [GDI32.@]
2114 BOOL WINAPI StrokePath(HDC hdc)
2116 BOOL ret = FALSE;
2117 DC *dc = get_dc_ptr( hdc );
2119 if (dc)
2121 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pStrokePath );
2122 ret = physdev->funcs->pStrokePath( physdev );
2123 release_dc_ptr( dc );
2125 return ret;
2129 /*******************************************************************
2130 * WidenPath [GDI32.@]
2134 BOOL WINAPI WidenPath(HDC hdc)
2136 BOOL ret = FALSE;
2137 DC *dc = get_dc_ptr( hdc );
2139 if (dc)
2141 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pWidenPath );
2142 ret = physdev->funcs->pWidenPath( physdev );
2143 release_dc_ptr( dc );
2145 return ret;
2149 /***********************************************************************
2150 * null driver fallback implementations
2153 BOOL nulldrv_BeginPath( PHYSDEV dev )
2155 DC *dc = get_nulldrv_dc( dev );
2157 /* If path is already open, do nothing */
2158 if (dc->path.state != PATH_Open)
2160 if (!path_driver.pCreateDC( &dc->physDev, NULL, NULL, NULL, NULL )) return FALSE;
2161 PATH_EmptyPath(&dc->path);
2162 dc->path.newStroke = TRUE;
2163 dc->path.state = PATH_Open;
2165 return TRUE;
2168 BOOL nulldrv_EndPath( PHYSDEV dev )
2170 SetLastError( ERROR_CAN_NOT_COMPLETE );
2171 return FALSE;
2174 BOOL nulldrv_AbortPath( PHYSDEV dev )
2176 DC *dc = get_nulldrv_dc( dev );
2178 PATH_EmptyPath( &dc->path );
2179 return TRUE;
2182 BOOL nulldrv_CloseFigure( PHYSDEV dev )
2184 DC *dc = get_nulldrv_dc( dev );
2186 if (dc->path.state != PATH_Open)
2188 SetLastError( ERROR_CAN_NOT_COMPLETE );
2189 return FALSE;
2191 /* Set PT_CLOSEFIGURE on the last entry and start a new stroke */
2192 /* It is not necessary to draw a line, PT_CLOSEFIGURE is a virtual closing line itself */
2193 if (dc->path.numEntriesUsed)
2195 dc->path.pFlags[dc->path.numEntriesUsed - 1] |= PT_CLOSEFIGURE;
2196 dc->path.newStroke = TRUE;
2198 return TRUE;
2201 BOOL nulldrv_SelectClipPath( PHYSDEV dev, INT mode )
2203 BOOL ret;
2204 HRGN hrgn;
2205 DC *dc = get_nulldrv_dc( dev );
2207 if (dc->path.state != PATH_Closed)
2209 SetLastError( ERROR_CAN_NOT_COMPLETE );
2210 return FALSE;
2212 if (!PATH_PathToRegion( &dc->path, GetPolyFillMode(dev->hdc), &hrgn )) return FALSE;
2213 ret = ExtSelectClipRgn( dev->hdc, hrgn, mode ) != ERROR;
2214 if (ret) PATH_EmptyPath( &dc->path );
2215 /* FIXME: Should this function delete the path even if it failed? */
2216 DeleteObject( hrgn );
2217 return ret;
2220 BOOL nulldrv_FillPath( PHYSDEV dev )
2222 DC *dc = get_nulldrv_dc( dev );
2224 if (dc->path.state != PATH_Closed)
2226 SetLastError( ERROR_CAN_NOT_COMPLETE );
2227 return FALSE;
2229 if (!PATH_FillPath( dc, &dc->path )) return FALSE;
2230 /* FIXME: Should the path be emptied even if conversion failed? */
2231 PATH_EmptyPath( &dc->path );
2232 return TRUE;
2235 BOOL nulldrv_StrokeAndFillPath( PHYSDEV dev )
2237 DC *dc = get_nulldrv_dc( dev );
2239 if (dc->path.state != PATH_Closed)
2241 SetLastError( ERROR_CAN_NOT_COMPLETE );
2242 return FALSE;
2244 if (!PATH_FillPath( dc, &dc->path )) return FALSE;
2245 if (!PATH_StrokePath( dc, &dc->path )) return FALSE;
2246 PATH_EmptyPath( &dc->path );
2247 return TRUE;
2250 BOOL nulldrv_StrokePath( PHYSDEV dev )
2252 DC *dc = get_nulldrv_dc( dev );
2254 if (dc->path.state != PATH_Closed)
2256 SetLastError( ERROR_CAN_NOT_COMPLETE );
2257 return FALSE;
2259 if (!PATH_StrokePath( dc, &dc->path )) return FALSE;
2260 PATH_EmptyPath( &dc->path );
2261 return TRUE;
2264 BOOL nulldrv_FlattenPath( PHYSDEV dev )
2266 DC *dc = get_nulldrv_dc( dev );
2268 if (dc->path.state != PATH_Closed)
2270 SetLastError( ERROR_CAN_NOT_COMPLETE );
2271 return FALSE;
2273 return PATH_FlattenPath( &dc->path );
2276 BOOL nulldrv_WidenPath( PHYSDEV dev )
2278 DC *dc = get_nulldrv_dc( dev );
2280 if (dc->path.state != PATH_Closed)
2282 SetLastError( ERROR_CAN_NOT_COMPLETE );
2283 return FALSE;
2285 return PATH_WidenPath( dc );
2288 const struct gdi_dc_funcs path_driver =
2290 NULL, /* pAbortDoc */
2291 pathdrv_AbortPath, /* pAbortPath */
2292 NULL, /* pAlphaBlend */
2293 NULL, /* pAngleArc */
2294 NULL, /* pArc */
2295 NULL, /* pArcTo */
2296 NULL, /* pBeginPath */
2297 NULL, /* pBlendImage */
2298 NULL, /* pChoosePixelFormat */
2299 NULL, /* pChord */
2300 NULL, /* pCloseFigure */
2301 NULL, /* pCreateBitmap */
2302 NULL, /* pCreateCompatibleDC */
2303 pathdrv_CreateDC, /* pCreateDC */
2304 NULL, /* pCreateDIBSection */
2305 NULL, /* pDeleteBitmap */
2306 pathdrv_DeleteDC, /* pDeleteDC */
2307 NULL, /* pDeleteObject */
2308 NULL, /* pDescribePixelFormat */
2309 NULL, /* pDeviceCapabilities */
2310 NULL, /* pEllipse */
2311 NULL, /* pEndDoc */
2312 NULL, /* pEndPage */
2313 pathdrv_EndPath, /* pEndPath */
2314 NULL, /* pEnumFonts */
2315 NULL, /* pEnumICMProfiles */
2316 NULL, /* pExcludeClipRect */
2317 NULL, /* pExtDeviceMode */
2318 NULL, /* pExtEscape */
2319 NULL, /* pExtFloodFill */
2320 NULL, /* pExtSelectClipRgn */
2321 NULL, /* pExtTextOut */
2322 NULL, /* pFillPath */
2323 NULL, /* pFillRgn */
2324 NULL, /* pFlattenPath */
2325 NULL, /* pFontIsLinked */
2326 NULL, /* pFrameRgn */
2327 NULL, /* pGdiComment */
2328 NULL, /* pGdiRealizationInfo */
2329 NULL, /* pGetCharABCWidths */
2330 NULL, /* pGetCharABCWidthsI */
2331 NULL, /* pGetCharWidth */
2332 NULL, /* pGetDeviceCaps */
2333 NULL, /* pGetDeviceGammaRamp */
2334 NULL, /* pGetFontData */
2335 NULL, /* pGetFontUnicodeRanges */
2336 NULL, /* pGetGlyphIndices */
2337 NULL, /* pGetGlyphOutline */
2338 NULL, /* pGetICMProfile */
2339 NULL, /* pGetImage */
2340 NULL, /* pGetKerningPairs */
2341 NULL, /* pGetNearestColor */
2342 NULL, /* pGetOutlineTextMetrics */
2343 NULL, /* pGetPixel */
2344 NULL, /* pGetPixelFormat */
2345 NULL, /* pGetSystemPaletteEntries */
2346 NULL, /* pGetTextCharsetInfo */
2347 NULL, /* pGetTextExtentExPoint */
2348 NULL, /* pGetTextExtentExPointI */
2349 NULL, /* pGetTextFace */
2350 NULL, /* pGetTextMetrics */
2351 NULL, /* pIntersectClipRect */
2352 NULL, /* pInvertRgn */
2353 pathdrv_LineTo, /* pLineTo */
2354 NULL, /* pModifyWorldTransform */
2355 pathdrv_MoveTo, /* pMoveTo */
2356 NULL, /* pOffsetClipRgn */
2357 NULL, /* pOffsetViewportOrg */
2358 NULL, /* pOffsetWindowOrg */
2359 NULL, /* pPaintRgn */
2360 NULL, /* pPatBlt */
2361 NULL, /* pPie */
2362 NULL, /* pPolyBezier */
2363 NULL, /* pPolyBezierTo */
2364 NULL, /* pPolyDraw */
2365 NULL, /* pPolyPolygon */
2366 NULL, /* pPolyPolyline */
2367 NULL, /* pPolygon */
2368 NULL, /* pPolyline */
2369 NULL, /* pPolylineTo */
2370 NULL, /* pPutImage */
2371 NULL, /* pRealizeDefaultPalette */
2372 NULL, /* pRealizePalette */
2373 NULL, /* pRectangle */
2374 NULL, /* pResetDC */
2375 NULL, /* pRestoreDC */
2376 NULL, /* pRoundRect */
2377 NULL, /* pSaveDC */
2378 NULL, /* pScaleViewportExt */
2379 NULL, /* pScaleWindowExt */
2380 NULL, /* pSelectBitmap */
2381 NULL, /* pSelectBrush */
2382 NULL, /* pSelectClipPath */
2383 NULL, /* pSelectFont */
2384 NULL, /* pSelectPalette */
2385 NULL, /* pSelectPen */
2386 NULL, /* pSetArcDirection */
2387 NULL, /* pSetBkColor */
2388 NULL, /* pSetBkMode */
2389 NULL, /* pSetDCBrushColor */
2390 NULL, /* pSetDCPenColor */
2391 NULL, /* pSetDIBColorTable */
2392 NULL, /* pSetDIBitsToDevice */
2393 NULL, /* pSetDeviceClipping */
2394 NULL, /* pSetDeviceGammaRamp */
2395 NULL, /* pSetLayout */
2396 NULL, /* pSetMapMode */
2397 NULL, /* pSetMapperFlags */
2398 NULL, /* pSetPixel */
2399 NULL, /* pSetPixelFormat */
2400 NULL, /* pSetPolyFillMode */
2401 NULL, /* pSetROP2 */
2402 NULL, /* pSetRelAbs */
2403 NULL, /* pSetStretchBltMode */
2404 NULL, /* pSetTextAlign */
2405 NULL, /* pSetTextCharacterExtra */
2406 NULL, /* pSetTextColor */
2407 NULL, /* pSetTextJustification */
2408 NULL, /* pSetViewportExt */
2409 NULL, /* pSetViewportOrg */
2410 NULL, /* pSetWindowExt */
2411 NULL, /* pSetWindowOrg */
2412 NULL, /* pSetWorldTransform */
2413 NULL, /* pStartDoc */
2414 NULL, /* pStartPage */
2415 NULL, /* pStretchBlt */
2416 NULL, /* pStretchDIBits */
2417 NULL, /* pStrokeAndFillPath */
2418 NULL, /* pStrokePath */
2419 NULL, /* pSwapBuffers */
2420 NULL, /* pUnrealizePalette */
2421 NULL, /* pWidenPath */
2422 /* OpenGL not supported */