From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/windowscodecs/clsfactory.c | 1 + dlls/windowscodecs/gifformat.c | 8 +- dlls/windowscodecs/imgfactory.c | 38 +++- dlls/windowscodecs/metadatahandler.c | 198 +++++++++++++++++- dlls/windowscodecs/regsvr.c | 18 ++ dlls/windowscodecs/tests/metadata.c | 44 +++- dlls/windowscodecs/wincodecs_private.h | 4 + dlls/windowscodecs/windowscodecs_wincodec.idl | 7 + 8 files changed, 292 insertions(+), 26 deletions(-)
diff --git a/dlls/windowscodecs/clsfactory.c b/dlls/windowscodecs/clsfactory.c index 1deb6d722d5..90309314d6d 100644 --- a/dlls/windowscodecs/clsfactory.c +++ b/dlls/windowscodecs/clsfactory.c @@ -65,6 +65,7 @@ static const classinfo wic_classes[] = { {&CLSID_WICIfdMetadataReader, IfdMetadataReader_CreateInstance}, {&CLSID_WICGpsMetadataReader, GpsMetadataReader_CreateInstance}, {&CLSID_WICExifMetadataReader, ExifMetadataReader_CreateInstance}, + {&CLSID_WICApp1MetadataReader, App1MetadataReader_CreateInstance}, {&CLSID_WICPngChrmMetadataReader, PngChrmReader_CreateInstance}, {&CLSID_WICPngGamaMetadataReader, PngGamaReader_CreateInstance}, {&CLSID_WICPngHistMetadataReader, PngHistReader_CreateInstance}, diff --git a/dlls/windowscodecs/gifformat.c b/dlls/windowscodecs/gifformat.c index 0aa2d9420dd..0b32f0ddfa7 100644 --- a/dlls/windowscodecs/gifformat.c +++ b/dlls/windowscodecs/gifformat.c @@ -533,7 +533,7 @@ static IStream *create_stream(const void *data, int data_size) return FAILED(hr) ? NULL : stream; }
-static HRESULT create_metadata_reader(const void *data, int data_size, +static HRESULT create_gif_metadata_reader(const void *data, int data_size, class_constructor constructor, IWICMetadataReader **reader) { @@ -998,7 +998,7 @@ static HRESULT WINAPI GifFrameDecode_Block_GetReaderByIndex(IWICMetadataBlockRea data_size = ext->ByteCount; }
- return create_metadata_reader(data, data_size, constructor, reader); + return create_gif_metadata_reader(data, data_size, constructor, reader); }
static HRESULT WINAPI GifFrameDecode_Block_GetEnumerator(IWICMetadataBlockReader *iface, @@ -1354,7 +1354,7 @@ static HRESULT WINAPI GifDecoder_Block_GetReaderByIndex(IWICMetadataBlockReader if (!reader) return E_INVALIDARG;
if (index == 0) - return create_metadata_reader(This->LSD_data, sizeof(This->LSD_data), + return create_gif_metadata_reader(This->LSD_data, sizeof(This->LSD_data), LSDReader_CreateInstance, reader);
for (i = 0; i < This->gif->Extensions.ExtensionBlockCount; i++) @@ -1370,7 +1370,7 @@ static HRESULT WINAPI GifDecoder_Block_GetReaderByIndex(IWICMetadataBlockReader else constructor = UnknownMetadataReader_CreateInstance;
- return create_metadata_reader(This->gif->Extensions.ExtensionBlocks[i].Bytes, + return create_gif_metadata_reader(This->gif->Extensions.ExtensionBlocks[i].Bytes, This->gif->Extensions.ExtensionBlocks[i].ByteCount, constructor, reader); } diff --git a/dlls/windowscodecs/imgfactory.c b/dlls/windowscodecs/imgfactory.c index 2f5617cedc1..7c9fe6a8920 100644 --- a/dlls/windowscodecs/imgfactory.c +++ b/dlls/windowscodecs/imgfactory.c @@ -1328,7 +1328,8 @@ static enum iterator_result create_metadata_reader_iterator(IUnknown *item, IWICPersistStream *persist_stream = NULL; IWICMetadataReaderInfo *readerinfo; IWICMetadataReader *reader = NULL; - LARGE_INTEGER zero = {{0}}; + LARGE_INTEGER move = {{0}}; + ULARGE_INTEGER pos; HRESULT hr; GUID guid;
@@ -1361,8 +1362,13 @@ static enum iterator_result create_metadata_reader_iterator(IUnknown *item,
if (context->stream) { + BOOL restore = FALSE; + if (SUCCEEDED(hr)) - hr = IStream_Seek(context->stream, zero, STREAM_SEEK_SET, NULL); + { + move.QuadPart = 0; + restore = SUCCEEDED(hr = IStream_Seek(context->stream, move, STREAM_SEEK_CUR, &pos)); + }
if (SUCCEEDED(hr)) hr = IWICMetadataReader_QueryInterface(reader, &IID_IWICPersistStream, (void **)&persist_stream); @@ -1371,6 +1377,12 @@ static enum iterator_result create_metadata_reader_iterator(IUnknown *item, hr = IWICPersistStream_LoadEx(persist_stream, context->stream, context->vendor, context->options & WICPersistOptionMask);
+ if (restore) + { + memcpy(&move, &pos, sizeof(pos)); + hr = IStream_Seek(context->stream, move, STREAM_SEEK_SET, NULL); + } + if (persist_stream) IWICPersistStream_Release(persist_stream); } @@ -1444,18 +1456,12 @@ static HRESULT create_unknown_metadata_reader(IStream *stream, DWORD options, IW return hr; }
-static HRESULT WINAPI ComponentFactory_CreateMetadataReader(IWICComponentFactory *iface, - REFGUID format, const GUID *vendor, DWORD options, IStream *stream, IWICMetadataReader **reader) +HRESULT create_metadata_reader(REFGUID format, const GUID *vendor, DWORD options, IStream *stream, + IWICMetadataReader **reader) { struct iterator_context context = { 0 }; HRESULT hr;
- TRACE("%p,%s,%s,%lx,%p,%p\n", iface, debugstr_guid(format), debugstr_guid(vendor), - options, stream, reader); - - if (!format || !reader) - return E_INVALIDARG; - context.format = format; context.vendor = vendor; context.options = options; @@ -1479,6 +1485,18 @@ static HRESULT WINAPI ComponentFactory_CreateMetadataReader(IWICComponentFactory return *reader ? S_OK : WINCODEC_ERR_COMPONENTNOTFOUND; }
+static HRESULT WINAPI ComponentFactory_CreateMetadataReader(IWICComponentFactory *iface, + REFGUID format, const GUID *vendor, DWORD options, IStream *stream, IWICMetadataReader **reader) +{ + TRACE("%p,%s,%s,%lx,%p,%p\n", iface, debugstr_guid(format), debugstr_guid(vendor), + options, stream, reader); + + if (!format || !reader) + return E_INVALIDARG; + + return create_metadata_reader(format, vendor, options, stream, reader); +} + static HRESULT WINAPI ComponentFactory_CreateMetadataReaderFromContainer(IWICComponentFactory *iface, REFGUID format, const GUID *vendor, DWORD options, IStream *stream, IWICMetadataReader **reader) { diff --git a/dlls/windowscodecs/metadatahandler.c b/dlls/windowscodecs/metadatahandler.c index 20c9e168e45..0fe900da30a 100644 --- a/dlls/windowscodecs/metadatahandler.c +++ b/dlls/windowscodecs/metadatahandler.c @@ -807,6 +807,12 @@ struct IFD_entry #define IFD_DOUBLE 12 #define IFD_IFD 13
+enum ifd_tags +{ + IFD_EXIF_TAG = 0x8769, + IFD_GPS_TAG = 0x8825, +}; + static int tag_to_vt(SHORT tag) { static const int tag2vt[] = @@ -829,13 +835,36 @@ static int tag_to_vt(SHORT tag) return (tag > 0 && tag <= 13) ? tag2vt[tag] : VT_BLOB; }
-static HRESULT load_IFD_entry(IStream *input, const struct IFD_entry *entry, - MetadataItem *item, BOOL native_byte_order) +static HRESULT create_stream_wrapper(IStream *input, ULONG offset, IStream **wrapper) +{ + ULARGE_INTEGER start, maxsize = {{~0UL}}; + IWICStream *wic_stream = NULL; + HRESULT hr; + + start.QuadPart = offset; + + hr = StreamImpl_Create(&wic_stream); + if (SUCCEEDED(hr)) + hr = IWICStream_InitializeFromIStreamRegion(wic_stream, input, start, maxsize); + + if (SUCCEEDED(hr)) + hr = IWICStream_QueryInterface(wic_stream, &IID_IStream, (void **)wrapper); + if (wic_stream) + IWICStream_Release(wic_stream); + + return hr; +} + +static HRESULT load_IFD_entry(IStream *input, const GUID *vendor, DWORD options, const struct IFD_entry *entry, + MetadataItem *item, BOOL resolve_pointer_tags) { + BOOL native_byte_order = !(options & WICPersistOptionBigEndian); ULONG count, value, i, bytesread; + IWICMetadataReader *sub_reader; + IStream *sub_stream; SHORT type; LARGE_INTEGER pos; - HRESULT hr; + HRESULT hr = S_OK;
item->schema.vt = VT_EMPTY; item->id.vt = VT_UI2; @@ -1105,11 +1134,47 @@ static HRESULT load_IFD_entry(IStream *input, const struct IFD_entry *entry, FIXME("loading field of type %d, count %lu is not implemented\n", type, count); break; } - return S_OK; + + switch (item->id.uiVal) + { + case IFD_EXIF_TAG: + case IFD_GPS_TAG: + + if (!resolve_pointer_tags) + break; + + if (item->value.vt != VT_UI4) + break; + + hr = create_stream_wrapper(input, 0, &sub_stream); + + pos.QuadPart = item->value.ulVal; + if (SUCCEEDED(hr)) + hr = IStream_Seek(sub_stream, pos, STREAM_SEEK_SET, NULL); + + if (SUCCEEDED(hr)) + hr = create_metadata_reader(item->id.uiVal == IFD_EXIF_TAG ? &GUID_MetadataFormatExif : &GUID_MetadataFormatGps, + vendor, options | WICMetadataCreationFailUnknown, sub_stream, &sub_reader); + + if (SUCCEEDED(hr)) + { + item->value.vt = VT_UNKNOWN; + item->value.punkVal = (IUnknown *)sub_reader; + } + + if (sub_stream) + IStream_Release(sub_stream); + + break; + default: + break; + } + + return hr; }
-static HRESULT LoadIfdMetadata(IStream *input, const GUID *preferred_vendor, - DWORD persist_options, MetadataItem **items, DWORD *item_count) +static HRESULT load_ifd_metadata_internal(IStream *input, const GUID *vendor, + DWORD persist_options, BOOL resolve_pointer_tags, MetadataItem **items, DWORD *item_count) { HRESULT hr; MetadataItem *result; @@ -1188,7 +1253,7 @@ static HRESULT LoadIfdMetadata(IStream *input, const GUID *preferred_vendor,
for (i = 0; i < count; i++) { - hr = load_IFD_entry(input, &entry[i], &result[i], native_byte_order); + hr = load_IFD_entry(input, vendor, persist_options, &entry[i], &result[i], resolve_pointer_tags); if (FAILED(hr)) { free(entry); @@ -1205,6 +1270,109 @@ static HRESULT LoadIfdMetadata(IStream *input, const GUID *preferred_vendor, return S_OK; }
+static HRESULT LoadIfdMetadata(IStream *input, const GUID *vendor, + DWORD options, MetadataItem **items, DWORD *item_count) +{ + TRACE("%p, %#lx.\n", input, options); + return load_ifd_metadata_internal(input, vendor, options, TRUE, items, item_count); +} + +static HRESULT LoadExifMetadata(IStream *input, const GUID *vendor, + DWORD options, MetadataItem **items, DWORD *item_count) +{ + TRACE("%p, %#lx.\n", input, options); + + return load_ifd_metadata_internal(input, vendor, options, FALSE, items, item_count); +} + +static HRESULT LoadGpsMetadata(IStream *input, const GUID *vendor, + DWORD options, MetadataItem **items, DWORD *item_count) +{ + TRACE("%p, %#lx.\n", input, options); + + return load_ifd_metadata_internal(input, vendor, options, FALSE, items, item_count); +} + +static HRESULT LoadApp1Metadata(IStream *input, const GUID *vendor, DWORD options, MetadataItem **items, DWORD *item_count) +{ + static const char exif_header[] = {'E','x','i','f',0,0}; + IWICMetadataReader *ifd_reader; + BOOL native_byte_order; + LARGE_INTEGER move; + +#include "pshpack2.h" + struct app1_header + { + BYTE exif_header[6]; + BYTE bom[2]; + USHORT marker; + ULONG ifd0_offset; + } header; +#include "poppack.h" + + IStream *ifd_stream; + ULONG length; + HRESULT hr; + + if (FAILED(hr = IStream_Read(input, &header, sizeof(header), &length))) + return hr; + if (length != sizeof(header)) + return WINCODEC_ERR_BADMETADATAHEADER; + + if (memcmp(header.exif_header, exif_header, sizeof(exif_header))) + return WINCODEC_ERR_BADMETADATAHEADER; + + options &= ~(WICPersistOptionLittleEndian | WICPersistOptionBigEndian); + options |= WICMetadataCreationFailUnknown; + if (!memcmp(header.bom, "II", 2)) + options |= WICPersistOptionLittleEndian; + else if (!memcmp(header.bom, "MM", 2)) + options |= WICPersistOptionBigEndian; + else + { + WARN("Unrecognized bom marker %#x%#x.\n", header.bom[0], header.bom[1]); + return WINCODEC_ERR_BADMETADATAHEADER; + } + native_byte_order = !(options & WICPersistOptionBigEndian); + + SWAP_USHORT(header.marker); + SWAP_ULONG(header.ifd0_offset); + + if (header.marker != 0x002a) + { + WARN("Unrecognized marker %#x.\n", header.marker); + return WINCODEC_ERR_BADMETADATAHEADER; + } + + if (FAILED(hr = create_stream_wrapper(input, sizeof(exif_header), &ifd_stream))) + return hr; + move.QuadPart = header.ifd0_offset; + hr = IStream_Seek(ifd_stream, move, STREAM_SEEK_SET, NULL); + + hr = create_metadata_reader(&GUID_MetadataFormatIfd, vendor, options, ifd_stream, &ifd_reader); + IStream_Release(ifd_stream); + + if (FAILED(hr)) + { + WARN("Failed to create IFD0 reader.\n"); + return hr; + } + + if (!(*items = calloc(1, sizeof(**items)))) + { + IWICMetadataReader_Release(ifd_reader); + return E_OUTOFMEMORY; + } + + (*items)[0].id.vt = VT_UI2; + (*items)[0].id.uiVal = 0; + (*items)[0].value.vt = VT_UNKNOWN; + (*items)[0].value.punkVal = (IUnknown *)ifd_reader; + *item_count = 1; + + return S_OK; +} + static const MetadataHandlerVtbl IfdMetadataReader_Vtbl = { 0, &CLSID_WICIfdMetadataReader, @@ -1220,7 +1388,7 @@ static const MetadataHandlerVtbl GpsMetadataReader_Vtbl = { 0, &CLSID_WICGpsMetadataReader, - LoadIfdMetadata + LoadGpsMetadata };
HRESULT GpsMetadataReader_CreateInstance(REFIID iid, void **ppv) @@ -1232,10 +1400,22 @@ static const MetadataHandlerVtbl ExifMetadataReader_Vtbl = { 0, &CLSID_WICExifMetadataReader, - LoadIfdMetadata + LoadExifMetadata };
HRESULT ExifMetadataReader_CreateInstance(REFIID iid, void **ppv) { return MetadataReader_Create(&ExifMetadataReader_Vtbl, iid, ppv); } + +static const MetadataHandlerVtbl App1MetadataReader_Vtbl = +{ + 0, + &CLSID_WICApp1MetadataReader, + LoadApp1Metadata +}; + +HRESULT App1MetadataReader_CreateInstance(REFIID iid, void **ppv) +{ + return MetadataReader_Create(&App1MetadataReader_Vtbl, iid, ppv); +} diff --git a/dlls/windowscodecs/regsvr.c b/dlls/windowscodecs/regsvr.c index d8886c3c888..f9ba2b083eb 100644 --- a/dlls/windowscodecs/regsvr.c +++ b/dlls/windowscodecs/regsvr.c @@ -1591,6 +1591,14 @@ static const struct reader_containers exif_containers[] = { { NULL } /* list terminator */ };
+static const struct reader_containers app1_containers[] = { + { + &GUID_ContainerFormatJpeg, + ifd_metadata_pattern + }, + { NULL } /* list terminator */ +}; + static const BYTE tEXt[] = "tEXt";
static const struct metadata_pattern pngtext_metadata_pattern[] = { @@ -1781,6 +1789,16 @@ static struct regsvr_metadatareader const metadatareader_list[] = { 1, 1, 0, exif_containers }, + { &CLSID_WICApp1MetadataReader, + "The Wine Project", + "App1 Reader", + "1.0.0.0", + "1.0.0.0", + &GUID_VendorMicrosoft, + &GUID_MetadataFormatApp1, + 1, 1, 0, + app1_containers + }, { &CLSID_WICPngChrmMetadataReader, "The Wine Project", "Chunk cHRM Reader", diff --git a/dlls/windowscodecs/tests/metadata.c b/dlls/windowscodecs/tests/metadata.c index 065347ebc60..c048a2d6cc3 100644 --- a/dlls/windowscodecs/tests/metadata.c +++ b/dlls/windowscodecs/tests/metadata.c @@ -1369,8 +1369,28 @@ static void test_ifd_content(IWICMetadataReader *reader)
static void test_metadata_Ifd(void) { +#include "pshpack2.h" + static const struct ifd0_data + { + USHORT count; + struct IFD_entry ifd0[2]; + ULONG next_IFD; + } + ifd0_data = + { + 2, + { + /* Exif IFD pointer */ + { 0x8769, IFD_LONG, 1, 0 }, + /* GPS IFD pointer */ + { 0x8825, IFD_LONG, 1, 0 }, + }, + }; +#include "poppack.h" + IWICMetadataReader *reader; IWICMetadataWriter *writer; + PROPVARIANT id, value; GUID format; UINT count; HRESULT hr; @@ -1404,6 +1424,26 @@ static void test_metadata_Ifd(void) hr = IWICMetadataReader_GetMetadataFormat(reader, NULL); ok(hr == E_INVALIDARG, "GetMetadataFormat should fail\n");
+ /* IFD contains pointer tags. */ + load_stream(reader, (const char *)&ifd0_data, sizeof(ifd0_data), 0); + hr = IWICMetadataReader_GetCount(reader, &count); + ok(hr == S_OK, "GetCount error %#lx\n", hr); + ok(count == 2, "unexpected count %u\n", count); + + PropVariantInit(&id); + PropVariantInit(&value); + hr = IWICMetadataReader_GetValueByIndex(reader, 0, NULL, &id, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(value.vt == VT_UNKNOWN, "Unexpected value type %u.\n", value.vt); + PropVariantClear(&value); + + PropVariantInit(&id); + PropVariantInit(&value); + hr = IWICMetadataReader_GetValueByIndex(reader, 1, NULL, &id, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(value.vt == VT_UNKNOWN, "Unexpected value type %u.\n", value.vt); + PropVariantClear(&value); + IWICMetadataReader_Release(reader);
hr = CoCreateInstance(&CLSID_WICIfdMetadataWriter, NULL, CLSCTX_INPROC_SERVER, @@ -1738,7 +1778,6 @@ static void test_CreateMetadataReader(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
hr = IWICPersistStream_LoadEx(persist_stream, stream, NULL, 0); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
hr = get_persist_stream(reader, &stream2); @@ -4008,9 +4047,7 @@ static void test_metadata_App1(void)
hr = CoCreateInstance(&CLSID_WICApp1MetadataReader, NULL, CLSCTX_INPROC_SERVER, &IID_IWICMetadataReader, (void **)&reader); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (FAILED(hr)) return;
check_interface(reader, &IID_IWICMetadataReader, TRUE); check_interface(reader, &IID_IPersist, TRUE); @@ -4175,6 +4212,7 @@ static void test_metadata_App1(void)
hr = CoCreateInstance(&CLSID_WICApp1MetadataWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IWICMetadataWriter, (void **)&writer); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); if (FAILED(hr)) return;
diff --git a/dlls/windowscodecs/wincodecs_private.h b/dlls/windowscodecs/wincodecs_private.h index 541ea13923e..e714163b660 100644 --- a/dlls/windowscodecs/wincodecs_private.h +++ b/dlls/windowscodecs/wincodecs_private.h @@ -219,6 +219,7 @@ extern HRESULT UnknownMetadataReader_CreateInstance(REFIID iid, void** ppv); extern HRESULT IfdMetadataReader_CreateInstance(REFIID iid, void **ppv); extern HRESULT GpsMetadataReader_CreateInstance(REFIID iid, void **ppv); extern HRESULT ExifMetadataReader_CreateInstance(REFIID iid, void **ppv); +extern HRESULT App1MetadataReader_CreateInstance(REFIID iid, void **ppv); extern HRESULT PngChrmReader_CreateInstance(REFIID iid, void** ppv); extern HRESULT PngGamaReader_CreateInstance(REFIID iid, void** ppv); extern HRESULT PngHistReader_CreateInstance(REFIID iid, void** ppv); @@ -317,6 +318,9 @@ HRESULT CDECL decoder_get_color_context(struct decoder* This, UINT frame, UINT n BYTE **data, DWORD *datasize); void CDECL decoder_destroy(struct decoder *This);
+HRESULT create_metadata_reader(REFGUID format, const GUID *vendor, DWORD options, IStream *stream, + IWICMetadataReader **reader); + struct encoder_funcs;
/* sync with encoder_option_properties */ diff --git a/dlls/windowscodecs/windowscodecs_wincodec.idl b/dlls/windowscodecs/windowscodecs_wincodec.idl index 1e119c132f2..122f6d0e007 100644 --- a/dlls/windowscodecs/windowscodecs_wincodec.idl +++ b/dlls/windowscodecs/windowscodecs_wincodec.idl @@ -174,6 +174,13 @@ coclass WICGpsMetadataReader { interface IWICMetadataReader; } ] coclass WICExifMetadataReader { interface IWICMetadataReader; }
+[ + helpstring("WIC App1 Metadata Reader"), + threading(both), + uuid(dde33513-774e-4bcd-ae79-02f4adfe62fc) +] +coclass WICApp1MetadataReader { interface IWICMetadataReader; } + [ helpstring("WIC Png cHRM Metadata Reader"), threading(both),