From: Dmitry Timoshkov dmitry@baikal.ru
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru Signed-off-by: Nikolay Sivov nsivov@codeweavers.com
---
v3: Add missing AddRef() to ::SetImage(). Thanks Nikolay. v4: Fix a typo in ::GetImage implementation. v5: Specify correct brush type, add a FIXME for D2D1_IMAGE_BRUSH_PROPERTIES.sourceRectangle, drop d2d_brush_bind_image() helper. Thanks Henri. v6: Add some tests. v7: Image brush description is a required argument
dlls/d2d1/brush.c | 243 ++++++++++++++++++++++++++++++++++++++- dlls/d2d1/d2d1_private.h | 12 ++ dlls/d2d1/device.c | 14 ++- 3 files changed, 265 insertions(+), 4 deletions(-)
diff --git a/dlls/d2d1/brush.c b/dlls/d2d1/brush.c index 14d9cb54e92..b9a673fe54c 100644 --- a/dlls/d2d1/brush.c +++ b/dlls/d2d1/brush.c @@ -1101,6 +1101,246 @@ HRESULT d2d_bitmap_brush_create(ID2D1Factory *factory, ID2D1Bitmap *bitmap, return S_OK; }
+static inline struct d2d_brush *impl_from_ID2D1ImageBrush(ID2D1ImageBrush *iface) +{ + return CONTAINING_RECORD((ID2D1Brush *)iface, struct d2d_brush, ID2D1Brush_iface); +} + +static HRESULT STDMETHODCALLTYPE d2d_image_brush_QueryInterface(ID2D1ImageBrush *iface, + REFIID iid, void **out) +{ + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_ID2D1ImageBrush) + || IsEqualGUID(iid, &IID_ID2D1Brush) + || IsEqualGUID(iid, &IID_ID2D1Resource) + || IsEqualGUID(iid, &IID_IUnknown)) + { + ID2D1ImageBrush_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_image_brush_AddRef(ID2D1ImageBrush *iface) +{ + struct d2d_brush *brush = impl_from_ID2D1ImageBrush(iface); + ULONG refcount = InterlockedIncrement(&brush->refcount); + + TRACE("%p increasing refcount to %lu.\n", iface, refcount); + + return refcount; +} + +static ULONG STDMETHODCALLTYPE d2d_image_brush_Release(ID2D1ImageBrush *iface) +{ + struct d2d_brush *brush = impl_from_ID2D1ImageBrush(iface); + ULONG refcount = InterlockedDecrement(&brush->refcount); + + TRACE("%p decreasing refcount to %lu.\n", iface, refcount); + + if (!refcount) + { + if (brush->u.image.image) + ID2D1Image_Release(brush->u.image.image); + d2d_brush_destroy(brush); + } + + return refcount; +} + +static void STDMETHODCALLTYPE d2d_image_brush_GetFactory(ID2D1ImageBrush *iface, + ID2D1Factory **factory) +{ + struct d2d_brush *brush = impl_from_ID2D1ImageBrush(iface); + + TRACE("iface %p, factory %p.\n", iface, factory); + + ID2D1Factory_AddRef(*factory = brush->factory); +} + +static void STDMETHODCALLTYPE d2d_image_brush_SetOpacity(ID2D1ImageBrush *iface, float opacity) +{ + struct d2d_brush *brush = impl_from_ID2D1ImageBrush(iface); + + TRACE("iface %p, opacity %.8e.\n", iface, opacity); + + brush->opacity = opacity; +} + +static void STDMETHODCALLTYPE d2d_image_brush_SetTransform(ID2D1ImageBrush *iface, + const D2D1_MATRIX_3X2_F *transform) +{ + struct d2d_brush *brush = impl_from_ID2D1ImageBrush(iface); + + TRACE("iface %p, transform %p.\n", iface, transform); + + brush->transform = *transform; +} + +static float STDMETHODCALLTYPE d2d_image_brush_GetOpacity(ID2D1ImageBrush *iface) +{ + struct d2d_brush *brush = impl_from_ID2D1ImageBrush(iface); + + TRACE("iface %p.\n", iface); + + return brush->opacity; +} + +static void STDMETHODCALLTYPE d2d_image_brush_GetTransform(ID2D1ImageBrush *iface, + D2D1_MATRIX_3X2_F *transform) +{ + struct d2d_brush *brush = impl_from_ID2D1ImageBrush(iface); + + TRACE("iface %p, transform %p.\n", iface, transform); + + *transform = brush->transform; +} + +static void STDMETHODCALLTYPE d2d_image_brush_SetImage(ID2D1ImageBrush *iface, ID2D1Image *image) +{ + struct d2d_brush *brush = impl_from_ID2D1ImageBrush(iface); + + TRACE("iface %p, image %p.\n", iface, image); + + if (image) + ID2D1Image_AddRef(image); + if (brush->u.image.image) + ID2D1Image_Release(brush->u.image.image); + brush->u.image.image = image; +} + +static void STDMETHODCALLTYPE d2d_image_brush_SetExtendModeX(ID2D1ImageBrush *iface, D2D1_EXTEND_MODE mode) +{ + struct d2d_brush *brush = impl_from_ID2D1ImageBrush(iface); + + TRACE("iface %p, mode %#x.\n", iface, mode); + + brush->u.image.extend_mode_x = mode; +} + +static void STDMETHODCALLTYPE d2d_image_brush_SetExtendModeY(ID2D1ImageBrush *iface, D2D1_EXTEND_MODE mode) +{ + struct d2d_brush *brush = impl_from_ID2D1ImageBrush(iface); + + TRACE("iface %p, mode %#x.\n", iface, mode); + + brush->u.image.extend_mode_y = mode; +} + +static void STDMETHODCALLTYPE d2d_image_brush_SetInterpolationMode(ID2D1ImageBrush *iface, + D2D1_INTERPOLATION_MODE mode) +{ + struct d2d_brush *brush = impl_from_ID2D1ImageBrush(iface); + + TRACE("iface %p, mode %#x.\n", iface, mode); + + brush->u.image.interpolation_mode = mode; +} + +static void STDMETHODCALLTYPE d2d_image_brush_SetSourceRectangle(ID2D1ImageBrush *iface, const D2D1_RECT_F *rect) +{ + struct d2d_brush *brush = impl_from_ID2D1ImageBrush(iface); + + TRACE("iface %p, rect %s.\n", iface, debug_d2d_rect_f(rect)); + + brush->u.image.source_rect = *rect; +} + +static void STDMETHODCALLTYPE d2d_image_brush_GetImage(ID2D1ImageBrush *iface, ID2D1Image **image) +{ + struct d2d_brush *brush = impl_from_ID2D1ImageBrush(iface); + + TRACE("iface %p, image %p.\n", iface, image); + + if ((*image = (ID2D1Image *)brush->u.image.image)) + ID2D1Image_AddRef(*image); +} + +static D2D1_EXTEND_MODE STDMETHODCALLTYPE d2d_image_brush_GetExtendModeX(ID2D1ImageBrush *iface) +{ + struct d2d_brush *brush = impl_from_ID2D1ImageBrush(iface); + + TRACE("iface %p.\n", iface); + + return brush->u.image.extend_mode_x; +} + +static D2D1_EXTEND_MODE STDMETHODCALLTYPE d2d_image_brush_GetExtendModeY(ID2D1ImageBrush *iface) +{ + struct d2d_brush *brush = impl_from_ID2D1ImageBrush(iface); + + TRACE("iface %p.\n", iface); + + return brush->u.image.extend_mode_y; +} + +static D2D1_INTERPOLATION_MODE STDMETHODCALLTYPE d2d_image_brush_GetInterpolationMode(ID2D1ImageBrush *iface) +{ + struct d2d_brush *brush = impl_from_ID2D1ImageBrush(iface); + + TRACE("iface %p.\n", iface); + + return brush->u.image.interpolation_mode; +} + +static void STDMETHODCALLTYPE d2d_image_brush_GetSourceRectangle(ID2D1ImageBrush *iface, D2D1_RECT_F *rect) +{ + struct d2d_brush *brush = impl_from_ID2D1ImageBrush(iface); + + TRACE("iface %p, rect %p.\n", iface, rect); + + *rect = brush->u.image.source_rect; +} + +static const struct ID2D1ImageBrushVtbl d2d_image_brush_vtbl = +{ + d2d_image_brush_QueryInterface, + d2d_image_brush_AddRef, + d2d_image_brush_Release, + d2d_image_brush_GetFactory, + d2d_image_brush_SetOpacity, + d2d_image_brush_SetTransform, + d2d_image_brush_GetOpacity, + d2d_image_brush_GetTransform, + d2d_image_brush_SetImage, + d2d_image_brush_SetExtendModeX, + d2d_image_brush_SetExtendModeY, + d2d_image_brush_SetInterpolationMode, + d2d_image_brush_SetSourceRectangle, + d2d_image_brush_GetImage, + d2d_image_brush_GetExtendModeX, + d2d_image_brush_GetExtendModeY, + d2d_image_brush_GetInterpolationMode, + d2d_image_brush_GetSourceRectangle +}; + +HRESULT d2d_image_brush_create(ID2D1Factory *factory, ID2D1Image *image, + const D2D1_IMAGE_BRUSH_PROPERTIES *image_brush_desc, const D2D1_BRUSH_PROPERTIES *brush_desc, + struct d2d_brush **brush) +{ + if (!(*brush = heap_alloc_zero(sizeof(**brush)))) + return E_OUTOFMEMORY; + + d2d_brush_init(*brush, factory, D2D_BRUSH_TYPE_IMAGE, + brush_desc, (ID2D1BrushVtbl *)&d2d_image_brush_vtbl); + if (((*brush)->u.image.image = image)) + ID2D1Image_AddRef((*brush)->u.image.image); + + (*brush)->u.image.source_rect = image_brush_desc->sourceRectangle; + (*brush)->u.image.extend_mode_x = image_brush_desc->extendModeX; + (*brush)->u.image.extend_mode_y = image_brush_desc->extendModeY; + (*brush)->u.image.interpolation_mode = image_brush_desc->interpolationMode; + + TRACE("Created brush %p.\n", *brush); + return S_OK; +} + struct d2d_brush *unsafe_impl_from_ID2D1Brush(ID2D1Brush *iface) { if (!iface) @@ -1108,7 +1348,8 @@ struct d2d_brush *unsafe_impl_from_ID2D1Brush(ID2D1Brush *iface) assert(iface->lpVtbl == (const ID2D1BrushVtbl *)&d2d_solid_color_brush_vtbl || iface->lpVtbl == (const ID2D1BrushVtbl *)&d2d_linear_gradient_brush_vtbl || iface->lpVtbl == (const ID2D1BrushVtbl *)&d2d_radial_gradient_brush_vtbl - || iface->lpVtbl == (const ID2D1BrushVtbl *)&d2d_bitmap_brush_vtbl); + || iface->lpVtbl == (const ID2D1BrushVtbl *)&d2d_bitmap_brush_vtbl + || iface->lpVtbl == (const ID2D1BrushVtbl *)&d2d_image_brush_vtbl); return CONTAINING_RECORD(iface, struct d2d_brush, ID2D1Brush_iface); }
diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h index b7e1a12b3b8..29469f5142e 100644 --- a/dlls/d2d1/d2d1_private.h +++ b/dlls/d2d1/d2d1_private.h @@ -42,6 +42,7 @@ enum d2d_brush_type D2D_BRUSH_TYPE_LINEAR, D2D_BRUSH_TYPE_RADIAL, D2D_BRUSH_TYPE_BITMAP, + D2D_BRUSH_TYPE_IMAGE, D2D_BRUSH_TYPE_COUNT, };
@@ -319,6 +320,14 @@ struct d2d_brush D2D1_EXTEND_MODE extend_mode_y; D2D1_INTERPOLATION_MODE interpolation_mode; } bitmap; + struct + { + ID2D1Image *image; + D2D1_EXTEND_MODE extend_mode_x; + D2D1_EXTEND_MODE extend_mode_y; + D2D1_INTERPOLATION_MODE interpolation_mode; + D2D1_RECT_F source_rect; + } image; } u; };
@@ -333,6 +342,9 @@ HRESULT d2d_radial_gradient_brush_create(ID2D1Factory *factory, HRESULT d2d_bitmap_brush_create(ID2D1Factory *factory, ID2D1Bitmap *bitmap, const D2D1_BITMAP_BRUSH_PROPERTIES1 *bitmap_brush_desc, const D2D1_BRUSH_PROPERTIES *brush_desc, struct d2d_brush **brush) DECLSPEC_HIDDEN; +HRESULT d2d_image_brush_create(ID2D1Factory *factory, ID2D1Image *image, + const D2D1_IMAGE_BRUSH_PROPERTIES *image_brush_desc, const D2D1_BRUSH_PROPERTIES *brush_desc, + struct d2d_brush **brush) DECLSPEC_HIDDEN; void d2d_brush_bind_resources(struct d2d_brush *brush, struct d2d_device_context *context, unsigned int brush_idx) DECLSPEC_HIDDEN; BOOL d2d_brush_fill_cb(const struct d2d_brush *brush, struct d2d_brush_cb *cb) DECLSPEC_HIDDEN; diff --git a/dlls/d2d1/device.c b/dlls/d2d1/device.c index 80badcddaac..eb0c1935ebb 100644 --- a/dlls/d2d1/device.c +++ b/dlls/d2d1/device.c @@ -1923,10 +1923,18 @@ static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateImageBrush(ID2D1Device ID2D1Image *image, const D2D1_IMAGE_BRUSH_PROPERTIES *image_brush_desc, const D2D1_BRUSH_PROPERTIES *brush_desc, ID2D1ImageBrush **brush) { - FIXME("iface %p, image %p, image_brush_desc %p, brush_desc %p, brush %p stub!\n", - iface, image, image_brush_desc, brush_desc, brush); + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + struct d2d_brush *object; + HRESULT hr;
- return E_NOTIMPL; + TRACE("iface %p, image %p, image_brush_desc %p, brush_desc %p, brush %p.\n", iface, image, image_brush_desc, + brush_desc, brush); + + if (SUCCEEDED(hr = d2d_image_brush_create(context->factory, image, image_brush_desc, + brush_desc, &object))) + *brush = (ID2D1ImageBrush *)&object->ID2D1Brush_iface; + + return hr; }
static HRESULT STDMETHODCALLTYPE d2d_device_context_ID2D1DeviceContext_CreateBitmapBrush(ID2D1DeviceContext *iface,
From: Dmitry Timoshkov dmitry@baikal.ru
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/d2d1/tests/d2d1.c | 115 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+)
diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c index ca80a8e946b..035915491fd 100644 --- a/dlls/d2d1/tests/d2d1.c +++ b/dlls/d2d1/tests/d2d1.c @@ -2291,6 +2291,120 @@ static void test_bitmap_brush(BOOL d3d11) release_test_context(&ctx); }
+static void test_image_brush(BOOL d3d11) +{ + D2D1_IMAGE_BRUSH_PROPERTIES image_brush_desc; + D2D1_INTERPOLATION_MODE interp_mode; + ID2D1DeviceContext *device_context; + D2D1_BITMAP_PROPERTIES bitmap_desc; + D2D1_BRUSH_PROPERTIES brush_desc; + ID2D1Image *image, *tmp_image; + struct d2d1_test_context ctx; + D2D1_EXTEND_MODE extend_mode; + D2D1_MATRIX_3X2_F matrix; + ID2D1ImageBrush *brush; + ID2D1Bitmap *bitmap; + D2D1_SIZE_U size; + D2D1_RECT_F rect; + ULONG refcount; + float opacity; + HRESULT hr; + BOOL match; + + static const DWORD bitmap_data[] = + { + 0xffff0000, 0xffffff00, 0xff00ff00, 0xff00ffff, + 0xff0000ff, 0xffff00ff, 0xff000000, 0xff7f7f7f, + 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, + 0xffffffff, 0xff000000, 0xff000000, 0xff000000, + }; + static const D2D1_MATRIX_3X2_F identity = + {{{ + 1.0f, 0.0f, + 0.0f, 1.0f, + 0.0f, 0.0f, + }}}; + + if (!init_test_context(&ctx, d3d11)) + return; + + hr = ID2D1RenderTarget_QueryInterface(ctx.rt, &IID_ID2D1DeviceContext, (void **)&device_context); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + set_size_u(&size, 4, 4); + bitmap_desc.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM; + bitmap_desc.pixelFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE; + bitmap_desc.dpiX = 96.0f; + bitmap_desc.dpiY = 96.0f; + hr = ID2D1RenderTarget_CreateBitmap(ctx.rt, size, bitmap_data, 4 * sizeof(*bitmap_data), &bitmap_desc, &bitmap); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = ID2D1Bitmap_QueryInterface(bitmap, &IID_ID2D1Image, (void **)&image); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + /* Only image brush description is required. */ + set_rect(&image_brush_desc.sourceRectangle, 1.0f, 2.0f, 3.0f, 4.0f); + image_brush_desc.extendModeX = D2D1_EXTEND_MODE_WRAP; + image_brush_desc.extendModeY = D2D1_EXTEND_MODE_MIRROR; + image_brush_desc.interpolationMode = D2D1_INTERPOLATION_MODE_LINEAR; + + hr = ID2D1DeviceContext_CreateImageBrush(device_context, NULL, &image_brush_desc, NULL, &brush); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + tmp_image = (void *)0xdeadbeef; + ID2D1ImageBrush_GetImage(brush, &tmp_image); + ok(!tmp_image, "Unexpected image %p.\n", image); + + opacity = ID2D1ImageBrush_GetOpacity(brush); + ok(opacity == 1.0f, "Unexpected opacity %.8e.\n", opacity); + ID2D1ImageBrush_GetTransform(brush, &matrix); + ok(!memcmp(&matrix, &identity, sizeof(matrix)), + "Got unexpected matrix {%.8e, %.8e, %.8e, %.8e, %.8e, %.8e}.\n", + matrix._11, matrix._12, matrix._21, matrix._22, matrix._31, matrix._32); + + ID2D1ImageBrush_GetSourceRectangle(brush, &rect); + match = compare_rect(&rect, 1.0f, 2.0f, 3.0f, 4.0f, 0); + ok(match, "Got unexpected rectangle {%.8e, %.8e, %.8e, %.8e}.\n", + rect.left, rect.top, rect.right, rect.bottom); + + extend_mode = ID2D1ImageBrush_GetExtendModeX(brush); + ok(extend_mode == D2D1_EXTEND_MODE_WRAP, "Unexpected extend mode %u.\n", extend_mode); + extend_mode = ID2D1ImageBrush_GetExtendModeY(brush); + ok(extend_mode == D2D1_EXTEND_MODE_MIRROR, "Unexpected extend mode %u.\n", extend_mode); + interp_mode = ID2D1ImageBrush_GetInterpolationMode(brush); + ok(interp_mode == D2D1_INTERPOLATION_MODE_LINEAR, "Unexpected interpolation mode %u.\n", interp_mode); + + ID2D1ImageBrush_Release(brush); + + /* Custom brush description and image pointer. */ + brush_desc.opacity = 2.0f; + set_matrix_identity(&brush_desc.transform); + scale_matrix(&brush_desc.transform, 2.0f, 3.0f); + + hr = ID2D1DeviceContext_CreateImageBrush(device_context, image, &image_brush_desc, &brush_desc, &brush); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + ID2D1ImageBrush_GetImage(brush, &tmp_image); + ok(tmp_image == image, "Got unexpected image %p, expected %p.\n", tmp_image, image); + ID2D1Image_Release(tmp_image); + + opacity = ID2D1ImageBrush_GetOpacity(brush); + ok(opacity == 2.0f, "Unexpected opacity %.8e.\n", opacity); + ID2D1ImageBrush_GetTransform(brush, &matrix); + ok(!memcmp(&matrix, &brush_desc.transform, sizeof(matrix)), + "Got unexpected matrix {%.8e, %.8e, %.8e, %.8e, %.8e, %.8e}.\n", + matrix._11, matrix._12, matrix._21, matrix._22, matrix._31, matrix._32); + + ID2D1ImageBrush_Release(brush); + + ID2D1Image_Release(image); + refcount = ID2D1Bitmap_Release(bitmap); + ok(!refcount, "Bitmap has %lu references left.\n", refcount); + + ID2D1DeviceContext_Release(device_context); + release_test_context(&ctx); +} + static void test_linear_brush(BOOL d3d11) { D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES gradient_properties; @@ -10919,6 +11033,7 @@ START_TEST(d2d1) queue_test(test_state_block); queue_test(test_color_brush); queue_test(test_bitmap_brush); + queue_test(test_image_brush); queue_test(test_linear_brush); queue_test(test_radial_brush); queue_test(test_path_geometry);
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=114744
Your paranoid android.
=== debian11 (64 bit WoW report) ===
d2d1: d2d1.c:6862: Test failed: d2d1.c:10389: Test marked todo: Test 0: Got unexpected output bounds {-1.00000000e+000, -1.00000000e+000, -1.00000000e+000, -1.00000000e+000}, expected {0.00000000e+000, 0.00000000e+000, 8.00000000e+000, 8.00000000e+000}.