From: Henri Verbeet Subject: Re: [PATCH 1/3] d2d1/tests: Enable developers to export generated images. Message-Id: Date: Tue, 21 Jan 2020 18:41:17 +0330 In-Reply-To: <20200121120207.2090944-1-gio@debian.org> References: <20200121120207.2090944-1-gio@debian.org> On Tue, 21 Jan 2020 at 15:32, Giovanni Mascellani wrote: > Is this kind of developer-oriented patch acceptable in Wine? Or is > there already an easy way to export the bitmaps generated during the > test? I could not find one. > I've been using something like the attached patch, mostly for getting data out of the testbot. For local debugging, in many cases it's enough to simply add a IDXGISwapchain_Present() call followed by a Sleep() in the appropriated place. I think the general sentiment is that these kinds of debugging patches are trivial enough for individual developers to write on their own when needed, but personally I'm not necessarily opposed to having something like this in the upstream tree. diff --git a/dlls/d2d1/tests/Makefile.in b/dlls/d2d1/tests/Makefile.in index 91ede7888aa..9c3d3ec393f 100644 --- a/dlls/d2d1/tests/Makefile.in +++ b/dlls/d2d1/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = d2d1.dll -IMPORTS = d2d1 d3d10_1 dwrite dxguid uuid user32 advapi32 ole32 gdi32 +IMPORTS = d2d1 d3d10_1 dwrite dxguid uuid user32 advapi32 shlwapi ole32 gdi32 C_SRCS = \ d2d1.c diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c index 0ed51c7f805..21931ed81db 100644 --- a/dlls/d2d1/tests/d2d1.c +++ b/dlls/d2d1/tests/d2d1.c @@ -42,6 +42,8 @@ struct resource_readback unsigned int width, height; }; +IStream * WINAPI SHCreateMemStream(const BYTE *data, UINT data_size); + struct figure { unsigned int *spans; @@ -106,6 +108,8 @@ static DWORD WINAPI thread_func(void *ctx) { LONG *i = ctx, j; + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + while (*i < mt_test_count) { j = *i; @@ -113,6 +117,8 @@ static DWORD WINAPI thread_func(void *ctx) mt_tests[j].test(); } + CoUninitialize(); + return 0; } @@ -345,6 +351,198 @@ static DWORD get_readback_colour(struct resource_readback *rb, unsigned int x, u return ((DWORD *)((BYTE *)rb->map_desc.pData + y * rb->map_desc.RowPitch))[x]; } +struct base64_state +{ + BYTE input[3]; + unsigned int count; +}; + +static const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static void base64_encode_quad(const BYTE *p, char *q) +{ + q[0] = base64_chars[(p[0] & 0xfc) >> 2]; + q[1] = base64_chars[((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4)]; + q[2] = base64_chars[((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6)]; + q[3] = base64_chars[p[2] & 0x3f]; +} + +void base64_encode(struct base64_state *state, const BYTE *input, unsigned int count, char **output) +{ + const BYTE *p = input; + char *q = *output; + + if (state->count) + { + unsigned int c = min(count, 3 - state->count); + memcpy(&state->input[state->count], p, c); + state->count += c; + + if (state->count < 3) + goto done; + + base64_encode_quad(state->input, q); + state->count = 0; + + count -= c; + p += c; + q += 4; + } + + while (count >= 3) + { + base64_encode_quad(p, q); + count -= 3; + p += 3; + q += 4; + } + + state->count = count; + memcpy(state->input, p, count); +done: + *output = q; +} + +void base64_encode_finish(struct base64_state *state, char **output) +{ + const BYTE *p = state->input; + char *q = *output; + + if (!state->count) + return; + + q[0] = base64_chars[(p[0] & 0xfc) >> 2]; + if (state->count < 2) + { + q[1] = base64_chars[(p[0] & 0x03) << 4]; + q[2] = '='; + q[3] = '='; + goto done; + } + + q[1] = base64_chars[((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4)]; + if (state->count < 3) + { + q[2] = base64_chars[((p[1] & 0x0f) << 2)]; + q[3] = '='; + goto done; + } + + q[2] = base64_chars[((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6)]; + q[3] = base64_chars[p[2] & 0x3f]; + +done: + state->count = 0; + *output = q + 4; +} + +static void base64_encode_png(void *data, unsigned int pitch, unsigned int width, unsigned int height) +{ + struct base64_state base64_state = {{0}}; + WICPixelFormatGUID format_guid; + IPropertyBag2 *options = NULL; + IWICBitmapFrameEncode *frame; + IWICImagingFactory *factory; + IWICBitmapEncoder *encoder; + unsigned int output_size; + LARGE_INTEGER p = {{0}}; + char *output, *ptr; + BYTE buffer[4096]; + IStream *stream; + ULONG count; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, + &IID_IWICImagingFactory, (void **)&factory); + ok(SUCCEEDED(hr), "Failed to create WIC imaging factory, hr %#x.\n", hr); + + hr = IWICImagingFactory_CreateEncoder(factory, &GUID_ContainerFormatPng, NULL, &encoder); + ok(SUCCEEDED(hr), "Failed to create WIC encoder, hr %#x.\n", hr); + stream = SHCreateMemStream(NULL, 0); + hr = IWICBitmapEncoder_Initialize(encoder, stream, WICBitmapEncoderNoCache); + ok(SUCCEEDED(hr), "Failed to initialize WIC encoder, hr %#x.\n", hr); + hr = IWICBitmapEncoder_CreateNewFrame(encoder, &frame, &options); + ok(SUCCEEDED(hr), "Failed to create frame, hr %#x.\n", hr); + IWICBitmapFrameEncode_Initialize(frame, NULL); + ok(SUCCEEDED(hr), "Failed to initialize frame, hr %#x.\n", hr); + + hr = IWICBitmapFrameEncode_SetSize(frame, width, height); + ok(SUCCEEDED(hr), "Failed to set frame size, hr %#x.\n", hr); + format_guid = GUID_WICPixelFormat32bppBGRA; + hr = IWICBitmapFrameEncode_SetPixelFormat(frame, &format_guid); + ok(IsEqualGUID(&format_guid, &GUID_WICPixelFormat32bppBGRA), "Failed to set pixel format.\n"); + hr = IWICBitmapFrameEncode_WritePixels(frame, height, pitch, height * pitch, data); + ok(SUCCEEDED(hr), "Failed to write pixels, hr %#x.\n", hr); + + hr = IWICBitmapFrameEncode_Commit(frame); + ok(SUCCEEDED(hr), "Failed to commit frame, hr %#x.\n", hr); + IWICBitmapEncoder_Commit(encoder); + ok(SUCCEEDED(hr), "Failed to commit encoder, hr %#x.\n", hr); + + IWICBitmapFrameEncode_Release(frame); + IWICBitmapEncoder_Release(encoder); + IWICImagingFactory_Release(factory); + + output_size = 8192; + output = heap_alloc(output_size); + ptr = output; + + hr = IStream_Seek(stream, p, STREAM_SEEK_SET, NULL); + ok(SUCCEEDED(hr), "Failed to seek, hr %#x.\n", hr); + do + { + unsigned int pos = ptr - output; + + hr = IStream_Read(stream, buffer, sizeof(buffer), &count); + ok(SUCCEEDED(hr), "Failed to read, hr %#x.\n", hr); + if (((count + 2) / 3) * 4 > output_size - pos) + { + output_size *= 2; + output = heap_realloc(output, output_size); + ptr = &output[pos]; + } + base64_encode(&base64_state, buffer, count, &ptr); + } while (count == sizeof(buffer)); + + IStream_Release(stream); + + if (base64_state.count) + { + unsigned int pos = ptr - output; + if (4 > output_size - pos) + { + output_size *= 2; + output = heap_realloc(output, output_size); + ptr = &output[pos]; + } + base64_encode_finish(&base64_state, &ptr); + } + + count = ptr - output; + ptr = output; + printf("begin-base64 600 d2d1.png\n"); + while (count > 100) + { + printf("%.100s\n", ptr); + count -= 100; + ptr += 100; + } + if (count) + printf("%.*s\n", count, ptr); + printf("====\n"); + + heap_free(output); +} + +static void dump_surface(IDXGISurface *surface) +{ + struct resource_readback rb; + + get_surface_readback(surface, &rb); + base64_encode_png(rb.map_desc.pData, rb.map_desc.RowPitch, rb.width, rb.height); + release_resource_readback(&rb); +} + static BOOL compare_colour(DWORD c1, DWORD c2, BYTE max_diff) { if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) @@ -1827,6 +2025,7 @@ static void test_bitmap_brush(void) ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); match = compare_surface(surface, "9437f4447d98feaad41a1c4202ee90aadc718ee6"); ok(match, "Surface does not match.\n"); +dump_surface(surface); ID2D1RenderTarget_BeginDraw(rt);