windowscodecs: Implement IWICBitmapFrameDecode::GetColorContexts for PNG images.
[wine] / dlls / windowscodecs / metadatahandler.c
1 /*
2  * Copyright 2012 Vincent Povirk for CodeWeavers
3  * Copyright 2012 Dmitry Timoshkov
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19
20 #include "config.h"
21
22 #include <stdarg.h>
23 #include <stdio.h>
24
25 #define COBJMACROS
26 #define NONAMELESSUNION
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winternl.h"
31 #include "objbase.h"
32 #include "wincodec.h"
33 #include "wincodecsdk.h"
34
35 #include "wincodecs_private.h"
36
37 #include "wine/debug.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
40
41 typedef struct MetadataHandler {
42     IWICMetadataWriter IWICMetadataWriter_iface;
43     LONG ref;
44     IWICPersistStream IWICPersistStream_iface;
45     const MetadataHandlerVtbl *vtable;
46     MetadataItem *items;
47     DWORD item_count;
48     CRITICAL_SECTION lock;
49 } MetadataHandler;
50
51 static inline MetadataHandler *impl_from_IWICMetadataWriter(IWICMetadataWriter *iface)
52 {
53     return CONTAINING_RECORD(iface, MetadataHandler, IWICMetadataWriter_iface);
54 }
55
56 static inline MetadataHandler *impl_from_IWICPersistStream(IWICPersistStream *iface)
57 {
58     return CONTAINING_RECORD(iface, MetadataHandler, IWICPersistStream_iface);
59 }
60
61 static void MetadataHandler_FreeItems(MetadataHandler *This)
62 {
63     int i;
64
65     for (i=0; i<This->item_count; i++)
66     {
67         PropVariantClear(&This->items[i].schema);
68         PropVariantClear(&This->items[i].id);
69         PropVariantClear(&This->items[i].value);
70     }
71
72     HeapFree(GetProcessHeap(), 0, This->items);
73 }
74
75 static HRESULT MetadataHandlerEnum_Create(MetadataHandler *parent, DWORD index,
76     IWICEnumMetadataItem **ppIEnumMetadataItem);
77
78 static HRESULT WINAPI MetadataHandler_QueryInterface(IWICMetadataWriter *iface, REFIID iid,
79     void **ppv)
80 {
81     MetadataHandler *This = impl_from_IWICMetadataWriter(iface);
82     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
83
84     if (!ppv) return E_INVALIDARG;
85
86     if (IsEqualIID(&IID_IUnknown, iid) ||
87         IsEqualIID(&IID_IWICMetadataReader, iid) ||
88         (IsEqualIID(&IID_IWICMetadataWriter, iid) && This->vtable->is_writer))
89     {
90         *ppv = &This->IWICMetadataWriter_iface;
91     }
92     else if (IsEqualIID(&IID_IPersist, iid) ||
93              IsEqualIID(&IID_IPersistStream, iid) ||
94              IsEqualIID(&IID_IWICPersistStream, iid))
95     {
96         *ppv = &This->IWICPersistStream_iface;
97     }
98     else
99     {
100         *ppv = NULL;
101         return E_NOINTERFACE;
102     }
103
104     IUnknown_AddRef((IUnknown*)*ppv);
105     return S_OK;
106 }
107
108 static ULONG WINAPI MetadataHandler_AddRef(IWICMetadataWriter *iface)
109 {
110     MetadataHandler *This = impl_from_IWICMetadataWriter(iface);
111     ULONG ref = InterlockedIncrement(&This->ref);
112
113     TRACE("(%p) refcount=%u\n", iface, ref);
114
115     return ref;
116 }
117
118 static ULONG WINAPI MetadataHandler_Release(IWICMetadataWriter *iface)
119 {
120     MetadataHandler *This = impl_from_IWICMetadataWriter(iface);
121     ULONG ref = InterlockedDecrement(&This->ref);
122
123     TRACE("(%p) refcount=%u\n", iface, ref);
124
125     if (ref == 0)
126     {
127         MetadataHandler_FreeItems(This);
128         This->lock.DebugInfo->Spare[0] = 0;
129         DeleteCriticalSection(&This->lock);
130         HeapFree(GetProcessHeap(), 0, This);
131     }
132
133     return ref;
134 }
135
136 static HRESULT WINAPI MetadataHandler_GetMetadataHandlerInfo(IWICMetadataWriter *iface,
137     IWICMetadataHandlerInfo **ppIHandler)
138 {
139     HRESULT hr;
140     IWICComponentInfo *component_info;
141     MetadataHandler *This = impl_from_IWICMetadataWriter(iface);
142
143     TRACE("%p,%p\n", iface, ppIHandler);
144
145     hr = CreateComponentInfo(This->vtable->clsid, &component_info);
146     if (FAILED(hr)) return hr;
147
148     hr = IWICComponentInfo_QueryInterface(component_info, &IID_IWICMetadataHandlerInfo,
149         (void **)ppIHandler);
150
151     IWICComponentInfo_Release(component_info);
152     return hr;
153 }
154
155 static HRESULT WINAPI MetadataHandler_GetMetadataFormat(IWICMetadataWriter *iface,
156     GUID *pguidMetadataFormat)
157 {
158     HRESULT hr;
159     IWICMetadataHandlerInfo *metadata_info;
160
161     TRACE("%p,%p\n", iface, pguidMetadataFormat);
162
163     if (!pguidMetadataFormat) return E_INVALIDARG;
164
165     hr = MetadataHandler_GetMetadataHandlerInfo(iface, &metadata_info);
166     if (FAILED(hr)) return hr;
167
168     hr = IWICMetadataHandlerInfo_GetMetadataFormat(metadata_info, pguidMetadataFormat);
169     IWICMetadataHandlerInfo_Release(metadata_info);
170
171     return hr;
172 }
173
174 static HRESULT WINAPI MetadataHandler_GetCount(IWICMetadataWriter *iface,
175     UINT *pcCount)
176 {
177     MetadataHandler *This = impl_from_IWICMetadataWriter(iface);
178
179     TRACE("%p,%p\n", iface, pcCount);
180
181     if (!pcCount) return E_INVALIDARG;
182
183     *pcCount = This->item_count;
184     return S_OK;
185 }
186
187 static HRESULT WINAPI MetadataHandler_GetValueByIndex(IWICMetadataWriter *iface,
188     UINT index, PROPVARIANT *schema, PROPVARIANT *id, PROPVARIANT *value)
189 {
190     HRESULT hr = S_OK;
191     MetadataHandler *This = impl_from_IWICMetadataWriter(iface);
192
193     TRACE("%p,%u,%p,%p,%p\n", iface, index, schema, id, value);
194
195     EnterCriticalSection(&This->lock);
196
197     if (index >= This->item_count)
198     {
199         LeaveCriticalSection(&This->lock);
200         return E_INVALIDARG;
201     }
202
203     if (schema)
204         hr = PropVariantCopy(schema, &This->items[index].schema);
205
206     if (SUCCEEDED(hr) && id)
207         hr = PropVariantCopy(id, &This->items[index].id);
208
209     if (SUCCEEDED(hr) && value)
210         hr = PropVariantCopy(value, &This->items[index].value);
211
212     LeaveCriticalSection(&This->lock);
213     return hr;
214 }
215
216 static BOOL get_int_value(const PROPVARIANT *pv, LONGLONG *value)
217 {
218     switch (pv->vt)
219     {
220     case VT_NULL:
221     case VT_EMPTY:
222         *value = 0;
223         break;
224     case VT_I1:
225         *value = pv->u.cVal;
226         break;
227     case VT_UI1:
228         *value = pv->u.bVal;
229         break;
230     case VT_I2:
231         *value = pv->u.iVal;
232         break;
233     case VT_UI2:
234         *value = pv->u.uiVal;
235         break;
236     case VT_I4:
237         *value = pv->u.lVal;
238         break;
239     case VT_UI4:
240         *value = pv->u.ulVal;
241         break;
242     case VT_I8:
243     case VT_UI8:
244         *value = pv->u.hVal.QuadPart;
245         break;
246     default:
247         FIXME("not supported variant type %d\n", pv->vt);
248         return FALSE;
249     }
250     return TRUE;
251 }
252
253 /* FiXME: Use propsys.PropVariantCompareEx once it's implemented */
254 static int propvar_cmp(const PROPVARIANT *v1, const PROPVARIANT *v2)
255 {
256     LONGLONG value1, value2;
257
258     if (v1->vt == VT_LPSTR && v2->vt == VT_LPSTR)
259     {
260         return lstrcmpA(v1->u.pszVal, v2->u.pszVal);
261     }
262
263     if (v1->vt == VT_LPWSTR && v2->vt == VT_LPWSTR)
264     {
265         return lstrcmpiW(v1->u.pwszVal, v2->u.pwszVal);
266     }
267
268     if (!get_int_value(v1, &value1)) return -1;
269     if (!get_int_value(v2, &value2)) return -1;
270
271     value1 -= value2;
272     if (value1) return value1 < 0 ? -1 : 1;
273     return 0;
274 }
275
276 static HRESULT WINAPI MetadataHandler_GetValue(IWICMetadataWriter *iface,
277     const PROPVARIANT *schema, const PROPVARIANT *id, PROPVARIANT *value)
278 {
279     UINT i;
280     HRESULT hr = WINCODEC_ERR_PROPERTYNOTFOUND;
281     MetadataHandler *This = impl_from_IWICMetadataWriter(iface);
282
283     TRACE("(%p,%p,%p,%p)\n", iface, schema, id, value);
284
285     if (!id) return E_INVALIDARG;
286
287     EnterCriticalSection(&This->lock);
288
289     for (i = 0; i < This->item_count; i++)
290     {
291         if (schema && This->items[i].schema.vt != VT_EMPTY)
292         {
293             if (propvar_cmp(schema, &This->items[i].schema) != 0) continue;
294         }
295
296         if (propvar_cmp(id, &This->items[i].id) != 0) continue;
297
298         hr = value ? PropVariantCopy(value, &This->items[i].value) : S_OK;
299         break;
300     }
301
302     LeaveCriticalSection(&This->lock);
303     return hr;
304 }
305
306 static HRESULT WINAPI MetadataHandler_GetEnumerator(IWICMetadataWriter *iface,
307     IWICEnumMetadataItem **ppIEnumMetadata)
308 {
309     MetadataHandler *This = impl_from_IWICMetadataWriter(iface);
310     TRACE("(%p,%p)\n", iface, ppIEnumMetadata);
311     return MetadataHandlerEnum_Create(This, 0, ppIEnumMetadata);
312 }
313
314 static HRESULT WINAPI MetadataHandler_SetValue(IWICMetadataWriter *iface,
315     const PROPVARIANT *pvarSchema, const PROPVARIANT *pvarId, const PROPVARIANT *pvarValue)
316 {
317     FIXME("(%p,%p,%p,%p): stub\n", iface, pvarSchema, pvarId, pvarValue);
318     return E_NOTIMPL;
319 }
320
321 static HRESULT WINAPI MetadataHandler_SetValueByIndex(IWICMetadataWriter *iface,
322     UINT nIndex, const PROPVARIANT *pvarSchema, const PROPVARIANT *pvarId, const PROPVARIANT *pvarValue)
323 {
324     FIXME("(%p,%u,%p,%p,%p): stub\n", iface, nIndex, pvarSchema, pvarId, pvarValue);
325     return E_NOTIMPL;
326 }
327
328 static HRESULT WINAPI MetadataHandler_RemoveValue(IWICMetadataWriter *iface,
329     const PROPVARIANT *pvarSchema, const PROPVARIANT *pvarId)
330 {
331     FIXME("(%p,%p,%p): stub\n", iface, pvarSchema, pvarId);
332     return E_NOTIMPL;
333 }
334
335 static HRESULT WINAPI MetadataHandler_RemoveValueByIndex(IWICMetadataWriter *iface,
336     UINT nIndex)
337 {
338     FIXME("(%p,%u): stub\n", iface, nIndex);
339     return E_NOTIMPL;
340 }
341
342 static const IWICMetadataWriterVtbl MetadataHandler_Vtbl = {
343     MetadataHandler_QueryInterface,
344     MetadataHandler_AddRef,
345     MetadataHandler_Release,
346     MetadataHandler_GetMetadataFormat,
347     MetadataHandler_GetMetadataHandlerInfo,
348     MetadataHandler_GetCount,
349     MetadataHandler_GetValueByIndex,
350     MetadataHandler_GetValue,
351     MetadataHandler_GetEnumerator,
352     MetadataHandler_SetValue,
353     MetadataHandler_SetValueByIndex,
354     MetadataHandler_RemoveValue,
355     MetadataHandler_RemoveValueByIndex
356 };
357
358 static HRESULT WINAPI MetadataHandler_PersistStream_QueryInterface(IWICPersistStream *iface,
359     REFIID iid, void **ppv)
360 {
361     MetadataHandler *This = impl_from_IWICPersistStream(iface);
362     return IWICMetadataWriter_QueryInterface(&This->IWICMetadataWriter_iface, iid, ppv);
363 }
364
365 static ULONG WINAPI MetadataHandler_PersistStream_AddRef(IWICPersistStream *iface)
366 {
367     MetadataHandler *This = impl_from_IWICPersistStream(iface);
368     return IWICMetadataWriter_AddRef(&This->IWICMetadataWriter_iface);
369 }
370
371 static ULONG WINAPI MetadataHandler_PersistStream_Release(IWICPersistStream *iface)
372 {
373     MetadataHandler *This = impl_from_IWICPersistStream(iface);
374     return IWICMetadataWriter_Release(&This->IWICMetadataWriter_iface);
375 }
376
377 static HRESULT WINAPI MetadataHandler_GetClassID(IWICPersistStream *iface,
378     CLSID *pClassID)
379 {
380     FIXME("(%p,%p): stub\n", iface, pClassID);
381     return E_NOTIMPL;
382 }
383
384 static HRESULT WINAPI MetadataHandler_IsDirty(IWICPersistStream *iface)
385 {
386     FIXME("(%p): stub\n", iface);
387     return E_NOTIMPL;
388 }
389
390 static HRESULT WINAPI MetadataHandler_Load(IWICPersistStream *iface,
391     IStream *pStm)
392 {
393     MetadataHandler *This = impl_from_IWICPersistStream(iface);
394     TRACE("(%p,%p)\n", iface, pStm);
395     return IWICPersistStream_LoadEx(&This->IWICPersistStream_iface, pStm, NULL, WICPersistOptionsDefault);
396 }
397
398 static HRESULT WINAPI MetadataHandler_Save(IWICPersistStream *iface,
399     IStream *pStm, BOOL fClearDirty)
400 {
401     FIXME("(%p,%p,%i): stub\n", iface, pStm, fClearDirty);
402     return E_NOTIMPL;
403 }
404
405 static HRESULT WINAPI MetadataHandler_GetSizeMax(IWICPersistStream *iface,
406     ULARGE_INTEGER *pcbSize)
407 {
408     FIXME("(%p,%p): stub\n", iface, pcbSize);
409     return E_NOTIMPL;
410 }
411
412 static HRESULT WINAPI MetadataHandler_LoadEx(IWICPersistStream *iface,
413     IStream *pIStream, const GUID *pguidPreferredVendor, DWORD dwPersistOptions)
414 {
415     MetadataHandler *This = impl_from_IWICPersistStream(iface);
416     HRESULT hr;
417     MetadataItem *new_items=NULL;
418     DWORD item_count=0;
419
420     TRACE("(%p,%p,%s,%x)\n", iface, pIStream, debugstr_guid(pguidPreferredVendor), dwPersistOptions);
421
422     EnterCriticalSection(&This->lock);
423
424     hr = This->vtable->fnLoad(pIStream, pguidPreferredVendor, dwPersistOptions,
425         &new_items, &item_count);
426
427     if (SUCCEEDED(hr))
428     {
429         MetadataHandler_FreeItems(This);
430         This->items = new_items;
431         This->item_count = item_count;
432     }
433
434     LeaveCriticalSection(&This->lock);
435
436     return hr;
437 }
438
439 static HRESULT WINAPI MetadataHandler_SaveEx(IWICPersistStream *iface,
440     IStream *pIStream, DWORD dwPersistOptions, BOOL fClearDirty)
441 {
442     FIXME("(%p,%p,%x,%i): stub\n", iface, pIStream, dwPersistOptions, fClearDirty);
443     return E_NOTIMPL;
444 }
445
446 static const IWICPersistStreamVtbl MetadataHandler_PersistStream_Vtbl = {
447     MetadataHandler_PersistStream_QueryInterface,
448     MetadataHandler_PersistStream_AddRef,
449     MetadataHandler_PersistStream_Release,
450     MetadataHandler_GetClassID,
451     MetadataHandler_IsDirty,
452     MetadataHandler_Load,
453     MetadataHandler_Save,
454     MetadataHandler_GetSizeMax,
455     MetadataHandler_LoadEx,
456     MetadataHandler_SaveEx
457 };
458
459 HRESULT MetadataReader_Create(const MetadataHandlerVtbl *vtable, IUnknown *pUnkOuter, REFIID iid, void** ppv)
460 {
461     MetadataHandler *This;
462     HRESULT hr;
463
464     TRACE("%s\n", debugstr_guid(vtable->clsid));
465
466     *ppv = NULL;
467
468     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
469
470     This = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataHandler));
471     if (!This) return E_OUTOFMEMORY;
472
473     This->IWICMetadataWriter_iface.lpVtbl = &MetadataHandler_Vtbl;
474     This->IWICPersistStream_iface.lpVtbl = &MetadataHandler_PersistStream_Vtbl;
475     This->ref = 1;
476     This->vtable = vtable;
477     This->items = NULL;
478     This->item_count = 0;
479
480     InitializeCriticalSection(&This->lock);
481     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MetadataHandler.lock");
482
483     hr = IWICMetadataWriter_QueryInterface(&This->IWICMetadataWriter_iface, iid, ppv);
484
485     IWICMetadataWriter_Release(&This->IWICMetadataWriter_iface);
486
487     return hr;
488 }
489
490 typedef struct MetadataHandlerEnum {
491     IWICEnumMetadataItem IWICEnumMetadataItem_iface;
492     LONG ref;
493     MetadataHandler *parent;
494     DWORD index;
495 } MetadataHandlerEnum;
496
497 static inline MetadataHandlerEnum *impl_from_IWICEnumMetadataItem(IWICEnumMetadataItem *iface)
498 {
499     return CONTAINING_RECORD(iface, MetadataHandlerEnum, IWICEnumMetadataItem_iface);
500 }
501
502 static HRESULT WINAPI MetadataHandlerEnum_QueryInterface(IWICEnumMetadataItem *iface, REFIID iid,
503     void **ppv)
504 {
505     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
506     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
507
508     if (!ppv) return E_INVALIDARG;
509
510     if (IsEqualIID(&IID_IUnknown, iid) ||
511         IsEqualIID(&IID_IWICEnumMetadataItem, iid))
512     {
513         *ppv = &This->IWICEnumMetadataItem_iface;
514     }
515     else
516     {
517         *ppv = NULL;
518         return E_NOINTERFACE;
519     }
520
521     IUnknown_AddRef((IUnknown*)*ppv);
522     return S_OK;
523 }
524
525 static ULONG WINAPI MetadataHandlerEnum_AddRef(IWICEnumMetadataItem *iface)
526 {
527     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
528     ULONG ref = InterlockedIncrement(&This->ref);
529
530     TRACE("(%p) refcount=%u\n", iface, ref);
531
532     return ref;
533 }
534
535 static ULONG WINAPI MetadataHandlerEnum_Release(IWICEnumMetadataItem *iface)
536 {
537     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
538     ULONG ref = InterlockedDecrement(&This->ref);
539
540     TRACE("(%p) refcount=%u\n", iface, ref);
541
542     if (ref == 0)
543     {
544         IWICMetadataWriter_Release(&This->parent->IWICMetadataWriter_iface);
545         HeapFree(GetProcessHeap(), 0, This);
546     }
547
548     return ref;
549 }
550
551 static HRESULT WINAPI MetadataHandlerEnum_Next(IWICEnumMetadataItem *iface,
552     ULONG celt, PROPVARIANT *rgeltSchema, PROPVARIANT *rgeltId,
553     PROPVARIANT *rgeltValue, ULONG *pceltFetched)
554 {
555     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
556     ULONG new_index;
557     HRESULT hr=S_FALSE;
558     int i;
559
560     TRACE("(%p,%i)\n", iface, celt);
561
562     EnterCriticalSection(&This->parent->lock);
563
564     if (This->index >= This->parent->item_count)
565     {
566         *pceltFetched = 0;
567         LeaveCriticalSection(&This->parent->lock);
568         return S_FALSE;
569     }
570
571     new_index = min(This->parent->item_count, This->index + celt);
572     *pceltFetched = new_index - This->index;
573
574     if (rgeltSchema)
575     {
576         for (i=0; SUCCEEDED(hr) && i < *pceltFetched; i++)
577             hr = PropVariantCopy(&rgeltSchema[i], &This->parent->items[i+This->index].schema);
578     }
579
580     for (i=0; SUCCEEDED(hr) && i < *pceltFetched; i++)
581         hr = PropVariantCopy(&rgeltId[i], &This->parent->items[i+This->index].id);
582
583     if (rgeltValue)
584     {
585         for (i=0; SUCCEEDED(hr) && i < *pceltFetched; i++)
586             hr = PropVariantCopy(&rgeltValue[i], &This->parent->items[i+This->index].value);
587     }
588
589     if (SUCCEEDED(hr))
590     {
591         This->index = new_index;
592     }
593
594     LeaveCriticalSection(&This->parent->lock);
595
596     return hr;
597 }
598
599 static HRESULT WINAPI MetadataHandlerEnum_Skip(IWICEnumMetadataItem *iface,
600     ULONG celt)
601 {
602     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
603
604     EnterCriticalSection(&This->parent->lock);
605
606     This->index += celt;
607
608     LeaveCriticalSection(&This->parent->lock);
609
610     return S_OK;
611 }
612
613 static HRESULT WINAPI MetadataHandlerEnum_Reset(IWICEnumMetadataItem *iface)
614 {
615     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
616
617     EnterCriticalSection(&This->parent->lock);
618
619     This->index = 0;
620
621     LeaveCriticalSection(&This->parent->lock);
622
623     return S_OK;
624 }
625
626 static HRESULT WINAPI MetadataHandlerEnum_Clone(IWICEnumMetadataItem *iface,
627     IWICEnumMetadataItem **ppIEnumMetadataItem)
628 {
629     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
630     HRESULT hr;
631
632     EnterCriticalSection(&This->parent->lock);
633
634     hr = MetadataHandlerEnum_Create(This->parent, This->index, ppIEnumMetadataItem);
635
636     LeaveCriticalSection(&This->parent->lock);
637
638     return hr;
639 }
640
641 static const IWICEnumMetadataItemVtbl MetadataHandlerEnum_Vtbl = {
642     MetadataHandlerEnum_QueryInterface,
643     MetadataHandlerEnum_AddRef,
644     MetadataHandlerEnum_Release,
645     MetadataHandlerEnum_Next,
646     MetadataHandlerEnum_Skip,
647     MetadataHandlerEnum_Reset,
648     MetadataHandlerEnum_Clone
649 };
650
651 static HRESULT MetadataHandlerEnum_Create(MetadataHandler *parent, DWORD index,
652     IWICEnumMetadataItem **ppIEnumMetadataItem)
653 {
654     MetadataHandlerEnum *This;
655
656     if (!ppIEnumMetadataItem) return E_INVALIDARG;
657
658     *ppIEnumMetadataItem = NULL;
659
660     This = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataHandlerEnum));
661     if (!This) return E_OUTOFMEMORY;
662
663     IWICMetadataWriter_AddRef(&parent->IWICMetadataWriter_iface);
664
665     This->IWICEnumMetadataItem_iface.lpVtbl = &MetadataHandlerEnum_Vtbl;
666     This->ref = 1;
667     This->parent = parent;
668     This->index = index;
669
670     *ppIEnumMetadataItem = &This->IWICEnumMetadataItem_iface;
671
672     return S_OK;
673 }
674
675 static HRESULT LoadUnknownMetadata(IStream *input, const GUID *preferred_vendor,
676     DWORD persist_options, MetadataItem **items, DWORD *item_count)
677 {
678     HRESULT hr;
679     MetadataItem *result;
680     STATSTG stat;
681     BYTE *data;
682     ULONG bytesread;
683
684     TRACE("\n");
685
686     hr = IStream_Stat(input, &stat, STATFLAG_NONAME);
687     if (FAILED(hr))
688         return hr;
689
690     data = HeapAlloc(GetProcessHeap(), 0, stat.cbSize.QuadPart);
691     if (!data) return E_OUTOFMEMORY;
692
693     hr = IStream_Read(input, data, stat.cbSize.QuadPart, &bytesread);
694     if (bytesread != stat.cbSize.QuadPart) hr = E_FAIL;
695     if (hr != S_OK)
696     {
697         HeapFree(GetProcessHeap(), 0, data);
698         return hr;
699     }
700
701     result = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataItem));
702     if (!result)
703     {
704         HeapFree(GetProcessHeap(), 0, data);
705         return E_OUTOFMEMORY;
706     }
707
708     PropVariantInit(&result[0].schema);
709     PropVariantInit(&result[0].id);
710     PropVariantInit(&result[0].value);
711
712     result[0].value.vt = VT_BLOB;
713     result[0].value.u.blob.cbSize = bytesread;
714     result[0].value.u.blob.pBlobData = data;
715
716     *items = result;
717     *item_count = 1;
718
719     return S_OK;
720 }
721
722 static const MetadataHandlerVtbl UnknownMetadataReader_Vtbl = {
723     0,
724     &CLSID_WICUnknownMetadataReader,
725     LoadUnknownMetadata
726 };
727
728 HRESULT UnknownMetadataReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
729 {
730     return MetadataReader_Create(&UnknownMetadataReader_Vtbl, pUnkOuter, iid, ppv);
731 }
732
733 #define SWAP_USHORT(x) do { if (!native_byte_order) (x) = RtlUshortByteSwap(x); } while(0)
734 #define SWAP_ULONG(x) do { if (!native_byte_order) (x) = RtlUlongByteSwap(x); } while(0)
735 #define SWAP_ULONGLONG(x) do { if (!native_byte_order) (x) = RtlUlonglongByteSwap(x); } while(0)
736
737 struct IFD_entry
738 {
739     SHORT id;
740     SHORT type;
741     ULONG count;
742     LONG  value;
743 };
744
745 #define IFD_BYTE 1
746 #define IFD_ASCII 2
747 #define IFD_SHORT 3
748 #define IFD_LONG 4
749 #define IFD_RATIONAL 5
750 #define IFD_SBYTE 6
751 #define IFD_UNDEFINED 7
752 #define IFD_SSHORT 8
753 #define IFD_SLONG 9
754 #define IFD_SRATIONAL 10
755 #define IFD_FLOAT 11
756 #define IFD_DOUBLE 12
757 #define IFD_IFD 13
758
759 static int tag_to_vt(SHORT tag)
760 {
761     static const int tag2vt[] =
762     {
763         VT_EMPTY, /* 0 */
764         VT_UI1,   /* IFD_BYTE 1 */
765         VT_LPSTR, /* IFD_ASCII 2 */
766         VT_UI2,   /* IFD_SHORT 3 */
767         VT_UI4,   /* IFD_LONG 4 */
768         VT_UI8,   /* IFD_RATIONAL 5 */
769         VT_I1,    /* IFD_SBYTE 6 */
770         VT_BLOB,  /* IFD_UNDEFINED 7 */
771         VT_I2,    /* IFD_SSHORT 8 */
772         VT_I4,    /* IFD_SLONG 9 */
773         VT_I8,    /* IFD_SRATIONAL 10 */
774         VT_R4,    /* IFD_FLOAT 11 */
775         VT_R8,    /* IFD_DOUBLE 12 */
776         VT_BLOB,  /* IFD_IFD 13 */
777     };
778     return (tag > 0 && tag <= 13) ? tag2vt[tag] : VT_BLOB;
779 }
780
781 static HRESULT load_IFD_entry(IStream *input, const struct IFD_entry *entry,
782                               MetadataItem *item, BOOL native_byte_order)
783 {
784     ULONG count, value, i, bytesread;
785     SHORT type;
786     LARGE_INTEGER pos;
787     HRESULT hr;
788
789     item->schema.vt = VT_EMPTY;
790     item->id.vt = VT_UI2;
791     item->id.u.uiVal = entry->id;
792     SWAP_USHORT(item->id.u.uiVal);
793
794     count = entry->count;
795     SWAP_ULONG(count);
796     type = entry->type;
797     SWAP_USHORT(type);
798     item->value.vt = tag_to_vt(type);
799     value = entry->value;
800     SWAP_ULONG(value);
801
802     switch (type)
803     {
804      case IFD_BYTE:
805      case IFD_SBYTE:
806         if (!count) count = 1;
807
808         if (count <= 4)
809         {
810             const BYTE *data = (const BYTE *)&entry->value;
811
812             if (count == 1)
813                 item->value.u.bVal = data[0];
814             else
815             {
816                 item->value.vt |= VT_VECTOR;
817                 item->value.u.caub.cElems = count;
818                 item->value.u.caub.pElems = HeapAlloc(GetProcessHeap(), 0, count);
819                 memcpy(item->value.u.caub.pElems, data, count);
820             }
821             break;
822         }
823
824         item->value.vt |= VT_VECTOR;
825         item->value.u.caub.cElems = count;
826         item->value.u.caub.pElems = HeapAlloc(GetProcessHeap(), 0, count);
827         if (!item->value.u.caub.pElems) return E_OUTOFMEMORY;
828
829         pos.QuadPart = value;
830         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
831         if (FAILED(hr))
832         {
833             HeapFree(GetProcessHeap(), 0, item->value.u.caub.pElems);
834             return hr;
835         }
836         hr = IStream_Read(input, item->value.u.caub.pElems, count, &bytesread);
837         if (bytesread != count) hr = E_FAIL;
838         if (hr != S_OK)
839         {
840             HeapFree(GetProcessHeap(), 0, item->value.u.caub.pElems);
841             return hr;
842         }
843         break;
844     case IFD_SHORT:
845     case IFD_SSHORT:
846         if (!count) count = 1;
847
848         if (count <= 2)
849         {
850             const SHORT *data = (const SHORT *)&entry->value;
851
852             if (count == 1)
853             {
854                 item->value.u.uiVal = data[0];
855                 SWAP_USHORT(item->value.u.uiVal);
856             }
857             else
858             {
859                 item->value.vt |= VT_VECTOR;
860                 item->value.u.caui.cElems = count;
861                 item->value.u.caui.pElems = HeapAlloc(GetProcessHeap(), 0, count * 2);
862                 memcpy(item->value.u.caui.pElems, data, count * 2);
863                 for (i = 0; i < count; i++)
864                     SWAP_USHORT(item->value.u.caui.pElems[i]);
865             }
866             break;
867         }
868
869         item->value.vt |= VT_VECTOR;
870         item->value.u.caui.cElems = count;
871         item->value.u.caui.pElems = HeapAlloc(GetProcessHeap(), 0, count * 2);
872         if (!item->value.u.caui.pElems) return E_OUTOFMEMORY;
873
874         pos.QuadPart = value;
875         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
876         if (FAILED(hr))
877         {
878             HeapFree(GetProcessHeap(), 0, item->value.u.caui.pElems);
879             return hr;
880         }
881         hr = IStream_Read(input, item->value.u.caui.pElems, count * 2, &bytesread);
882         if (bytesread != count * 2) hr = E_FAIL;
883         if (hr != S_OK)
884         {
885             HeapFree(GetProcessHeap(), 0, item->value.u.caui.pElems);
886             return hr;
887         }
888         for (i = 0; i < count; i++)
889             SWAP_USHORT(item->value.u.caui.pElems[i]);
890         break;
891     case IFD_LONG:
892     case IFD_SLONG:
893     case IFD_FLOAT:
894         if (!count) count = 1;
895
896         if (count == 1)
897         {
898             item->value.u.ulVal = value;
899             break;
900         }
901
902         item->value.vt |= VT_VECTOR;
903         item->value.u.caul.cElems = count;
904         item->value.u.caul.pElems = HeapAlloc(GetProcessHeap(), 0, count * 4);
905         if (!item->value.u.caul.pElems) return E_OUTOFMEMORY;
906
907         pos.QuadPart = value;
908         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
909         if (FAILED(hr))
910         {
911             HeapFree(GetProcessHeap(), 0, item->value.u.caul.pElems);
912             return hr;
913         }
914         hr = IStream_Read(input, item->value.u.caul.pElems, count * 4, &bytesread);
915         if (bytesread != count * 4) hr = E_FAIL;
916         if (hr != S_OK)
917         {
918             HeapFree(GetProcessHeap(), 0, item->value.u.caul.pElems);
919             return hr;
920         }
921         for (i = 0; i < count; i++)
922             SWAP_ULONG(item->value.u.caul.pElems[i]);
923         break;
924     case IFD_RATIONAL:
925     case IFD_SRATIONAL:
926     case IFD_DOUBLE:
927         if (!count)
928         {
929             FIXME("IFD field type %d, count 0\n", type);
930             item->value.vt = VT_EMPTY;
931             break;
932         }
933
934         if (count == 1)
935         {
936             ULONGLONG ull;
937
938             pos.QuadPart = value;
939             hr = IStream_Seek(input, pos, SEEK_SET, NULL);
940             if (FAILED(hr)) return hr;
941
942             hr = IStream_Read(input, &ull, sizeof(ull), &bytesread);
943             if (bytesread != sizeof(ull)) hr = E_FAIL;
944             if (hr != S_OK) return hr;
945
946             item->value.u.uhVal.QuadPart = ull;
947
948             if (type == IFD_DOUBLE)
949                 SWAP_ULONGLONG(item->value.u.uhVal.QuadPart);
950             else
951             {
952                 SWAP_ULONG(item->value.u.uhVal.u.LowPart);
953                 SWAP_ULONG(item->value.u.uhVal.u.HighPart);
954             }
955             break;
956         }
957         else
958         {
959             item->value.vt |= VT_VECTOR;
960             item->value.u.cauh.cElems = count;
961             item->value.u.cauh.pElems = HeapAlloc(GetProcessHeap(), 0, count * 8);
962             if (!item->value.u.cauh.pElems) return E_OUTOFMEMORY;
963
964             pos.QuadPart = value;
965             hr = IStream_Seek(input, pos, SEEK_SET, NULL);
966             if (FAILED(hr))
967             {
968                 HeapFree(GetProcessHeap(), 0, item->value.u.cauh.pElems);
969                 return hr;
970             }
971             hr = IStream_Read(input, item->value.u.cauh.pElems, count * 8, &bytesread);
972             if (bytesread != count * 8) hr = E_FAIL;
973             if (hr != S_OK)
974             {
975                 HeapFree(GetProcessHeap(), 0, item->value.u.cauh.pElems);
976                 return hr;
977             }
978             for (i = 0; i < count; i++)
979             {
980                 if (type == IFD_DOUBLE)
981                     SWAP_ULONGLONG(item->value.u.cauh.pElems[i].QuadPart);
982                 else
983                 {
984                     SWAP_ULONG(item->value.u.cauh.pElems[i].u.LowPart);
985                     SWAP_ULONG(item->value.u.cauh.pElems[i].u.HighPart);
986                 }
987             }
988         }
989         break;
990     case IFD_ASCII:
991         item->value.u.pszVal = HeapAlloc(GetProcessHeap(), 0, count + 1);
992         if (!item->value.u.pszVal) return E_OUTOFMEMORY;
993
994         if (count <= 4)
995         {
996             const char *data = (const char *)&entry->value;
997             memcpy(item->value.u.pszVal, data, count);
998             item->value.u.pszVal[count] = 0;
999             break;
1000         }
1001
1002         pos.QuadPart = value;
1003         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
1004         if (FAILED(hr))
1005         {
1006             HeapFree(GetProcessHeap(), 0, item->value.u.pszVal);
1007             return hr;
1008         }
1009         hr = IStream_Read(input, item->value.u.pszVal, count, &bytesread);
1010         if (bytesread != count) hr = E_FAIL;
1011         if (hr != S_OK)
1012         {
1013             HeapFree(GetProcessHeap(), 0, item->value.u.pszVal);
1014             return hr;
1015         }
1016         item->value.u.pszVal[count] = 0;
1017         break;
1018     case IFD_UNDEFINED:
1019         if (!count)
1020         {
1021             FIXME("IFD field type %d, count 0\n", type);
1022             item->value.vt = VT_EMPTY;
1023             break;
1024         }
1025
1026         item->value.u.blob.pBlobData = HeapAlloc(GetProcessHeap(), 0, count);
1027         if (!item->value.u.blob.pBlobData) return E_OUTOFMEMORY;
1028
1029         item->value.u.blob.cbSize = count;
1030
1031         if (count <= 4)
1032         {
1033             const char *data = (const char *)&entry->value;
1034             memcpy(item->value.u.blob.pBlobData, data, count);
1035             break;
1036         }
1037
1038         pos.QuadPart = value;
1039         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
1040         if (FAILED(hr))
1041         {
1042             HeapFree(GetProcessHeap(), 0, item->value.u.blob.pBlobData);
1043             return hr;
1044         }
1045         hr = IStream_Read(input, item->value.u.blob.pBlobData, count, &bytesread);
1046         if (bytesread != count) hr = E_FAIL;
1047         if (hr != S_OK)
1048         {
1049             HeapFree(GetProcessHeap(), 0, item->value.u.blob.pBlobData);
1050             return hr;
1051         }
1052         break;
1053     default:
1054         FIXME("loading field of type %d, count %u is not implemented\n", type, count);
1055         break;
1056     }
1057     return S_OK;
1058 }
1059
1060 static HRESULT LoadIfdMetadata(IStream *input, const GUID *preferred_vendor,
1061     DWORD persist_options, MetadataItem **items, DWORD *item_count)
1062 {
1063     HRESULT hr;
1064     MetadataItem *result;
1065     USHORT count, i;
1066     struct IFD_entry *entry;
1067     BOOL native_byte_order = TRUE;
1068     ULONG bytesread;
1069
1070     TRACE("\n");
1071
1072 #ifdef WORDS_BIGENDIAN
1073     if (persist_options & WICPersistOptionsLittleEndian)
1074 #else
1075     if (persist_options & WICPersistOptionsBigEndian)
1076 #endif
1077         native_byte_order = FALSE;
1078
1079     hr = IStream_Read(input, &count, sizeof(count), &bytesread);
1080     if (bytesread != sizeof(count)) hr = E_FAIL;
1081     if (hr != S_OK) return hr;
1082
1083     SWAP_USHORT(count);
1084
1085     entry = HeapAlloc(GetProcessHeap(), 0, count * sizeof(*entry));
1086     if (!entry) return E_OUTOFMEMORY;
1087
1088     hr = IStream_Read(input, entry, count * sizeof(*entry), &bytesread);
1089     if (bytesread != count * sizeof(*entry)) hr = E_FAIL;
1090     if (hr != S_OK)
1091     {
1092         HeapFree(GetProcessHeap(), 0, entry);
1093         return hr;
1094     }
1095
1096     /* limit number of IFDs to 4096 to avoid infinite loop */
1097     for (i = 0; i < 4096; i++)
1098     {
1099         ULONG next_ifd_offset;
1100         LARGE_INTEGER pos;
1101         USHORT next_ifd_count;
1102
1103         hr = IStream_Read(input, &next_ifd_offset, sizeof(next_ifd_offset), &bytesread);
1104         if (bytesread != sizeof(next_ifd_offset)) hr = E_FAIL;
1105         if (hr != S_OK) break;
1106
1107         SWAP_ULONG(next_ifd_offset);
1108         if (!next_ifd_offset) break;
1109
1110         pos.QuadPart = next_ifd_offset;
1111         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
1112         if (FAILED(hr)) break;
1113
1114         hr = IStream_Read(input, &next_ifd_count, sizeof(next_ifd_count), &bytesread);
1115         if (bytesread != sizeof(next_ifd_count)) hr = E_FAIL;
1116         if (hr != S_OK) break;
1117
1118         SWAP_USHORT(next_ifd_count);
1119
1120         pos.QuadPart = next_ifd_count * sizeof(*entry);
1121         hr = IStream_Seek(input, pos, SEEK_CUR, NULL);
1122         if (FAILED(hr)) break;
1123     }
1124
1125     if (hr != S_OK || i == 4096)
1126     {
1127         HeapFree(GetProcessHeap(), 0, entry);
1128         return WINCODEC_ERR_BADMETADATAHEADER;
1129     }
1130
1131     result = HeapAlloc(GetProcessHeap(), 0, count * sizeof(*result));
1132     if (!result)
1133     {
1134         HeapFree(GetProcessHeap(), 0, entry);
1135         return E_OUTOFMEMORY;
1136     }
1137
1138     for (i = 0; i < count; i++)
1139     {
1140         hr = load_IFD_entry(input, &entry[i], &result[i], native_byte_order);
1141         if (FAILED(hr))
1142         {
1143             HeapFree(GetProcessHeap(), 0, entry);
1144             HeapFree(GetProcessHeap(), 0, result);
1145             return hr;
1146         }
1147     }
1148
1149     HeapFree(GetProcessHeap(), 0, entry);
1150
1151     *items = result;
1152     *item_count = count;
1153
1154     return S_OK;
1155 }
1156
1157 static const MetadataHandlerVtbl IfdMetadataReader_Vtbl = {
1158     0,
1159     &CLSID_WICIfdMetadataReader,
1160     LoadIfdMetadata
1161 };
1162
1163 HRESULT IfdMetadataReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppv)
1164 {
1165     return MetadataReader_Create(&IfdMetadataReader_Vtbl, pUnkOuter, iid, ppv);
1166 }