gdiplus: Initial GdipFlattenPath implementation.

This commit is contained in:
Nikolay Sivov 2008-09-03 19:11:53 +04:00 committed by Alexandre Julliard
parent 78312704d9
commit 72304d8377
2 changed files with 259 additions and 14 deletions

View File

@ -33,6 +33,135 @@
WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
typedef struct path_list_node_t path_list_node_t;
struct path_list_node_t {
GpPointF pt;
BYTE type; /* PathPointTypeStart or PathPointTypeLine */
path_list_node_t *next;
};
/* init list */
static BOOL init_path_list(path_list_node_t **node, REAL x, REAL y)
{
*node = GdipAlloc(sizeof(path_list_node_t));
if(!*node)
return FALSE;
(*node)->pt.X = x;
(*node)->pt.Y = y;
(*node)->type = PathPointTypeStart;
(*node)->next = NULL;
return TRUE;
}
/* free all nodes including argument */
static void free_path_list(path_list_node_t *node)
{
path_list_node_t *n = node;
while(n){
n = n->next;
GdipFree(node);
node = n;
}
}
/* Add a node after 'node' */
/*
* Returns
* pointer on success
* NULL on allocation problems
*/
static path_list_node_t* add_path_list_node(path_list_node_t *node, REAL x, REAL y, BOOL type)
{
path_list_node_t *new;
new = GdipAlloc(sizeof(path_list_node_t));
if(!new)
return NULL;
new->pt.X = x;
new->pt.Y = y;
new->type = type;
new->next = node->next;
node->next = new;
return new;
}
/* returns element count */
static INT path_list_count(path_list_node_t *node)
{
INT count = 1;
while((node = node->next))
++count;
return count;
}
/* GdipFlattenPath helper */
/*
* Used to recursively flatten single Bezier curve
* Parameters:
* - start : pointer to start point node;
* - (x2, y2): first control point;
* - (x3, y3): second control point;
* - end : pointer to end point node
* - flatness: admissible error of linear approximation.
*
* Return value:
* TRUE : success
* FALSE: out of memory
*
* TODO: used quality criteria should be revised to match native as
* closer as possible.
*/
static BOOL flatten_bezier(path_list_node_t *start, REAL x2, REAL y2, REAL x3, REAL y3,
path_list_node_t *end, REAL flatness)
{
/* this 5 middle points with start/end define to half-curves */
GpPointF mp[5];
GpPointF pt, pt_st;
path_list_node_t *node;
/* calculate bezier curve middle points == new control points */
mp[0].X = (start->pt.X + x2) / 2.0;
mp[0].Y = (start->pt.Y + y2) / 2.0;
/* middle point between control points */
pt.X = (x2 + x3) / 2.0;
pt.Y = (y2 + y3) / 2.0;
mp[1].X = (mp[0].X + pt.X) / 2.0;
mp[1].Y = (mp[0].Y + pt.Y) / 2.0;
mp[4].X = (end->pt.X + x3) / 2.0;
mp[4].Y = (end->pt.Y + y3) / 2.0;
mp[3].X = (mp[4].X + pt.X) / 2.0;
mp[3].Y = (mp[4].Y + pt.Y) / 2.0;
mp[2].X = (mp[1].X + mp[3].X) / 2.0;
mp[2].Y = (mp[1].Y + mp[3].Y) / 2.0;
pt = end->pt;
pt_st = start->pt;
/* check flatness as a half of distance between middle point and a linearized path */
if(fabs(((pt.Y - pt_st.Y)*mp[2].X + (pt_st.X - pt.X)*mp[2].Y +
(pt_st.Y*pt.X - pt_st.X*pt.Y))) <=
(0.5 * flatness*sqrtf((powf(pt.Y - pt_st.Y, 2.0) + powf(pt_st.X - pt.X, 2.0))))){
return TRUE;
}
else
/* add a middle point */
if(!(node = add_path_list_node(start, mp[2].X, mp[2].Y, PathPointTypeLine)))
return FALSE;
/* do the same with halfs */
flatten_bezier(start, mp[0].X, mp[0].Y, mp[1].X, mp[1].Y, node, flatness);
flatten_bezier(node, mp[3].X, mp[3].Y, mp[4].X, mp[4].Y, end, flatness);
return TRUE;
}
GpStatus WINGDIPAPI GdipAddPathArc(GpPath *path, REAL x1, REAL y1, REAL x2,
REAL y2, REAL startAngle, REAL sweepAngle)
{
@ -818,15 +947,106 @@ GpStatus WINGDIPAPI GdipDeletePath(GpPath *path)
GpStatus WINGDIPAPI GdipFlattenPath(GpPath *path, GpMatrix* matrix, REAL flatness)
{
static int calls;
path_list_node_t *list, *node;
GpPointF pt;
INT i = 1;
INT startidx = 0;
TRACE("(%p, %p, %.2f)\n", path, matrix, flatness);
if(!path)
return InvalidParameter;
if(!(calls++))
FIXME("not implemented\n");
if(matrix){
WARN("transformation not supported yet!\n");
return NotImplemented;
}
return NotImplemented;
if(path->pathdata.Count == 0)
return Ok;
pt = path->pathdata.Points[0];
if(!init_path_list(&list, pt.X, pt.Y))
return OutOfMemory;
node = list;
while(i < path->pathdata.Count){
BYTE type = path->pathdata.Types[i] & PathPointTypePathTypeMask;
path_list_node_t *start;
pt = path->pathdata.Points[i];
/* save last start point index */
if(type == PathPointTypeStart)
startidx = i;
/* always add line points and start points */
if((type == PathPointTypeStart) || (type == PathPointTypeLine)){
type = (path->pathdata.Types[i] & ~PathPointTypeBezier) | PathPointTypeLine;
if(!add_path_list_node(node, pt.X, pt.Y, type))
goto memout;
node = node->next;
continue;
}
/* Bezier curve always stored as 4 points */
if((path->pathdata.Types[i-1] & PathPointTypePathTypeMask) != PathPointTypeStart){
type = (path->pathdata.Types[i] & ~PathPointTypeBezier) | PathPointTypeLine;
if(!add_path_list_node(node, pt.X, pt.Y, type))
goto memout;
node = node->next;
}
/* test for closed figure */
if(path->pathdata.Types[i+1] & PathPointTypeCloseSubpath){
pt = path->pathdata.Points[startidx];
++i;
}
else
{
i += 2;
pt = path->pathdata.Points[i];
};
start = node;
/* add Bezier end point */
type = (path->pathdata.Types[i] & ~PathPointTypeBezier) | PathPointTypeLine;
if(!add_path_list_node(node, pt.X, pt.Y, type))
goto memout;
node = node->next;
/* flatten curve */
if(!flatten_bezier(start, path->pathdata.Points[i-2].X, path->pathdata.Points[i-2].Y,
path->pathdata.Points[i-1].X, path->pathdata.Points[i-1].Y,
node, flatness))
goto memout;
++i;
}/* while */
/* store path data back */
i = path_list_count(list);
if(!lengthen_path(path, i))
goto memout;
path->pathdata.Count = i;
node = list;
for(i = 0; i < path->pathdata.Count; i++){
path->pathdata.Points[i] = node->pt;
path->pathdata.Types[i] = node->type;
node = node->next;
}
free_path_list(list);
return Ok;
memout:
free_path_list(list);
return OutOfMemory;
}
GpStatus WINGDIPAPI GdipGetPathData(GpPath *path, GpPathData* pathData)

View File

@ -888,10 +888,10 @@ static void test_addpie(void)
static path_test_t flattenellipse_path[] = {
{100.0, 25.0,PathPointTypeStart, 0, 0}, /*0*/
{99.0, 30.0, PathPointTypeLine, 0, 1}, /*1*/
{96.0, 34.8, PathPointTypeLine, 0, 1}, /*2*/
{91.5, 39.0, PathPointTypeLine, 0, 1}, /*3*/
{85.5, 42.8, PathPointTypeLine, 0, 1}, /*4*/
{99.0, 30.0, PathPointTypeLine, 0, 0}, /*1*/
{96.0, 34.8, PathPointTypeLine, 0, 0}, /*2*/
{91.5, 39.0, PathPointTypeLine, 0, 0}, /*3*/
{85.5, 42.8, PathPointTypeLine, 0, 0}, /*4*/
{69.5, 48.0, PathPointTypeLine, 0, 1}, /*5*/
{50.0, 50.0, PathPointTypeLine, 0, 1}, /*6*/
{30.5, 48.0, PathPointTypeLine, 0, 1}, /*7*/
@ -916,14 +916,26 @@ static path_test_t flattenellipse_path[] = {
static path_test_t flattenarc_path[] = {
{100.0, 25.0,PathPointTypeStart, 0, 0}, /*0*/
{99.0, 30.0, PathPointTypeLine, 0, 1}, /*1*/
{96.0, 34.8, PathPointTypeLine, 0, 1}, /*2*/
{91.5, 39.0, PathPointTypeLine, 0, 1}, /*3*/
{85.5, 42.8, PathPointTypeLine, 0, 1}, /*4*/
{99.0, 30.0, PathPointTypeLine, 0, 0}, /*1*/
{96.0, 34.8, PathPointTypeLine, 0, 0}, /*2*/
{91.5, 39.0, PathPointTypeLine, 0, 0}, /*3*/
{85.5, 42.8, PathPointTypeLine, 0, 0}, /*4*/
{69.5, 48.0, PathPointTypeLine, 0, 1}, /*5*/
{50.0, 50.0, PathPointTypeLine, 0, 1} /*6*/
};
static path_test_t flattenquater_path[] = {
{100.0, 50.0,PathPointTypeStart, 0, 0}, /*0*/
{99.0, 60.0, PathPointTypeLine, 0, 0}, /*1*/
{96.0, 69.5, PathPointTypeLine, 0, 0}, /*2*/
{91.5, 78.0, PathPointTypeLine, 0, 0}, /*3*/
{85.5, 85.5, PathPointTypeLine, 0, 0}, /*4*/
{78.0, 91.5, PathPointTypeLine, 0, 0}, /*5*/
{69.5, 96.0, PathPointTypeLine, 0, 0}, /*6*/
{60.0, 99.0, PathPointTypeLine, 0, 0}, /*7*/
{50.0, 100.0,PathPointTypeLine, 0, 0} /*8*/
};
static void test_flatten(void)
{
GpStatus status;
@ -941,11 +953,15 @@ static void test_flatten(void)
status = GdipFlattenPath(NULL, m, 0.0);
expect(InvalidParameter, status);
/* flatten empty path */
status = GdipFlattenPath(path, NULL, 1.0);
expect(Ok, status);
status = GdipAddPathEllipse(path, 0.0, 0.0, 100.0, 50.0);
expect(Ok, status);
status = GdipFlattenPath(path, NULL, 1.0);
todo_wine expect(Ok, status);
expect(Ok, status);
ok_path(path, flattenellipse_path, sizeof(flattenellipse_path)/sizeof(path_test_t), TRUE);
status = GdipResetPath(path);
@ -953,9 +969,18 @@ static void test_flatten(void)
status = GdipAddPathArc(path, 0.0, 0.0, 100.0, 50.0, 0.0, 90.0);
expect(Ok, status);
status = GdipFlattenPath(path, NULL, 1.0);
todo_wine expect(Ok, status);
expect(Ok, status);
ok_path(path, flattenarc_path, sizeof(flattenarc_path)/sizeof(path_test_t), TRUE);
/* easy case - quater of a full circle */
status = GdipResetPath(path);
expect(Ok, status);
status = GdipAddPathArc(path, 0.0, 0.0, 100.0, 100.0, 0.0, 90.0);
expect(Ok, status);
status = GdipFlattenPath(path, NULL, 1.0);
expect(Ok, status);
ok_path(path, flattenquater_path, sizeof(flattenquater_path)/sizeof(path_test_t), FALSE);
GdipDeleteMatrix(m);
GdipDeletePath(path);
}