Release 971221
Fri Dec 19 10:50:46 1997 Douglas Ridgway <ridgway@winehq.com>
* [Make.rules.in] [Makefile.in] [documentation/Makefile.in]
[documentation/README.documentation]
First cut at Wine API documentation. No longer install reference
manual by default.
Wed Dec 17 21:32:23 1997 Andreas Mohr <100.30936@germany.net>
* [files/file.c]
Fixed GetTempFileName16() to use current path of requested drive
as needed.
* [if1632/Makefile.in] [if1632/builtin.c] [if1632/dciman32.spec]
[if1632/msvfw32.spec] [if1632/tapi32.spec] [if1632/wow32.spec]
Added misc DLLs needed by various apps.
Wed Dec 17 12:01:50 1997 Morten Eriksen <mortene@sim.no>
* [if1632/gdi32.spec] [include/windows.h] [objects/palette.c]
Inserted empty stub for CreateHalftonePalette.
Tue Dec 16 22:08:06 1997 Huw D M Davies <h.davies1@physics.oxford.ac.uk>
* [windows/mdi.c]
Use VK_TAB instead of VK_SEPARATOR in TranslateMDISysAccel().
* [graphics/metafiledrv/init.c]
DeleteDC() on a MetaDC doesn't do anything - it shouldn't. Therefore
fix cleanup of MetaDCs in CloseMetaFile(); they now actually get
removed from the GDI heap!
* [graphics/x11drv/xfont.c]
Preserve FO_MATCH_XYINDEP flag in XFONT_MatchFIList(). Should reduce
the number of bold-italic matches.
Tue Dec 16 20:11:43 1997 Bertho Stultiens <bertho@panter.soci.aau.dk>
* [graphics/painting.c]
Included an implementation of DrawState
* [if1632/thunk.c]
Changed many fprintfs into dprintf_thunk
* [include/cache.h] [graphics/cache.c]
New files to hold cached handles to regulary used GDI object.
* [include/windows.h]
Added DRAWSTATExx typedefs
Added DSS_DEFAULT define for DrawState
* [objects/text.c]
New implementation of GrayString()
* [controls/uitools.c]
Implemented DrawFrameControl() functions
Changed DrawEdge() behaviour to win95 implementation
Mon Dec 15 23:43:01 1997 Martin Boehme <boehme@informatik.mu-luebeck.de>
* [graphics/path.c] [include/path.h] [graphics/painting.c]
[if1632/gdi32.spec] [include/gdi.h] [include/windows.h]
[objects/dc.c]
Added preliminary support for GDI paths.
* [objects/dc.c]
Added DC_Init_DC_INFO function for initializing WIN_DC_INFO
structure.
* [include/windows.h] [include/gdi.h] [objects/gdiobj.c]
Added DEFAULT_GUI_FONT.
* [include/winerror.h]
Added a few error codes.
* [memory/heap.c]
Changed HeapAlloc to make the correct calls to SetLastError
(now conforms to NT's behaviour).
* [windows/win.c]
Changed WIN_CreateWindowEx to allow child windows with zero
width / height.
Sun Dec 14 12:01:07 1997 Alexandre Julliard <julliard@lrc.epfl.ch>
* [if1632/*] [relay32/*]
Moved all 32-bit relay stuff to relay32/
* [fi1632/thunk.c] [win32/kernel32.c]
Moved all KERNEL32 ordinal functions to kernel32.c
* [memory/selector.c]
Initialize selectors in AllocSelectorArray.
* [tools/build.c]
Generate C instead of assembly for Win32 relays.
Fixed stack corruption in CallTo16 functions, found by Bertho
Stultiens.
Sun Dec 14 10:55:00 1997 Andreas Mohr <100.30936@germany.net>
* [if1632/Makefile.in] [if1632/builtin.c] [if1632/ole2thk.spec]
Added built-in OLE2THK.DLL.
* [if1632/toolhelp.spec] [include/toolhelp.h] [memory/selector.c]
[misc/toolhelp.c]
Added stubs for StackTraceFirst(), StackTraceCSIPFirst(),
StackTraceNext(), UTSelectorOffsetToLinear()
and UTLinearToSelectorOffset().
Sat Dec 13 17:26:41 1997 Alex Korobka <alex@trantor.pharm.sunysb.edu>
* [misc/winsock.c]
32-bit API fixes for reported problems (thanks to Marcus
and David).
* [graphics/x11drv/xfont.c]
Little tweak in point size calculation.
* [windows/defwnd.c] [windows/dce.c] [windows/winhelp.c]
[windows/winproc.c] [windows/win.c]
Bug fixes.
Sat Dec 13 16:35:14 1997 Kai Morich <kai.morich@rhein-neckar.netsurf.de>
* [files/dos_fs.c]
OpenFile with empty filename and OF_PARSE returns current dir.
* [misc/commdlg.c]
Ignore initial dir if bogus.
* [files/file.c]
Locking an identic region in a file must not be an error.
* [misc/lstr.c]
Use wide char ctype functions.
Fri Dec 12 23:46:22 1997 Uwe Bonnes <bon@elektron.ikp.physik.tu-darmstadt.de>
* [file/drive.c]
First attempt for GetDiskFreeSpaceEx.
Fri Dec 12 23:18:41 1997 Marcus Meissner <msmeissn@cip.informatik.uni-erlangen.de>
* [loader/pe_resource.c]
Fixed wrongly appearing menus problem (only use default lookups in
last resource subtree).
* [multimedia/*.c]
Added win32 support for time* and joy* lowlevel drivers,
(not excessively tested), some misc fixes and cleanups.
* [misc/shellord.c][misc/shell.c][ole/folders.c][ole/ifs.c]
[include/interfaces.h][include/shlobj.h]
Added some more undocumented SHELL32 functions, some shell folder
interface stubs added, SHGetMalloc, SHGetDesktopFolder,
SHGetSpecialFolderLocation, SHGetPathFromIDList stubs added,
IMalloc, IUnknown implemented.
* [windows/msgbox.c]
Implemented MessageBoxIndirect*, some internal changes.
* [if1632/thunk.c]
KERNEL_431 implemented.
* [objects/gdiobj.c]
GetCurrentObject implemented.
Wed Dec 3 01:09:17 1997 Gordon Chaffee <chaffee@apu.cs.berkeley.edu>
* [objects/dib.c]
Fix a couple small DIB problems.
* [controls/edit.c]
Fix a typo.
* [files/dos_fs.c]
Try normal readdir in case fs is specified as vfat but isn't.
* [files/profile.c]
Implementation of WritePrivateProfileSection32A from Uwe Bonnes.
* [misc/printdrv.c]
OpenPrinter32A stub, helps Word97 start.
* [objects/text.c]
Fixup implementation of GetTextCharsetInfo.
* [scheduler/process.c]
Fix environment variable expansion.
* [win32/code_page.c]
Make MultiByteToWideChar and WideCharToMultiByte conform in return
values and error conditions to those in Windows NT 4.0.
* [windows/message.c]
Fix broadcasting problems in Win32. The Win32 docs say to use
HWND_TOPMOST to broadcast to all Win32 Windows.
* [memory/virtual.c] [loader/pe_image.c]
Do not map in VirtualAlloc if address is specified and space is
not available. This is required by Win32.
* [include/pen.h] [include/x11drv.h] [objects/dc.c]
[objects/pen.c] [graphics/x11drv/pen.c]
Support for ExtCreatePen.
Tue Dec 2 20:22:06 1997 Morten Welinder <terra@diku.dk>
* [*/*.c] [*/*.h]
Add lots of prototypes.
* [if1632/kernel32.spec][include/windows.h][include/winnt.h]
[misc/cpu.c]
Define IsProcessorFeaturePresent.
* [misc/crtdll.c]
(CRTDLL__getcwd): Allocate enough memory for the terminating zero.
* [misc/ver.c]
Improve check for null component in _find_data[AW]. Plug leaks
in VerQueryValue*.
* [win32/console.c][if1632/kernel32.spec]
Add stubs for GetConsoleCursorInfo32, SetConsoleCursorInfo32.
* [windows/message.c][if1632/user32.spec][include/windows.h]
Define SendMessageTimeout*.
* [graphics/x11drv/xfont.c]
Change algorithm of __genericCheckSum to be alignment safe.
* [misc/main.c] [misc/winsock.c] [misc/winsock_dns.c]
Include winsock.h early to avoid Solaris problem.
* [include/windows.h]
Undef FSHIFT before we define it.
* [rc/winerc.c]
Include <fcntl.h> instead of <sys/fcntl.h>.
* [files/file.c]
Use strerror in FILE_SetDosError if available.
* [include/config.h.in] [configure.in]
Check for strerror.
* [objects/gdiobj.c]
Make static font structures aligned.
Mon Dec 1 10:10:21 1997 Karl Garrison <karlos@eznet.net>
* [win32/console.c] [if1632/kernel32.spec] [include/windows.h]
Added stub for GetNumberOfConsoleMouseButtons.
Added stub for PeekConsoleInput(A,W).
Fixed parameter list for WriteConsole(A,W).
GetNumberOfConsoleInputEvents now returns 0 events instead of 1
(since low-level console functions are not yet supported).
GetConsoleMode no longer returns ENABLE_WINDOW_INPUT and
ENABLE_MOUSE_INPUT since these are not currently implemented.
diff --git a/graphics/path.c b/graphics/path.c
new file mode 100644
index 0000000..e4e4013
--- /dev/null
+++ b/graphics/path.c
@@ -0,0 +1,704 @@
+/*
+ * Graphics paths (BeginPath, EndPath etc.)
+ *
+ * Copyright 1997 Martin Boehme
+ */
+
+#include <assert.h>
+#include <malloc.h>
+
+#include "windows.h"
+#include "winerror.h"
+
+#include "dc.h"
+#include "debug.h"
+#include "path.h"
+
+/* Notes on the implementation
+ *
+ * The implementation is based on dynamically resizable arrays of points and
+ * flags. I dithered for a bit before deciding on this implementation, and
+ * I had even done a bit of work on a linked list version before switching
+ * to arrays. It's a bit of a tradeoff. When you use linked lists, the
+ * implementation of FlattenPath is easier, because you can rip the
+ * PT_BEZIERTO entries out of the middle of the list and link the
+ * corresponding PT_LINETO entries in. However, when you use arrays,
+ * PathToRegion becomes easier, since you can essentially just pass your array
+ * of points to CreatePolyPolygonRgn. Also, if I'd used linked lists, I would
+ * have had the extra effort of creating a chunk-based allocation scheme
+ * in order to use memory effectively. That's why I finally decided to use
+ * arrays. Note by the way that the array based implementation has the same
+ * linear time complexity that linked lists would have since the arrays grow
+ * exponentially.
+ *
+ * The points are stored in the path in device coordinates. This is
+ * consistent with the way Windows does things (for instance, see the Win32
+ * SDK documentation for GetPath).
+ *
+ * The word "stroke" appears in several places (e.g. in the flag
+ * GdiPath.newStroke). A stroke consists of a PT_MOVETO followed by one or
+ * more PT_LINETOs or PT_BEZIERTOs, up to, but not including, the next
+ * PT_MOVETO. Note that this is not the same as the definition of a figure;
+ * a figure can contain several strokes.
+ *
+ * I modified the drawing functions (MoveTo, LineTo etc.) to test whether
+ * the path is open and to call the corresponding function in path.c if this
+ * is the case. A more elegant approach would be to modify the function
+ * pointers in the DC_FUNCTIONS structure; however, this would be a lot more
+ * complex. Also, the performance degradation caused by my approach in the
+ * case where no path is open is so small that it cannot be measured.
+ *
+ * Martin Boehme
+ */
+
+/* FIXME: A lot of stuff isn't implemented yet. There is much more to come. */
+
+#define NUM_ENTRIES_INITIAL 16 /* Initial size of points / flags arrays */
+#define GROW_FACTOR_NUMER 2 /* Numerator of grow factor for the array */
+#define GROW_FACTOR_DENOM 1 /* Denominator of grow factor */
+
+
+static BOOL32 PATH_PathToRegion(const GdiPath *pPath, INT32 nPolyFillMode,
+ HRGN32 *pHrgn);
+static void PATH_EmptyPath(GdiPath *pPath);
+static BOOL32 PATH_AddEntry(GdiPath *pPath, POINT32 point, BYTE flags);
+static BOOL32 PATH_ReserveEntries(GdiPath *pPath, INT32 numEntries);
+static BOOL32 PATH_GetPathFromHDC(HDC32 hdc, GdiPath **ppPath);
+
+/***********************************************************************
+ * BeginPath32 (GDI32.9)
+ */
+BOOL32 WINAPI BeginPath32(HDC32 hdc)
+{
+ GdiPath *pPath;
+
+ /* Get pointer to path */
+ if(!PATH_GetPathFromHDC(hdc, &pPath))
+ {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+
+ /* If path is already open, do nothing */
+ if(pPath->state==PATH_Open)
+ return TRUE;
+
+ /* Make sure that path is empty */
+ PATH_EmptyPath(pPath);
+
+ /* Initialize variables for new path */
+ pPath->newStroke=TRUE;
+ pPath->state=PATH_Open;
+
+ return TRUE;
+}
+
+
+/***********************************************************************
+ * EndPath32 (GDI32.78)
+ */
+BOOL32 WINAPI EndPath32(HDC32 hdc)
+{
+ GdiPath *pPath;
+
+ /* Get pointer to path */
+ if(!PATH_GetPathFromHDC(hdc, &pPath))
+ {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+
+ /* Check that path is currently being constructed */
+ if(pPath->state!=PATH_Open)
+ {
+ SetLastError(ERROR_CAN_NOT_COMPLETE);
+ return FALSE;
+ }
+
+ /* Set flag to indicate that path is finished */
+ pPath->state=PATH_Closed;
+
+ return TRUE;
+}
+
+
+/***********************************************************************
+ * AbortPath32 (GDI32.1)
+ */
+BOOL32 WINAPI AbortPath32(HDC32 hdc)
+/* FIXME: Check that SetLastError is being called correctly */
+{
+ GdiPath *pPath;
+
+ /* Get pointer to path */
+ if(!PATH_GetPathFromHDC(hdc, &pPath))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ /* Remove all entries from the path */
+ PATH_EmptyPath(pPath);
+
+ return TRUE;
+}
+
+
+/***********************************************************************
+ * CloseFigure32 (GDI32.16)
+ */
+BOOL32 WINAPI CloseFigure32(HDC32 hdc)
+/* FIXME: Check that SetLastError is being called correctly */
+{
+ GdiPath *pPath;
+
+ /* Get pointer to path */
+ if(!PATH_GetPathFromHDC(hdc, &pPath))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ /* Check that path is open */
+ if(pPath->state!=PATH_Open)
+ {
+ SetLastError(ERROR_CAN_NOT_COMPLETE);
+ return FALSE;
+ }
+
+ /* Set PT_CLOSEFIGURE on the last entry and start a new stroke */
+ if(pPath->numEntriesUsed)
+ {
+ pPath->pFlags[pPath->numEntriesUsed-1]|=PT_CLOSEFIGURE;
+ pPath->newStroke=TRUE;
+ }
+
+ return TRUE;
+}
+
+
+/***********************************************************************
+ * GetPath32 (GDI32.210)
+ */
+INT32 WINAPI GetPath32(HDC32 hdc, LPPOINT32 pPoints, LPBYTE pTypes,
+ INT32 nSize)
+{
+ GdiPath *pPath;
+ BOOL32 temp_flag;
+
+ /* Get pointer to path */
+ if(!PATH_GetPathFromHDC(hdc, &pPath))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return -1;
+ }
+
+ /* Check that path is closed */
+ if(pPath->state!=PATH_Closed)
+ {
+ SetLastError(ERROR_CAN_NOT_COMPLETE);
+ return -1;
+ }
+
+ if(nSize==0)
+ return pPath->numEntriesUsed;
+ else if(nSize<pPath->numEntriesUsed)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return -1;
+ }
+ else
+ {
+ memcpy(pPoints, pPath->pPoints, sizeof(POINT32)*pPath->numEntriesUsed);
+ memcpy(pTypes, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
+
+ /* Convert the points to logical coordinates */
+ temp_flag=DPtoLP32(hdc, pPoints, pPath->numEntriesUsed);
+
+ /* Since hdc is valid, conversion should never fail */
+ assert(temp_flag);
+
+ return pPath->numEntriesUsed;
+ }
+}
+
+
+/***********************************************************************
+ * PathToRegion32 (GDI32.261)
+ */
+HRGN32 WINAPI PathToRegion32(HDC32 hdc)
+/* FIXME: Check that SetLastError is being called correctly */
+/* The documentation does not state this explicitly, but a test under Windows
+ * shows that the region which is returned should be in device coordinates.
+ */
+{
+ GdiPath *pPath;
+ HRGN32 hrgnRval;
+
+ /* Get pointer to path */
+ if(!PATH_GetPathFromHDC(hdc, &pPath))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ /* Check that path is closed */
+ if(pPath->state!=PATH_Closed)
+ {
+ SetLastError(ERROR_CAN_NOT_COMPLETE);
+ return 0;
+ }
+
+ /* FIXME: Should we empty the path even if conversion failed? */
+ if(PATH_PathToRegion(pPath, GetPolyFillMode32(hdc), &hrgnRval))
+ PATH_EmptyPath(pPath);
+ else
+ hrgnRval=0;
+
+ return hrgnRval;
+}
+
+
+/***********************************************************************
+ * FillPath32 (GDI32.100)
+ */
+BOOL32 WINAPI FillPath32(HDC32 hdc)
+/* FIXME: Check that SetLastError is being called correctly */
+{
+ GdiPath *pPath;
+ INT32 mapMode;
+ POINT32 ptViewportExt, ptViewportOrg, ptWindowExt, ptWindowOrg;
+ HRGN32 hrgn;
+
+ /* Get pointer to path */
+ if(!PATH_GetPathFromHDC(hdc, &pPath))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ /* Check that path is closed */
+ if(pPath->state!=PATH_Closed)
+ {
+ SetLastError(ERROR_CAN_NOT_COMPLETE);
+ return FALSE;
+ }
+
+ /* Construct a region from the path and fill it */
+ if(PATH_PathToRegion(pPath, GetPolyFillMode32(hdc), &hrgn))
+ {
+ /* Since PaintRgn interprets the region as being in logical coordinates
+ * but the points we store for the path are already in device
+ * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
+ */
+
+ /* Save the information about the old mapping mode */
+ mapMode=GetMapMode32(hdc);
+ GetViewportExtEx32(hdc, &ptViewportExt);
+ GetViewportOrgEx32(hdc, &ptViewportOrg);
+ GetWindowExtEx32(hdc, &ptWindowExt);
+ GetWindowOrgEx32(hdc, &ptWindowOrg);
+
+ /* FIXME: Once world transforms become available, we will have to do
+ * a GetWorldTransform, too (along with a SetWorldTransform later on).
+ * Moral: Perhaps I should have used SaveDC right away. The reason why
+ * I didn't is that I wanted to avoid the overhead of a full SaveDC
+ * (especially since SaveDC now saves the current path as well).
+ */
+
+ /* Set MM_TEXT */
+ SetMapMode32(hdc, MM_TEXT);
+
+ /* Paint the region */
+ PaintRgn32(hdc, hrgn);
+
+ /* Restore the old mapping mode */
+ SetMapMode32(hdc, mapMode);
+ SetViewportExtEx32(hdc, ptViewportExt.x, ptViewportExt.y, NULL);
+ SetViewportOrgEx32(hdc, ptViewportOrg.x, ptViewportOrg.y, NULL);
+ SetWindowExtEx32(hdc, ptWindowExt.x, ptWindowExt.y, NULL);
+ SetWindowOrgEx32(hdc, ptWindowOrg.x, ptWindowOrg.y, NULL);
+
+ /* Empty the path */
+ PATH_EmptyPath(pPath);
+ return TRUE;
+ }
+ else
+ {
+ /* FIXME: Should the path be emptied even if conversion failed? */
+ /* PATH_EmptyPath(pPath); */
+ return FALSE;
+ }
+}
+
+
+/***********************************************************************
+ * SelectClipPath32 (GDI32.296)
+ */
+BOOL32 WINAPI SelectClipPath32(HDC32 hdc, int iMode)
+/* FIXME: Check that SetLastError is being called correctly */
+{
+ GdiPath *pPath;
+ HRGN32 hrgnPath, hrgnClip;
+ BOOL32 success = FALSE;
+
+ /* Get pointer to path */
+ if(!PATH_GetPathFromHDC(hdc, &pPath))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ /* Check that path is closed */
+ if(pPath->state!=PATH_Closed)
+ {
+ SetLastError(ERROR_CAN_NOT_COMPLETE);
+ return FALSE;
+ }
+
+ /* Construct a region from the path */
+ if(PATH_PathToRegion(pPath, GetPolyFillMode32(hdc), &hrgnPath))
+ {
+ hrgnClip=CreateRectRgn32(0, 0, 0, 0);
+ if(hrgnClip!=NULL)
+ {
+ success=(GetClipRgn32(hdc, hrgnClip)!=-1) &&
+ (CombineRgn32(hrgnClip, hrgnClip, hrgnPath, iMode)!=ERROR) &&
+ (SelectClipRgn32(hdc, hrgnClip)!=ERROR);
+ DeleteObject32(hrgnClip);
+ }
+
+ DeleteObject32(hrgnPath);
+
+ /* Empty the path */
+ if(success)
+ PATH_EmptyPath(pPath);
+ /* FIXME: Should this function delete the path even if it failed? */
+
+ return success;
+ }
+ else
+ return FALSE;
+}
+
+
+/***********************************************************************
+ * Exported functions
+ */
+
+/* PATH_InitGdiPath
+ *
+ * Initializes the GdiPath structure.
+ */
+void PATH_InitGdiPath(GdiPath *pPath)
+{
+ assert(pPath!=NULL);
+
+ pPath->state=PATH_Null;
+ pPath->pPoints=NULL;
+ pPath->pFlags=NULL;
+ pPath->numEntriesUsed=0;
+ pPath->numEntriesAllocated=0;
+}
+
+/* PATH_DestroyGdiPath
+ *
+ * Destroys a GdiPath structure (frees the memory in the arrays).
+ */
+void PATH_DestroyGdiPath(GdiPath *pPath)
+{
+ assert(pPath!=NULL);
+
+ free(pPath->pPoints);
+ free(pPath->pFlags);
+}
+
+/* PATH_AssignGdiPath
+ *
+ * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
+ * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
+ * not just the pointers. Since this means that the arrays in pPathDest may
+ * need to be resized, pPathDest should have been initialized using
+ * PATH_InitGdiPath (in C++, this function would be an assignment operator,
+ * not a copy constructor).
+ * Returns TRUE if successful, else FALSE.
+ */
+BOOL32 PATH_AssignGdiPath(GdiPath *pPathDest, const GdiPath *pPathSrc)
+{
+ assert(pPathDest!=NULL && pPathSrc!=NULL);
+
+ /* Make sure destination arrays are big enough */
+ if(!PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed))
+ return FALSE;
+
+ /* Perform the copy operation */
+ memcpy(pPathDest->pPoints, pPathSrc->pPoints,
+ sizeof(POINT32)*pPathSrc->numEntriesUsed);
+ memcpy(pPathDest->pFlags, pPathSrc->pFlags,
+ sizeof(INT32)*pPathSrc->numEntriesUsed);
+ pPathDest->state=pPathSrc->state;
+ pPathDest->numEntriesUsed=pPathSrc->numEntriesUsed;
+ pPathDest->newStroke=pPathSrc->newStroke;
+
+ return TRUE;
+}
+
+/* PATH_MoveTo
+ *
+ * Should be called when a MoveTo is performed on a DC that has an
+ * open path. This starts a new stroke. Returns TRUE if successful, else
+ * FALSE.
+ */
+BOOL32 PATH_MoveTo(HDC32 hdc)
+{
+ GdiPath *pPath;
+
+ /* Get pointer to path */
+ if(!PATH_GetPathFromHDC(hdc, &pPath))
+ return FALSE;
+
+ /* Check that path is open */
+ if(pPath->state!=PATH_Open)
+ /* FIXME: Do we have to call SetLastError? */
+ return FALSE;
+
+ /* Start a new stroke */
+ pPath->newStroke=TRUE;
+
+ return TRUE;
+}
+
+/* PATH_LineTo
+ *
+ * Should be called when a LineTo is performed on a DC that has an
+ * open path. This adds a PT_LINETO entry to the path (and possibly
+ * a PT_MOVETO entry, if this is the first LineTo in a stroke).
+ * Returns TRUE if successful, else FALSE.
+ */
+BOOL32 PATH_LineTo(HDC32 hdc, INT32 x, INT32 y)
+{
+ GdiPath *pPath;
+ POINT32 point, pointCurPos;
+
+ /* Get pointer to path */
+ if(!PATH_GetPathFromHDC(hdc, &pPath))
+ return FALSE;
+
+ /* Check that path is open */
+ if(pPath->state!=PATH_Open)
+ /* FIXME: Do we have to call SetLastError? */
+ return FALSE;
+
+ /* Convert point to device coordinates */
+ point.x=x;
+ point.y=y;
+ if(!LPtoDP32(hdc, &point, 1))
+ return FALSE;
+
+ /* Add a PT_MOVETO if necessary */
+ if(pPath->newStroke)
+ {
+ pPath->newStroke=FALSE;
+ if(!GetCurrentPositionEx32(hdc, &pointCurPos) ||
+ !LPtoDP32(hdc, &pointCurPos, 1))
+ return FALSE;
+ if(!PATH_AddEntry(pPath, pointCurPos, PT_MOVETO))
+ return FALSE;
+ }
+
+ /* Add a PT_LINETO entry */
+ return PATH_AddEntry(pPath, point, PT_LINETO);
+}
+
+
+/***********************************************************************
+ * Internal functions
+ */
+
+/* PATH_PathToRegion
+ *
+ * Creates a region from the specified path using the specified polygon
+ * filling mode. The path is left unchanged. A handle to the region that
+ * was created is stored in *pHrgn. If successful, TRUE is returned; if an
+ * error occurs, SetLastError is called with the appropriate value and
+ * FALSE is returned.
+ */
+static BOOL32 PATH_PathToRegion(const GdiPath *pPath, INT32 nPolyFillMode,
+ HRGN32 *pHrgn)
+{
+ int numStrokes, iStroke, i;
+ INT32 *pNumPointsInStroke;
+ HRGN32 hrgn;
+
+ assert(pPath!=NULL);
+ assert(pHrgn!=NULL);
+
+ /* FIXME: What happens when number of points is zero? */
+
+ /* First pass: Find out how many strokes there are in the path */
+ /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
+ numStrokes=0;
+ for(i=0; i<pPath->numEntriesUsed; i++)
+ if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
+ numStrokes++;
+
+ /* Allocate memory for number-of-points-in-stroke array */
+ pNumPointsInStroke=(int *)malloc(sizeof(int)*numStrokes);
+ if(!pNumPointsInStroke)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ /* Second pass: remember number of points in each polygon */
+ iStroke=-1; /* Will get incremented to 0 at beginning of first stroke */
+ for(i=0; i<pPath->numEntriesUsed; i++)
+ {
+ /* Is this the beginning of a new stroke? */
+ if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
+ {
+ iStroke++;
+ pNumPointsInStroke[iStroke]=0;
+ }
+
+ pNumPointsInStroke[iStroke]++;
+ }
+
+ /* Create a region from the strokes */
+ hrgn=CreatePolyPolygonRgn32(pPath->pPoints, pNumPointsInStroke,
+ numStrokes, nPolyFillMode);
+ if(hrgn==NULL)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ /* Free memory for number-of-points-in-stroke array */
+ free(pNumPointsInStroke);
+
+ /* Success! */
+ *pHrgn=hrgn;
+ return TRUE;
+}
+
+/* PATH_EmptyPath
+ *
+ * Removes all entries from the path and sets the path state to PATH_Null.
+ */
+static void PATH_EmptyPath(GdiPath *pPath)
+{
+ assert(pPath!=NULL);
+
+ pPath->state=PATH_Null;
+ pPath->numEntriesUsed=0;
+}
+
+/* PATH_AddEntry
+ *
+ * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
+ * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
+ * successful, FALSE otherwise (e.g. if not enough memory was available).
+ */
+BOOL32 PATH_AddEntry(GdiPath *pPath, POINT32 point, BYTE flags)
+{
+ assert(pPath!=NULL);
+
+ /* Check that path is open */
+ if(pPath->state!=PATH_Open)
+ return FALSE;
+
+ /* Reserve enough memory for an extra path entry */
+ if(!PATH_ReserveEntries(pPath, pPath->numEntriesUsed+1))
+ return FALSE;
+
+ /* Store information in path entry */
+ pPath->pPoints[pPath->numEntriesUsed]=point;
+ pPath->pFlags[pPath->numEntriesUsed]=flags;
+
+ /* Increment entry count */
+ pPath->numEntriesUsed++;
+
+ return TRUE;
+}
+
+/* PATH_ReserveEntries
+ *
+ * Ensures that at least "numEntries" entries (for points and flags) have
+ * been allocated; allocates larger arrays and copies the existing entries
+ * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
+ */
+static BOOL32 PATH_ReserveEntries(GdiPath *pPath, INT32 numEntries)
+{
+ INT32 numEntriesToAllocate;
+ POINT32 *pPointsNew;
+ BYTE *pFlagsNew;
+
+ assert(pPath!=NULL);
+ assert(numEntries>=0);
+
+ /* Do we have to allocate more memory? */
+ if(numEntries > pPath->numEntriesAllocated)
+ {
+ /* Find number of entries to allocate. We let the size of the array
+ * grow exponentially, since that will guarantee linear time
+ * complexity. */
+ if(pPath->numEntriesAllocated)
+ {
+ numEntriesToAllocate=pPath->numEntriesAllocated;
+ while(numEntriesToAllocate<numEntries)
+ numEntriesToAllocate=numEntriesToAllocate*GROW_FACTOR_NUMER/
+ GROW_FACTOR_DENOM;
+ }
+ else
+ numEntriesToAllocate=NUM_ENTRIES_INITIAL;
+
+ /* Allocate new arrays */
+ pPointsNew=(POINT32 *)malloc(numEntriesToAllocate * sizeof(POINT32));
+ if(!pPointsNew)
+ return FALSE;
+ pFlagsNew=(BYTE *)malloc(numEntriesToAllocate * sizeof(BYTE));
+ if(!pFlagsNew)
+ {
+ free(pPointsNew);
+ return FALSE;
+ }
+
+ /* Copy old arrays to new arrays and discard old arrays */
+ if(pPath->pPoints)
+ {
+ assert(pPath->pFlags);
+
+ memcpy(pPointsNew, pPath->pPoints,
+ sizeof(POINT32)*pPath->numEntriesUsed);
+ memcpy(pFlagsNew, pPath->pFlags,
+ sizeof(BYTE)*pPath->numEntriesUsed);
+
+ free(pPath->pPoints);
+ free(pPath->pFlags);
+ }
+ pPath->pPoints=pPointsNew;
+ pPath->pFlags=pFlagsNew;
+ pPath->numEntriesAllocated=numEntriesToAllocate;
+ }
+
+ return TRUE;
+}
+
+/* PATH_GetPathFromHDC
+ *
+ * Retrieves a pointer to the GdiPath structure contained in an HDC and
+ * places it in *ppPath. TRUE is returned if successful, FALSE otherwise.
+ */
+static BOOL32 PATH_GetPathFromHDC(HDC32 hdc, GdiPath **ppPath)
+{
+ DC *pDC;
+
+ pDC=DC_GetDCPtr(hdc);
+ if(pDC)
+ {
+ *ppPath=&pDC->w.path;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}