
D3DKMTSetVidPnSourceOwner needs to be implemented in the graphics drivers because we need to maintain the VidPN source ownership information list in the graphics drivers. For example, the graphics drivers need to release the exclusive ownership when a new window is moved to a monitor which has been taken exclusive ownership. Signed-off-by: Zhiyi Zhang <zzhang@codeweavers.com> Signed-off-by: Huw Davies <huw@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2257 lines
72 KiB
C
2257 lines
72 KiB
C
/*
|
|
* Graphics paths (BeginPath, EndPath etc.)
|
|
*
|
|
* Copyright 1997, 1998 Martin Boehme
|
|
* 1999 Huw D M Davies
|
|
* Copyright 2005 Dmitry Timoshkov
|
|
* Copyright 2011 Alexandre Julliard
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "wine/port.h"
|
|
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#if defined(HAVE_FLOAT_H)
|
|
#include <float.h>
|
|
#endif
|
|
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "wingdi.h"
|
|
#include "winerror.h"
|
|
|
|
#include "gdi_private.h"
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(gdi);
|
|
|
|
/* 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.
|
|
*
|
|
* Martin Boehme
|
|
*/
|
|
|
|
#define NUM_ENTRIES_INITIAL 16 /* Initial size of points / flags arrays */
|
|
|
|
/* A floating point version of the POINT structure */
|
|
typedef struct tagFLOAT_POINT
|
|
{
|
|
double x, y;
|
|
} FLOAT_POINT;
|
|
|
|
struct gdi_path
|
|
{
|
|
POINT *points;
|
|
BYTE *flags;
|
|
int count;
|
|
int allocated;
|
|
BOOL newStroke;
|
|
POINT pos; /* current cursor position */
|
|
POINT points_buf[NUM_ENTRIES_INITIAL];
|
|
BYTE flags_buf[NUM_ENTRIES_INITIAL];
|
|
};
|
|
|
|
struct path_physdev
|
|
{
|
|
struct gdi_physdev dev;
|
|
struct gdi_path *path;
|
|
};
|
|
|
|
static inline struct path_physdev *get_path_physdev( PHYSDEV dev )
|
|
{
|
|
return CONTAINING_RECORD( dev, struct path_physdev, dev );
|
|
}
|
|
|
|
void free_gdi_path( struct gdi_path *path )
|
|
{
|
|
if (path->points != path->points_buf)
|
|
HeapFree( GetProcessHeap(), 0, path->points );
|
|
HeapFree( GetProcessHeap(), 0, path );
|
|
}
|
|
|
|
static struct gdi_path *alloc_gdi_path( int count )
|
|
{
|
|
struct gdi_path *path = HeapAlloc( GetProcessHeap(), 0, sizeof(*path) );
|
|
|
|
if (!path)
|
|
{
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
return NULL;
|
|
}
|
|
count = max( NUM_ENTRIES_INITIAL, count );
|
|
if (count > NUM_ENTRIES_INITIAL)
|
|
{
|
|
path->points = HeapAlloc( GetProcessHeap(), 0,
|
|
count * (sizeof(path->points[0]) + sizeof(path->flags[0])) );
|
|
if (!path->points)
|
|
{
|
|
HeapFree( GetProcessHeap(), 0, path );
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
return NULL;
|
|
}
|
|
path->flags = (BYTE *)(path->points + count);
|
|
}
|
|
else
|
|
{
|
|
path->points = path->points_buf;
|
|
path->flags = path->flags_buf;
|
|
}
|
|
path->count = 0;
|
|
path->allocated = count;
|
|
path->newStroke = TRUE;
|
|
path->pos.x = path->pos.y = 0;
|
|
return path;
|
|
}
|
|
|
|
static struct gdi_path *copy_gdi_path( const struct gdi_path *src_path )
|
|
{
|
|
struct gdi_path *path = alloc_gdi_path( src_path->count );
|
|
|
|
if (!path) return NULL;
|
|
|
|
path->count = src_path->count;
|
|
path->newStroke = src_path->newStroke;
|
|
path->pos = src_path->pos;
|
|
memcpy( path->points, src_path->points, path->count * sizeof(*path->points) );
|
|
memcpy( path->flags, src_path->flags, path->count * sizeof(*path->flags) );
|
|
return path;
|
|
}
|
|
|
|
/* Performs a world-to-viewport transformation on the specified point (which
|
|
* is in floating point format).
|
|
*/
|
|
static inline void INTERNAL_LPTODP_FLOAT( DC *dc, FLOAT_POINT *point, int count )
|
|
{
|
|
double x, y;
|
|
|
|
while (count--)
|
|
{
|
|
x = point->x;
|
|
y = point->y;
|
|
point->x = x * dc->xformWorld2Vport.eM11 + y * dc->xformWorld2Vport.eM21 + dc->xformWorld2Vport.eDx;
|
|
point->y = x * dc->xformWorld2Vport.eM12 + y * dc->xformWorld2Vport.eM22 + dc->xformWorld2Vport.eDy;
|
|
point++;
|
|
}
|
|
}
|
|
|
|
static inline INT int_from_fixed(FIXED f)
|
|
{
|
|
return (f.fract >= 0x8000) ? (f.value + 1) : f.value;
|
|
}
|
|
|
|
|
|
/* 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 BOOL PATH_ReserveEntries(struct gdi_path *path, INT count)
|
|
{
|
|
POINT *pts_new;
|
|
int size;
|
|
|
|
assert(count>=0);
|
|
|
|
/* Do we have to allocate more memory? */
|
|
if (count > path->allocated)
|
|
{
|
|
/* Find number of entries to allocate. We let the size of the array
|
|
* grow exponentially, since that will guarantee linear time
|
|
* complexity. */
|
|
count = max( path->allocated * 2, count );
|
|
size = count * (sizeof(path->points[0]) + sizeof(path->flags[0]));
|
|
|
|
if (path->points == path->points_buf)
|
|
{
|
|
pts_new = HeapAlloc( GetProcessHeap(), 0, size );
|
|
if (!pts_new) return FALSE;
|
|
memcpy( pts_new, path->points, path->count * sizeof(path->points[0]) );
|
|
memcpy( pts_new + count, path->flags, path->count * sizeof(path->flags[0]) );
|
|
}
|
|
else
|
|
{
|
|
pts_new = HeapReAlloc( GetProcessHeap(), 0, path->points, size );
|
|
if (!pts_new) return FALSE;
|
|
memmove( pts_new + count, pts_new + path->allocated, path->count * sizeof(path->flags[0]) );
|
|
}
|
|
|
|
path->points = pts_new;
|
|
path->flags = (BYTE *)(pts_new + count);
|
|
path->allocated = count;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* 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).
|
|
*/
|
|
static BOOL PATH_AddEntry(struct gdi_path *pPath, const POINT *pPoint, BYTE flags)
|
|
{
|
|
/* FIXME: If newStroke is true, perhaps we want to check that we're
|
|
* getting a PT_MOVETO
|
|
*/
|
|
TRACE("(%d,%d) - %d\n", pPoint->x, pPoint->y, flags);
|
|
|
|
/* Reserve enough memory for an extra path entry */
|
|
if(!PATH_ReserveEntries(pPath, pPath->count+1))
|
|
return FALSE;
|
|
|
|
/* Store information in path entry */
|
|
pPath->points[pPath->count]=*pPoint;
|
|
pPath->flags[pPath->count]=flags;
|
|
|
|
pPath->count++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* add a number of points, converting them to device coords */
|
|
/* return a pointer to the first type byte so it can be fixed up if necessary */
|
|
static BYTE *add_log_points( DC *dc, struct gdi_path *path, const POINT *points,
|
|
DWORD count, BYTE type )
|
|
{
|
|
BYTE *ret;
|
|
|
|
if (!PATH_ReserveEntries( path, path->count + count )) return NULL;
|
|
|
|
ret = &path->flags[path->count];
|
|
memcpy( &path->points[path->count], points, count * sizeof(*points) );
|
|
lp_to_dp( dc, &path->points[path->count], count );
|
|
memset( ret, type, count );
|
|
path->count += count;
|
|
return ret;
|
|
}
|
|
|
|
/* add a number of points that are already in device coords */
|
|
/* return a pointer to the first type byte so it can be fixed up if necessary */
|
|
static BYTE *add_points( struct gdi_path *path, const POINT *points, DWORD count, BYTE type )
|
|
{
|
|
BYTE *ret;
|
|
|
|
if (!PATH_ReserveEntries( path, path->count + count )) return NULL;
|
|
|
|
ret = &path->flags[path->count];
|
|
memcpy( &path->points[path->count], points, count * sizeof(*points) );
|
|
memset( ret, type, count );
|
|
path->count += count;
|
|
return ret;
|
|
}
|
|
|
|
/* reverse the order of an array of points */
|
|
static void reverse_points( POINT *points, UINT count )
|
|
{
|
|
UINT i;
|
|
for (i = 0; i < count / 2; i++)
|
|
{
|
|
POINT pt = points[i];
|
|
points[i] = points[count - i - 1];
|
|
points[count - i - 1] = pt;
|
|
}
|
|
}
|
|
|
|
/* start a new path stroke if necessary */
|
|
static BOOL start_new_stroke( struct gdi_path *path )
|
|
{
|
|
if (!path->newStroke && path->count &&
|
|
!(path->flags[path->count - 1] & PT_CLOSEFIGURE) &&
|
|
path->points[path->count - 1].x == path->pos.x &&
|
|
path->points[path->count - 1].y == path->pos.y)
|
|
return TRUE;
|
|
|
|
path->newStroke = FALSE;
|
|
return add_points( path, &path->pos, 1, PT_MOVETO ) != NULL;
|
|
}
|
|
|
|
/* set current position to the last point that was added to the path */
|
|
static void update_current_pos( struct gdi_path *path )
|
|
{
|
|
assert( path->count );
|
|
path->pos = path->points[path->count - 1];
|
|
}
|
|
|
|
/* close the current figure */
|
|
static void close_figure( struct gdi_path *path )
|
|
{
|
|
assert( path->count );
|
|
path->flags[path->count - 1] |= PT_CLOSEFIGURE;
|
|
}
|
|
|
|
/* add a number of points, starting a new stroke if necessary */
|
|
static BOOL add_log_points_new_stroke( DC *dc, struct gdi_path *path, const POINT *points,
|
|
DWORD count, BYTE type )
|
|
{
|
|
if (!start_new_stroke( path )) return FALSE;
|
|
if (!add_log_points( dc, path, points, count, type )) return FALSE;
|
|
update_current_pos( path );
|
|
return TRUE;
|
|
}
|
|
|
|
/* convert a (flattened) path to a region */
|
|
static HRGN path_to_region( const struct gdi_path *path, int mode )
|
|
{
|
|
int i, pos, polygons, *counts;
|
|
HRGN hrgn;
|
|
|
|
if (!path->count) return 0;
|
|
|
|
if (!(counts = HeapAlloc( GetProcessHeap(), 0, (path->count / 2) * sizeof(*counts) ))) return 0;
|
|
|
|
pos = polygons = 0;
|
|
assert( path->flags[0] == PT_MOVETO );
|
|
for (i = 1; i < path->count; i++)
|
|
{
|
|
if (path->flags[i] != PT_MOVETO) continue;
|
|
counts[polygons++] = i - pos;
|
|
pos = i;
|
|
}
|
|
if (i > pos + 1) counts[polygons++] = i - pos;
|
|
|
|
assert( polygons <= path->count / 2 );
|
|
hrgn = CreatePolyPolygonRgn( path->points, counts, polygons, mode );
|
|
HeapFree( GetProcessHeap(), 0, counts );
|
|
return hrgn;
|
|
}
|
|
|
|
/* PATH_CheckCorners
|
|
*
|
|
* Helper function for RoundRect() and Rectangle()
|
|
*/
|
|
static BOOL PATH_CheckCorners( DC *dc, POINT corners[], INT x1, INT y1, INT x2, INT y2 )
|
|
{
|
|
INT temp;
|
|
|
|
/* Convert points to device coordinates */
|
|
corners[0].x=x1;
|
|
corners[0].y=y1;
|
|
corners[1].x=x2;
|
|
corners[1].y=y2;
|
|
lp_to_dp( dc, corners, 2 );
|
|
|
|
/* Make sure first corner is top left and second corner is bottom right */
|
|
if(corners[0].x>corners[1].x)
|
|
{
|
|
temp=corners[0].x;
|
|
corners[0].x=corners[1].x;
|
|
corners[1].x=temp;
|
|
}
|
|
if(corners[0].y>corners[1].y)
|
|
{
|
|
temp=corners[0].y;
|
|
corners[0].y=corners[1].y;
|
|
corners[1].y=temp;
|
|
}
|
|
|
|
/* In GM_COMPATIBLE, don't include bottom and right edges */
|
|
if (dc->GraphicsMode == GM_COMPATIBLE)
|
|
{
|
|
if (corners[0].x == corners[1].x) return FALSE;
|
|
if (corners[0].y == corners[1].y) return FALSE;
|
|
corners[1].x--;
|
|
corners[1].y--;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* PATH_AddFlatBezier
|
|
*/
|
|
static BOOL PATH_AddFlatBezier(struct gdi_path *pPath, POINT *pt, BOOL closed)
|
|
{
|
|
POINT *pts;
|
|
BOOL ret;
|
|
INT no;
|
|
|
|
pts = GDI_Bezier( pt, 4, &no );
|
|
if(!pts) return FALSE;
|
|
|
|
ret = (add_points( pPath, pts + 1, no - 1, PT_LINETO ) != NULL);
|
|
if (ret && closed) close_figure( pPath );
|
|
HeapFree( GetProcessHeap(), 0, pts );
|
|
return ret;
|
|
}
|
|
|
|
/* PATH_FlattenPath
|
|
*
|
|
* Replaces Beziers with line segments
|
|
*
|
|
*/
|
|
static struct gdi_path *PATH_FlattenPath(const struct gdi_path *pPath)
|
|
{
|
|
struct gdi_path *new_path;
|
|
INT srcpt;
|
|
|
|
if (!(new_path = alloc_gdi_path( pPath->count ))) return NULL;
|
|
|
|
for(srcpt = 0; srcpt < pPath->count; srcpt++) {
|
|
switch(pPath->flags[srcpt] & ~PT_CLOSEFIGURE) {
|
|
case PT_MOVETO:
|
|
case PT_LINETO:
|
|
if (!PATH_AddEntry(new_path, &pPath->points[srcpt], pPath->flags[srcpt]))
|
|
{
|
|
free_gdi_path( new_path );
|
|
return NULL;
|
|
}
|
|
break;
|
|
case PT_BEZIERTO:
|
|
if (!PATH_AddFlatBezier(new_path, &pPath->points[srcpt-1],
|
|
pPath->flags[srcpt+2] & PT_CLOSEFIGURE))
|
|
{
|
|
free_gdi_path( new_path );
|
|
return NULL;
|
|
}
|
|
srcpt += 2;
|
|
break;
|
|
}
|
|
}
|
|
return new_path;
|
|
}
|
|
|
|
/* PATH_ScaleNormalizedPoint
|
|
*
|
|
* Scales a normalized point (x, y) with respect to the box whose corners are
|
|
* passed in "corners". The point is stored in "*pPoint". The normalized
|
|
* coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
|
|
* (1.0, 1.0) correspond to corners[1].
|
|
*/
|
|
static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
|
|
double y, POINT *pPoint)
|
|
{
|
|
pPoint->x = GDI_ROUND( corners[0].x + (corners[1].x-corners[0].x)*0.5*(x+1.0) );
|
|
pPoint->y = GDI_ROUND( corners[0].y + (corners[1].y-corners[0].y)*0.5*(y+1.0) );
|
|
}
|
|
|
|
/* PATH_NormalizePoint
|
|
*
|
|
* Normalizes a point with respect to the box whose corners are passed in
|
|
* "corners". The normalized coordinates are stored in "*pX" and "*pY".
|
|
*/
|
|
static void PATH_NormalizePoint(FLOAT_POINT corners[],
|
|
const FLOAT_POINT *pPoint,
|
|
double *pX, double *pY)
|
|
{
|
|
*pX = (pPoint->x-corners[0].x)/(corners[1].x-corners[0].x) * 2.0 - 1.0;
|
|
*pY = (pPoint->y-corners[0].y)/(corners[1].y-corners[0].y) * 2.0 - 1.0;
|
|
}
|
|
|
|
/* PATH_DoArcPart
|
|
*
|
|
* Creates a Bezier spline that corresponds to part of an arc and appends the
|
|
* corresponding points to the path. The start and end angles are passed in
|
|
* "angleStart" and "angleEnd"; these angles should span a quarter circle
|
|
* at most. If "startEntryType" is non-zero, an entry of that type for the first
|
|
* control point is added to the path; otherwise, it is assumed that the current
|
|
* position is equal to the first control point.
|
|
*/
|
|
static BOOL PATH_DoArcPart(struct gdi_path *pPath, FLOAT_POINT corners[],
|
|
double angleStart, double angleEnd, BYTE startEntryType)
|
|
{
|
|
double halfAngle, a;
|
|
double xNorm[4], yNorm[4];
|
|
POINT points[4];
|
|
BYTE *type;
|
|
int i, start;
|
|
|
|
assert(fabs(angleEnd-angleStart)<=M_PI_2);
|
|
|
|
/* FIXME: Is there an easier way of computing this? */
|
|
|
|
/* Compute control points */
|
|
halfAngle=(angleEnd-angleStart)/2.0;
|
|
if(fabs(halfAngle)>1e-8)
|
|
{
|
|
a=4.0/3.0*(1-cos(halfAngle))/sin(halfAngle);
|
|
xNorm[0]=cos(angleStart);
|
|
yNorm[0]=sin(angleStart);
|
|
xNorm[1]=xNorm[0] - a*yNorm[0];
|
|
yNorm[1]=yNorm[0] + a*xNorm[0];
|
|
xNorm[3]=cos(angleEnd);
|
|
yNorm[3]=sin(angleEnd);
|
|
xNorm[2]=xNorm[3] + a*yNorm[3];
|
|
yNorm[2]=yNorm[3] - a*xNorm[3];
|
|
}
|
|
else
|
|
for(i=0; i<4; i++)
|
|
{
|
|
xNorm[i]=cos(angleStart);
|
|
yNorm[i]=sin(angleStart);
|
|
}
|
|
|
|
/* Add starting point to path if desired */
|
|
start = !startEntryType;
|
|
for (i = start; i < 4; i++) PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &points[i]);
|
|
if (!(type = add_points( pPath, points + start, 4 - start, PT_BEZIERTO ))) return FALSE;
|
|
if (!start) type[0] = startEntryType;
|
|
return TRUE;
|
|
}
|
|
|
|
/* retrieve a flattened path in device coordinates, and optionally its region */
|
|
/* the DC path is deleted; the returned data must be freed by caller using free_gdi_path() */
|
|
/* helper for stroke_and_fill_path in the DIB driver */
|
|
struct gdi_path *get_gdi_flat_path( DC *dc, HRGN *rgn )
|
|
{
|
|
struct gdi_path *ret = NULL;
|
|
|
|
if (dc->path)
|
|
{
|
|
ret = PATH_FlattenPath( dc->path );
|
|
|
|
free_gdi_path( dc->path );
|
|
dc->path = NULL;
|
|
if (ret && rgn) *rgn = path_to_region( ret, dc->polyFillMode );
|
|
}
|
|
else SetLastError( ERROR_CAN_NOT_COMPLETE );
|
|
|
|
return ret;
|
|
}
|
|
|
|
int get_gdi_path_data( struct gdi_path *path, POINT **pts, BYTE **flags )
|
|
{
|
|
*pts = path->points;
|
|
*flags = path->flags;
|
|
return path->count;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* BeginPath (GDI32.@)
|
|
*/
|
|
BOOL WINAPI BeginPath(HDC hdc)
|
|
{
|
|
BOOL ret = FALSE;
|
|
DC *dc = get_dc_ptr( hdc );
|
|
|
|
if (dc)
|
|
{
|
|
PHYSDEV physdev = GET_DC_PHYSDEV( dc, pBeginPath );
|
|
ret = physdev->funcs->pBeginPath( physdev );
|
|
release_dc_ptr( dc );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* EndPath (GDI32.@)
|
|
*/
|
|
BOOL WINAPI EndPath(HDC hdc)
|
|
{
|
|
BOOL ret = FALSE;
|
|
DC *dc = get_dc_ptr( hdc );
|
|
|
|
if (dc)
|
|
{
|
|
PHYSDEV physdev = GET_DC_PHYSDEV( dc, pEndPath );
|
|
ret = physdev->funcs->pEndPath( physdev );
|
|
release_dc_ptr( dc );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* AbortPath [GDI32.@]
|
|
* Closes and discards paths from device context
|
|
*
|
|
* NOTES
|
|
* Check that SetLastError is being called correctly
|
|
*
|
|
* PARAMS
|
|
* hdc [I] Handle to device context
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*/
|
|
BOOL WINAPI AbortPath( HDC hdc )
|
|
{
|
|
BOOL ret = FALSE;
|
|
DC *dc = get_dc_ptr( hdc );
|
|
|
|
if (dc)
|
|
{
|
|
PHYSDEV physdev = GET_DC_PHYSDEV( dc, pAbortPath );
|
|
ret = physdev->funcs->pAbortPath( physdev );
|
|
release_dc_ptr( dc );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* CloseFigure (GDI32.@)
|
|
*
|
|
* FIXME: Check that SetLastError is being called correctly
|
|
*/
|
|
BOOL WINAPI CloseFigure(HDC hdc)
|
|
{
|
|
BOOL ret = FALSE;
|
|
DC *dc = get_dc_ptr( hdc );
|
|
|
|
if (dc)
|
|
{
|
|
PHYSDEV physdev = GET_DC_PHYSDEV( dc, pCloseFigure );
|
|
ret = physdev->funcs->pCloseFigure( physdev );
|
|
release_dc_ptr( dc );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetPath (GDI32.@)
|
|
*/
|
|
INT WINAPI GetPath(HDC hdc, LPPOINT pPoints, LPBYTE pTypes, INT nSize)
|
|
{
|
|
INT ret = -1;
|
|
DC *dc = get_dc_ptr( hdc );
|
|
|
|
if(!dc) return -1;
|
|
|
|
if (!dc->path)
|
|
{
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
goto done;
|
|
}
|
|
|
|
if(nSize==0)
|
|
ret = dc->path->count;
|
|
else if(nSize<dc->path->count)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto done;
|
|
}
|
|
else
|
|
{
|
|
memcpy(pPoints, dc->path->points, sizeof(POINT)*dc->path->count);
|
|
memcpy(pTypes, dc->path->flags, sizeof(BYTE)*dc->path->count);
|
|
|
|
/* Convert the points to logical coordinates */
|
|
if(!dp_to_lp(dc, pPoints, dc->path->count))
|
|
{
|
|
/* FIXME: Is this the correct value? */
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
goto done;
|
|
}
|
|
else ret = dc->path->count;
|
|
}
|
|
done:
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* PathToRegion (GDI32.@)
|
|
*/
|
|
HRGN WINAPI PathToRegion(HDC hdc)
|
|
{
|
|
HRGN ret = 0;
|
|
DC *dc = get_dc_ptr( hdc );
|
|
|
|
if (!dc) return 0;
|
|
|
|
if (dc->path)
|
|
{
|
|
struct gdi_path *path = PATH_FlattenPath( dc->path );
|
|
|
|
free_gdi_path( dc->path );
|
|
dc->path = NULL;
|
|
if (path)
|
|
{
|
|
ret = path_to_region( path, dc->polyFillMode );
|
|
free_gdi_path( path );
|
|
}
|
|
}
|
|
else SetLastError( ERROR_CAN_NOT_COMPLETE );
|
|
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* FillPath (GDI32.@)
|
|
*
|
|
* FIXME
|
|
* Check that SetLastError is being called correctly
|
|
*/
|
|
BOOL WINAPI FillPath(HDC hdc)
|
|
{
|
|
BOOL ret = FALSE;
|
|
DC *dc = get_dc_ptr( hdc );
|
|
|
|
if (dc)
|
|
{
|
|
PHYSDEV physdev = GET_DC_PHYSDEV( dc, pFillPath );
|
|
ret = physdev->funcs->pFillPath( physdev );
|
|
release_dc_ptr( dc );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SelectClipPath (GDI32.@)
|
|
*/
|
|
BOOL WINAPI SelectClipPath(HDC hdc, INT iMode)
|
|
{
|
|
BOOL ret = FALSE;
|
|
DC *dc = get_dc_ptr( hdc );
|
|
|
|
if (dc)
|
|
{
|
|
PHYSDEV physdev = GET_DC_PHYSDEV( dc, pSelectClipPath );
|
|
ret = physdev->funcs->pSelectClipPath( physdev, iMode );
|
|
release_dc_ptr( dc );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* pathdrv_BeginPath
|
|
*/
|
|
static BOOL CDECL pathdrv_BeginPath( PHYSDEV dev )
|
|
{
|
|
/* path already open, nothing to do */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* pathdrv_AbortPath
|
|
*/
|
|
static BOOL CDECL pathdrv_AbortPath( PHYSDEV dev )
|
|
{
|
|
DC *dc = get_physdev_dc( dev );
|
|
|
|
path_driver.pDeleteDC( pop_dc_driver( dc, &path_driver ));
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* pathdrv_EndPath
|
|
*/
|
|
static BOOL CDECL pathdrv_EndPath( PHYSDEV dev )
|
|
{
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
DC *dc = get_physdev_dc( dev );
|
|
|
|
dc->path = physdev->path;
|
|
pop_dc_driver( dc, &path_driver );
|
|
HeapFree( GetProcessHeap(), 0, physdev );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* pathdrv_CreateDC
|
|
*/
|
|
static BOOL CDECL pathdrv_CreateDC( PHYSDEV *dev, LPCWSTR driver, LPCWSTR device,
|
|
LPCWSTR output, const DEVMODEW *devmode )
|
|
{
|
|
struct path_physdev *physdev = HeapAlloc( GetProcessHeap(), 0, sizeof(*physdev) );
|
|
|
|
if (!physdev) return FALSE;
|
|
push_dc_driver( dev, &physdev->dev, &path_driver );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_DeleteDC
|
|
*/
|
|
static BOOL CDECL pathdrv_DeleteDC( PHYSDEV dev )
|
|
{
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
|
|
free_gdi_path( physdev->path );
|
|
HeapFree( GetProcessHeap(), 0, physdev );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL PATH_SavePath( DC *dst, DC *src )
|
|
{
|
|
PHYSDEV dev;
|
|
|
|
if (src->path)
|
|
{
|
|
if (!(dst->path = copy_gdi_path( src->path ))) return FALSE;
|
|
}
|
|
else if ((dev = find_dc_driver( src, &path_driver )))
|
|
{
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
if (!(dst->path = copy_gdi_path( physdev->path ))) return FALSE;
|
|
dst->path_open = TRUE;
|
|
}
|
|
else dst->path = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL PATH_RestorePath( DC *dst, DC *src )
|
|
{
|
|
PHYSDEV dev;
|
|
struct path_physdev *physdev;
|
|
|
|
if ((dev = pop_dc_driver( dst, &path_driver )))
|
|
{
|
|
physdev = get_path_physdev( dev );
|
|
free_gdi_path( physdev->path );
|
|
HeapFree( GetProcessHeap(), 0, physdev );
|
|
}
|
|
|
|
if (src->path && src->path_open)
|
|
{
|
|
if (!path_driver.pCreateDC( &dst->physDev, NULL, NULL, NULL, NULL )) return FALSE;
|
|
physdev = get_path_physdev( find_dc_driver( dst, &path_driver ));
|
|
physdev->path = src->path;
|
|
src->path_open = FALSE;
|
|
src->path = NULL;
|
|
}
|
|
|
|
if (dst->path) free_gdi_path( dst->path );
|
|
dst->path = src->path;
|
|
src->path = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_MoveTo
|
|
*/
|
|
static BOOL CDECL pathdrv_MoveTo( PHYSDEV dev, INT x, INT y )
|
|
{
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
DC *dc = get_physdev_dc( dev );
|
|
|
|
physdev->path->newStroke = TRUE;
|
|
physdev->path->pos.x = x;
|
|
physdev->path->pos.y = y;
|
|
lp_to_dp( dc, &physdev->path->pos, 1 );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_LineTo
|
|
*/
|
|
static BOOL CDECL pathdrv_LineTo( PHYSDEV dev, INT x, INT y )
|
|
{
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
DC *dc = get_physdev_dc( dev );
|
|
POINT point;
|
|
|
|
point.x = x;
|
|
point.y = y;
|
|
return add_log_points_new_stroke( dc, physdev->path, &point, 1, PT_LINETO );
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_Rectangle
|
|
*/
|
|
static BOOL CDECL pathdrv_Rectangle( PHYSDEV dev, INT x1, INT y1, INT x2, INT y2 )
|
|
{
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
DC *dc = get_physdev_dc( dev );
|
|
POINT corners[2], points[4];
|
|
BYTE *type;
|
|
|
|
if (!PATH_CheckCorners( dc, corners, x1, y1, x2, y2 )) return TRUE;
|
|
|
|
points[0].x = corners[1].x;
|
|
points[0].y = corners[0].y;
|
|
points[1] = corners[0];
|
|
points[2].x = corners[0].x;
|
|
points[2].y = corners[1].y;
|
|
points[3] = corners[1];
|
|
if (dc->ArcDirection == AD_CLOCKWISE) reverse_points( points, 4 );
|
|
|
|
if (!(type = add_points( physdev->path, points, 4, PT_LINETO ))) return FALSE;
|
|
type[0] = PT_MOVETO;
|
|
close_figure( physdev->path );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_RoundRect
|
|
*/
|
|
static BOOL CDECL pathdrv_RoundRect( PHYSDEV dev, INT x1, INT y1, INT x2, INT y2, INT ell_width, INT ell_height )
|
|
{
|
|
const double factor = 0.55428475; /* 4 / 3 * (sqrt(2) - 1) */
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
DC *dc = get_physdev_dc( dev );
|
|
POINT corners[2], ellipse[2], points[16];
|
|
BYTE *type;
|
|
double width, height;
|
|
|
|
if (!ell_width || !ell_height) return pathdrv_Rectangle( dev, x1, y1, x2, y2 );
|
|
|
|
if (!PATH_CheckCorners( dc, corners, x1, y1, x2, y2 )) return TRUE;
|
|
|
|
ellipse[0].x = ellipse[0].y = 0;
|
|
ellipse[1].x = ell_width;
|
|
ellipse[1].y = ell_height;
|
|
lp_to_dp( dc, (POINT *)&ellipse, 2 );
|
|
ell_width = min( abs( ellipse[1].x - ellipse[0].x ), corners[1].x - corners[0].x );
|
|
ell_height = min( abs( ellipse[1].y - ellipse[0].y ), corners[1].y - corners[0].y );
|
|
width = ell_width / 2.0;
|
|
height = ell_height / 2.0;
|
|
|
|
/* starting point */
|
|
points[0].x = corners[1].x;
|
|
points[0].y = corners[0].y + GDI_ROUND( height );
|
|
/* first curve */
|
|
points[1].x = corners[1].x;
|
|
points[1].y = corners[0].y + GDI_ROUND( height * (1 - factor) );
|
|
points[2].x = corners[1].x - GDI_ROUND( width * (1 - factor) );
|
|
points[2].y = corners[0].y;
|
|
points[3].x = corners[1].x - GDI_ROUND( width );
|
|
points[3].y = corners[0].y;
|
|
/* horizontal line */
|
|
points[4].x = corners[0].x + GDI_ROUND( width );
|
|
points[4].y = corners[0].y;
|
|
/* second curve */
|
|
points[5].x = corners[0].x + GDI_ROUND( width * (1 - factor) );
|
|
points[5].y = corners[0].y;
|
|
points[6].x = corners[0].x;
|
|
points[6].y = corners[0].y + GDI_ROUND( height * (1 - factor) );
|
|
points[7].x = corners[0].x;
|
|
points[7].y = corners[0].y + GDI_ROUND( height );
|
|
/* vertical line */
|
|
points[8].x = corners[0].x;
|
|
points[8].y = corners[1].y - GDI_ROUND( height );
|
|
/* third curve */
|
|
points[9].x = corners[0].x;
|
|
points[9].y = corners[1].y - GDI_ROUND( height * (1 - factor) );
|
|
points[10].x = corners[0].x + GDI_ROUND( width * (1 - factor) );
|
|
points[10].y = corners[1].y;
|
|
points[11].x = corners[0].x + GDI_ROUND( width );
|
|
points[11].y = corners[1].y;
|
|
/* horizontal line */
|
|
points[12].x = corners[1].x - GDI_ROUND( width );
|
|
points[12].y = corners[1].y;
|
|
/* fourth curve */
|
|
points[13].x = corners[1].x - GDI_ROUND( width * (1 - factor) );
|
|
points[13].y = corners[1].y;
|
|
points[14].x = corners[1].x;
|
|
points[14].y = corners[1].y - GDI_ROUND( height * (1 - factor) );
|
|
points[15].x = corners[1].x;
|
|
points[15].y = corners[1].y - GDI_ROUND( height );
|
|
|
|
if (dc->ArcDirection == AD_CLOCKWISE) reverse_points( points, 16 );
|
|
if (!(type = add_points( physdev->path, points, 16, PT_BEZIERTO ))) return FALSE;
|
|
type[0] = PT_MOVETO;
|
|
type[4] = type[8] = type[12] = PT_LINETO;
|
|
close_figure( physdev->path );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_Ellipse
|
|
*/
|
|
static BOOL CDECL pathdrv_Ellipse( PHYSDEV dev, INT x1, INT y1, INT x2, INT y2 )
|
|
{
|
|
const double factor = 0.55428475; /* 4 / 3 * (sqrt(2) - 1) */
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
DC *dc = get_physdev_dc( dev );
|
|
POINT corners[2], points[13];
|
|
BYTE *type;
|
|
double width, height;
|
|
|
|
if (!PATH_CheckCorners( dc, corners, x1, y1, x2, y2 )) return TRUE;
|
|
|
|
width = (corners[1].x - corners[0].x) / 2.0;
|
|
height = (corners[1].y - corners[0].y) / 2.0;
|
|
|
|
/* starting point */
|
|
points[0].x = corners[1].x;
|
|
points[0].y = corners[0].y + GDI_ROUND( height );
|
|
/* first curve */
|
|
points[1].x = corners[1].x;
|
|
points[1].y = corners[0].y + GDI_ROUND( height * (1 - factor) );
|
|
points[2].x = corners[1].x - GDI_ROUND( width * (1 - factor) );
|
|
points[2].y = corners[0].y;
|
|
points[3].x = corners[0].x + GDI_ROUND( width );
|
|
points[3].y = corners[0].y;
|
|
/* second curve */
|
|
points[4].x = corners[0].x + GDI_ROUND( width * (1 - factor) );
|
|
points[4].y = corners[0].y;
|
|
points[5].x = corners[0].x;
|
|
points[5].y = corners[0].y + GDI_ROUND( height * (1 - factor) );
|
|
points[6].x = corners[0].x;
|
|
points[6].y = corners[0].y + GDI_ROUND( height );
|
|
/* third curve */
|
|
points[7].x = corners[0].x;
|
|
points[7].y = corners[1].y - GDI_ROUND( height * (1 - factor) );
|
|
points[8].x = corners[0].x + GDI_ROUND( width * (1 - factor) );
|
|
points[8].y = corners[1].y;
|
|
points[9].x = corners[0].x + GDI_ROUND( width );
|
|
points[9].y = corners[1].y;
|
|
/* fourth curve */
|
|
points[10].x = corners[1].x - GDI_ROUND( width * (1 - factor) );
|
|
points[10].y = corners[1].y;
|
|
points[11].x = corners[1].x;
|
|
points[11].y = corners[1].y - GDI_ROUND( height * (1 - factor) );
|
|
points[12].x = corners[1].x;
|
|
points[12].y = corners[1].y - GDI_ROUND( height );
|
|
|
|
if (dc->ArcDirection == AD_CLOCKWISE) reverse_points( points, 13 );
|
|
if (!(type = add_points( physdev->path, points, 13, PT_BEZIERTO ))) return FALSE;
|
|
type[0] = PT_MOVETO;
|
|
close_figure( physdev->path );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* PATH_Arc
|
|
*
|
|
* Should be called when a call to Arc is performed on a DC that has
|
|
* an open path. This adds up to five Bezier splines representing the arc
|
|
* to the path. When 'lines' is 1, we add 1 extra line to get a chord,
|
|
* when 'lines' is 2, we add 2 extra lines to get a pie, and when 'lines' is
|
|
* -1 we add 1 extra line from the current DC position to the starting position
|
|
* of the arc before drawing the arc itself (arcto). Returns TRUE if successful,
|
|
* else FALSE.
|
|
*/
|
|
static BOOL PATH_Arc( PHYSDEV dev, INT x1, INT y1, INT x2, INT y2,
|
|
INT xStart, INT yStart, INT xEnd, INT yEnd, int direction, int lines )
|
|
{
|
|
DC *dc = get_physdev_dc( dev );
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
double angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
|
|
/* Initialize angleEndQuadrant to silence gcc's warning */
|
|
double x, y;
|
|
FLOAT_POINT corners[2], pointStart, pointEnd;
|
|
POINT centre;
|
|
BOOL start, end;
|
|
INT temp;
|
|
|
|
/* FIXME: Do we have to respect newStroke? */
|
|
|
|
/* Check for zero height / width */
|
|
/* FIXME: Only in GM_COMPATIBLE? */
|
|
if(x1==x2 || y1==y2)
|
|
return TRUE;
|
|
|
|
/* Convert points to device coordinates */
|
|
corners[0].x = x1;
|
|
corners[0].y = y1;
|
|
corners[1].x = x2;
|
|
corners[1].y = y2;
|
|
pointStart.x = xStart;
|
|
pointStart.y = yStart;
|
|
pointEnd.x = xEnd;
|
|
pointEnd.y = yEnd;
|
|
INTERNAL_LPTODP_FLOAT(dc, corners, 2);
|
|
INTERNAL_LPTODP_FLOAT(dc, &pointStart, 1);
|
|
INTERNAL_LPTODP_FLOAT(dc, &pointEnd, 1);
|
|
|
|
/* Make sure first corner is top left and second corner is bottom right */
|
|
if(corners[0].x>corners[1].x)
|
|
{
|
|
temp=corners[0].x;
|
|
corners[0].x=corners[1].x;
|
|
corners[1].x=temp;
|
|
}
|
|
if(corners[0].y>corners[1].y)
|
|
{
|
|
temp=corners[0].y;
|
|
corners[0].y=corners[1].y;
|
|
corners[1].y=temp;
|
|
}
|
|
|
|
/* Compute start and end angle */
|
|
PATH_NormalizePoint(corners, &pointStart, &x, &y);
|
|
angleStart=atan2(y, x);
|
|
PATH_NormalizePoint(corners, &pointEnd, &x, &y);
|
|
angleEnd=atan2(y, x);
|
|
|
|
/* Make sure the end angle is "on the right side" of the start angle */
|
|
if (direction == AD_CLOCKWISE)
|
|
{
|
|
if(angleEnd<=angleStart)
|
|
{
|
|
angleEnd+=2*M_PI;
|
|
assert(angleEnd>=angleStart);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(angleEnd>=angleStart)
|
|
{
|
|
angleEnd-=2*M_PI;
|
|
assert(angleEnd<=angleStart);
|
|
}
|
|
}
|
|
|
|
/* In GM_COMPATIBLE, don't include bottom and right edges */
|
|
if (dc->GraphicsMode == GM_COMPATIBLE)
|
|
{
|
|
corners[1].x--;
|
|
corners[1].y--;
|
|
}
|
|
|
|
/* arcto: Add a PT_MOVETO only if this is the first entry in a stroke */
|
|
if (lines == -1 && !start_new_stroke( physdev->path )) return FALSE;
|
|
|
|
/* Add the arc to the path with one Bezier spline per quadrant that the
|
|
* arc spans */
|
|
start=TRUE;
|
|
end=FALSE;
|
|
do
|
|
{
|
|
/* Determine the start and end angles for this quadrant */
|
|
if(start)
|
|
{
|
|
angleStartQuadrant=angleStart;
|
|
if (direction == AD_CLOCKWISE)
|
|
angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
|
|
else
|
|
angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
|
|
}
|
|
else
|
|
{
|
|
angleStartQuadrant=angleEndQuadrant;
|
|
if (direction == AD_CLOCKWISE)
|
|
angleEndQuadrant+=M_PI_2;
|
|
else
|
|
angleEndQuadrant-=M_PI_2;
|
|
}
|
|
|
|
/* Have we reached the last part of the arc? */
|
|
if((direction == AD_CLOCKWISE && angleEnd<angleEndQuadrant) ||
|
|
(direction == AD_COUNTERCLOCKWISE && angleEnd>angleEndQuadrant))
|
|
{
|
|
/* Adjust the end angle for this quadrant */
|
|
angleEndQuadrant=angleEnd;
|
|
end=TRUE;
|
|
}
|
|
|
|
/* Add the Bezier spline to the path */
|
|
PATH_DoArcPart(physdev->path, corners, angleStartQuadrant, angleEndQuadrant,
|
|
start ? (lines==-1 ? PT_LINETO : PT_MOVETO) : 0);
|
|
start=FALSE;
|
|
} while(!end);
|
|
|
|
/* chord: close figure. pie: add line and close figure */
|
|
switch (lines)
|
|
{
|
|
case -1:
|
|
update_current_pos( physdev->path );
|
|
break;
|
|
case 1:
|
|
close_figure( physdev->path );
|
|
break;
|
|
case 2:
|
|
centre.x = (corners[0].x+corners[1].x)/2;
|
|
centre.y = (corners[0].y+corners[1].y)/2;
|
|
if(!PATH_AddEntry(physdev->path, ¢re, PT_LINETO | PT_CLOSEFIGURE))
|
|
return FALSE;
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_AngleArc
|
|
*/
|
|
static BOOL CDECL pathdrv_AngleArc( PHYSDEV dev, INT x, INT y, DWORD radius, FLOAT eStartAngle, FLOAT eSweepAngle)
|
|
{
|
|
int x1 = GDI_ROUND( x + cos(eStartAngle*M_PI/180) * radius );
|
|
int y1 = GDI_ROUND( y - sin(eStartAngle*M_PI/180) * radius );
|
|
int x2 = GDI_ROUND( x + cos((eStartAngle+eSweepAngle)*M_PI/180) * radius );
|
|
int y2 = GDI_ROUND( y - sin((eStartAngle+eSweepAngle)*M_PI/180) * radius );
|
|
return PATH_Arc( dev, x-radius, y-radius, x+radius, y+radius, x1, y1, x2, y2,
|
|
eSweepAngle >= 0 ? AD_COUNTERCLOCKWISE : AD_CLOCKWISE, -1 );
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_Arc
|
|
*/
|
|
static BOOL CDECL pathdrv_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
|
|
INT xstart, INT ystart, INT xend, INT yend )
|
|
{
|
|
DC *dc = get_physdev_dc( dev );
|
|
return PATH_Arc( dev, left, top, right, bottom, xstart, ystart, xend, yend,
|
|
dc->ArcDirection, 0 );
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_ArcTo
|
|
*/
|
|
static BOOL CDECL pathdrv_ArcTo( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
|
|
INT xstart, INT ystart, INT xend, INT yend )
|
|
{
|
|
DC *dc = get_physdev_dc( dev );
|
|
return PATH_Arc( dev, left, top, right, bottom, xstart, ystart, xend, yend,
|
|
dc->ArcDirection, -1 );
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_Chord
|
|
*/
|
|
static BOOL CDECL pathdrv_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
|
|
INT xstart, INT ystart, INT xend, INT yend )
|
|
{
|
|
DC *dc = get_physdev_dc( dev );
|
|
return PATH_Arc( dev, left, top, right, bottom, xstart, ystart, xend, yend,
|
|
dc->ArcDirection, 1 );
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_Pie
|
|
*/
|
|
static BOOL CDECL pathdrv_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
|
|
INT xstart, INT ystart, INT xend, INT yend )
|
|
{
|
|
DC *dc = get_physdev_dc( dev );
|
|
return PATH_Arc( dev, left, top, right, bottom, xstart, ystart, xend, yend,
|
|
dc->ArcDirection, 2 );
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_PolyBezierTo
|
|
*/
|
|
static BOOL CDECL pathdrv_PolyBezierTo( PHYSDEV dev, const POINT *pts, DWORD cbPoints )
|
|
{
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
DC *dc = get_physdev_dc( dev );
|
|
|
|
return add_log_points_new_stroke( dc, physdev->path, pts, cbPoints, PT_BEZIERTO );
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_PolyBezier
|
|
*/
|
|
static BOOL CDECL pathdrv_PolyBezier( PHYSDEV dev, const POINT *pts, DWORD cbPoints )
|
|
{
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
DC *dc = get_physdev_dc( dev );
|
|
BYTE *type = add_log_points( dc, physdev->path, pts, cbPoints, PT_BEZIERTO );
|
|
|
|
if (!type) return FALSE;
|
|
type[0] = PT_MOVETO;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_PolyDraw
|
|
*/
|
|
static BOOL CDECL pathdrv_PolyDraw( PHYSDEV dev, const POINT *pts, const BYTE *types, DWORD cbPoints )
|
|
{
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
struct gdi_path *path = physdev->path;
|
|
DC *dc = get_physdev_dc( dev );
|
|
POINT orig_pos;
|
|
INT i, lastmove = 0;
|
|
|
|
for (i = 0; i < path->count; i++) if (path->flags[i] == PT_MOVETO) lastmove = i;
|
|
orig_pos = path->pos;
|
|
|
|
for(i = 0; i < cbPoints; i++)
|
|
{
|
|
switch (types[i])
|
|
{
|
|
case PT_MOVETO:
|
|
path->newStroke = TRUE;
|
|
path->pos = pts[i];
|
|
lp_to_dp( dc, &path->pos, 1 );
|
|
lastmove = path->count;
|
|
break;
|
|
case PT_LINETO:
|
|
case PT_LINETO | PT_CLOSEFIGURE:
|
|
if (!add_log_points_new_stroke( dc, path, &pts[i], 1, PT_LINETO )) return FALSE;
|
|
break;
|
|
case PT_BEZIERTO:
|
|
if ((i + 2 < cbPoints) && (types[i + 1] == PT_BEZIERTO) &&
|
|
(types[i + 2] & ~PT_CLOSEFIGURE) == PT_BEZIERTO)
|
|
{
|
|
if (!add_log_points_new_stroke( dc, path, &pts[i], 3, PT_BEZIERTO )) return FALSE;
|
|
i += 2;
|
|
break;
|
|
}
|
|
/* fall through */
|
|
default:
|
|
/* restore original position */
|
|
path->pos = orig_pos;
|
|
return FALSE;
|
|
}
|
|
|
|
if (types[i] & PT_CLOSEFIGURE)
|
|
{
|
|
close_figure( path );
|
|
path->pos = path->points[lastmove];
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_Polyline
|
|
*/
|
|
static BOOL CDECL pathdrv_Polyline( PHYSDEV dev, const POINT *pts, INT count )
|
|
{
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
DC *dc = get_physdev_dc( dev );
|
|
BYTE *type;
|
|
|
|
if (count < 2) return FALSE;
|
|
if (!(type = add_log_points( dc, physdev->path, pts, count, PT_LINETO ))) return FALSE;
|
|
type[0] = PT_MOVETO;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_PolylineTo
|
|
*/
|
|
static BOOL CDECL pathdrv_PolylineTo( PHYSDEV dev, const POINT *pts, INT count )
|
|
{
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
DC *dc = get_physdev_dc( dev );
|
|
|
|
if (count < 1) return FALSE;
|
|
return add_log_points_new_stroke( dc, physdev->path, pts, count, PT_LINETO );
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_Polygon
|
|
*/
|
|
static BOOL CDECL pathdrv_Polygon( PHYSDEV dev, const POINT *pts, INT count )
|
|
{
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
DC *dc = get_physdev_dc( dev );
|
|
BYTE *type;
|
|
|
|
if (count < 2) return FALSE;
|
|
if (!(type = add_log_points( dc, physdev->path, pts, count, PT_LINETO ))) return FALSE;
|
|
type[0] = PT_MOVETO;
|
|
type[count - 1] = PT_LINETO | PT_CLOSEFIGURE;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_PolyPolygon
|
|
*/
|
|
static BOOL CDECL pathdrv_PolyPolygon( PHYSDEV dev, const POINT* pts, const INT* counts, UINT polygons )
|
|
{
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
DC *dc = get_physdev_dc( dev );
|
|
UINT poly, count;
|
|
BYTE *type;
|
|
|
|
if (!polygons) return FALSE;
|
|
for (poly = count = 0; poly < polygons; poly++)
|
|
{
|
|
if (counts[poly] < 2) return FALSE;
|
|
count += counts[poly];
|
|
}
|
|
|
|
type = add_log_points( dc, physdev->path, pts, count, PT_LINETO );
|
|
if (!type) return FALSE;
|
|
|
|
/* make the first point of each polyline a PT_MOVETO, and close the last one */
|
|
for (poly = 0; poly < polygons; type += counts[poly++])
|
|
{
|
|
type[0] = PT_MOVETO;
|
|
type[counts[poly] - 1] = PT_LINETO | PT_CLOSEFIGURE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_PolyPolyline
|
|
*/
|
|
static BOOL CDECL pathdrv_PolyPolyline( PHYSDEV dev, const POINT* pts, const DWORD* counts, DWORD polylines )
|
|
{
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
DC *dc = get_physdev_dc( dev );
|
|
UINT poly, count;
|
|
BYTE *type;
|
|
|
|
if (!polylines) return FALSE;
|
|
for (poly = count = 0; poly < polylines; poly++)
|
|
{
|
|
if (counts[poly] < 2) return FALSE;
|
|
count += counts[poly];
|
|
}
|
|
|
|
type = add_log_points( dc, physdev->path, pts, count, PT_LINETO );
|
|
if (!type) return FALSE;
|
|
|
|
/* make the first point of each polyline a PT_MOVETO */
|
|
for (poly = 0; poly < polylines; type += counts[poly++]) *type = PT_MOVETO;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* PATH_BezierTo
|
|
*
|
|
* internally used by PATH_add_outline
|
|
*/
|
|
static void PATH_BezierTo(struct gdi_path *pPath, POINT *lppt, INT n)
|
|
{
|
|
if (n < 2) return;
|
|
|
|
if (n == 2)
|
|
{
|
|
PATH_AddEntry(pPath, &lppt[1], PT_LINETO);
|
|
}
|
|
else if (n == 3)
|
|
{
|
|
add_points( pPath, lppt, 3, PT_BEZIERTO );
|
|
}
|
|
else
|
|
{
|
|
POINT pt[3];
|
|
INT i = 0;
|
|
|
|
pt[2] = lppt[0];
|
|
n--;
|
|
|
|
while (n > 2)
|
|
{
|
|
pt[0] = pt[2];
|
|
pt[1] = lppt[i+1];
|
|
pt[2].x = (lppt[i+2].x + lppt[i+1].x) / 2;
|
|
pt[2].y = (lppt[i+2].y + lppt[i+1].y) / 2;
|
|
add_points( pPath, pt, 3, PT_BEZIERTO );
|
|
n--;
|
|
i++;
|
|
}
|
|
|
|
pt[0] = pt[2];
|
|
pt[1] = lppt[i+1];
|
|
pt[2] = lppt[i+2];
|
|
add_points( pPath, pt, 3, PT_BEZIERTO );
|
|
}
|
|
}
|
|
|
|
static BOOL PATH_add_outline(struct path_physdev *physdev, INT x, INT y,
|
|
TTPOLYGONHEADER *header, DWORD size)
|
|
{
|
|
TTPOLYGONHEADER *start;
|
|
POINT pt;
|
|
|
|
start = header;
|
|
|
|
while ((char *)header < (char *)start + size)
|
|
{
|
|
TTPOLYCURVE *curve;
|
|
|
|
if (header->dwType != TT_POLYGON_TYPE)
|
|
{
|
|
FIXME("Unknown header type %d\n", header->dwType);
|
|
return FALSE;
|
|
}
|
|
|
|
pt.x = x + int_from_fixed(header->pfxStart.x);
|
|
pt.y = y - int_from_fixed(header->pfxStart.y);
|
|
PATH_AddEntry(physdev->path, &pt, PT_MOVETO);
|
|
|
|
curve = (TTPOLYCURVE *)(header + 1);
|
|
|
|
while ((char *)curve < (char *)header + header->cb)
|
|
{
|
|
/*TRACE("curve->wType %d\n", curve->wType);*/
|
|
|
|
switch(curve->wType)
|
|
{
|
|
case TT_PRIM_LINE:
|
|
{
|
|
WORD i;
|
|
|
|
for (i = 0; i < curve->cpfx; i++)
|
|
{
|
|
pt.x = x + int_from_fixed(curve->apfx[i].x);
|
|
pt.y = y - int_from_fixed(curve->apfx[i].y);
|
|
PATH_AddEntry(physdev->path, &pt, PT_LINETO);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TT_PRIM_QSPLINE:
|
|
case TT_PRIM_CSPLINE:
|
|
{
|
|
WORD i;
|
|
POINTFX ptfx;
|
|
POINT *pts = HeapAlloc(GetProcessHeap(), 0, (curve->cpfx + 1) * sizeof(POINT));
|
|
|
|
if (!pts) return FALSE;
|
|
|
|
ptfx = *(POINTFX *)((char *)curve - sizeof(POINTFX));
|
|
|
|
pts[0].x = x + int_from_fixed(ptfx.x);
|
|
pts[0].y = y - int_from_fixed(ptfx.y);
|
|
|
|
for(i = 0; i < curve->cpfx; i++)
|
|
{
|
|
pts[i + 1].x = x + int_from_fixed(curve->apfx[i].x);
|
|
pts[i + 1].y = y - int_from_fixed(curve->apfx[i].y);
|
|
}
|
|
|
|
PATH_BezierTo(physdev->path, pts, curve->cpfx + 1);
|
|
|
|
HeapFree(GetProcessHeap(), 0, pts);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
FIXME("Unknown curve type %04x\n", curve->wType);
|
|
return FALSE;
|
|
}
|
|
|
|
curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
|
|
}
|
|
|
|
header = (TTPOLYGONHEADER *)((char *)header + header->cb);
|
|
}
|
|
|
|
close_figure( physdev->path );
|
|
return TRUE;
|
|
}
|
|
|
|
/*************************************************************
|
|
* pathdrv_ExtTextOut
|
|
*/
|
|
static BOOL CDECL pathdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags, const RECT *lprc,
|
|
LPCWSTR str, UINT count, const INT *dx )
|
|
{
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
unsigned int idx, ggo_flags = GGO_NATIVE;
|
|
POINT offset = {0, 0};
|
|
|
|
if (!count) return TRUE;
|
|
if (flags & ETO_GLYPH_INDEX) ggo_flags |= GGO_GLYPH_INDEX;
|
|
|
|
for (idx = 0; idx < count; idx++)
|
|
{
|
|
static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
|
|
GLYPHMETRICS gm;
|
|
DWORD dwSize;
|
|
void *outline;
|
|
|
|
dwSize = GetGlyphOutlineW(dev->hdc, str[idx], ggo_flags, &gm, 0, NULL, &identity);
|
|
if (dwSize == GDI_ERROR) continue;
|
|
|
|
/* add outline only if char is printable */
|
|
if(dwSize)
|
|
{
|
|
outline = HeapAlloc(GetProcessHeap(), 0, dwSize);
|
|
if (!outline) return FALSE;
|
|
|
|
GetGlyphOutlineW(dev->hdc, str[idx], ggo_flags, &gm, dwSize, outline, &identity);
|
|
PATH_add_outline(physdev, x + offset.x, y + offset.y, outline, dwSize);
|
|
|
|
HeapFree(GetProcessHeap(), 0, outline);
|
|
}
|
|
|
|
if (dx)
|
|
{
|
|
if(flags & ETO_PDY)
|
|
{
|
|
offset.x += dx[idx * 2];
|
|
offset.y += dx[idx * 2 + 1];
|
|
}
|
|
else
|
|
offset.x += dx[idx];
|
|
}
|
|
else
|
|
{
|
|
offset.x += gm.gmCellIncX;
|
|
offset.y += gm.gmCellIncY;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* pathdrv_CloseFigure
|
|
*/
|
|
static BOOL CDECL pathdrv_CloseFigure( PHYSDEV dev )
|
|
{
|
|
struct path_physdev *physdev = get_path_physdev( dev );
|
|
|
|
/* Set PT_CLOSEFIGURE on the last entry and start a new stroke */
|
|
/* It is not necessary to draw a line, PT_CLOSEFIGURE is a virtual closing line itself */
|
|
if (physdev->path->count) close_figure( physdev->path );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* FlattenPath [GDI32.@]
|
|
*
|
|
*
|
|
*/
|
|
BOOL WINAPI FlattenPath(HDC hdc)
|
|
{
|
|
BOOL ret = FALSE;
|
|
DC *dc = get_dc_ptr( hdc );
|
|
|
|
if (dc)
|
|
{
|
|
PHYSDEV physdev = GET_DC_PHYSDEV( dc, pFlattenPath );
|
|
ret = physdev->funcs->pFlattenPath( physdev );
|
|
release_dc_ptr( dc );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
#define round(x) ((int)((x)>0?(x)+0.5:(x)-0.5))
|
|
|
|
static struct gdi_path *PATH_WidenPath(DC *dc)
|
|
{
|
|
INT i, j, numStrokes, penWidth, penWidthIn, penWidthOut, size, penStyle;
|
|
struct gdi_path *flat_path, *pNewPath, **pStrokes = NULL, *pUpPath, *pDownPath;
|
|
EXTLOGPEN *elp;
|
|
BYTE *type;
|
|
DWORD obj_type, joint, endcap, penType;
|
|
|
|
size = GetObjectW( dc->hPen, 0, NULL );
|
|
if (!size) {
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
return NULL;
|
|
}
|
|
|
|
elp = HeapAlloc( GetProcessHeap(), 0, size );
|
|
GetObjectW( dc->hPen, size, elp );
|
|
|
|
obj_type = GetObjectType(dc->hPen);
|
|
if(obj_type == OBJ_PEN) {
|
|
penStyle = ((LOGPEN*)elp)->lopnStyle;
|
|
}
|
|
else if(obj_type == OBJ_EXTPEN) {
|
|
penStyle = elp->elpPenStyle;
|
|
}
|
|
else {
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
HeapFree( GetProcessHeap(), 0, elp );
|
|
return NULL;
|
|
}
|
|
|
|
penWidth = elp->elpWidth;
|
|
HeapFree( GetProcessHeap(), 0, elp );
|
|
|
|
endcap = (PS_ENDCAP_MASK & penStyle);
|
|
joint = (PS_JOIN_MASK & penStyle);
|
|
penType = (PS_TYPE_MASK & penStyle);
|
|
|
|
/* The function cannot apply to cosmetic pens */
|
|
if(obj_type == OBJ_EXTPEN && penType == PS_COSMETIC) {
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
return NULL;
|
|
}
|
|
|
|
if (!(flat_path = PATH_FlattenPath( dc->path ))) return NULL;
|
|
|
|
penWidthIn = penWidth / 2;
|
|
penWidthOut = penWidth / 2;
|
|
if(penWidthIn + penWidthOut < penWidth)
|
|
penWidthOut++;
|
|
|
|
numStrokes = 0;
|
|
|
|
for(i = 0, j = 0; i < flat_path->count; i++, j++) {
|
|
POINT point;
|
|
if((i == 0 || (flat_path->flags[i-1] & PT_CLOSEFIGURE)) &&
|
|
(flat_path->flags[i] != PT_MOVETO)) {
|
|
ERR("Expected PT_MOVETO %s, got path flag %c\n",
|
|
i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
|
|
flat_path->flags[i]);
|
|
free_gdi_path( flat_path );
|
|
return NULL;
|
|
}
|
|
switch(flat_path->flags[i]) {
|
|
case PT_MOVETO:
|
|
numStrokes++;
|
|
j = 0;
|
|
if(numStrokes == 1)
|
|
pStrokes = HeapAlloc(GetProcessHeap(), 0, sizeof(*pStrokes));
|
|
else
|
|
pStrokes = HeapReAlloc(GetProcessHeap(), 0, pStrokes, numStrokes * sizeof(*pStrokes));
|
|
if(!pStrokes) return NULL;
|
|
pStrokes[numStrokes - 1] = alloc_gdi_path(0);
|
|
/* fall through */
|
|
case PT_LINETO:
|
|
case (PT_LINETO | PT_CLOSEFIGURE):
|
|
point.x = flat_path->points[i].x;
|
|
point.y = flat_path->points[i].y;
|
|
PATH_AddEntry(pStrokes[numStrokes - 1], &point, flat_path->flags[i]);
|
|
break;
|
|
case PT_BEZIERTO:
|
|
/* should never happen because of the FlattenPath call */
|
|
ERR("Should never happen\n");
|
|
break;
|
|
default:
|
|
ERR("Got path flag %c\n", flat_path->flags[i]);
|
|
for(i = 0; i < numStrokes; i++) free_gdi_path(pStrokes[i]);
|
|
HeapFree(GetProcessHeap(), 0, pStrokes);
|
|
free_gdi_path(flat_path);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
pNewPath = alloc_gdi_path( flat_path->count );
|
|
|
|
for(i = 0; i < numStrokes; i++) {
|
|
pUpPath = alloc_gdi_path( pStrokes[i]->count );
|
|
pDownPath = alloc_gdi_path( pStrokes[i]->count );
|
|
|
|
for(j = 0; j < pStrokes[i]->count; j++) {
|
|
/* Beginning or end of the path if not closed */
|
|
if((!(pStrokes[i]->flags[pStrokes[i]->count - 1] & PT_CLOSEFIGURE)) && (j == 0 || j == pStrokes[i]->count - 1) ) {
|
|
/* Compute segment angle */
|
|
double xo, yo, xa, ya, theta;
|
|
POINT pt;
|
|
FLOAT_POINT corners[2];
|
|
if(j == 0) {
|
|
xo = pStrokes[i]->points[j].x;
|
|
yo = pStrokes[i]->points[j].y;
|
|
xa = pStrokes[i]->points[1].x;
|
|
ya = pStrokes[i]->points[1].y;
|
|
}
|
|
else {
|
|
xa = pStrokes[i]->points[j - 1].x;
|
|
ya = pStrokes[i]->points[j - 1].y;
|
|
xo = pStrokes[i]->points[j].x;
|
|
yo = pStrokes[i]->points[j].y;
|
|
}
|
|
theta = atan2( ya - yo, xa - xo );
|
|
switch(endcap) {
|
|
case PS_ENDCAP_SQUARE :
|
|
pt.x = xo + round(sqrt(2) * penWidthOut * cos(M_PI_4 + theta));
|
|
pt.y = yo + round(sqrt(2) * penWidthOut * sin(M_PI_4 + theta));
|
|
PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO) );
|
|
pt.x = xo + round(sqrt(2) * penWidthIn * cos(- M_PI_4 + theta));
|
|
pt.y = yo + round(sqrt(2) * penWidthIn * sin(- M_PI_4 + theta));
|
|
PATH_AddEntry(pUpPath, &pt, PT_LINETO);
|
|
break;
|
|
case PS_ENDCAP_FLAT :
|
|
pt.x = xo + round( penWidthOut * cos(theta + M_PI_2) );
|
|
pt.y = yo + round( penWidthOut * sin(theta + M_PI_2) );
|
|
PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
|
|
pt.x = xo - round( penWidthIn * cos(theta + M_PI_2) );
|
|
pt.y = yo - round( penWidthIn * sin(theta + M_PI_2) );
|
|
PATH_AddEntry(pUpPath, &pt, PT_LINETO);
|
|
break;
|
|
case PS_ENDCAP_ROUND :
|
|
default :
|
|
corners[0].x = xo - penWidthIn;
|
|
corners[0].y = yo - penWidthIn;
|
|
corners[1].x = xo + penWidthOut;
|
|
corners[1].y = yo + penWidthOut;
|
|
PATH_DoArcPart(pUpPath ,corners, theta + M_PI_2 , theta + 3 * M_PI_4, (j == 0 ? PT_MOVETO : 0));
|
|
PATH_DoArcPart(pUpPath ,corners, theta + 3 * M_PI_4 , theta + M_PI, 0);
|
|
PATH_DoArcPart(pUpPath ,corners, theta + M_PI, theta + 5 * M_PI_4, 0);
|
|
PATH_DoArcPart(pUpPath ,corners, theta + 5 * M_PI_4 , theta + 3 * M_PI_2, 0);
|
|
break;
|
|
}
|
|
}
|
|
/* Corpse of the path */
|
|
else {
|
|
/* Compute angle */
|
|
INT previous, next;
|
|
double xa, ya, xb, yb, xo, yo;
|
|
double alpha, theta, miterWidth;
|
|
DWORD _joint = joint;
|
|
POINT pt;
|
|
struct gdi_path *pInsidePath, *pOutsidePath;
|
|
if(j > 0 && j < pStrokes[i]->count - 1) {
|
|
previous = j - 1;
|
|
next = j + 1;
|
|
}
|
|
else if (j == 0) {
|
|
previous = pStrokes[i]->count - 1;
|
|
next = j + 1;
|
|
}
|
|
else {
|
|
previous = j - 1;
|
|
next = 0;
|
|
}
|
|
xo = pStrokes[i]->points[j].x;
|
|
yo = pStrokes[i]->points[j].y;
|
|
xa = pStrokes[i]->points[previous].x;
|
|
ya = pStrokes[i]->points[previous].y;
|
|
xb = pStrokes[i]->points[next].x;
|
|
yb = pStrokes[i]->points[next].y;
|
|
theta = atan2( yo - ya, xo - xa );
|
|
alpha = atan2( yb - yo, xb - xo ) - theta;
|
|
if (alpha > 0) alpha -= M_PI;
|
|
else alpha += M_PI;
|
|
if(_joint == PS_JOIN_MITER && dc->miterLimit < fabs(1 / sin(alpha/2))) {
|
|
_joint = PS_JOIN_BEVEL;
|
|
}
|
|
if(alpha > 0) {
|
|
pInsidePath = pUpPath;
|
|
pOutsidePath = pDownPath;
|
|
}
|
|
else if(alpha < 0) {
|
|
pInsidePath = pDownPath;
|
|
pOutsidePath = pUpPath;
|
|
}
|
|
else {
|
|
continue;
|
|
}
|
|
/* Inside angle points */
|
|
if(alpha > 0) {
|
|
pt.x = xo - round( penWidthIn * cos(theta + M_PI_2) );
|
|
pt.y = yo - round( penWidthIn * sin(theta + M_PI_2) );
|
|
}
|
|
else {
|
|
pt.x = xo + round( penWidthIn * cos(theta + M_PI_2) );
|
|
pt.y = yo + round( penWidthIn * sin(theta + M_PI_2) );
|
|
}
|
|
PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
|
|
if(alpha > 0) {
|
|
pt.x = xo + round( penWidthIn * cos(M_PI_2 + alpha + theta) );
|
|
pt.y = yo + round( penWidthIn * sin(M_PI_2 + alpha + theta) );
|
|
}
|
|
else {
|
|
pt.x = xo - round( penWidthIn * cos(M_PI_2 + alpha + theta) );
|
|
pt.y = yo - round( penWidthIn * sin(M_PI_2 + alpha + theta) );
|
|
}
|
|
PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
|
|
/* Outside angle point */
|
|
switch(_joint) {
|
|
case PS_JOIN_MITER :
|
|
miterWidth = fabs(penWidthOut / cos(M_PI_2 - fabs(alpha) / 2));
|
|
pt.x = xo + round( miterWidth * cos(theta + alpha / 2) );
|
|
pt.y = yo + round( miterWidth * sin(theta + alpha / 2) );
|
|
PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
|
|
break;
|
|
case PS_JOIN_BEVEL :
|
|
if(alpha > 0) {
|
|
pt.x = xo + round( penWidthOut * cos(theta + M_PI_2) );
|
|
pt.y = yo + round( penWidthOut * sin(theta + M_PI_2) );
|
|
}
|
|
else {
|
|
pt.x = xo - round( penWidthOut * cos(theta + M_PI_2) );
|
|
pt.y = yo - round( penWidthOut * sin(theta + M_PI_2) );
|
|
}
|
|
PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
|
|
if(alpha > 0) {
|
|
pt.x = xo - round( penWidthOut * cos(M_PI_2 + alpha + theta) );
|
|
pt.y = yo - round( penWidthOut * sin(M_PI_2 + alpha + theta) );
|
|
}
|
|
else {
|
|
pt.x = xo + round( penWidthOut * cos(M_PI_2 + alpha + theta) );
|
|
pt.y = yo + round( penWidthOut * sin(M_PI_2 + alpha + theta) );
|
|
}
|
|
PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
|
|
break;
|
|
case PS_JOIN_ROUND :
|
|
default :
|
|
if(alpha > 0) {
|
|
pt.x = xo + round( penWidthOut * cos(theta + M_PI_2) );
|
|
pt.y = yo + round( penWidthOut * sin(theta + M_PI_2) );
|
|
}
|
|
else {
|
|
pt.x = xo - round( penWidthOut * cos(theta + M_PI_2) );
|
|
pt.y = yo - round( penWidthOut * sin(theta + M_PI_2) );
|
|
}
|
|
PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
|
|
pt.x = xo + round( penWidthOut * cos(theta + alpha / 2) );
|
|
pt.y = yo + round( penWidthOut * sin(theta + alpha / 2) );
|
|
PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
|
|
if(alpha > 0) {
|
|
pt.x = xo - round( penWidthOut * cos(M_PI_2 + alpha + theta) );
|
|
pt.y = yo - round( penWidthOut * sin(M_PI_2 + alpha + theta) );
|
|
}
|
|
else {
|
|
pt.x = xo + round( penWidthOut * cos(M_PI_2 + alpha + theta) );
|
|
pt.y = yo + round( penWidthOut * sin(M_PI_2 + alpha + theta) );
|
|
}
|
|
PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
type = add_points( pNewPath, pUpPath->points, pUpPath->count, PT_LINETO );
|
|
type[0] = PT_MOVETO;
|
|
reverse_points( pDownPath->points, pDownPath->count );
|
|
type = add_points( pNewPath, pDownPath->points, pDownPath->count, PT_LINETO );
|
|
if (pStrokes[i]->flags[pStrokes[i]->count - 1] & PT_CLOSEFIGURE) type[0] = PT_MOVETO;
|
|
|
|
free_gdi_path( pStrokes[i] );
|
|
free_gdi_path( pUpPath );
|
|
free_gdi_path( pDownPath );
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, pStrokes);
|
|
free_gdi_path( flat_path );
|
|
return pNewPath;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* StrokeAndFillPath [GDI32.@]
|
|
*
|
|
*
|
|
*/
|
|
BOOL WINAPI StrokeAndFillPath(HDC hdc)
|
|
{
|
|
BOOL ret = FALSE;
|
|
DC *dc = get_dc_ptr( hdc );
|
|
|
|
if (dc)
|
|
{
|
|
PHYSDEV physdev = GET_DC_PHYSDEV( dc, pStrokeAndFillPath );
|
|
ret = physdev->funcs->pStrokeAndFillPath( physdev );
|
|
release_dc_ptr( dc );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* StrokePath [GDI32.@]
|
|
*
|
|
*
|
|
*/
|
|
BOOL WINAPI StrokePath(HDC hdc)
|
|
{
|
|
BOOL ret = FALSE;
|
|
DC *dc = get_dc_ptr( hdc );
|
|
|
|
if (dc)
|
|
{
|
|
PHYSDEV physdev = GET_DC_PHYSDEV( dc, pStrokePath );
|
|
ret = physdev->funcs->pStrokePath( physdev );
|
|
release_dc_ptr( dc );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* WidenPath [GDI32.@]
|
|
*
|
|
*
|
|
*/
|
|
BOOL WINAPI WidenPath(HDC hdc)
|
|
{
|
|
BOOL ret = FALSE;
|
|
DC *dc = get_dc_ptr( hdc );
|
|
|
|
if (dc)
|
|
{
|
|
PHYSDEV physdev = GET_DC_PHYSDEV( dc, pWidenPath );
|
|
ret = physdev->funcs->pWidenPath( physdev );
|
|
release_dc_ptr( dc );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* null driver fallback implementations
|
|
*/
|
|
|
|
BOOL CDECL nulldrv_BeginPath( PHYSDEV dev )
|
|
{
|
|
DC *dc = get_nulldrv_dc( dev );
|
|
struct path_physdev *physdev;
|
|
struct gdi_path *path = alloc_gdi_path(0);
|
|
|
|
if (!path) return FALSE;
|
|
if (!path_driver.pCreateDC( &dc->physDev, NULL, NULL, NULL, NULL ))
|
|
{
|
|
free_gdi_path( path );
|
|
return FALSE;
|
|
}
|
|
physdev = get_path_physdev( find_dc_driver( dc, &path_driver ));
|
|
physdev->path = path;
|
|
path->pos = dc->cur_pos;
|
|
lp_to_dp( dc, &path->pos, 1 );
|
|
if (dc->path) free_gdi_path( dc->path );
|
|
dc->path = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CDECL nulldrv_EndPath( PHYSDEV dev )
|
|
{
|
|
SetLastError( ERROR_CAN_NOT_COMPLETE );
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CDECL nulldrv_AbortPath( PHYSDEV dev )
|
|
{
|
|
DC *dc = get_nulldrv_dc( dev );
|
|
|
|
if (dc->path) free_gdi_path( dc->path );
|
|
dc->path = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CDECL nulldrv_CloseFigure( PHYSDEV dev )
|
|
{
|
|
SetLastError( ERROR_CAN_NOT_COMPLETE );
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CDECL nulldrv_SelectClipPath( PHYSDEV dev, INT mode )
|
|
{
|
|
BOOL ret = FALSE;
|
|
HRGN hrgn = PathToRegion( dev->hdc );
|
|
|
|
if (hrgn)
|
|
{
|
|
ret = ExtSelectClipRgn( dev->hdc, hrgn, mode ) != ERROR;
|
|
DeleteObject( hrgn );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
BOOL CDECL nulldrv_FillPath( PHYSDEV dev )
|
|
{
|
|
if (GetPath( dev->hdc, NULL, NULL, 0 ) == -1) return FALSE;
|
|
AbortPath( dev->hdc );
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CDECL nulldrv_StrokeAndFillPath( PHYSDEV dev )
|
|
{
|
|
if (GetPath( dev->hdc, NULL, NULL, 0 ) == -1) return FALSE;
|
|
AbortPath( dev->hdc );
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CDECL nulldrv_StrokePath( PHYSDEV dev )
|
|
{
|
|
if (GetPath( dev->hdc, NULL, NULL, 0 ) == -1) return FALSE;
|
|
AbortPath( dev->hdc );
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CDECL nulldrv_FlattenPath( PHYSDEV dev )
|
|
{
|
|
DC *dc = get_nulldrv_dc( dev );
|
|
struct gdi_path *path;
|
|
|
|
if (!dc->path)
|
|
{
|
|
SetLastError( ERROR_CAN_NOT_COMPLETE );
|
|
return FALSE;
|
|
}
|
|
if (!(path = PATH_FlattenPath( dc->path ))) return FALSE;
|
|
free_gdi_path( dc->path );
|
|
dc->path = path;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CDECL nulldrv_WidenPath( PHYSDEV dev )
|
|
{
|
|
DC *dc = get_nulldrv_dc( dev );
|
|
struct gdi_path *path;
|
|
|
|
if (!dc->path)
|
|
{
|
|
SetLastError( ERROR_CAN_NOT_COMPLETE );
|
|
return FALSE;
|
|
}
|
|
if (!(path = PATH_WidenPath( dc ))) return FALSE;
|
|
free_gdi_path( dc->path );
|
|
dc->path = path;
|
|
return TRUE;
|
|
}
|
|
|
|
const struct gdi_dc_funcs path_driver =
|
|
{
|
|
NULL, /* pAbortDoc */
|
|
pathdrv_AbortPath, /* pAbortPath */
|
|
NULL, /* pAlphaBlend */
|
|
pathdrv_AngleArc, /* pAngleArc */
|
|
pathdrv_Arc, /* pArc */
|
|
pathdrv_ArcTo, /* pArcTo */
|
|
pathdrv_BeginPath, /* pBeginPath */
|
|
NULL, /* pBlendImage */
|
|
pathdrv_Chord, /* pChord */
|
|
pathdrv_CloseFigure, /* pCloseFigure */
|
|
NULL, /* pCreateCompatibleDC */
|
|
pathdrv_CreateDC, /* pCreateDC */
|
|
pathdrv_DeleteDC, /* pDeleteDC */
|
|
NULL, /* pDeleteObject */
|
|
NULL, /* pDeviceCapabilities */
|
|
pathdrv_Ellipse, /* pEllipse */
|
|
NULL, /* pEndDoc */
|
|
NULL, /* pEndPage */
|
|
pathdrv_EndPath, /* pEndPath */
|
|
NULL, /* pEnumFonts */
|
|
NULL, /* pEnumICMProfiles */
|
|
NULL, /* pExcludeClipRect */
|
|
NULL, /* pExtDeviceMode */
|
|
NULL, /* pExtEscape */
|
|
NULL, /* pExtFloodFill */
|
|
NULL, /* pExtSelectClipRgn */
|
|
pathdrv_ExtTextOut, /* pExtTextOut */
|
|
NULL, /* pFillPath */
|
|
NULL, /* pFillRgn */
|
|
NULL, /* pFlattenPath */
|
|
NULL, /* pFontIsLinked */
|
|
NULL, /* pFrameRgn */
|
|
NULL, /* pGdiComment */
|
|
NULL, /* pGetBoundsRect */
|
|
NULL, /* pGetCharABCWidths */
|
|
NULL, /* pGetCharABCWidthsI */
|
|
NULL, /* pGetCharWidth */
|
|
NULL, /* pGetCharWidthInfo */
|
|
NULL, /* pGetDeviceCaps */
|
|
NULL, /* pGetDeviceGammaRamp */
|
|
NULL, /* pGetFontData */
|
|
NULL, /* pGetFontRealizationInfo */
|
|
NULL, /* pGetFontUnicodeRanges */
|
|
NULL, /* pGetGlyphIndices */
|
|
NULL, /* pGetGlyphOutline */
|
|
NULL, /* pGetICMProfile */
|
|
NULL, /* pGetImage */
|
|
NULL, /* pGetKerningPairs */
|
|
NULL, /* pGetNearestColor */
|
|
NULL, /* pGetOutlineTextMetrics */
|
|
NULL, /* pGetPixel */
|
|
NULL, /* pGetSystemPaletteEntries */
|
|
NULL, /* pGetTextCharsetInfo */
|
|
NULL, /* pGetTextExtentExPoint */
|
|
NULL, /* pGetTextExtentExPointI */
|
|
NULL, /* pGetTextFace */
|
|
NULL, /* pGetTextMetrics */
|
|
NULL, /* pGradientFill */
|
|
NULL, /* pIntersectClipRect */
|
|
NULL, /* pInvertRgn */
|
|
pathdrv_LineTo, /* pLineTo */
|
|
NULL, /* pModifyWorldTransform */
|
|
pathdrv_MoveTo, /* pMoveTo */
|
|
NULL, /* pOffsetClipRgn */
|
|
NULL, /* pOffsetViewportOrg */
|
|
NULL, /* pOffsetWindowOrg */
|
|
NULL, /* pPaintRgn */
|
|
NULL, /* pPatBlt */
|
|
pathdrv_Pie, /* pPie */
|
|
pathdrv_PolyBezier, /* pPolyBezier */
|
|
pathdrv_PolyBezierTo, /* pPolyBezierTo */
|
|
pathdrv_PolyDraw, /* pPolyDraw */
|
|
pathdrv_PolyPolygon, /* pPolyPolygon */
|
|
pathdrv_PolyPolyline, /* pPolyPolyline */
|
|
pathdrv_Polygon, /* pPolygon */
|
|
pathdrv_Polyline, /* pPolyline */
|
|
pathdrv_PolylineTo, /* pPolylineTo */
|
|
NULL, /* pPutImage */
|
|
NULL, /* pRealizeDefaultPalette */
|
|
NULL, /* pRealizePalette */
|
|
pathdrv_Rectangle, /* pRectangle */
|
|
NULL, /* pResetDC */
|
|
NULL, /* pRestoreDC */
|
|
pathdrv_RoundRect, /* pRoundRect */
|
|
NULL, /* pSaveDC */
|
|
NULL, /* pScaleViewportExt */
|
|
NULL, /* pScaleWindowExt */
|
|
NULL, /* pSelectBitmap */
|
|
NULL, /* pSelectBrush */
|
|
NULL, /* pSelectClipPath */
|
|
NULL, /* pSelectFont */
|
|
NULL, /* pSelectPalette */
|
|
NULL, /* pSelectPen */
|
|
NULL, /* pSetArcDirection */
|
|
NULL, /* pSetBkColor */
|
|
NULL, /* pSetBkMode */
|
|
NULL, /* pSetDCBrushColor */
|
|
NULL, /* pSetDCPenColor */
|
|
NULL, /* pSetDIBColorTable */
|
|
NULL, /* pSetDIBitsToDevice */
|
|
NULL, /* pSetDeviceClipping */
|
|
NULL, /* pSetDeviceGammaRamp */
|
|
NULL, /* pSetLayout */
|
|
NULL, /* pSetMapMode */
|
|
NULL, /* pSetMapperFlags */
|
|
NULL, /* pSetPixel */
|
|
NULL, /* pSetPolyFillMode */
|
|
NULL, /* pSetROP2 */
|
|
NULL, /* pSetRelAbs */
|
|
NULL, /* pSetStretchBltMode */
|
|
NULL, /* pSetTextAlign */
|
|
NULL, /* pSetTextCharacterExtra */
|
|
NULL, /* pSetTextColor */
|
|
NULL, /* pSetTextJustification */
|
|
NULL, /* pSetViewportExt */
|
|
NULL, /* pSetViewportOrg */
|
|
NULL, /* pSetWindowExt */
|
|
NULL, /* pSetWindowOrg */
|
|
NULL, /* pSetWorldTransform */
|
|
NULL, /* pStartDoc */
|
|
NULL, /* pStartPage */
|
|
NULL, /* pStretchBlt */
|
|
NULL, /* pStretchDIBits */
|
|
NULL, /* pStrokeAndFillPath */
|
|
NULL, /* pStrokePath */
|
|
NULL, /* pUnrealizePalette */
|
|
NULL, /* pWidenPath */
|
|
NULL, /* pD3DKMTCheckVidPnExclusiveOwnership */
|
|
NULL, /* pD3DKMTSetVidPnSourceOwner */
|
|
NULL, /* wine_get_wgl_driver */
|
|
NULL, /* wine_get_vulkan_driver */
|
|
GDI_PRIORITY_PATH_DRV /* priority */
|
|
};
|