From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/colorcnv/tests/Makefile.in | 3 +- dlls/colorcnv/tests/colorcnv.c | 269 ++++++++++++++++++++++++ dlls/colorcnv/tests/resource.rc | 28 +++ dlls/colorcnv/tests/rgb24frame_flip.bmp | Bin 0 -> 27702 bytes dlls/colorcnv/tests/rgb32frame.bmp | Bin 0 -> 36918 bytes 5 files changed, 299 insertions(+), 1 deletion(-) create mode 100644 dlls/colorcnv/tests/resource.rc create mode 100644 dlls/colorcnv/tests/rgb24frame_flip.bmp create mode 100755 dlls/colorcnv/tests/rgb32frame.bmp diff --git a/dlls/colorcnv/tests/Makefile.in b/dlls/colorcnv/tests/Makefile.in index 2ec3b1231f4..ec48b88392f 100644 --- a/dlls/colorcnv/tests/Makefile.in +++ b/dlls/colorcnv/tests/Makefile.in @@ -2,4 +2,5 @@ TESTDLL = colorcnv.dll IMPORTS = ole32 msdmo dmoguids wmcodecdspuuid strmiids uuid SOURCES = \ - colorcnv.c + colorcnv.c \ + resource.rc diff --git a/dlls/colorcnv/tests/colorcnv.c b/dlls/colorcnv/tests/colorcnv.c index c42fd6766ff..6030a4100fc 100644 --- a/dlls/colorcnv/tests/colorcnv.c +++ b/dlls/colorcnv/tests/colorcnv.c @@ -27,6 +27,189 @@ #include <amvideo.h> #include <dmort.h> +#pragma pack(push, 2) +struct bmp_header +{ + char magic[2]; + DWORD length; + DWORD reserved; + DWORD offset; +}; +#pragma pack(pop) + +struct media_buffer +{ + IMediaBuffer IMediaBuffer_iface; + LONG refcount; + + BYTE *ptr; + DWORD length; + DWORD max_length; +}; + +static struct media_buffer *media_buffer_from_IMediaBuffer(IMediaBuffer *iface) +{ + return CONTAINING_RECORD(iface, struct media_buffer, IMediaBuffer_iface); +} + +static WINAPI HRESULT media_buffer_QueryInterface(IMediaBuffer *iface, REFIID riid, void **obj) +{ + if (IsEqualGUID(riid, &IID_IMediaBuffer) || + IsEqualGUID(riid, &IID_IUnknown)) + { + *obj = iface; + } + else + { + *obj = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*obj); + return S_OK; +} + +static WINAPI ULONG media_buffer_AddRef(IMediaBuffer *iface) +{ + struct media_buffer *media_buffer = media_buffer_from_IMediaBuffer(iface); + ULONG refcount; + + refcount = InterlockedIncrement(&media_buffer->refcount); + + return refcount; +} + +static WINAPI ULONG media_buffer_Release(IMediaBuffer *iface) +{ + struct media_buffer *media_buffer = media_buffer_from_IMediaBuffer(iface); + ULONG refcount; + + refcount = InterlockedDecrement(&media_buffer->refcount); + + if (!refcount) + free(media_buffer); + + return refcount; +} + +static WINAPI HRESULT media_buffer_SetLength(IMediaBuffer *iface, DWORD length) +{ + struct media_buffer *media_buffer = media_buffer_from_IMediaBuffer(iface); + + media_buffer->length = length; + + return S_OK; +} + +static WINAPI HRESULT media_buffer_GetMaxLength(IMediaBuffer *iface, DWORD *max_length) +{ + struct media_buffer *media_buffer = media_buffer_from_IMediaBuffer(iface); + + *max_length = media_buffer->max_length; + + return S_OK; +} + +static WINAPI HRESULT media_buffer_GetBufferAndLength(IMediaBuffer *iface, BYTE **ptr, DWORD *length) +{ + struct media_buffer *media_buffer = media_buffer_from_IMediaBuffer(iface); + + if (ptr) + *ptr = media_buffer->ptr; + if (length) + *length = media_buffer->length; + + return S_OK; +} + +static IMediaBufferVtbl media_buffer_vtbl = +{ + media_buffer_QueryInterface, + media_buffer_AddRef, + media_buffer_Release, + media_buffer_SetLength, + media_buffer_GetMaxLength, + media_buffer_GetBufferAndLength, +}; + +static struct media_buffer *create_media_buffer(BYTE *ptr, DWORD length, DWORD max_length) +{ + struct media_buffer *media_buffer; + + media_buffer = calloc(1, sizeof(*media_buffer)); + media_buffer->IMediaBuffer_iface.lpVtbl = &media_buffer_vtbl; + media_buffer->refcount = 1; + + media_buffer->ptr = ptr; + media_buffer->length = length; + media_buffer->max_length = max_length; + + return media_buffer; +} + +static struct media_buffer *resource_as_media_buffer(const WCHAR *resource_name) +{ + struct bmp_header *header; + HRSRC resource; + BYTE *data; + DWORD size; + + resource = FindResourceW(NULL, L"rgb24frame_flip.bmp", (const WCHAR *)RT_RCDATA); + header = (struct bmp_header *)LockResource(LoadResource(GetModuleHandleW(NULL), resource)); + data = (BYTE *)header + header->offset; + size = header->length - header->offset; + + return create_media_buffer(data, size, size); +} + +static void write_bitmap_to_file(const BITMAPINFOHEADER *bitmap_header, const BYTE *data, HANDLE output) +{ + struct bmp_header header = + { + .magic = "BM", + .length = sizeof(header) + sizeof(*bitmap_header) + bitmap_header->biSizeImage, + .offset = sizeof(header) + sizeof(*bitmap_header), + }; + DWORD written; + BOOL ret; + + ret = WriteFile(output, &header, sizeof(header), &written, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(written == sizeof(header), "written %lu bytes\n", written); + ret = WriteFile(output, bitmap_header, sizeof(*bitmap_header), &written, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(written == sizeof(*bitmap_header), "written %lu bytes\n", written); + ret = WriteFile(output, data, bitmap_header->biSizeImage, &written, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(written == bitmap_header->biSizeImage, "written %lu bytes\n", written); +} + +static HANDLE open_temp_file(const WCHAR *output_filename) +{ + WCHAR path[MAX_PATH]; + HANDLE output; + + GetTempPathW(ARRAY_SIZE(path), path); + lstrcatW(path, output_filename); + + output = CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + ok(output != INVALID_HANDLE_VALUE, "CreateFileW failed, error %lu\n", GetLastError()); + + trace("created %s\n", debugstr_w(path)); + + return output; +} + +static void write_bitmap(const BITMAPINFOHEADER *bitmap_header, const BYTE *data, const WCHAR *output_filename) +{ + HANDLE output; + + output = open_temp_file(output_filename); + write_bitmap_to_file(bitmap_header, data, output); + + CloseHandle(output); +} + static void test_media_object_media_types(void) { static const GUID *input_types[] = @@ -239,10 +422,95 @@ static void test_media_object_media_types(void) ok(hr == E_INVALIDARG, "Got hr %#lx.\n", hr); video_info.bmiHeader.biSizeImage = 96 * 96 * 4; + hr = IMediaObject_SetOutputType(media_object, 0, &mt, 0); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + refcount = IMediaObject_Release(media_object); + ok(refcount == 0, "Unexpected refcount %lu.\n", refcount); + + CoUninitialize(); +} + +void test_media_object_color_conversion(void) +{ + struct media_buffer *input_buffer, *output_buffer; + const struct bmp_header *expect_rgb32_header; + DMO_OUTPUT_DATA_BUFFER output_data; + VIDEOINFOHEADER video_info = {0}; + const BYTE *expect_rgb32_data; + BYTE rgb32_data[96 * 96 * 4]; + IMediaObject *media_object; + DMO_MEDIA_TYPE mt = {0}; + DWORD status, diff; + HRSRC resource; + ULONG refcount; + HRESULT hr; + + hr = CoInitialize(NULL); + ok(hr == S_OK, "CoInitialize failed, hr %#lx.\n", hr); + + hr = CoCreateInstance(&CLSID_CColorConvertDMO, NULL, CLSCTX_INPROC_SERVER, &IID_IMediaObject, + (void **)&media_object); + ok(hr == S_OK, "Failed to create media object, hr %#lx.\n", hr); + + mt.majortype = MEDIATYPE_Video; + mt.subtype = MEDIASUBTYPE_RGB24; + mt.bFixedSizeSamples = TRUE; + mt.lSampleSize = 96 * 96 * 3; + mt.formattype = FORMAT_VideoInfo; + mt.cbFormat = sizeof(video_info); + mt.pbFormat = (BYTE *)&video_info; + video_info.bmiHeader.biSize = sizeof(video_info.bmiHeader); + video_info.bmiHeader.biWidth = 96; + video_info.bmiHeader.biHeight = 96; + video_info.bmiHeader.biPlanes = 1; + video_info.bmiHeader.biBitCount = 24; + video_info.bmiHeader.biCompression = BI_RGB; + video_info.bmiHeader.biSizeImage = 96 * 96 * 3; hr = IMediaObject_SetInputType(media_object, 0, &mt, 0); todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + mt.subtype = MEDIASUBTYPE_RGB32; + mt.lSampleSize = 96 * 96 * 4; + video_info.bmiHeader.biHeight = -96; + video_info.bmiHeader.biBitCount = 32; + video_info.bmiHeader.biSizeImage = 96 * 96 * 4; + hr = IMediaObject_SetOutputType(media_object, 0, &mt, 0); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + input_buffer = resource_as_media_buffer(L"rgb24frame_flip.bmp"); + hr = IMediaObject_ProcessInput(media_object, 0, &input_buffer->IMediaBuffer_iface, 0, 0, 0); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + output_buffer = create_media_buffer(rgb32_data, 0, sizeof(rgb32_data)); + output_data.pBuffer = &output_buffer->IMediaBuffer_iface; + hr = IMediaObject_ProcessOutput(media_object, 0, 1, &output_data, &status); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + write_bitmap(&video_info.bmiHeader, rgb32_data, L"rgb32frame.bmp"); + + resource = FindResourceW(NULL, L"rgb32frame.bmp", (const WCHAR *)RT_RCDATA); + expect_rgb32_header = ((const struct bmp_header *)LockResource(LoadResource(GetModuleHandleW(NULL), resource))); + expect_rgb32_data = (const BYTE *)expect_rgb32_header + expect_rgb32_header->offset; + + diff = 0; + for (unsigned int i = 0; i < mt.lSampleSize; ++i) + diff += abs((int)output_buffer->ptr[i] - (int)expect_rgb32_data[i]); + diff = diff * 100 / 256 / mt.lSampleSize; + todo_wine + ok(diff == 0, "Got %lu%% difference.\n", diff); + + refcount = IMediaBuffer_Release(&input_buffer->IMediaBuffer_iface); + ok(refcount == 0, "Unexpected refcount %lu.\n", refcount); + + refcount = IMediaBuffer_Release(&output_buffer->IMediaBuffer_iface); + ok(refcount == 0, "Unexpected refcount %lu.\n", refcount); + refcount = IMediaObject_Release(media_object); ok(refcount == 0, "Unexpected refcount %lu.\n", refcount); @@ -252,4 +520,5 @@ static void test_media_object_media_types(void) START_TEST(colorcnv) { test_media_object_media_types(); + test_media_object_color_conversion(); } diff --git a/dlls/colorcnv/tests/resource.rc b/dlls/colorcnv/tests/resource.rc new file mode 100644 index 00000000000..123ddb5cdbf --- /dev/null +++ b/dlls/colorcnv/tests/resource.rc @@ -0,0 +1,28 @@ +/* + * Copyright 2026 Brendan McGrath 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 + */ + +/* Generated with: */ +/* ffmpeg -f lavfi -i pal100bars=size=96x96 \ + * -vf transpose -pix_fmt bgr24 -frames:v 1 \ + * rgb24frame_flip.bmp */ +/* @makedep: rgb24frame_flip.bmp */ +rgb24frame_flip.bmp RCDATA rgb24frame_flip.bmp + +/* Generated by running the media object color conversion test on Windows */ +/* @makedep: rgb32frame.bmp */ +rgb32frame.bmp RCDATA rgb32frame.bmp diff --git a/dlls/colorcnv/tests/rgb24frame_flip.bmp b/dlls/colorcnv/tests/rgb24frame_flip.bmp new file mode 100644 index 0000000000000000000000000000000000000000..7795fcf3989b8bf6594935c15eedf22829f14229 GIT binary patch literal 27702 zcmZ?rHOpZD12YB&1`P%Vh6E^PWRL)hGeG4boKbQ#1V%$(Gz3ONU^E0qLtr!nMnhmU z1V%$(Gz3ONfSw`nkAYznjfTKz2#kinXb6mkz-S1JhQMeDjE2By2#kinXb8|D1Q;0p zj-t^J7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu;sI)uPqhJT}IGz3ONU^E0qLtr!n zMnhmU1V%$(Gz3ONU^E0qLx2t;FuMA1Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0q zLx4UZ@c%!<C>jlc(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc44LkKYZ{X2?ALtr!n zMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1n3X~|3|@S2#kinXb6mkz-S1JhQMeDjE2By Q2#kinXb6mk09`@=09!zwZ~y=R literal 0 HcmV?d00001 diff --git a/dlls/colorcnv/tests/rgb32frame.bmp b/dlls/colorcnv/tests/rgb32frame.bmp new file mode 100755 index 0000000000000000000000000000000000000000..7a4298bf8e1804b7e84072280290ddd0684cfa25 GIT binary patch literal 36918 zcmZ?rHJiWy24)Nl3>pj!3<(Sj3=97M|If&v02XI}%0oC1^8Y9p4S~@R7!85Z5Eu=C z(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c)*-;~_wWBvJQ@b0X<#%BjHZFnG%%V5 zM$^D(8W>FjqiJ9?4UDFN(KIlc21e7sXc`#4Y2g2VhX12@Gz>=5z-Ss6O#`E8U^ESk zrh(BkFq#HN)4*sN7)=ACX<#%BjHZFnG%$SA0K-3q|D$*`3`WzyXc`zz1EXnRG!2ZV zfzdQDng&MGz-Ss6O#`E8U^ESkrh(BkFnrU%Uxt7GNAYMFjHZFnG%%V5M$^D(8W>Fj zqiJ9?4UDFN(KIlc21e7sXc`zz1EXnR_@)5{hQI$u@n{%~rh(BkFq#HN)4*sN7)=AC zX<#%BjHZFnG%%V5M$^D(8W>FjqiJCHrh$J94F5;*Xc&y9fzdQDng&MGz-Ss6O#`E8 zU^ESkrh(BkFq#HN)4*sN7)=ACX<+!K0R{$!|D$*`3`WzyXc`zz1EXnRG!2ZVfzdQD cng&MGz-Ss6O#`E8U^ESkrh(BkFnrSh02ft&82|tP literal 0 HcmV?d00001 -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10709