[PATCH 5/5] d2d1: Implement initial support for quadratic bezier outlines.

Henri Verbeet hverbeet at codeweavers.com
Sun May 21 10:53:43 CDT 2017


Signed-off-by: Henri Verbeet <hverbeet at codeweavers.com>
---
 dlls/d2d1/d2d1_private.h  |  16 +++
 dlls/d2d1/geometry.c      | 102 ++++++++++++++
 dlls/d2d1/render_target.c | 270 +++++++++++++++++++++++++++++++++++-
 dlls/d2d1/tests/d2d1.c    | 339 +++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 725 insertions(+), 2 deletions(-)

diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h
index d39ed14..7d8027b 100644
--- a/dlls/d2d1/d2d1_private.h
+++ b/dlls/d2d1/d2d1_private.h
@@ -41,6 +41,7 @@ enum d2d_brush_type
 enum d2d_shape_type
 {
     D2D_SHAPE_TYPE_OUTLINE,
+    D2D_SHAPE_TYPE_BEZIER_OUTLINE,
     D2D_SHAPE_TYPE_TRIANGLE,
     D2D_SHAPE_TYPE_BEZIER,
     D2D_SHAPE_TYPE_COUNT,
@@ -315,6 +316,13 @@ struct d2d_outline_vertex
     D2D1_POINT_2F next;
 };
 
+struct d2d_bezier_outline_vertex
+{
+    D2D1_POINT_2F position;
+    D2D1_POINT_2F p0, p1, p2;
+    D2D1_POINT_2F prev, next;
+};
+
 struct d2d_geometry
 {
     ID2D1Geometry ID2D1Geometry_iface;
@@ -346,6 +354,14 @@ struct d2d_geometry
         struct d2d_face *faces;
         size_t faces_size;
         size_t face_count;
+
+        struct d2d_bezier_outline_vertex *beziers;
+        size_t beziers_size;
+        size_t bezier_count;
+
+        struct d2d_face *bezier_faces;
+        size_t bezier_faces_size;
+        size_t bezier_face_count;
     } outline;
 
     union
diff --git a/dlls/d2d1/geometry.c b/dlls/d2d1/geometry.c
index 5ef62cb..6c8738f 100644
--- a/dlls/d2d1/geometry.c
+++ b/dlls/d2d1/geometry.c
@@ -142,6 +142,18 @@ static void d2d_outline_vertex_set(struct d2d_outline_vertex *v, float x, float
     d2d_point_set(&v->next, next_x, next_y);
 }
 
+static void d2d_bezier_outline_vertex_set(struct d2d_bezier_outline_vertex *b, const D2D1_POINT_2F *position,
+        const D2D1_POINT_2F *p0, const D2D1_POINT_2F *p1, const D2D1_POINT_2F *p2,
+        float prev_x, float prev_y, float next_x, float next_y)
+{
+    b->position = *position;
+    b->p0 = *p0;
+    b->p1 = *p1;
+    b->p2 = *p2;
+    d2d_point_set(&b->prev, prev_x, prev_y);
+    d2d_point_set(&b->next, next_x, next_y);
+}
+
 static void d2d_fp_two_sum(float *out, float a, float b)
 {
     float a_virt, a_round, b_virt, b_round;
@@ -362,6 +374,13 @@ static void d2d_point_scale(D2D1_POINT_2F *p, float scale)
     p->y *= scale;
 }
 
+static void d2d_point_lerp(D2D1_POINT_2F *out,
+        const D2D1_POINT_2F *a, const D2D1_POINT_2F *b, float t)
+{
+    out->x = a->x * (1.0f - t) + b->x * t;
+    out->y = a->y * (1.0f - t) + b->y * t;
+}
+
 static float d2d_point_dot(const D2D1_POINT_2F *p0, const D2D1_POINT_2F *p1)
 {
     return p0->x * p1->x + p0->y * p1->y;
@@ -1842,6 +1861,70 @@ static BOOL d2d_geometry_outline_add_line_segment(struct d2d_geometry *geometry,
     return TRUE;
 }
 
+static BOOL d2d_geometry_outline_add_bezier_segment(struct d2d_geometry *geometry,
+        const D2D1_POINT_2F *p0, const D2D1_POINT_2F *p1, const D2D1_POINT_2F *p2)
+{
+    struct d2d_bezier_outline_vertex *b;
+    D2D1_POINT_2F r0, r1, r2;
+    D2D1_POINT_2F q0, q1, q2;
+    struct d2d_face *f;
+    size_t base_idx;
+
+    if (!d2d_array_reserve((void **)&geometry->outline.beziers, &geometry->outline.beziers_size,
+            geometry->outline.bezier_count + 7, sizeof(*geometry->outline.beziers)))
+    {
+        ERR("Failed to grow outline beziers array.\n");
+        return FALSE;
+    }
+    base_idx = geometry->outline.bezier_count;
+    b = &geometry->outline.beziers[base_idx];
+
+    if (!d2d_array_reserve((void **)&geometry->outline.bezier_faces, &geometry->outline.bezier_faces_size,
+            geometry->outline.bezier_face_count + 5, sizeof(*geometry->outline.bezier_faces)))
+    {
+        ERR("Failed to grow outline faces array.\n");
+        return FALSE;
+    }
+    f = &geometry->outline.bezier_faces[geometry->outline.bezier_face_count];
+
+    d2d_point_lerp(&q0, p0, p1, 0.5f);
+    d2d_point_lerp(&q1, p1, p2, 0.5f);
+    d2d_point_lerp(&q2, &q0, &q1, 0.5f);
+
+    d2d_point_subtract(&r0, &q0, p0);
+    d2d_point_subtract(&r1, &q1, &q0);
+    d2d_point_subtract(&r2, p2, &q1);
+
+    d2d_point_normalise(&r0);
+    d2d_point_normalise(&r1);
+    d2d_point_normalise(&r2);
+
+    if (d2d_point_ccw(p0, p1, p2) > 0.0f)
+    {
+        d2d_point_scale(&r0, -1.0f);
+        d2d_point_scale(&r1, -1.0f);
+        d2d_point_scale(&r2, -1.0f);
+    }
+
+    d2d_bezier_outline_vertex_set(&b[0],  p0, p0, p1, p2,  r0.x,  r0.y,  r0.x,  r0.y);
+    d2d_bezier_outline_vertex_set(&b[1],  p0, p0, p1, p2, -r0.x, -r0.y, -r0.x, -r0.y);
+    d2d_bezier_outline_vertex_set(&b[2], &q0, p0, p1, p2,  r0.x,  r0.y,  r1.x,  r1.y);
+    d2d_bezier_outline_vertex_set(&b[3], &q2, p0, p1, p2, -r1.x, -r1.y, -r1.x, -r1.y);
+    d2d_bezier_outline_vertex_set(&b[4], &q1, p0, p1, p2,  r1.x,  r1.y,  r2.x,  r2.y);
+    d2d_bezier_outline_vertex_set(&b[5],  p2, p0, p1, p2, -r2.x, -r2.y, -r2.x, -r2.y);
+    d2d_bezier_outline_vertex_set(&b[6],  p2, p0, p1, p2,  r2.x,  r2.y,  r2.x,  r2.y);
+    geometry->outline.bezier_count += 7;
+
+    d2d_face_set(&f[0], base_idx + 0, base_idx + 1, base_idx + 2);
+    d2d_face_set(&f[1], base_idx + 2, base_idx + 1, base_idx + 3);
+    d2d_face_set(&f[2], base_idx + 3, base_idx + 4, base_idx + 2);
+    d2d_face_set(&f[3], base_idx + 5, base_idx + 4, base_idx + 3);
+    d2d_face_set(&f[4], base_idx + 5, base_idx + 6, base_idx + 4);
+    geometry->outline.bezier_face_count += 5;
+
+    return TRUE;
+}
+
 static BOOL d2d_geometry_add_figure_outline(struct d2d_geometry *geometry,
         struct d2d_figure *figure, D2D1_FIGURE_END figure_end)
 {
@@ -1894,6 +1977,21 @@ static BOOL d2d_geometry_add_figure_outline(struct d2d_geometry *geometry,
             ERR("Failed to add line segment.\n");
             return FALSE;
         }
+        else if (type == D2D_VERTEX_TYPE_BEZIER)
+        {
+            const D2D1_POINT_2F *p2;
+
+            if (i == figure->vertex_count - 1)
+                p2 = &figure->vertices[0];
+            else
+                p2 = &figure->vertices[i + 1];
+
+            if (!d2d_geometry_outline_add_bezier_segment(geometry, p0, next, p2))
+            {
+                ERR("Failed to add bezier segment.\n");
+                return FALSE;
+            }
+        }
     }
 
     return TRUE;
@@ -1901,6 +1999,8 @@ static BOOL d2d_geometry_add_figure_outline(struct d2d_geometry *geometry,
 
 static void d2d_geometry_cleanup(struct d2d_geometry *geometry)
 {
+    HeapFree(GetProcessHeap(), 0, geometry->outline.bezier_faces);
+    HeapFree(GetProcessHeap(), 0, geometry->outline.beziers);
     HeapFree(GetProcessHeap(), 0, geometry->outline.faces);
     HeapFree(GetProcessHeap(), 0, geometry->outline.vertices);
     HeapFree(GetProcessHeap(), 0, geometry->fill.bezier_vertices);
@@ -2925,6 +3025,8 @@ static ULONG STDMETHODCALLTYPE d2d_transformed_geometry_Release(ID2D1Transformed
 
     if (!refcount)
     {
+        geometry->outline.bezier_faces = NULL;
+        geometry->outline.beziers = NULL;
         geometry->outline.faces = NULL;
         geometry->outline.vertices = NULL;
         geometry->fill.bezier_vertices = NULL;
diff --git a/dlls/d2d1/render_target.c b/dlls/d2d1/render_target.c
index 8b1dfb7..99c8713 100644
--- a/dlls/d2d1/render_target.c
+++ b/dlls/d2d1/render_target.c
@@ -725,6 +725,36 @@ static void d2d_rt_draw_geometry(struct d2d_d3d_render_target *render_target,
         ID3D10Buffer_Release(ib);
     }
 
+    if (geometry->outline.bezier_face_count)
+    {
+        buffer_desc.ByteWidth = geometry->outline.bezier_face_count * sizeof(*geometry->outline.bezier_faces);
+        buffer_desc.BindFlags = D3D10_BIND_INDEX_BUFFER;
+        buffer_data.pSysMem = geometry->outline.bezier_faces;
+
+        if (FAILED(hr = ID3D10Device_CreateBuffer(render_target->device, &buffer_desc, &buffer_data, &ib)))
+        {
+            WARN("Failed to create beziers index buffer, hr %#x.\n", hr);
+            goto done;
+        }
+
+        buffer_desc.ByteWidth = geometry->outline.bezier_count * sizeof(*geometry->outline.beziers);
+        buffer_desc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
+        buffer_data.pSysMem = geometry->outline.beziers;
+
+        if (FAILED(hr = ID3D10Device_CreateBuffer(render_target->device, &buffer_desc, &buffer_data, &vb)))
+        {
+            ERR("Failed to create beziers vertex buffer, hr %#x.\n", hr);
+            ID3D10Buffer_Release(ib);
+            goto done;
+        }
+
+        d2d_rt_draw(render_target, D2D_SHAPE_TYPE_BEZIER_OUTLINE, ib, 3 * geometry->outline.bezier_face_count, vb,
+                sizeof(*geometry->outline.beziers), vs_cb, ps_cb, brush, NULL);
+
+        ID3D10Buffer_Release(vb);
+        ID3D10Buffer_Release(ib);
+    }
+
 done:
     ID3D10Buffer_Release(ps_cb);
     ID3D10Buffer_Release(vs_cb);
@@ -2031,6 +2061,15 @@ static HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_t
     {
         {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0},
     };
+    static const D3D10_INPUT_ELEMENT_DESC il_desc_bezier_outline[] =
+    {
+        {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0},
+        {"P", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D10_INPUT_PER_VERTEX_DATA, 0},
+        {"P", 1, DXGI_FORMAT_R32G32_FLOAT, 0, 16, D3D10_INPUT_PER_VERTEX_DATA, 0},
+        {"P", 2, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D10_INPUT_PER_VERTEX_DATA, 0},
+        {"PREV", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 32, D3D10_INPUT_PER_VERTEX_DATA, 0},
+        {"NEXT", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 40, D3D10_INPUT_PER_VERTEX_DATA, 0},
+    };
     static const D3D10_INPUT_ELEMENT_DESC il_desc_bezier[] =
     {
         {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0},
@@ -2131,6 +2170,164 @@ static HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_t
         0x00000000, 0x00208246, 0x00000000, 0x00000001, 0x05000036, 0x001020c2, 0x00000000, 0x00101ea6,
         0x00000000, 0x0100003e,
     };
+    /*     ⎡p0.x p0.y 1⎤
+     * A = ⎢p1.x p1.y 1⎥
+     *     ⎣p2.x p2.y 1⎦
+     *
+     *     ⎡0 0⎤
+     * B = ⎢½ 0⎥
+     *     ⎣1 1⎦
+     *
+     * A' = ⎡p1.x-p0.x p1.y-p0.y⎤
+     *      ⎣p2.x-p0.x p2.y-p0.y⎦
+     *
+     * B' = ⎡½ 0⎤
+     *      ⎣1 1⎦
+     *
+     * A'T = B'
+     * T = A'⁻¹B'
+     */
+    static const DWORD vs_code_bezier_outline[] =
+    {
+#if 0
+        float3x2 transform_geometry;
+        float stroke_width;
+        float4 transform_rtx;
+        float4 transform_rty;
+
+        float4 main(float2 position : POSITION, float2 p0 : P0, float2 p1 : P1, float2 p2 : P2,
+                float2 prev : PREV, float2 next : NEXT, out float4 texcoord : UV,
+                out float2x2 stroke_transform : STROKE_TRANSFORM) : SV_POSITION
+        {
+            float2 q_prev, q_next, v_p, q_i, p;
+            float2x2 geom, rt;
+            float l;
+
+            geom = float2x2(transform_geometry._11_21, transform_geometry._12_22);
+            rt = float2x2(transform_rtx.xy, transform_rty.xy);
+            stroke_transform = rt * stroke_width * 0.5f;
+
+            p = mul(geom, position);
+            p0 = mul(geom, p0);
+            p1 = mul(geom, p1);
+            p2 = mul(geom, p2);
+
+            p -= p0;
+            p1 -= p0;
+            p2 -= p0;
+
+            q_prev = normalize(mul(geom, prev));
+            q_next = normalize(mul(geom, next));
+
+            v_p = float2(-q_prev.y, q_prev.x);
+            l = -dot(v_p, q_next) / (1.0f + dot(q_prev, q_next));
+            q_i = l * q_prev + v_p;
+            p += stroke_width * q_i;
+
+            v_p = normalize(float2(-p2.y, p2.x));
+            if (abs(dot(p1, v_p)) < 1.0f)
+            {
+                texcoord.xzw = float3(0.0f, 0.0f, 0.0f);
+                texcoord.y = dot(p, v_p);
+            }
+            else
+            {
+                texcoord.zw = sign(dot(p1, v_p)) * v_p;
+                v_p = -float2(-p.y, p.x) / dot(float2(-p1.y, p1.x), p2);
+                texcoord.x = dot(v_p, p1 - 0.5f * p2);
+                texcoord.y = dot(v_p, p1);
+            }
+
+            position = mul(geom, position)
+                    + float2(transform_geometry._31, transform_geometry._32) + stroke_width * q_i;
+            position = mul(rt, position) + float2(transform_rtx.z, transform_rty.z);
+            position = position * float2(transform_rtx.w, transform_rty.w) + float2(-1.0f, 1.0f);
+
+            return float4(position, 0.0f, 1.0f);
+        }
+#endif
+        0x43425844, 0x411d8ea2, 0xae9762e9, 0x31a7c2d1, 0x529c1b7e, 0x00000001, 0x00000a04, 0x00000003,
+        0x0000002c, 0x000000e4, 0x00000174, 0x4e475349, 0x000000b0, 0x00000006, 0x00000008, 0x00000098,
+        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000303, 0x000000a1, 0x00000000, 0x00000000,
+        0x00000003, 0x00000001, 0x00000303, 0x000000a1, 0x00000001, 0x00000000, 0x00000003, 0x00000002,
+        0x00000303, 0x000000a1, 0x00000002, 0x00000000, 0x00000003, 0x00000003, 0x00000303, 0x000000a3,
+        0x00000000, 0x00000000, 0x00000003, 0x00000004, 0x00000303, 0x000000a8, 0x00000000, 0x00000000,
+        0x00000003, 0x00000005, 0x00000303, 0x49534f50, 0x4e4f4954, 0x50005000, 0x00564552, 0x5458454e,
+        0xababab00, 0x4e47534f, 0x00000088, 0x00000004, 0x00000008, 0x00000068, 0x00000000, 0x00000001,
+        0x00000003, 0x00000000, 0x0000000f, 0x00000074, 0x00000000, 0x00000000, 0x00000003, 0x00000001,
+        0x0000000f, 0x00000077, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000c03, 0x00000077,
+        0x00000001, 0x00000000, 0x00000003, 0x00000003, 0x00000c03, 0x505f5653, 0x5449534f, 0x004e4f49,
+        0x53005655, 0x4b4f5254, 0x52545f45, 0x46534e41, 0x004d524f, 0x52444853, 0x00000888, 0x00010040,
+        0x00000222, 0x04000059, 0x00208e46, 0x00000000, 0x00000004, 0x0300005f, 0x00101032, 0x00000000,
+        0x0300005f, 0x00101032, 0x00000001, 0x0300005f, 0x00101032, 0x00000002, 0x0300005f, 0x00101032,
+        0x00000003, 0x0300005f, 0x00101032, 0x00000004, 0x0300005f, 0x00101032, 0x00000005, 0x04000067,
+        0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000001, 0x03000065, 0x00102032,
+        0x00000002, 0x03000065, 0x00102032, 0x00000003, 0x02000068, 0x00000004, 0x0800000f, 0x00100012,
+        0x00000000, 0x00208046, 0x00000000, 0x00000000, 0x00101046, 0x00000005, 0x0800000f, 0x00100022,
+        0x00000000, 0x00208046, 0x00000000, 0x00000001, 0x00101046, 0x00000005, 0x0700000f, 0x00100042,
+        0x00000000, 0x00100046, 0x00000000, 0x00100046, 0x00000000, 0x05000044, 0x00100042, 0x00000000,
+        0x0010002a, 0x00000000, 0x07000038, 0x00100032, 0x00000000, 0x00100aa6, 0x00000000, 0x00100046,
+        0x00000000, 0x0800000f, 0x00100012, 0x00000001, 0x00208046, 0x00000000, 0x00000000, 0x00101046,
+        0x00000004, 0x0800000f, 0x00100022, 0x00000001, 0x00208046, 0x00000000, 0x00000001, 0x00101046,
+        0x00000004, 0x0700000f, 0x00100042, 0x00000000, 0x00100046, 0x00000001, 0x00100046, 0x00000001,
+        0x05000044, 0x00100042, 0x00000000, 0x0010002a, 0x00000000, 0x07000038, 0x00100032, 0x00000001,
+        0x00100aa6, 0x00000000, 0x00100046, 0x00000001, 0x06000036, 0x001000c2, 0x00000001, 0x80100556,
+        0x00000041, 0x00000001, 0x0700000f, 0x00100042, 0x00000000, 0x00100a26, 0x00000001, 0x00100046,
+        0x00000000, 0x0700000f, 0x00100012, 0x00000000, 0x00100046, 0x00000001, 0x00100046, 0x00000000,
+        0x07000000, 0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x3f800000, 0x0800000e,
+        0x00100012, 0x00000000, 0x8010002a, 0x00000041, 0x00000000, 0x0010000a, 0x00000000, 0x09000032,
+        0x00100032, 0x00000000, 0x00100006, 0x00000000, 0x00100046, 0x00000001, 0x00100f36, 0x00000001,
+        0x0800000f, 0x00100012, 0x00000001, 0x00208046, 0x00000000, 0x00000000, 0x00101046, 0x00000000,
+        0x08000000, 0x00100012, 0x00000002, 0x0010000a, 0x00000001, 0x0020802a, 0x00000000, 0x00000000,
+        0x0800000f, 0x00100022, 0x00000001, 0x00208046, 0x00000000, 0x00000001, 0x00101046, 0x00000000,
+        0x08000000, 0x00100022, 0x00000002, 0x0010001a, 0x00000001, 0x0020802a, 0x00000000, 0x00000001,
+        0x0a000032, 0x001000c2, 0x00000000, 0x00208ff6, 0x00000000, 0x00000001, 0x00100406, 0x00000000,
+        0x00100406, 0x00000002, 0x0800000f, 0x00100042, 0x00000001, 0x00208046, 0x00000000, 0x00000002,
+        0x00100ae6, 0x00000000, 0x0800000f, 0x00100042, 0x00000000, 0x00208046, 0x00000000, 0x00000003,
+        0x00100ae6, 0x00000000, 0x08000000, 0x00100042, 0x00000000, 0x0010002a, 0x00000000, 0x0020802a,
+        0x00000000, 0x00000003, 0x08000038, 0x00100022, 0x00000002, 0x0010002a, 0x00000000, 0x0020803a,
+        0x00000000, 0x00000003, 0x08000000, 0x00100042, 0x00000000, 0x0010002a, 0x00000001, 0x0020802a,
+        0x00000000, 0x00000002, 0x08000038, 0x00100012, 0x00000002, 0x0010002a, 0x00000000, 0x0020803a,
+        0x00000000, 0x00000002, 0x0a000000, 0x00102032, 0x00000000, 0x00100046, 0x00000002, 0x00004002,
+        0xbf800000, 0x3f800000, 0x00000000, 0x00000000, 0x08000036, 0x001020c2, 0x00000000, 0x00004002,
+        0x00000000, 0x00000000, 0x00000000, 0x3f800000, 0x0800000f, 0x00100012, 0x00000002, 0x00208046,
+        0x00000000, 0x00000000, 0x00101046, 0x00000001, 0x0800000f, 0x00100022, 0x00000002, 0x00208046,
+        0x00000000, 0x00000001, 0x00101046, 0x00000001, 0x08000000, 0x001000c2, 0x00000000, 0x00100406,
+        0x00000001, 0x80100406, 0x00000041, 0x00000002, 0x0a000032, 0x00100032, 0x00000000, 0x00208ff6,
+        0x00000000, 0x00000001, 0x00100046, 0x00000000, 0x00100ae6, 0x00000000, 0x06000036, 0x00100042,
+        0x00000000, 0x8010001a, 0x00000041, 0x00000000, 0x0800000f, 0x00100012, 0x00000001, 0x00208046,
+        0x00000000, 0x00000000, 0x00101046, 0x00000002, 0x0800000f, 0x00100022, 0x00000001, 0x00208046,
+        0x00000000, 0x00000001, 0x00101046, 0x00000002, 0x08000000, 0x00100032, 0x00000001, 0x80100046,
+        0x00000041, 0x00000002, 0x00100046, 0x00000001, 0x06000036, 0x00100042, 0x00000001, 0x8010001a,
+        0x00000041, 0x00000001, 0x0800000f, 0x00100012, 0x00000003, 0x00208046, 0x00000000, 0x00000000,
+        0x00101046, 0x00000003, 0x0800000f, 0x00100022, 0x00000003, 0x00208046, 0x00000000, 0x00000001,
+        0x00101046, 0x00000003, 0x08000000, 0x00100032, 0x00000002, 0x80100046, 0x00000041, 0x00000002,
+        0x00100046, 0x00000003, 0x0700000f, 0x00100082, 0x00000000, 0x00100a26, 0x00000001, 0x00100046,
+        0x00000002, 0x0800000e, 0x001000c2, 0x00000000, 0x801002a6, 0x00000041, 0x00000000, 0x00100ff6,
+        0x00000000, 0x0d000032, 0x001000c2, 0x00000001, 0x80100406, 0x00000041, 0x00000002, 0x00004002,
+        0x00000000, 0x00000000, 0x3f000000, 0x3f000000, 0x00100406, 0x00000001, 0x0700000f, 0x00100012,
+        0x00000003, 0x00100ae6, 0x00000000, 0x00100ae6, 0x00000001, 0x0700000f, 0x00100022, 0x00000003,
+        0x00100ae6, 0x00000000, 0x00100046, 0x00000001, 0x06000036, 0x001000c2, 0x00000002, 0x80100556,
+        0x00000041, 0x00000002, 0x0700000f, 0x00100042, 0x00000000, 0x00100086, 0x00000002, 0x001000c6,
+        0x00000002, 0x0a000038, 0x001000c2, 0x00000001, 0x00100156, 0x00000002, 0x00004002, 0x00000000,
+        0x00000000, 0xbf800000, 0x3f800000, 0x05000044, 0x00100042, 0x00000000, 0x0010002a, 0x00000000,
+        0x07000038, 0x001000c2, 0x00000000, 0x00100aa6, 0x00000000, 0x00100ea6, 0x00000001, 0x0700000f,
+        0x00100022, 0x00000002, 0x00100046, 0x00000000, 0x00100ae6, 0x00000000, 0x08000036, 0x001000d2,
+        0x00000002, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0700000f, 0x00100012,
+        0x00000000, 0x00100046, 0x00000001, 0x00100ae6, 0x00000000, 0x07000031, 0x00100022, 0x00000000,
+        0x00004001, 0x00000000, 0x0010000a, 0x00000000, 0x07000031, 0x00100012, 0x00000001, 0x0010000a,
+        0x00000000, 0x00004001, 0x00000000, 0x08000031, 0x00100012, 0x00000000, 0x8010000a, 0x00000081,
+        0x00000000, 0x00004001, 0x3f800000, 0x0800001e, 0x00100022, 0x00000000, 0x8010001a, 0x00000041,
+        0x00000000, 0x0010000a, 0x00000001, 0x0500002b, 0x00100022, 0x00000000, 0x0010001a, 0x00000000,
+        0x07000038, 0x001000c2, 0x00000003, 0x00100ea6, 0x00000000, 0x00100556, 0x00000000, 0x09000037,
+        0x001020f2, 0x00000001, 0x00100006, 0x00000000, 0x00100e46, 0x00000002, 0x00100e46, 0x00000003,
+        0x06000036, 0x00100032, 0x00000000, 0x00208046, 0x00000000, 0x00000002, 0x06000036, 0x001000c2,
+        0x00000000, 0x00208406, 0x00000000, 0x00000003, 0x08000038, 0x001000f2, 0x00000000, 0x00100e46,
+        0x00000000, 0x00208ff6, 0x00000000, 0x00000001, 0x0a000038, 0x001000f2, 0x00000000, 0x00100e46,
+        0x00000000, 0x00004002, 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000, 0x05000036, 0x00102032,
+        0x00000002, 0x00100086, 0x00000000, 0x05000036, 0x00102032, 0x00000003, 0x001005d6, 0x00000000,
+        0x0100003e,
+    };
     static const DWORD vs_code_bezier[] =
     {
 #if 0
@@ -2361,6 +2558,59 @@ static HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_t
         0x00000000, 0x00000001, 0x0010003a, 0x00000001, 0x07000038, 0x001020f2, 0x00000000, 0x00100006,
         0x00000000, 0x00100e46, 0x00000001, 0x0100003e,
     };
+    /* Evaluate the implicit form of the curve (u² - v = 0) in texture space,
+     * using the screen-space partial derivatives to convert the calculated
+     * distance to object space.
+     *
+     * d(x, y) = |f(x, y)| / ‖∇f(x, y)‖
+     *         = |f(x, y)| / √((∂f/∂x)² + (∂f/∂y)²)
+     * f(x, y) = u(x, y)² - v(x, y)
+     * ∂f/∂x = 2u · ∂u/∂x - ∂v/∂x
+     * ∂f/∂y = 2u · ∂u/∂y - ∂v/∂y */
+    static const DWORD ps_code_bezier_solid_outline[] =
+    {
+#if 0
+        float4 color;
+
+        float4 main(float4 position : SV_POSITION, float4 uv : UV,
+                nointerpolation float2x2 stroke_transform : STROKE_TRANSFORM) : SV_Target
+        {
+            float2 du, dv, df;
+
+            du = float2(ddx(uv.x), ddy(uv.x));
+            dv = float2(ddx(uv.y), ddy(uv.y));
+            df = 2.0f * uv.x * du - dv;
+
+            clip(dot(df, uv.zw));
+            clip(length(mul(stroke_transform, df)) - abs(uv.x * uv.x - uv.y));
+            return color;
+        }
+#endif
+        0x43425844, 0x9da521d4, 0x4c86449e, 0x4f2c1641, 0x6f798508, 0x00000001, 0x000002f4, 0x00000003,
+        0x0000002c, 0x000000bc, 0x000000f0, 0x4e475349, 0x00000088, 0x00000004, 0x00000008, 0x00000068,
+        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000074, 0x00000000, 0x00000000,
+        0x00000003, 0x00000001, 0x00000f0f, 0x00000077, 0x00000000, 0x00000000, 0x00000003, 0x00000002,
+        0x00000303, 0x00000077, 0x00000001, 0x00000000, 0x00000003, 0x00000003, 0x00000303, 0x505f5653,
+        0x5449534f, 0x004e4f49, 0x53005655, 0x4b4f5254, 0x52545f45, 0x46534e41, 0x004d524f, 0x4e47534f,
+        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
+        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x000001fc, 0x00000040, 0x0000007f,
+        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03001062, 0x001010f2, 0x00000001, 0x03000862,
+        0x00101032, 0x00000002, 0x03000862, 0x00101032, 0x00000003, 0x03000065, 0x001020f2, 0x00000000,
+        0x02000068, 0x00000002, 0x0500000b, 0x00100032, 0x00000000, 0x00101046, 0x00000001, 0x0500000c,
+        0x001000c2, 0x00000000, 0x00101406, 0x00000001, 0x07000000, 0x00100012, 0x00000001, 0x0010100a,
+        0x00000001, 0x0010100a, 0x00000001, 0x0a000032, 0x00100032, 0x00000000, 0x00100006, 0x00000001,
+        0x00100086, 0x00000000, 0x801005d6, 0x00000041, 0x00000000, 0x0700000f, 0x00100042, 0x00000000,
+        0x00100046, 0x00000000, 0x00101ae6, 0x00000001, 0x07000031, 0x00100042, 0x00000000, 0x0010002a,
+        0x00000000, 0x00004001, 0x00000000, 0x0304000d, 0x0010002a, 0x00000000, 0x07000038, 0x00100062,
+        0x00000000, 0x00100556, 0x00000000, 0x00101106, 0x00000003, 0x09000032, 0x00100032, 0x00000000,
+        0x00101046, 0x00000002, 0x00100006, 0x00000000, 0x00100596, 0x00000000, 0x0700000f, 0x00100012,
+        0x00000000, 0x00100046, 0x00000000, 0x00100046, 0x00000000, 0x0500004b, 0x00100012, 0x00000000,
+        0x0010000a, 0x00000000, 0x0a000032, 0x00100022, 0x00000000, 0x0010100a, 0x00000001, 0x0010100a,
+        0x00000001, 0x8010101a, 0x00000041, 0x00000001, 0x08000000, 0x00100012, 0x00000000, 0x8010001a,
+        0x000000c1, 0x00000000, 0x0010000a, 0x00000000, 0x07000031, 0x00100012, 0x00000000, 0x0010000a,
+        0x00000000, 0x00004001, 0x00000000, 0x0304000d, 0x0010000a, 0x00000000, 0x06000036, 0x001020f2,
+        0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x0100003e,
+    };
     /* The basic idea here is to evaluate the implicit form of the curve in
      * texture space. "t.z" determines which side of the curve is shaded. */
     static const DWORD ps_code_bezier_solid[] =
@@ -2400,7 +2650,7 @@ static HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_t
         {ps_code_triangle_solid, sizeof(ps_code_triangle_solid),
                 D2D_SHAPE_TYPE_TRIANGLE, D2D_BRUSH_TYPE_SOLID, D2D_BRUSH_TYPE_COUNT},
         {ps_code_triangle_solid_bitmap, sizeof(ps_code_triangle_solid_bitmap),
-            D2D_SHAPE_TYPE_TRIANGLE, D2D_BRUSH_TYPE_SOLID, D2D_BRUSH_TYPE_BITMAP},
+                D2D_SHAPE_TYPE_TRIANGLE, D2D_BRUSH_TYPE_SOLID, D2D_BRUSH_TYPE_BITMAP},
         {ps_code_triangle_bitmap, sizeof(ps_code_triangle_bitmap),
                 D2D_SHAPE_TYPE_TRIANGLE, D2D_BRUSH_TYPE_BITMAP, D2D_BRUSH_TYPE_COUNT},
         {ps_code_triangle_bitmap_solid, sizeof(ps_code_triangle_bitmap_solid),
@@ -2409,6 +2659,8 @@ static HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_t
                 D2D_SHAPE_TYPE_TRIANGLE, D2D_BRUSH_TYPE_BITMAP, D2D_BRUSH_TYPE_BITMAP},
         {ps_code_bezier_solid, sizeof(ps_code_bezier_solid),
                 D2D_SHAPE_TYPE_BEZIER, D2D_BRUSH_TYPE_SOLID, D2D_BRUSH_TYPE_COUNT},
+        {ps_code_bezier_solid_outline, sizeof(ps_code_bezier_solid_outline),
+                D2D_SHAPE_TYPE_BEZIER_OUTLINE, D2D_BRUSH_TYPE_SOLID, D2D_BRUSH_TYPE_COUNT},
     };
     static const struct
     {
@@ -2501,6 +2753,15 @@ static HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_t
         goto err;
     }
 
+    if (FAILED(hr = ID3D10Device_CreateInputLayout(render_target->device, il_desc_bezier_outline,
+            sizeof(il_desc_bezier_outline) / sizeof(*il_desc_bezier_outline),
+            vs_code_bezier_outline, sizeof(vs_code_bezier_outline),
+            &render_target->shape_resources[D2D_SHAPE_TYPE_BEZIER_OUTLINE].il)))
+    {
+        WARN("Failed to create bezier outline input layout, hr %#x.\n", hr);
+        goto err;
+    }
+
     if (FAILED(hr = ID3D10Device_CreateInputLayout(render_target->device, il_desc_bezier,
             sizeof(il_desc_bezier) / sizeof(*il_desc_bezier), vs_code_bezier, sizeof(vs_code_bezier),
             &render_target->shape_resources[D2D_SHAPE_TYPE_BEZIER].il)))
@@ -2516,6 +2777,13 @@ static HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_t
         goto err;
     }
 
+    if (FAILED(hr = ID3D10Device_CreateVertexShader(render_target->device, vs_code_bezier_outline,
+            sizeof(vs_code_bezier_outline), &render_target->shape_resources[D2D_SHAPE_TYPE_BEZIER_OUTLINE].vs)))
+    {
+        WARN("Failed to create bezier outline vertex shader, hr %#x.\n", hr);
+        goto err;
+    }
+
     if (FAILED(hr = ID3D10Device_CreateVertexShader(render_target->device, vs_code_triangle,
             sizeof(vs_code_triangle), &render_target->shape_resources[D2D_SHAPE_TYPE_TRIANGLE].vs)))
     {
diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c
index 941c938..2d64615 100644
--- a/dlls/d2d1/tests/d2d1.c
+++ b/dlls/d2d1/tests/d2d1.c
@@ -3671,7 +3671,7 @@ static void test_gradient(void)
 
 static void test_draw_geometry(void)
 {
-    ID2D1TransformedGeometry *transformed_geometry[3];
+    ID2D1TransformedGeometry *transformed_geometry[4];
     ID2D1RectangleGeometry *rect_geometry[2];
     D2D1_POINT_2F point = {0.0f, 0.0f};
     ID2D1SolidColorBrush *brush;
@@ -4061,6 +4061,343 @@ static void test_draw_geometry(void)
             "tAIKtgIJuAIHugIEvQIC82IA");
     ok(match, "Figure does not match.\n");
 
+    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, 20.0f, 80.0f);
+    ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_HOLLOW);
+    quadratic_to(sink, 40.0f,  80.0f, 60.0f,  80.0f);
+    quadratic_to(sink, 60.0f, 160.0f, 60.0f, 240.0f);
+    quadratic_to(sink, 40.0f, 240.0f, 20.0f, 240.0f);
+    quadratic_to(sink, 20.0f, 160.0f, 20.0f,  80.0f);
+    ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED);
+
+    set_point(&point, 100.0f, 80.0f);
+    ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_HOLLOW);
+    quadratic_to(sink, 105.0f,  80.0f, 140.0f,  80.0f);
+    quadratic_to(sink, 140.0f, 100.0f, 140.0f, 240.0f);
+    quadratic_to(sink, 135.0f, 240.0f, 100.0f, 240.0f);
+    quadratic_to(sink, 100.0f, 220.0f, 100.0f,  80.0f);
+    ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED);
+
+    set_point(&point, 180.0f, 80.0f);
+    ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_HOLLOW);
+    quadratic_to(sink, 215.0f,  80.0f, 220.0f,  80.0f);
+    quadratic_to(sink, 220.0f, 220.0f, 220.0f, 240.0f);
+    quadratic_to(sink, 185.0f, 240.0f, 180.0f, 240.0f);
+    quadratic_to(sink, 180.0f, 100.0f, 180.0f,  80.0f);
+    ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED);
+
+    set_point(&point, 260.0f, 80.0f);
+    ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_HOLLOW);
+    quadratic_to(sink, 280.0f,  80.0f, 300.0f,  80.0f);
+    quadratic_to(sink, 300.0f, 160.0f, 300.0f, 240.0f);
+    quadratic_to(sink, 280.0f, 240.0f, 260.0f, 240.0f);
+    quadratic_to(sink, 260.0f, 160.0f, 260.0f,  80.0f);
+    ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_OPEN);
+
+    set_point(&point, 20.0f, 400.0f);
+    ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_HOLLOW);
+    quadratic_to(sink, 40.0f, 420.0f, 60.0f, 400.0f);
+    quadratic_to(sink, 55.0f, 480.0f, 60.0f, 560.0f);
+    quadratic_to(sink, 40.0f, 540.0f, 20.0f, 560.0f);
+    quadratic_to(sink, 25.0f, 480.0f, 20.0f, 400.0f);
+    ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED);
+
+    set_point(&point, 100.0f, 400.0f);
+    ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_HOLLOW);
+    quadratic_to(sink, 105.0f, 420.0f, 140.0f, 400.0f);
+    quadratic_to(sink, 135.0f, 420.0f, 140.0f, 560.0f);
+    quadratic_to(sink, 135.0f, 540.0f, 100.0f, 560.0f);
+    quadratic_to(sink, 105.0f, 540.0f, 100.0f, 400.0f);
+    ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED);
+
+    set_point(&point, 180.0f, 400.0f);
+    ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_HOLLOW);
+    quadratic_to(sink, 215.0f, 420.0f, 220.0f, 400.0f);
+    quadratic_to(sink, 215.0f, 540.0f, 220.0f, 560.0f);
+    quadratic_to(sink, 185.0f, 540.0f, 180.0f, 560.0f);
+    quadratic_to(sink, 185.0f, 420.0f, 180.0f, 400.0f);
+    ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED);
+
+    set_point(&point, 260.0f, 400.0f);
+    ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_HOLLOW);
+    quadratic_to(sink, 280.0f, 420.0f, 300.0f, 400.0f);
+    quadratic_to(sink, 295.0f, 480.0f, 300.0f, 560.0f);
+    quadratic_to(sink, 280.0f, 540.0f, 260.0f, 560.0f);
+    quadratic_to(sink, 265.0f, 480.0f, 260.0f, 400.0f);
+    ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_OPEN);
+
+    set_point(&point, 20.0f, 720.0f);
+    ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_HOLLOW);
+    quadratic_to(sink, 40.0f, 700.0f, 60.0f, 720.0f);
+    quadratic_to(sink, 65.0f, 800.0f, 60.0f, 880.0f);
+    quadratic_to(sink, 40.0f, 900.0f, 20.0f, 880.0f);
+    quadratic_to(sink, 15.0f, 800.0f, 20.0f, 720.0f);
+    ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED);
+
+    set_point(&point, 100.0f, 720.0f);
+    ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_HOLLOW);
+    quadratic_to(sink, 105.0f, 700.0f, 140.0f, 720.0f);
+    quadratic_to(sink, 145.0f, 740.0f, 140.0f, 880.0f);
+    quadratic_to(sink, 135.0f, 900.0f, 100.0f, 880.0f);
+    quadratic_to(sink,  95.0f, 860.0f, 100.0f, 720.0f);
+    ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED);
+
+    set_point(&point, 180.0f, 720.0f);
+    ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_HOLLOW);
+    quadratic_to(sink, 215.0f, 700.0f, 220.0f, 720.0f);
+    quadratic_to(sink, 225.0f, 860.0f, 220.0f, 880.0f);
+    quadratic_to(sink, 185.0f, 900.0f, 180.0f, 880.0f);
+    quadratic_to(sink, 175.0f, 740.0f, 180.0f, 720.0f);
+    ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED);
+
+    set_point(&point, 260.0f, 720.0f);
+    ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_HOLLOW);
+    quadratic_to(sink, 280.0f, 700.0f, 300.0f, 720.0f);
+    quadratic_to(sink, 305.0f, 800.0f, 300.0f, 880.0f);
+    quadratic_to(sink, 280.0f, 900.0f, 260.0f, 880.0f);
+    quadratic_to(sink, 255.0f, 800.0f, 260.0f, 720.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);
+
+    ID2D1RenderTarget_BeginDraw(rt);
+    ID2D1RenderTarget_Clear(rt, &color);
+    ID2D1RenderTarget_DrawGeometry(rt, (ID2D1Geometry *)geometry, (ID2D1Brush *)brush, 10.0f, NULL);
+    hr = ID2D1RenderTarget_EndDraw(rt, NULL, NULL);
+    ok(SUCCEEDED(hr), "Failed to end draw, hr %#x.\n", hr);
+    ID2D1PathGeometry_Release(geometry);
+
+    match = compare_figure(surface,   0,   0, 160, 160, 0xff652e89, 0,
+            "vi5kPGQ8ZDxkPGQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwU"
+            "PBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8"
+            "FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwU"
+            "PBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8"
+            "FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwU"
+            "PBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8ZDxkPGQ8ZDxk3i8A");
+    ok(match, "Figure does not match.\n");
+    match = compare_figure(surface, 160,   0, 160, 160, 0xff652e89, 0,
+            "vi5kPGQ8ZDxkPGQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwU"
+            "PBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8"
+            "FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwU"
+            "PBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8"
+            "FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwU"
+            "PBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8ZDxkPGQ8ZDxk3i8A");
+    ok(match, "Figure does not match.\n");
+    match = compare_figure(surface, 320,   0, 160, 160, 0xff652e89, 0,
+            "vi5kPGQ8ZDxkPGQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwU"
+            "PBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8"
+            "FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwU"
+            "PBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8"
+            "FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwU"
+            "PBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8ZDxkPGQ8ZDxk3i8A");
+    ok(match, "Figure does not match.\n");
+    match = compare_figure(surface, 480,   0, 160, 160, 0xff652e89, 0,
+            "yC5aRlpGWjxkPGQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwU"
+            "PBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8"
+            "FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwU"
+            "PBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8"
+            "FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwU"
+            "PBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8FDwUPBQ8ZDxkPGQ8ZDxk3i8A");
+    ok(match, "Figure does not match.\n");
+
+    match = compare_figure(surface,   0, 160, 160, 160, 0xff652e89, 64,
+            "3SoDYAM6B1gHOgtQCzoPSA87EkASPBc2FzwcLBw8IiAiPWI+Yj5iPhQBOAEUPhQKJgoUPxQ4FEAU"
+            "OBRAFDgUQBQ4FEAUOBRBFDYUQhQ2FEIUNhRCFDYUQhQ2FEIUNhRDFDQURBQ0FEQUNBREFDQURBQ0"
+            "FEQUNBREFDQURBQ0FEQUNBREFDQURRQyFEYUMhRGFDIURhQyFEYUMhRGFDIURhQyFEYUMhRGFDIU"
+            "RhQyFEYUMhRGFDIURhQyFEYUMhRGFDIURhQyFEYUMhRGFDIURhQyFEYUMhRGFDIURhQyFEYUMhRG"
+            "FDIURRQ0FEQUNBREFDQURBQ0FEQUNBREFDQURBQ0FEQUNBREFDQURBQ0FEMUNhRCFDYUQhQ2FEIU"
+            "NhRCFDYUQhQ2FEEUOBRAFDgUQBQ4FEAUOBRAFDgUPxQKJgoUPhQBOAEUPmI+Yj5iPSIgIjwcLBw8"
+            "FzYXPBJAEjsPSA86C1ALOgdYBzoDYAPdKgAA");
+    ok(match, "Figure does not match.\n");
+    match = compare_figure(surface, 160, 160, 160, 160, 0xff652e89, 1024,
+            "uxUBnwECngEDnQEEnAEFmwEGmwEGmgEHmQEImAEJlwEKlgELlQEMlQEMlAENkwEOkgEPkQEQkAER"
+            "VQQ2Ek0KOBJFEDkTPRY6FDUcOxUrJDwYHi09Yj5iP2BAQwkUQDgUFEAUOBRAFDcUQRQ3FEEUNxRC"
+            "FDYUQhQ2FEIUNhRCFDUUQxQ1FEMUNRRDFDUUQxQ1FEQUNBREFDQURBQ0FEQUNBREFDQURBQ0FEQU"
+            "NBREFDQURBQ0FEQUNBRFFDMURRQzFEUUMxRFFDMURRQzFEUUMxRFFDMURRQzFEUUMxRFFDMURRQ0"
+            "FEQUNBRFFDMURRQzFEUUMxRFFDMURRQzFEUUMxRFFDMURRQzFEUUMxRFFDMURRQ0FEQUNBREFDQU"
+            "RBQ0FEQUNBREFDQURBQ0FEQUNBREFDQURBQ0FEQUNRRDFDUUQxQ1FEMUNRRDFDUUQhQ2FEIUNhRC"
+            "FDYUQhQ3FEEUNxRBFDcUQBQ4FEAUFDhAFAlDQGA/Yj5iPS0eGDwkKxU7HDUUOhY9EzkQRRI4Ck0S"
+            "NgRVEZABEJEBD5IBDpMBDZQBDJUBDJUBC5YBCpcBCZgBCJkBB5oBBpsBBpsBBZwBBJ0BA54BAp8B"
+            "AbsV");
+    ok(match, "Figure does not match.\n");
+    match = compare_figure(surface, 320, 160, 160, 160, 0xff652e89, 1024,
+            "pBYBngECnQEDnAEEmwEFmgEGmQEGmQEHmAEIlwEJlgEKlQELlAEMkwEMkwENkgEOkQEPkAEQNgRV"
+            "ETcKTRI4EEUSOhY9EzscNRQ8JCsVPS0eGD5iPmI/YEAUCUNAFBQ4QBQ4FEEUNxRBFDcUQRQ3FEEU"
+            "NhRCFDYUQhQ2FEMUNRRDFDUUQxQ1FEMUNRRDFDUUQxQ0FEQUNBREFDQURBQ0FEQUNBREFDQURBQ0"
+            "FEQUNBREFDQURBQ0FEQUMxRFFDMURRQzFEUUMxRFFDMURRQzFEUUMxRFFDMURRQzFEUUMxREFDQU"
+            "RBQ0FEQUMxRFFDMURRQzFEUUMxRFFDMURRQzFEUUMxRFFDMURRQzFEUUMxREFDQURBQ0FEQUNBRE"
+            "FDQURBQ0FEQUNBREFDQURBQ0FEQUNBREFDQUQxQ1FEMUNRRDFDUUQxQ1FEMUNRRDFDYUQhQ2FEIU"
+            "NhRBFDcUQRQ3FEEUNxRBFDgUQDgUFEBDCRRAYD9iPmI+GB4tPRUrJDwUNRw7Ez0WOhJFEDgSTQo3"
+            "EVUENhCQAQ+RAQ6SAQ2TAQyTAQyUAQuVAQqWAQmXAQiYAQeZAQaZAQaaAQWbAQScAQOdAQKeAQGk"
+            "FgAA");
+    ok(match, "Figure does not match.\n");
+    match = compare_figure(surface, 480, 160, 160, 160, 0xff652e89, 64,
+            "wCsDmQEHlQELkQEPSwJAEkgLNhc8HCwcPCIgIj1iPmI+Yj4UATgBFD4UCiYKFD8UOBRAFDgUQBQ4"
+            "FEAUOBRAFDgUQRQ2FEIUNhRCFDYUQhQ2FEIUNhRCFDYUQxQ0FEQUNBREFDQURBQ0FEQUNBREFDQU"
+            "RBQ0FEQUNBREFDQURBQ0FEUUMhRGFDIURhQyFEYUMhRGFDIURhQyFEYUMhRGFDIURhQyFEYUMhRG"
+            "FDIURhQyFEYUMhRGFDIURhQyFEYUMhRGFDIURhQyFEYUMhRGFDIURhQyFEYUMhRGFDIURhQyFEUU"
+            "NBREFDQURBQ0FEQUNBREFDQURBQ0FEQUNBREFDQURBQ0FEQUNBRDFDYUQhQ2FEIUNhRCFDYUQhQ2"
+            "FEIUNhRBFDgUQBQ4FEAUOBRAFDgUQBQ4FD8UCiYKFD4UATgBFD5iPmI+Yj0iICI8HCwcPBc2FzwS"
+            "QBI7D0gPOgtQCzoHWAc6A2AD3SoA");
+    ok(match, "Figure does not match.\n");
+
+    match = compare_figure(surface,   0, 320, 160, 160, 0xff652e89, 64,
+            "3SkmcThiRFdOTVhEICAgPhwsHDwXNhc8FDwUOxQ+FDoUPhQ6FD4UOhQ+FDoUPhQ5FEAUOBRAFDgU"
+            "QBQ4FEAUOBRAFDcUQhQ2FEIUNhRCFDYUQhQ2FEIUNhRCFDUURBQ0FEQUNBREFDQURBQ0FEQUNBRE"
+            "FDQURBQ0FEQUNBREFDQURBQzFEYUMhRGFDIURhQyFEYUMhRGFDIURhQyFEYUMhRGFDIURhQyFEYU"
+            "MhRGFDIURhQyFEYUMhRGFDIURhQyFEYUMhRGFDIURhQyFEYUMhRGFDIURhQyFEYUMhRGFDIURhQz"
+            "FEQUNBREFDQURBQ0FEQUNBREFDQURBQ0FEQUNBREFDQURBQ0FEQUNRRCFDYUQhQ2FEIUNhRCFDYU"
+            "QhQ2FEIUNxRAFDgUQBQ4FEAUOBRAFDgUQBQ5FD4UOhQ+FDoUPhQ6FD4UOhQ+FDsUPBQ8FzYXPBws"
+            "HD4gICBEWE1OV0RiOHEm3SkA");
+    ok(match, "Figure does not match.\n");
+    match = compare_figure(surface, 160, 320, 160, 160, 0xff652e89, 1024,
+            "zykoczhkRVhQTlpEFx4tPRUrJDwUNRw7FDwVOxQ+FDoUPhQ5FEAUOBRAFDgUQBQ4FEAUOBRBFDcU"
+            "QRQ3FEEUNhRCFDYUQhQ2FEIUNhRDFDUUQxQ1FEMUNRRDFDUUQxQ0FEQUNBREFDQURBQ0FEQUNBRE"
+            "FDQURBQ0FEQUNBREFDQURBQ0FEQUMxRFFDMURRQzFEUUMxRFFDMURRQzFEUUMxRFFDMURRQzFEUU"
+            "MxRFFDMURBQ0FEQUMxRFFDMURRQzFEUUMxRFFDMURRQzFEUUMxRFFDMURRQzFEUUMxRFFDMURBQ0"
+            "FEQUNBREFDQURBQ0FEQUNBREFDQURBQ0FEQUNBREFDQURBQ0FEMUNRRDFDUUQxQ1FEMUNRRDFDYU"
+            "QhQ2FEIUNhRCFDYUQRQ3FEEUNxRBFDgUQBQ4FEAUOBRAFDgUQBQ5FD4UOhQ+FDsVPBQ7HDUUPCQr"
+            "FT0tHhdEWk5QWEVkOHMozykA");
+    ok(match, "Figure does not match.\n");
+    match = compare_figure(surface, 320, 320, 160, 160, 0xff652e89, 1024,
+            "6SkobThfRVNQSFpALR4XPSQrFTscNRQ7FTwUOhQ+FDoUPhQ5FEAUOBRAFDgUQBQ4FEAUNxRBFDcU"
+            "QRQ3FEEUNxRCFDYUQhQ2FEIUNRRDFDUUQxQ1FEMUNRRDFDUUQxQ1FEQUNBREFDQURBQ0FEQUNBRE"
+            "FDQURBQ0FEQUNBREFDQURBQ0FEQUNBRFFDMURRQzFEUUMxRFFDMURRQzFEUUMxRFFDMURRQzFEUU"
+            "MxRFFDQURBQ0FEQUNBRFFDMURRQzFEUUMxRFFDMURRQzFEUUMxRFFDMURRQzFEUUMxRFFDQURBQ0"
+            "FEQUNBREFDQURBQ0FEQUNBREFDQURBQ0FEQUNBREFDQURBQ1FEMUNRRDFDUUQxQ1FEMUNRRDFDUU"
+            "QhQ2FEIUNhRCFDcUQRQ3FEEUNxRBFDcUQBQ4FEAUOBRAFDgUQBQ5FD4UOhQ+FDoUPBU7FDUcOxUr"
+            "JD0XHi1AWkhQU0VfOG0o6SkA");
+    ok(match, "Figure does not match.\n");
+    match = compare_figure(surface, 480, 320, 160, 160, 0xff652e89, 64,
+            "3SkmcThiRFdOTVhGHiAgRhQsHDwXNhc8FDwUOxQ+FDoUPhQ6FD4UOhQ+FDoUPhQ5FEAUOBRAFDgU"
+            "QBQ4FEAUOBRAFDcUQhQ2FEIUNhRCFDYUQhQ2FEIUNhRCFDUURBQ0FEQUNBREFDQURBQ0FEQUNBRE"
+            "FDQURBQ0FEQUNBREFDQURBQzFEYUMhRGFDIURhQyFEYUMhRGFDIURhQyFEYUMhRGFDIURhQyFEYU"
+            "MhRGFDIURhQyFEYUMhRGFDIURhQyFEYUMhRGFDIURhQyFEYUMhRGFDIURhQyFEYUMhRGFDIURhQz"
+            "FEQUNBREFDQURBQ0FEQUNBREFDQURBQ0FEQUNBREFDQURBQ0FEQUNRRCFDYUQhQ2FEIUNhRCFDYU"
+            "QhQ2FEIUNxRAFDgUQBQ4FEAUOBRAFDgUQBQ5FD4UOhQ+FDoUPhQ6FD4UOhQ+FDsUPBQ8FzYXPBws"
+            "HD4gICBEWE1OV0RiOHEm3SkA");
+    ok(match, "Figure does not match.\n");
+
+    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, -0.402914f, 0.915514f);
+    ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_HOLLOW);
+    quadratic_to(sink, -0.310379f,  0.882571f, -0.116057f,  0.824000f);
+    quadratic_to(sink,  0.008350f,  0.693614f, -0.052343f,  0.448886f);
+    quadratic_to(sink, -0.154236f,  0.246072f, -0.279229f,  0.025343f);
+    quadratic_to(sink, -0.370064f, -0.588586f, -0.383029f, -0.924114f);
+    quadratic_to(sink, -0.295479f, -0.958764f, -0.017086f, -0.988400f);
+    quadratic_to(sink,  0.208836f, -0.954157f,  0.272200f, -0.924114f);
+    quadratic_to(sink,  0.295614f, -0.569071f,  0.230143f,  0.022886f);
+    quadratic_to(sink,  0.101664f,  0.220643f,  0.012057f,  0.451571f);
+    quadratic_to(sink, -0.028764f,  0.709014f,  0.104029f,  0.833943f);
+    quadratic_to(sink,  0.319414f,  0.913057f,  0.403229f,  0.942628f);
+    quadratic_to(sink,  0.317721f,  1.023450f, -0.017086f,  1.021771f);
+    quadratic_to(sink, -0.310843f,  1.007472f, -0.402914f,  0.915514f);
+    ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED);
+
+    hr = ID2D1GeometrySink_Close(sink);
+    ok(SUCCEEDED(hr), "Failed to close geometry sink, hr %#x.\n", hr);
+    ID2D1GeometrySink_Release(sink);
+
+    set_matrix_identity(&matrix);
+    translate_matrix(&matrix, 40.0f, 160.0f);
+    scale_matrix(&matrix, 20.0f, 80.0f);
+    hr = ID2D1Factory_CreateTransformedGeometry(factory,
+            (ID2D1Geometry *)geometry, &matrix, &transformed_geometry[0]);
+    ok(SUCCEEDED(hr), "Failed to create geometry, hr %#x.\n", hr);
+
+    set_matrix_identity(&matrix);
+    translate_matrix(&matrix, 160.0f, 640.0f);
+    scale_matrix(&matrix, 40.0f, 160.0f);
+    rotate_matrix(&matrix, M_PI / -5.0f);
+    hr = ID2D1Factory_CreateTransformedGeometry(factory,
+            (ID2D1Geometry *)geometry, &matrix, &transformed_geometry[1]);
+    ok(SUCCEEDED(hr), "Failed to create geometry, hr %#x.\n", hr);
+    ID2D1PathGeometry_Release(geometry);
+
+    set_matrix_identity(&matrix);
+    scale_matrix(&matrix, 0.5f, 1.0f);
+    translate_matrix(&matrix, -80.0f, 0.0f);
+    hr = ID2D1Factory_CreateTransformedGeometry(factory,
+            (ID2D1Geometry *)transformed_geometry[1], &matrix, &transformed_geometry[2]);
+    ok(SUCCEEDED(hr), "Failed to create geometry, hr %#x.\n", hr);
+
+    set_matrix_identity(&matrix);
+    rotate_matrix(&matrix, M_PI / 2.0f);
+    translate_matrix(&matrix, 80.0f, -320.0f);
+    scale_matrix(&matrix, 2.0f, 0.25f);
+    hr = ID2D1Factory_CreateTransformedGeometry(factory,
+            (ID2D1Geometry *)transformed_geometry[2], &matrix, &transformed_geometry[3]);
+    ok(SUCCEEDED(hr), "Failed to create geometry, hr %#x.\n", hr);
+
+    ID2D1RenderTarget_BeginDraw(rt);
+    ID2D1RenderTarget_Clear(rt, &color);
+    ID2D1RenderTarget_DrawGeometry(rt, (ID2D1Geometry *)transformed_geometry[0], (ID2D1Brush *)brush, 2.0f, NULL);
+    ID2D1RenderTarget_DrawGeometry(rt, (ID2D1Geometry *)transformed_geometry[1], (ID2D1Brush *)brush, 10.0f, NULL);
+    ID2D1RenderTarget_DrawGeometry(rt, (ID2D1Geometry *)transformed_geometry[2], (ID2D1Brush *)brush, 5.0f, NULL);
+    ID2D1RenderTarget_DrawGeometry(rt, (ID2D1Geometry *)transformed_geometry[3], (ID2D1Brush *)brush, 15.0f, NULL);
+    hr = ID2D1RenderTarget_EndDraw(rt, NULL, NULL);
+    ok(SUCCEEDED(hr), "Failed to end draw, hr %#x.\n", hr);
+    ID2D1TransformedGeometry_Release(transformed_geometry[3]);
+    ID2D1TransformedGeometry_Release(transformed_geometry[2]);
+    ID2D1TransformedGeometry_Release(transformed_geometry[1]);
+    ID2D1TransformedGeometry_Release(transformed_geometry[0]);
+
+    match = compare_figure(surface,   0,   0, 160, 160, 0xff652e89, 128,
+            "yjIJkQEHBwaIAQUSBYMBBBYEggEEFgSCAQQWBIIBBBYEggEEFgSCAQQWBIIBBBYEggEEFgSCAQQW"
+            "BIIBBBYEggEEFgSDAQQVBIMBBBUEgwEEFQSDAQQVBIMBBBUEgwEEFQSDAQQVBIMBBBUEgwEEFQSD"
+            "AQQVBIQBBBQEhAEEFASEAQQTBIUBBBMEhQEEEwSFAQQTBIUBBBMEhQEEEwSGAQQSBIYBBBIEhgEE"
+            "EgSGAQQSBIYBBBIEhgEEEgSGAQQRBIgBBBAEiAEEEASIAQQQBIkBBA4EigEEDgSLAQQMBIwBBAwE"
+            "jQEECgSOAQQJBJABBAgEkAEFBgSSAQQGBJMBBAQElAEEBASVAQQDBJUBBAIElwEEAQSXAQiZAQeZ"
+            "AQaaAQaaAQaaAQabAQWbAQWbAQWbAQWaAQeZAQeZAQeZAQiXAQQBBJYBBAMElQEEAwWRAQUGBY0B"
+            "BQwFhwEFEgSCAQUXBYABBBoFfgUYBIIBBhEFiAEUpTEA");
+    ok(match, "Figure does not match.\n");
+    match = compare_figure(surface, 160,   0, 320, 160, 0xff652e89, 512,
+            "yJIBArkCDa4CGKMCIZoCK5ECM4gCO4ECQ/gBS/EBUesBLAYl5QEsDiPeASwWIdkBLBwh0wEsISHO"
+            "ASsgKMsBKR4vyAEnHDPIASUaNMsBIxg1mQEFMCIUN54BCygiDzijAREhIgY9qAEYGWGuAR4RXbMB"
+            "JAhbuQGAAcABesYBc84Ba9YBTvQBP4MCOIoCNI4CM5ACMZICL5QCLZYCK5kCKJsCJ54CI6MCHq8C"
+            "EraSAQAA");
+    ok(match, "Figure does not match.\n");
+    match = compare_figure(surface,   0, 160, 160, 320, 0xff652e89, 256,
+            "xWkCmwEFmAEJlQELlAENkgEOkQEPjwESjQETjAEVigELAQqJAQsCCogBCwQKhwEKBQqGAQoGCoYB"
+            "CgcKhAEKCAqEAQoIC4IBCgoKggEKCgqBAQoMCoABCgwKfwoNCn8KDgp9Cg8KfQoPCnwKEQp7ChEK"
+            "egoSCnoKEwp4ChQKeAoUCncLFQp2ChYKdgoWCnYKFwp2ChYKdgoWCncKFgp2ChYKdgoWCncKFQt2"
+            "ChYKdwoVCncKFQp4ChUKdwoVCncKFQp4ChUKdwoVCngKFAp4ChUKeAoUCngKFAp4CxMKeQoUCngK"
+            "FAp5ChMKeQoUCnkKEwp5ChMKegoSC3kKEwp6ChIKegoSCnoLEgp6ChIKegoSCnsKEQp7ChEKfAoQ"
+            "CnwKEAp9Cg8KfQoPCn4KDgp+Cg4KfwoOCn4KDgp/Cg0KfwoNCoABCgwKgAEKDAqBAQoLCoEBCgsK"
+            "gQELCgqCAQoKCoIBCwkKgwEKCQqDAQoJCoQBCggKhAEKCQqEAQsHCoUBCwYKhgELBQqHAQsECogB"
+            "CwMKiQELAgqLAQoBCowBFI0BE44BE44BEo8BEZABEJEBD5IBDpMBDpMBDZMBDZQBDJQBDZQBDJQB"
+            "DBUCfgwSBH4MEQV/DA4GgAEMDAiAAQ0KCYEBDAgLgQENBQ2BAQ0EDoIBDQEPgwEdgwEdgwEdgwEc"
+            "hAEKAgQCCoUBCgYKhgEKBgqGAQoFC4YBCgUKhwEKBAqIAQoECogBCgMKiQEKAwqIAQoDCokBCgMK"
+            "iQEKAgqJAQoCCooBCgIKiQEKAgqKAQoBCosBCgEKigEKAQqLARSMARSLARSMAROMARONARKOARGO"
+            "ARGPARCQAQ6RAQ2YAQTEZAAA");
+    ok(match, "Figure does not match.\n");
+    match = compare_figure(surface, 160, 160, 320, 320, 0xff652e89, 1024,
+            "ytABA7gCCbICD60CFKkCF6cCGqMCHqACIZ0CJJoCJpgCKZUCFgIUkgIWBBWPAhYHFI4CFQoUjAIV"
+            "DBSKAhUNFYgCFQ8UhwIVERSFAhUTFIMCFRQVgQIUFxSAAhQZFP4BFBoV/AEUHBT7ARQeFPkBFB8V"
+            "9wEUIRT2ARQjFPQBFSMV8gEVJRTxARUnFPABFCgV7gEUKhTtARQsFOwBFCwV7AEULBTsARUsFOwB"
+            "FSsV7AEULBTtARQsFO0BFCsU7QEVKxTtARUqFe0BFSoU7gEUKxTuARQqFe4BFCoU7wEUKhTuARUp"
+            "FO8BFSkU7wEVKBXvARUoFPABFCkU8AEUKBTxARQoFPEBFCcV8QEUJxTxARUnFPEBFSYU8gEVJhTy"
+            "ARUlFfIBFSUU8wEUJRXzARQlFPQBFCUU9AEUJBT1ARQkFPUBFCMU9gEUIhT2ARUhFPcBFSAU+AEV"
+            "HxT5ARUeFPoBFR4U+gEVHRT7ARUcFPwBFRsU/QEVGhT+ARUZFP8BFBkUgAIUGBSBAhQXFIICFBcU"
+            "ggIUFhSDAhQVFIQCFBQUhQIUExSGAhQSFIcCFBIUhwIUERSIAhUPFIkCFg0UigIXCxSNAhYJFI8C"
+            "FggUkAIXBRSSAhcDFJQCFwEUlgIrlwIpmgImnAIkngIjnwIhoQIfowIepAIcpgIbpgIaqAIZqAIZ"
+            "qAIYKwP7ARgnBf0BGCMI/QEZHgz+ARgbD/8BGBcSgAIYEhaAAhoNGIICGggcgwIaBB+DAjyEAjyF"
+            "AjqGAjmIAjiIAiECFIkCFAIIBBSKAhQNFIsCFAwUjAIUCxSNAhQKFI4CFAkUjwIUBxWQAhQGFZEC"
+            "FAUVkQIUBRWRAhQFFZECFQMVkwIUAxWTAhQDFZMCFAIVlAIVARWVAiqVAimWAimWAiiYAiaZAiaZ"
+            "AiWaAiScAiKdAiGeAh+hAhyjAhmuAg3GxgEA");
+    ok(match, "Figure does not match.\n");
+
     ID2D1SolidColorBrush_Release(brush);
     ID2D1RenderTarget_Release(rt);
     refcount = ID2D1Factory_Release(factory);
-- 
2.1.4




More information about the wine-patches mailing list