Author: akhaldi Date: Sun Sep 24 11:19:20 2017 New Revision: 75954
URL: http://svn.reactos.org/svn/reactos?rev=75954&view=rev Log: [WINDOWSCODECS_WINETEST] Sync with Wine Staging 2.16. CORE-13762
Modified: trunk/rostests/winetests/windowscodecs/CMakeLists.txt trunk/rostests/winetests/windowscodecs/converter.c trunk/rostests/winetests/windowscodecs/metadata.c
Modified: trunk/rostests/winetests/windowscodecs/CMakeLists.txt URL: http://svn.reactos.org/svn/reactos/trunk/rostests/winetests/windowscodecs/CM... ============================================================================== --- trunk/rostests/winetests/windowscodecs/CMakeLists.txt [iso-8859-1] (original) +++ trunk/rostests/winetests/windowscodecs/CMakeLists.txt [iso-8859-1] Sun Sep 24 11:19:20 2017 @@ -18,7 +18,7 @@
add_executable(windowscodecs_winetest ${SOURCE}) set_module_type(windowscodecs_winetest win32cui) -add_importlibs(windowscodecs_winetest windowscodecs oleaut32 ole32 user32 gdi32 shlwapi msvcrt kernel32) +add_importlibs(windowscodecs_winetest windowscodecs oleaut32 ole32 propsys user32 gdi32 shlwapi msvcrt kernel32)
if(MSVC) add_importlibs(windowscodecs_winetest ntdll)
Modified: trunk/rostests/winetests/windowscodecs/converter.c URL: http://svn.reactos.org/svn/reactos/trunk/rostests/winetests/windowscodecs/co... ============================================================================== --- trunk/rostests/winetests/windowscodecs/converter.c [iso-8859-1] (original) +++ trunk/rostests/winetests/windowscodecs/converter.c [iso-8859-1] Sun Sep 24 11:19:20 2017 @@ -54,6 +54,9 @@ const bitmap_data *data; } BitmapTestSrc;
+extern HRESULT STDMETHODCALLTYPE IWICBitmapFrameEncode_WriteSource_Proxy(IWICBitmapFrameEncode* This, + IWICBitmapSource *pIBitmapSource, WICRect *prc); + static BOOL near_equal(float a, float b) { return fabsf(a - b) < 0.001; @@ -631,6 +634,12 @@ static const WCHAR wszCompressionQuality[] = {'C','o','m','p','r','e','s','s','i','o','n','Q','u','a','l','i','t','y',0}; static const WCHAR wszInterlaceOption[] = {'I','n','t','e','r','l','a','c','e','O','p','t','i','o','n',0}; static const WCHAR wszFilterOption[] = {'F','i','l','t','e','r','O','p','t','i','o','n',0}; +static const WCHAR wszImageQuality[] = {'I','m','a','g','e','Q','u','a','l','i','t','y',0}; +static const WCHAR wszBitmapTransform[] = {'B','i','t','m','a','p','T','r','a','n','s','f','o','r','m',0}; +static const WCHAR wszLuminance[] = {'L','u','m','i','n','a','n','c','e',0}; +static const WCHAR wszChrominance[] = {'C','h','r','o','m','i','n','a','n','c','e',0}; +static const WCHAR wszJpegYCrCbSubsampling[] = {'J','p','e','g','Y','C','r','C','b','S','u','b','s','a','m','p','l','i','n','g',0}; +static const WCHAR wszSuppressApp0[] = {'S','u','p','p','r','e','s','s','A','p','p','0',0};
static const struct property_opt_test_data testdata_tiff_props[] = { { wszTiffCompressionMethod, VT_UI1, VT_UI1, WICTiffCompressionDontCare }, @@ -641,6 +650,16 @@ static const struct property_opt_test_data testdata_png_props[] = { { wszInterlaceOption, VT_BOOL, VT_BOOL, 0 }, { wszFilterOption, VT_UI1, VT_UI1, WICPngFilterUnspecified, 0.0f, TRUE /* not supported on XP/2k3 */}, + { NULL } +}; + +static const struct property_opt_test_data testdata_jpeg_props[] = { + { wszImageQuality, VT_R4, VT_EMPTY }, + { wszBitmapTransform, VT_UI1, VT_UI1, WICBitmapTransformRotate0 }, + { wszLuminance, VT_I4|VT_ARRAY, VT_EMPTY }, + { wszChrominance, VT_I4|VT_ARRAY, VT_EMPTY }, + { wszJpegYCrCbSubsampling, VT_UI1, VT_UI1, WICJpegYCrCbSubsamplingDefault, 0.0f, TRUE }, /* not supported on XP/2k3 */ + { wszSuppressApp0, VT_BOOL, VT_BOOL, FALSE }, { NULL } };
@@ -768,6 +787,8 @@ test_specific_encoder_properties(options, testdata_tiff_props, all_props, cProperties2); else if (IsEqualCLSID(clsid_encoder, &CLSID_WICPngEncoder)) test_specific_encoder_properties(options, testdata_png_props, all_props, cProperties2); + else if (IsEqualCLSID(clsid_encoder, &CLSID_WICJpegEncoder)) + test_specific_encoder_properties(options, testdata_jpeg_props, all_props, cProperties2);
for (i=0; i < cProperties2; i++) { @@ -1367,6 +1388,13 @@ DeleteTestBitmap(src_obj);
i++; + } + + if (clsid_decoder == NULL) + { + IStream_Release(stream); + IWICBitmapEncoder_Release(encoder); + return; }
if (SUCCEEDED(hr)) @@ -1869,6 +1897,9 @@ test_encoder(&testdata_24bppBGR, &CLSID_WICTiffEncoder, &testdata_24bppBGR, &CLSID_WICTiffDecoder, "TIFF encoder 24bppBGR");
+ test_encoder(&testdata_24bppBGR, &CLSID_WICJpegEncoder, + &testdata_24bppBGR, NULL, "JPEG encoder 24bppBGR"); + test_multi_encoder(multiple_frames, &CLSID_WICTiffEncoder, multiple_frames, &CLSID_WICTiffDecoder, NULL, NULL, "TIFF encoder multi-frame", NULL);
Modified: trunk/rostests/winetests/windowscodecs/metadata.c URL: http://svn.reactos.org/svn/reactos/trunk/rostests/winetests/windowscodecs/me... ============================================================================== --- trunk/rostests/winetests/windowscodecs/metadata.c [iso-8859-1] (original) +++ trunk/rostests/winetests/windowscodecs/metadata.c [iso-8859-1] Sun Sep 24 11:19:20 2017 @@ -30,10 +30,16 @@
#include <windef.h> #include <winbase.h> +#include <winnls.h> +#include <winreg.h> #include <ole2.h> //#include "wincodec.h" #include <wincodecsdk.h> +#include <propvarutil.h> #include <wine/test.h> + +#include <initguid.h> +DEFINE_GUID(IID_MdbrUnknown, 0x00240e6f,0x3f23,0x4432,0xb0,0xcc,0x48,0xd5,0xbb,0xff,0x6c,0x36);
#define expect_blob(propvar, data, length) do { \ ok((propvar).vt == VT_BLOB, "unexpected vt: %i\n", (propvar).vt); \ @@ -1552,13 +1558,207 @@ IWICMetadataBlockReader_Release(blockreader); }
+ hr = IWICBitmapDecoder_GetMetadataQueryReader(decoder, &queryreader); + ok(hr == S_OK || broken(hr == WINCODEC_ERR_UNSUPPORTEDOPERATION) /* before Vista */, + "GetMetadataQueryReader error %#x\n", hr); + if (SUCCEEDED(hr)) + { + static const struct + { + const char *query; + HRESULT hr; + UINT vt; + } decoder_data[] = + { + { "/logscrdesc/Signature", S_OK, VT_UI1 | VT_VECTOR }, + { "/[0]logscrdesc/Signature", S_OK, VT_UI1 | VT_VECTOR }, + { "/logscrdesc/\Signature", S_OK, VT_UI1 | VT_VECTOR }, + { "/Logscrdesc/\signature", S_OK, VT_UI1 | VT_VECTOR }, + { "/logscrdesc/{str=signature}", S_OK, VT_UI1 | VT_VECTOR }, + { "/[0]logscrdesc/{str=signature}", S_OK, VT_UI1 | VT_VECTOR }, + { "/logscrdesc/{wstr=signature}", S_OK, VT_UI1 | VT_VECTOR }, + { "/[0]logscrdesc/{wstr=signature}", S_OK, VT_UI1 | VT_VECTOR }, + { "/appext/Application", S_OK, VT_UI1 | VT_VECTOR }, + { "/appext/{STR=APPlication}", S_OK, VT_UI1 | VT_VECTOR }, + { "/appext/{WSTR=APPlication}", S_OK, VT_UI1 | VT_VECTOR }, + { "/LogSCRdesC", S_OK, VT_UNKNOWN }, + { "/[0]LogSCRdesC", S_OK, VT_UNKNOWN }, + { "/appEXT", S_OK, VT_UNKNOWN }, + { "/[0]appEXT", S_OK, VT_UNKNOWN }, + { "grctlext", WINCODEC_ERR_PROPERTYNOTSUPPORTED, 0 }, + { "/imgdesc", WINCODEC_ERR_PROPERTYNOTFOUND, 0 }, + }; + static const WCHAR rootW[] = {'/',0}; + WCHAR name[256]; + UINT len, i, j; + PROPVARIANT value; + IWICMetadataQueryReader *meta_reader; + + hr = IWICMetadataQueryReader_GetContainerFormat(queryreader, &format); + ok(hr == S_OK, "GetContainerFormat error %#x\n", hr); + ok(IsEqualGUID(&format, &GUID_ContainerFormatGif), + "wrong container format %s\n", wine_dbgstr_guid(&format)); + + name[0] = 0; + len = 0xdeadbeef; + hr = IWICMetadataQueryReader_GetLocation(queryreader, 256, name, &len); + ok(hr == S_OK, "GetLocation error %#x\n", hr); + ok(len == 2, "expected 2, got %u\n", len); + ok(!lstrcmpW(name, rootW), "expected '/', got %s\n", wine_dbgstr_w(name)); + + for (i = 0; i < sizeof(decoder_data)/sizeof(decoder_data[0]); i++) + { + WCHAR queryW[256]; + + if (winetest_debug > 1) + trace("query: %s\n", decoder_data[i].query); + MultiByteToWideChar(CP_ACP, 0, decoder_data[i].query, -1, queryW, 256); + PropVariantInit(&value); + hr = IWICMetadataQueryReader_GetMetadataByName(queryreader, queryW, &value); + ok(hr == decoder_data[i].hr, "GetMetadataByName(%s) returned %#x, expected %#x\n", wine_dbgstr_w(queryW), hr, decoder_data[i].hr); + ok(value.vt == decoder_data[i].vt, "expected %#x, got %#x\n", decoder_data[i].vt, value.vt); + if (hr == S_OK && value.vt == VT_UNKNOWN) + { + hr = IUnknown_QueryInterface(value.punkVal, &IID_IWICMetadataQueryReader, (void **)&meta_reader); + ok(hr == S_OK, "QueryInterface error %#x\n", hr); + + name[0] = 0; + len = 0xdeadbeef; + hr = IWICMetadataQueryReader_GetLocation(meta_reader, 256, name, &len); + ok(hr == S_OK, "GetLocation error %#x\n", hr); + ok(len == lstrlenW(queryW) + 1, "expected %u, got %u\n", lstrlenW(queryW) + 1, len); + ok(!lstrcmpW(name, queryW), "expected %s, got %s\n", wine_dbgstr_w(queryW), wine_dbgstr_w(name)); + + for (j = 0; j < sizeof(decoder_data)/sizeof(decoder_data[0]); j++) + { + MultiByteToWideChar(CP_ACP, 0, decoder_data[j].query, -1, queryW, 256); + + if (CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, queryW, len-1, name, len-1) == CSTR_EQUAL && decoder_data[j].query[len - 1] != 0) + { + if (winetest_debug > 1) + trace("query: %s\n", wine_dbgstr_w(queryW + len - 1)); + PropVariantClear(&value); + hr = IWICMetadataQueryReader_GetMetadataByName(meta_reader, queryW + len - 1, &value); + ok(hr == decoder_data[j].hr, "GetMetadataByName(%s) returned %#x, expected %#x\n", wine_dbgstr_w(queryW + len - 1), hr, decoder_data[j].hr); + ok(value.vt == decoder_data[j].vt, "expected %#x, got %#x\n", decoder_data[j].vt, value.vt); + } + } + + IWICMetadataQueryReader_Release(meta_reader); + } + + PropVariantClear(&value); + } + + IWICMetadataQueryReader_Release(queryreader); + } + hr = IWICBitmapFrameDecode_GetMetadataQueryReader(frame, &queryreader); - ok(hr == S_OK || - broken(hr == WINCODEC_ERR_UNSUPPORTEDOPERATION) /* before Vista */, - "GetMetadataQueryReader failed: %08x\n", hr); - + ok(hr == S_OK || broken(hr == WINCODEC_ERR_UNSUPPORTEDOPERATION) /* before Vista */, + "GetMetadataQueryReader error %#x\n", hr); if (SUCCEEDED(hr)) { + static const struct + { + const char *query; + HRESULT hr; + UINT vt; + } frame_data[] = + { + { "/grctlext/Delay", S_OK, VT_UI2 }, + { "/[0]grctlext/Delay", S_OK, VT_UI2 }, + { "/grctlext/{str=delay}", S_OK, VT_UI2 }, + { "/[0]grctlext/{str=delay}", S_OK, VT_UI2 }, + { "/grctlext/{wstr=delay}", S_OK, VT_UI2 }, + { "/[0]grctlext/{wstr=delay}", S_OK, VT_UI2 }, + { "/imgdesc/InterlaceFlag", S_OK, VT_BOOL }, + { "/imgdesc/{STR=interlaceFLAG}", S_OK, VT_BOOL }, + { "/imgdesc/{WSTR=interlaceFLAG}", S_OK, VT_BOOL }, + { "/grctlext", S_OK, VT_UNKNOWN }, + { "/[0]grctlext", S_OK, VT_UNKNOWN }, + { "/imgdesc", S_OK, VT_UNKNOWN }, + { "/[0]imgdesc", S_OK, VT_UNKNOWN }, + { "/LogSCRdesC", WINCODEC_ERR_PROPERTYNOTFOUND, 0 }, + { "/appEXT", WINCODEC_ERR_PROPERTYNOTFOUND, 0 }, + { "/grctlext/{\str=delay}", WINCODEC_ERR_WRONGSTATE, 0 }, + { "/grctlext/{str=\delay}", S_OK, VT_UI2 }, + { "grctlext/Delay", WINCODEC_ERR_PROPERTYNOTSUPPORTED, 0 }, + }; + static const WCHAR rootW[] = {'/',0}; + static const WCHAR guidW[] = {'/','{','g','u','i','d','=','\',0}; + static const WCHAR imgdescW[] = {'i','m','g','d','e','s','c',0}; + static const WCHAR ImgDescW[] = {'I','m','g','D','e','s','c',0}; + WCHAR name[256], queryW[256]; + UINT len, i; + PROPVARIANT value; + IWICMetadataQueryReader *meta_reader; + + hr = IWICMetadataQueryReader_GetContainerFormat(queryreader, &format); + ok(hr == S_OK, "GetContainerFormat error %#x\n", hr); + ok(IsEqualGUID(&format, &GUID_ContainerFormatGif), + "wrong container format %s\n", wine_dbgstr_guid(&format)); + + name[0] = 0; + len = 0xdeadbeef; + hr = IWICMetadataQueryReader_GetLocation(queryreader, 256, name, &len); + ok(hr == S_OK, "GetLocation error %#x\n", hr); + ok(len == 2, "expected 2, got %u\n", len); + ok(!lstrcmpW(name, rootW), "expected '/', got %s\n", wine_dbgstr_w(name)); + + for (i = 0; i < sizeof(frame_data)/sizeof(frame_data[0]); i++) + { + if (winetest_debug > 1) + trace("query: %s\n", frame_data[i].query); + MultiByteToWideChar(CP_ACP, 0, frame_data[i].query, -1, queryW, 256); + PropVariantInit(&value); + hr = IWICMetadataQueryReader_GetMetadataByName(queryreader, queryW, &value); + ok(hr == frame_data[i].hr, "GetMetadataByName(%s) returned %#x, expected %#x\n", wine_dbgstr_w(queryW), hr, frame_data[i].hr); + ok(value.vt == frame_data[i].vt, "expected %#x, got %#x\n", frame_data[i].vt, value.vt); + if (hr == S_OK && value.vt == VT_UNKNOWN) + { + hr = IUnknown_QueryInterface(value.punkVal, &IID_IWICMetadataQueryReader, (void **)&meta_reader); + ok(hr == S_OK, "QueryInterface error %#x\n", hr); + + name[0] = 0; + len = 0xdeadbeef; + hr = IWICMetadataQueryReader_GetLocation(meta_reader, 256, name, &len); + ok(hr == S_OK, "GetLocation error %#x\n", hr); + ok(len == lstrlenW(queryW) + 1, "expected %u, got %u\n", lstrlenW(queryW) + 1, len); + ok(!lstrcmpW(name, queryW), "expected %s, got %s\n", wine_dbgstr_w(queryW), wine_dbgstr_w(name)); + + IWICMetadataQueryReader_Release(meta_reader); + } + + PropVariantClear(&value); + } + + name[0] = 0; + len = 0xdeadbeef; + hr = WICMapGuidToShortName(&GUID_MetadataFormatIMD, 256, name, &len); + ok(hr == S_OK, "WICMapGuidToShortName error %#x\n", hr); + ok(!lstrcmpW(name, imgdescW), "wrong short name %s\n", wine_dbgstr_w(name)); + + format = GUID_NULL; + hr = WICMapShortNameToGuid(imgdescW, &format); + ok(hr == S_OK, "WICMapGuidToShortName error %#x\n", hr); + ok(IsEqualGUID(&format, &GUID_MetadataFormatIMD), "wrong guid %s\n", wine_dbgstr_guid(&format)); + + format = GUID_NULL; + hr = WICMapShortNameToGuid(ImgDescW, &format); + ok(hr == S_OK, "WICMapGuidToShortName error %#x\n", hr); + ok(IsEqualGUID(&format, &GUID_MetadataFormatIMD), "wrong guid %s\n", wine_dbgstr_guid(&format)); + + lstrcpyW(queryW, guidW); + StringFromGUID2(&GUID_MetadataFormatIMD, queryW + lstrlenW(queryW) - 1, 39); + memcpy(queryW, guidW, sizeof(guidW) - 2); + if (winetest_debug > 1) + trace("query: %s\n", wine_dbgstr_w(queryW)); + PropVariantInit(&value); + hr = IWICMetadataQueryReader_GetMetadataByName(queryreader, queryW, &value); + ok(hr == S_OK, "GetMetadataByName(%s) error %#x\n", wine_dbgstr_w(queryW), hr); + ok(value.vt == VT_UNKNOWN, "expected VT_UNKNOWN, got %#x\n", value.vt); + PropVariantClear(&value); + IWICMetadataQueryReader_Release(queryreader); }
@@ -2252,10 +2452,610 @@ } }
+struct metadata_item +{ + const char *schema, *id_str; + UINT id, type, value; +}; + +struct metadata_block +{ + const GUID *metadata_format; + UINT count; + const struct metadata_item *item; +}; + +struct metadata +{ + const GUID *container_format; + UINT count; + const struct metadata_block *block; +}; + +static const struct metadata *current_metadata; +static const struct metadata_block *current_metadata_block; + +static char the_best[] = "The Best"; +static char the_worst[] = "The Worst"; + +static HRESULT WINAPI mdr_QueryInterface(IWICMetadataReader *iface, REFIID iid, void **out) +{ + trace("%p,%s,%p\n", iface, wine_dbgstr_guid(iid), out); + + if (IsEqualIID(iid, &IID_IUnknown) || + IsEqualIID(iid, &IID_IWICMetadataReader)) + { + *out = iface; + return S_OK; + } + + ok(0, "unknown iid %s\n", wine_dbgstr_guid(iid)); + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI mdr_AddRef(IWICMetadataReader *iface) +{ + return 2; +} + +static ULONG WINAPI mdr_Release(IWICMetadataReader *iface) +{ + return 1; +} + +static HRESULT WINAPI mdr_GetMetadataFormat(IWICMetadataReader *iface, GUID *format) +{ + trace("%p,%p\n", iface, format); + + ok(current_metadata_block != NULL, "current_metadata_block can't be NULL\n"); + if (!current_metadata_block) return E_POINTER; + + *format = *current_metadata_block->metadata_format; + return S_OK; +} + +static HRESULT WINAPI mdr_GetMetadataHandlerInfo(IWICMetadataReader *iface, IWICMetadataHandlerInfo **handler) +{ + ok(0, "not implemented\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI mdr_GetCount(IWICMetadataReader *iface, UINT *count) +{ + trace("%p,%p\n", iface, count); + + ok(current_metadata_block != NULL, "current_metadata_block can't be NULL\n"); + if (!current_metadata_block) return E_POINTER; + + *count = current_metadata_block->count; + return S_OK; +} + +static HRESULT WINAPI mdr_GetValueByIndex(IWICMetadataReader *iface, UINT index, PROPVARIANT *schema, PROPVARIANT *id, PROPVARIANT *value) +{ + ok(0, "not implemented\n"); + return E_NOTIMPL; +} + +static char *get_temp_buffer(int size) +{ + static char buf[16][256]; + static int idx; + char *p; + + assert(size < 256); + + p = buf[idx & 0x0f]; + idx++; + return p; +} + +static const char *wine_dbgstr_propvariant(const PROPVARIANT *var) +{ + char *ret; + + if (!var) return "(null)"; + + switch (var->vt) + { + case VT_LPWSTR: + ret = get_temp_buffer(lstrlenW(U(*var).pwszVal) + 16); + sprintf(ret, "(VT_LPWSTR:%s)", wine_dbgstr_w(U(*var).pwszVal)); + break; + + case VT_LPSTR: + ret = get_temp_buffer(lstrlenA(U(*var).pszVal) + 16); + sprintf(ret, "(VT_LPSTR:%s)", U(*var).pszVal); + break; + + default: + ret = get_temp_buffer(16); + sprintf(ret, "(vt:%u)", var->vt); + break; + } + + return ret; +} + +static int propvar_cmp(const PROPVARIANT *v1, LONGLONG value2) +{ + LONGLONG value1; + + if (PropVariantToInt64(v1, &value1) != S_OK) return -1; + + value1 -= value2; + if (value1) return value1 < 0 ? -1 : 1; + return 0; +} + +static HRESULT WINAPI mdr_GetValue(IWICMetadataReader *iface, const PROPVARIANT *schema, const PROPVARIANT *id, PROPVARIANT *value) +{ + UINT i; + + trace("%p,%s,%s,%s\n", iface, wine_dbgstr_propvariant(schema), wine_dbgstr_propvariant(id), wine_dbgstr_propvariant(value)); + + ok(current_metadata_block != NULL, "current_metadata_block can't be NULL\n"); + if (!current_metadata_block) return E_POINTER; + + ok(schema != NULL && id != NULL && value != NULL, "%p, %p, %p should not be NULL\n", schema, id, value); + + for (i = 0; i < current_metadata_block->count; i++) + { + if (schema->vt != VT_EMPTY) + { + if (!current_metadata_block->item[i].schema) + continue; + + switch (schema->vt) + { + case VT_LPSTR: + if (lstrcmpA(U(*schema).pszVal, current_metadata_block->item[i].schema) != 0) + continue; + break; + + case VT_LPWSTR: + { + char schemaA[256]; + WideCharToMultiByte(CP_ACP, 0, U(*schema).pwszVal, -1, schemaA, sizeof(schemaA), NULL, NULL); + if (lstrcmpA(schemaA, current_metadata_block->item[i].schema) != 0) + continue; + break; + } + + default: + ok(0, "unsupported schema vt %u\n", schema->vt); + continue; + } + } + else if (current_metadata_block->item[i].schema) + continue; + + switch (id->vt) + { + case VT_LPSTR: + if (current_metadata_block->item[i].id_str) + { + if (!lstrcmpA(U(*id).pszVal, current_metadata_block->item[i].id_str)) + { + value->vt = VT_LPSTR; + U(*value).pszVal = the_best; + return S_OK; + } + break; + } + break; + + case VT_LPWSTR: + if (current_metadata_block->item[i].id_str) + { + char idA[256]; + WideCharToMultiByte(CP_ACP, 0, U(*id).pwszVal, -1, idA, sizeof(idA), NULL, NULL); + if (!lstrcmpA(idA, current_metadata_block->item[i].id_str)) + { + value->vt = VT_LPSTR; + U(*value).pszVal = the_worst; + return S_OK; + } + break; + } + break; + + case VT_CLSID: + if (IsEqualGUID(U(*id).puuid, &GUID_MetadataFormatXMP) || + IsEqualGUID(U(*id).puuid, &GUID_ContainerFormatTiff)) + { + value->vt = VT_UNKNOWN; + value->punkVal = (IUnknown *)iface; + return S_OK; + } + break; + + default: + if (!propvar_cmp(id, current_metadata_block->item[i].id)) + { + value->vt = current_metadata_block->item[i].type; + U(*value).uiVal = current_metadata_block->item[i].value; + return S_OK; + } + break; + } + } + + return 0xdeadbeef; +} + +static HRESULT WINAPI mdr_GetEnumerator(IWICMetadataReader *iface, IWICEnumMetadataItem **enumerator) +{ + ok(0, "not implemented\n"); + return E_NOTIMPL; +} + +static /* const */ IWICMetadataReaderVtbl mdr_vtbl = +{ + mdr_QueryInterface, + mdr_AddRef, + mdr_Release, + mdr_GetMetadataFormat, + mdr_GetMetadataHandlerInfo, + mdr_GetCount, + mdr_GetValueByIndex, + mdr_GetValue, + mdr_GetEnumerator +}; + +static IWICMetadataReader mdr = { &mdr_vtbl }; + +static HRESULT WINAPI mdbr_QueryInterface(IWICMetadataBlockReader *iface, REFIID iid, void **out) +{ + if (IsEqualIID(iid, &IID_IUnknown) || + IsEqualIID(iid, &IID_IWICMetadataBlockReader)) + { + *out = iface; + return S_OK; + } + + /* Windows 8/10 query for some undocumented IID */ + if (!IsEqualIID(iid, &IID_MdbrUnknown)) + ok(0, "unknown iid %s\n", wine_dbgstr_guid(iid)); + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI mdbr_AddRef(IWICMetadataBlockReader *iface) +{ + return 2; +} + +static ULONG WINAPI mdbr_Release(IWICMetadataBlockReader *iface) +{ + return 1; +} + +static HRESULT WINAPI mdbr_GetContainerFormat(IWICMetadataBlockReader *iface, GUID *format) +{ + trace("%p,%p\n", iface, format); + + ok(current_metadata != NULL, "current_metadata can't be NULL\n"); + if (!current_metadata) return E_POINTER; + + *format = *current_metadata->container_format; + return S_OK; +} + +static HRESULT WINAPI mdbr_GetCount(IWICMetadataBlockReader *iface, UINT *count) +{ + trace("%p,%p\n", iface, count); + + ok(current_metadata != NULL, "current_metadata can't be NULL\n"); + if (!current_metadata) return E_POINTER; + + *count = current_metadata->count; + return S_OK; +} + +static HRESULT WINAPI mdbr_GetReaderByIndex(IWICMetadataBlockReader *iface, UINT index, IWICMetadataReader **out) +{ + trace("%p,%u,%p\n", iface, index, out); + + *out = NULL; + + ok(current_metadata != NULL, "current_metadata can't be NULL\n"); + if (!current_metadata) return E_POINTER; + + if (index < current_metadata->count) + { + current_metadata_block = ¤t_metadata->block[index]; + *out = &mdr; + return S_OK; + } + + current_metadata_block = NULL; + return E_INVALIDARG; +} + +static HRESULT WINAPI mdbr_GetEnumerator(IWICMetadataBlockReader *iface, IEnumUnknown **enumerator) +{ + ok(0, "not implemented\n"); + return E_NOTIMPL; +} + +static /* const */ IWICMetadataBlockReaderVtbl mdbr_vtbl = +{ + mdbr_QueryInterface, + mdbr_AddRef, + mdbr_Release, + mdbr_GetContainerFormat, + mdbr_GetCount, + mdbr_GetReaderByIndex, + mdbr_GetEnumerator +}; + +static IWICMetadataBlockReader mdbr = { &mdbr_vtbl }; + +static const char xmp[] = "http://ns.adobe.com/xap/1.0/"; +static const char dc[] = "http://purl.org/dc/elements/1.1/"; +static const char tiff[] = "http://ns.adobe.com/tiff/1.0/"; + +static const struct metadata_item item1[] = +{ + { NULL, NULL, 1, 2, 3 } +}; + +static const struct metadata_item item2[] = +{ + { NULL, NULL, 1, 2, 3 }, + { "xmp", "Rating", 4, 5, 6 }, + { NULL, "Rating", 7, 8, 9 } +}; + +static const struct metadata_item item3[] = +{ + { NULL, NULL, 1, 2, 3 }, + { NULL, NULL, 4, 5, 6 }, + { NULL, NULL, 7, 8, 9 }, + { NULL, NULL, 10, 11, 12 } +}; + +static const struct metadata_item item4[] = +{ + { NULL, NULL, 1, 2, 3 }, + { xmp, "Rating", 4, 5, 6 }, + { dc, NULL, 7, 8, 9 }, + { tiff, NULL, 10, 11, 12 }, + { NULL, "RATING", 13, 14, 15 }, + { NULL, "R}ATING", 16, 17, 18 }, + { NULL, "xmp", 19, 20, 21 } +}; + +static const struct metadata_block block1[] = +{ + { &GUID_MetadataFormatIfd, 1, item1 } +}; + +static const struct metadata_block block2[] = +{ + { &GUID_MetadataFormatXMP, 1, item1 }, + { &GUID_MetadataFormatIfd, 3, item2 } +}; + +static const struct metadata_block block3[] = +{ + { &GUID_MetadataFormatXMP, 1, item1 }, + { &GUID_MetadataFormatIfd, 3, item2 }, + { &GUID_MetadataFormatXMP, 4, item3 }, + { &GUID_MetadataFormatXMP, 7, item4 }, + { &GUID_MetadataFormatIfd, 7, item4 } +}; + +static const struct metadata data1 = +{ + &GUID_ContainerFormatGif, + 1, block1 +}; + +static const struct metadata data2 = +{ + &GUID_ContainerFormatTiff, + 2, block2 +}; + +static const struct metadata data3 = +{ + &GUID_ContainerFormatPng, + 5, block3 +}; + +static void test_queryreader(void) +{ + static const char q1[] = "/ifd/{uchar=1}"; + static const char q2[] = "/ifd/xmp:{long=4}"; + static const char q3[] = "/ifd/{str=xmp}:{uint=4}"; + static const char q4[] = "/xmp/{char=7}"; + static const char q5[] = "/[1]xmp/{short=7}"; + static const char q6[] = "/[1]ifd/{str=dc}:{uint=7}"; + static const char q7[] = "/[1]ifd/{str=http://purl.org/dc/elements/1.1/%7D:%7Blonglong=7%7D"; + static const char q8[] = "/[1]ifd/{str=http://ns.adobe.com/tiff/1.0/%7D:%7Bint=10%7D"; + static const char q9[] = "/[2]xmp/xmp:{ulong=4}"; + static const char q10[] = "/[2]xmp/{str=xmp}:{ulong=4}"; + static const char q11[] = "/xmp"; + static const char q12[] = "/ifd/xmp"; + static const char q13[] = "/ifd/xmp/tiff"; + static const char q14[] = "/[0]ifd/[0]xmp/[0]tiff"; + static const char q15[] = "/[*]xmp"; + + static const char q20[] = "/ifd/\Rating"; + static const char q21[] = "/[0]ifd/Rating"; + static const char q22[] = "/[2]xmp/xmp:{str=Rating}"; + static const char q23[] = "/[2]xmp/xmp:Rating"; + + static const char q24[] = "/[1]ifd/{str=http://ns.adobe.com/xap/1.0/%7D:Rating"; + static const char q25[] = "/[1]ifd/{str=http://ns.adobe.com/xap/1.0/%7D:%7Bstr=Rating%7D"; + static const char q26[] = "/[1]ifd/{wstr=\RATING}"; + static const char q27[] = "/[1]ifd/{str=R\ATING}"; + static const char q28[] = "/[1]ifd/{str=R\}ATING}"; + + static const char q40[] = "[0]/ifd/Rating"; + static const char q41[] = "/[+1]ifd/Rating"; + static const char q42[] = "/[-1]ifd/Rating"; + static const char q43[] = "/ifd/{\str=Rating}"; + static const char q44[] = "/ifd/{badtype=0}"; + static const char q45[] = "/ifd/{uint=0x1234}"; + static const char q46[] = "/ifd/[0]Rating"; + static const char q47[] = "/ifd/[*]Rating"; + static const struct + { + BOOL todo; + const struct metadata *data; + const char *query; + HRESULT hr; + UINT vt, value; + const char *str_value; + } test_data[] = + { + { FALSE, &data1, q1, S_OK, 2, 3, NULL }, + { FALSE, &data2, q2, S_OK, 5, 6, NULL }, + { FALSE, &data2, q3, S_OK, 5, 6, NULL }, + { FALSE, &data3, q4, 0xdeadbeef }, + { FALSE, &data3, q5, S_OK, 8, 9, NULL }, + { FALSE, &data3, q6, 0xdeadbeef }, + { FALSE, &data3, q7, S_OK, 8, 9, NULL }, + { FALSE, &data3, q8, S_OK, 11, 12, NULL }, + { FALSE, &data3, q9, S_OK, 5, 6, NULL }, + { FALSE, &data3, q10, 0xdeadbeef }, + + { FALSE, &data3, q11, S_OK, VT_UNKNOWN, 0, NULL }, + { FALSE, &data3, q12, S_OK, VT_UNKNOWN, 0, NULL }, + { FALSE, &data3, q13, S_OK, VT_UNKNOWN, 0, NULL }, + { FALSE, &data3, q14, S_OK, VT_UNKNOWN, 0, NULL }, + { TRUE, &data3, q15, S_OK, VT_LPSTR, 0, the_worst }, + + { FALSE, &data3, q20, S_OK, VT_LPSTR, 0, the_worst }, + { FALSE, &data3, q21, S_OK, VT_LPSTR, 0, the_worst }, + { FALSE, &data3, q22, S_OK, VT_LPSTR, 0, the_best }, + { FALSE, &data3, q23, S_OK, VT_LPSTR, 0, the_worst }, + { FALSE, &data3, q24, S_OK, VT_LPSTR, 0, the_worst }, + { FALSE, &data3, q25, S_OK, VT_LPSTR, 0, the_best }, + { FALSE, &data3, q26, S_OK, VT_LPSTR, 0, the_worst }, + { FALSE, &data3, q27, S_OK, VT_LPSTR, 0, the_best }, + { FALSE, &data3, q28, S_OK, VT_LPSTR, 0, the_best }, + + { FALSE, &data1, q40, WINCODEC_ERR_PROPERTYNOTSUPPORTED }, + { TRUE, &data1, q41, WINCODEC_ERR_INVALIDQUERYCHARACTER }, + { TRUE, &data1, q42, WINCODEC_ERR_INVALIDQUERYCHARACTER }, + { FALSE, &data1, q43, WINCODEC_ERR_WRONGSTATE }, + { FALSE, &data1, q44, WINCODEC_ERR_WRONGSTATE }, + { TRUE, &data1, q45, DISP_E_TYPEMISMATCH }, + { TRUE, &data1, q46, E_INVALIDARG }, + { TRUE, &data1, q47, WINCODEC_ERR_REQUESTONLYVALIDATMETADATAROOT }, + }; + WCHAR queryW[256]; + HRESULT hr; + IWICComponentFactory *factory; + IWICMetadataQueryReader *reader; + GUID format; + PROPVARIANT value; + UINT i; + + hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, + &IID_IWICComponentFactory, (void **)&factory); + ok(hr == S_OK, "CoCreateInstance error %#x\n", hr); + + hr = IWICComponentFactory_CreateQueryReaderFromBlockReader(factory, &mdbr, &reader); + ok(hr == S_OK, "CreateQueryReaderFromBlockReader error %#x\n", hr); + + for (i = 0; i < sizeof(test_data)/sizeof(test_data[0]); i++) + { + current_metadata = test_data[i].data; + + hr = IWICMetadataQueryReader_GetContainerFormat(reader, &format); + ok(hr == S_OK, "%u: GetContainerFormat error %#x\n", i, hr); + ok(IsEqualGUID(&format, test_data[i].data->container_format), "%u: expected %s, got %s\n", + i, wine_dbgstr_guid(test_data[i].data->container_format), wine_dbgstr_guid(&format)); + + MultiByteToWideChar(CP_ACP, 0, test_data[i].query, -1, queryW, 256); + PropVariantInit(&value); + hr = IWICMetadataQueryReader_GetMetadataByName(reader, queryW, &value); + todo_wine_if(test_data[i].todo) + ok(hr == test_data[i].hr, "%u: expected %#x, got %#x\n", i, test_data[i].hr, hr); + if (hr == S_OK) + { + ok(value.vt == test_data[i].vt, "%u: expected %u, got %u\n", i, test_data[i].vt, value.vt); + if (test_data[i].vt == value.vt) + { + if (value.vt == VT_UNKNOWN) + { + IWICMetadataQueryReader *new_reader; + WCHAR location[256]; + UINT len; + + hr = IUnknown_QueryInterface(value.punkVal, &IID_IWICMetadataQueryReader, (void **)&new_reader); + ok(hr == S_OK, "QueryInterface error %#x\n", hr); + + location[0] = 0; + len = 0xdeadbeef; + hr = IWICMetadataQueryReader_GetLocation(new_reader, 256, location, &len); + ok(hr == S_OK, "GetLocation error %#x\n", hr); + ok(len == lstrlenW(queryW) + 1, "expected %u, got %u\n", lstrlenW(queryW) + 1, len); + ok(!lstrcmpW(location, queryW), "expected %s, got %s\n", wine_dbgstr_w(queryW), wine_dbgstr_w(location)); + + hr = IWICMetadataQueryReader_GetLocation(new_reader, 256, location, NULL); + ok(hr == E_INVALIDARG, "got %#x\n", hr); + + location[0] = 0; + len = 0xdeadbeef; + hr = IWICMetadataQueryReader_GetLocation(new_reader, 3, location, &len); + ok(hr == WINCODEC_ERR_INSUFFICIENTBUFFER, "got %#x\n", hr); + ok(len == 0xdeadbeef, "got %u\n", len); + ok(!location[0], "got %s\n", wine_dbgstr_w(location)); + + location[0] = 0; + len = 0xdeadbeef; + hr = IWICMetadataQueryReader_GetLocation(new_reader, 0, location, &len); + ok(hr == WINCODEC_ERR_INSUFFICIENTBUFFER, "got %#x\n", hr); + ok(len == 0xdeadbeef, "got %u\n", len); + ok(!location[0], "got %s\n", wine_dbgstr_w(location)); + + len = 0xdeadbeef; + hr = IWICMetadataQueryReader_GetLocation(new_reader, 0, NULL, &len); + ok(hr == S_OK, "GetLocation error %#x\n", hr); + ok(len == lstrlenW(queryW) + 1, "expected %u, got %u\n", lstrlenW(queryW) + 1, len); + + len = 0xdeadbeef; + hr = IWICMetadataQueryReader_GetLocation(new_reader, 3, NULL, &len); + ok(hr == S_OK, "GetLocation error %#x\n", hr); + ok(len == lstrlenW(queryW) + 1, "expected %u, got %u\n", lstrlenW(queryW) + 1, len); + + hr = IWICMetadataQueryReader_GetLocation(new_reader, 0, NULL, NULL); + ok(hr == E_INVALIDARG, "got %#x\n", hr); + + IWICMetadataQueryReader_Release(new_reader); + } + else if (value.vt == VT_LPSTR) + ok(!lstrcmpA(U(value).pszVal, test_data[i].str_value), "%u: expected %s, got %s\n", + i, test_data[i].str_value, U(value).pszVal); + else + ok(U(value).uiVal == test_data[i].value, "%u: expected %u, got %u\n", + i, test_data[i].value, U(value).uiVal); + } + + /* + * Do NOT call PropVariantClear(&value) for fake value types. + */ + } + } + + IWICMetadataQueryReader_Release(reader); + IWICComponentFactory_Release(factory); +} + START_TEST(metadata) { CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+ test_queryreader(); test_WICMapGuidToShortName(); test_WICMapShortNameToGuid(); test_WICMapSchemaToName();