Signed-off-by: Nikolay Sivov nsivov@codeweavers.com
-- v2: d2d1: Add a stub for geometry realization object. d2d1: Implement mesh population methods.
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/d2d1/d2d1_private.h | 13 +++++ dlls/d2d1/mesh.c | 101 ++++++++++++++++++++++++++++++++++++++- dlls/d2d1/tests/d2d1.c | 77 +++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+), 2 deletions(-)
diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h index 46999eaa272..e910b19db28 100644 --- a/dlls/d2d1/d2d1_private.h +++ b/dlls/d2d1/d2d1_private.h @@ -410,12 +410,25 @@ struct d2d_layer
HRESULT d2d_layer_create(ID2D1Factory *factory, const D2D1_SIZE_F *size, struct d2d_layer **layer);
+enum d2d_mesh_state +{ + D2D_MESH_STATE_INITIAL = 0, + D2D_MESH_STATE_OPEN, + D2D_MESH_STATE_CLOSED, +}; + struct d2d_mesh { ID2D1Mesh ID2D1Mesh_iface; + ID2D1TessellationSink ID2D1TessellationSink_iface; LONG refcount;
ID2D1Factory *factory; + enum d2d_mesh_state state; + + D2D1_TRIANGLE *triangles; + size_t count; + size_t size; };
HRESULT d2d_mesh_create(ID2D1Factory *factory, struct d2d_mesh **mesh); diff --git a/dlls/d2d1/mesh.c b/dlls/d2d1/mesh.c index cd6a5d13a89..a782e01a551 100644 --- a/dlls/d2d1/mesh.c +++ b/dlls/d2d1/mesh.c @@ -25,6 +25,11 @@ static inline struct d2d_mesh *impl_from_ID2D1Mesh(ID2D1Mesh *iface) return CONTAINING_RECORD(iface, struct d2d_mesh, ID2D1Mesh_iface); }
+static inline struct d2d_mesh *impl_from_ID2D1TessellationSink(ID2D1TessellationSink *iface) +{ + return CONTAINING_RECORD(iface, struct d2d_mesh, ID2D1TessellationSink_iface); +} + static HRESULT STDMETHODCALLTYPE d2d_mesh_QueryInterface(ID2D1Mesh *iface, REFIID iid, void **out) { TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); @@ -64,6 +69,7 @@ static ULONG STDMETHODCALLTYPE d2d_mesh_Release(ID2D1Mesh *iface) if (!refcount) { ID2D1Factory_Release(mesh->factory); + free(mesh->triangles); free(mesh); }
@@ -81,9 +87,21 @@ static void STDMETHODCALLTYPE d2d_mesh_GetFactory(ID2D1Mesh *iface, ID2D1Factory
static HRESULT STDMETHODCALLTYPE d2d_mesh_Open(ID2D1Mesh *iface, ID2D1TessellationSink **sink) { - FIXME("iface %p, sink %p stub!\n", iface, sink); + struct d2d_mesh *mesh = impl_from_ID2D1Mesh(iface); + + TRACE("iface %p, sink %p.\n", iface, sink); + + *sink = NULL; + + if (mesh->state != D2D_MESH_STATE_INITIAL) + return D2DERR_WRONG_STATE;
- return E_NOTIMPL; + *sink = &mesh->ID2D1TessellationSink_iface; + ID2D1TessellationSink_AddRef(*sink); + + mesh->state = D2D_MESH_STATE_OPEN; + + return S_OK; }
static const struct ID2D1MeshVtbl d2d_mesh_vtbl = @@ -95,12 +113,91 @@ static const struct ID2D1MeshVtbl d2d_mesh_vtbl = d2d_mesh_Open, };
+static HRESULT STDMETHODCALLTYPE d2d_mesh_sink_QueryInterface(ID2D1TessellationSink *iface, + REFIID iid, void **out) +{ + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_ID2D1TessellationSink) + || IsEqualGUID(iid, &IID_IUnknown)) + { + ID2D1TessellationSink_AddRef(iface); + *out = iface; + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG STDMETHODCALLTYPE d2d_mesh_sink_AddRef(ID2D1TessellationSink *iface) +{ + struct d2d_mesh *mesh = impl_from_ID2D1TessellationSink(iface); + + TRACE("iface %p.\n", iface); + + return ID2D1Mesh_AddRef(&mesh->ID2D1Mesh_iface); +} + +static ULONG STDMETHODCALLTYPE d2d_mesh_sink_Release(ID2D1TessellationSink *iface) +{ + struct d2d_mesh *mesh = impl_from_ID2D1TessellationSink(iface); + + TRACE("iface %p.\n", iface); + + return ID2D1Mesh_Release(&mesh->ID2D1Mesh_iface); +} + +static void STDMETHODCALLTYPE d2d_mesh_sink_AddTriangles(ID2D1TessellationSink *iface, + const D2D1_TRIANGLE *triangles, UINT32 count) +{ + struct d2d_mesh *mesh = impl_from_ID2D1TessellationSink(iface); + + TRACE("iface %p, triangles %p, count %u.\n", iface, triangles, count); + + if (!d2d_array_reserve((void **)&mesh->triangles, &mesh->size, + mesh->count + count, sizeof(*mesh->triangles))) + { + WARN("Failed to grow mesh triangles array.\n"); + return; + } + + memcpy(&mesh->triangles[mesh->count], triangles, count * sizeof(*triangles)); + mesh->count += count; +} + +static HRESULT STDMETHODCALLTYPE d2d_mesh_sink_Close(ID2D1TessellationSink *iface) +{ + struct d2d_mesh *mesh = impl_from_ID2D1TessellationSink(iface); + + TRACE("iface %p.\n", iface); + + if (mesh->state != D2D_MESH_STATE_OPEN) + return D2DERR_WRONG_STATE; + + mesh->state = D2D_MESH_STATE_CLOSED; + + return S_OK; +} + +static const ID2D1TessellationSinkVtbl d2d_mesh_sink_vtbl = +{ + d2d_mesh_sink_QueryInterface, + d2d_mesh_sink_AddRef, + d2d_mesh_sink_Release, + d2d_mesh_sink_AddTriangles, + d2d_mesh_sink_Close, +}; + HRESULT d2d_mesh_create(ID2D1Factory *factory, struct d2d_mesh **mesh) { if (!(*mesh = calloc(1, sizeof(**mesh)))) return E_OUTOFMEMORY;
(*mesh)->ID2D1Mesh_iface.lpVtbl = &d2d_mesh_vtbl; + (*mesh)->ID2D1TessellationSink_iface.lpVtbl = &d2d_mesh_sink_vtbl; (*mesh)->refcount = 1; ID2D1Factory_AddRef((*mesh)->factory = factory);
diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c index 12c199e2377..5e8cbed748e 100644 --- a/dlls/d2d1/tests/d2d1.c +++ b/dlls/d2d1/tests/d2d1.c @@ -15913,6 +15913,82 @@ static void test_no_target(BOOL d3d11) release_test_context(&ctx); }
+static void test_mesh(BOOL d3d11) +{ + ID2D1TessellationSink *sink, *sink2; + struct d2d1_test_context ctx; + D2D1_TRIANGLE triangles[2]; + ID2D1RenderTarget *rt; + ID2D1Factory *factory; + ID2D1Mesh *mesh; + HRESULT hr; + + if (!init_test_context(&ctx, d3d11)) + return; + + rt = ctx.rt; + hr = ID2D1RenderTarget_CreateMesh(rt, &mesh); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + check_interface(mesh, &IID_IUnknown, TRUE); + check_interface(mesh, &IID_ID2D1Resource, TRUE); + check_interface(mesh, &IID_ID2D1Mesh, TRUE); + check_interface(mesh, &IID_ID2D1TessellationSink, FALSE); + + ID2D1Mesh_GetFactory(mesh, &factory); + ok(ctx.factory == factory, "Unexpected factory.\n"); + ID2D1Factory_Release(factory); + + hr = ID2D1Mesh_Open(mesh, &sink); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + check_interface(sink, &IID_IUnknown, TRUE); + check_interface(sink, &IID_ID2D1TessellationSink, TRUE); + check_interface(sink, &IID_ID2D1Mesh, FALSE); + + sink2 = (void *)0xdeadbeef; + hr = ID2D1Mesh_Open(mesh, &sink2); + ok(hr == D2DERR_WRONG_STATE, "Got unexpected hr %#lx.\n", hr); + ok(!sink2, "Unexpected pointer %p.\n", sink2); + + /* Close empty sink */ + hr = ID2D1TessellationSink_Close(sink); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = ID2D1TessellationSink_Close(sink); + ok(hr == D2DERR_WRONG_STATE, "Got unexpected hr %#lx.\n", hr); + + ID2D1TessellationSink_Release(sink); + + hr = ID2D1Mesh_Open(mesh, &sink); + ok(hr == D2DERR_WRONG_STATE, "Got unexpected hr %#lx.\n", hr); + + ID2D1Mesh_Release(mesh); + + /* Add some triangles */ + hr = ID2D1RenderTarget_CreateMesh(rt, &mesh); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = ID2D1Mesh_Open(mesh, &sink); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + set_point(&triangles[0].point1, 0.0f, 0.0f); + set_point(&triangles[0].point2, 10.0f, 20.0f); + set_point(&triangles[0].point3, 40.0f, -20.0f); + set_point(&triangles[1].point1, -100.0f, 0.0f); + set_point(&triangles[1].point2, 30.0f, 200.0f); + set_point(&triangles[1].point3, 50.0f, -200.0f); + ID2D1TessellationSink_AddTriangles(sink, triangles, 2); + hr = ID2D1TessellationSink_Close(sink); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + ID2D1TessellationSink_Release(sink); + + ID2D1Mesh_Release(mesh); + + release_test_context(&ctx); +} + START_TEST(d2d1) { HMODULE d2d1_dll = GetModuleHandleA("d2d1.dll"); @@ -16012,6 +16088,7 @@ START_TEST(d2d1) queue_test(test_effect_blob_property); queue_test(test_get_dxgi_device); queue_test(test_no_target); + queue_test(test_mesh);
run_queued_tests(); }
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/d2d1/d2d1_private.h | 17 ++++++++ dlls/d2d1/device.c | 66 +++++++++++++++++++++++++--- dlls/d2d1/geometry.c | 92 ++++++++++++++++++++++++++++++++++++++++ dlls/d2d1/tests/d2d1.c | 56 ++++++++++++++++++++++++ 4 files changed, 226 insertions(+), 5 deletions(-)
diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h index e910b19db28..e99dc165bac 100644 --- a/dlls/d2d1/d2d1_private.h +++ b/dlls/d2d1/d2d1_private.h @@ -620,6 +620,23 @@ HRESULT d2d_geometry_group_init(struct d2d_geometry *geometry, ID2D1Factory *fac D2D1_FILL_MODE fill_mode, ID2D1Geometry **src_geometries, unsigned int geometry_count); struct d2d_geometry *unsafe_impl_from_ID2D1Geometry(ID2D1Geometry *iface);
+struct d2d_geometry_realization +{ + ID2D1GeometryRealization ID2D1GeometryRealization_iface; + LONG refcount; + + ID2D1Factory *factory; + ID2D1Geometry *geometry; + bool filled; + + ID2D1StrokeStyle *stroke_style; + float stroke_width; +}; + +HRESULT d2d_geometry_realization_init(struct d2d_geometry_realization *realization, + ID2D1Factory *factory, ID2D1Geometry *geometry); +struct d2d_geometry_realization *unsafe_impl_from_ID2D1GeometryRealization(ID2D1GeometryRealization *iface); + struct d2d_device { ID2D1Device6 ID2D1Device6_iface; diff --git a/dlls/d2d1/device.c b/dlls/d2d1/device.c index 59e93bfac38..3969046d5b3 100644 --- a/dlls/d2d1/device.c +++ b/dlls/d2d1/device.c @@ -2749,26 +2749,82 @@ static void STDMETHODCALLTYPE d2d_device_context_ID2D1DeviceContext_FillOpacityM static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateFilledGeometryRealization(ID2D1DeviceContext6 *iface, ID2D1Geometry *geometry, float tolerance, ID2D1GeometryRealization **realization) { - FIXME("iface %p, geometry %p, tolerance %.8e, realization %p stub!\n", iface, geometry, tolerance, + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + struct d2d_geometry_realization *object; + HRESULT hr; + + TRACE("iface %p, geometry %p, tolerance %.8e, realization %p.\n", iface, geometry, tolerance, realization);
- return E_NOTIMPL; + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = d2d_geometry_realization_init(object, context->factory, geometry))) + { + WARN("Failed to initialise geometry realization, hr %#lx.\n", hr); + free(object); + return hr; + } + object->filled = true; + + TRACE("Created geometry realization %p.\n", object); + *realization = &object->ID2D1GeometryRealization_iface; + + return S_OK; }
static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateStrokedGeometryRealization( ID2D1DeviceContext6 *iface, ID2D1Geometry *geometry, float tolerance, float stroke_width, ID2D1StrokeStyle *stroke_style, ID2D1GeometryRealization **realization) { - FIXME("iface %p, geometry %p, tolerance %.8e, stroke_width %.8e, stroke_style %p, realization %p stub!\n", + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + struct d2d_geometry_realization *object; + HRESULT hr; + + TRACE("iface %p, geometry %p, tolerance %.8e, stroke_width %.8e, stroke_style %p, realization %p.\n", iface, geometry, tolerance, stroke_width, stroke_style, realization);
- return E_NOTIMPL; + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = d2d_geometry_realization_init(object, context->factory, geometry))) + { + WARN("Failed to initialise geometry realization, hr %#lx.\n", hr); + free(object); + return hr; + } + object->stroke_width = stroke_width; + object->stroke_style = stroke_style; + if (object->stroke_style) + ID2D1StrokeStyle_AddRef(object->stroke_style); + + TRACE("Created geometry realization %p.\n", object); + *realization = &object->ID2D1GeometryRealization_iface; + + return S_OK; }
static void STDMETHODCALLTYPE d2d_device_context_DrawGeometryRealization(ID2D1DeviceContext6 *iface, ID2D1GeometryRealization *realization, ID2D1Brush *brush) { - FIXME("iface %p, realization %p, brush %p stub!\n", iface, realization, brush); + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + struct d2d_geometry_realization *r = unsafe_impl_from_ID2D1GeometryRealization(realization); + + FIXME("iface %p, realization %p, brush %p semi-stub!\n", iface, realization, brush); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + { + if (r->filled) + { + d2d_command_list_fill_geometry(context->target.command_list, context, r->geometry, brush, NULL); + } + else + { + d2d_command_list_draw_geometry(context->target.command_list, context, r->geometry, brush, + r->stroke_width, r->stroke_style); + } + return; + } }
static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateInk(ID2D1DeviceContext6 *iface, diff --git a/dlls/d2d1/geometry.c b/dlls/d2d1/geometry.c index 97592ba6dd9..e9ed1e43ff5 100644 --- a/dlls/d2d1/geometry.c +++ b/dlls/d2d1/geometry.c @@ -5507,3 +5507,95 @@ struct d2d_geometry *unsafe_impl_from_ID2D1Geometry(ID2D1Geometry *iface) || iface->lpVtbl == (const ID2D1GeometryVtbl *)&d2d_geometry_group_vtbl); return CONTAINING_RECORD(iface, struct d2d_geometry, ID2D1Geometry_iface); } + +static inline struct d2d_geometry_realization *impl_from_ID2D1GeometryRealization( + ID2D1GeometryRealization *iface) +{ + return CONTAINING_RECORD(iface, struct d2d_geometry_realization, ID2D1GeometryRealization_iface); +} + +static HRESULT STDMETHODCALLTYPE d2d_geometry_realization_QueryInterface( + ID2D1GeometryRealization *iface, REFIID iid, void **out) +{ + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_ID2D1GeometryRealization) + || IsEqualGUID(iid, &IID_ID2D1Resource) + || IsEqualGUID(iid, &IID_IUnknown)) + { + ID2D1GeometryRealization_AddRef(iface); + *out = iface; + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG STDMETHODCALLTYPE d2d_geometry_realization_AddRef(ID2D1GeometryRealization *iface) +{ + struct d2d_geometry_realization *realization = impl_from_ID2D1GeometryRealization(iface); + ULONG refcount = InterlockedIncrement(&realization->refcount); + + TRACE("%p increasing refcount to %lu.\n", iface, refcount); + + return refcount; +} + +static ULONG STDMETHODCALLTYPE d2d_geometry_realization_Release(ID2D1GeometryRealization *iface) +{ + struct d2d_geometry_realization *realization = impl_from_ID2D1GeometryRealization(iface); + ULONG refcount = InterlockedDecrement(&realization->refcount); + + TRACE("%p decreasing refcount to %lu.\n", iface, refcount); + + if (!refcount) + { + if (realization->stroke_style) + ID2D1StrokeStyle_Release(realization->stroke_style); + ID2D1Geometry_Release(realization->geometry); + ID2D1Factory_Release(realization->factory); + free(realization); + } + + return refcount; +} + +static void STDMETHODCALLTYPE d2d_geometry_realization_GetFactory(ID2D1GeometryRealization *iface, + ID2D1Factory **factory) +{ + struct d2d_geometry_realization *realization = impl_from_ID2D1GeometryRealization(iface); + + TRACE("iface %p, factory %p.\n", iface, factory); + + ID2D1Factory_AddRef(*factory = realization->factory); +} + +static const ID2D1GeometryRealizationVtbl d2d_geometry_realization_vtbl = +{ + d2d_geometry_realization_QueryInterface, + d2d_geometry_realization_AddRef, + d2d_geometry_realization_Release, + d2d_geometry_realization_GetFactory, +}; + +HRESULT d2d_geometry_realization_init(struct d2d_geometry_realization *realization, + ID2D1Factory *factory, ID2D1Geometry *geometry) +{ + realization->ID2D1GeometryRealization_iface.lpVtbl = &d2d_geometry_realization_vtbl; + realization->refcount = 1; + ID2D1Factory_AddRef(realization->factory = factory); + ID2D1Geometry_AddRef(realization->geometry = geometry); + + return S_OK; +} + +struct d2d_geometry_realization *unsafe_impl_from_ID2D1GeometryRealization(ID2D1GeometryRealization *iface) +{ + if (!iface) + return NULL; + assert(iface->lpVtbl == &d2d_geometry_realization_vtbl); + return CONTAINING_RECORD(iface, struct d2d_geometry_realization, ID2D1GeometryRealization_iface); +} diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c index 5e8cbed748e..24829994e16 100644 --- a/dlls/d2d1/tests/d2d1.c +++ b/dlls/d2d1/tests/d2d1.c @@ -15989,6 +15989,61 @@ static void test_mesh(BOOL d3d11) release_test_context(&ctx); }
+static void test_geometry_realization(BOOL d3d11) +{ + ID2D1GeometryRealization *realization, *realization2; + ID2D1RectangleGeometry *rectangle_geometry; + ID2D1DeviceContext1 *device_context; + struct d2d1_test_context ctx; + ID2D1SolidColorBrush *brush; + D2D1_COLOR_F color; + D2D1_RECT_F rect; + HRESULT hr; + + if (!init_test_context(&ctx, d3d11)) + return; + + if (!ctx.factory2) + { + skip("Geometry realizations are not supported.\n"); + release_test_context(&ctx); + return; + } + + hr = ID2D1DeviceContext_QueryInterface(ctx.context, &IID_ID2D1DeviceContext1, (void **)&device_context); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + set_rect(&rect, -1.0f, -1.0f, 1.0f, 1.0f); + hr = ID2D1Factory_CreateRectangleGeometry(ctx.factory, &rect, &rectangle_geometry); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = ID2D1DeviceContext1_CreateFilledGeometryRealization(device_context, + (ID2D1Geometry *)rectangle_geometry, 10.0f, &realization); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = ID2D1DeviceContext1_CreateStrokedGeometryRealization(device_context, + (ID2D1Geometry *)rectangle_geometry, 0.0f, 2.0f, NULL, &realization2); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ID2D1RectangleGeometry_Release(rectangle_geometry); + + set_color(&color, 0.0f, 0.0f, 0.0f, 0.0f); + hr = ID2D1DeviceContext1_CreateSolidColorBrush(device_context, &color, NULL, &brush); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + ID2D1DeviceContext1_BeginDraw(device_context); + ID2D1DeviceContext1_DrawGeometryRealization(device_context, realization, (ID2D1Brush *)brush); + ID2D1DeviceContext1_DrawGeometryRealization(device_context, realization2, (ID2D1Brush *)brush); + hr = ID2D1DeviceContext1_EndDraw(device_context, NULL, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + ID2D1SolidColorBrush_Release(brush); + + ID2D1GeometryRealization_Release(realization); + ID2D1GeometryRealization_Release(realization2); + ID2D1DeviceContext1_Release(device_context); + release_test_context(&ctx); +} + START_TEST(d2d1) { HMODULE d2d1_dll = GetModuleHandleA("d2d1.dll"); @@ -16089,6 +16144,7 @@ START_TEST(d2d1) queue_test(test_get_dxgi_device); queue_test(test_no_target); queue_test(test_mesh); + queue_test(test_geometry_realization);
run_queued_tests(); }