[PATCH v3 0/5] MR10467: iyuv/tests: Add stub DLL and tests.
This MR adds a stub for the `iyuv_32.dll` and tests its functionality. -- v3: iyuv_32/tests: Add a compression test. iyuv_32/tests: Add a decompression test. iyuv_32/tests: Test which formats are supported. iyuv_32/tests: Add test stub. iyuv_32: Add stub dll. https://gitlab.winehq.org/wine/wine/-/merge_requests/10467
From: Brendan McGrath <bmcgrath@codeweavers.com> --- configure.ac | 1 + dlls/iyuv_32/Makefile.in | 5 + dlls/iyuv_32/iyuv.c | 191 ++++++++++++++++++++++++++++++++++++++ dlls/iyuv_32/iyuv_32.spec | 1 + loader/wine.inf.in | 2 + 5 files changed, 200 insertions(+) create mode 100644 dlls/iyuv_32/Makefile.in create mode 100644 dlls/iyuv_32/iyuv.c create mode 100644 dlls/iyuv_32/iyuv_32.spec diff --git a/configure.ac b/configure.ac index 9a437851996..953edcce7b9 100644 --- a/configure.ac +++ b/configure.ac @@ -2880,6 +2880,7 @@ WINE_CONFIG_MAKEFILE(dlls/irprops.cpl) WINE_CONFIG_MAKEFILE(dlls/itircl) WINE_CONFIG_MAKEFILE(dlls/itss) WINE_CONFIG_MAKEFILE(dlls/itss/tests) +WINE_CONFIG_MAKEFILE(dlls/iyuv_32) WINE_CONFIG_MAKEFILE(dlls/joy.cpl) WINE_CONFIG_MAKEFILE(dlls/jscript) WINE_CONFIG_MAKEFILE(dlls/jscript/tests) diff --git a/dlls/iyuv_32/Makefile.in b/dlls/iyuv_32/Makefile.in new file mode 100644 index 00000000000..6b9b11569b1 --- /dev/null +++ b/dlls/iyuv_32/Makefile.in @@ -0,0 +1,5 @@ +MODULE = iyuv_32.dll +IMPORTS = user32 mfplat ole32 + +SOURCES = \ + iyuv.c diff --git a/dlls/iyuv_32/iyuv.c b/dlls/iyuv_32/iyuv.c new file mode 100644 index 00000000000..e114fde50e0 --- /dev/null +++ b/dlls/iyuv_32/iyuv.c @@ -0,0 +1,191 @@ +/* + * iyuv Video "Decoder" + * 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 + * + */ + +#include "windef.h" +#include <stdarg.h> +#include <stdlib.h> + +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" + +#include "commdlg.h" +#include "initguid.h" +#include "vfw.h" +#include "wmcodecdsp.h" + +#include "wine/debug.h" + +#define COBJMACROS +#include "mfapi.h" +#include "mferror.h" +#include "mfobjects.h" +#include "mfidl.h" +#include "mftransform.h" + +WINE_DEFAULT_DEBUG_CHANNEL(iyuv_32); + +static HINSTANCE IYUV_32_module; + +static LRESULT IYUV_Open(const ICINFO *icinfo) +{ + FIXME("DRV_OPEN %p\n", icinfo); + + return 0; +} + +static LRESULT IYUV_DecompressQuery(const BITMAPINFOHEADER *in, const BITMAPINFOHEADER *out) +{ + FIXME("ICM_DECOMPRESS_QUERY %p %p\n", in, out); + + return ICERR_UNSUPPORTED; +} + +static LRESULT IYUV_DecompressGetFormat(BITMAPINFOHEADER *in, BITMAPINFOHEADER *out) +{ + FIXME("ICM_DECOMPRESS_GETFORMAT %p %p\n", in, out); + + return ICERR_UNSUPPORTED; +} + +static LRESULT IYUV_DecompressBegin(IMFTransform *transform, const BITMAPINFOHEADER *in, const BITMAPINFOHEADER *out) +{ + FIXME("ICM_DECOMPRESS_BEGIN %p %p %p\n", transform, in, out); + + return ICERR_UNSUPPORTED; +} + +static LRESULT IYUV_Decompress(IMFTransform *transform, const ICDECOMPRESS *params) +{ + FIXME("ICM_DECOMPRESS %p %p\n", transform, params); + + return ICERR_UNSUPPORTED; +} + +static LRESULT IYUV_GetInfo(ICINFO *icinfo, DWORD size) +{ + FIXME("ICM_GETINFO %p %lu\n", icinfo, size); + + return ICERR_UNSUPPORTED; +} + +/*********************************************************************** + * DriverProc (IYUV_32.@) + */ +LRESULT WINAPI IYUV_DriverProc(DWORD_PTR driver_id, HDRVR hdrvr, UINT msg, LPARAM param1, LPARAM param2) +{ + IMFTransform *transform = (IMFTransform *)driver_id; + LRESULT r = ICERR_UNSUPPORTED; + + TRACE("%Id %p %04x %08Ix %08Ix\n", driver_id, hdrvr, msg, param1, param2); + + switch (msg) + { + case DRV_LOAD: + TRACE("DRV_LOAD\n"); + r = TRUE; + break; + + case DRV_OPEN: + r = IYUV_Open((ICINFO *)param2); + break; + + case DRV_CLOSE: + TRACE("DRV_CLOSE\n"); + if (transform) + IMFTransform_Release(transform); + r = TRUE; + break; + + case DRV_ENABLE: + case DRV_DISABLE: + case DRV_FREE: + break; + + case ICM_GETINFO: + r = IYUV_GetInfo((ICINFO *)param1, (DWORD)param2); + break; + + case ICM_DECOMPRESS_QUERY: + r = IYUV_DecompressQuery((BITMAPINFOHEADER *)param1, (BITMAPINFOHEADER *)param2); + break; + + case ICM_DECOMPRESS_GET_FORMAT: + r = IYUV_DecompressGetFormat((BITMAPINFOHEADER *)param1, (BITMAPINFOHEADER *)param2); + break; + + case ICM_DECOMPRESS_GET_PALETTE: + FIXME("ICM_DECOMPRESS_GET_PALETTE\n"); + break; + + case ICM_DECOMPRESS: + r = IYUV_Decompress(transform, (ICDECOMPRESS *)param1); + break; + + case ICM_DECOMPRESS_BEGIN: + r = IYUV_DecompressBegin(transform, (BITMAPINFOHEADER *)param1, (BITMAPINFOHEADER *)param2); + break; + + case ICM_DECOMPRESS_END: + r = ICERR_OK; + break; + + case ICM_DECOMPRESSEX_QUERY: + case ICM_DECOMPRESSEX_BEGIN: + case ICM_DECOMPRESSEX: + case ICM_DECOMPRESSEX_END: + /* unsupported */ + break; + + case ICM_COMPRESS_QUERY: + r = ICERR_BADFORMAT; + /* fall through */ + case ICM_COMPRESS_GET_FORMAT: + case ICM_COMPRESS_END: + case ICM_COMPRESS: + FIXME("compression not implemented\n"); + break; + + case ICM_CONFIGURE: + break; + + default: + FIXME("Unknown message: %04x %Id %Id\n", msg, param1, param2); + } + + return r; +} + +/*********************************************************************** + * DllMain + */ +BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID reserved) +{ + TRACE("(%p,%lu,%p)\n", module, reason, reserved); + + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(module); + IYUV_32_module = module; + break; + } + return TRUE; +} diff --git a/dlls/iyuv_32/iyuv_32.spec b/dlls/iyuv_32/iyuv_32.spec new file mode 100644 index 00000000000..a0c64b96696 --- /dev/null +++ b/dlls/iyuv_32/iyuv_32.spec @@ -0,0 +1 @@ +@ stdcall -private DriverProc(long long long long long) IYUV_DriverProc diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 8c58fb781e4..fc3f085e68b 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -887,6 +887,8 @@ system.ini, drivers32,,"vidc.mrle=msrle32.dll" system.ini, drivers32,,"vidc.msvc=msvidc32.dll" system.ini, drivers32,,"vidc.cvid=iccvid.dll" system.ini, drivers32,,"vidc.IV50=ir50_32.dll" +system.ini, drivers32,,"vidc.i420=iyuv_32.dll" +system.ini, drivers32,,"vidc.iyuv=iyuv_32.dll" system.ini, drivers32,,"; vidc.IV31=ir32_32.dll" system.ini, drivers32,,"; vidc.IV32=ir32_32.dll" -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10467
From: Brendan McGrath <bmcgrath@codeweavers.com> --- configure.ac | 1 + dlls/iyuv_32/tests/Makefile.in | 5 ++++ dlls/iyuv_32/tests/iyuv_32.c | 46 ++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 dlls/iyuv_32/tests/Makefile.in create mode 100644 dlls/iyuv_32/tests/iyuv_32.c diff --git a/configure.ac b/configure.ac index 953edcce7b9..bc898b9bbb5 100644 --- a/configure.ac +++ b/configure.ac @@ -2881,6 +2881,7 @@ WINE_CONFIG_MAKEFILE(dlls/itircl) WINE_CONFIG_MAKEFILE(dlls/itss) WINE_CONFIG_MAKEFILE(dlls/itss/tests) WINE_CONFIG_MAKEFILE(dlls/iyuv_32) +WINE_CONFIG_MAKEFILE(dlls/iyuv_32/tests) WINE_CONFIG_MAKEFILE(dlls/joy.cpl) WINE_CONFIG_MAKEFILE(dlls/jscript) WINE_CONFIG_MAKEFILE(dlls/jscript/tests) diff --git a/dlls/iyuv_32/tests/Makefile.in b/dlls/iyuv_32/tests/Makefile.in new file mode 100644 index 00000000000..05779d3c2dc --- /dev/null +++ b/dlls/iyuv_32/tests/Makefile.in @@ -0,0 +1,5 @@ +TESTDLL = iyuv_32.dll +IMPORTS = msvfw32 + +SOURCES = \ + iyuv_32.c diff --git a/dlls/iyuv_32/tests/iyuv_32.c b/dlls/iyuv_32/tests/iyuv_32.c new file mode 100644 index 00000000000..e6ca206dccf --- /dev/null +++ b/dlls/iyuv_32/tests/iyuv_32.c @@ -0,0 +1,46 @@ +/* + * 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 + */ + +#include <windef.h> +#include <wingdi.h> + +#include "wine/test.h" +#include <vfw.h> + +#define FOURCC_I420 mmioFOURCC('I', '4', '2', '0') + +START_TEST(iyuv_32) +{ + BITMAPINFOHEADER in = + { + .biSize = sizeof(BITMAPINFOHEADER), + .biCompression = FOURCC_I420, + .biPlanes = 1, + .biWidth = 96, + .biHeight = 96, + .biSizeImage = 96 * 96 * 3 / 2, + }; + HIC hic; + + hic = ICLocate(ICTYPE_VIDEO, FOURCC_I420, &in, NULL, ICMODE_DECOMPRESS); + todo_wine + ok(!!hic, "Failed to locate iyuv codec\n"); + if (!hic) + return; + ICClose(hic); +} -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10467
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/iyuv_32/tests/iyuv_32.c | 137 +++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/dlls/iyuv_32/tests/iyuv_32.c b/dlls/iyuv_32/tests/iyuv_32.c index e6ca206dccf..dc2248850e0 100644 --- a/dlls/iyuv_32/tests/iyuv_32.c +++ b/dlls/iyuv_32/tests/iyuv_32.c @@ -23,9 +23,138 @@ #include <vfw.h> #define FOURCC_I420 mmioFOURCC('I', '4', '2', '0') +#define FOURCC_IYUV mmioFOURCC('I', 'Y', 'U', 'V') + +static void test_formats(DWORD handler) +{ + BITMAPINFOHEADER in = {.biSize = sizeof(BITMAPINFOHEADER)}; + BITMAPINFOHEADER out = {.biSize = sizeof(BITMAPINFOHEADER)}; + ICINFO info; + LRESULT lr; + DWORD res; + HIC hic; + + in.biCompression = handler; + + hic = ICLocate(ICTYPE_VIDEO, handler, &in, NULL, ICMODE_DECOMPRESS); + ok(!hic, "Should fail to open codec without width and/or height.\n"); + + in.biWidth = 320; + in.biHeight = 240; + + hic = ICLocate(ICTYPE_VIDEO, handler, &in, NULL, ICMODE_DECOMPRESS); + ok(!!hic, "Failed to open codec.\n"); + + lr = ICGetInfo(hic, &info, sizeof(info)); + ok(lr == sizeof(info), "Got %Id.\n", lr); + ok(info.dwSize == sizeof(info), "Got size %lu.\n", info.dwSize); + ok(info.fccType == ICTYPE_VIDEO, "Got type %#lx.\n", info.fccType); + ok(info.fccHandler == FOURCC_IYUV, "Got handler %#lx.\n", info.fccHandler); + ok(info.dwFlags == 0, "Got flags %#lx.\n", info.dwFlags); + ok(info.dwVersion == 0, "Got version %#lx.\n", info.dwVersion); + ok(info.dwVersionICM == ICVERSION, "Got ICM version %#lx.\n", info.dwVersionICM); + + res = ICDecompressQuery(hic, &in, NULL); + ok(res == ICERR_OK, "Got %ld.\n", res); + + res = ICDecompressGetFormat(hic, &in, NULL); + ok(res == sizeof(BITMAPINFOHEADER), "Got %ld.\n", res); + + res = ICDecompressGetFormat(hic, &in, &out); + ok(res == ICERR_OK, "Got %ld.\n", res); + ok(out.biSize == sizeof(BITMAPINFOHEADER), "Got size %lu.\n", out.biSize); + ok(out.biWidth == 320, "Got width %ld.\n", out.biWidth); + ok(out.biHeight == 240, "Got height %ld.\n", out.biHeight); + ok(out.biPlanes == 1, "Got %u planes.\n", out.biPlanes); + ok(out.biBitCount == 24, "Got depth %u.\n", out.biBitCount); + ok(out.biCompression == BI_RGB, "Got compression %#lx.\n", out.biCompression); + ok(out.biSizeImage == 320 * 240 * 3, "Got image size %lu.\n", out.biSizeImage); + ok(!out.biXPelsPerMeter, "Got horizontal resolution %ld.\n", out.biXPelsPerMeter); + ok(!out.biYPelsPerMeter, "Got vertical resolution %ld.\n", out.biYPelsPerMeter); + ok(!out.biClrUsed, "Got %lu used colours.\n", out.biClrUsed); + ok(!out.biClrImportant, "Got %lu important colours.\n", out.biClrImportant); + + res = ICDecompressQuery(hic, &in, &out); + ok(res == ICERR_OK, "Got %ld.\n", res); + + out.biHeight = -240; + res = ICDecompressQuery(hic, &in, &out); + ok(res == ICERR_BADFORMAT, "Got %ld.\n", res); + + out.biHeight = -480; + res = ICDecompressQuery(hic, &in, &out); + ok(res == ICERR_BADFORMAT, "Got %ld.\n", res); + + out.biHeight = 480; + res = ICDecompressQuery(hic, &in, &out); + ok(res == ICERR_BADFORMAT, "Got %ld.\n", res); + + out.biWidth = 640; + res = ICDecompressQuery(hic, &in, &out); + ok(res == ICERR_BADFORMAT, "Got %ld.\n", res); + + out.biWidth = 160; + out.biHeight = 120; + res = ICDecompressQuery(hic, &in, &out); + ok(res == ICERR_BADFORMAT, "Got %ld.\n", res); + + out.biWidth = 320; + out.biHeight = 240; + out.biBitCount = 8; + res = ICDecompressQuery(hic, &in, &out); + ok(res == ICERR_OK, "Got %ld.\n", res); + out.biBitCount = 16; + res = ICDecompressQuery(hic, &in, &out); + ok(res == ICERR_OK, "Got %ld.\n", res); + out.biBitCount = 32; + res = ICDecompressQuery(hic, &in, &out); + ok(res == ICERR_BADFORMAT, "Got %ld.\n", res); + + out.biBitCount = 16; + out.biCompression = BI_BITFIELDS; + res = ICDecompressQuery(hic, &in, &out); + ok(res == ICERR_BADFORMAT, "Got %ld.\n", res); + + out.biCompression = mmioFOURCC('C', 'L', 'J', 'R'); + out.biBitCount = 8; + res = ICDecompressQuery(hic, &in, &out); + ok(res == ICERR_BADFORMAT, "Got %ld.\n", res); + + out.biBitCount = 16; + out.biCompression = mmioFOURCC('U', 'Y', 'V', 'Y'); + res = ICDecompressQuery(hic, &in, &out); + ok(res == ICERR_BADFORMAT, "Got %ld.\n", res); + out.biCompression = mmioFOURCC('Y', 'U', 'Y', '2'); + res = ICDecompressQuery(hic, &in, &out); + ok(res == ICERR_BADFORMAT, "Got %ld.\n", res); + + res = ICDecompressEnd(hic); + ok(res == ICERR_OK, "Got %ld.\n", res); + + out.biCompression = BI_RGB; + out.biBitCount = 24; + res = ICDecompressBegin(hic, &in, &out); + todo_wine + ok(res == ICERR_OK, "Got %ld.\n", res); + + res = ICDecompressEnd(hic); + ok(res == ICERR_OK, "Got %ld.\n", res); + + lr = ICDecompressExQuery(hic, 0, &in, NULL, 0, 0, 320, 240, NULL, NULL, 0, 0, 0, 0); + ok(lr == ICERR_UNSUPPORTED, "Got %Id.\n", lr); + + lr = ICDecompressExBegin(hic, 0, &in, NULL, 0, 0, 320, 240, &out, NULL, 20, 20, 320, 240); + ok(lr == ICERR_UNSUPPORTED, "Got %Id.\n", lr); + lr = ICDecompressExEnd(hic); + ok(lr == ICERR_UNSUPPORTED, "Got %Id.\n", lr); + + res = ICClose(hic); + ok(res == ICERR_OK, "Got %ld.\n", res); +} START_TEST(iyuv_32) { + static const DWORD handler[] = {FOURCC_IYUV, FOURCC_I420}; BITMAPINFOHEADER in = { .biSize = sizeof(BITMAPINFOHEADER), @@ -36,6 +165,7 @@ START_TEST(iyuv_32) .biSizeImage = 96 * 96 * 3 / 2, }; HIC hic; + int i; hic = ICLocate(ICTYPE_VIDEO, FOURCC_I420, &in, NULL, ICMODE_DECOMPRESS); todo_wine @@ -43,4 +173,11 @@ START_TEST(iyuv_32) if (!hic) return; ICClose(hic); + + for (i = 0; i < ARRAY_SIZE(handler); i++) + { + winetest_push_context("handler %.4s", (char *)&handler[i]); + test_formats(handler[i]); + winetest_pop_context(); + } } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10467
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/iyuv_32/tests/Makefile.in | 3 +- dlls/iyuv_32/tests/i420frame.bmp | Bin 0 -> 50742 bytes dlls/iyuv_32/tests/iyuv_32.c | 131 +++++++++++++++++++++++++ dlls/iyuv_32/tests/resource.rc | 30 ++++++ dlls/iyuv_32/tests/rgb24frame_flip.bmp | Bin 0 -> 27702 bytes 5 files changed, 163 insertions(+), 1 deletion(-) create mode 100755 dlls/iyuv_32/tests/i420frame.bmp create mode 100644 dlls/iyuv_32/tests/resource.rc create mode 100644 dlls/iyuv_32/tests/rgb24frame_flip.bmp diff --git a/dlls/iyuv_32/tests/Makefile.in b/dlls/iyuv_32/tests/Makefile.in index 05779d3c2dc..7d47050a25b 100644 --- a/dlls/iyuv_32/tests/Makefile.in +++ b/dlls/iyuv_32/tests/Makefile.in @@ -2,4 +2,5 @@ TESTDLL = iyuv_32.dll IMPORTS = msvfw32 SOURCES = \ - iyuv_32.c + iyuv_32.c \ + resource.rc diff --git a/dlls/iyuv_32/tests/i420frame.bmp b/dlls/iyuv_32/tests/i420frame.bmp new file mode 100755 index 0000000000000000000000000000000000000000..acebee03e4d77aa6f5720df1c0b0bc04940c67fe GIT binary patch literal 50742 zcmZ?rHJiWy24)Nl3>pj!3<(Sj3=97M|If&v02XI}%0oC1^54II|3~p?7>uTY(KIlc z21e7sXc`zz1EXnRG!2ZVfzdQDng&MGz-Ss6O#`E8VECp1hQELRkK)lV7)=ACX<#%B zjHZFnG%%V5M$^D(8W>FjqiJ9?4UDFN(KIlc21e7s@J$2%|1<m_#iL;`ng&MGz-Ss6 zO#`E8U^ESkrh(BkFq#HN)4*sN7)=ACX<#%BjHZF%n+6#EGW;LKqhT<b21e7sXc`zz z1EXnRG!2ZVfzdQDng&MGz-Ss6O#`E8U^ESkrh(y`2L3So`#*|D!(cQGjHZFnG%%V5 zM$^D(8W>FjqiJ9?4UDFN(KIlc21e7sXc`zz1H(5BFfjc2KZ-}gU^ESkrh(BkFq#HN z)4*sN7)=ACX<#%BjHZFnG%%V5M$^D(8W>Fj!#555XJGh0ibum>G!2ZVfzdQDng&MG zz-Ss6O#`E8U^ESkrh(BkFq#HN)4*sN7)=AiHw`c_F#I3IqhT<b21e7sXc`zz1EXnR zG!2ZVfzdQDng&MGz-Ss6O#`E8U^ESkrh(y`240VX(GVC7fzc2cpb)q?3PwX<Gz3ON zV1Pnk)hHMZfzc2c4S@j)feE8vGz3ONU^D~<C<L-b!DtAKhQMeD3{VIJj)Kt;7!85Z z5E!5k&>RJ$Aut*OqaiRrAs{dcMnhmU1V%$(fI^^Q6yObk0g9F(W9_n0fHwrpMgiUs zI5`UNhCtLPz#9S|MgiUsz+1VEN{)sAfe@H93h;)&kZGt5kVBP60p1X}Hwy5E!23~v RHw5xV0p1WGP`!<KX#n}>2Mz!L literal 0 HcmV?d00001 diff --git a/dlls/iyuv_32/tests/iyuv_32.c b/dlls/iyuv_32/tests/iyuv_32.c index dc2248850e0..bfea1998deb 100644 --- a/dlls/iyuv_32/tests/iyuv_32.c +++ b/dlls/iyuv_32/tests/iyuv_32.c @@ -25,6 +25,64 @@ #define FOURCC_I420 mmioFOURCC('I', '4', '2', '0') #define FOURCC_IYUV mmioFOURCC('I', 'Y', 'U', 'V') +#pragma pack(push, 2) +struct bmp_header +{ + char magic[2]; + DWORD length; + DWORD reserved; + DWORD offset; +}; +#pragma pack(pop) + +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_formats(DWORD handler) { BITMAPINFOHEADER in = {.biSize = sizeof(BITMAPINFOHEADER)}; @@ -152,6 +210,78 @@ static void test_formats(DWORD handler) ok(res == ICERR_OK, "Got %ld.\n", res); } +static void test_decompress(DWORD handler) +{ + static const DWORD width = 96, height = 96; + + const struct bmp_header *expect_rgb_header; + const BYTE *expect_rgb_data; + BYTE rgb_data[96 * 96 * 3]; + DWORD res, diff; + BYTE *i420_data; + HRSRC resource; + HIC hic; + + BITMAPINFOHEADER i420_info = + { + .biSize = sizeof(i420_info), + .biWidth = width, + .biHeight = height, + .biPlanes = 1, + .biBitCount = 12, + .biCompression = handler, + }; + + BITMAPINFOHEADER rgb_info = + { + .biSize = sizeof(rgb_info), + .biWidth = width, + .biHeight = height, + .biPlanes = 1, + .biBitCount = 24, + .biCompression = BI_RGB, + .biSizeImage = sizeof(rgb_data), + }; + + resource = FindResourceW(NULL, L"i420frame.bmp", (const WCHAR *)RT_RCDATA); + i420_data = LockResource(LoadResource(GetModuleHandleW(NULL), resource)); + i420_data += *(DWORD *)(i420_data + 2); + i420_info.biSizeImage = SizeofResource(GetModuleHandleW(NULL), resource); + + hic = ICOpen(ICTYPE_VIDEO, handler, ICMODE_DECOMPRESS); + ok(!!hic, "Failed to open codec.\n"); + + res = ICDecompressBegin(hic, &i420_info, &rgb_info); + todo_wine + ok(res == ICERR_OK, "Got %ld.\n", res); + + if (res != ICERR_OK) + goto skip_decompression_tests; + + res = ICDecompress(hic, 0, &i420_info, i420_data, &rgb_info, rgb_data); + todo_wine + ok(res == ICERR_OK, "Got %ld.\n", res); + res = ICDecompressEnd(hic); + ok(res == ICERR_OK, "Got %ld.\n", res); + + write_bitmap(&rgb_info, rgb_data, L"rgb24frame_flip.bmp"); + + resource = FindResourceW(NULL, L"rgb24frame_flip.bmp", (const WCHAR *)RT_RCDATA); + expect_rgb_header = ((const struct bmp_header *)LockResource(LoadResource(GetModuleHandleW(NULL), resource))); + expect_rgb_data = (const BYTE *)((uintptr_t)expect_rgb_header + expect_rgb_header->offset); + + diff = 0; + for (unsigned int i = 0; i < rgb_info.biSizeImage; ++i) + diff += abs((int)rgb_data[i] - (int)expect_rgb_data[i]); + diff = diff * 100 / 256 / rgb_info.biSizeImage; + todo_wine + ok(diff == 0, "Got %lu%% difference.\n", diff); + +skip_decompression_tests: + res = ICClose(hic); + ok(res == ICERR_OK, "Got %ld.\n", res); +} + START_TEST(iyuv_32) { static const DWORD handler[] = {FOURCC_IYUV, FOURCC_I420}; @@ -178,6 +308,7 @@ START_TEST(iyuv_32) { winetest_push_context("handler %.4s", (char *)&handler[i]); test_formats(handler[i]); + test_decompress(handler[i]); winetest_pop_context(); } } diff --git a/dlls/iyuv_32/tests/resource.rc b/dlls/iyuv_32/tests/resource.rc new file mode 100644 index 00000000000..d963e812105 --- /dev/null +++ b/dlls/iyuv_32/tests/resource.rc @@ -0,0 +1,30 @@ +/* + * 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 + */ + +#include "windef.h" + +/* 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 compression test on Windows */ +/* @makedep: i420frame.bmp */ +i420frame.bmp RCDATA i420frame.bmp diff --git a/dlls/iyuv_32/tests/rgb24frame_flip.bmp b/dlls/iyuv_32/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 -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10467
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/iyuv_32/tests/iyuv_32.c | 159 +++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/dlls/iyuv_32/tests/iyuv_32.c b/dlls/iyuv_32/tests/iyuv_32.c index bfea1998deb..97572411e6e 100644 --- a/dlls/iyuv_32/tests/iyuv_32.c +++ b/dlls/iyuv_32/tests/iyuv_32.c @@ -35,6 +35,8 @@ struct bmp_header }; #pragma pack(pop) +typedef UINT32 (*yuv_to_bgr32_converter)(WORD x, WORD y, WORD stride, WORD height, const BYTE *data); + static void write_bitmap_to_file(const BITMAPINFOHEADER *bitmap_header, const BYTE *data, HANDLE output) { struct bmp_header header = @@ -83,6 +85,81 @@ static void write_bitmap(const BITMAPINFOHEADER *bitmap_header, const BYTE *data CloseHandle(output); } +static void convert_yuv_and_write_bitmap(const BITMAPINFOHEADER *bitmap_header, const BYTE *data, + yuv_to_bgr32_converter convert, const WCHAR *output_filename) +{ + BITMAPINFOHEADER bgr32_header; + UINT32 *bgr32_ptr; + BYTE *bgr32_data; + HANDLE output; + DWORD written; + WORD x, y; + BOOL ret; + + bgr32_header = *bitmap_header; + bgr32_header.biBitCount = 32; + bgr32_header.biCompression = BI_RGB; + bgr32_header.biSizeImage = bgr32_header.biWidth * bgr32_header.biHeight * bgr32_header.biBitCount / 8; + bgr32_header.biHeight = -bitmap_header->biHeight; + bgr32_data = malloc(bgr32_header.biSizeImage); + + output = open_temp_file(output_filename); + bgr32_ptr = (UINT32 *)bgr32_data; + for (y = 0; y < bitmap_header->biWidth; y++) + for (x = 0; x < bitmap_header->biHeight; x++) + *bgr32_ptr++ = convert(x, y, bitmap_header->biWidth, bitmap_header->biHeight, data); + + write_bitmap_to_file(&bgr32_header, (BYTE *)bgr32_data, output); + 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); + + free(bgr32_data); + CloseHandle(output); +} + +#define CLAMP(x) ((x) > 255 ? 255 : (x) < 0 ? 0 : (x)) + +static UINT32 convert_yuv_to_bgr32(INT y, INT u, INT v) +{ + const INT y_offset = 16; + const INT uv_offset = 128; + const INT adj = 1024; + const INT c1 = 1.164 * adj + .5; + const INT c2 = 1.596 * adj + .5; + const INT c3 = 0.391 * adj + .5; + const INT c4 = 0.813 * adj + .5; + const INT c5 = 2.018 * adj + .5; + + INT r, g, b; + + y -= y_offset; + u -= uv_offset; + v -= uv_offset; + + r = CLAMP((c1 * y + c2 * v) / adj); + g = CLAMP((c1 * y - c4 * v - c3 * u) / adj); + b = CLAMP((c1 * y + c5 * u) / adj); + + return 0xff000000 | r << 16 | g << 8 | b; +} + +static UINT32 convert_i420_to_bgr32(WORD x, WORD y, WORD stride, WORD height, const BYTE *data) +{ + WORD uv_offset, u_offset, v_offset; + BYTE yuv_y, yuv_u, yuv_v; + + u_offset = height * stride; + v_offset = u_offset * 5 / 4; + uv_offset = (y / 2) * stride / 2 + x / 2; + + yuv_y = data[y * stride + x]; + yuv_u = data[u_offset + uv_offset]; + yuv_v = data[v_offset + uv_offset]; + + return convert_yuv_to_bgr32(yuv_y, yuv_u, yuv_v); +} + static void test_formats(DWORD handler) { BITMAPINFOHEADER in = {.biSize = sizeof(BITMAPINFOHEADER)}; @@ -282,6 +359,87 @@ skip_decompression_tests: ok(res == ICERR_OK, "Got %ld.\n", res); } +static void test_compress(DWORD handler) +{ + const struct bmp_header *expect_i420_header; + const struct bmp_header *rgb_header; + const BITMAPINFOHEADER *rgb_info; + BYTE i420_data[96 * 96 * 3 / 2]; + const BYTE *expect_i420_data; + BITMAPINFOHEADER i420_info; + DWORD res, flags, diff; + const BYTE *rgb_data; + HRSRC resource; + HIC hic; + + hic = ICOpen(ICTYPE_VIDEO, handler, ICMODE_COMPRESS); + ok(!!hic, "Failed to open codec.\n"); + + resource = FindResourceW(NULL, L"rgb24frame_flip.bmp", (const WCHAR *)RT_RCDATA); + rgb_header = ((const struct bmp_header *)LockResource(LoadResource(GetModuleHandleW(NULL), resource))); + rgb_info = (const BITMAPINFOHEADER *)(rgb_header + 1); + rgb_data = (const BYTE *)(rgb_info + 1); + + res = ICCompressQuery(hic, rgb_info, NULL); + todo_wine + ok(res == ICERR_OK, "Got %ld.\n", res); + + if (res != ICERR_OK) + goto skip_compression_tests; + + res = ICCompressGetSize(hic, rgb_info, NULL); + ok(res == rgb_info->biWidth * rgb_info->biHeight * 3, "Got %ld.\n", res); + + res = ICCompressGetFormatSize(hic, rgb_info); + ok(res == sizeof(BITMAPINFOHEADER), "Got %ld.\n", res); + + memset(&i420_info, 0xcc, sizeof(i420_info)); + res = ICCompressGetFormat(hic, rgb_info, &i420_info); + ok(res == ICERR_OK, "Got %ld.\n", res); + ok(i420_info.biSize == sizeof(BITMAPINFOHEADER), "Got size %lu.\n", i420_info.biSize); + ok(i420_info.biWidth == 96, "Got width %ld.\n", i420_info.biWidth); + ok(i420_info.biHeight == 96, "Got height %ld.\n", i420_info.biHeight); + ok(i420_info.biPlanes == 1, "Got %u planes.\n", i420_info.biPlanes); + ok(i420_info.biBitCount == 24, "Got depth %u.\n", i420_info.biBitCount); + ok(i420_info.biCompression == FOURCC_IYUV, "Got compression %#lx.\n", i420_info.biCompression); + ok(i420_info.biSizeImage == rgb_info->biWidth * rgb_info->biHeight * 3, "Got image size %lu.\n", + i420_info.biSizeImage); + ok(!i420_info.biXPelsPerMeter, "Got horizontal resolution %ld.\n", i420_info.biXPelsPerMeter); + ok(!i420_info.biYPelsPerMeter, "Got vertical resolution %ld.\n", i420_info.biYPelsPerMeter); + ok(!i420_info.biClrUsed, "Got %lu used colours.\n", i420_info.biClrUsed); + ok(!i420_info.biClrImportant, "Got %lu important colours.\n", i420_info.biClrImportant); + + res = ICCompressQuery(hic, rgb_info, &i420_info); + ok(res == ICERR_OK, "Got %ld.\n", res); + + res = ICCompressBegin(hic, rgb_info, &i420_info); + ok(res == ICERR_OK, "Got %ld.\n", res); + memset(i420_data, 0xcd, sizeof(i420_data)); + res = ICCompress(hic, ICCOMPRESS_KEYFRAME, &i420_info, i420_data, (BITMAPINFOHEADER *)rgb_info, (void *)rgb_data, + NULL, &flags, 1, 0, 0, NULL, NULL); + ok(res == ICERR_OK, "Got %ld.\n", res); + ok(flags == AVIIF_KEYFRAME, "got flags %#lx\n", flags); + ok(i420_info.biSizeImage == sizeof(i420_data), "Got size %ld.\n", i420_info.biSizeImage); + res = ICCompressEnd(hic); + ok(res == ICERR_OK, "Got %ld.\n", res); + + convert_yuv_and_write_bitmap(&i420_info, i420_data, convert_i420_to_bgr32, L"i420frame.bmp"); + + resource = FindResourceW(NULL, L"i420frame.bmp", (const WCHAR *)RT_RCDATA); + expect_i420_header = ((const struct bmp_header *)LockResource(LoadResource(GetModuleHandleW(NULL), resource))); + expect_i420_data = (const BYTE *)((uintptr_t)expect_i420_header + expect_i420_header->length); + + diff = 0; + for (unsigned int i = 0; i < i420_info.biSizeImage; ++i) + diff += abs((int)i420_data[i] - (int)expect_i420_data[i]); + diff = diff * 100 / 256 / i420_info.biSizeImage; + ok(diff == 0, "Got %lu%% difference.\n", diff); + +skip_compression_tests: + res = ICClose(hic); + ok(res == ICERR_OK, "Got %ld.\n", res); +} + START_TEST(iyuv_32) { static const DWORD handler[] = {FOURCC_IYUV, FOURCC_I420}; @@ -309,6 +467,7 @@ START_TEST(iyuv_32) winetest_push_context("handler %.4s", (char *)&handler[i]); test_formats(handler[i]); test_decompress(handler[i]); + test_compress(handler[i]); winetest_pop_context(); } } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10467
On Mon Mar 30 02:36:49 2026 +0000, Brendan McGrath wrote:
changed this line in [version 3 of the diff](/wine/wine/-/merge_requests/10467/diffs?diff_id=255503&start_sha=16124da14f5fb589085a722b364bfb3630794198#30f26154602482476fa8e38926f7fccae30ad784_85_92) OK, thanks for that. I've used `clang-format` with the `Microsoft` style. So spacing should now be fixed. I also manually changed the variable names, removing the use of Hungarian notation.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10467#note_134301
On Fri Mar 27 08:59:32 2026 +0000, Rémi Bernon wrote:
These are unused at this point. OK. I'm now adding this file in the commit when it is first used (along with the bitmaps themselves).
I've also removed the `iyuv_32.rc` and `iyuv_private.h` files from this MR (as their content was not used). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10467#note_134302
v3: - Remove use of Hungarian notation - Use `clang-format` to produce consistent spacing - Move the introduction of `resource.rc` and its referenced bitmaps to a later commit (when they are first needed) - Remove `iyuv_32.rc` and `iyuv_private.h` (as their contents is not used in this MR) -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10467#note_134303
This merge request was approved by Rémi Bernon. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10467
participants (3)
-
Brendan McGrath -
Brendan McGrath (@redmcg) -
Rémi Bernon (@rbernon)