d2d1: Use the TYPE_END vertex type when last/first vertices coincide.
When the last vertex is coincident with the first vertex, the last segment should be suppressed for both END_OPEN and END_CLOSED. Only when last and first vertex are not coincident the additional line segment may be added - always for intersection tests and similar, and for stroking operations when the figure is CLOSED. Trying to use an zero-length segment in d2d_geometry_intersect_self() will create invalid segments, causing infinite loops later. Instead of reducing the vertex_count for coincident first/last vertices add a dedicated type. This is required as some operations need the last segment, others do not. This also allows to remove some replicated code in StrokeContains()/GetBounds()/Simplify(), as a last Bézier segment is always processed in the regular loop. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51139 Signed-off-by: Stefan Brüns <stefan.bruens@rwth-aachen.de> Signed-off-by: Henri Verbeet <hverbeet@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
065917d101
commit
b92b6c2929
|
@ -50,6 +50,7 @@ enum d2d_vertex_type
|
||||||
D2D_VERTEX_TYPE_LINE,
|
D2D_VERTEX_TYPE_LINE,
|
||||||
D2D_VERTEX_TYPE_BEZIER,
|
D2D_VERTEX_TYPE_BEZIER,
|
||||||
D2D_VERTEX_TYPE_SPLIT_BEZIER,
|
D2D_VERTEX_TYPE_SPLIT_BEZIER,
|
||||||
|
D2D_VERTEX_TYPE_END,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct d2d_segment_idx
|
struct d2d_segment_idx
|
||||||
|
@ -2219,7 +2220,9 @@ static BOOL d2d_geometry_intersect_self(struct d2d_geometry *geometry)
|
||||||
idx_p.control_idx = 0;
|
idx_p.control_idx = 0;
|
||||||
for (idx_p.vertex_idx = 0; idx_p.vertex_idx < figure_p->vertex_count; ++idx_p.vertex_idx)
|
for (idx_p.vertex_idx = 0; idx_p.vertex_idx < figure_p->vertex_count; ++idx_p.vertex_idx)
|
||||||
{
|
{
|
||||||
type_p = figure_p->vertex_types[idx_p.vertex_idx];
|
if ((type_p = figure_p->vertex_types[idx_p.vertex_idx]) == D2D_VERTEX_TYPE_END)
|
||||||
|
continue;
|
||||||
|
|
||||||
for (idx_q.figure_idx = 0; idx_q.figure_idx <= idx_p.figure_idx; ++idx_q.figure_idx)
|
for (idx_q.figure_idx = 0; idx_q.figure_idx <= idx_p.figure_idx; ++idx_q.figure_idx)
|
||||||
{
|
{
|
||||||
figure_q = &geometry->u.path.figures[idx_q.figure_idx];
|
figure_q = &geometry->u.path.figures[idx_q.figure_idx];
|
||||||
|
@ -2227,7 +2230,9 @@ static BOOL d2d_geometry_intersect_self(struct d2d_geometry *geometry)
|
||||||
{
|
{
|
||||||
if (!d2d_rect_check_overlap(&figure_p->bounds, &figure_q->bounds))
|
if (!d2d_rect_check_overlap(&figure_p->bounds, &figure_q->bounds))
|
||||||
continue;
|
continue;
|
||||||
max_q = figure_q->vertex_count;
|
if ((max_q = figure_q->vertex_count)
|
||||||
|
&& figure_q->vertex_types[max_q - 1] == D2D_VERTEX_TYPE_END)
|
||||||
|
--max_q;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -2607,6 +2612,9 @@ static BOOL d2d_geometry_add_figure_outline(struct d2d_geometry *geometry,
|
||||||
p0 = &figure->vertices[0];
|
p0 = &figure->vertices[0];
|
||||||
if (figure_end == D2D1_FIGURE_END_CLOSED)
|
if (figure_end == D2D1_FIGURE_END_CLOSED)
|
||||||
{
|
{
|
||||||
|
if (figure->vertex_types[vertex_count - 1] == D2D_VERTEX_TYPE_END && !--vertex_count)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
/* In case of a CLOSED path, a join between first and last vertex is
|
/* In case of a CLOSED path, a join between first and last vertex is
|
||||||
* required. */
|
* required. */
|
||||||
if (d2d_vertex_type_is_bezier(figure->vertex_types[vertex_count - 1]))
|
if (d2d_vertex_type_is_bezier(figure->vertex_types[vertex_count - 1]))
|
||||||
|
@ -2921,13 +2929,14 @@ static void STDMETHODCALLTYPE d2d_geometry_sink_EndFigure(ID2D1GeometrySink *ifa
|
||||||
}
|
}
|
||||||
|
|
||||||
figure = &geometry->u.path.figures[geometry->u.path.figure_count - 1];
|
figure = &geometry->u.path.figures[geometry->u.path.figure_count - 1];
|
||||||
figure->vertex_types[figure->vertex_count - 1] = D2D_VERTEX_TYPE_LINE;
|
if (memcmp(&figure->vertices[0], &figure->vertices[figure->vertex_count - 1], sizeof(*figure->vertices)))
|
||||||
|
figure->vertex_types[figure->vertex_count - 1] = D2D_VERTEX_TYPE_LINE;
|
||||||
|
else
|
||||||
|
figure->vertex_types[figure->vertex_count - 1] = D2D_VERTEX_TYPE_END;
|
||||||
if (figure_end == D2D1_FIGURE_END_CLOSED)
|
if (figure_end == D2D1_FIGURE_END_CLOSED)
|
||||||
{
|
{
|
||||||
++geometry->u.path.segment_count;
|
++geometry->u.path.segment_count;
|
||||||
figure->flags |= D2D_FIGURE_FLAG_CLOSED;
|
figure->flags |= D2D_FIGURE_FLAG_CLOSED;
|
||||||
if (!memcmp(&figure->vertices[0], &figure->vertices[figure->vertex_count - 1], sizeof(*figure->vertices)))
|
|
||||||
--figure->vertex_count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!d2d_geometry_add_figure_outline(geometry, figure, figure_end))
|
if (!d2d_geometry_add_figure_outline(geometry, figure, figure_end))
|
||||||
|
@ -3476,14 +3485,6 @@ static HRESULT STDMETHODCALLTYPE d2d_path_geometry_GetBounds(ID2D1PathGeometry *
|
||||||
if (figure->flags & D2D_FIGURE_FLAG_HOLLOW)
|
if (figure->flags & D2D_FIGURE_FLAG_HOLLOW)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Single vertex figures are reduced by CloseFigure(). */
|
|
||||||
if (figure->vertex_count == 0)
|
|
||||||
{
|
|
||||||
d2d_point_transform(&p, transform, figure->bounds.left, figure->bounds.top);
|
|
||||||
d2d_rect_expand(bounds, &p);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < figure->vertex_count; ++j)
|
for (j = 0; j < figure->vertex_count; ++j)
|
||||||
{
|
{
|
||||||
if (figure->vertex_types[j] == D2D_VERTEX_TYPE_NONE)
|
if (figure->vertex_types[j] == D2D_VERTEX_TYPE_NONE)
|
||||||
|
@ -3498,8 +3499,10 @@ static HRESULT STDMETHODCALLTYPE d2d_path_geometry_GetBounds(ID2D1PathGeometry *
|
||||||
|
|
||||||
for (bezier_idx = 0, ++j; j < figure->vertex_count; ++j)
|
for (bezier_idx = 0, ++j; j < figure->vertex_count; ++j)
|
||||||
{
|
{
|
||||||
if (figure->vertex_types[j] == D2D_VERTEX_TYPE_NONE
|
enum d2d_vertex_type next_type;
|
||||||
|| d2d_vertex_type_is_split_bezier(figure->vertex_types[j]))
|
|
||||||
|
if ((next_type = figure->vertex_types[j]) == D2D_VERTEX_TYPE_NONE
|
||||||
|
|| d2d_vertex_type_is_split_bezier(next_type))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
|
@ -3537,26 +3540,7 @@ static HRESULT STDMETHODCALLTYPE d2d_path_geometry_GetBounds(ID2D1PathGeometry *
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
type = figure->vertex_types[j];
|
type = next_type;
|
||||||
}
|
|
||||||
|
|
||||||
if (d2d_vertex_type_is_bezier(type))
|
|
||||||
{
|
|
||||||
/* FIXME: This attempts to approximate a cubic Bézier with
|
|
||||||
* a quadratic one. */
|
|
||||||
p1 = figure->original_bezier_controls[bezier_idx++];
|
|
||||||
d2d_point_transform(&p1, transform, p1.x, p1.y);
|
|
||||||
p2 = figure->original_bezier_controls[bezier_idx++];
|
|
||||||
d2d_point_transform(&p2, transform, p2.x, p2.y);
|
|
||||||
p1.x = (p1.x + p2.x) * 0.75f;
|
|
||||||
p1.y = (p1.y + p2.y) * 0.75f;
|
|
||||||
p2 = figure->vertices[0];
|
|
||||||
d2d_point_transform(&p2, transform, p2.x, p2.y);
|
|
||||||
p1.x -= (p.x + p2.x) * 0.25f;
|
|
||||||
p1.y -= (p.y + p2.y) * 0.25f;
|
|
||||||
|
|
||||||
d2d_rect_get_bezier_bounds(&bezier_bounds, &p, &p1, &p2);
|
|
||||||
d2d_rect_union(bounds, &bezier_bounds);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3619,8 +3603,10 @@ static HRESULT STDMETHODCALLTYPE d2d_path_geometry_StrokeContainsPoint(ID2D1Path
|
||||||
|
|
||||||
for (bezier_idx = 0, ++j; j < figure->vertex_count; ++j)
|
for (bezier_idx = 0, ++j; j < figure->vertex_count; ++j)
|
||||||
{
|
{
|
||||||
if (figure->vertex_types[j] == D2D_VERTEX_TYPE_NONE
|
enum d2d_vertex_type next_type;
|
||||||
|| d2d_vertex_type_is_split_bezier(figure->vertex_types[j]))
|
|
||||||
|
if ((next_type = figure->vertex_types[j]) == D2D_VERTEX_TYPE_NONE
|
||||||
|
|| d2d_vertex_type_is_split_bezier(next_type))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
|
@ -3646,25 +3632,14 @@ static HRESULT STDMETHODCALLTYPE d2d_path_geometry_StrokeContainsPoint(ID2D1Path
|
||||||
}
|
}
|
||||||
if (*contains)
|
if (*contains)
|
||||||
return S_OK;
|
return S_OK;
|
||||||
type = figure->vertex_types[j];
|
type = next_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (figure->flags & D2D_FIGURE_FLAG_CLOSED && (!*contains))
|
if (type == D2D_VERTEX_TYPE_LINE)
|
||||||
{
|
{
|
||||||
if (type == D2D_VERTEX_TYPE_LINE)
|
p1 = figure->vertices[0];
|
||||||
{
|
if (figure->flags & D2D_FIGURE_FLAG_CLOSED)
|
||||||
p1 = figure->vertices[0];
|
|
||||||
*contains = d2d_point_on_line_segment(&point, &p, &p1, transform, stroke_width * 0.5f, tolerance);
|
*contains = d2d_point_on_line_segment(&point, &p, &p1, transform, stroke_width * 0.5f, tolerance);
|
||||||
p = p1;
|
|
||||||
}
|
|
||||||
else if (d2d_vertex_type_is_bezier(type))
|
|
||||||
{
|
|
||||||
b.point1 = figure->original_bezier_controls[bezier_idx++];
|
|
||||||
b.point2 = figure->original_bezier_controls[bezier_idx++];
|
|
||||||
b.point3 = figure->vertices[0];
|
|
||||||
*contains = d2d_point_on_bezier_segment(&point, &p, &b, transform, stroke_width, tolerance);
|
|
||||||
p = b.point3;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*contains)
|
if (*contains)
|
||||||
|
@ -3787,8 +3762,10 @@ static HRESULT STDMETHODCALLTYPE d2d_path_geometry_Simplify(ID2D1PathGeometry *i
|
||||||
|
|
||||||
for (bezier_idx = 0, ++j; j < figure->vertex_count; ++j)
|
for (bezier_idx = 0, ++j; j < figure->vertex_count; ++j)
|
||||||
{
|
{
|
||||||
if (figure->vertex_types[j] == D2D_VERTEX_TYPE_NONE
|
enum d2d_vertex_type next_type;
|
||||||
|| d2d_vertex_type_is_split_bezier(figure->vertex_types[j]))
|
|
||||||
|
if ((next_type = figure->vertex_types[j]) == D2D_VERTEX_TYPE_NONE
|
||||||
|
|| d2d_vertex_type_is_split_bezier(next_type))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
|
@ -3827,25 +3804,7 @@ static HRESULT STDMETHODCALLTYPE d2d_path_geometry_Simplify(ID2D1PathGeometry *i
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
type = figure->vertex_types[j];
|
type = next_type;
|
||||||
}
|
|
||||||
|
|
||||||
if (d2d_vertex_type_is_bezier(type))
|
|
||||||
{
|
|
||||||
b.point1 = figure->original_bezier_controls[bezier_idx++];
|
|
||||||
b.point2 = figure->original_bezier_controls[bezier_idx++];
|
|
||||||
b.point3 = figure->vertices[0];
|
|
||||||
if (transform)
|
|
||||||
{
|
|
||||||
d2d_point_transform(&b.point1, transform, b.point1.x, b.point1.y);
|
|
||||||
d2d_point_transform(&b.point2, transform, b.point2.x, b.point2.y);
|
|
||||||
d2d_point_transform(&b.point3, transform, b.point3.x, b.point3.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (option == D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES)
|
|
||||||
d2d_geometry_flatten_cubic(sink, &p, &b, tolerance);
|
|
||||||
else
|
|
||||||
ID2D1SimplifiedGeometrySink_AddBeziers(sink, &b, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end = figure->flags & D2D_FIGURE_FLAG_CLOSED ? D2D1_FIGURE_END_CLOSED : D2D1_FIGURE_END_OPEN;
|
end = figure->flags & D2D_FIGURE_FLAG_CLOSED ? D2D1_FIGURE_END_CLOSED : D2D1_FIGURE_END_OPEN;
|
||||||
|
|
|
@ -3856,6 +3856,18 @@ static void test_path_geometry(BOOL d3d11)
|
||||||
hr = ID2D1PathGeometry_Open(geometry, &sink);
|
hr = ID2D1PathGeometry_Open(geometry, &sink);
|
||||||
ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
|
ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
|
||||||
|
|
||||||
|
set_point(&point, 20.0f, 80.0f);
|
||||||
|
ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_FILLED);
|
||||||
|
line_to(sink, 40.0f, 100.0f);
|
||||||
|
quadratic_to(sink, 40.0f, 65.0f, 20.0f, 80.0f);
|
||||||
|
ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_OPEN);
|
||||||
|
|
||||||
|
set_point(&point, 80.0f, 20.0f);
|
||||||
|
ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_FILLED);
|
||||||
|
line_to(sink, 60.0f, 40.0f);
|
||||||
|
quadratic_to(sink, 95.0f, 40.0f, 80.0f, 20.0f);
|
||||||
|
ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_OPEN);
|
||||||
|
|
||||||
set_point(&point, 120.0f, 180.0f);
|
set_point(&point, 120.0f, 180.0f);
|
||||||
ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_FILLED);
|
ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_FILLED);
|
||||||
line_to(sink, 140.0f, 160.0f);
|
line_to(sink, 140.0f, 160.0f);
|
||||||
|
@ -3875,7 +3887,7 @@ static void test_path_geometry(BOOL d3d11)
|
||||||
set_rect(&rect, 0.0f, 0.0f, 0.0f, 0.0f);
|
set_rect(&rect, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
hr = ID2D1PathGeometry_GetBounds(geometry, NULL, &rect);
|
hr = ID2D1PathGeometry_GetBounds(geometry, NULL, &rect);
|
||||||
ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
|
ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
|
||||||
match = compare_rect(&rect, 115.5f, 110.0f, 180.0f, 180.0f, 1);
|
match = compare_rect(&rect, 20.0f, 20.0f, 180.0f, 180.0f, 0);
|
||||||
ok(match, "Got unexpected rectangle {%.8e, %.8e, %.8e, %.8e}.\n",
|
ok(match, "Got unexpected rectangle {%.8e, %.8e, %.8e, %.8e}.\n",
|
||||||
rect.left, rect.top, rect.right, rect.bottom);
|
rect.left, rect.top, rect.right, rect.bottom);
|
||||||
|
|
||||||
|
@ -3883,7 +3895,7 @@ static void test_path_geometry(BOOL d3d11)
|
||||||
hr = ID2D1PathGeometry_Simplify(geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
|
hr = ID2D1PathGeometry_Simplify(geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
|
||||||
NULL, 0.0f, &simplify_sink.ID2D1SimplifiedGeometrySink_iface);
|
NULL, 0.0f, &simplify_sink.ID2D1SimplifiedGeometrySink_iface);
|
||||||
ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
|
ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
|
||||||
geometry_sink_check(&simplify_sink, D2D1_FILL_MODE_ALTERNATE, 2, &expected_figures[31], 1);
|
geometry_sink_check(&simplify_sink, D2D1_FILL_MODE_ALTERNATE, 4, &expected_figures[29], 1);
|
||||||
geometry_sink_cleanup(&simplify_sink);
|
geometry_sink_cleanup(&simplify_sink);
|
||||||
|
|
||||||
ID2D1PathGeometry_Release(geometry);
|
ID2D1PathGeometry_Release(geometry);
|
||||||
|
|
Loading…
Reference in New Issue