>From 67148e3e7a5dfb61ecf5075fd4e49b014d69758d Mon Sep 17 00:00:00 2001
From: Dmitry Timoshkov <dmitry@baikal.ru>
Date: Fri, 3 Jun 2022 15:36:19 +0300
Subject: [PATCH v2] d2d1: Add support for painting image brush as a bitmap.
Content-Type: text/plain; charset=UTF-8
To: wine-devel@winehq.org

Signed-off-by: Dmitry Timoshkov <dmitry@baikal.ru>
---
 dlls/d2d1/brush.c      | 85 +++++++++++++++++++++++++++++++++++++-----
 dlls/d2d1/tests/d2d1.c | 46 +++++++++++++++++++++++
 2 files changed, 121 insertions(+), 10 deletions(-)

diff --git a/dlls/d2d1/brush.c b/dlls/d2d1/brush.c
index b9a673fe54c..7febafc375c 100644
--- a/dlls/d2d1/brush.c
+++ b/dlls/d2d1/brush.c
@@ -1450,7 +1450,23 @@ BOOL d2d_brush_fill_cb(const struct d2d_brush *brush, struct d2d_brush_cb *cb)
             return TRUE;
 
         case D2D_BRUSH_TYPE_BITMAP:
-            bitmap = brush->u.bitmap.bitmap;
+        case D2D_BRUSH_TYPE_IMAGE:
+        {
+            ID2D1Bitmap *src_bitmap;
+
+            if (brush->type == D2D_BRUSH_TYPE_BITMAP)
+                bitmap = brush->u.bitmap.bitmap;
+            else
+            {
+                if (FAILED(ID2D1Image_QueryInterface(brush->u.image.image, &IID_ID2D1Bitmap, (void **)&src_bitmap)))
+                {
+                    FIXME("ID2D1Image doesn't support ID2D1Bitmap interface.\n");
+                    return FALSE;
+                }
+
+                bitmap = unsafe_impl_from_ID2D1Bitmap(src_bitmap);
+                cb->type = D2D_BRUSH_TYPE_BITMAP;
+            }
 
             /* Scale for bitmap size and dpi. */
             b = brush->transform;
@@ -1477,7 +1493,11 @@ BOOL d2d_brush_fill_cb(const struct d2d_brush *brush, struct d2d_brush_cb *cb)
 
             cb->u.bitmap.ignore_alpha = bitmap->format.alphaMode == D2D1_ALPHA_MODE_IGNORE;
 
+            if (brush->type == D2D_BRUSH_TYPE_IMAGE)
+                ID2D1Bitmap_Release(src_bitmap);
+
             return TRUE;
+        }
 
         default:
             FIXME("Unhandled brush type %#x.\n", brush->type);
@@ -1485,31 +1505,31 @@ BOOL d2d_brush_fill_cb(const struct d2d_brush *brush, struct d2d_brush_cb *cb)
     }
 }
 
-static void d2d_brush_bind_bitmap(struct d2d_brush *brush, struct d2d_device_context *context,
-        unsigned int brush_idx)
+static void d2d_brush_bind(struct d2d_bitmap *bitmap, struct d2d_device_context *context,
+        const D2D1_IMAGE_BRUSH_PROPERTIES *image_brush_desc, unsigned int brush_idx)
 {
     ID3D11SamplerState **sampler_state;
     ID3D11DeviceContext *d3d_context;
     HRESULT hr;
 
     ID3D11Device1_GetImmediateContext(context->d3d_device, &d3d_context);
-    ID3D11DeviceContext_PSSetShaderResources(d3d_context, brush_idx, 1, &brush->u.bitmap.bitmap->srv);
+    ID3D11DeviceContext_PSSetShaderResources(d3d_context, brush_idx, 1, &bitmap->srv);
 
     sampler_state = &context->sampler_states
-            [brush->u.bitmap.interpolation_mode % D2D_SAMPLER_INTERPOLATION_MODE_COUNT]
-            [brush->u.bitmap.extend_mode_x % D2D_SAMPLER_EXTEND_MODE_COUNT]
-            [brush->u.bitmap.extend_mode_y % D2D_SAMPLER_EXTEND_MODE_COUNT];
+            [image_brush_desc->interpolationMode % D2D_SAMPLER_INTERPOLATION_MODE_COUNT]
+            [image_brush_desc->extendModeX % D2D_SAMPLER_EXTEND_MODE_COUNT]
+            [image_brush_desc->extendModeY % D2D_SAMPLER_EXTEND_MODE_COUNT];
 
     if (!*sampler_state)
     {
         D3D11_SAMPLER_DESC sampler_desc;
 
-        if (brush->u.bitmap.interpolation_mode == D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR)
+        if (image_brush_desc->interpolationMode == 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.bitmap.extend_mode_x);
-        sampler_desc.AddressV = texture_address_mode_from_extend_mode(brush->u.bitmap.extend_mode_y);
+        sampler_desc.AddressU = texture_address_mode_from_extend_mode(image_brush_desc->extendModeX);
+        sampler_desc.AddressV = texture_address_mode_from_extend_mode(image_brush_desc->extendModeY);
         sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
         sampler_desc.MipLODBias = 0.0f;
         sampler_desc.MaxAnisotropy = 0;
@@ -1529,6 +1549,47 @@ static void d2d_brush_bind_bitmap(struct d2d_brush *brush, struct d2d_device_con
     ID3D11DeviceContext_Release(d3d_context);
 }
 
+static void d2d_brush_bind_bitmap(struct d2d_brush *brush, struct d2d_device_context *context,
+        unsigned int brush_idx)
+{
+    D2D1_IMAGE_BRUSH_PROPERTIES image_brush_desc;
+
+    image_brush_desc.sourceRectangle.left = 0.0f;
+    image_brush_desc.sourceRectangle.top = 0.0f;
+    image_brush_desc.sourceRectangle.right = brush->u.bitmap.bitmap->pixel_size.width;
+    image_brush_desc.sourceRectangle.bottom = brush->u.bitmap.bitmap->pixel_size.height;
+    image_brush_desc.extendModeX = brush->u.bitmap.extend_mode_x;
+    image_brush_desc.extendModeY = brush->u.bitmap.extend_mode_y;
+    image_brush_desc.interpolationMode = brush->u.bitmap.interpolation_mode;
+
+    d2d_brush_bind(brush->u.bitmap.bitmap, context, &image_brush_desc, brush_idx);
+}
+
+static void d2d_brush_bind_image(struct d2d_brush *brush, struct d2d_device_context *context,
+        unsigned int brush_idx)
+{
+    ID2D1Bitmap *src_bitmap;
+    struct d2d_bitmap *bitmap;
+    D2D1_IMAGE_BRUSH_PROPERTIES image_brush_desc;
+
+    if (FAILED(ID2D1Image_QueryInterface(brush->u.image.image, &IID_ID2D1Bitmap, (void **)&src_bitmap)))
+    {
+        FIXME("ID2D1Image doesn't support ID2D1Bitmap interface.\n");
+        return;
+    }
+
+    bitmap = unsafe_impl_from_ID2D1Bitmap(src_bitmap);
+
+    image_brush_desc.sourceRectangle = brush->u.image.source_rect;
+    image_brush_desc.extendModeX = brush->u.bitmap.extend_mode_x;
+    image_brush_desc.extendModeY = brush->u.bitmap.extend_mode_y;
+    image_brush_desc.interpolationMode = brush->u.bitmap.interpolation_mode;
+
+    d2d_brush_bind(bitmap, context, &image_brush_desc, brush_idx);
+
+    ID2D1Bitmap_Release(src_bitmap);
+}
+
 void d2d_brush_bind_resources(struct d2d_brush *brush, struct d2d_device_context *context, unsigned int brush_idx)
 {
     switch (brush->type)
@@ -1548,6 +1609,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/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c
index bc662b4f8ae..752e34bf4da 100644
--- a/dlls/d2d1/tests/d2d1.c
+++ b/dlls/d2d1/tests/d2d1.c
@@ -2509,11 +2509,13 @@ static void test_image_brush(BOOL d3d11)
     D2D1_BITMAP_PROPERTIES bitmap_desc;
     D2D1_BRUSH_PROPERTIES brush_desc;
     ID2D1Image *image, *tmp_image;
+    D2D1_RECT_F dst_rect;
     struct d2d1_test_context ctx;
     D2D1_EXTEND_MODE extend_mode;
     D2D1_MATRIX_3X2_F matrix;
     ID2D1ImageBrush *brush;
     ID2D1Bitmap *bitmap;
+    D2D1_COLOR_F color;
     D2D1_SIZE_U size;
     D2D1_RECT_F rect;
     ULONG refcount;
@@ -2585,6 +2587,50 @@ static void test_image_brush(BOOL d3d11)
 
     ID2D1ImageBrush_Release(brush);
 
+    /* FillRectangle */
+    set_rect(&image_brush_desc.sourceRectangle, 0.0f, 0.0f, 4.0f, 4.0f);
+    hr = ID2D1DeviceContext_CreateImageBrush(device_context, image, &image_brush_desc, NULL, &brush);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+
+    ID2D1ImageBrush_SetInterpolationMode(brush, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
+
+    ID2D1RenderTarget_BeginDraw(ctx.rt);
+
+    set_color(&color, 0.0f, 0.0f, 1.0f, 1.0f);
+    ID2D1RenderTarget_Clear(ctx.rt, &color);
+
+    set_rect(&dst_rect, 40.0f, 120.0f, 120.0f, 360.0f);
+    ID2D1RenderTarget_FillRectangle(ctx.rt, &dst_rect, (ID2D1Brush *)brush);
+
+    hr = ID2D1RenderTarget_EndDraw(ctx.rt, NULL, NULL);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+    match = compare_surface(&ctx, "89917481db82e6d683a75f068d3984fe2703cce5");
+    ok(match, "Surface does not match.\n");
+
+    ID2D1ImageBrush_Release(brush);
+
+    set_rect(&image_brush_desc.sourceRectangle, 0.0f, 0.0f, 1.0f, 1.0f);
+    hr = ID2D1DeviceContext_CreateImageBrush(device_context, image, &image_brush_desc, NULL, &brush);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+
+    ID2D1ImageBrush_SetInterpolationMode(brush, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
+
+    ID2D1RenderTarget_BeginDraw(ctx.rt);
+
+    set_color(&color, 0.0f, 0.0f, 1.0f, 1.0f);
+    ID2D1RenderTarget_Clear(ctx.rt, &color);
+
+    set_rect(&dst_rect, 40.0f, 120.0f, 120.0f, 360.0f);
+    ID2D1RenderTarget_FillRectangle(ctx.rt, &dst_rect, (ID2D1Brush *)brush);
+
+    hr = ID2D1RenderTarget_EndDraw(ctx.rt, NULL, NULL);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+    match = compare_surface(&ctx, "23544adf9695a51428c194a1cffd531be3416e65");
+    todo_wine
+    ok(match, "Surface does not match.\n");
+
+    ID2D1ImageBrush_Release(brush);
+
     /* Custom brush description and image pointer. */
     brush_desc.opacity = 2.0f;
     set_matrix_identity(&brush_desc.transform);
-- 
2.36.1

