2 * Graphics paths (BeginPath, EndPath etc.)
4 * Copyright 1997, 1998 Martin Boehme
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
25 #include "wine/port.h"
32 #if defined(HAVE_FLOAT_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
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.
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
89 struct gdi_physdev dev;
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)
113 /* Perform the transformation */
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;
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;
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
156 if(pPath->numEntriesAllocated)
158 numEntriesToAllocate=pPath->numEntriesAllocated;
159 while(numEntriesToAllocate<numEntries)
160 numEntriesToAllocate=numEntriesToAllocate*GROW_FACTOR_NUMER/
164 numEntriesToAllocate=numEntries;
166 /* Allocate new arrays */
167 pPointsNew=HeapAlloc( GetProcessHeap(), 0, numEntriesToAllocate * sizeof(POINT) );
170 pFlagsNew=HeapAlloc( GetProcessHeap(), 0, numEntriesToAllocate * sizeof(BYTE) );
173 HeapFree( GetProcessHeap(), 0, pPointsNew );
177 /* Copy old arrays to new arrays and discard old arrays */
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;
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)
215 /* Reserve enough memory for an extra path entry */
216 if(!PATH_ReserveEntries(pPath, pPath->numEntriesUsed+1))
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++;
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))
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;
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)
270 /* Convert points to device coordinates */
275 if(!LPtoDP(dc->hSelf, corners, 2))
278 /* Make sure first corner is top left and second corner is bottom right */
279 if(corners[0].x>corners[1].x)
282 corners[0].x=corners[1].x;
285 if(corners[0].y>corners[1].y)
288 corners[0].y=corners[1].y;
292 /* In GM_COMPATIBLE, don't include bottom and right edges */
293 if(dc->GraphicsMode==GM_COMPATIBLE)
302 /* PATH_AddFlatBezier
304 static BOOL PATH_AddFlatBezier(GdiPath *pPath, POINT *pt, BOOL closed)
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 );
320 * Replaces Beziers with line segments
323 static BOOL PATH_FlattenPath(GdiPath *pPath)
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) {
334 PATH_AddEntry(&newPath, &pPath->pPoints[srcpt],
335 pPath->pFlags[srcpt]);
338 PATH_AddFlatBezier(&newPath, &pPath->pPoints[srcpt-1],
339 pPath->pFlags[srcpt+2] & PT_CLOSEFIGURE);
344 newPath.state = PATH_Closed;
345 PATH_AssignGdiPath(pPath, &newPath);
346 PATH_DestroyGdiPath(&newPath);
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
358 static BOOL PATH_PathToRegion(GdiPath *pPath, INT nPolyFillMode,
361 int numStrokes, iStroke, i;
362 INT *pNumPointsInStroke;
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 */
372 for(i=0; i<pPath->numEntriesUsed; i++)
373 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
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);
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)
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 );
407 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
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;
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)
456 double xNorm[4], yNorm[4];
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];
481 xNorm[i]=cos(angleStart);
482 yNorm[i]=sin(angleStart);
485 /* Add starting point to path if desired */
488 PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
489 if(!PATH_AddEntry(pPath, &point, startEntryType))
493 /* Add remaining control points */
496 PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &point);
497 if(!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
505 /***********************************************************************
506 * BeginPath (GDI32.@)
508 BOOL WINAPI BeginPath(HDC hdc)
511 DC *dc = get_dc_ptr( hdc );
515 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pBeginPath );
516 ret = physdev->funcs->pBeginPath( physdev );
517 release_dc_ptr( dc );
523 /***********************************************************************
526 BOOL WINAPI EndPath(HDC hdc)
529 DC *dc = get_dc_ptr( hdc );
533 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pEndPath );
534 ret = physdev->funcs->pEndPath( physdev );
535 release_dc_ptr( dc );
541 /******************************************************************************
542 * AbortPath [GDI32.@]
543 * Closes and discards paths from device context
546 * Check that SetLastError is being called correctly
549 * hdc [I] Handle to device context
555 BOOL WINAPI AbortPath( HDC hdc )
558 DC *dc = get_dc_ptr( hdc );
562 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pAbortPath );
563 ret = physdev->funcs->pAbortPath( physdev );
564 release_dc_ptr( dc );
570 /***********************************************************************
571 * CloseFigure (GDI32.@)
573 * FIXME: Check that SetLastError is being called correctly
575 BOOL WINAPI CloseFigure(HDC hdc)
578 DC *dc = get_dc_ptr( hdc );
582 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pCloseFigure );
583 ret = physdev->funcs->pCloseFigure( physdev );
584 release_dc_ptr( dc );
590 /***********************************************************************
593 INT WINAPI GetPath(HDC hdc, LPPOINT pPoints, LPBYTE pTypes,
598 DC *dc = get_dc_ptr( hdc );
604 /* Check that path is closed */
605 if(pPath->state!=PATH_Closed)
607 SetLastError(ERROR_CAN_NOT_COMPLETE);
612 ret = pPath->numEntriesUsed;
613 else if(nSize<pPath->numEntriesUsed)
615 SetLastError(ERROR_INVALID_PARAMETER);
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);
630 else ret = pPath->numEntriesUsed;
633 release_dc_ptr( dc );
638 /***********************************************************************
639 * PathToRegion (GDI32.@)
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)
651 DC *dc = get_dc_ptr( hdc );
653 /* Get pointer to path */
658 /* Check that path is closed */
659 if(pPath->state!=PATH_Closed) SetLastError(ERROR_CAN_NOT_COMPLETE);
662 /* FIXME: Should we empty the path even if conversion failed? */
663 if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnRval))
664 PATH_EmptyPath(pPath);
668 release_dc_ptr( dc );
672 static BOOL PATH_FillPath(DC *dc, GdiPath *pPath)
674 INT mapMode, graphicsMode;
675 SIZE ptViewportExt, ptWindowExt;
676 POINT ptViewportOrg, ptWindowOrg;
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);
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);
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);
736 /***********************************************************************
740 * Check that SetLastError is being called correctly
742 BOOL WINAPI FillPath(HDC hdc)
745 DC *dc = get_dc_ptr( hdc );
749 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pFillPath );
750 ret = physdev->funcs->pFillPath( physdev );
751 release_dc_ptr( dc );
757 /***********************************************************************
758 * SelectClipPath (GDI32.@)
760 * Check that SetLastError is being called correctly
762 BOOL WINAPI SelectClipPath(HDC hdc, INT iMode)
765 DC *dc = get_dc_ptr( hdc );
769 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pSelectClipPath );
770 ret = physdev->funcs->pSelectClipPath( physdev, iMode );
771 release_dc_ptr( dc );
777 /***********************************************************************
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 );
792 /***********************************************************************
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 );
807 /***********************************************************************
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) );
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 );
825 /*************************************************************
828 static BOOL pathdrv_DeleteDC( PHYSDEV dev )
830 assert( 0 ); /* should never be called */
837 * Initializes the GdiPath structure.
839 void PATH_InitGdiPath(GdiPath *pPath)
843 pPath->state=PATH_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)
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 )
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 );
888 /*************************************************************
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;
900 * Should be called when a LineTo is performed on a DC that has an
901 * open path. This adds a PT_LINETO entry to the path (and possibly
902 * a PT_MOVETO entry, if this is the first LineTo in a stroke).
903 * Returns TRUE if successful, else FALSE.
905 BOOL PATH_LineTo(DC *dc, INT x, INT y)
907 GdiPath *pPath = &dc->path;
908 POINT point, pointCurPos;
910 /* Check that path is open */
911 if(pPath->state!=PATH_Open)
914 /* Convert point to device coordinates */
917 if(!LPtoDP(dc->hSelf, &point, 1))
920 /* Add a PT_MOVETO if necessary */
923 pPath->newStroke=FALSE;
924 pointCurPos.x = dc->CursPosX;
925 pointCurPos.y = dc->CursPosY;
926 if(!LPtoDP(dc->hSelf, &pointCurPos, 1))
928 if(!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
932 /* Add a PT_LINETO entry */
933 return PATH_AddEntry(pPath, &point, PT_LINETO);
938 * Should be called when a call to RoundRect is performed on a DC that has
939 * an open path. Returns TRUE if successful, else FALSE.
941 * FIXME: it adds the same entries to the path as windows does, but there
942 * is an error in the bezier drawing code so that there are small pixel-size
943 * gaps when the resulting path is drawn by StrokePath()
945 BOOL PATH_RoundRect(DC *dc, INT x1, INT y1, INT x2, INT y2, INT ell_width, INT ell_height)
947 GdiPath *pPath = &dc->path;
948 POINT corners[2], pointTemp;
949 FLOAT_POINT ellCorners[2];
951 /* Check that path is open */
952 if(pPath->state!=PATH_Open)
955 if(!PATH_CheckCorners(dc,corners,x1,y1,x2,y2))
958 /* Add points to the roundrect path */
959 ellCorners[0].x = corners[1].x-ell_width;
960 ellCorners[0].y = corners[0].y;
961 ellCorners[1].x = corners[1].x;
962 ellCorners[1].y = corners[0].y+ell_height;
963 if(!PATH_DoArcPart(pPath, ellCorners, 0, -M_PI_2, PT_MOVETO))
965 pointTemp.x = corners[0].x+ell_width/2;
966 pointTemp.y = corners[0].y;
967 if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
969 ellCorners[0].x = corners[0].x;
970 ellCorners[1].x = corners[0].x+ell_width;
971 if(!PATH_DoArcPart(pPath, ellCorners, -M_PI_2, -M_PI, FALSE))
973 pointTemp.x = corners[0].x;
974 pointTemp.y = corners[1].y-ell_height/2;
975 if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
977 ellCorners[0].y = corners[1].y-ell_height;
978 ellCorners[1].y = corners[1].y;
979 if(!PATH_DoArcPart(pPath, ellCorners, M_PI, M_PI_2, FALSE))
981 pointTemp.x = corners[1].x-ell_width/2;
982 pointTemp.y = corners[1].y;
983 if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
985 ellCorners[0].x = corners[1].x-ell_width;
986 ellCorners[1].x = corners[1].x;
987 if(!PATH_DoArcPart(pPath, ellCorners, M_PI_2, 0, FALSE))
990 /* Close the roundrect figure */
991 if(!CloseFigure(dc->hSelf))
999 * Should be called when a call to Rectangle is performed on a DC that has
1000 * an open path. Returns TRUE if successful, else FALSE.
1002 BOOL PATH_Rectangle(DC *dc, INT x1, INT y1, INT x2, INT y2)
1004 GdiPath *pPath = &dc->path;
1005 POINT corners[2], pointTemp;
1007 /* Check that path is open */
1008 if(pPath->state!=PATH_Open)
1011 if(!PATH_CheckCorners(dc,corners,x1,y1,x2,y2))
1014 /* Close any previous figure */
1015 if(!CloseFigure(dc->hSelf))
1017 /* The CloseFigure call shouldn't have failed */
1022 /* Add four points to the path */
1023 pointTemp.x=corners[1].x;
1024 pointTemp.y=corners[0].y;
1025 if(!PATH_AddEntry(pPath, &pointTemp, PT_MOVETO))
1027 if(!PATH_AddEntry(pPath, corners, PT_LINETO))
1029 pointTemp.x=corners[0].x;
1030 pointTemp.y=corners[1].y;
1031 if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
1033 if(!PATH_AddEntry(pPath, corners+1, PT_LINETO))
1036 /* Close the rectangle figure */
1037 if(!CloseFigure(dc->hSelf))
1039 /* The CloseFigure call shouldn't have failed */
1049 * Should be called when a call to Ellipse is performed on a DC that has
1050 * an open path. This adds four Bezier splines representing the ellipse
1051 * to the path. Returns TRUE if successful, else FALSE.
1053 BOOL PATH_Ellipse(DC *dc, INT x1, INT y1, INT x2, INT y2)
1055 return( PATH_Arc(dc, x1, y1, x2, y2, x1, (y1+y2)/2, x1, (y1+y2)/2,0) &&
1056 CloseFigure(dc->hSelf) );
1061 * Should be called when a call to Arc is performed on a DC that has
1062 * an open path. This adds up to five Bezier splines representing the arc
1063 * to the path. When 'lines' is 1, we add 1 extra line to get a chord,
1064 * when 'lines' is 2, we add 2 extra lines to get a pie, and when 'lines' is
1065 * -1 we add 1 extra line from the current DC position to the starting position
1066 * of the arc before drawing the arc itself (arcto). Returns TRUE if successful,
1069 BOOL PATH_Arc(DC *dc, INT x1, INT y1, INT x2, INT y2,
1070 INT xStart, INT yStart, INT xEnd, INT yEnd, INT lines)
1072 GdiPath *pPath = &dc->path;
1073 double angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
1074 /* Initialize angleEndQuadrant to silence gcc's warning */
1076 FLOAT_POINT corners[2], pointStart, pointEnd;
1077 POINT centre, pointCurPos;
1081 /* FIXME: This function should check for all possible error returns */
1082 /* FIXME: Do we have to respect newStroke? */
1084 /* Check that path is open */
1085 if(pPath->state!=PATH_Open)
1088 /* Check for zero height / width */
1089 /* FIXME: Only in GM_COMPATIBLE? */
1090 if(x1==x2 || y1==y2)
1093 /* Convert points to device coordinates */
1098 pointStart.x = xStart;
1099 pointStart.y = yStart;
1102 INTERNAL_LPTODP_FLOAT(dc, corners);
1103 INTERNAL_LPTODP_FLOAT(dc, corners+1);
1104 INTERNAL_LPTODP_FLOAT(dc, &pointStart);
1105 INTERNAL_LPTODP_FLOAT(dc, &pointEnd);
1107 /* Make sure first corner is top left and second corner is bottom right */
1108 if(corners[0].x>corners[1].x)
1111 corners[0].x=corners[1].x;
1114 if(corners[0].y>corners[1].y)
1117 corners[0].y=corners[1].y;
1121 /* Compute start and end angle */
1122 PATH_NormalizePoint(corners, &pointStart, &x, &y);
1123 angleStart=atan2(y, x);
1124 PATH_NormalizePoint(corners, &pointEnd, &x, &y);
1125 angleEnd=atan2(y, x);
1127 /* Make sure the end angle is "on the right side" of the start angle */
1128 if(dc->ArcDirection==AD_CLOCKWISE)
1130 if(angleEnd<=angleStart)
1133 assert(angleEnd>=angleStart);
1138 if(angleEnd>=angleStart)
1141 assert(angleEnd<=angleStart);
1145 /* In GM_COMPATIBLE, don't include bottom and right edges */
1146 if(dc->GraphicsMode==GM_COMPATIBLE)
1152 /* arcto: Add a PT_MOVETO only if this is the first entry in a stroke */
1153 if(lines==-1 && pPath->newStroke)
1155 pPath->newStroke=FALSE;
1156 pointCurPos.x = dc->CursPosX;
1157 pointCurPos.y = dc->CursPosY;
1158 if(!LPtoDP(dc->hSelf, &pointCurPos, 1))
1160 if(!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
1164 /* Add the arc to the path with one Bezier spline per quadrant that the
1170 /* Determine the start and end angles for this quadrant */
1173 angleStartQuadrant=angleStart;
1174 if(dc->ArcDirection==AD_CLOCKWISE)
1175 angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
1177 angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
1181 angleStartQuadrant=angleEndQuadrant;
1182 if(dc->ArcDirection==AD_CLOCKWISE)
1183 angleEndQuadrant+=M_PI_2;
1185 angleEndQuadrant-=M_PI_2;
1188 /* Have we reached the last part of the arc? */
1189 if((dc->ArcDirection==AD_CLOCKWISE &&
1190 angleEnd<angleEndQuadrant) ||
1191 (dc->ArcDirection==AD_COUNTERCLOCKWISE &&
1192 angleEnd>angleEndQuadrant))
1194 /* Adjust the end angle for this quadrant */
1195 angleEndQuadrant=angleEnd;
1199 /* Add the Bezier spline to the path */
1200 PATH_DoArcPart(pPath, corners, angleStartQuadrant, angleEndQuadrant,
1201 start ? (lines==-1 ? PT_LINETO : PT_MOVETO) : FALSE);
1205 /* chord: close figure. pie: add line and close figure */
1208 if(!CloseFigure(dc->hSelf))
1213 centre.x = (corners[0].x+corners[1].x)/2;
1214 centre.y = (corners[0].y+corners[1].y)/2;
1215 if(!PATH_AddEntry(pPath, ¢re, PT_LINETO | PT_CLOSEFIGURE))
1222 BOOL PATH_PolyBezierTo(DC *dc, const POINT *pts, DWORD cbPoints)
1224 GdiPath *pPath = &dc->path;
1228 /* Check that path is open */
1229 if(pPath->state!=PATH_Open)
1232 /* Add a PT_MOVETO if necessary */
1233 if(pPath->newStroke)
1235 pPath->newStroke=FALSE;
1236 pt.x = dc->CursPosX;
1237 pt.y = dc->CursPosY;
1238 if(!LPtoDP(dc->hSelf, &pt, 1))
1240 if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
1244 for(i = 0; i < cbPoints; i++) {
1246 if(!LPtoDP(dc->hSelf, &pt, 1))
1248 PATH_AddEntry(pPath, &pt, PT_BEZIERTO);
1253 BOOL PATH_PolyBezier(DC *dc, const POINT *pts, DWORD cbPoints)
1255 GdiPath *pPath = &dc->path;
1259 /* Check that path is open */
1260 if(pPath->state!=PATH_Open)
1263 for(i = 0; i < cbPoints; i++) {
1265 if(!LPtoDP(dc->hSelf, &pt, 1))
1267 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_BEZIERTO);
1274 * Should be called when a call to PolyDraw is performed on a DC that has
1275 * an open path. Returns TRUE if successful, else FALSE.
1277 BOOL PATH_PolyDraw(DC *dc, const POINT *pts, const BYTE *types,
1280 GdiPath *pPath = &dc->path;
1281 POINT lastmove, orig_pos;
1284 GetCurrentPositionEx( dc->hSelf, &orig_pos );
1285 lastmove = orig_pos;
1287 for(i = pPath->numEntriesUsed - 1; i >= 0; i--){
1288 if(pPath->pFlags[i] == PT_MOVETO){
1289 lastmove = pPath->pPoints[i];
1290 DPtoLP(dc->hSelf, &lastmove, 1);
1295 for(i = 0; i < cbPoints; i++)
1300 MoveToEx( dc->hSelf, pts[i].x, pts[i].y, NULL );
1303 case PT_LINETO | PT_CLOSEFIGURE:
1304 LineTo( dc->hSelf, pts[i].x, pts[i].y );
1307 if ((i + 2 < cbPoints) && (types[i + 1] == PT_BEZIERTO) &&
1308 (types[i + 2] & ~PT_CLOSEFIGURE) == PT_BEZIERTO)
1310 PolyBezierTo( dc->hSelf, &pts[i], 3 );
1316 if (i) /* restore original position */
1318 if (!(types[i - 1] & PT_CLOSEFIGURE)) lastmove = pts[i - 1];
1319 if (lastmove.x != orig_pos.x || lastmove.y != orig_pos.y)
1320 MoveToEx( dc->hSelf, orig_pos.x, orig_pos.y, NULL );
1325 if(types[i] & PT_CLOSEFIGURE){
1326 pPath->pFlags[pPath->numEntriesUsed-1] |= PT_CLOSEFIGURE;
1327 MoveToEx( dc->hSelf, lastmove.x, lastmove.y, NULL );
1334 BOOL PATH_Polyline(DC *dc, const POINT *pts, DWORD cbPoints)
1336 GdiPath *pPath = &dc->path;
1340 /* Check that path is open */
1341 if(pPath->state!=PATH_Open)
1344 for(i = 0; i < cbPoints; i++) {
1346 if(!LPtoDP(dc->hSelf, &pt, 1))
1348 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_LINETO);
1353 BOOL PATH_PolylineTo(DC *dc, const POINT *pts, DWORD cbPoints)
1355 GdiPath *pPath = &dc->path;
1359 /* Check that path is open */
1360 if(pPath->state!=PATH_Open)
1363 /* Add a PT_MOVETO if necessary */
1364 if(pPath->newStroke)
1366 pPath->newStroke=FALSE;
1367 pt.x = dc->CursPosX;
1368 pt.y = dc->CursPosY;
1369 if(!LPtoDP(dc->hSelf, &pt, 1))
1371 if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
1375 for(i = 0; i < cbPoints; i++) {
1377 if(!LPtoDP(dc->hSelf, &pt, 1))
1379 PATH_AddEntry(pPath, &pt, PT_LINETO);
1386 BOOL PATH_Polygon(DC *dc, const POINT *pts, DWORD cbPoints)
1388 GdiPath *pPath = &dc->path;
1392 /* Check that path is open */
1393 if(pPath->state!=PATH_Open)
1396 for(i = 0; i < cbPoints; i++) {
1398 if(!LPtoDP(dc->hSelf, &pt, 1))
1400 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO :
1401 ((i == cbPoints-1) ? PT_LINETO | PT_CLOSEFIGURE :
1407 BOOL PATH_PolyPolygon( DC *dc, const POINT* pts, const INT* counts,
1410 GdiPath *pPath = &dc->path;
1415 /* Check that path is open */
1416 if(pPath->state!=PATH_Open)
1419 for(i = 0, poly = 0; poly < polygons; poly++) {
1420 for(point = 0; point < counts[poly]; point++, i++) {
1422 if(!LPtoDP(dc->hSelf, &pt, 1))
1424 if(point == 0) startpt = pt;
1425 PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1427 /* win98 adds an extra line to close the figure for some reason */
1428 PATH_AddEntry(pPath, &startpt, PT_LINETO | PT_CLOSEFIGURE);
1433 BOOL PATH_PolyPolyline( DC *dc, const POINT* pts, const DWORD* counts,
1436 GdiPath *pPath = &dc->path;
1438 UINT poly, point, i;
1440 /* Check that path is open */
1441 if(pPath->state!=PATH_Open)
1444 for(i = 0, poly = 0; poly < polylines; poly++) {
1445 for(point = 0; point < counts[poly]; point++, i++) {
1447 if(!LPtoDP(dc->hSelf, &pt, 1))
1449 PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1456 /**********************************************************************
1459 * internally used by PATH_add_outline
1461 static void PATH_BezierTo(GdiPath *pPath, POINT *lppt, INT n)
1467 PATH_AddEntry(pPath, &lppt[1], PT_LINETO);
1471 PATH_AddEntry(pPath, &lppt[0], PT_BEZIERTO);
1472 PATH_AddEntry(pPath, &lppt[1], PT_BEZIERTO);
1473 PATH_AddEntry(pPath, &lppt[2], PT_BEZIERTO);
1487 pt[2].x = (lppt[i+2].x + lppt[i+1].x) / 2;
1488 pt[2].y = (lppt[i+2].y + lppt[i+1].y) / 2;
1489 PATH_BezierTo(pPath, pt, 3);
1497 PATH_BezierTo(pPath, pt, 3);
1501 static BOOL PATH_add_outline(DC *dc, INT x, INT y, TTPOLYGONHEADER *header, DWORD size)
1503 GdiPath *pPath = &dc->path;
1504 TTPOLYGONHEADER *start;
1509 while ((char *)header < (char *)start + size)
1513 if (header->dwType != TT_POLYGON_TYPE)
1515 FIXME("Unknown header type %d\n", header->dwType);
1519 pt.x = x + int_from_fixed(header->pfxStart.x);
1520 pt.y = y - int_from_fixed(header->pfxStart.y);
1521 PATH_AddEntry(pPath, &pt, PT_MOVETO);
1523 curve = (TTPOLYCURVE *)(header + 1);
1525 while ((char *)curve < (char *)header + header->cb)
1527 /*TRACE("curve->wType %d\n", curve->wType);*/
1529 switch(curve->wType)
1535 for (i = 0; i < curve->cpfx; i++)
1537 pt.x = x + int_from_fixed(curve->apfx[i].x);
1538 pt.y = y - int_from_fixed(curve->apfx[i].y);
1539 PATH_AddEntry(pPath, &pt, PT_LINETO);
1544 case TT_PRIM_QSPLINE:
1545 case TT_PRIM_CSPLINE:
1549 POINT *pts = HeapAlloc(GetProcessHeap(), 0, (curve->cpfx + 1) * sizeof(POINT));
1551 if (!pts) return FALSE;
1553 ptfx = *(POINTFX *)((char *)curve - sizeof(POINTFX));
1555 pts[0].x = x + int_from_fixed(ptfx.x);
1556 pts[0].y = y - int_from_fixed(ptfx.y);
1558 for(i = 0; i < curve->cpfx; i++)
1560 pts[i + 1].x = x + int_from_fixed(curve->apfx[i].x);
1561 pts[i + 1].y = y - int_from_fixed(curve->apfx[i].y);
1564 PATH_BezierTo(pPath, pts, curve->cpfx + 1);
1566 HeapFree(GetProcessHeap(), 0, pts);
1571 FIXME("Unknown curve type %04x\n", curve->wType);
1575 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
1578 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
1581 return CloseFigure(dc->hSelf);
1584 /**********************************************************************
1587 BOOL PATH_ExtTextOut(DC *dc, INT x, INT y, UINT flags, const RECT *lprc,
1588 LPCWSTR str, UINT count, const INT *dx)
1591 HDC hdc = dc->hSelf;
1592 POINT offset = {0, 0};
1594 TRACE("%p, %d, %d, %08x, %s, %s, %d, %p)\n", hdc, x, y, flags,
1595 wine_dbgstr_rect(lprc), debugstr_wn(str, count), count, dx);
1597 if (!count) return TRUE;
1599 for (idx = 0; idx < count; idx++)
1601 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
1606 dwSize = GetGlyphOutlineW(hdc, str[idx], GGO_GLYPH_INDEX | GGO_NATIVE, &gm, 0, NULL, &identity);
1607 if (dwSize == GDI_ERROR) return FALSE;
1609 /* add outline only if char is printable */
1612 outline = HeapAlloc(GetProcessHeap(), 0, dwSize);
1613 if (!outline) return FALSE;
1615 GetGlyphOutlineW(hdc, str[idx], GGO_GLYPH_INDEX | GGO_NATIVE, &gm, dwSize, outline, &identity);
1617 PATH_add_outline(dc, x + offset.x, y + offset.y, outline, dwSize);
1619 HeapFree(GetProcessHeap(), 0, outline);
1626 offset.x += dx[idx * 2];
1627 offset.y += dx[idx * 2 + 1];
1630 offset.x += dx[idx];
1634 offset.x += gm.gmCellIncX;
1635 offset.y += gm.gmCellIncY;
1642 /*******************************************************************
1643 * FlattenPath [GDI32.@]
1647 BOOL WINAPI FlattenPath(HDC hdc)
1650 DC *dc = get_dc_ptr( hdc );
1654 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pFlattenPath );
1655 ret = physdev->funcs->pFlattenPath( physdev );
1656 release_dc_ptr( dc );
1662 static BOOL PATH_StrokePath(DC *dc, GdiPath *pPath)
1664 INT i, nLinePts, nAlloc;
1666 POINT ptViewportOrg, ptWindowOrg;
1667 SIZE szViewportExt, szWindowExt;
1668 DWORD mapMode, graphicsMode;
1672 /* Save the mapping mode info */
1673 mapMode=GetMapMode(dc->hSelf);
1674 GetViewportExtEx(dc->hSelf, &szViewportExt);
1675 GetViewportOrgEx(dc->hSelf, &ptViewportOrg);
1676 GetWindowExtEx(dc->hSelf, &szWindowExt);
1677 GetWindowOrgEx(dc->hSelf, &ptWindowOrg);
1678 GetWorldTransform(dc->hSelf, &xform);
1681 SetMapMode(dc->hSelf, MM_TEXT);
1682 SetViewportOrgEx(dc->hSelf, 0, 0, NULL);
1683 SetWindowOrgEx(dc->hSelf, 0, 0, NULL);
1684 graphicsMode=GetGraphicsMode(dc->hSelf);
1685 SetGraphicsMode(dc->hSelf, GM_ADVANCED);
1686 ModifyWorldTransform(dc->hSelf, &xform, MWT_IDENTITY);
1687 SetGraphicsMode(dc->hSelf, graphicsMode);
1689 /* Allocate enough memory for the worst case without beziers (one PT_MOVETO
1690 * and the rest PT_LINETO with PT_CLOSEFIGURE at the end) plus some buffer
1691 * space in case we get one to keep the number of reallocations small. */
1692 nAlloc = pPath->numEntriesUsed + 1 + 300;
1693 pLinePts = HeapAlloc(GetProcessHeap(), 0, nAlloc * sizeof(POINT));
1696 for(i = 0; i < pPath->numEntriesUsed; i++) {
1697 if((i == 0 || (pPath->pFlags[i-1] & PT_CLOSEFIGURE)) &&
1698 (pPath->pFlags[i] != PT_MOVETO)) {
1699 ERR("Expected PT_MOVETO %s, got path flag %d\n",
1700 i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
1701 (INT)pPath->pFlags[i]);
1705 switch(pPath->pFlags[i]) {
1707 TRACE("Got PT_MOVETO (%d, %d)\n",
1708 pPath->pPoints[i].x, pPath->pPoints[i].y);
1710 Polyline(dc->hSelf, pLinePts, nLinePts);
1712 pLinePts[nLinePts++] = pPath->pPoints[i];
1715 case (PT_LINETO | PT_CLOSEFIGURE):
1716 TRACE("Got PT_LINETO (%d, %d)\n",
1717 pPath->pPoints[i].x, pPath->pPoints[i].y);
1718 pLinePts[nLinePts++] = pPath->pPoints[i];
1721 TRACE("Got PT_BEZIERTO\n");
1722 if(pPath->pFlags[i+1] != PT_BEZIERTO ||
1723 (pPath->pFlags[i+2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO) {
1724 ERR("Path didn't contain 3 successive PT_BEZIERTOs\n");
1728 INT nBzrPts, nMinAlloc;
1729 POINT *pBzrPts = GDI_Bezier(&pPath->pPoints[i-1], 4, &nBzrPts);
1730 /* Make sure we have allocated enough memory for the lines of
1731 * this bezier and the rest of the path, assuming we won't get
1732 * another one (since we won't reallocate again then). */
1733 nMinAlloc = nLinePts + (pPath->numEntriesUsed - i) + nBzrPts;
1734 if(nAlloc < nMinAlloc)
1736 nAlloc = nMinAlloc * 2;
1737 pLinePts = HeapReAlloc(GetProcessHeap(), 0, pLinePts,
1738 nAlloc * sizeof(POINT));
1740 memcpy(&pLinePts[nLinePts], &pBzrPts[1],
1741 (nBzrPts - 1) * sizeof(POINT));
1742 nLinePts += nBzrPts - 1;
1743 HeapFree(GetProcessHeap(), 0, pBzrPts);
1748 ERR("Got path flag %d\n", (INT)pPath->pFlags[i]);
1752 if(pPath->pFlags[i] & PT_CLOSEFIGURE)
1753 pLinePts[nLinePts++] = pLinePts[0];
1756 Polyline(dc->hSelf, pLinePts, nLinePts);
1759 HeapFree(GetProcessHeap(), 0, pLinePts);
1761 /* Restore the old mapping mode */
1762 SetMapMode(dc->hSelf, mapMode);
1763 SetWindowExtEx(dc->hSelf, szWindowExt.cx, szWindowExt.cy, NULL);
1764 SetWindowOrgEx(dc->hSelf, ptWindowOrg.x, ptWindowOrg.y, NULL);
1765 SetViewportExtEx(dc->hSelf, szViewportExt.cx, szViewportExt.cy, NULL);
1766 SetViewportOrgEx(dc->hSelf, ptViewportOrg.x, ptViewportOrg.y, NULL);
1768 /* Go to GM_ADVANCED temporarily to restore the world transform */
1769 graphicsMode=GetGraphicsMode(dc->hSelf);
1770 SetGraphicsMode(dc->hSelf, GM_ADVANCED);
1771 SetWorldTransform(dc->hSelf, &xform);
1772 SetGraphicsMode(dc->hSelf, graphicsMode);
1774 /* If we've moved the current point then get its new position
1775 which will be in device (MM_TEXT) co-ords, convert it to
1776 logical co-ords and re-set it. This basically updates
1777 dc->CurPosX|Y so that their values are in the correct mapping
1782 GetCurrentPositionEx(dc->hSelf, &pt);
1783 DPtoLP(dc->hSelf, &pt, 1);
1784 MoveToEx(dc->hSelf, pt.x, pt.y, NULL);
1790 #define round(x) ((int)((x)>0?(x)+0.5:(x)-0.5))
1792 static BOOL PATH_WidenPath(DC *dc)
1794 INT i, j, numStrokes, penWidth, penWidthIn, penWidthOut, size, penStyle;
1796 GdiPath *pPath, *pNewPath, **pStrokes = NULL, *pUpPath, *pDownPath;
1798 DWORD obj_type, joint, endcap, penType;
1802 PATH_FlattenPath(pPath);
1804 size = GetObjectW( dc->hPen, 0, NULL );
1806 SetLastError(ERROR_CAN_NOT_COMPLETE);
1810 elp = HeapAlloc( GetProcessHeap(), 0, size );
1811 GetObjectW( dc->hPen, size, elp );
1813 obj_type = GetObjectType(dc->hPen);
1814 if(obj_type == OBJ_PEN) {
1815 penStyle = ((LOGPEN*)elp)->lopnStyle;
1817 else if(obj_type == OBJ_EXTPEN) {
1818 penStyle = elp->elpPenStyle;
1821 SetLastError(ERROR_CAN_NOT_COMPLETE);
1822 HeapFree( GetProcessHeap(), 0, elp );
1826 penWidth = elp->elpWidth;
1827 HeapFree( GetProcessHeap(), 0, elp );
1829 endcap = (PS_ENDCAP_MASK & penStyle);
1830 joint = (PS_JOIN_MASK & penStyle);
1831 penType = (PS_TYPE_MASK & penStyle);
1833 /* The function cannot apply to cosmetic pens */
1834 if(obj_type == OBJ_EXTPEN && penType == PS_COSMETIC) {
1835 SetLastError(ERROR_CAN_NOT_COMPLETE);
1839 penWidthIn = penWidth / 2;
1840 penWidthOut = penWidth / 2;
1841 if(penWidthIn + penWidthOut < penWidth)
1846 for(i = 0, j = 0; i < pPath->numEntriesUsed; i++, j++) {
1848 if((i == 0 || (pPath->pFlags[i-1] & PT_CLOSEFIGURE)) &&
1849 (pPath->pFlags[i] != PT_MOVETO)) {
1850 ERR("Expected PT_MOVETO %s, got path flag %c\n",
1851 i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
1855 switch(pPath->pFlags[i]) {
1857 if(numStrokes > 0) {
1858 pStrokes[numStrokes - 1]->state = PATH_Closed;
1863 pStrokes = HeapAlloc(GetProcessHeap(), 0, sizeof(GdiPath*));
1865 pStrokes = HeapReAlloc(GetProcessHeap(), 0, pStrokes, numStrokes * sizeof(GdiPath*));
1866 if(!pStrokes) return FALSE;
1867 pStrokes[numStrokes - 1] = HeapAlloc(GetProcessHeap(), 0, sizeof(GdiPath));
1868 PATH_InitGdiPath(pStrokes[numStrokes - 1]);
1869 pStrokes[numStrokes - 1]->state = PATH_Open;
1872 case (PT_LINETO | PT_CLOSEFIGURE):
1873 point.x = pPath->pPoints[i].x;
1874 point.y = pPath->pPoints[i].y;
1875 PATH_AddEntry(pStrokes[numStrokes - 1], &point, pPath->pFlags[i]);
1878 /* should never happen because of the FlattenPath call */
1879 ERR("Should never happen\n");
1882 ERR("Got path flag %c\n", pPath->pFlags[i]);
1887 pNewPath = HeapAlloc(GetProcessHeap(), 0, sizeof(GdiPath));
1888 PATH_InitGdiPath(pNewPath);
1889 pNewPath->state = PATH_Open;
1891 for(i = 0; i < numStrokes; i++) {
1892 pUpPath = HeapAlloc(GetProcessHeap(), 0, sizeof(GdiPath));
1893 PATH_InitGdiPath(pUpPath);
1894 pUpPath->state = PATH_Open;
1895 pDownPath = HeapAlloc(GetProcessHeap(), 0, sizeof(GdiPath));
1896 PATH_InitGdiPath(pDownPath);
1897 pDownPath->state = PATH_Open;
1899 for(j = 0; j < pStrokes[i]->numEntriesUsed; j++) {
1900 /* Beginning or end of the path if not closed */
1901 if((!(pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE)) && (j == 0 || j == pStrokes[i]->numEntriesUsed - 1) ) {
1902 /* Compute segment angle */
1903 double xo, yo, xa, ya, theta;
1905 FLOAT_POINT corners[2];
1907 xo = pStrokes[i]->pPoints[j].x;
1908 yo = pStrokes[i]->pPoints[j].y;
1909 xa = pStrokes[i]->pPoints[1].x;
1910 ya = pStrokes[i]->pPoints[1].y;
1913 xa = pStrokes[i]->pPoints[j - 1].x;
1914 ya = pStrokes[i]->pPoints[j - 1].y;
1915 xo = pStrokes[i]->pPoints[j].x;
1916 yo = pStrokes[i]->pPoints[j].y;
1918 theta = atan2( ya - yo, xa - xo );
1920 case PS_ENDCAP_SQUARE :
1921 pt.x = xo + round(sqrt(2) * penWidthOut * cos(M_PI_4 + theta));
1922 pt.y = yo + round(sqrt(2) * penWidthOut * sin(M_PI_4 + theta));
1923 PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO) );
1924 pt.x = xo + round(sqrt(2) * penWidthIn * cos(- M_PI_4 + theta));
1925 pt.y = yo + round(sqrt(2) * penWidthIn * sin(- M_PI_4 + theta));
1926 PATH_AddEntry(pUpPath, &pt, PT_LINETO);
1928 case PS_ENDCAP_FLAT :
1929 pt.x = xo + round( penWidthOut * cos(theta + M_PI_2) );
1930 pt.y = yo + round( penWidthOut * sin(theta + M_PI_2) );
1931 PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
1932 pt.x = xo - round( penWidthIn * cos(theta + M_PI_2) );
1933 pt.y = yo - round( penWidthIn * sin(theta + M_PI_2) );
1934 PATH_AddEntry(pUpPath, &pt, PT_LINETO);
1936 case PS_ENDCAP_ROUND :
1938 corners[0].x = xo - penWidthIn;
1939 corners[0].y = yo - penWidthIn;
1940 corners[1].x = xo + penWidthOut;
1941 corners[1].y = yo + penWidthOut;
1942 PATH_DoArcPart(pUpPath ,corners, theta + M_PI_2 , theta + 3 * M_PI_4, (j == 0 ? PT_MOVETO : FALSE));
1943 PATH_DoArcPart(pUpPath ,corners, theta + 3 * M_PI_4 , theta + M_PI, FALSE);
1944 PATH_DoArcPart(pUpPath ,corners, theta + M_PI, theta + 5 * M_PI_4, FALSE);
1945 PATH_DoArcPart(pUpPath ,corners, theta + 5 * M_PI_4 , theta + 3 * M_PI_2, FALSE);
1949 /* Corpse of the path */
1953 double xa, ya, xb, yb, xo, yo;
1954 double alpha, theta, miterWidth;
1955 DWORD _joint = joint;
1957 GdiPath *pInsidePath, *pOutsidePath;
1958 if(j > 0 && j < pStrokes[i]->numEntriesUsed - 1) {
1963 previous = pStrokes[i]->numEntriesUsed - 1;
1970 xo = pStrokes[i]->pPoints[j].x;
1971 yo = pStrokes[i]->pPoints[j].y;
1972 xa = pStrokes[i]->pPoints[previous].x;
1973 ya = pStrokes[i]->pPoints[previous].y;
1974 xb = pStrokes[i]->pPoints[next].x;
1975 yb = pStrokes[i]->pPoints[next].y;
1976 theta = atan2( yo - ya, xo - xa );
1977 alpha = atan2( yb - yo, xb - xo ) - theta;
1978 if (alpha > 0) alpha -= M_PI;
1980 if(_joint == PS_JOIN_MITER && dc->miterLimit < fabs(1 / sin(alpha/2))) {
1981 _joint = PS_JOIN_BEVEL;
1984 pInsidePath = pUpPath;
1985 pOutsidePath = pDownPath;
1987 else if(alpha < 0) {
1988 pInsidePath = pDownPath;
1989 pOutsidePath = pUpPath;
1994 /* Inside angle points */
1996 pt.x = xo - round( penWidthIn * cos(theta + M_PI_2) );
1997 pt.y = yo - round( penWidthIn * sin(theta + M_PI_2) );
2000 pt.x = xo + round( penWidthIn * cos(theta + M_PI_2) );
2001 pt.y = yo + round( penWidthIn * sin(theta + M_PI_2) );
2003 PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
2005 pt.x = xo + round( penWidthIn * cos(M_PI_2 + alpha + theta) );
2006 pt.y = yo + round( penWidthIn * sin(M_PI_2 + alpha + theta) );
2009 pt.x = xo - round( penWidthIn * cos(M_PI_2 + alpha + theta) );
2010 pt.y = yo - round( penWidthIn * sin(M_PI_2 + alpha + theta) );
2012 PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
2013 /* Outside angle point */
2015 case PS_JOIN_MITER :
2016 miterWidth = fabs(penWidthOut / cos(M_PI_2 - fabs(alpha) / 2));
2017 pt.x = xo + round( miterWidth * cos(theta + alpha / 2) );
2018 pt.y = yo + round( miterWidth * sin(theta + alpha / 2) );
2019 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2021 case PS_JOIN_BEVEL :
2023 pt.x = xo + round( penWidthOut * cos(theta + M_PI_2) );
2024 pt.y = yo + round( penWidthOut * sin(theta + M_PI_2) );
2027 pt.x = xo - round( penWidthOut * cos(theta + M_PI_2) );
2028 pt.y = yo - round( penWidthOut * sin(theta + M_PI_2) );
2030 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2032 pt.x = xo - round( penWidthOut * cos(M_PI_2 + alpha + theta) );
2033 pt.y = yo - round( penWidthOut * sin(M_PI_2 + alpha + theta) );
2036 pt.x = xo + round( penWidthOut * cos(M_PI_2 + alpha + theta) );
2037 pt.y = yo + round( penWidthOut * sin(M_PI_2 + alpha + theta) );
2039 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2041 case PS_JOIN_ROUND :
2044 pt.x = xo + round( penWidthOut * cos(theta + M_PI_2) );
2045 pt.y = yo + round( penWidthOut * sin(theta + M_PI_2) );
2048 pt.x = xo - round( penWidthOut * cos(theta + M_PI_2) );
2049 pt.y = yo - round( penWidthOut * sin(theta + M_PI_2) );
2051 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2052 pt.x = xo + round( penWidthOut * cos(theta + alpha / 2) );
2053 pt.y = yo + round( penWidthOut * sin(theta + alpha / 2) );
2054 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2056 pt.x = xo - round( penWidthOut * cos(M_PI_2 + alpha + theta) );
2057 pt.y = yo - round( penWidthOut * sin(M_PI_2 + alpha + theta) );
2060 pt.x = xo + round( penWidthOut * cos(M_PI_2 + alpha + theta) );
2061 pt.y = yo + round( penWidthOut * sin(M_PI_2 + alpha + theta) );
2063 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2068 for(j = 0; j < pUpPath->numEntriesUsed; j++) {
2070 pt.x = pUpPath->pPoints[j].x;
2071 pt.y = pUpPath->pPoints[j].y;
2072 PATH_AddEntry(pNewPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
2074 for(j = 0; j < pDownPath->numEntriesUsed; j++) {
2076 pt.x = pDownPath->pPoints[pDownPath->numEntriesUsed - j - 1].x;
2077 pt.y = pDownPath->pPoints[pDownPath->numEntriesUsed - j - 1].y;
2078 PATH_AddEntry(pNewPath, &pt, ( (j == 0 && (pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE)) ? PT_MOVETO : PT_LINETO));
2081 PATH_DestroyGdiPath(pStrokes[i]);
2082 HeapFree(GetProcessHeap(), 0, pStrokes[i]);
2083 PATH_DestroyGdiPath(pUpPath);
2084 HeapFree(GetProcessHeap(), 0, pUpPath);
2085 PATH_DestroyGdiPath(pDownPath);
2086 HeapFree(GetProcessHeap(), 0, pDownPath);
2088 HeapFree(GetProcessHeap(), 0, pStrokes);
2090 pNewPath->state = PATH_Closed;
2091 if (!(ret = PATH_AssignGdiPath(pPath, pNewPath)))
2092 ERR("Assign path failed\n");
2093 PATH_DestroyGdiPath(pNewPath);
2094 HeapFree(GetProcessHeap(), 0, pNewPath);
2099 /*******************************************************************
2100 * StrokeAndFillPath [GDI32.@]
2104 BOOL WINAPI StrokeAndFillPath(HDC hdc)
2107 DC *dc = get_dc_ptr( hdc );
2111 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pStrokeAndFillPath );
2112 ret = physdev->funcs->pStrokeAndFillPath( physdev );
2113 release_dc_ptr( dc );
2119 /*******************************************************************
2120 * StrokePath [GDI32.@]
2124 BOOL WINAPI StrokePath(HDC hdc)
2127 DC *dc = get_dc_ptr( hdc );
2131 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pStrokePath );
2132 ret = physdev->funcs->pStrokePath( physdev );
2133 release_dc_ptr( dc );
2139 /*******************************************************************
2140 * WidenPath [GDI32.@]
2144 BOOL WINAPI WidenPath(HDC hdc)
2147 DC *dc = get_dc_ptr( hdc );
2151 PHYSDEV physdev = GET_DC_PHYSDEV( dc, pWidenPath );
2152 ret = physdev->funcs->pWidenPath( physdev );
2153 release_dc_ptr( dc );
2159 /***********************************************************************
2160 * null driver fallback implementations
2163 BOOL nulldrv_BeginPath( PHYSDEV dev )
2165 DC *dc = get_nulldrv_dc( dev );
2167 /* If path is already open, do nothing */
2168 if (dc->path.state != PATH_Open)
2170 if (!path_driver.pCreateDC( &dc->physDev, NULL, NULL, NULL, NULL )) return FALSE;
2171 PATH_EmptyPath(&dc->path);
2172 dc->path.newStroke = TRUE;
2173 dc->path.state = PATH_Open;
2178 BOOL nulldrv_EndPath( PHYSDEV dev )
2180 SetLastError( ERROR_CAN_NOT_COMPLETE );
2184 BOOL nulldrv_AbortPath( PHYSDEV dev )
2186 DC *dc = get_nulldrv_dc( dev );
2188 PATH_EmptyPath( &dc->path );
2192 BOOL nulldrv_CloseFigure( PHYSDEV dev )
2194 DC *dc = get_nulldrv_dc( dev );
2196 if (dc->path.state != PATH_Open)
2198 SetLastError( ERROR_CAN_NOT_COMPLETE );
2201 /* Set PT_CLOSEFIGURE on the last entry and start a new stroke */
2202 /* It is not necessary to draw a line, PT_CLOSEFIGURE is a virtual closing line itself */
2203 if (dc->path.numEntriesUsed)
2205 dc->path.pFlags[dc->path.numEntriesUsed - 1] |= PT_CLOSEFIGURE;
2206 dc->path.newStroke = TRUE;
2211 BOOL nulldrv_SelectClipPath( PHYSDEV dev, INT mode )
2215 DC *dc = get_nulldrv_dc( dev );
2217 if (dc->path.state != PATH_Closed)
2219 SetLastError( ERROR_CAN_NOT_COMPLETE );
2222 if (!PATH_PathToRegion( &dc->path, GetPolyFillMode(dev->hdc), &hrgn )) return FALSE;
2223 ret = ExtSelectClipRgn( dev->hdc, hrgn, mode ) != ERROR;
2224 if (ret) PATH_EmptyPath( &dc->path );
2225 /* FIXME: Should this function delete the path even if it failed? */
2226 DeleteObject( hrgn );
2230 BOOL nulldrv_FillPath( PHYSDEV dev )
2232 DC *dc = get_nulldrv_dc( dev );
2234 if (dc->path.state != PATH_Closed)
2236 SetLastError( ERROR_CAN_NOT_COMPLETE );
2239 if (!PATH_FillPath( dc, &dc->path )) return FALSE;
2240 /* FIXME: Should the path be emptied even if conversion failed? */
2241 PATH_EmptyPath( &dc->path );
2245 BOOL nulldrv_StrokeAndFillPath( PHYSDEV dev )
2247 DC *dc = get_nulldrv_dc( dev );
2249 if (dc->path.state != PATH_Closed)
2251 SetLastError( ERROR_CAN_NOT_COMPLETE );
2254 if (!PATH_FillPath( dc, &dc->path )) return FALSE;
2255 if (!PATH_StrokePath( dc, &dc->path )) return FALSE;
2256 PATH_EmptyPath( &dc->path );
2260 BOOL nulldrv_StrokePath( PHYSDEV dev )
2262 DC *dc = get_nulldrv_dc( dev );
2264 if (dc->path.state != PATH_Closed)
2266 SetLastError( ERROR_CAN_NOT_COMPLETE );
2269 if (!PATH_StrokePath( dc, &dc->path )) return FALSE;
2270 PATH_EmptyPath( &dc->path );
2274 BOOL nulldrv_FlattenPath( PHYSDEV dev )
2276 DC *dc = get_nulldrv_dc( dev );
2278 if (dc->path.state != PATH_Closed)
2280 SetLastError( ERROR_CAN_NOT_COMPLETE );
2283 return PATH_FlattenPath( &dc->path );
2286 BOOL nulldrv_WidenPath( PHYSDEV dev )
2288 DC *dc = get_nulldrv_dc( dev );
2290 if (dc->path.state != PATH_Closed)
2292 SetLastError( ERROR_CAN_NOT_COMPLETE );
2295 return PATH_WidenPath( dc );
2298 const struct gdi_dc_funcs path_driver =
2300 NULL, /* pAbortDoc */
2301 pathdrv_AbortPath, /* pAbortPath */
2302 NULL, /* pAlphaBlend */
2303 NULL, /* pAngleArc */
2306 NULL, /* pBeginPath */
2307 NULL, /* pBlendImage */
2308 NULL, /* pChoosePixelFormat */
2310 NULL, /* pCloseFigure */
2311 NULL, /* pCreateBitmap */
2312 NULL, /* pCreateCompatibleDC */
2313 pathdrv_CreateDC, /* pCreateDC */
2314 NULL, /* pCreateDIBSection */
2315 NULL, /* pDeleteBitmap */
2316 pathdrv_DeleteDC, /* pDeleteDC */
2317 NULL, /* pDeleteObject */
2318 NULL, /* pDescribePixelFormat */
2319 NULL, /* pDeviceCapabilities */
2320 NULL, /* pEllipse */
2322 NULL, /* pEndPage */
2323 pathdrv_EndPath, /* pEndPath */
2324 NULL, /* pEnumFonts */
2325 NULL, /* pEnumICMProfiles */
2326 NULL, /* pExcludeClipRect */
2327 NULL, /* pExtDeviceMode */
2328 NULL, /* pExtEscape */
2329 NULL, /* pExtFloodFill */
2330 NULL, /* pExtSelectClipRgn */
2331 NULL, /* pExtTextOut */
2332 NULL, /* pFillPath */
2333 NULL, /* pFillRgn */
2334 NULL, /* pFlattenPath */
2335 NULL, /* pFontIsLinked */
2336 NULL, /* pFrameRgn */
2337 NULL, /* pGdiComment */
2338 NULL, /* pGdiRealizationInfo */
2339 NULL, /* pGetCharABCWidths */
2340 NULL, /* pGetCharABCWidthsI */
2341 NULL, /* pGetCharWidth */
2342 NULL, /* pGetDeviceCaps */
2343 NULL, /* pGetDeviceGammaRamp */
2344 NULL, /* pGetFontData */
2345 NULL, /* pGetFontUnicodeRanges */
2346 NULL, /* pGetGlyphIndices */
2347 NULL, /* pGetGlyphOutline */
2348 NULL, /* pGetICMProfile */
2349 NULL, /* pGetImage */
2350 NULL, /* pGetKerningPairs */
2351 NULL, /* pGetNearestColor */
2352 NULL, /* pGetOutlineTextMetrics */
2353 NULL, /* pGetPixel */
2354 NULL, /* pGetPixelFormat */
2355 NULL, /* pGetSystemPaletteEntries */
2356 NULL, /* pGetTextCharsetInfo */
2357 NULL, /* pGetTextExtentExPoint */
2358 NULL, /* pGetTextExtentExPointI */
2359 NULL, /* pGetTextFace */
2360 NULL, /* pGetTextMetrics */
2361 NULL, /* pIntersectClipRect */
2362 NULL, /* pInvertRgn */
2364 NULL, /* pModifyWorldTransform */
2365 pathdrv_MoveTo, /* pMoveTo */
2366 NULL, /* pOffsetClipRgn */
2367 NULL, /* pOffsetViewportOrg */
2368 NULL, /* pOffsetWindowOrg */
2369 NULL, /* pPaintRgn */
2372 NULL, /* pPolyBezier */
2373 NULL, /* pPolyBezierTo */
2374 NULL, /* pPolyDraw */
2375 NULL, /* pPolyPolygon */
2376 NULL, /* pPolyPolyline */
2377 NULL, /* pPolygon */
2378 NULL, /* pPolyline */
2379 NULL, /* pPolylineTo */
2380 NULL, /* pPutImage */
2381 NULL, /* pRealizeDefaultPalette */
2382 NULL, /* pRealizePalette */
2383 NULL, /* pRectangle */
2384 NULL, /* pResetDC */
2385 NULL, /* pRestoreDC */
2386 NULL, /* pRoundRect */
2388 NULL, /* pScaleViewportExt */
2389 NULL, /* pScaleWindowExt */
2390 NULL, /* pSelectBitmap */
2391 NULL, /* pSelectBrush */
2392 NULL, /* pSelectClipPath */
2393 NULL, /* pSelectFont */
2394 NULL, /* pSelectPalette */
2395 NULL, /* pSelectPen */
2396 NULL, /* pSetArcDirection */
2397 NULL, /* pSetBkColor */
2398 NULL, /* pSetBkMode */
2399 NULL, /* pSetDCBrushColor */
2400 NULL, /* pSetDCPenColor */
2401 NULL, /* pSetDIBColorTable */
2402 NULL, /* pSetDIBitsToDevice */
2403 NULL, /* pSetDeviceClipping */
2404 NULL, /* pSetDeviceGammaRamp */
2405 NULL, /* pSetLayout */
2406 NULL, /* pSetMapMode */
2407 NULL, /* pSetMapperFlags */
2408 NULL, /* pSetPixel */
2409 NULL, /* pSetPixelFormat */
2410 NULL, /* pSetPolyFillMode */
2411 NULL, /* pSetROP2 */
2412 NULL, /* pSetRelAbs */
2413 NULL, /* pSetStretchBltMode */
2414 NULL, /* pSetTextAlign */
2415 NULL, /* pSetTextCharacterExtra */
2416 NULL, /* pSetTextColor */
2417 NULL, /* pSetTextJustification */
2418 NULL, /* pSetViewportExt */
2419 NULL, /* pSetViewportOrg */
2420 NULL, /* pSetWindowExt */
2421 NULL, /* pSetWindowOrg */
2422 NULL, /* pSetWorldTransform */
2423 NULL, /* pStartDoc */
2424 NULL, /* pStartPage */
2425 NULL, /* pStretchBlt */
2426 NULL, /* pStretchDIBits */
2427 NULL, /* pStrokeAndFillPath */
2428 NULL, /* pStrokePath */
2429 NULL, /* pSwapBuffers */
2430 NULL, /* pUnrealizePalette */
2431 NULL, /* pWidenPath */
2432 /* OpenGL not supported */