[PATCH v8 0/3] MR11249: Implement sprite batches.
-- v8: d2d1: Implement transform matrixes for sprite batches. d2d1: Implement sprite batches. https://gitlab.winehq.org/wine/wine/-/merge_requests/11249
From: Santino Mazza <smazza@codeweavers.com> --- dlls/d2d1/tests/d2d1.c | 384 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 384 insertions(+) diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c index c01c6eefde4..b8bc2023382 100644 --- a/dlls/d2d1/tests/d2d1.c +++ b/dlls/d2d1/tests/d2d1.c @@ -909,6 +909,14 @@ static BOOL compare_rect(const D2D1_RECT_F *rect, float left, float top, float r && compare_float(rect->bottom, bottom, ulps); } +static BOOL compare_rect_u(const D2D1_RECT_U *rect, UINT left, UINT top, UINT right, UINT bottom) +{ + return rect->left == left && + rect->top == top && + rect->right == right && + rect->bottom == bottom; +} + static BOOL compare_bezier_segment(const D2D1_BEZIER_SEGMENT *b, float x1, float y1, float x2, float y2, float x3, float y3, unsigned int ulps) { @@ -10668,6 +10676,7 @@ static ID2D1DeviceContext *create_device_context(ID2D1Factory1 *factory, IDXGIDe static void test_command_list(BOOL d3d11) { + D2D1_RECT_F test_destination_rect[2] = {{0, 0, 5, 5}, {5, 5, 9, 9}}; static const DWORD bitmap_data[] = { 0xffff0000, 0xffffff00, 0xff00ff00, 0xff00ffff, @@ -10686,6 +10695,8 @@ static void test_command_list(BOOL d3d11) D2D1_BITMAP_PROPERTIES bitmap_desc; ID2D1StrokeStyle *stroke_style; ID2D1CommandList *command_list; + ID2D1SpriteBatch *sprite_batch; + ID2D1DeviceContext3 *context3; struct d2d1_test_context ctx; D2D1_PIXEL_FORMAT format; ID2D1Geometry *geometry; @@ -10730,6 +10741,9 @@ static void test_command_list(BOOL d3d11) hr = ID2D1DeviceContext_QueryInterface(device_context, &IID_ID2D1RenderTarget, (void **)&rt); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = ID2D1DeviceContext_QueryInterface(device_context, &IID_ID2D1DeviceContext3, (void **)&context3); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + /* Test how resources are referenced by the list. */ /* Bitmap. */ @@ -10870,6 +10884,25 @@ static void test_command_list(BOOL d3d11) refcount = ID2D1StrokeStyle_Release(stroke_style); ok(refcount == 1, "Got unexpected refcount %lu.\n", refcount); + /* Sprite batch. */ + ID2D1DeviceContext3_SetAntialiasMode(context3, D2D1_ANTIALIAS_MODE_ALIASED); + hr = ID2D1RenderTarget_CreateBitmap(rt, size, bitmap_data, 4 * sizeof(*bitmap_data), &bitmap_desc, &bitmap); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = ID2D1DeviceContext3_CreateSpriteBatch(context3, &sprite_batch); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, test_destination_rect, NULL, NULL, NULL, sizeof(*test_destination_rect), 0, 0, 0); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + ID2D1DeviceContext3_DrawSpriteBatch(context3, sprite_batch, 0, 2, bitmap, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, D2D1_SPRITE_OPTIONS_NONE); + + refcount = ID2D1SpriteBatch_Release(sprite_batch); + todo_wine ok(refcount == 1, "Got unexpected refcount %lu.\n", refcount); + refcount = ID2D1Bitmap_Release(bitmap); + todo_wine ok(refcount == 1, "Got unexpected refcount %lu.\n", refcount); + ID2D1DeviceContext3_Release(context3); + /* Close on attached list. */ ID2D1DeviceContext_GetTarget(device_context, &target); ok(target == (ID2D1Image *)command_list, "Unexpected context target.\n"); @@ -18004,6 +18037,356 @@ static void test_glyph_run_world_bounds(BOOL d3d11) release_test_context(&ctx); } +static void test_sprite_batches(BOOL d3d11) +{ + ID2D1SpriteBatch *sprite_batch; + struct d2d1_test_context ctx; + ID2D1DeviceContext3 *device; + ID2D1Bitmap *bitmap; + UINT32 sprite_count; + BOOL check; + HRESULT hr; + + static const DWORD bitmap_data[] = + { + 0xffff0000, 0xffffff00, 0xff00ff00, 0xff00ffff, + 0xff0000ff, 0xffff00ff, 0xff000000, 0xff7f7f7f, + 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, + 0xffffffff, 0xff000000, 0xff000000, 0xff000000, + }; + const D2D1_SIZE_U bitmap_size = {4, 4}; + const D2D1_BITMAP_PROPERTIES bitmap_desc = { + {DXGI_FORMAT_B8G8R8A8_UNORM, + D2D1_ALPHA_MODE_IGNORE}, + 96.0f, + 96.0f + }; + + static const D2D1_MATRIX_3X2_F identity = + {{{ + 1.0f, 0.0f, + 0.0f, 1.0f, + 0.0f, 0.0f, + }}}; + + D2D1_RECT_F test_destination_rect[2] = {{0, 0, bitmap_size.width, bitmap_size.height}, {5, 5, 9, 9}}; + D2D1_RECT_U test_source_rect[2] = {{0, 0, 4, 4}, {1, 1, 4, 4}}; + D2D1_COLOR_F test_colors[2] = {{1.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 1.0f}}; + D2D1_MATRIX_3X2_F test_matrixes[2]; + + D2D1_RECT_F destination_rects[4]; + D2D1_RECT_U source_rects[4]; + D2D1_COLOR_F colors[4]; + D2D1_MATRIX_3X2_F transforms[4]; + + set_matrix_identity(&test_matrixes[0]); + set_matrix_identity(&test_matrixes[1]); + + if (!init_test_context(&ctx, d3d11)) + return; + + hr = ID2D1DeviceContext_QueryInterface(ctx.context, &IID_ID2D1DeviceContext3, (void**)&device); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + hr = ID2D1DeviceContext3_CreateSpriteBatch(device, &sprite_batch); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + if (!sprite_batch) + { + ID2D1DeviceContext3_Release(device); + return; + } + + todo_wine check_interface(sprite_batch, &IID_ID2D1Resource, TRUE); + + hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, NULL, NULL, NULL, NULL, 0, 0, 0, 0); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); + ok(sprite_count == 0, "Expected sprite count of 0 got %d\n", sprite_count); + + hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, NULL, test_source_rect, NULL, NULL, 0, 0, 0, 0); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); + ok(sprite_count == 0, "Expected sprite count of 0 got %d\n", sprite_count); + + hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, test_destination_rect, NULL, NULL, NULL, 0, 0, 0, 0); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); + todo_wine ok(sprite_count == 2, "Expected sprite count of 2 got %d\n", sprite_count); + + hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 2, destination_rects, source_rects, colors, transforms); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + todo_wine ok(compare_rect(&destination_rects[0], 0, 0, 4, 4, 0), + "Got unexpected rectangle {%f, %f, %f, %f}.\n", + destination_rects[0].left, destination_rects[0].top, + destination_rects[0].right, destination_rects[0].bottom); + + todo_wine ok(compare_rect(&destination_rects[1], 0, 0, 4, 4, 0), + "Got unexpected rectangle {%f, %f, %f, %f}.\n", + destination_rects[1].left, destination_rects[1].top, + destination_rects[1].right, destination_rects[1].bottom); + + todo_wine ok(compare_rect_u(&source_rects[0], 0, 0, UINT_MAX, UINT_MAX), + "Got unexpected rectangle {%u, %u, %u, %u}.\n", + source_rects[0].left, source_rects[0].top, + source_rects[0].right, source_rects[0].bottom); + + todo_wine ok(compare_rect_u(&source_rects[1], 0, 0, UINT_MAX, UINT_MAX), + "Got unexpected rectangle {%u, %u, %u, %u}.\n", + source_rects[1].left, source_rects[1].top, + source_rects[1].right, source_rects[1].bottom); + + todo_wine ok(!memcmp(&transforms[0], &identity, sizeof(identity)), "Expected identity matrix.\n"); + todo_wine ok(!memcmp(&transforms[1], &identity, sizeof(identity)), "Expected identity matrix.\n"); + + hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, test_destination_rect, NULL, NULL, NULL, sizeof(*test_destination_rect), 0, 0, 0); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + + sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); + todo_wine ok(sprite_count == 4, "Expected sprite count of 4 got %d\n", sprite_count); + + hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 4, destination_rects, source_rects, colors, transforms); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + todo_wine ok(compare_rect(&destination_rects[0], 0, 0, 4, 4, 0), + "Got unexpected rectangle {%f, %f, %f, %f}.\n", + destination_rects[0].left, destination_rects[0].top, + destination_rects[0].right, destination_rects[0].bottom); + + todo_wine ok(compare_rect(&destination_rects[1], 0, 0, 4, 4, 0), + "Got unexpected rectangle {%f, %f, %f, %f}.\n", + destination_rects[1].left, destination_rects[1].top, + destination_rects[1].right, destination_rects[1].bottom); + + todo_wine ok(compare_rect(&destination_rects[2], 0, 0, 4, 4, 0), + "Got unexpected rectangle {%f, %f, %f, %f}.\n", + destination_rects[2].left, destination_rects[2].top, + destination_rects[2].right, destination_rects[2].bottom); + + todo_wine ok(compare_rect(&destination_rects[3], 5, 5, 9, 9, 0), + "Got unexpected rectangle {%f, %f, %f, %f}.\n", + destination_rects[3].left, destination_rects[3].top, + destination_rects[3].right, destination_rects[3].bottom); + + hr = ID2D1SpriteBatch_SetSprites(sprite_batch, 0, 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + hr = ID2D1SpriteBatch_SetSprites(sprite_batch, 0, 8, NULL, NULL, NULL, NULL, 0, 0, 0, 0); + todo_wine ok(hr == E_INVALIDARG, "Got unexpected hr %#lx\n", hr); + + hr = ID2D1SpriteBatch_SetSprites(sprite_batch, 5, 1, NULL, NULL, NULL, NULL, 0, 0, 0, 0); + todo_wine ok(hr == E_INVALIDARG, "Got unexpected hr %#lx\n", hr); + + hr = ID2D1SpriteBatch_SetSprites(sprite_batch, 0, 1, NULL, NULL, NULL, NULL, 0, 0, 0, 0); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + hr = ID2D1SpriteBatch_SetSprites(sprite_batch, 0, 8, &test_destination_rect[1], NULL, NULL, NULL, 0, 0, 0, 0); + todo_wine ok(hr == E_INVALIDARG, "Got unexpected hr %#lx\n", hr); + + hr = ID2D1SpriteBatch_SetSprites(sprite_batch, 0, 1, &test_destination_rect[1], NULL, NULL, NULL, 0, 0, 0, 0); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 1, destination_rects, NULL, NULL, NULL); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + todo_wine ok(compare_rect(&destination_rects[0], 5, 5, 9, 9, 0), + "Got unexpected rectangle {%f, %f, %f, %f}.\n", + destination_rects[0].left, destination_rects[0].top, + destination_rects[0].right, destination_rects[0].bottom); + + ID2D1SpriteBatch_Clear(sprite_batch); + sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); + ok(sprite_count == 0, "Expected sprite count of 0 got %d\n", sprite_count); + + + hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, test_destination_rect, NULL, NULL, NULL, sizeof(test_destination_rect->top), 0, 0, 0); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); + todo_wine ok(sprite_count == 2, "Expected sprite count of 2 got %d\n", sprite_count); + + hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 2, destination_rects, source_rects, colors, transforms); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + todo_wine ok(compare_rect(&destination_rects[0], 0, 0, 4, 4, 0), + "Got unexpected rectangle {%f, %f, %f, %f}.\n", + destination_rects[0].left, destination_rects[0].top, + destination_rects[0].right, destination_rects[0].bottom); + + todo_wine ok(compare_rect(&destination_rects[1], 0, 4, 4, 5, 0), + "Got unexpected rectangle {%f, %f, %f, %f}.\n", + destination_rects[1].left, destination_rects[1].top, + destination_rects[1].right, destination_rects[1].bottom); + + ID2D1SpriteBatch_Clear(sprite_batch); + sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); + ok(sprite_count == 0, "Expected sprite count of 0 got %d\n", sprite_count); + + + hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, test_destination_rect, NULL, NULL, NULL, sizeof(*test_destination_rect), 0, 0, 0); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); + todo_wine ok(sprite_count == 2, "Expected sprite count of 2 got %d\n", sprite_count); + + hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 4, destination_rects, source_rects, colors, transforms); + todo_wine ok(hr == E_INVALIDARG, "Got unexpected hr %#lx\n", hr); + + hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 3, 1, destination_rects, source_rects, colors, transforms); + todo_wine ok(hr == E_INVALIDARG, "Got unexpected hr %#lx\n", hr); + + hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 3, 0, destination_rects, source_rects, colors, transforms); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 0, destination_rects, source_rects, colors, transforms); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 2, NULL, NULL, NULL, NULL); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 2, destination_rects, source_rects, colors, transforms); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 2, NULL, source_rects, NULL, NULL); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + + hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 2, destination_rects, NULL, NULL, NULL); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + todo_wine ok(compare_rect(&destination_rects[0], 0, 0, 4, 4, 0), + "Got unexpected rectangle {%f, %f, %f, %f}.\n", + destination_rects[0].left, destination_rects[0].top, + destination_rects[0].right, destination_rects[0].bottom); + + todo_wine ok(compare_rect(&destination_rects[1], 5, 5, 9, 9, 0), + "Got unexpected rectangle {%f, %f, %f, %f}.\n", + destination_rects[1].left, destination_rects[1].top, + destination_rects[1].right, destination_rects[1].bottom); + + hr = ID2D1RenderTarget_CreateBitmap(ctx.rt, bitmap_size, bitmap_data, bitmap_size.width * sizeof(*bitmap_data), &bitmap_desc, &bitmap); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + ID2D1DeviceContext3_SetAntialiasMode(device, D2D1_ANTIALIAS_MODE_ALIASED); + + ID2D1DeviceContext3_BeginDraw(device); + ID2D1DeviceContext3_DrawSpriteBatch(device, sprite_batch, 0, 1, bitmap, 0, D2D1_SPRITE_OPTIONS_NONE); + hr = ID2D1DeviceContext3_EndDraw(device, 0, 0); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + check = compare_surface(&ctx, "5e8144c13d1a71f0b59d54eba7156218693fa7bd"); + todo_wine ok(check, "Surface does not match.\n"); + + ID2D1DeviceContext3_BeginDraw(device); + ID2D1DeviceContext3_DrawSpriteBatch(device, sprite_batch, 0, 2, bitmap, 0, D2D1_SPRITE_OPTIONS_NONE); + hr = ID2D1DeviceContext3_EndDraw(device, 0, 0); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + check = compare_surface(&ctx, "064e9e8b46799904cab52b14d69a235d4f41409b"); + todo_wine ok(check, "Surface does not match.\n"); + + ID2D1SpriteBatch_Clear(sprite_batch); + sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); + ok(sprite_count == 0, "Expected sprite count of 0 got %d\n", sprite_count); + + hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, test_destination_rect, test_source_rect, NULL, NULL, sizeof(*test_destination_rect), sizeof(*test_source_rect), 0, 0); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 2, destination_rects, source_rects, NULL, NULL); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + todo_wine ok(compare_rect(&destination_rects[0], 0, 0, 4, 4, 0), + "Got unexpected rectangle {%f, %f, %f, %f}.\n", + destination_rects[0].left, destination_rects[0].top, + destination_rects[0].right, destination_rects[0].bottom); + + todo_wine ok(compare_rect(&destination_rects[1], 5, 5, 9, 9, 0), + "Got unexpected rectangle {%f, %f, %f, %f}.\n", + destination_rects[1].left, destination_rects[1].top, + destination_rects[1].right, destination_rects[1].bottom); + + todo_wine ok(compare_rect_u(&source_rects[0], 0, 0, 4, 4), + "Got unexpected rectangle {%u, %u, %u, %u}.\n", + source_rects[0].left, source_rects[0].top, + source_rects[0].right, source_rects[0].bottom); + + todo_wine ok(compare_rect_u(&source_rects[1], 1, 1, 4, 4), + "Got unexpected rectangle {%u, %u, %u, %u}.\n", + source_rects[1].left, source_rects[1].top, + source_rects[1].right, source_rects[1].bottom); + + ID2D1DeviceContext3_BeginDraw(device); + ID2D1DeviceContext3_DrawSpriteBatch(device, sprite_batch, 0, 2, bitmap, 0, D2D1_SPRITE_OPTIONS_NONE); + hr = ID2D1DeviceContext3_EndDraw(device, 0, 0); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + check = compare_surface(&ctx, "9133aab139f4c0ec2edea7e3c63069370ff88083"); + todo_wine ok(check, "Surface does not match.\n"); + + + ID2D1SpriteBatch_Clear(sprite_batch); + sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); + ok(sprite_count == 0, "Expected sprite count of 0 got %d\n", sprite_count); + + set_matrix_identity(&test_matrixes[1]); + scale_matrix(&test_matrixes[1], 4.0f, 4.0f); + hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, test_destination_rect, test_source_rect, NULL, test_matrixes, sizeof(*test_destination_rect), sizeof(*test_source_rect), 0, sizeof(D2D1_MATRIX_3X2_F)); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + ID2D1DeviceContext3_BeginDraw(device); + ID2D1DeviceContext3_DrawSpriteBatch(device, sprite_batch, 0, 2, bitmap, 0, D2D1_SPRITE_OPTIONS_NONE); + hr = ID2D1DeviceContext3_EndDraw(device, 0, 0); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + check = compare_surface(&ctx, "30bf2de6f4f10ae8ebfc61261ad0d0a4abfed094"); + todo_wine ok(check, "Surface does not match.\n"); + + + ID2D1SpriteBatch_Clear(sprite_batch); + sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); + ok(sprite_count == 0, "Expected sprite count of 0 got %d\n", sprite_count); + + hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, test_destination_rect, test_source_rect, test_colors, NULL, sizeof(*test_destination_rect), sizeof(*test_source_rect), sizeof(D2D1_COLOR_F), 0); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + ID2D1DeviceContext3_BeginDraw(device); + ID2D1DeviceContext3_DrawSpriteBatch(device, sprite_batch, 0, 2, bitmap, 0, D2D1_SPRITE_OPTIONS_NONE); + hr = ID2D1DeviceContext3_EndDraw(device, 0, 0); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + check = compare_surface(&ctx, "e21f4ac578dcc4ed266959872d243a8ce4f493d7"); + todo_wine ok(check, "Surface does not match.\n"); + + + ID2D1SpriteBatch_Clear(sprite_batch); + sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); + ok(sprite_count == 0, "Expected sprite count of 0 got %d\n", sprite_count); + + set_matrix_identity(&test_matrixes[1]); + skew_matrix(&test_matrixes[1], 0.5f, 0.5f); + hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, test_destination_rect, test_source_rect, NULL, test_matrixes, sizeof(*test_destination_rect), sizeof(*test_source_rect), 0, sizeof(D2D1_MATRIX_3X2_F)); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + + ID2D1DeviceContext3_BeginDraw(device); + ID2D1DeviceContext3_DrawSpriteBatch(device, sprite_batch, 0, 2, bitmap, 0, D2D1_SPRITE_OPTIONS_NONE); + hr = ID2D1DeviceContext3_EndDraw(device, 0, 0); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + check = compare_surface(&ctx, "72d4c7723073eabc2d7d1939b6f41040546b21f7"); + todo_wine ok(check, "Surface does not match.\n"); + + ID2D1DeviceContext3_SetAntialiasMode(device, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + + ID2D1DeviceContext3_BeginDraw(device); + ID2D1DeviceContext3_DrawSpriteBatch(device, sprite_batch, 0, 2, bitmap, 0, D2D1_SPRITE_OPTIONS_NONE); + hr = ID2D1DeviceContext3_EndDraw(device, 0, 0); + todo_wine ok(hr == D2DERR_WRONG_STATE, "Got unexpected hr %#lx.\n", hr); + + ID2D1Bitmap_Release(bitmap); + + ID2D1DeviceContext3_Release(device); + ID2D1SpriteBatch_Release(sprite_batch); + release_test_context(&ctx); +} + START_TEST(d2d1) { HMODULE d2d1_dll = GetModuleHandleA("d2d1.dll"); @@ -18127,6 +18510,7 @@ START_TEST(d2d1) queue_d3d10_test(test_path_geometry_stream); queue_d3d10_test(test_transformed_geometry); queue_d3d10_test(test_glyph_run_world_bounds); + queue_test(test_sprite_batches); run_queued_tests(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11249
From: Santino Mazza <smazza@codeweavers.com> --- dlls/d2d1/Makefile.in | 1 + dlls/d2d1/command_list.c | 63 +++++++++ dlls/d2d1/d2d1_private.h | 24 ++++ dlls/d2d1/device.c | 55 +++++++- dlls/d2d1/sprite_batch.c | 278 +++++++++++++++++++++++++++++++++++++++ dlls/d2d1/tests/d2d1.c | 114 ++++++++-------- 6 files changed, 474 insertions(+), 61 deletions(-) create mode 100644 dlls/d2d1/sprite_batch.c diff --git a/dlls/d2d1/Makefile.in b/dlls/d2d1/Makefile.in index 7c4596462aa..73add39286b 100644 --- a/dlls/d2d1/Makefile.in +++ b/dlls/d2d1/Makefile.in @@ -19,6 +19,7 @@ SOURCES = \ hwnd_render_target.c \ layer.c \ mesh.c \ + sprite_batch.c \ state_block.c \ stroke.c \ wic_render_target.c diff --git a/dlls/d2d1/command_list.c b/dlls/d2d1/command_list.c index 34242ddd88a..c8e22d95a7a 100644 --- a/dlls/d2d1/command_list.c +++ b/dlls/d2d1/command_list.c @@ -40,6 +40,7 @@ enum d2d_command_type D2D_COMMAND_DRAW_GEOMETRY, D2D_COMMAND_DRAW_RECTANGLE, D2D_COMMAND_DRAW_BITMAP, + D2D_COMMAND_DRAW_SPRITE_BATCH, D2D_COMMAND_DRAW_IMAGE, D2D_COMMAND_FILL_MESH, D2D_COMMAND_FILL_OPACITY_MASK, @@ -197,6 +198,17 @@ struct d2d_command_draw_bitmap D2D1_MATRIX_4X4_F *perspective_transform; }; +struct d2d_command_draw_sprite_batch +{ + struct d2d_command c; + ID2D1SpriteBatch *sprite_batch; + ID2D1Bitmap *bitmap; + UINT32 start_index; + UINT32 sprite_count; + D2D1_BITMAP_INTERPOLATION_MODE interpolation_mode; + D2D1_SPRITE_OPTIONS sprite_options; +}; + struct d2d_command_draw_image { struct d2d_command c; @@ -275,6 +287,7 @@ static void STDMETHODCALLTYPE d2d_command_list_GetFactory(ID2D1CommandList *ifac static HRESULT STDMETHODCALLTYPE d2d_command_list_Stream(ID2D1CommandList *iface, ID2D1CommandSink *sink) { struct d2d_command_list *command_list = impl_from_ID2D1CommandList(iface); + ID2D1CommandSink3 *sink3; const void *data, *end; HRESULT hr; @@ -406,6 +419,34 @@ static HRESULT STDMETHODCALLTYPE d2d_command_list_Stream(ID2D1CommandList *iface c->interpolation_mode, c->src_rect, c->perspective_transform); break; } + case D2D_COMMAND_DRAW_SPRITE_BATCH: + { + struct d2d_sprite *sprite; + D2D1_RECT_F dst_rect = {0}; + const struct d2d_command_draw_sprite_batch *c = data; + struct d2d_sprite_batch *sprite_batch_impl = unsafe_impl_from_ID2D1SpriteBatch(c->sprite_batch); + + hr = ID2D1CommandSink_QueryInterface(sink, &IID_ID2D1CommandSink3, (void**)&sink3); + if (hr == E_NOINTERFACE) + { + WARN("Sink interface doesn't support DrawSpriteBatches.\n"); + + for (int i = 0; i < sprite_batch_impl->sprite_count; ++i) + { + sprite = &sprite_batch_impl->sprites[i]; + dst_rect.left = min(dst_rect.left, sprite->destination_rectangle.left); + dst_rect.top = min(dst_rect.top, sprite->destination_rectangle.top); + dst_rect.bottom = max(dst_rect.bottom, sprite->destination_rectangle.bottom); + dst_rect.right = max(dst_rect.right, sprite->destination_rectangle.right); + } + + hr = ID2D1CommandSink_DrawBitmap(sink, c->bitmap, &dst_rect, 1.0f, (D2D1_INTERPOLATION_MODE)c->interpolation_mode, NULL, NULL); + break; + } + + hr = ID2D1CommandSink3_DrawSpriteBatch(sink3, c->sprite_batch, c->start_index, c->sprite_count, c->bitmap, c->interpolation_mode, c->sprite_options); + break; + } case D2D_COMMAND_DRAW_IMAGE: { const struct d2d_command_draw_image *c = data; @@ -1022,6 +1063,28 @@ void d2d_command_list_draw_bitmap(struct d2d_command_list *command_list, ID2D1Bi d2d_command_list_write_field(&data, &command->perspective_transform, perspective_transform, sizeof(*perspective_transform)); } +void d2d_command_list_draw_sprite_batch(struct d2d_command_list *command_list, ID2D1SpriteBatch *sprite_batch, + UINT32 start_index, UINT32 sprite_count, ID2D1Bitmap *bitmap, D2D1_BITMAP_INTERPOLATION_MODE interpolation_mode, + D2D1_SPRITE_OPTIONS sprite_options) +{ + struct d2d_command_draw_sprite_batch *command; + size_t size; + + size = sizeof(*command); + + d2d_command_list_reference_object(command_list, sprite_batch); + d2d_command_list_reference_object(command_list, bitmap); + + command = d2d_command_list_require_space(command_list, size); + command->c.op = D2D_COMMAND_DRAW_SPRITE_BATCH; + command->sprite_batch = sprite_batch; + command->bitmap = bitmap; + command->start_index = start_index; + command->sprite_count = sprite_count; + command->interpolation_mode = interpolation_mode; + command->sprite_options = sprite_options; +} + void d2d_command_list_draw_image(struct d2d_command_list *command_list, ID2D1Image *image, const D2D1_POINT_2F *target_offset, const D2D1_RECT_F *image_rect, D2D1_INTERPOLATION_MODE interpolation_mode, D2D1_COMPOSITE_MODE composite_mode) diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h index 9c78aaf92e2..32bc70aa2fa 100644 --- a/dlls/d2d1/d2d1_private.h +++ b/dlls/d2d1/d2d1_private.h @@ -460,6 +460,27 @@ HRESULT d2d_bitmap_create_from_wic_bitmap(struct d2d_device_context *context, IW unsigned int d2d_get_bitmap_options_for_surface(IDXGISurface *surface); struct d2d_bitmap *unsafe_impl_from_ID2D1Bitmap(ID2D1Bitmap *iface); +struct d2d_sprite +{ + D2D1_RECT_F destination_rectangle; + D2D1_RECT_U source_rectangle; +}; + +struct d2d_sprite_batch +{ + ID2D1SpriteBatch ID2D1SpriteBatch_iface; + + ID2D1Factory *factory; + LONG refcount; + + size_t sprite_count; + size_t sprites_allocated; + struct d2d_sprite *sprites; +}; + +HRESULT d2d_create_sprite_batch(struct d2d_device_context *context, struct d2d_sprite_batch **out); +struct d2d_sprite_batch *unsafe_impl_from_ID2D1SpriteBatch(ID2D1SpriteBatch *iface); + struct d2d_state_block { ID2D1DrawingStateBlock1 ID2D1DrawingStateBlock1_iface; @@ -929,6 +950,9 @@ void d2d_command_list_draw_glyph_run(struct d2d_command_list *command_list, void d2d_command_list_draw_bitmap(struct d2d_command_list *command_list, ID2D1Bitmap *bitmap, const D2D1_RECT_F *dst_rect, float opacity, D2D1_INTERPOLATION_MODE interpolation_mode, const D2D1_RECT_F *src_rect, const D2D1_MATRIX_4X4_F *perspective_transform); +void d2d_command_list_draw_sprite_batch(struct d2d_command_list *command_list, ID2D1SpriteBatch *sprite_batch, + UINT32 start_index, UINT32 sprite_count, ID2D1Bitmap *bitmap, D2D1_BITMAP_INTERPOLATION_MODE interpolation_mode, + D2D1_SPRITE_OPTIONS sprite_options); void d2d_command_list_draw_image(struct d2d_command_list *command_list, ID2D1Image *image, const D2D1_POINT_2F *target_offset, const D2D1_RECT_F *image_rect, D2D1_INTERPOLATION_MODE interpolation_mode, D2D1_COMPOSITE_MODE composite_mode); diff --git a/dlls/d2d1/device.c b/dlls/d2d1/device.c index b3560f09c12..2fffd2cc074 100644 --- a/dlls/d2d1/device.c +++ b/dlls/d2d1/device.c @@ -3037,18 +3037,65 @@ static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateTransformedImageSource static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateSpriteBatch(ID2D1DeviceContext6 *iface, ID2D1SpriteBatch **sprite_batch) { - FIXME("iface %p, sprite_batch %p stub!\n", iface, sprite_batch); + struct d2d_device_context *ctx = impl_from_ID2D1DeviceContext(iface); + struct d2d_sprite_batch *object; + HRESULT hr; - return E_NOTIMPL; + TRACE("iface %p, sprite_batch %p.\n", iface, sprite_batch); + + if (!sprite_batch) + return E_INVALIDARG; + + if (SUCCEEDED(hr = d2d_create_sprite_batch(ctx, &object))) + *sprite_batch = &object->ID2D1SpriteBatch_iface; + + return S_OK; +} + +static inline void convert_rect_u_to_rect_f(const D2D1_RECT_U *rect_u, D2D1_RECT_F *rect_f) +{ + rect_f->bottom = (float)(rect_u->bottom); + rect_f->top = (float)(rect_u->top); + rect_f->right = (float)(rect_u->right); + rect_f->left = (float)(rect_u->left); } static void STDMETHODCALLTYPE d2d_device_context_DrawSpriteBatch(ID2D1DeviceContext6 *iface, ID2D1SpriteBatch *sprite_batch, UINT32 start_index, UINT32 sprite_count, ID2D1Bitmap *bitmap, D2D1_BITMAP_INTERPOLATION_MODE interpolation_mode, D2D1_SPRITE_OPTIONS sprite_options) { - FIXME("iface %p, sprite_batch %p, start_index %u, sprite_count %u, bitmap %p, interpolation_mode %u," - "sprite_options %u stub!\n", iface, sprite_batch, start_index, sprite_count, bitmap, + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + struct d2d_sprite_batch *sprite_batch_impl = unsafe_impl_from_ID2D1SpriteBatch(sprite_batch); + struct d2d_sprite *sprite; + D2D1_RECT_F source_rect; + + TRACE("iface %p, sprite_batch %p, start_index %u, sprite_count %u, bitmap %p, interpolation_mode %u," + "sprite_options %u.\n", iface, sprite_batch, start_index, sprite_count, bitmap, interpolation_mode, sprite_options); + + if (sprite_batch_impl->sprite_count < (UINT64)start_index + (UINT64)sprite_count) + { + d2d_device_context_set_error(context, E_INVALIDARG); + return; + } + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + { + d2d_command_list_draw_sprite_batch(context->target.command_list, sprite_batch, start_index, + sprite_count, bitmap, interpolation_mode, sprite_options); + } + else + { + for (int i = start_index; i < start_index + sprite_count; ++i) + { + sprite = &sprite_batch_impl->sprites[i]; + + convert_rect_u_to_rect_f(&sprite->source_rectangle, &source_rect); + + ID2D1DeviceContext6_DrawBitmap(iface, bitmap, &sprite->destination_rectangle, 1.0f, (D2D1_INTERPOLATION_MODE)interpolation_mode, + &source_rect, 0); + } + } } static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateSvgGlyphStyle(ID2D1DeviceContext6 *iface, diff --git a/dlls/d2d1/sprite_batch.c b/dlls/d2d1/sprite_batch.c new file mode 100644 index 00000000000..1f59e23e533 --- /dev/null +++ b/dlls/d2d1/sprite_batch.c @@ -0,0 +1,278 @@ +/* + * Copyright 2026 Santino Mazza for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "d2d1_private.h" +#include <windows.h> + +WINE_DEFAULT_DEBUG_CHANNEL(d2d); + +static BOOL array_reserve(void **elements, size_t *capacity, size_t count, size_t size) +{ + unsigned int max_capacity, new_capacity; + void *new_elements; + + if (count <= *capacity) + return TRUE; + + max_capacity = ~0u / size; + if (count > max_capacity) + return FALSE; + + new_capacity = max(8, *capacity); + while (new_capacity < count && new_capacity <= max_capacity / 2) + new_capacity *= 2; + if (new_capacity < count) + new_capacity = count; + + if (!(new_elements = realloc(*elements, new_capacity * size))) + { + ERR("Failed to allocate memory.\n"); + return FALSE; + } + + *elements = new_elements; + *capacity = new_capacity; + return TRUE; +} + +static inline struct d2d_sprite_batch *impl_from_ID2D1SpriteBatch(ID2D1SpriteBatch *iface) +{ + return CONTAINING_RECORD(iface, struct d2d_sprite_batch, ID2D1SpriteBatch_iface); +} + +static HRESULT STDMETHODCALLTYPE d2d_sprite_batch_QueryInterface(ID2D1SpriteBatch *iface, REFIID iid, void **out) +{ + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_ID2D1SpriteBatch) + || IsEqualGUID(iid, &IID_ID2D1Resource) + || IsEqualGUID(iid, &IID_IUnknown)) + { + ID2D1SpriteBatch_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_sprite_batch_AddRef(ID2D1SpriteBatch *iface) +{ + struct d2d_sprite_batch *batch = impl_from_ID2D1SpriteBatch(iface); + ULONG refcount = InterlockedIncrement(&batch->refcount); + + TRACE("%p increasing refcount to %lu.\n", iface, refcount); + + return refcount; +} + +static ULONG STDMETHODCALLTYPE d2d_sprite_batch_Release(ID2D1SpriteBatch *iface) +{ + struct d2d_sprite_batch *batch = impl_from_ID2D1SpriteBatch(iface); + ULONG refcount = InterlockedDecrement(&batch->refcount); + + TRACE("%p decreasing refcount to %lu.\n", iface, refcount); + + if (!refcount) + { + ID2D1Factory_Release(batch->factory); + free(batch->sprites); + free(batch); + } + + return refcount; +} + +static void STDMETHODCALLTYPE d2d_sprite_batch_GetFactory(ID2D1SpriteBatch *iface, ID2D1Factory **factory) +{ + struct d2d_sprite_batch *batch = impl_from_ID2D1SpriteBatch(iface); + + TRACE("iface %p, factory %p.\n", iface, factory); + + *factory = batch->factory; + ID2D1Factory_AddRef(*factory); +} + +static HRESULT STDMETHODCALLTYPE d2d_sprite_batch_AddSprites(ID2D1SpriteBatch *iface, UINT32 sprite_count, + const D2D1_RECT_F *destination_rectangles, const D2D1_RECT_U *source_rectangles, const D2D1_COLOR_F *colors, + const D2D1_MATRIX_3X2_F *transforms, UINT32 destination_rectangles_stride, UINT32 source_rectangles_stride, + UINT32 colors_stride, UINT32 transforms_stride) +{ + struct d2d_sprite_batch *batch = impl_from_ID2D1SpriteBatch(iface); + struct d2d_sprite *sprite; + + TRACE("iface %p, sprite_count %u, destination_rectangles %p, source_rectangles %p," + "colors %p, transforms %p, destination_rectangles_stride %u," + "source_rectangles_stride %u, colors_stride %u, transforms_stride %u.\n", + iface, sprite_count, destination_rectangles, source_rectangles, colors, transforms, + destination_rectangles_stride, source_rectangles_stride, colors_stride, transforms_stride); + + if (!destination_rectangles) + return S_OK; + + if (colors) + FIXME("Color mask not implemented\n"); + + if (transforms) + FIXME("Transform matrixes not implemented.\n"); + + array_reserve((void**)&batch->sprites, &batch->sprites_allocated, batch->sprite_count + sprite_count, sizeof(struct d2d_sprite)); + + for (int i = 0; i < sprite_count; ++i) + { + sprite = &batch->sprites[i + batch->sprite_count]; + + sprite->destination_rectangle = *(D2D1_RECT_F *)(((UCHAR*)destination_rectangles) + i * destination_rectangles_stride); + + if (source_rectangles) + sprite->source_rectangle = *(D2D1_RECT_U *)(((UCHAR*)source_rectangles) + i * source_rectangles_stride); + else + sprite->source_rectangle = (D2D1_RECT_U){0, 0, UINT_MAX, UINT_MAX}; + } + + batch->sprite_count += sprite_count; + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE d2d_sprite_batch_SetSprites(ID2D1SpriteBatch *iface, UINT32 start_index, UINT32 sprite_count, + const D2D1_RECT_F *destination_rectangles, const D2D1_RECT_U *source_rectangles, const D2D1_COLOR_F *colors, + const D2D1_MATRIX_3X2_F *transforms, UINT32 destination_rectangles_stride, UINT32 source_rectangles_stride, + UINT32 colors_stride, UINT32 transforms_stride) +{ + struct d2d_sprite_batch *batch = impl_from_ID2D1SpriteBatch(iface); + struct d2d_sprite *sprite; + + TRACE("iface %p, start_index %u, sprite_count %u, destination_rectangles %p," + "source_rectangles %p, colors %p, transforms %p, destination_rectangles_stride %u," + "source_rectangles_stride %u, colors_stride %u, transforms_stride %u.\n", + iface, start_index, sprite_count, destination_rectangles, source_rectangles, colors, transforms, + destination_rectangles_stride, source_rectangles_stride, colors_stride, transforms_stride); + + if (start_index >= batch->sprite_count || sprite_count > batch->sprite_count - start_index) + return E_INVALIDARG; + + if (!sprite_count || (!destination_rectangles && !source_rectangles && !colors && !transforms)) + return S_OK; + + if (colors) + FIXME("Color mask not implemented\n"); + + if (transforms) + FIXME("Transform matrixes not implemented\n"); + + for (int i = start_index; i < start_index + sprite_count; ++i) + { + sprite = &batch->sprites[i]; + if (destination_rectangles) + sprite->destination_rectangle = *(D2D1_RECT_F *)(((UCHAR*)destination_rectangles) + i * destination_rectangles_stride); + + if (source_rectangles) + sprite->source_rectangle = *(D2D1_RECT_U *)(((UCHAR*)source_rectangles) + i * source_rectangles_stride); + } + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE d2d_sprite_batch_GetSprites(ID2D1SpriteBatch *iface, UINT32 start_index, UINT32 sprite_count, + D2D1_RECT_F *destination_rectangles, D2D1_RECT_U *source_rectangles, D2D1_COLOR_F *colors, + D2D1_MATRIX_3X2_F *transforms) +{ + struct d2d_sprite_batch *batch = impl_from_ID2D1SpriteBatch(iface); + struct d2d_sprite *sprite; + + TRACE("iface %p, start_index %u, sprite_count %u, destination_rectangles %p, source_rectangles %p," + "colors %p, transforms %p.\n", iface, start_index, sprite_count, destination_rectangles, + source_rectangles, colors, transforms); + + if (!sprite_count) + return S_OK; + + if (start_index >= batch->sprite_count || sprite_count > batch->sprite_count - start_index) + return E_INVALIDARG; + + for (int i = start_index; i < start_index + sprite_count; ++i) + { + sprite = &batch->sprites[i]; + + if (destination_rectangles) + destination_rectangles[i] = sprite->destination_rectangle; + + if (source_rectangles) + source_rectangles[i] = sprite->source_rectangle; + } + + return S_OK; +} + +static UINT32 STDMETHODCALLTYPE d2d_sprite_batch_GetSpritesCount(ID2D1SpriteBatch *iface) +{ + struct d2d_sprite_batch *batch = impl_from_ID2D1SpriteBatch(iface); + TRACE("iface %p.\n", iface); + return batch->sprite_count; +} + +static void STDMETHODCALLTYPE d2d_sprite_batch_Clear(ID2D1SpriteBatch *iface) +{ + struct d2d_sprite_batch *batch = impl_from_ID2D1SpriteBatch(iface); + TRACE("iface %p.\n", iface); + + batch->sprite_count = 0; +} + +static const struct ID2D1SpriteBatchVtbl d2d_sprite_batch_vtbl = +{ + d2d_sprite_batch_QueryInterface, + d2d_sprite_batch_AddRef, + d2d_sprite_batch_Release, + d2d_sprite_batch_GetFactory, + d2d_sprite_batch_AddSprites, + d2d_sprite_batch_SetSprites, + d2d_sprite_batch_GetSprites, + d2d_sprite_batch_GetSpritesCount, + d2d_sprite_batch_Clear +}; + +HRESULT d2d_create_sprite_batch(struct d2d_device_context *context, struct d2d_sprite_batch **out) +{ + struct d2d_sprite_batch *sprite_batch; + + *out = NULL; + if (!(sprite_batch = calloc(1, sizeof(*sprite_batch)))) + return E_OUTOFMEMORY; + + sprite_batch->ID2D1SpriteBatch_iface.lpVtbl = &d2d_sprite_batch_vtbl; + + sprite_batch->refcount = 1; + ID2D1Factory_AddRef(sprite_batch->factory = context->factory); + + *out = sprite_batch; + + return S_OK; +} + +struct d2d_sprite_batch *unsafe_impl_from_ID2D1SpriteBatch(ID2D1SpriteBatch *iface) +{ + if (!iface) + return NULL; + assert(iface->lpVtbl == &d2d_sprite_batch_vtbl); + return CONTAINING_RECORD(iface, struct d2d_sprite_batch, ID2D1SpriteBatch_iface); +} diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c index b8bc2023382..35770ddfeb5 100644 --- a/dlls/d2d1/tests/d2d1.c +++ b/dlls/d2d1/tests/d2d1.c @@ -10898,9 +10898,9 @@ static void test_command_list(BOOL d3d11) ID2D1DeviceContext3_DrawSpriteBatch(context3, sprite_batch, 0, 2, bitmap, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, D2D1_SPRITE_OPTIONS_NONE); refcount = ID2D1SpriteBatch_Release(sprite_batch); - todo_wine ok(refcount == 1, "Got unexpected refcount %lu.\n", refcount); + ok(refcount == 1, "Got unexpected refcount %lu.\n", refcount); refcount = ID2D1Bitmap_Release(bitmap); - todo_wine ok(refcount == 1, "Got unexpected refcount %lu.\n", refcount); + ok(refcount == 1, "Got unexpected refcount %lu.\n", refcount); ID2D1DeviceContext3_Release(context3); /* Close on attached list. */ @@ -18089,52 +18089,52 @@ static void test_sprite_batches(BOOL d3d11) ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); hr = ID2D1DeviceContext3_CreateSpriteBatch(device, &sprite_batch); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); if (!sprite_batch) { ID2D1DeviceContext3_Release(device); return; } - todo_wine check_interface(sprite_batch, &IID_ID2D1Resource, TRUE); + check_interface(sprite_batch, &IID_ID2D1Resource, TRUE); hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, NULL, NULL, NULL, NULL, 0, 0, 0, 0); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); ok(sprite_count == 0, "Expected sprite count of 0 got %d\n", sprite_count); hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, NULL, test_source_rect, NULL, NULL, 0, 0, 0, 0); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); ok(sprite_count == 0, "Expected sprite count of 0 got %d\n", sprite_count); hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, test_destination_rect, NULL, NULL, NULL, 0, 0, 0, 0); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); - todo_wine ok(sprite_count == 2, "Expected sprite count of 2 got %d\n", sprite_count); + ok(sprite_count == 2, "Expected sprite count of 2 got %d\n", sprite_count); hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 2, destination_rects, source_rects, colors, transforms); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); - todo_wine ok(compare_rect(&destination_rects[0], 0, 0, 4, 4, 0), + ok(compare_rect(&destination_rects[0], 0, 0, 4, 4, 0), "Got unexpected rectangle {%f, %f, %f, %f}.\n", destination_rects[0].left, destination_rects[0].top, destination_rects[0].right, destination_rects[0].bottom); - todo_wine ok(compare_rect(&destination_rects[1], 0, 0, 4, 4, 0), + ok(compare_rect(&destination_rects[1], 0, 0, 4, 4, 0), "Got unexpected rectangle {%f, %f, %f, %f}.\n", destination_rects[1].left, destination_rects[1].top, destination_rects[1].right, destination_rects[1].bottom); - todo_wine ok(compare_rect_u(&source_rects[0], 0, 0, UINT_MAX, UINT_MAX), + ok(compare_rect_u(&source_rects[0], 0, 0, UINT_MAX, UINT_MAX), "Got unexpected rectangle {%u, %u, %u, %u}.\n", source_rects[0].left, source_rects[0].top, source_rects[0].right, source_rects[0].bottom); - todo_wine ok(compare_rect_u(&source_rects[1], 0, 0, UINT_MAX, UINT_MAX), + ok(compare_rect_u(&source_rects[1], 0, 0, UINT_MAX, UINT_MAX), "Got unexpected rectangle {%u, %u, %u, %u}.\n", source_rects[1].left, source_rects[1].top, source_rects[1].right, source_rects[1].bottom); @@ -18143,57 +18143,57 @@ static void test_sprite_batches(BOOL d3d11) todo_wine ok(!memcmp(&transforms[1], &identity, sizeof(identity)), "Expected identity matrix.\n"); hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, test_destination_rect, NULL, NULL, NULL, sizeof(*test_destination_rect), 0, 0, 0); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); - todo_wine ok(sprite_count == 4, "Expected sprite count of 4 got %d\n", sprite_count); + ok(sprite_count == 4, "Expected sprite count of 4 got %d\n", sprite_count); hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 4, destination_rects, source_rects, colors, transforms); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); - todo_wine ok(compare_rect(&destination_rects[0], 0, 0, 4, 4, 0), + ok(compare_rect(&destination_rects[0], 0, 0, 4, 4, 0), "Got unexpected rectangle {%f, %f, %f, %f}.\n", destination_rects[0].left, destination_rects[0].top, destination_rects[0].right, destination_rects[0].bottom); - todo_wine ok(compare_rect(&destination_rects[1], 0, 0, 4, 4, 0), + ok(compare_rect(&destination_rects[1], 0, 0, 4, 4, 0), "Got unexpected rectangle {%f, %f, %f, %f}.\n", destination_rects[1].left, destination_rects[1].top, destination_rects[1].right, destination_rects[1].bottom); - todo_wine ok(compare_rect(&destination_rects[2], 0, 0, 4, 4, 0), + ok(compare_rect(&destination_rects[2], 0, 0, 4, 4, 0), "Got unexpected rectangle {%f, %f, %f, %f}.\n", destination_rects[2].left, destination_rects[2].top, destination_rects[2].right, destination_rects[2].bottom); - todo_wine ok(compare_rect(&destination_rects[3], 5, 5, 9, 9, 0), + ok(compare_rect(&destination_rects[3], 5, 5, 9, 9, 0), "Got unexpected rectangle {%f, %f, %f, %f}.\n", destination_rects[3].left, destination_rects[3].top, destination_rects[3].right, destination_rects[3].bottom); hr = ID2D1SpriteBatch_SetSprites(sprite_batch, 0, 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); hr = ID2D1SpriteBatch_SetSprites(sprite_batch, 0, 8, NULL, NULL, NULL, NULL, 0, 0, 0, 0); - todo_wine ok(hr == E_INVALIDARG, "Got unexpected hr %#lx\n", hr); + ok(hr == E_INVALIDARG, "Got unexpected hr %#lx\n", hr); hr = ID2D1SpriteBatch_SetSprites(sprite_batch, 5, 1, NULL, NULL, NULL, NULL, 0, 0, 0, 0); - todo_wine ok(hr == E_INVALIDARG, "Got unexpected hr %#lx\n", hr); + ok(hr == E_INVALIDARG, "Got unexpected hr %#lx\n", hr); hr = ID2D1SpriteBatch_SetSprites(sprite_batch, 0, 1, NULL, NULL, NULL, NULL, 0, 0, 0, 0); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); hr = ID2D1SpriteBatch_SetSprites(sprite_batch, 0, 8, &test_destination_rect[1], NULL, NULL, NULL, 0, 0, 0, 0); - todo_wine ok(hr == E_INVALIDARG, "Got unexpected hr %#lx\n", hr); + ok(hr == E_INVALIDARG, "Got unexpected hr %#lx\n", hr); hr = ID2D1SpriteBatch_SetSprites(sprite_batch, 0, 1, &test_destination_rect[1], NULL, NULL, NULL, 0, 0, 0, 0); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 1, destination_rects, NULL, NULL, NULL); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); - todo_wine ok(compare_rect(&destination_rects[0], 5, 5, 9, 9, 0), + ok(compare_rect(&destination_rects[0], 5, 5, 9, 9, 0), "Got unexpected rectangle {%f, %f, %f, %f}.\n", destination_rects[0].left, destination_rects[0].top, destination_rects[0].right, destination_rects[0].bottom); @@ -18204,20 +18204,20 @@ static void test_sprite_batches(BOOL d3d11) hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, test_destination_rect, NULL, NULL, NULL, sizeof(test_destination_rect->top), 0, 0, 0); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); - todo_wine ok(sprite_count == 2, "Expected sprite count of 2 got %d\n", sprite_count); + ok(sprite_count == 2, "Expected sprite count of 2 got %d\n", sprite_count); hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 2, destination_rects, source_rects, colors, transforms); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); - todo_wine ok(compare_rect(&destination_rects[0], 0, 0, 4, 4, 0), + ok(compare_rect(&destination_rects[0], 0, 0, 4, 4, 0), "Got unexpected rectangle {%f, %f, %f, %f}.\n", destination_rects[0].left, destination_rects[0].top, destination_rects[0].right, destination_rects[0].bottom); - todo_wine ok(compare_rect(&destination_rects[1], 0, 4, 4, 5, 0), + ok(compare_rect(&destination_rects[1], 0, 4, 4, 5, 0), "Got unexpected rectangle {%f, %f, %f, %f}.\n", destination_rects[1].left, destination_rects[1].top, destination_rects[1].right, destination_rects[1].bottom); @@ -18228,42 +18228,42 @@ static void test_sprite_batches(BOOL d3d11) hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, test_destination_rect, NULL, NULL, NULL, sizeof(*test_destination_rect), 0, 0, 0); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); - todo_wine ok(sprite_count == 2, "Expected sprite count of 2 got %d\n", sprite_count); + ok(sprite_count == 2, "Expected sprite count of 2 got %d\n", sprite_count); hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 4, destination_rects, source_rects, colors, transforms); - todo_wine ok(hr == E_INVALIDARG, "Got unexpected hr %#lx\n", hr); + ok(hr == E_INVALIDARG, "Got unexpected hr %#lx\n", hr); hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 3, 1, destination_rects, source_rects, colors, transforms); - todo_wine ok(hr == E_INVALIDARG, "Got unexpected hr %#lx\n", hr); + ok(hr == E_INVALIDARG, "Got unexpected hr %#lx\n", hr); hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 3, 0, destination_rects, source_rects, colors, transforms); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 0, destination_rects, source_rects, colors, transforms); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 2, NULL, NULL, NULL, NULL); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 2, destination_rects, source_rects, colors, transforms); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 2, NULL, source_rects, NULL, NULL); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 2, destination_rects, NULL, NULL, NULL); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); - todo_wine ok(compare_rect(&destination_rects[0], 0, 0, 4, 4, 0), + ok(compare_rect(&destination_rects[0], 0, 0, 4, 4, 0), "Got unexpected rectangle {%f, %f, %f, %f}.\n", destination_rects[0].left, destination_rects[0].top, destination_rects[0].right, destination_rects[0].bottom); - todo_wine ok(compare_rect(&destination_rects[1], 5, 5, 9, 9, 0), + ok(compare_rect(&destination_rects[1], 5, 5, 9, 9, 0), "Got unexpected rectangle {%f, %f, %f, %f}.\n", destination_rects[1].left, destination_rects[1].top, destination_rects[1].right, destination_rects[1].bottom); @@ -18278,41 +18278,41 @@ static void test_sprite_batches(BOOL d3d11) hr = ID2D1DeviceContext3_EndDraw(device, 0, 0); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); check = compare_surface(&ctx, "5e8144c13d1a71f0b59d54eba7156218693fa7bd"); - todo_wine ok(check, "Surface does not match.\n"); + ok(check, "Surface does not match.\n"); ID2D1DeviceContext3_BeginDraw(device); ID2D1DeviceContext3_DrawSpriteBatch(device, sprite_batch, 0, 2, bitmap, 0, D2D1_SPRITE_OPTIONS_NONE); hr = ID2D1DeviceContext3_EndDraw(device, 0, 0); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); check = compare_surface(&ctx, "064e9e8b46799904cab52b14d69a235d4f41409b"); - todo_wine ok(check, "Surface does not match.\n"); + ok(check, "Surface does not match.\n"); ID2D1SpriteBatch_Clear(sprite_batch); sprite_count = ID2D1SpriteBatch_GetSpriteCount(sprite_batch); ok(sprite_count == 0, "Expected sprite count of 0 got %d\n", sprite_count); hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, test_destination_rect, test_source_rect, NULL, NULL, sizeof(*test_destination_rect), sizeof(*test_source_rect), 0, 0); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); hr = ID2D1SpriteBatch_GetSprites(sprite_batch, 0, 2, destination_rects, source_rects, NULL, NULL); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); - todo_wine ok(compare_rect(&destination_rects[0], 0, 0, 4, 4, 0), + ok(compare_rect(&destination_rects[0], 0, 0, 4, 4, 0), "Got unexpected rectangle {%f, %f, %f, %f}.\n", destination_rects[0].left, destination_rects[0].top, destination_rects[0].right, destination_rects[0].bottom); - todo_wine ok(compare_rect(&destination_rects[1], 5, 5, 9, 9, 0), + ok(compare_rect(&destination_rects[1], 5, 5, 9, 9, 0), "Got unexpected rectangle {%f, %f, %f, %f}.\n", destination_rects[1].left, destination_rects[1].top, destination_rects[1].right, destination_rects[1].bottom); - todo_wine ok(compare_rect_u(&source_rects[0], 0, 0, 4, 4), + ok(compare_rect_u(&source_rects[0], 0, 0, 4, 4), "Got unexpected rectangle {%u, %u, %u, %u}.\n", source_rects[0].left, source_rects[0].top, source_rects[0].right, source_rects[0].bottom); - todo_wine ok(compare_rect_u(&source_rects[1], 1, 1, 4, 4), + ok(compare_rect_u(&source_rects[1], 1, 1, 4, 4), "Got unexpected rectangle {%u, %u, %u, %u}.\n", source_rects[1].left, source_rects[1].top, source_rects[1].right, source_rects[1].bottom); @@ -18322,7 +18322,7 @@ static void test_sprite_batches(BOOL d3d11) hr = ID2D1DeviceContext3_EndDraw(device, 0, 0); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); check = compare_surface(&ctx, "9133aab139f4c0ec2edea7e3c63069370ff88083"); - todo_wine ok(check, "Surface does not match.\n"); + ok(check, "Surface does not match.\n"); ID2D1SpriteBatch_Clear(sprite_batch); @@ -18332,7 +18332,7 @@ static void test_sprite_batches(BOOL d3d11) set_matrix_identity(&test_matrixes[1]); scale_matrix(&test_matrixes[1], 4.0f, 4.0f); hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, test_destination_rect, test_source_rect, NULL, test_matrixes, sizeof(*test_destination_rect), sizeof(*test_source_rect), 0, sizeof(D2D1_MATRIX_3X2_F)); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); ID2D1DeviceContext3_BeginDraw(device); ID2D1DeviceContext3_DrawSpriteBatch(device, sprite_batch, 0, 2, bitmap, 0, D2D1_SPRITE_OPTIONS_NONE); @@ -18347,7 +18347,7 @@ static void test_sprite_batches(BOOL d3d11) ok(sprite_count == 0, "Expected sprite count of 0 got %d\n", sprite_count); hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, test_destination_rect, test_source_rect, test_colors, NULL, sizeof(*test_destination_rect), sizeof(*test_source_rect), sizeof(D2D1_COLOR_F), 0); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); ID2D1DeviceContext3_BeginDraw(device); ID2D1DeviceContext3_DrawSpriteBatch(device, sprite_batch, 0, 2, bitmap, 0, D2D1_SPRITE_OPTIONS_NONE); @@ -18364,7 +18364,7 @@ static void test_sprite_batches(BOOL d3d11) set_matrix_identity(&test_matrixes[1]); skew_matrix(&test_matrixes[1], 0.5f, 0.5f); hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, test_destination_rect, test_source_rect, NULL, test_matrixes, sizeof(*test_destination_rect), sizeof(*test_source_rect), 0, sizeof(D2D1_MATRIX_3X2_F)); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); ID2D1DeviceContext3_BeginDraw(device); ID2D1DeviceContext3_DrawSpriteBatch(device, sprite_batch, 0, 2, bitmap, 0, D2D1_SPRITE_OPTIONS_NONE); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11249
From: Santino Mazza <smazza@codeweavers.com> --- dlls/d2d1/d2d1_private.h | 1 + dlls/d2d1/device.c | 8 ++++++++ dlls/d2d1/sprite_batch.c | 23 +++++++++++++++++------ dlls/d2d1/tests/d2d1.c | 8 ++++---- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h index 32bc70aa2fa..d451e73c1f3 100644 --- a/dlls/d2d1/d2d1_private.h +++ b/dlls/d2d1/d2d1_private.h @@ -464,6 +464,7 @@ struct d2d_sprite { D2D1_RECT_F destination_rectangle; D2D1_RECT_U source_rectangle; + D2D1_MATRIX_3X2_F transform_matrix; }; struct d2d_sprite_batch diff --git a/dlls/d2d1/device.c b/dlls/d2d1/device.c index 2fffd2cc074..2b330a91047 100644 --- a/dlls/d2d1/device.c +++ b/dlls/d2d1/device.c @@ -3066,6 +3066,7 @@ static void STDMETHODCALLTYPE d2d_device_context_DrawSpriteBatch(ID2D1DeviceCont { struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); struct d2d_sprite_batch *sprite_batch_impl = unsafe_impl_from_ID2D1SpriteBatch(sprite_batch); + D2D1_MATRIX_3X2_F prev_transform; struct d2d_sprite *sprite; D2D1_RECT_F source_rect; @@ -3086,16 +3087,23 @@ static void STDMETHODCALLTYPE d2d_device_context_DrawSpriteBatch(ID2D1DeviceCont } else { + ID2D1DeviceContext6_GetTransform(iface, &prev_transform); + for (int i = start_index; i < start_index + sprite_count; ++i) { sprite = &sprite_batch_impl->sprites[i]; convert_rect_u_to_rect_f(&sprite->source_rectangle, &source_rect); + ID2D1DeviceContext6_SetTransform(iface, &sprite->transform_matrix); + ID2D1DeviceContext6_DrawBitmap(iface, bitmap, &sprite->destination_rectangle, 1.0f, (D2D1_INTERPOLATION_MODE)interpolation_mode, &source_rect, 0); } + + ID2D1DeviceContext6_SetTransform(iface, &prev_transform); } + } static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateSvgGlyphStyle(ID2D1DeviceContext6 *iface, diff --git a/dlls/d2d1/sprite_batch.c b/dlls/d2d1/sprite_batch.c index 1f59e23e533..d01e27642a0 100644 --- a/dlls/d2d1/sprite_batch.c +++ b/dlls/d2d1/sprite_batch.c @@ -21,6 +21,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(d2d); +static const D2D1_MATRIX_3X2_F identity = +{{{ + 1.0f, 0.0f, + 0.0f, 1.0f, + 0.0f, 0.0f, +}}}; + static BOOL array_reserve(void **elements, size_t *capacity, size_t count, size_t size) { unsigned int max_capacity, new_capacity; @@ -131,9 +138,6 @@ static HRESULT STDMETHODCALLTYPE d2d_sprite_batch_AddSprites(ID2D1SpriteBatch *i if (colors) FIXME("Color mask not implemented\n"); - if (transforms) - FIXME("Transform matrixes not implemented.\n"); - array_reserve((void**)&batch->sprites, &batch->sprites_allocated, batch->sprite_count + sprite_count, sizeof(struct d2d_sprite)); for (int i = 0; i < sprite_count; ++i) @@ -146,6 +150,11 @@ static HRESULT STDMETHODCALLTYPE d2d_sprite_batch_AddSprites(ID2D1SpriteBatch *i sprite->source_rectangle = *(D2D1_RECT_U *)(((UCHAR*)source_rectangles) + i * source_rectangles_stride); else sprite->source_rectangle = (D2D1_RECT_U){0, 0, UINT_MAX, UINT_MAX}; + + if (transforms) + sprite->transform_matrix = *(D2D1_MATRIX_3X2_F *)(((UCHAR*)transforms) + i * transforms_stride); + else + sprite->transform_matrix = identity; } batch->sprite_count += sprite_count; @@ -176,9 +185,6 @@ static HRESULT STDMETHODCALLTYPE d2d_sprite_batch_SetSprites(ID2D1SpriteBatch *i if (colors) FIXME("Color mask not implemented\n"); - if (transforms) - FIXME("Transform matrixes not implemented\n"); - for (int i = start_index; i < start_index + sprite_count; ++i) { sprite = &batch->sprites[i]; @@ -187,6 +193,9 @@ static HRESULT STDMETHODCALLTYPE d2d_sprite_batch_SetSprites(ID2D1SpriteBatch *i if (source_rectangles) sprite->source_rectangle = *(D2D1_RECT_U *)(((UCHAR*)source_rectangles) + i * source_rectangles_stride); + + if (transforms) + sprite->transform_matrix = *(D2D1_MATRIX_3X2_F *)(((UCHAR*)transforms) + i * transforms_stride); } return S_OK; @@ -218,6 +227,8 @@ static HRESULT STDMETHODCALLTYPE d2d_sprite_batch_GetSprites(ID2D1SpriteBatch *i if (source_rectangles) source_rectangles[i] = sprite->source_rectangle; + if (transforms) + transforms[i] = sprite->transform_matrix; } return S_OK; diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c index 35770ddfeb5..cc0a7e5f0c5 100644 --- a/dlls/d2d1/tests/d2d1.c +++ b/dlls/d2d1/tests/d2d1.c @@ -18139,8 +18139,8 @@ static void test_sprite_batches(BOOL d3d11) source_rects[1].left, source_rects[1].top, source_rects[1].right, source_rects[1].bottom); - todo_wine ok(!memcmp(&transforms[0], &identity, sizeof(identity)), "Expected identity matrix.\n"); - todo_wine ok(!memcmp(&transforms[1], &identity, sizeof(identity)), "Expected identity matrix.\n"); + ok(!memcmp(&transforms[0], &identity, sizeof(identity)), "Expected identity matrix.\n"); + ok(!memcmp(&transforms[1], &identity, sizeof(identity)), "Expected identity matrix.\n"); hr = ID2D1SpriteBatch_AddSprites(sprite_batch, 2, test_destination_rect, NULL, NULL, NULL, sizeof(*test_destination_rect), 0, 0, 0); ok(hr == S_OK, "Got unexpected hr %#lx\n", hr); @@ -18339,7 +18339,7 @@ static void test_sprite_batches(BOOL d3d11) hr = ID2D1DeviceContext3_EndDraw(device, 0, 0); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); check = compare_surface(&ctx, "30bf2de6f4f10ae8ebfc61261ad0d0a4abfed094"); - todo_wine ok(check, "Surface does not match.\n"); + ok(check, "Surface does not match.\n"); ID2D1SpriteBatch_Clear(sprite_batch); @@ -18371,7 +18371,7 @@ static void test_sprite_batches(BOOL d3d11) hr = ID2D1DeviceContext3_EndDraw(device, 0, 0); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); check = compare_surface(&ctx, "72d4c7723073eabc2d7d1939b6f41040546b21f7"); - todo_wine ok(check, "Surface does not match.\n"); + todo_wine ok(check, "Surface does not match.\n"); // FIXME: Anti Aliasing issue? If instead of skewing I just translate or scale the surface matches. ID2D1DeviceContext3_SetAntialiasMode(device, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11249
On Wed Jul 29 02:30:03 2026 +0000, Santino Mazza wrote:
I'm not entirely sure either, but this is what Windows seems to do by looking at how the CommandList calls the drawing methods of a stub CommandSink that doesn't support DrawSpriteBatch. https://gitlab.winehq.org/tati/wine/-/commit/a3da90e7a843eab3b0bb1bb34a5cf91... I looked a bit more, it's not what happens. What we should do is to create a separate bitmap as a "target", render batch to it, and then DrawBitmap() with it. That's why those maximum extents are used. Using original source bitmap like this is not meaningful. I'll resubmit this without with this compat part stubbed.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11249#note_148404
Todo list for this one would be: - [ ] add a separate rendering case just for sprites. It would be much simpler shaders than for geometries - you'll need a vertex shaders to apply transform(s), and pixel shader to just sample and modulate with given color; - [ ] for command list streaming fallback case it will have to create temporary target bitmap of sufficient size based on union of destination rectangles, then render to it, and use always available DrawBitmap(). This fallback logic can definitely wait. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11249#note_148831
participants (3)
-
Nikolay Sivov (@nsivov) -
Santino Mazza -
Santino Mazza (@tati)