[PATCH 2/4] d2d1: Implement d2d_path_geometry_Simplify().

Henri Verbeet hverbeet at codeweavers.com
Wed Jun 7 14:31:52 CDT 2017


Signed-off-by: Henri Verbeet <hverbeet at codeweavers.com>
---
 dlls/d2d1/geometry.c   | 165 +++++++++++++-
 dlls/d2d1/tests/d2d1.c | 577 ++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 660 insertions(+), 82 deletions(-)

diff --git a/dlls/d2d1/geometry.c b/dlls/d2d1/geometry.c
index c2de634..6327923 100644
--- a/dlls/d2d1/geometry.c
+++ b/dlls/d2d1/geometry.c
@@ -26,6 +26,9 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(d2d);
 
+#define D2D_FIGURE_FLAG_CLOSED          0x00000001u
+#define D2D_FIGURE_FLAG_HOLLOW          0x00000002u
+
 #define D2D_CDT_EDGE_FLAG_FREED         0x80000000u
 #define D2D_CDT_EDGE_FLAG_VISITED(r)    (1u << (r))
 
@@ -66,6 +69,7 @@ struct d2d_figure
     size_t bezier_control_count;
 
     D2D1_RECT_F bounds;
+    unsigned int flags;
 };
 
 struct d2d_cdt_edge_ref
@@ -2054,6 +2058,8 @@ static void STDMETHODCALLTYPE d2d_geometry_sink_SetFillMode(ID2D1GeometrySink *i
 
     TRACE("iface %p, mode %#x.\n", iface, mode);
 
+    if (geometry->u.path.state == D2D_GEOMETRY_STATE_CLOSED)
+        return;
     geometry->u.path.fill_mode = mode;
 }
 
@@ -2066,6 +2072,7 @@ static void STDMETHODCALLTYPE d2d_geometry_sink_BeginFigure(ID2D1GeometrySink *i
         D2D1_POINT_2F start_point, D2D1_FIGURE_BEGIN figure_begin)
 {
     struct d2d_geometry *geometry = impl_from_ID2D1GeometrySink(iface);
+    struct d2d_figure *figure;
 
     TRACE("iface %p, start_point {%.8e, %.8e}, figure_begin %#x.\n",
             iface, start_point.x, start_point.y, figure_begin);
@@ -2086,7 +2093,11 @@ static void STDMETHODCALLTYPE d2d_geometry_sink_BeginFigure(ID2D1GeometrySink *i
         return;
     }
 
-    if (!d2d_figure_add_vertex(&geometry->u.path.figures[geometry->u.path.figure_count - 1], start_point))
+    figure = &geometry->u.path.figures[geometry->u.path.figure_count - 1];
+    if (figure_begin == D2D1_FIGURE_BEGIN_HOLLOW)
+        figure->flags |= D2D_FIGURE_FLAG_HOLLOW;
+
+    if (!d2d_figure_add_vertex(figure, start_point))
     {
         ERR("Failed to add vertex.\n");
         geometry->u.path.state = D2D_GEOMETRY_STATE_ERROR;
@@ -2183,9 +2194,12 @@ static void STDMETHODCALLTYPE d2d_geometry_sink_EndFigure(ID2D1GeometrySink *ifa
 
     figure = &geometry->u.path.figures[geometry->u.path.figure_count - 1];
     figure->vertex_types[figure->vertex_count - 1] = D2D_VERTEX_TYPE_LINE;
-    if (figure_end == D2D1_FIGURE_END_CLOSED && !memcmp(&figure->vertices[0],
-            &figure->vertices[figure->vertex_count - 1], sizeof(*figure->vertices)))
-        --figure->vertex_count;
+    if (figure_end == D2D1_FIGURE_END_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))
     {
@@ -2526,14 +2540,153 @@ static HRESULT STDMETHODCALLTYPE d2d_path_geometry_CompareWithGeometry(ID2D1Path
     return E_NOTIMPL;
 }
 
+static void d2d_geometry_flatten_cubic(ID2D1SimplifiedGeometrySink *sink, const D2D1_POINT_2F *p0,
+        const D2D1_BEZIER_SEGMENT *b, float tolerance)
+{
+    D2D1_BEZIER_SEGMENT b0, b1;
+    D2D1_POINT_2F q;
+    float d;
+
+    /* It's certainly possible to calculate the maximum deviation of the
+     * approximation from the curve, but it's a little involved. Instead, note
+     * that if the control points were evenly spaced and collinear, p1 would
+     * be exactly between p0 and p2, and p2 would be exactly between p1 and
+     * p3. The deviation is a decent enough approximation, and much easier to
+     * calculate.
+     *
+     * p1' = (p0 + p2) / 2
+     * p2' = (p1 + p3) / 2
+     *   d = ‖p1 - p1'‖₁ + ‖p2 - p2'‖₁ */
+    d2d_point_lerp(&q, p0, &b->point2, 0.5f);
+    d2d_point_subtract(&q, &b->point1, &q);
+    d = fabsf(q.x) + fabsf(q.y);
+    d2d_point_lerp(&q, &b->point1, &b->point3, 0.5f);
+    d2d_point_subtract(&q, &b->point2, &q);
+    d += fabsf(q.x) + fabsf(q.y);
+    if (d < tolerance)
+    {
+        ID2D1SimplifiedGeometrySink_AddLines(sink, &b->point3, 1);
+        return;
+    }
+
+    d2d_point_lerp(&q, &b->point1, &b->point2, 0.5f);
+
+    b1.point3 = b->point3;
+    d2d_point_lerp(&b1.point2, &b1.point3, &b->point2, 0.5f);
+    d2d_point_lerp(&b1.point1, &b1.point2, &q, 0.5f);
+
+    d2d_point_lerp(&b0.point1, p0, &b->point1, 0.5f);
+    d2d_point_lerp(&b0.point2, &b0.point1, &q, 0.5f);
+    d2d_point_lerp(&b0.point3, &b0.point2, &b1.point1, 0.5f);
+
+    d2d_geometry_flatten_cubic(sink, p0, &b0, tolerance);
+    ID2D1SimplifiedGeometrySink_SetSegmentFlags(sink, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN);
+    d2d_geometry_flatten_cubic(sink, &b0.point3, &b1, tolerance);
+    ID2D1SimplifiedGeometrySink_SetSegmentFlags(sink, D2D1_PATH_SEGMENT_NONE);
+}
+
+static void d2d_geometry_simplify_quadratic(ID2D1SimplifiedGeometrySink *sink,
+        D2D1_GEOMETRY_SIMPLIFICATION_OPTION option, const D2D1_POINT_2F *p0,
+        const D2D1_POINT_2F *p1, const D2D1_POINT_2F *p2, float tolerance)
+{
+    D2D1_BEZIER_SEGMENT b;
+
+    d2d_point_lerp(&b.point1, p0, p1, 2.0f / 3.0f);
+    d2d_point_lerp(&b.point2, p2, p1, 2.0f / 3.0f);
+    b.point3 = *p2;
+
+    if (option == D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES)
+        d2d_geometry_flatten_cubic(sink, p0, &b, tolerance);
+    else
+        ID2D1SimplifiedGeometrySink_AddBeziers(sink, &b, 1);
+}
+
 static HRESULT STDMETHODCALLTYPE d2d_path_geometry_Simplify(ID2D1PathGeometry *iface,
         D2D1_GEOMETRY_SIMPLIFICATION_OPTION option, const D2D1_MATRIX_3X2_F *transform, float tolerance,
         ID2D1SimplifiedGeometrySink *sink)
 {
-    FIXME("iface %p, option %#x, transform %p, tolerance %.8e, sink %p stub!\n",
+    struct d2d_geometry *geometry = impl_from_ID2D1PathGeometry(iface);
+    enum d2d_vertex_type type = D2D_VERTEX_TYPE_NONE;
+    unsigned int i, j, bezier_idx;
+    D2D1_FIGURE_BEGIN begin;
+    D2D1_POINT_2F p, p1, p2;
+    D2D1_FIGURE_END end;
+
+    TRACE("iface %p, option %#x, transform %p, tolerance %.8e, sink %p.\n",
             iface, option, transform, tolerance, sink);
 
-    return E_NOTIMPL;
+    ID2D1SimplifiedGeometrySink_SetFillMode(sink, geometry->u.path.fill_mode);
+    for (i = 0; i < geometry->u.path.figure_count; ++i)
+    {
+        const struct d2d_figure *figure = &geometry->u.path.figures[i];
+
+        for (j = 0; j < figure->vertex_count; ++j)
+        {
+            if (figure->vertex_types[j] == D2D_VERTEX_TYPE_NONE)
+                continue;
+
+            p = figure->vertices[j];
+            if (transform)
+                d2d_point_transform(&p, transform, p.x, p.y);
+            begin = figure->flags & D2D_FIGURE_FLAG_HOLLOW ? D2D1_FIGURE_BEGIN_HOLLOW : D2D1_FIGURE_BEGIN_FILLED;
+            ID2D1SimplifiedGeometrySink_BeginFigure(sink, p, begin);
+            type = figure->vertex_types[j];
+            break;
+        }
+
+        for (bezier_idx = 0, ++j; j < figure->vertex_count; ++j)
+        {
+            if (figure->vertex_types[j] == D2D_VERTEX_TYPE_NONE)
+                continue;
+
+            switch (type)
+            {
+                case D2D_VERTEX_TYPE_LINE:
+                    p = figure->vertices[j];
+                    if (transform)
+                        d2d_point_transform(&p, transform, p.x, p.y);
+                    ID2D1SimplifiedGeometrySink_AddLines(sink, &p, 1);
+                    break;
+
+                case D2D_VERTEX_TYPE_BEZIER:
+                    p1 = figure->bezier_controls[bezier_idx++];
+                    if (transform)
+                        d2d_point_transform(&p1, transform, p1.x, p1.y);
+                    p2 = figure->vertices[j];
+                    if (transform)
+                        d2d_point_transform(&p2, transform, p2.x, p2.y);
+                    d2d_geometry_simplify_quadratic(sink, option, &p, &p1, &p2, tolerance);
+                    p = p2;
+                    break;
+
+                default:
+                    FIXME("Unhandled vertex type %#x.\n", type);
+                    p = figure->vertices[j];
+                    if (transform)
+                        d2d_point_transform(&p, transform, p.x, p.y);
+                    ID2D1SimplifiedGeometrySink_AddLines(sink, &p, 1);
+                    break;
+            }
+
+            type = figure->vertex_types[j];
+        }
+
+        if (type == D2D_VERTEX_TYPE_BEZIER)
+        {
+            p1 = figure->bezier_controls[bezier_idx++];
+            if (transform)
+                d2d_point_transform(&p1, transform, p1.x, p1.y);
+            p2 = figure->vertices[0];
+            if (transform)
+                d2d_point_transform(&p2, transform, p2.x, p2.y);
+            d2d_geometry_simplify_quadratic(sink, option, &p, &p1, &p2, tolerance);
+        }
+
+        end = figure->flags & D2D_FIGURE_FLAG_CLOSED ? D2D1_FIGURE_END_CLOSED : D2D1_FIGURE_END_OPEN;
+        ID2D1SimplifiedGeometrySink_EndFigure(sink, end);
+    }
+
+    return S_OK;
 }
 
 static HRESULT STDMETHODCALLTYPE d2d_path_geometry_Tessellate(ID2D1PathGeometry *iface,
diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c
index 768f8e7..d1f8398 100644
--- a/dlls/d2d1/tests/d2d1.c
+++ b/dlls/d2d1/tests/d2d1.c
@@ -54,6 +54,7 @@ struct geometry_sink
                 D2D1_BEZIER_SEGMENT bezier;
                 D2D1_POINT_2F line;
             } u;
+            DWORD flags;
         } *segments;
         unsigned int segments_size;
         unsigned int segment_count;
@@ -62,6 +63,7 @@ struct geometry_sink
     unsigned int figure_count;
 
     D2D1_FILL_MODE fill_mode;
+    DWORD segment_flags;
     BOOL closed;
 };
 
@@ -709,7 +711,9 @@ static void STDMETHODCALLTYPE geometry_sink_SetFillMode(ID2D1SimplifiedGeometryS
 static void STDMETHODCALLTYPE geometry_sink_SetSegmentFlags(ID2D1SimplifiedGeometrySink *iface,
         D2D1_PATH_SEGMENT flags)
 {
-    ok(0, "Got unexpected segment flags %#x.\n", flags);
+    struct geometry_sink *sink = impl_from_ID2D1SimplifiedGeometrySink(iface);
+
+    sink->segment_flags = flags;
 }
 
 static void STDMETHODCALLTYPE geometry_sink_BeginFigure(ID2D1SimplifiedGeometrySink *iface,
@@ -757,6 +761,7 @@ static void STDMETHODCALLTYPE geometry_sink_AddLines(ID2D1SimplifiedGeometrySink
         segment = geometry_figure_add_segment(figure);
         segment->type = SEGMENT_LINE;
         segment->u.line = points[i];
+        segment->flags = sink->segment_flags;
     }
 }
 
@@ -773,6 +778,7 @@ static void STDMETHODCALLTYPE geometry_sink_AddBeziers(ID2D1SimplifiedGeometrySi
         segment = geometry_figure_add_segment(figure);
         segment->type = SEGMENT_BEZIER;
         segment->u.bezier = beziers[i];
+        segment->flags = sink->segment_flags;
     }
 }
 
@@ -828,6 +834,84 @@ static void geometry_sink_cleanup(struct geometry_sink *sink)
     HeapFree(GetProcessHeap(), 0, sink->figures);
 }
 
+#define geometry_sink_check(a, b, c, d, e) geometry_sink_check_(__LINE__, a, b, c, d, e)
+static void geometry_sink_check_(unsigned int line, const struct geometry_sink *sink, D2D1_FILL_MODE fill_mode,
+        unsigned int figure_count, const struct expected_geometry_figure *expected_figures, unsigned int ulps)
+{
+    const struct geometry_segment *segment, *expected_segment;
+    const struct expected_geometry_figure *expected_figure;
+    const struct geometry_figure *figure;
+    unsigned int i, j;
+    BOOL match;
+
+    ok_(__FILE__, line)(sink->fill_mode == fill_mode,
+            "Got unexpected fill mode %#x.\n", sink->fill_mode);
+    ok_(__FILE__, line)(sink->figure_count == figure_count,
+            "Got unexpected figure count %u, expected %u.\n", sink->figure_count, figure_count);
+    ok_(__FILE__, line)(!sink->closed, "Sink is closed.\n");
+
+    for (i = 0; i < figure_count; ++i)
+    {
+        expected_figure = &expected_figures[i];
+        figure = &sink->figures[i];
+
+        ok_(__FILE__, line)(figure->begin == expected_figure->begin,
+                "Got unexpected figure %u begin %#x, expected %#x.\n",
+                i, figure->begin, expected_figure->begin);
+        ok_(__FILE__, line)(figure->end == expected_figure->end,
+                "Got unexpected figure %u end %#x, expected %#x.\n",
+                i, figure->end, expected_figure->end);
+        match = compare_point(&figure->start_point,
+                expected_figure->start_point.x, expected_figure->start_point.y, ulps);
+        ok_(__FILE__, line)(match, "Got unexpected figure %u start point {%.8e, %.8e}, expected {%.8e, %.8e}.\n",
+                i, figure->start_point.x, figure->start_point.y,
+                expected_figure->start_point.x, expected_figure->start_point.y);
+        ok_(__FILE__, line)(figure->segment_count == expected_figure->segment_count,
+                "Got unexpected figure %u segment count %u, expected %u.\n",
+                i, figure->segment_count, expected_figure->segment_count);
+
+        for (j = 0; j < figure->segment_count; ++j)
+        {
+            expected_segment = &expected_figure->segments[j];
+            segment = &figure->segments[j];
+            ok_(__FILE__, line)(segment->type == expected_segment->type,
+                    "Got unexpected figure %u, segment %u type %#x, expected %#x.\n",
+                    i, j, segment->type, expected_segment->type);
+            ok_(__FILE__, line)(segment->flags == expected_segment->flags,
+                    "Got unexpected figure %u, segment %u flags %#x, expected %#x.\n",
+                    i, j, segment->flags, expected_segment->flags);
+            switch (segment->type)
+            {
+                case SEGMENT_LINE:
+                    match = compare_point(&segment->u.line,
+                            expected_segment->u.line.x, expected_segment->u.line.y, ulps);
+                    ok_(__FILE__, line)(match, "Got unexpected figure %u segment %u {%.8e, %.8e}, "
+                            "expected {%.8e, %.8e}.\n",
+                            i, j, segment->u.line.x, segment->u.line.y,
+                            expected_segment->u.line.x, expected_segment->u.line.y);
+                    break;
+
+                case SEGMENT_BEZIER:
+                    match = compare_bezier_segment(&segment->u.bezier,
+                            expected_segment->u.bezier.point1.x, expected_segment->u.bezier.point1.y,
+                            expected_segment->u.bezier.point2.x, expected_segment->u.bezier.point2.y,
+                            expected_segment->u.bezier.point3.x, expected_segment->u.bezier.point3.y,
+                            ulps);
+                    ok_(__FILE__, line)(match, "Got unexpected figure %u segment %u "
+                            "{%.8e, %.8e, %.8e, %.8e, %.8e, %.8e}, "
+                            "expected {%.8e, %.8e, %.8e, %.8e, %.8e, %.8e}.\n",
+                            i, j, segment->u.bezier.point1.x, segment->u.bezier.point1.y,
+                            segment->u.bezier.point2.x, segment->u.bezier.point2.y,
+                            segment->u.bezier.point3.x, segment->u.bezier.point3.y,
+                            expected_segment->u.bezier.point1.x, expected_segment->u.bezier.point1.y,
+                            expected_segment->u.bezier.point2.x, expected_segment->u.bezier.point2.y,
+                            expected_segment->u.bezier.point3.x, expected_segment->u.bezier.point3.y);
+                    break;
+            }
+        }
+    }
+}
+
 static void test_clip(void)
 {
     IDXGISwapChain *swapchain;
@@ -1571,6 +1655,7 @@ static void test_path_geometry(void)
     ID2D1TransformedGeometry *transformed_geometry;
     D2D1_MATRIX_3X2_F matrix, tmp_matrix;
     ID2D1GeometrySink *sink, *tmp_sink;
+    struct geometry_sink simplify_sink;
     D2D1_POINT_2F point = {0.0f, 0.0f};
     ID2D1SolidColorBrush *brush;
     ID2D1PathGeometry *geometry;
@@ -1587,6 +1672,320 @@ static void test_path_geometry(void)
     HWND window;
     HRESULT hr;
 
+    static const struct geometry_segment expected_segments[] =
+    {
+        /* Figure 0. */
+        {SEGMENT_LINE,   {{{ 55.0f,  20.0f}}}},
+        {SEGMENT_LINE,   {{{ 55.0f, 220.0f}}}},
+        {SEGMENT_LINE,   {{{ 25.0f, 220.0f}}}},
+        {SEGMENT_LINE,   {{{ 25.0f, 100.0f}}}},
+        {SEGMENT_LINE,   {{{ 75.0f, 100.0f}}}},
+        {SEGMENT_LINE,   {{{ 75.0f, 300.0f}}}},
+        {SEGMENT_LINE,   {{{  5.0f, 300.0f}}}},
+        {SEGMENT_LINE,   {{{  5.0f,  60.0f}}}},
+        {SEGMENT_LINE,   {{{ 45.0f,  60.0f}}}},
+        {SEGMENT_LINE,   {{{ 45.0f, 180.0f}}}},
+        {SEGMENT_LINE,   {{{ 35.0f, 180.0f}}}},
+        {SEGMENT_LINE,   {{{ 35.0f, 140.0f}}}},
+        {SEGMENT_LINE,   {{{ 65.0f, 140.0f}}}},
+        {SEGMENT_LINE,   {{{ 65.0f, 260.0f}}}},
+        {SEGMENT_LINE,   {{{ 15.0f, 260.0f}}}},
+        /* Figure 1. */
+        {SEGMENT_LINE,   {{{155.0f, 160.0f}}}},
+        {SEGMENT_LINE,   {{{ 85.0f, 160.0f}}}},
+        {SEGMENT_LINE,   {{{ 85.0f, 300.0f}}}},
+        {SEGMENT_LINE,   {{{120.0f, 300.0f}}}},
+        {SEGMENT_LINE,   {{{120.0f,  20.0f}}}},
+        {SEGMENT_LINE,   {{{155.0f,  20.0f}}}},
+        {SEGMENT_LINE,   {{{155.0f, 160.0f}}}},
+        {SEGMENT_LINE,   {{{ 85.0f, 160.0f}}}},
+        {SEGMENT_LINE,   {{{ 85.0f,  20.0f}}}},
+        {SEGMENT_LINE,   {{{120.0f,  20.0f}}}},
+        {SEGMENT_LINE,   {{{120.0f, 300.0f}}}},
+        /* Figure 2. */
+        {SEGMENT_LINE,   {{{165.0f, 300.0f}}}},
+        {SEGMENT_LINE,   {{{235.0f, 300.0f}}}},
+        {SEGMENT_LINE,   {{{235.0f,  20.0f}}}},
+        /* Figure 3. */
+        {SEGMENT_LINE,   {{{225.0f, 260.0f}}}},
+        {SEGMENT_LINE,   {{{175.0f, 260.0f}}}},
+        {SEGMENT_LINE,   {{{175.0f,  60.0f}}}},
+        /* Figure 4. */
+        {SEGMENT_LINE,   {{{185.0f, 220.0f}}}},
+        {SEGMENT_LINE,   {{{185.0f, 100.0f}}}},
+        {SEGMENT_LINE,   {{{215.0f, 100.0f}}}},
+        /* Figure 5. */
+        {SEGMENT_LINE,   {{{205.0f, 180.0f}}}},
+        {SEGMENT_LINE,   {{{205.0f, 140.0f}}}},
+        {SEGMENT_LINE,   {{{195.0f, 140.0f}}}},
+        /* Figure 6. */
+        {SEGMENT_LINE,   {{{135.0f, 620.0f}}}},
+        {SEGMENT_LINE,   {{{135.0f, 420.0f}}}},
+        {SEGMENT_LINE,   {{{105.0f, 420.0f}}}},
+        {SEGMENT_LINE,   {{{105.0f, 540.0f}}}},
+        {SEGMENT_LINE,   {{{155.0f, 540.0f}}}},
+        {SEGMENT_LINE,   {{{155.0f, 340.0f}}}},
+        {SEGMENT_LINE,   {{{ 85.0f, 340.0f}}}},
+        {SEGMENT_LINE,   {{{ 85.0f, 580.0f}}}},
+        {SEGMENT_LINE,   {{{125.0f, 580.0f}}}},
+        {SEGMENT_LINE,   {{{125.0f, 460.0f}}}},
+        {SEGMENT_LINE,   {{{115.0f, 460.0f}}}},
+        {SEGMENT_LINE,   {{{115.0f, 500.0f}}}},
+        {SEGMENT_LINE,   {{{145.0f, 500.0f}}}},
+        {SEGMENT_LINE,   {{{145.0f, 380.0f}}}},
+        {SEGMENT_LINE,   {{{ 95.0f, 380.0f}}}},
+        /* Figure 7. */
+        {SEGMENT_LINE,   {{{235.0f, 480.0f}}}},
+        {SEGMENT_LINE,   {{{165.0f, 480.0f}}}},
+        {SEGMENT_LINE,   {{{165.0f, 340.0f}}}},
+        {SEGMENT_LINE,   {{{200.0f, 340.0f}}}},
+        {SEGMENT_LINE,   {{{200.0f, 620.0f}}}},
+        {SEGMENT_LINE,   {{{235.0f, 620.0f}}}},
+        {SEGMENT_LINE,   {{{235.0f, 480.0f}}}},
+        {SEGMENT_LINE,   {{{165.0f, 480.0f}}}},
+        {SEGMENT_LINE,   {{{165.0f, 620.0f}}}},
+        {SEGMENT_LINE,   {{{200.0f, 620.0f}}}},
+        {SEGMENT_LINE,   {{{200.0f, 340.0f}}}},
+        /* Figure 8. */
+        {SEGMENT_LINE,   {{{245.0f, 340.0f}}}},
+        {SEGMENT_LINE,   {{{315.0f, 340.0f}}}},
+        {SEGMENT_LINE,   {{{315.0f, 620.0f}}}},
+        /* Figure 9. */
+        {SEGMENT_LINE,   {{{305.0f, 380.0f}}}},
+        {SEGMENT_LINE,   {{{255.0f, 380.0f}}}},
+        {SEGMENT_LINE,   {{{255.0f, 580.0f}}}},
+        /* Figure 10. */
+        {SEGMENT_LINE,   {{{265.0f, 420.0f}}}},
+        {SEGMENT_LINE,   {{{265.0f, 540.0f}}}},
+        {SEGMENT_LINE,   {{{295.0f, 540.0f}}}},
+        /* Figure 11. */
+        {SEGMENT_LINE,   {{{285.0f, 460.0f}}}},
+        {SEGMENT_LINE,   {{{285.0f, 500.0f}}}},
+        {SEGMENT_LINE,   {{{275.0f, 500.0f}}}},
+        /* Figure 12. */
+        {SEGMENT_BEZIER, {{{2.83333340e+01f, 1.60000000e+02f},
+                           {4.00000000e+01f, 1.13333336e+02f},
+                           {4.00000000e+01f, 2.00000000e+01f}}}},
+        {SEGMENT_BEZIER, {{{4.00000000e+01f, 1.13333336e+02f},
+                           {5.16666641e+01f, 1.60000000e+02f},
+                           {7.50000000e+01f, 1.60000000e+02f}}}},
+        {SEGMENT_BEZIER, {{{5.16666641e+01f, 1.60000000e+02f},
+                           {4.00000000e+01f, 2.06666656e+02f},
+                           {4.00000000e+01f, 3.00000000e+02f}}}},
+        {SEGMENT_BEZIER, {{{4.00000000e+01f, 2.06666656e+02f},
+                           {2.83333340e+01f, 1.60000000e+02f},
+                           {5.00000000e+00f, 1.60000000e+02f}}}},
+        /* Figure 13. */
+        {SEGMENT_BEZIER, {{{2.00000000e+01f, 1.06666664e+02f},
+                           {2.66666660e+01f, 8.00000000e+01f},
+                           {4.00000000e+01f, 8.00000000e+01f}}}},
+        {SEGMENT_BEZIER, {{{5.33333321e+01f, 8.00000000e+01f},
+                           {6.00000000e+01f, 1.06666664e+02f},
+                           {6.00000000e+01f, 1.60000000e+02f}}}},
+        {SEGMENT_BEZIER, {{{6.00000000e+01f, 2.13333328e+02f},
+                           {5.33333321e+01f, 2.40000000e+02f},
+                           {4.00000000e+01f, 2.40000000e+02f}}}},
+        {SEGMENT_BEZIER, {{{2.66666660e+01f, 2.40000000e+02f},
+                           {2.00000000e+01f, 2.13333328e+02f},
+                           {2.00000000e+01f, 1.60000000e+02f}}}},
+        /* Figure 14. */
+        {SEGMENT_BEZIER, {{{2.83333340e+01f, 6.12000000e+02f},
+                           {4.00000000e+01f, 6.58666687e+02f},
+                           {4.00000000e+01f, 7.52000000e+02f}}}},
+        {SEGMENT_BEZIER, {{{4.00000000e+01f, 6.58666687e+02f},
+                           {5.16666641e+01f, 6.12000000e+02f},
+                           {7.50000000e+01f, 6.12000000e+02f}}}},
+        {SEGMENT_BEZIER, {{{5.16666641e+01f, 6.12000000e+02f},
+                           {4.00000000e+01f, 5.65333313e+02f},
+                           {4.00000000e+01f, 4.72000000e+02f}}}},
+        {SEGMENT_BEZIER, {{{4.00000000e+01f, 5.65333313e+02f},
+                           {2.83333340e+01f, 6.12000000e+02f},
+                           {5.00000000e+00f, 6.12000000e+02f}}}},
+        /* Figure 15. */
+        {SEGMENT_BEZIER, {{{2.00000000e+01f, 6.65333313e+02f},
+                           {2.66666660e+01f, 6.92000000e+02f},
+                           {4.00000000e+01f, 6.92000000e+02f}}}},
+        {SEGMENT_BEZIER, {{{5.33333321e+01f, 6.92000000e+02f},
+                           {6.00000000e+01f, 6.65333313e+02f},
+                           {6.00000000e+01f, 6.12000000e+02f}}}},
+        {SEGMENT_BEZIER, {{{6.00000000e+01f, 5.58666687e+02f},
+                           {5.33333321e+01f, 5.32000000e+02f},
+                           {4.00000000e+01f, 5.32000000e+02f}}}},
+        {SEGMENT_BEZIER, {{{2.66666660e+01f, 5.32000000e+02f},
+                           {2.00000000e+01f, 5.58666687e+02f},
+                           {2.00000000e+01f, 6.12000000e+02f}}}},
+        /* Figure 16. */
+        {SEGMENT_BEZIER, {{{1.91750427e+02f, 1.27275856e+02f},
+                           {2.08249573e+02f, 1.27275856e+02f},
+                           {2.24748734e+02f, 6.12792168e+01f}}}},
+        {SEGMENT_BEZIER, {{{2.08249573e+02f, 1.27275856e+02f},
+                           {2.08249573e+02f, 1.93272476e+02f},
+                           {2.24748734e+02f, 2.59269104e+02f}}}},
+        {SEGMENT_BEZIER, {{{2.08249573e+02f, 1.93272476e+02f},
+                           {1.91750427e+02f, 1.93272476e+02f},
+                           {1.75251266e+02f, 2.59269104e+02f}}}},
+        {SEGMENT_BEZIER, {{{1.91750427e+02f, 1.93272476e+02f},
+                           {1.91750427e+02f, 1.27275856e+02f},
+                           {1.75251266e+02f, 6.12792168e+01f}}}},
+        /* Figure 17. */
+        {SEGMENT_BEZIER, {{{1.95285950e+02f, 6.59932632e+01f},
+                           {2.04714050e+02f, 6.59932632e+01f},
+                           {2.14142136e+02f, 1.03705627e+02f}}}},
+        {SEGMENT_BEZIER, {{{2.23570221e+02f, 1.41417984e+02f},
+                           {2.23570221e+02f, 1.79130356e+02f},
+                           {2.14142136e+02f, 2.16842712e+02f}}}},
+        {SEGMENT_BEZIER, {{{2.04714050e+02f, 2.54555069e+02f},
+                           {1.95285950e+02f, 2.54555069e+02f},
+                           {1.85857864e+02f, 2.16842712e+02f}}}},
+        {SEGMENT_BEZIER, {{{1.76429779e+02f, 1.79130356e+02f},
+                           {1.76429779e+02f, 1.41417984e+02f},
+                           {1.85857864e+02f, 1.03705627e+02f}}}},
+        /* Figure 18. */
+        {SEGMENT_BEZIER, {{{1.11847351e+02f, 4.46888092e+02f},
+                           {1.11847351e+02f, 5.12884705e+02f},
+                           {9.53481979e+01f, 5.78881348e+02f}}}},
+        {SEGMENT_BEZIER, {{{1.11847351e+02f, 5.12884705e+02f},
+                           {1.28346512e+02f, 5.12884705e+02f},
+                           {1.44845673e+02f, 5.78881348e+02f}}}},
+        {SEGMENT_BEZIER, {{{1.28346512e+02f, 5.12884705e+02f},
+                           {1.28346512e+02f, 4.46888092e+02f},
+                           {1.44845673e+02f, 3.80891479e+02f}}}},
+        {SEGMENT_BEZIER, {{{1.28346512e+02f, 4.46888092e+02f},
+                           {1.11847351e+02f, 4.46888092e+02f},
+                           {9.53481979e+01f, 3.80891479e+02f}}}},
+        /* Figure 19. */
+        {SEGMENT_BEZIER, {{{9.65267105e+01f, 4.61030243e+02f},
+                           {9.65267105e+01f, 4.98742584e+02f},
+                           {1.05954803e+02f, 5.36454956e+02f}}}},
+        {SEGMENT_BEZIER, {{{1.15382889e+02f, 5.74167297e+02f},
+                           {1.24810982e+02f, 5.74167297e+02f},
+                           {1.34239075e+02f, 5.36454956e+02f}}}},
+        {SEGMENT_BEZIER, {{{1.43667160e+02f, 4.98742584e+02f},
+                           {1.43667160e+02f, 4.61030243e+02f},
+                           {1.34239075e+02f, 4.23317871e+02f}}}},
+        {SEGMENT_BEZIER, {{{1.24810982e+02f, 3.85605499e+02f},
+                           {1.15382889e+02f, 3.85605499e+02f},
+                           {1.05954803e+02f, 4.23317871e+02f}}}},
+        /* Figure 20. */
+        {SEGMENT_LINE,   {{{ 40.0f,  20.0f}}}},
+        {SEGMENT_LINE,   {{{ 75.0f, 160.0f}}}},
+        {SEGMENT_LINE,   {{{ 40.0f, 300.0f}}}},
+        {SEGMENT_LINE,   {{{  5.0f, 160.0f}}}},
+        /* Figure 21. */
+        {SEGMENT_LINE,   {{{ 40.0f,  80.0f}}}},
+        {SEGMENT_LINE,   {{{ 60.0f, 160.0f}}}},
+        {SEGMENT_LINE,   {{{ 40.0f, 240.0f}}}},
+        {SEGMENT_LINE,   {{{ 20.0f, 160.0f}}}},
+        /* Figure 22. */
+        {SEGMENT_LINE,   {{{ 40.0f, 752.0f}}}},
+        {SEGMENT_LINE,   {{{ 75.0f, 612.0f}}}},
+        {SEGMENT_LINE,   {{{ 40.0f, 472.0f}}}},
+        {SEGMENT_LINE,   {{{  5.0f, 612.0f}}}},
+        /* Figure 23. */
+        {SEGMENT_LINE,   {{{ 40.0f, 692.0f}}}},
+        {SEGMENT_LINE,   {{{ 60.0f, 612.0f}}}},
+        {SEGMENT_LINE,   {{{ 40.0f, 532.0f}}}},
+        {SEGMENT_LINE,   {{{ 20.0f, 612.0f}}}},
+        /* Figure 24. */
+        {SEGMENT_LINE,   {{{2.03125019e+01f, 1.51250000e+02f}}}},
+        {SEGMENT_LINE,   {{{3.12500019e+01f, 1.25000008e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{3.78125000e+01f, 8.12500076e+01f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{4.00000000e+01f, 2.00000000e+01f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{4.21875000e+01f, 8.12500076e+01f}}}},
+        {SEGMENT_LINE,   {{{4.87500000e+01f, 1.25000008e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{5.96875000e+01f, 1.51250000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{7.50000000e+01f, 1.60000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{5.96875000e+01f, 1.68750000e+02f}}}},
+        {SEGMENT_LINE,   {{{4.87500000e+01f, 1.95000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{4.21875000e+01f, 2.38750000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{4.00000000e+01f, 3.00000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{3.78125000e+01f, 2.38750000e+02f}}}},
+        {SEGMENT_LINE,   {{{3.12500019e+01f, 1.95000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{2.03125019e+01f, 1.68750000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{5.00000000e+00f, 1.60000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        /* Figure 25. */
+        {SEGMENT_LINE,   {{{2.50000000e+01f, 1.00000000e+02f}}}},
+        {SEGMENT_LINE,   {{{4.00000000e+01f, 8.00000000e+01f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{5.50000000e+01f, 1.00000000e+02f}}}},
+        {SEGMENT_LINE,   {{{6.00000000e+01f, 1.60000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{5.50000000e+01f, 2.20000000e+02f}}}},
+        {SEGMENT_LINE,   {{{4.00000000e+01f, 2.40000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{2.50000000e+01f, 2.20000000e+02f}}}},
+        {SEGMENT_LINE,   {{{2.00000000e+01f, 1.60000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        /* Figure 26. */
+        {SEGMENT_LINE,   {{{2.03125019e+01f, 6.20750000e+02f}}}},
+        {SEGMENT_LINE,   {{{3.12500019e+01f, 6.47000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{3.78125000e+01f, 6.90750000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{4.00000000e+01f, 7.52000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{4.21875000e+01f, 6.90750000e+02f}}}},
+        {SEGMENT_LINE,   {{{4.87500000e+01f, 6.47000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{5.96875000e+01f, 6.20750000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{7.50000000e+01f, 6.12000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{5.96875000e+01f, 6.03250000e+02f}}}},
+        {SEGMENT_LINE,   {{{4.87500000e+01f, 5.77000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{4.21875000e+01f, 5.33250000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{4.00000000e+01f, 4.72000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{3.78125000e+01f, 5.33250000e+02f}}}},
+        {SEGMENT_LINE,   {{{3.12500019e+01f, 5.77000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{2.03125019e+01f, 6.03250000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{5.00000000e+00f, 6.12000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        /* Figure 27. */
+        {SEGMENT_LINE,   {{{2.50000000e+01f, 6.72000000e+02f}}}},
+        {SEGMENT_LINE,   {{{4.00000000e+01f, 6.92000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{5.50000000e+01f, 6.72000000e+02f}}}},
+        {SEGMENT_LINE,   {{{6.00000000e+01f, 6.12000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{5.50000000e+01f, 5.52000000e+02f}}}},
+        {SEGMENT_LINE,   {{{4.00000000e+01f, 5.32000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        {SEGMENT_LINE,   {{{2.50000000e+01f, 5.52000000e+02f}}}},
+        {SEGMENT_LINE,   {{{2.00000000e+01f, 6.12000000e+02f}}}, D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN},
+        /* Figure 28. */
+        {SEGMENT_LINE,   {{{ 75.0f, 300.0f}}}},
+        {SEGMENT_LINE,   {{{  5.0f, 300.0f}}}},
+    };
+    static const struct expected_geometry_figure expected_figures[] =
+    {
+        /* 0 */
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, { 15.0f,  20.0f}, 15, &expected_segments[0]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {155.0f, 300.0f}, 11, &expected_segments[15]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {165.0f,  20.0f},  3, &expected_segments[26]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {225.0f,  60.0f},  3, &expected_segments[29]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {215.0f, 220.0f},  3, &expected_segments[32]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {195.0f, 180.0f},  3, &expected_segments[35]},
+        /* 6 */
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, { 95.0f, 620.0f}, 15, &expected_segments[38]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {235.0f, 340.0f}, 11, &expected_segments[53]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {245.0f, 620.0f},  3, &expected_segments[64]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {305.0f, 580.0f},  3, &expected_segments[67]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {295.0f, 420.0f},  3, &expected_segments[70]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {275.0f, 460.0f},  3, &expected_segments[73]},
+        /* 12 */
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {  5.0f, 160.0f},  4, &expected_segments[76]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, { 20.0f, 160.0f},  4, &expected_segments[80]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {  5.0f, 612.0f},  4, &expected_segments[84]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, { 20.0f, 612.0f},  4, &expected_segments[88]},
+        /* 16 */
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED,
+                {1.75251266e+02f, 6.12792168e+01f}, 4, &expected_segments[92]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED,
+                {1.85857864e+02f, 1.03705627e+02f}, 4, &expected_segments[96]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED,
+                {9.53481979e+01f, 3.80891479e+02f}, 4, &expected_segments[100]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED,
+                {1.05954803e+02f, 4.23317871e+02f}, 4, &expected_segments[104]},
+        /* 20 */
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {  5.0f, 160.0f},  4, &expected_segments[108]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, { 20.0f, 160.0f},  4, &expected_segments[112]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {  5.0f, 612.0f},  4, &expected_segments[116]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, { 20.0f, 612.0f},  4, &expected_segments[120]},
+        /* 24 */
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {  5.0f, 160.0f}, 16, &expected_segments[124]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, { 20.0f, 160.0f},  8, &expected_segments[140]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, {  5.0f, 612.0f}, 16, &expected_segments[148]},
+        {D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_END_CLOSED, { 20.0f, 612.0f},  8, &expected_segments[164]},
+        /* 28 */
+        {D2D1_FIGURE_BEGIN_HOLLOW, D2D1_FIGURE_END_OPEN,   { 40.0f,  20.0f},  2, &expected_segments[172]},
+    };
+
     if (!(device = create_device()))
     {
         skip("Failed to create device, skipping tests.\n");
@@ -1778,21 +2177,47 @@ static void test_path_geometry(void)
     ID2D1GeometrySink_SetFillMode(sink, D2D1_FILL_MODE_WINDING);
     ID2D1GeometrySink_Release(sink);
 
+    geometry_sink_init(&simplify_sink);
+    hr = ID2D1PathGeometry_Simplify(geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES,
+            NULL, 0.0f, &simplify_sink.ID2D1SimplifiedGeometrySink_iface);
+    ok(SUCCEEDED(hr), "Failed to simplify geometry, hr %#x.\n", hr);
+    geometry_sink_check(&simplify_sink, D2D1_FILL_MODE_ALTERNATE, 6, &expected_figures[0], 0);
+    geometry_sink_cleanup(&simplify_sink);
+    geometry_sink_init(&simplify_sink);
+    hr = ID2D1PathGeometry_Simplify(geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
+            NULL, 0.0f, &simplify_sink.ID2D1SimplifiedGeometrySink_iface);
+    ok(SUCCEEDED(hr), "Failed to simplify geometry, hr %#x.\n", hr);
+    geometry_sink_check(&simplify_sink, D2D1_FILL_MODE_ALTERNATE, 6, &expected_figures[0], 0);
+    geometry_sink_cleanup(&simplify_sink);
+
     set_matrix_identity(&matrix);
     translate_matrix(&matrix, 80.0f, 640.0f);
     scale_matrix(&matrix, 1.0f, -1.0f);
     hr = ID2D1Factory_CreateTransformedGeometry(factory, (ID2D1Geometry *)geometry, &matrix, &transformed_geometry);
     ok(SUCCEEDED(hr), "Failed to create transformed geometry, hr %#x.\n", hr);
 
+    geometry_sink_init(&simplify_sink);
+    hr = ID2D1TransformedGeometry_Simplify(transformed_geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES,
+            NULL, 0.0f, &simplify_sink.ID2D1SimplifiedGeometrySink_iface);
+    ok(SUCCEEDED(hr), "Failed to simplify geometry, hr %#x.\n", hr);
+    geometry_sink_check(&simplify_sink, D2D1_FILL_MODE_ALTERNATE, 6, &expected_figures[6], 0);
+    geometry_sink_cleanup(&simplify_sink);
+
     ID2D1TransformedGeometry_GetSourceGeometry(transformed_geometry, &tmp_geometry);
     ok(tmp_geometry == (ID2D1Geometry *)geometry,
             "Got unexpected source geometry %p, expected %p.\n", tmp_geometry, geometry);
-    ID2D1Geometry_Release(tmp_geometry);
     ID2D1TransformedGeometry_GetTransform(transformed_geometry, &tmp_matrix);
     ok(!memcmp(&tmp_matrix, &matrix, sizeof(matrix)),
             "Got unexpected matrix {%.8e, %.8e, %.8e, %.8e, %.8e, %.8e}.\n",
             tmp_matrix._11, tmp_matrix._12, tmp_matrix._21,
             tmp_matrix._22, tmp_matrix._31, tmp_matrix._32);
+    geometry_sink_init(&simplify_sink);
+    hr = ID2D1Geometry_Simplify(tmp_geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES,
+            &tmp_matrix, 0.0f, &simplify_sink.ID2D1SimplifiedGeometrySink_iface);
+    ok(SUCCEEDED(hr), "Failed to simplify geometry, hr %#x.\n", hr);
+    geometry_sink_check(&simplify_sink, D2D1_FILL_MODE_ALTERNATE, 6, &expected_figures[6], 0);
+    geometry_sink_cleanup(&simplify_sink);
+    ID2D1Geometry_Release(tmp_geometry);
 
     ID2D1RenderTarget_BeginDraw(rt);
     set_color(&color, 0.396f, 0.180f, 0.537f, 1.0f);
@@ -1845,6 +2270,13 @@ static void test_path_geometry(void)
     ok(count == 44, "Got unexpected segment count %u.\n", count);
     ID2D1GeometrySink_Release(sink);
 
+    geometry_sink_init(&simplify_sink);
+    hr = ID2D1PathGeometry_Simplify(geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES,
+            NULL, 0.0f, &simplify_sink.ID2D1SimplifiedGeometrySink_iface);
+    ok(SUCCEEDED(hr), "Failed to simplify geometry, hr %#x.\n", hr);
+    geometry_sink_check(&simplify_sink, D2D1_FILL_MODE_WINDING, 6, &expected_figures[0], 0);
+    geometry_sink_cleanup(&simplify_sink);
+
     set_matrix_identity(&matrix);
     translate_matrix(&matrix, 320.0f, 320.0f);
     scale_matrix(&matrix, -1.0f, 1.0f);
@@ -1877,6 +2309,25 @@ static void test_path_geometry(void)
     ok(count == 20, "Got unexpected segment count %u.\n", count);
     ID2D1GeometrySink_Release(sink);
 
+    geometry_sink_init(&simplify_sink);
+    hr = ID2D1PathGeometry_Simplify(geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
+            NULL, 0.0f, &simplify_sink.ID2D1SimplifiedGeometrySink_iface);
+    ok(SUCCEEDED(hr), "Failed to simplify geometry, hr %#x.\n", hr);
+    geometry_sink_check(&simplify_sink, D2D1_FILL_MODE_ALTERNATE, 4, &expected_figures[12], 1);
+    geometry_sink_cleanup(&simplify_sink);
+    geometry_sink_init(&simplify_sink);
+    hr = ID2D1PathGeometry_Simplify(geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES,
+            NULL, 100.0f, &simplify_sink.ID2D1SimplifiedGeometrySink_iface);
+    ok(SUCCEEDED(hr), "Failed to simplify geometry, hr %#x.\n", hr);
+    geometry_sink_check(&simplify_sink, D2D1_FILL_MODE_ALTERNATE, 4, &expected_figures[20], 1);
+    geometry_sink_cleanup(&simplify_sink);
+    geometry_sink_init(&simplify_sink);
+    hr = ID2D1PathGeometry_Simplify(geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES,
+            NULL, 10.0f, &simplify_sink.ID2D1SimplifiedGeometrySink_iface);
+    ok(SUCCEEDED(hr), "Failed to simplify geometry, hr %#x.\n", hr);
+    geometry_sink_check(&simplify_sink, D2D1_FILL_MODE_ALTERNATE, 4, &expected_figures[24], 1);
+    geometry_sink_cleanup(&simplify_sink);
+
     set_matrix_identity(&matrix);
     scale_matrix(&matrix, 0.5f, 2.0f);
     translate_matrix(&matrix, 400.0f, -33.0f);
@@ -1885,6 +2336,13 @@ static void test_path_geometry(void)
     hr = ID2D1Factory_CreateTransformedGeometry(factory, (ID2D1Geometry *)geometry, &matrix, &transformed_geometry);
     ok(SUCCEEDED(hr), "Failed to create transformed geometry, hr %#x.\n", hr);
 
+    geometry_sink_init(&simplify_sink);
+    hr = ID2D1TransformedGeometry_Simplify(transformed_geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
+            NULL, 0.0f, &simplify_sink.ID2D1SimplifiedGeometrySink_iface);
+    ok(SUCCEEDED(hr), "Failed to simplify geometry, hr %#x.\n", hr);
+    geometry_sink_check(&simplify_sink, D2D1_FILL_MODE_ALTERNATE, 4, &expected_figures[16], 4);
+    geometry_sink_cleanup(&simplify_sink);
+
     ID2D1RenderTarget_BeginDraw(rt);
     ID2D1RenderTarget_Clear(rt, &color);
     ID2D1RenderTarget_FillGeometry(rt, (ID2D1Geometry *)geometry, (ID2D1Brush *)brush, NULL);
@@ -1954,6 +2412,25 @@ static void test_path_geometry(void)
     ok(count == 20, "Got unexpected segment count %u.\n", count);
     ID2D1GeometrySink_Release(sink);
 
+    geometry_sink_init(&simplify_sink);
+    hr = ID2D1PathGeometry_Simplify(geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
+            NULL, 0.0f, &simplify_sink.ID2D1SimplifiedGeometrySink_iface);
+    ok(SUCCEEDED(hr), "Failed to simplify geometry, hr %#x.\n", hr);
+    geometry_sink_check(&simplify_sink, D2D1_FILL_MODE_WINDING, 4, &expected_figures[12], 1);
+    geometry_sink_cleanup(&simplify_sink);
+    geometry_sink_init(&simplify_sink);
+    hr = ID2D1PathGeometry_Simplify(geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES,
+            NULL, 100.0f, &simplify_sink.ID2D1SimplifiedGeometrySink_iface);
+    ok(SUCCEEDED(hr), "Failed to simplify geometry, hr %#x.\n", hr);
+    geometry_sink_check(&simplify_sink, D2D1_FILL_MODE_WINDING, 4, &expected_figures[20], 1);
+    geometry_sink_cleanup(&simplify_sink);
+    geometry_sink_init(&simplify_sink);
+    hr = ID2D1PathGeometry_Simplify(geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES,
+            NULL, 10.0f, &simplify_sink.ID2D1SimplifiedGeometrySink_iface);
+    ok(SUCCEEDED(hr), "Failed to simplify geometry, hr %#x.\n", hr);
+    geometry_sink_check(&simplify_sink, D2D1_FILL_MODE_WINDING, 4, &expected_figures[24], 1);
+    geometry_sink_cleanup(&simplify_sink);
+
     set_matrix_identity(&matrix);
     scale_matrix(&matrix, 0.5f, 2.0f);
     translate_matrix(&matrix, 127.0f, 80.0f);
@@ -2039,6 +2516,28 @@ static void test_path_geometry(void)
     ok(match, "Surface does not match.\n");
     ID2D1PathGeometry_Release(geometry);
 
+    hr = ID2D1Factory_CreatePathGeometry(factory, &geometry);
+    ok(SUCCEEDED(hr), "Failed to create path geometry, hr %#x.\n", hr);
+    hr = ID2D1PathGeometry_Open(geometry, &sink);
+    ok(SUCCEEDED(hr), "Failed to open geometry sink, hr %#x.\n", hr);
+    set_point(&point, 40.0f,   20.0f);
+    ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_HOLLOW);
+    line_to(sink, 75.0f,  300.0f);
+    line_to(sink,  5.0f,  300.0f);
+    ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_OPEN);
+    hr = ID2D1GeometrySink_Close(sink);
+    ok(SUCCEEDED(hr), "Failed to close geometry sink, hr %#x.\n", hr);
+    ID2D1GeometrySink_Release(sink);
+
+    geometry_sink_init(&simplify_sink);
+    hr = ID2D1PathGeometry_Simplify(geometry, D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES,
+            NULL, 0.0f, &simplify_sink.ID2D1SimplifiedGeometrySink_iface);
+    ok(SUCCEEDED(hr), "Failed to simplify geometry, hr %#x.\n", hr);
+    geometry_sink_check(&simplify_sink, D2D1_FILL_MODE_ALTERNATE, 1, &expected_figures[28], 1);
+    geometry_sink_cleanup(&simplify_sink);
+
+    ID2D1PathGeometry_Release(geometry);
+
     ID2D1SolidColorBrush_Release(brush);
     ID2D1RenderTarget_Release(rt);
     refcount = ID2D1Factory_Release(factory);
@@ -2049,80 +2548,6 @@ static void test_path_geometry(void)
     DestroyWindow(window);
 }
 
-#define geometry_sink_check(a, b, c, d, e) geometry_sink_check_(__LINE__, a, b, c, d, e)
-static void geometry_sink_check_(unsigned int line, const struct geometry_sink *sink, D2D1_FILL_MODE fill_mode,
-        unsigned int figure_count, const struct expected_geometry_figure *expected_figures, unsigned int ulps)
-{
-    const struct geometry_segment *segment, *expected_segment;
-    const struct expected_geometry_figure *expected_figure;
-    const struct geometry_figure *figure;
-    unsigned int i, j;
-    BOOL match;
-
-    ok_(__FILE__, line)(sink->fill_mode == D2D1_FILL_MODE_ALTERNATE,
-            "Got unexpected fill mode %#x.\n", sink->fill_mode);
-    ok_(__FILE__, line)(sink->figure_count == 1, "Got unexpected figure count %u.\n", sink->figure_count);
-    ok_(__FILE__, line)(!sink->closed, "Sink is closed.\n");
-
-    for (i = 0; i < figure_count; ++i)
-    {
-        expected_figure = &expected_figures[i];
-        figure = &sink->figures[i];
-
-        ok_(__FILE__, line)(figure->begin == expected_figure->begin,
-                "Got unexpected figure %u begin %#x, expected %#x.\n",
-                i, figure->begin, expected_figure->begin);
-        ok_(__FILE__, line)(figure->end == expected_figure->end,
-                "Got unexpected figure %u end %#x, expected %#x.\n",
-                i, figure->end, expected_figure->end);
-        match = compare_point(&figure->start_point,
-                expected_figure->start_point.x, expected_figure->start_point.y, ulps);
-        ok_(__FILE__, line)(match, "Got unexpected figure %u start point {%.8e, %.8e}, expected {%.8e, %.8e}.\n",
-                i, figure->start_point.x, figure->start_point.y,
-                expected_figure->start_point.x, expected_figure->start_point.y);
-        ok_(__FILE__, line)(figure->segment_count == expected_figure->segment_count,
-                "Got unexpected figure %u segment count %u, expected %u.\n",
-                i, figure->segment_count, expected_figure->segment_count);
-
-        for (j = 0; j < figure->segment_count; ++j)
-        {
-            expected_segment = &expected_figure->segments[j];
-            segment = &figure->segments[j];
-            ok_(__FILE__, line)(segment->type == expected_segment->type,
-                    "Got unexpected figure %u, segment %u type %#x, expected %#x.\n",
-                    i, j, segment->type, expected_segment->type);
-            switch (segment->type)
-            {
-                case SEGMENT_LINE:
-                    match = compare_point(&segment->u.line,
-                            expected_segment->u.line.x, expected_segment->u.line.y, ulps);
-                    ok_(__FILE__, line)(match, "Got unexpected figure %u segment %u {%.8e, %.8e}, "
-                            "expected {%.8e, %.8e}.\n",
-                            i, j, segment->u.line.x, segment->u.line.y,
-                            expected_segment->u.line.x, expected_segment->u.line.y);
-                    break;
-
-                case SEGMENT_BEZIER:
-                    match = compare_bezier_segment(&segment->u.bezier,
-                            expected_segment->u.bezier.point1.x, expected_segment->u.bezier.point1.y,
-                            expected_segment->u.bezier.point2.x, expected_segment->u.bezier.point2.y,
-                            expected_segment->u.bezier.point3.x, expected_segment->u.bezier.point3.y,
-                            ulps);
-                    ok_(__FILE__, line)(match, "Got unexpected figure %u segment %u "
-                            "{%.8e, %.8e, %.8e, %.8e, %.8e, %.8e}, "
-                            "expected {%.8e, %.8e, %.8e, %.8e, %.8e, %.8e}.\n",
-                            i, j, segment->u.bezier.point1.x, segment->u.bezier.point1.y,
-                            segment->u.bezier.point2.x, segment->u.bezier.point2.y,
-                            segment->u.bezier.point3.x, segment->u.bezier.point3.y,
-                            expected_segment->u.bezier.point1.x, expected_segment->u.bezier.point1.y,
-                            expected_segment->u.bezier.point2.x, expected_segment->u.bezier.point2.y,
-                            expected_segment->u.bezier.point3.x, expected_segment->u.bezier.point3.y);
-                    break;
-            }
-        }
-    }
-}
-
 static void test_rectangle_geometry(void)
 {
     ID2D1TransformedGeometry *transformed_geometry;
-- 
2.1.4




More information about the wine-patches mailing list