Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/d2d1/brush.c | 300 ++++++++++++++++++++++++++++++++++++++- dlls/d2d1/d2d1_private.h | 11 ++ dlls/d2d1/device.c | 14 +- 3 files changed, 321 insertions(+), 4 deletions(-)
diff --git a/dlls/d2d1/brush.c b/dlls/d2d1/brush.c index 14d9cb54e92..704e6275fd5 100644 --- a/dlls/d2d1/brush.c +++ b/dlls/d2d1/brush.c @@ -1101,6 +1101,243 @@ 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 (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) +{ + FIXME("iface %p, rect %s stub!\n", iface, debug_d2d_rect_f(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) +{ + FIXME("iface %p, rect %p stub!\n", iface, 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_BITMAP, + brush_desc, (ID2D1BrushVtbl *)&d2d_image_brush_vtbl); + if (((*brush)->u.image.image = image)) + ID2D1Image_AddRef((*brush)->u.image.image); + if (image_brush_desc) + { + (*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; + } + else + { + (*brush)->u.image.extend_mode_x = D2D1_EXTEND_MODE_CLAMP; + (*brush)->u.image.extend_mode_y = D2D1_EXTEND_MODE_CLAMP; + (*brush)->u.image.interpolation_mode = D2D1_INTERPOLATION_MODE_LINEAR; + } + + TRACE("Created brush %p.\n", *brush); + return S_OK; +} + struct d2d_brush *unsafe_impl_from_ID2D1Brush(ID2D1Brush *iface) { if (!iface) @@ -1108,7 +1345,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); }
@@ -1288,6 +1526,62 @@ static void d2d_brush_bind_bitmap(struct d2d_brush *brush, struct d2d_device_con ID3D11DeviceContext_Release(d3d_context); }
+static void d2d_brush_bind_image(struct d2d_brush *brush, struct d2d_device_context *context, + unsigned int brush_idx) +{ + ID3D11SamplerState **sampler_state; + ID3D11DeviceContext *d3d_context; + ID2D1Bitmap *bitmap; + struct d2d_bitmap *src_impl; + HRESULT hr; + + hr = ID2D1Image_QueryInterface(brush->u.image.image, &IID_ID2D1Bitmap, (void **)&bitmap); + if (FAILED(hr)) + { + FIXME("ID2D1Image doesn't support ID2D1Bitmap interface.\n"); + return; + } + + src_impl = unsafe_impl_from_ID2D1Bitmap(bitmap); + + ID3D11Device1_GetImmediateContext(context->d3d_device, &d3d_context); + ID3D11DeviceContext_PSSetShaderResources(d3d_context, brush_idx, 1, &src_impl->srv); + + sampler_state = &context->sampler_states + [brush->u.image.interpolation_mode % D2D_SAMPLER_INTERPOLATION_MODE_COUNT] + [brush->u.image.extend_mode_x % D2D_SAMPLER_EXTEND_MODE_COUNT] + [brush->u.image.extend_mode_y % D2D_SAMPLER_EXTEND_MODE_COUNT]; + + if (!*sampler_state) + { + D3D11_SAMPLER_DESC sampler_desc; + + if (brush->u.image.interpolation_mode == D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR) + sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + else + sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + sampler_desc.AddressU = texture_address_mode_from_extend_mode(brush->u.image.extend_mode_x); + sampler_desc.AddressV = texture_address_mode_from_extend_mode(brush->u.image.extend_mode_y); + sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; + sampler_desc.MipLODBias = 0.0f; + sampler_desc.MaxAnisotropy = 0; + sampler_desc.ComparisonFunc = D3D11_COMPARISON_NEVER; + sampler_desc.BorderColor[0] = 0.0f; + sampler_desc.BorderColor[1] = 0.0f; + sampler_desc.BorderColor[2] = 0.0f; + sampler_desc.BorderColor[3] = 0.0f; + sampler_desc.MinLOD = 0.0f; + sampler_desc.MaxLOD = 0.0f; + + if (FAILED(hr = ID3D11Device1_CreateSamplerState(context->d3d_device, &sampler_desc, sampler_state))) + ERR("Failed to create sampler state, hr %#lx.\n", hr); + } + + ID3D11DeviceContext_PSSetSamplers(d3d_context, brush_idx, 1, sampler_state); + ID3D11DeviceContext_Release(d3d_context); + ID2D1Bitmap_Release(bitmap); +} + void d2d_brush_bind_resources(struct d2d_brush *brush, struct d2d_device_context *context, unsigned int brush_idx) { switch (brush->type) @@ -1307,6 +1601,10 @@ void d2d_brush_bind_resources(struct d2d_brush *brush, struct d2d_device_context d2d_brush_bind_bitmap(brush, context, brush_idx); break;
+ case D2D_BRUSH_TYPE_IMAGE: + d2d_brush_bind_image(brush, context, brush_idx); + break; + default: FIXME("Unhandled brush type %#x.\n", brush->type); break; diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h index aa8e8569455..4d4fd16ac00 100644 --- a/dlls/d2d1/d2d1_private.h +++ b/dlls/d2d1/d2d1_private.h @@ -41,6 +41,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, };
@@ -318,6 +319,13 @@ 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; + } image; } u; };
@@ -332,6 +340,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 13c99458cfa..c047f781410 100644 --- a/dlls/d2d1/device.c +++ b/dlls/d2d1/device.c @@ -1929,10 +1929,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,
On 5/6/22 19:34, Dmitry Timoshkov wrote:
+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 (brush->u.image.image)
ID2D1Image_Release(brush->u.image.image);
- brush->u.image.image = image;
+}
This one is probably missing an AddRef. Is it possible to have null images here? If not, it could be unconditional here, and on release.
Nikolay Sivov nsivov@codeweavers.com wrote:
+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 (brush->u.image.image)
ID2D1Image_Release(brush->u.image.image);
- brush->u.image.image = image;
+}
This one is probably missing an AddRef. Is it possible to have null images here? If not, it could be unconditional here, and on release.
This implementation is based on the ::SetBitmap(), and it looks like I really missed the AddRef, thanks for spotting that, much appreciated.