[PATCH 3/5] d2d1: Implement initial support for drawing bezier curves.

Henri Verbeet hverbeet at codeweavers.com
Mon Jul 20 04:07:28 CDT 2015


---
 dlls/d2d1/brush.c         |  17 +--
 dlls/d2d1/d2d1_private.h  |  39 +++++-
 dlls/d2d1/geometry.c      | 123 +++++++++++++++++--
 dlls/d2d1/render_target.c | 282 +++++++++++++++++++++++++++++++-------------
 dlls/d2d1/tests/d2d1.c    | 293 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 643 insertions(+), 111 deletions(-)

diff --git a/dlls/d2d1/brush.c b/dlls/d2d1/brush.c
index 762d201..28b896b 100644
--- a/dlls/d2d1/brush.c
+++ b/dlls/d2d1/brush.c
@@ -796,20 +796,21 @@ HRESULT d2d_brush_get_ps_cb(struct d2d_brush *brush, struct d2d_d3d_render_targe
     return hr;
 }
 
-void d2d_brush_bind_resources(struct d2d_brush *brush, struct d2d_d3d_render_target *render_target)
+void d2d_brush_bind_resources(struct d2d_brush *brush, struct d2d_d3d_render_target *render_target,
+        enum d2d_shape_type shape_type)
 {
     static const float blend_factor[] = {1.0f, 1.0f, 1.0f, 1.0f};
     ID3D10Device *device = render_target->device;
+    ID3D10PixelShader *ps;
     HRESULT hr;
 
     ID3D10Device_OMSetBlendState(device, render_target->bs, blend_factor, D3D10_DEFAULT_SAMPLE_MASK);
-    if (brush->type == D2D_BRUSH_TYPE_SOLID)
-    {
-        ID3D10Device_PSSetShader(device, render_target->rect_solid_ps);
-    }
-    else if (brush->type == D2D_BRUSH_TYPE_BITMAP)
+    if (!(ps = render_target->shape_resources[shape_type].ps[brush->type]))
+        FIXME("No pixel shader for shape type %#x and brush type %#x.\n", shape_type, brush->type);
+    ID3D10Device_PSSetShader(device, ps);
+
+    if (brush->type == D2D_BRUSH_TYPE_BITMAP)
     {
-        ID3D10Device_PSSetShader(device, render_target->rect_bitmap_ps);
         ID3D10Device_PSSetShaderResources(device, 0, 1, &brush->u.bitmap.bitmap->view);
         if (!brush->u.bitmap.sampler_state)
         {
@@ -838,7 +839,7 @@ void d2d_brush_bind_resources(struct d2d_brush *brush, struct d2d_d3d_render_tar
         }
         ID3D10Device_PSSetSamplers(device, 0, 1, &brush->u.bitmap.sampler_state);
     }
-    else
+    else if (brush->type != D2D_BRUSH_TYPE_SOLID)
     {
         FIXME("Unhandled brush type %#x.\n", brush->type);
     }
diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h
index 44c8bd5..377c72a 100644
--- a/dlls/d2d1/d2d1_private.h
+++ b/dlls/d2d1/d2d1_private.h
@@ -35,6 +35,14 @@ enum d2d_brush_type
     D2D_BRUSH_TYPE_SOLID,
     D2D_BRUSH_TYPE_LINEAR,
     D2D_BRUSH_TYPE_BITMAP,
+    D2D_BRUSH_TYPE_COUNT,
+};
+
+enum d2d_shape_type
+{
+    D2D_SHAPE_TYPE_TRIANGLE,
+    D2D_SHAPE_TYPE_BEZIER,
+    D2D_SHAPE_TYPE_COUNT,
 };
 
 struct d2d_clip_stack
@@ -44,6 +52,13 @@ struct d2d_clip_stack
     unsigned int count;
 };
 
+struct d2d_shape_resources
+{
+    ID3D10InputLayout *il;
+    ID3D10VertexShader *vs;
+    ID3D10PixelShader *ps[D2D_BRUSH_TYPE_COUNT];
+};
+
 struct d2d_d3d_render_target
 {
     ID2D1RenderTarget ID2D1RenderTarget_iface;
@@ -54,17 +69,13 @@ struct d2d_d3d_render_target
     ID3D10Device *device;
     ID3D10RenderTargetView *view;
     ID3D10StateBlock *stateblock;
-    ID3D10InputLayout *il;
+    struct d2d_shape_resources shape_resources[D2D_SHAPE_TYPE_COUNT];
     ID3D10Buffer *ib;
     unsigned int vb_stride;
     ID3D10Buffer *vb;
-    ID3D10VertexShader *vs;
     ID3D10RasterizerState *rs;
     ID3D10BlendState *bs;
 
-    ID3D10PixelShader *rect_solid_ps;
-    ID3D10PixelShader *rect_bitmap_ps;
-
     D2D1_DRAWING_STATE_DESCRIPTION drawing_state;
     IDWriteRenderingParams *text_rendering_params;
 
@@ -142,7 +153,8 @@ void d2d_linear_gradient_brush_init(struct d2d_brush *brush, ID2D1RenderTarget *
 HRESULT d2d_bitmap_brush_init(struct d2d_brush *brush, struct d2d_d3d_render_target *render_target,
         ID2D1Bitmap *bitmap, const D2D1_BITMAP_BRUSH_PROPERTIES *bitmap_brush_desc,
         const D2D1_BRUSH_PROPERTIES *brush_desc) DECLSPEC_HIDDEN;
-void d2d_brush_bind_resources(struct d2d_brush *brush, struct d2d_d3d_render_target *render_target) DECLSPEC_HIDDEN;
+void d2d_brush_bind_resources(struct d2d_brush *brush, struct d2d_d3d_render_target *render_target,
+        enum d2d_shape_type shape_type) DECLSPEC_HIDDEN;
 HRESULT d2d_brush_get_ps_cb(struct d2d_brush *brush, struct d2d_d3d_render_target *render_target,
         ID3D10Buffer **ps_cb) DECLSPEC_HIDDEN;
 struct d2d_brush *unsafe_impl_from_ID2D1Brush(ID2D1Brush *iface) DECLSPEC_HIDDEN;
@@ -201,6 +213,18 @@ enum d2d_geometry_state
     D2D_GEOMETRY_STATE_FIGURE,
 };
 
+struct d2d_bezier
+{
+    struct
+    {
+        D2D1_POINT_2F position;
+        struct
+        {
+            float u, v, sign;
+        } texcoord;
+    } v[3];
+};
+
 struct d2d_face
 {
     UINT16 v[3];
@@ -218,6 +242,9 @@ struct d2d_geometry
     size_t faces_size;
     size_t face_count;
 
+    struct d2d_bezier *beziers;
+    size_t bezier_count;
+
     union
     {
         struct
diff --git a/dlls/d2d1/geometry.c b/dlls/d2d1/geometry.c
index a511104..ff53bb8 100644
--- a/dlls/d2d1/geometry.c
+++ b/dlls/d2d1/geometry.c
@@ -39,6 +39,10 @@ struct d2d_figure
     D2D1_POINT_2F *vertices;
     size_t vertices_size;
     size_t vertex_count;
+
+    struct d2d_bezier *beziers;
+    size_t beziers_size;
+    size_t bezier_count;
 };
 
 struct d2d_cdt_edge_ref
@@ -71,6 +75,16 @@ static void d2d_point_subtract(D2D1_POINT_2F *out,
     out->y = a->y - b->y;
 }
 
+static float d2d_point_ccw(const D2D1_POINT_2F *a, const D2D1_POINT_2F *b, const D2D1_POINT_2F *c)
+{
+    D2D1_POINT_2F ab, ac;
+
+    d2d_point_subtract(&ab, b, a);
+    d2d_point_subtract(&ac, c, a);
+
+    return ab.x * ac.y - ab.y * ac.x;
+}
+
 static BOOL d2d_array_reserve(void **elements, size_t *capacity, size_t element_count, size_t element_size)
 {
     size_t new_capacity, max_capacity;
@@ -133,6 +147,55 @@ static BOOL d2d_figure_add_vertex(struct d2d_figure *figure, D2D1_POINT_2F verte
     return TRUE;
 }
 
+/* FIXME: No inside/outside testing is done for beziers. */
+static BOOL d2d_figure_add_bezier(struct d2d_figure *figure, D2D1_POINT_2F p0, D2D1_POINT_2F p1, D2D1_POINT_2F p2)
+{
+    struct d2d_bezier *b;
+    unsigned int idx1, idx2;
+    float sign;
+
+    if (!d2d_array_reserve((void **)&figure->beziers, &figure->beziers_size,
+            figure->bezier_count + 1, sizeof(*figure->beziers)))
+    {
+        ERR("Failed to grow beziers array.\n");
+        return FALSE;
+    }
+
+    if (d2d_point_ccw(&p0, &p1, &p2) > 0.0f)
+    {
+        sign = -1.0f;
+        idx1 = 1;
+        idx2 = 2;
+    }
+    else
+    {
+        sign = 1.0f;
+        idx1 = 2;
+        idx2 = 1;
+    }
+
+    b = &figure->beziers[figure->bezier_count];
+    b->v[0].position = p0;
+    b->v[0].texcoord.u = 0.0f;
+    b->v[0].texcoord.v = 0.0f;
+    b->v[0].texcoord.sign = sign;
+    b->v[idx1].position = p1;
+    b->v[idx1].texcoord.u = 0.5f;
+    b->v[idx1].texcoord.v = 0.0f;
+    b->v[idx1].texcoord.sign = sign;
+    b->v[idx2].position = p2;
+    b->v[idx2].texcoord.u = 1.0f;
+    b->v[idx2].texcoord.v = 1.0f;
+    b->v[idx2].texcoord.sign = sign;
+    ++figure->bezier_count;
+
+    if (sign > 0.0f && !d2d_figure_add_vertex(figure, p1))
+        return FALSE;
+    if (!d2d_figure_add_vertex(figure, p2))
+        return FALSE;
+    return TRUE;
+}
+
 static void d2d_cdt_edge_rot(struct d2d_cdt_edge_ref *dst, const struct d2d_cdt_edge_ref *src)
 {
     dst->idx = src->idx;
@@ -193,12 +256,7 @@ static void d2d_cdt_edge_set_destination(const struct d2d_cdt *cdt,
 
 static float d2d_cdt_ccw(const struct d2d_cdt *cdt, size_t a, size_t b, size_t c)
 {
-    D2D1_POINT_2F ab, ac;
-
-    d2d_point_subtract(&ab, &cdt->vertices[b], &cdt->vertices[a]);
-    d2d_point_subtract(&ac, &cdt->vertices[c], &cdt->vertices[a]);
-
-    return ab.x * ac.y - ab.y * ac.x;
+    return d2d_point_ccw(&cdt->vertices[a], &cdt->vertices[b], &cdt->vertices[c]);
 }
 
 static BOOL d2d_cdt_rightof(const struct d2d_cdt *cdt, size_t p, const struct d2d_cdt_edge_ref *e)
@@ -773,6 +831,7 @@ static BOOL d2d_cdt_insert_segments(struct d2d_cdt *cdt, struct d2d_geometry *ge
 /* Intersect the geometry's segments with themselves. This uses the
  * straightforward approach of testing everything against everything, but
  * there certainly exist more scalable algorithms for this. */
+/* FIXME: Beziers can't currently self-intersect. */
 static BOOL d2d_geometry_intersect_self(struct d2d_geometry *geometry)
 {
     D2D1_POINT_2F p0, p1, q0, q1, v_p, v_q, v_qp, intersection;
@@ -920,6 +979,7 @@ static BOOL d2d_path_geometry_add_figure(struct d2d_geometry *geometry)
 
 static void d2d_geometry_destroy(struct d2d_geometry *geometry)
 {
+    HeapFree(GetProcessHeap(), 0, geometry->beziers);
     HeapFree(GetProcessHeap(), 0, geometry->faces);
     HeapFree(GetProcessHeap(), 0, geometry->vertices);
     HeapFree(GetProcessHeap(), 0, geometry);
@@ -1048,9 +1108,11 @@ static void STDMETHODCALLTYPE d2d_geometry_sink_AddBeziers(ID2D1GeometrySink *if
         const D2D1_BEZIER_SEGMENT *beziers, UINT32 count)
 {
     struct d2d_geometry *geometry = impl_from_ID2D1GeometrySink(iface);
+    struct d2d_figure *figure = &geometry->u.path.figures[geometry->u.path.figure_count - 1];
+    D2D1_POINT_2F p;
     unsigned int i;
 
-    FIXME("iface %p, beziers %p, count %u stub!\n", iface, beziers, count);
+    TRACE("iface %p, beziers %p, count %u.\n", iface, beziers, count);
 
     if (geometry->u.path.state != D2D_GEOMETRY_STATE_FIGURE)
     {
@@ -1060,9 +1122,14 @@ static void STDMETHODCALLTYPE d2d_geometry_sink_AddBeziers(ID2D1GeometrySink *if
 
     for (i = 0; i < count; ++i)
     {
-        if (!d2d_figure_add_vertex(&geometry->u.path.figures[geometry->u.path.figure_count - 1], beziers[i].point3))
+        /* FIXME: This tries to approximate a cubic bezier with a quadratic one. */
+        p.x = (beziers[i].point1.x + beziers[i].point2.x) * 0.75f;
+        p.y = (beziers[i].point1.y + beziers[i].point2.y) * 0.75f;
+        p.x -= (figure->vertices[figure->vertex_count - 1].x + beziers[i].point3.x) * 0.25f;
+        p.y -= (figure->vertices[figure->vertex_count - 1].y + beziers[i].point3.y) * 0.25f;
+        if (!d2d_figure_add_bezier(figure, figure->vertices[figure->vertex_count - 1], p, beziers[i].point3))
         {
-            ERR("Failed to add vertex.\n");
+            ERR("Failed to add bezier.\n");
             return;
         }
     }
@@ -1097,6 +1164,7 @@ static void d2d_path_geometry_free_figures(struct d2d_geometry *geometry)
 
     for (i = 0; i < geometry->u.path.figure_count; ++i)
     {
+        HeapFree(GetProcessHeap(), 0, geometry->u.path.figures[i].beziers);
         HeapFree(GetProcessHeap(), 0, geometry->u.path.figures[i].vertices);
     }
     HeapFree(GetProcessHeap(), 0, geometry->u.path.figures);
@@ -1108,6 +1176,7 @@ static HRESULT STDMETHODCALLTYPE d2d_geometry_sink_Close(ID2D1GeometrySink *ifac
 {
     struct d2d_geometry *geometry = impl_from_ID2D1GeometrySink(iface);
     HRESULT hr = E_FAIL;
+    size_t i, start;
 
     TRACE("iface %p.\n", iface);
 
@@ -1121,7 +1190,33 @@ static HRESULT STDMETHODCALLTYPE d2d_geometry_sink_Close(ID2D1GeometrySink *ifac
 
     if (!d2d_geometry_intersect_self(geometry))
         goto done;
-    hr = d2d_path_geometry_triangulate(geometry);
+    if (FAILED(hr = d2d_path_geometry_triangulate(geometry)))
+        goto done;
+
+    for (i = 0; i < geometry->u.path.figure_count; ++i)
+    {
+        geometry->bezier_count += geometry->u.path.figures[i].bezier_count;
+    }
+
+    if (!(geometry->beziers = HeapAlloc(GetProcessHeap(), 0,
+            geometry->bezier_count * sizeof(*geometry->beziers))))
+    {
+        ERR("Failed to allocate beziers array.\n");
+        geometry->bezier_count = 0;
+        hr = E_OUTOFMEMORY;
+        goto done;
+    }
+
+    for (i = 0, start = 0; i < geometry->u.path.figure_count; ++i)
+    {
+        struct d2d_figure *figure = &geometry->u.path.figures[i];
+        if (figure->bezier_count)
+        {
+            memcpy(&geometry->beziers[start], figure->beziers,
+                    figure->bezier_count * sizeof(*figure->beziers));
+            start += figure->bezier_count;
+        }
+    }
 
 done:
     d2d_path_geometry_free_figures(geometry);
@@ -1156,9 +1251,10 @@ static void STDMETHODCALLTYPE d2d_geometry_sink_AddQuadraticBeziers(ID2D1Geometr
         const D2D1_QUADRATIC_BEZIER_SEGMENT *beziers, UINT32 bezier_count)
 {
     struct d2d_geometry *geometry = impl_from_ID2D1GeometrySink(iface);
+    struct d2d_figure *figure = &geometry->u.path.figures[geometry->u.path.figure_count - 1];
     unsigned int i;
 
-    FIXME("iface %p, beziers %p, bezier_count %u stub!\n", iface, beziers, bezier_count);
+    TRACE("iface %p, beziers %p, bezier_count %u.\n", iface, beziers, bezier_count);
 
     if (geometry->u.path.state != D2D_GEOMETRY_STATE_FIGURE)
     {
@@ -1168,9 +1264,10 @@ static void STDMETHODCALLTYPE d2d_geometry_sink_AddQuadraticBeziers(ID2D1Geometr
 
     for (i = 0; i < bezier_count; ++i)
     {
-        if (!d2d_figure_add_vertex(&geometry->u.path.figures[geometry->u.path.figure_count - 1], beziers[i].point2))
+        if (!d2d_figure_add_bezier(figure, figure->vertices[figure->vertex_count - 1],
+                beziers[i].point1, beziers[i].point2))
         {
-            ERR("Failed to add vertex.\n");
+            ERR("Failed to add bezier.\n");
             return;
         }
     }
diff --git a/dlls/d2d1/render_target.c b/dlls/d2d1/render_target.c
index 2da50bc..37a8d57 100644
--- a/dlls/d2d1/render_target.c
+++ b/dlls/d2d1/render_target.c
@@ -127,10 +127,11 @@ static void d2d_clip_stack_pop(struct d2d_clip_stack *stack)
     --stack->count;
 }
 
-static void d2d_draw(struct d2d_d3d_render_target *render_target, ID3D10Buffer *ib, unsigned int index_count,
-        ID3D10Buffer *vb, unsigned int vb_stride, ID3D10Buffer *vs_cb, ID3D10Buffer *ps_cb,
-        struct d2d_brush *brush)
+static void d2d_draw(struct d2d_d3d_render_target *render_target, enum d2d_shape_type shape_type,
+        ID3D10Buffer *ib, unsigned int index_count, ID3D10Buffer *vb, unsigned int vb_stride,
+        ID3D10Buffer *vs_cb, ID3D10Buffer *ps_cb, struct d2d_brush *brush)
 {
+    struct d2d_shape_resources *shape_resources = &render_target->shape_resources[shape_type];
     ID3D10Device *device = render_target->device;
     unsigned int offset;
     D3D10_VIEWPORT vp;
@@ -151,13 +152,13 @@ static void d2d_draw(struct d2d_d3d_render_target *render_target, ID3D10Buffer *
 
     ID3D10Device_ClearState(device);
 
-    ID3D10Device_IASetInputLayout(device, render_target->il);
+    ID3D10Device_IASetInputLayout(device, shape_resources->il);
     ID3D10Device_IASetPrimitiveTopology(device, D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
     ID3D10Device_IASetIndexBuffer(device, ib, DXGI_FORMAT_R16_UINT, 0);
     offset = 0;
     ID3D10Device_IASetVertexBuffers(device, 0, 1, &vb, &vb_stride, &offset);
     ID3D10Device_VSSetConstantBuffers(device, 0, 1, &vs_cb);
-    ID3D10Device_VSSetShader(device, render_target->vs);
+    ID3D10Device_VSSetShader(device, shape_resources->vs);
     ID3D10Device_PSSetConstantBuffers(device, 0, 1, &ps_cb);
     ID3D10Device_RSSetViewports(device, 1, &vp);
     if (render_target->clip_stack.count)
@@ -175,11 +176,14 @@ static void d2d_draw(struct d2d_d3d_render_target *render_target, ID3D10Buffer *
     }
     ID3D10Device_OMSetRenderTargets(device, 1, &render_target->view, NULL);
     if (brush)
-        d2d_brush_bind_resources(brush, render_target);
+        d2d_brush_bind_resources(brush, render_target, shape_type);
     else
-        ID3D10Device_PSSetShader(device, render_target->rect_solid_ps);
+        ID3D10Device_PSSetShader(device, shape_resources->ps[D2D_BRUSH_TYPE_SOLID]);
 
-    ID3D10Device_DrawIndexed(device, index_count, 0, 0);
+    if (ib)
+        ID3D10Device_DrawIndexed(device, index_count, 0, 0);
+    else
+        ID3D10Device_Draw(device, index_count, 0);
 
     if (FAILED(hr = render_target->stateblock->lpVtbl->Apply(render_target->stateblock)))
         WARN("Failed to apply stateblock, hr %#x.\n", hr);
@@ -228,17 +232,25 @@ static ULONG STDMETHODCALLTYPE d2d_d3d_render_target_Release(ID2D1RenderTarget *
 
     if (!refcount)
     {
+        unsigned int i, j;
+
         d2d_clip_stack_cleanup(&render_target->clip_stack);
         if (render_target->text_rendering_params)
             IDWriteRenderingParams_Release(render_target->text_rendering_params);
-        ID3D10PixelShader_Release(render_target->rect_bitmap_ps);
-        ID3D10PixelShader_Release(render_target->rect_solid_ps);
         ID3D10BlendState_Release(render_target->bs);
         ID3D10RasterizerState_Release(render_target->rs);
-        ID3D10VertexShader_Release(render_target->vs);
         ID3D10Buffer_Release(render_target->vb);
         ID3D10Buffer_Release(render_target->ib);
-        ID3D10InputLayout_Release(render_target->il);
+        for (i = 0; i < D2D_SHAPE_TYPE_COUNT; ++i)
+        {
+            for (j = 0; j < D2D_BRUSH_TYPE_COUNT; ++j)
+            {
+                if (render_target->shape_resources[i].ps[j])
+                    ID3D10PixelShader_Release(render_target->shape_resources[i].ps[j]);
+            }
+            ID3D10VertexShader_Release(render_target->shape_resources[i].vs);
+            ID3D10InputLayout_Release(render_target->shape_resources[i].il);
+        }
         render_target->stateblock->lpVtbl->Release(render_target->stateblock);
         ID3D10RenderTargetView_Release(render_target->view);
         ID3D10Device_Release(render_target->device);
@@ -606,33 +618,6 @@ static void STDMETHODCALLTYPE d2d_d3d_render_target_FillGeometry(ID2D1RenderTarg
 
     geometry_impl = unsafe_impl_from_ID2D1Geometry(geometry);
 
-    buffer_desc.ByteWidth = geometry_impl->face_count * sizeof(*geometry_impl->faces);
-    buffer_desc.Usage = D3D10_USAGE_DEFAULT;
-    buffer_desc.BindFlags = D3D10_BIND_INDEX_BUFFER;
-    buffer_desc.CPUAccessFlags = 0;
-    buffer_desc.MiscFlags = 0;
-
-    buffer_data.pSysMem = geometry_impl->faces;
-    buffer_data.SysMemPitch = 0;
-    buffer_data.SysMemSlicePitch = 0;
-
-    if (FAILED(hr = ID3D10Device_CreateBuffer(render_target->device, &buffer_desc, &buffer_data, &ib)))
-    {
-        WARN("Failed to create index buffer, hr %#x.\n", hr);
-        return;
-    }
-
-    buffer_desc.ByteWidth = geometry_impl->vertex_count * sizeof(*geometry_impl->vertices);
-    buffer_desc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
-    buffer_data.pSysMem = geometry_impl->vertices;
-
-    if (FAILED(hr = ID3D10Device_CreateBuffer(render_target->device, &buffer_desc, &buffer_data, &vb)))
-    {
-        ERR("Failed to create vertex buffer, hr %#x.\n", hr);
-        ID3D10Buffer_Release(ib);
-        return;
-    }
-
     tmp_x =  (2.0f * render_target->dpi_x) / (96.0f * render_target->pixel_size.width);
     tmp_y = -(2.0f * render_target->dpi_y) / (96.0f * render_target->pixel_size.height);
     transform._11 = render_target->drawing_state.transform._11 * tmp_x;
@@ -645,14 +630,18 @@ static void STDMETHODCALLTYPE d2d_d3d_render_target_FillGeometry(ID2D1RenderTarg
     transform.pad1 = 0.0f;
 
     buffer_desc.ByteWidth = sizeof(transform);
+    buffer_desc.Usage = D3D10_USAGE_DEFAULT;
     buffer_desc.BindFlags = D3D10_BIND_CONSTANT_BUFFER;
+    buffer_desc.CPUAccessFlags = 0;
+    buffer_desc.MiscFlags = 0;
+
     buffer_data.pSysMem = &transform;
+    buffer_data.SysMemPitch = 0;
+    buffer_data.SysMemSlicePitch = 0;
 
     if (FAILED(hr = ID3D10Device_CreateBuffer(render_target->device, &buffer_desc, &buffer_data, &vs_cb)))
     {
         WARN("Failed to create constant buffer, hr %#x.\n", hr);
-        ID3D10Buffer_Release(vb);
-        ID3D10Buffer_Release(ib);
         return;
     }
 
@@ -660,18 +649,56 @@ static void STDMETHODCALLTYPE d2d_d3d_render_target_FillGeometry(ID2D1RenderTarg
     {
         WARN("Failed to get ps constant buffer, hr %#x.\n", hr);
         ID3D10Buffer_Release(vs_cb);
-        ID3D10Buffer_Release(vb);
-        ID3D10Buffer_Release(ib);
         return;
     }
 
-    d2d_draw(render_target, ib, 3 * geometry_impl->face_count, vb,
+    buffer_desc.ByteWidth = geometry_impl->face_count * sizeof(*geometry_impl->faces);
+    buffer_desc.BindFlags = D3D10_BIND_INDEX_BUFFER;
+    buffer_data.pSysMem = geometry_impl->faces;
+
+    if (FAILED(hr = ID3D10Device_CreateBuffer(render_target->device, &buffer_desc, &buffer_data, &ib)))
+    {
+        WARN("Failed to create index buffer, hr %#x.\n", hr);
+        goto done;
+    }
+
+    buffer_desc.ByteWidth = geometry_impl->vertex_count * sizeof(*geometry_impl->vertices);
+    buffer_desc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
+    buffer_data.pSysMem = geometry_impl->vertices;
+
+    if (FAILED(hr = ID3D10Device_CreateBuffer(render_target->device, &buffer_desc, &buffer_data, &vb)))
+    {
+        ERR("Failed to create vertex buffer, hr %#x.\n", hr);
+        ID3D10Buffer_Release(ib);
+        goto done;
+    }
+
+    d2d_draw(render_target, D2D_SHAPE_TYPE_TRIANGLE, ib, 3 * geometry_impl->face_count, vb,
             sizeof(*geometry_impl->vertices), vs_cb, ps_cb, brush_impl);
 
-    ID3D10Buffer_Release(ps_cb);
-    ID3D10Buffer_Release(vs_cb);
     ID3D10Buffer_Release(vb);
     ID3D10Buffer_Release(ib);
+
+    if (geometry_impl->bezier_count)
+    {
+        buffer_desc.ByteWidth = geometry_impl->bezier_count * sizeof(*geometry_impl->beziers);
+        buffer_data.pSysMem = geometry_impl->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);
+            goto done;
+        }
+
+        d2d_draw(render_target, D2D_SHAPE_TYPE_BEZIER, NULL, 3 * geometry_impl->bezier_count, vb,
+                sizeof(*geometry_impl->beziers->v), vs_cb, ps_cb, brush_impl);
+
+        ID3D10Buffer_Release(vb);
+    }
+
+done:
+    ID3D10Buffer_Release(ps_cb);
+    ID3D10Buffer_Release(vs_cb);
 }
 
 static void STDMETHODCALLTYPE d2d_d3d_render_target_FillMesh(ID2D1RenderTarget *iface,
@@ -1095,7 +1122,8 @@ static void STDMETHODCALLTYPE d2d_d3d_render_target_Clear(ID2D1RenderTarget *ifa
         return;
     }
 
-    d2d_draw(render_target, render_target->ib, 6, render_target->vb, render_target->vb_stride, vs_cb, ps_cb, NULL);
+    d2d_draw(render_target, D2D_SHAPE_TYPE_TRIANGLE, render_target->ib, 6,
+            render_target->vb, render_target->vb_stride, vs_cb, ps_cb, NULL);
 
     ID3D10Buffer_Release(ps_cb);
     ID3D10Buffer_Release(vs_cb);
@@ -1408,13 +1436,19 @@ HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_target,
     D3D10_BUFFER_DESC buffer_desc;
     D3D10_BLEND_DESC blend_desc;
     ID3D10Resource *resource;
+    unsigned int i, j;
     HRESULT hr;
 
-    static const D3D10_INPUT_ELEMENT_DESC il_desc[] =
+    static const D3D10_INPUT_ELEMENT_DESC il_desc_triangle[] =
     {
         {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0},
     };
-    static const DWORD vs_code[] =
+    static const D3D10_INPUT_ELEMENT_DESC il_desc_bezier[] =
+    {
+        {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0},
+        {"TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 8, D3D10_INPUT_PER_VERTEX_DATA, 0},
+    };
+    static const DWORD vs_code_triangle[] =
     {
         /* float3x2 transform;
          *
@@ -1433,7 +1467,32 @@ HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_target,
         0x00000000, 0x00208246, 0x00000000, 0x00000001, 0x05000036, 0x001020c2, 0x00000000, 0x00101ea6,
         0x00000000, 0x0100003e,
     };
-    static const DWORD rect_solid_ps_code[] =
+    static const DWORD vs_code_bezier[] =
+    {
+#if 0
+        float3x2 transform;
+
+        float4 main(float4 position : POSITION,
+              inout float3 texcoord : TEXCOORD0) : SV_POSITION
+        {
+            return float4(mul(position.xyw, transform), position.zw);
+        }
+#endif
+        0x43425844, 0x5e578adb, 0x093f7e27, 0x50d478af, 0xec3dfa4f, 0x00000001, 0x00000198, 0x00000003,
+        0x0000002c, 0x00000080, 0x000000d8, 0x4e475349, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
+        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000041, 0x00000000, 0x00000000,
+        0x00000003, 0x00000001, 0x00000707, 0x49534f50, 0x4e4f4954, 0x58455400, 0x524f4f43, 0xabab0044,
+        0x4e47534f, 0x00000050, 0x00000002, 0x00000008, 0x00000038, 0x00000000, 0x00000001, 0x00000003,
+        0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000807,
+        0x505f5653, 0x5449534f, 0x004e4f49, 0x43584554, 0x44524f4f, 0xababab00, 0x52444853, 0x000000b8,
+        0x00010040, 0x0000002e, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x0300005f, 0x001010f2,
+        0x00000000, 0x0300005f, 0x00101072, 0x00000001, 0x04000067, 0x001020f2, 0x00000000, 0x00000001,
+        0x03000065, 0x00102072, 0x00000001, 0x08000010, 0x00102012, 0x00000000, 0x00101346, 0x00000000,
+        0x00208246, 0x00000000, 0x00000000, 0x08000010, 0x00102022, 0x00000000, 0x00101346, 0x00000000,
+        0x00208246, 0x00000000, 0x00000001, 0x05000036, 0x001020c2, 0x00000000, 0x00101ea6, 0x00000000,
+        0x05000036, 0x00102072, 0x00000001, 0x00101246, 0x00000001, 0x0100003e,
+    };
+    static const DWORD ps_code_triangle_solid[] =
     {
         /* float4 color;
          *
@@ -1449,7 +1508,7 @@ HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_target,
         0x00000010, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
         0x06000036, 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x0100003e,
     };
-    static const DWORD rect_bitmap_ps_code[] =
+    static const DWORD ps_code_triangle_bitmap[] =
     {
 #if 0
         float3x2 transform;
@@ -1487,6 +1546,32 @@ HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_target,
         0x00000000, 0x0010003a, 0x00000000, 0x0020803a, 0x00000000, 0x00000001, 0x05000036, 0x00102072,
         0x00000000, 0x00100246, 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[] =
+    {
+#if 0
+        float4 color;
+
+        float4 main(float4 position : SV_POSITION, float3 t : TEXCOORD0) : SV_Target
+        {
+            clip((t.x * t.x - t.y) * t.z);
+            return color;
+        }
+#endif
+        0x43425844, 0x66075f9e, 0x2ffe405b, 0xb551ee63, 0xa0d9f457, 0x00000001, 0x00000180, 0x00000003,
+        0x0000002c, 0x00000084, 0x000000b8, 0x4e475349, 0x00000050, 0x00000002, 0x00000008, 0x00000038,
+        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000,
+        0x00000003, 0x00000001, 0x00000707, 0x505f5653, 0x5449534f, 0x004e4f49, 0x43584554, 0x44524f4f,
+        0xababab00, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000,
+        0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x000000c0,
+        0x00000040, 0x00000030, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03001062, 0x00101072,
+        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0a000032, 0x00100012,
+        0x00000000, 0x0010100a, 0x00000001, 0x0010100a, 0x00000001, 0x8010101a, 0x00000041, 0x00000001,
+        0x07000038, 0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x0010102a, 0x00000001, 0x07000031,
+        0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x00000000, 0x0304000d, 0x0010000a,
+        0x00000000, 0x06000036, 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x0100003e,
+    };
     static const struct
     {
         float x, y;
@@ -1547,11 +1632,57 @@ HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_target,
         goto err;
     }
 
-    if (FAILED(hr = ID3D10Device_CreateInputLayout(render_target->device, il_desc,
-            sizeof(il_desc) / sizeof(*il_desc), vs_code, sizeof(vs_code),
-            &render_target->il)))
+    if (FAILED(hr = ID3D10Device_CreateInputLayout(render_target->device, il_desc_triangle,
+            sizeof(il_desc_triangle) / sizeof(*il_desc_triangle), vs_code_triangle, sizeof(vs_code_triangle),
+            &render_target->shape_resources[D2D_SHAPE_TYPE_TRIANGLE].il)))
+    {
+        WARN("Failed to create triangle 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)))
+    {
+        WARN("Failed to create bezier input layout, 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)))
+    {
+        WARN("Failed to create triangle vertex shader, hr %#x.\n", hr);
+        goto err;
+    }
+
+    if (FAILED(hr = ID3D10Device_CreateVertexShader(render_target->device,  vs_code_bezier,
+            sizeof(vs_code_bezier), &render_target->shape_resources[D2D_SHAPE_TYPE_BEZIER].vs)))
     {
-        WARN("Failed to create clear input layout, hr %#x.\n", hr);
+        WARN("Failed to create bezier vertex shader, hr %#x.\n", hr);
+        goto err;
+    }
+
+    if (FAILED(hr = ID3D10Device_CreatePixelShader(render_target->device,
+            ps_code_triangle_solid, sizeof(ps_code_triangle_solid),
+            &render_target->shape_resources[D2D_SHAPE_TYPE_TRIANGLE].ps[D2D_BRUSH_TYPE_SOLID])))
+    {
+        WARN("Failed to create triangle/solid pixel shader, hr %#x.\n", hr);
+        goto err;
+    }
+
+    if (FAILED(hr = ID3D10Device_CreatePixelShader(render_target->device,
+            ps_code_triangle_bitmap, sizeof(ps_code_triangle_bitmap),
+            &render_target->shape_resources[D2D_SHAPE_TYPE_TRIANGLE].ps[D2D_BRUSH_TYPE_BITMAP])))
+    {
+        WARN("Failed to create triangle/bitmap pixel shader, hr %#x.\n", hr);
+        goto err;
+    }
+
+    if (FAILED(hr = ID3D10Device_CreatePixelShader(render_target->device,
+            ps_code_bezier_solid, sizeof(ps_code_bezier_solid),
+            &render_target->shape_resources[D2D_SHAPE_TYPE_BEZIER].ps[D2D_BRUSH_TYPE_SOLID])))
+    {
+        WARN("Failed to create bezier/solid pixel shader, hr %#x.\n", hr);
         goto err;
     }
 
@@ -1584,13 +1715,6 @@ HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_target,
         goto err;
     }
 
-    if (FAILED(hr = ID3D10Device_CreateVertexShader(render_target->device,
-            vs_code, sizeof(vs_code), &render_target->vs)))
-    {
-        WARN("Failed to create clear vertex shader, hr %#x.\n", hr);
-        goto err;
-    }
-
     rs_desc.FillMode = D3D10_FILL_SOLID;
     rs_desc.CullMode = D3D10_CULL_BACK;
     rs_desc.FrontCounterClockwise = FALSE;
@@ -1622,20 +1746,6 @@ HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_target,
         goto err;
     }
 
-    if (FAILED(hr = ID3D10Device_CreatePixelShader(render_target->device,
-            rect_solid_ps_code, sizeof(rect_solid_ps_code), &render_target->rect_solid_ps)))
-    {
-        WARN("Failed to create pixel shader, hr %#x.\n", hr);
-        goto err;
-    }
-
-    if (FAILED(hr = ID3D10Device_CreatePixelShader(render_target->device,
-            rect_bitmap_ps_code, sizeof(rect_bitmap_ps_code), &render_target->rect_bitmap_ps)))
-    {
-        WARN("Failed to create pixel shader, hr %#x.\n", hr);
-        goto err;
-    }
-
     if (FAILED(hr = IDXGISurface_GetDesc(surface, &surface_desc)))
     {
         WARN("Failed to get surface desc, hr %#x.\n", hr);
@@ -1665,22 +1775,26 @@ HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_target,
     return S_OK;
 
 err:
-    if (render_target->rect_bitmap_ps)
-        ID3D10PixelShader_Release(render_target->rect_bitmap_ps);
-    if (render_target->rect_solid_ps)
-        ID3D10PixelShader_Release(render_target->rect_solid_ps);
     if (render_target->bs)
         ID3D10BlendState_Release(render_target->bs);
     if (render_target->rs)
         ID3D10RasterizerState_Release(render_target->rs);
-    if (render_target->vs)
-        ID3D10VertexShader_Release(render_target->vs);
     if (render_target->vb)
         ID3D10Buffer_Release(render_target->vb);
     if (render_target->ib)
         ID3D10Buffer_Release(render_target->ib);
-    if (render_target->il)
-        ID3D10InputLayout_Release(render_target->il);
+    for (i = 0; i < D2D_SHAPE_TYPE_COUNT; ++i)
+    {
+        for (j = 0; j < D2D_BRUSH_TYPE_COUNT; ++j)
+        {
+            if (render_target->shape_resources[i].ps[j])
+                ID3D10PixelShader_Release(render_target->shape_resources[i].ps[j]);
+        }
+        if (render_target->shape_resources[i].vs)
+            ID3D10VertexShader_Release(render_target->shape_resources[i].vs);
+        if (render_target->shape_resources[i].il)
+            ID3D10InputLayout_Release(render_target->shape_resources[i].il);
+    }
     if (render_target->stateblock)
         render_target->stateblock->lpVtbl->Release(render_target->stateblock);
     if (render_target->view)
diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c
index 8351dbe..f8ebecc 100644
--- a/dlls/d2d1/tests/d2d1.c
+++ b/dlls/d2d1/tests/d2d1.c
@@ -24,12 +24,27 @@
 #include "initguid.h"
 #include "dwrite.h"
 
+struct figure
+{
+    unsigned int *spans;
+    unsigned int spans_size;
+    unsigned int span_count;
+};
+
 static void set_point(D2D1_POINT_2F *point, float x, float y)
 {
     point->x = x;
     point->y = y;
 }
 
+static void set_quadratic(D2D1_QUADRATIC_BEZIER_SEGMENT *quadratic, float x1, float y1, float x2, float y2)
+{
+    quadratic->point1.x = x1;
+    quadratic->point1.y = y1;
+    quadratic->point2.x = x2;
+    quadratic->point2.y = y2;
+}
+
 static void set_rect(D2D1_RECT_F *rect, float left, float top, float right, float bottom)
 {
     rect->left = left;
@@ -179,6 +194,192 @@ static BOOL compare_surface(IDXGISurface *surface, const char *ref_sha1)
     return ret;
 }
 
+static void serialize_figure(struct figure *figure)
+{
+    static const char lookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+    unsigned int i, j, k, span;
+    char output[76];
+    char t[3];
+    char *p;
+
+    for (i = 0, j = 0, k = 0, p = output; i < figure->span_count; ++i)
+    {
+        span = figure->spans[i];
+        while (span)
+        {
+            t[j] = span & 0x7f;
+            if (span > 0x7f)
+                t[j] |= 0x80;
+            span >>= 7;
+            if (++j == 3)
+            {
+                p[0] = lookup[(t[0] & 0xfc) >> 2];
+                p[1] = lookup[((t[0] & 0x03) << 4) | ((t[1] & 0xf0) >> 4)];
+                p[2] = lookup[((t[1] & 0x0f) << 2) | ((t[2] & 0xc0) >> 6)];
+                p[3] = lookup[t[2] & 0x3f];
+                p += 4;
+                if (++k == 19)
+                {
+                    trace("%.76s\n", output);
+                    p = output;
+                    k = 0;
+                }
+                j = 0;
+            }
+        }
+    }
+    if (j)
+    {
+        for (i = j; i < 3; ++i)
+            t[i] = 0;
+        p[0] = lookup[(t[0] & 0xfc) >> 2];
+        p[1] = lookup[((t[0] & 0x03) << 4) | ((t[1] & 0xf0) >> 4)];
+        p[2] = lookup[((t[1] & 0x0f) << 2) | ((t[2] & 0xc0) >> 6)];
+        p[3] = lookup[t[2] & 0x3f];
+        ++k;
+    }
+    if (k)
+        trace("%.*s\n", k * 4, output);
+}
+
+static void deserialize_span(struct figure *figure, unsigned int *current, unsigned int *shift, unsigned int c)
+{
+    *current |= (c & 0x7f) << *shift;
+    if (c & 0x80)
+    {
+        *shift += 7;
+        return;
+    }
+
+    if (figure->span_count == figure->spans_size)
+    {
+        figure->spans_size *= 2;
+        figure->spans = HeapReAlloc(GetProcessHeap(), 0, figure->spans,
+                figure->spans_size * sizeof(*figure->spans));
+    }
+
+    figure->spans[figure->span_count++] = *current;
+    *current = 0;
+    *shift = 0;
+}
+
+static void deserialize_figure(struct figure *figure, const BYTE *s)
+{
+    static const BYTE lookup[] =
+    {
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+        0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+        0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+        0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff,
+    };
+    unsigned int current = 0, shift = 0;
+    const BYTE *ptr;
+    BYTE x, y;
+
+    figure->span_count = 0;
+    figure->spans_size = 64;
+    figure->spans = HeapAlloc(GetProcessHeap(), 0, figure->spans_size * sizeof(*figure->spans));
+
+    for (ptr = s; *ptr; ptr += 4)
+    {
+        x = lookup[ptr[0]];
+        y = lookup[ptr[1]];
+        deserialize_span(figure, &current, &shift, ((x & 0x3f) << 2) | ((y & 0x3f) >> 4));
+        x = lookup[ptr[2]];
+        deserialize_span(figure, &current, &shift, ((y & 0x0f) << 4) | ((x & 0x3f) >> 2));
+        y = lookup[ptr[3]];
+        deserialize_span(figure, &current, &shift, ((x & 0x03) << 6) | (y & 0x3f));
+    }
+}
+
+static BOOL compare_figure(IDXGISurface *surface, unsigned int x, unsigned int y,
+        unsigned int w, unsigned int h, DWORD prev, unsigned int max_diff, const char *ref)
+{
+    D3D10_MAPPED_TEXTURE2D mapped_texture;
+    D3D10_TEXTURE2D_DESC texture_desc;
+    struct figure ref_figure, figure;
+    DXGI_SURFACE_DESC surface_desc;
+    unsigned int i, j, span, diff;
+    ID3D10Resource *src_resource;
+    ID3D10Texture2D *texture;
+    ID3D10Device *device;
+    HRESULT hr;
+
+    hr = IDXGISurface_GetDevice(surface, &IID_ID3D10Device, (void **)&device);
+    ok(SUCCEEDED(hr), "Failed to get device, hr %#x.\n", hr);
+    hr = IDXGISurface_QueryInterface(surface, &IID_ID3D10Resource, (void **)&src_resource);
+    ok(SUCCEEDED(hr), "Failed to query resource interface, hr %#x.\n", hr);
+
+    hr = IDXGISurface_GetDesc(surface, &surface_desc);
+    ok(SUCCEEDED(hr), "Failed to get surface desc, hr %#x.\n", hr);
+    texture_desc.Width = surface_desc.Width;
+    texture_desc.Height = surface_desc.Height;
+    texture_desc.MipLevels = 1;
+    texture_desc.ArraySize = 1;
+    texture_desc.Format = surface_desc.Format;
+    texture_desc.SampleDesc = surface_desc.SampleDesc;
+    texture_desc.Usage = D3D10_USAGE_STAGING;
+    texture_desc.BindFlags = 0;
+    texture_desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ;
+    texture_desc.MiscFlags = 0;
+    hr = ID3D10Device_CreateTexture2D(device, &texture_desc, NULL, &texture);
+    ok(SUCCEEDED(hr), "Failed to create texture, hr %#x.\n", hr);
+
+    ID3D10Device_CopyResource(device, (ID3D10Resource *)texture, src_resource);
+    hr = ID3D10Texture2D_Map(texture, 0, D3D10_MAP_READ, 0, &mapped_texture);
+    ok(SUCCEEDED(hr), "Failed to map texture, hr %#x.\n", hr);
+
+    figure.span_count = 0;
+    figure.spans_size = 64;
+    figure.spans = HeapAlloc(GetProcessHeap(), 0, figure.spans_size * sizeof(*figure.spans));
+
+    for (i = 0, span = 0; i < h; ++i)
+    {
+        const DWORD *row = (DWORD *)((BYTE *)mapped_texture.pData + (y + i) * mapped_texture.RowPitch + x * 4);
+        for (j = 0; j < w; ++j, ++span)
+        {
+            if ((i || j) && prev != row[j])
+            {
+                if (figure.span_count == figure.spans_size)
+                {
+                    figure.spans_size *= 2;
+                    figure.spans = HeapReAlloc(GetProcessHeap(), 0, figure.spans,
+                            figure.spans_size * sizeof(*figure.spans));
+                }
+                figure.spans[figure.span_count++] = span;
+                prev = row[j];
+                span = 0;
+            }
+        }
+    }
+
+    deserialize_figure(&ref_figure, (BYTE *)ref);
+
+    j = min(figure.span_count, ref_figure.span_count);
+    for (i = 0, diff = 0; i < j; ++i)
+        diff += abs(figure.spans[i] - ref_figure.spans[i]);
+    for (i = j; j < figure.span_count; ++j)
+        diff += figure.spans[i];
+    for (i = j; j < ref_figure.span_count; ++j)
+        diff += ref_figure.spans[i];
+    if (diff > max_diff)
+        serialize_figure(&figure);
+
+    HeapFree(GetProcessHeap(), 0, ref_figure.spans);
+    HeapFree(GetProcessHeap(), 0, figure.spans);
+    ID3D10Texture2D_Unmap(texture, 0);
+
+    ID3D10Texture2D_Release(texture);
+    ID3D10Resource_Release(src_resource);
+    ID3D10Device_Release(device);
+
+    return diff <= max_diff;
+}
+
 static ID3D10Device1 *create_device(void)
 {
     ID3D10Device1 *device;
@@ -982,6 +1183,36 @@ static void fill_geometry_sink(ID2D1GeometrySink *sink)
     ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED);
 }
 
+static void fill_geometry_sink_bezier(ID2D1GeometrySink *sink)
+{
+    D2D1_QUADRATIC_BEZIER_SEGMENT quadratic;
+    D2D1_POINT_2F point;
+
+    set_point(&point, 5.0f, 160.0f);
+    ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_FILLED);
+    set_quadratic(&quadratic, 40.0f, 160.0f, 40.0f,  20.0f);
+    ID2D1GeometrySink_AddQuadraticBezier(sink, &quadratic);
+    set_quadratic(&quadratic, 40.0f, 160.0f, 75.0f, 160.0f);
+    ID2D1GeometrySink_AddQuadraticBezier(sink, &quadratic);
+    set_quadratic(&quadratic, 40.0f, 160.0f, 40.0f, 300.0f);
+    ID2D1GeometrySink_AddQuadraticBezier(sink, &quadratic);
+    set_quadratic(&quadratic, 40.0f, 160.0f,  5.0f, 160.0f);
+    ID2D1GeometrySink_AddQuadraticBezier(sink, &quadratic);
+    ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED);
+
+    set_point(&point, 20.0f, 160.0f);
+    ID2D1GeometrySink_BeginFigure(sink, point, D2D1_FIGURE_BEGIN_FILLED);
+    set_quadratic(&quadratic, 20.0f,  80.0f, 40.0f,  80.0f);
+    ID2D1GeometrySink_AddQuadraticBezier(sink, &quadratic);
+    set_quadratic(&quadratic, 60.0f,  80.0f, 60.0f, 160.0f);
+    ID2D1GeometrySink_AddQuadraticBezier(sink, &quadratic);
+    set_quadratic(&quadratic, 60.0f, 240.0f, 40.0f, 240.0f);
+    ID2D1GeometrySink_AddQuadraticBezier(sink, &quadratic);
+    set_quadratic(&quadratic, 20.0f, 240.0f, 20.0f, 160.0f);
+    ID2D1GeometrySink_AddQuadraticBezier(sink, &quadratic);
+    ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED);
+}
+
 static void test_path_geometry(void)
 {
     ID2D1GeometrySink *sink, *tmp_sink;
@@ -1227,6 +1458,68 @@ 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);
+    fill_geometry_sink_bezier(sink);
+    hr = ID2D1GeometrySink_Close(sink);
+    ok(SUCCEEDED(hr), "Failed to close geometry sink, hr %#x.\n", hr);
+    hr = ID2D1PathGeometry_GetFigureCount(geometry, &count);
+    ok(SUCCEEDED(hr), "Failed to get figure count, hr %#x.\n", hr);
+    ok(count == 2, "Got unexpected figure count %u.\n", count);
+    hr = ID2D1PathGeometry_GetSegmentCount(geometry, &count);
+    ok(SUCCEEDED(hr), "Failed to get segment count, hr %#x.\n", hr);
+    ok(count == 10, "Got unexpected segment count %u.\n", count);
+    ID2D1GeometrySink_Release(sink);
+
+    ID2D1RenderTarget_BeginDraw(rt);
+    ID2D1RenderTarget_Clear(rt, &color);
+    ID2D1RenderTarget_FillGeometry(rt, (ID2D1Geometry *)geometry, (ID2D1Brush *)brush, NULL);
+    hr = ID2D1RenderTarget_EndDraw(rt, NULL, NULL);
+    ok(SUCCEEDED(hr), "Failed to end draw, hr %#x.\n", hr);
+    match = compare_figure(surface, 0, 0, 160, 160, 0xff652e89, 64,
+            "7xoCngECngECngECngECngECngECngECnQEEnAEEnAEEnAEEnAEEmwEGmgEGmgEGmgEGmQEImAEI"
+            "lAEECASLAQgKCIEBDQoMew8KD3YQDBByEgwSbhMOEmwUDhRpFBAUZxUQFWUVEhVjFhIWYRYUFl8X"
+            "FBddFxYWXRYYFlsXGBdaFhoWWRYcFlgVHhVXFSAVVhQiFFUUIxRVEyYTVBIoElQRKhFUECwQUxAu"
+            "EFIOMg5SDTQNUgs4C1IJPAlRCEAIUAZEBlAESARQAU4BTgJQAkgGUAY/C1ALMhNQEyoTUBMyC1AL"
+            "PwZQBkgCUAJOAU4BUARIBFAGRAZQCEAIUQk8CVILOAtSDTQNUg4yDlIQLhBTECwQVBEqEVQSKBJU"
+            "EyYTVBQjFFYUIhRWFSAVVxUeFVgWHBZZFhoWWhcYF1sWGBZcFxYWXhcUF18WFBZhFhIWYxUSFWUV"
+            "EBVnFBAUaRQOFGsTDhJvEgwSchAMEHYPCg96DQoMggEICgiLAQQIBJQBCJgBCJkBBpoBBpoBBpoB"
+            "BpsBBJwBBJwBBJwBBJwBBJ0BAp4BAp4BAp4BAp4BAp4BAp4BAp4BAgAA");
+    todo_wine ok(match, "Figure 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);
+    fill_geometry_sink_bezier(sink);
+    ID2D1GeometrySink_SetFillMode(sink, D2D1_FILL_MODE_WINDING);
+    hr = ID2D1GeometrySink_Close(sink);
+    ok(SUCCEEDED(hr), "Failed to close geometry sink, hr %#x.\n", hr);
+    hr = ID2D1PathGeometry_GetFigureCount(geometry, &count);
+    ok(SUCCEEDED(hr), "Failed to get figure count, hr %#x.\n", hr);
+    ok(count == 2, "Got unexpected figure count %u.\n", count);
+    hr = ID2D1PathGeometry_GetSegmentCount(geometry, &count);
+    ok(SUCCEEDED(hr), "Failed to get segment count, hr %#x.\n", hr);
+    ok(count == 10, "Got unexpected segment count %u.\n", count);
+    ID2D1GeometrySink_Release(sink);
+
+    ID2D1RenderTarget_BeginDraw(rt);
+    ID2D1RenderTarget_Clear(rt, &color);
+    ID2D1RenderTarget_FillGeometry(rt, (ID2D1Geometry *)geometry, (ID2D1Brush *)brush, NULL);
+    hr = ID2D1RenderTarget_EndDraw(rt, NULL, NULL);
+    ok(SUCCEEDED(hr), "Failed to end draw, hr %#x.\n", hr);
+    match = compare_figure(surface, 0, 0, 160, 160, 0xff652e89, 64,
+            "7xoCngECngECngECngECngECngECngECnQEEnAEEnAEEnAEEnAEEmwEGmgEGmgEGmgEGmQEImAEI"
+            "lAEQiwEagQEjeyh2LHIwbjNsNmk4ZzplPGM+YUBfQl1DXURbRlpGWUhYSFdKVkpVS1VMVExUTFRM"
+            "U05STlJOUk5STlFQUFBQUFBQTlRIXD9mMnYqdjJmP1xIVE5QUFBQUFBQUU5STlJOUk5STlNMVExU"
+            "TFRMVEtWSlZKV0hYSFlGWkZbRFxDXkJfQGE+YzxlOmc4aTZrM28wcix2KHojggEaiwEQlAEImAEI"
+            "mQEGmgEGmgEGmgEGmwEEnAEEnAEEnAEEnAEEnQECngECngECngECngECngECngECngEC");
+    ok(match, "Figure does not match.\n");
+    ID2D1PathGeometry_Release(geometry);
+
     ID2D1SolidColorBrush_Release(brush);
     ID2D1RenderTarget_Release(rt);
     refcount = ID2D1Factory_Release(factory);
-- 
2.1.4




More information about the wine-patches mailing list