d3dx9_36: Add support for DIB file in D3DXGetImageInfoFromFileInMemory.
[wine] / dlls / windowscodecs / gifformat.c
1 /*
2  * Copyright 2009 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
24 #define COBJMACROS
25 #define NONAMELESSUNION
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "objbase.h"
30 #include "wincodec.h"
31 #include "wincodecsdk.h"
32
33 #include "ungif.h"
34
35 #include "wincodecs_private.h"
36
37 #include "wine/debug.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
40
41 static LPWSTR strdupAtoW(const char *src)
42 {
43     int len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
44     LPWSTR dst = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
45     if (dst) MultiByteToWideChar(CP_ACP, 0, src, -1, dst, len);
46     return dst;
47 }
48
49 static HRESULT load_LSD_metadata(IStream *stream, const GUID *vendor, DWORD options,
50                                  MetadataItem **items, DWORD *count)
51 {
52 #include "pshpack1.h"
53     struct logical_screen_descriptor
54     {
55         char signature[6];
56         USHORT width;
57         USHORT height;
58         BYTE packed;
59         /* global_color_table_flag : 1;
60          * color_resolution : 3;
61          * sort_flag : 1;
62          * global_color_table_size : 3;
63          */
64         BYTE background_color_index;
65         BYTE pixel_aspect_ratio;
66     } lsd_data;
67 #include "poppack.h"
68     HRESULT hr;
69     ULONG bytesread, i;
70     MetadataItem *result;
71
72     *items = NULL;
73     *count = 0;
74
75     hr = IStream_Read(stream, &lsd_data, sizeof(lsd_data), &bytesread);
76     if (FAILED(hr) || bytesread != sizeof(lsd_data)) return S_OK;
77
78     result = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataItem) * 9);
79     if (!result) return E_OUTOFMEMORY;
80
81     for (i = 0; i < 9; i++)
82     {
83         PropVariantInit(&result[i].schema);
84         PropVariantInit(&result[i].id);
85         PropVariantInit(&result[i].value);
86     }
87
88     result[0].id.vt = VT_LPWSTR;
89     result[0].id.u.pwszVal = strdupAtoW("Signature");
90     result[0].value.vt = VT_UI1|VT_VECTOR;
91     result[0].value.u.caub.cElems = sizeof(lsd_data.signature);
92     result[0].value.u.caub.pElems = HeapAlloc(GetProcessHeap(), 0, sizeof(lsd_data.signature));
93     memcpy(result[0].value.u.caub.pElems, lsd_data.signature, sizeof(lsd_data.signature));
94
95     result[1].id.vt = VT_LPWSTR;
96     result[1].id.u.pwszVal = strdupAtoW("Width");
97     result[1].value.vt = VT_UI2;
98     result[1].value.u.uiVal = lsd_data.width;
99
100     result[2].id.vt = VT_LPWSTR;
101     result[2].id.u.pwszVal = strdupAtoW("Height");
102     result[2].value.vt = VT_UI2;
103     result[2].value.u.uiVal = lsd_data.height;
104
105     result[3].id.vt = VT_LPWSTR;
106     result[3].id.u.pwszVal = strdupAtoW("GlobalColorTableFlag");
107     result[3].value.vt = VT_BOOL;
108     result[3].value.u.boolVal = (lsd_data.packed >> 7) & 1;
109
110     result[4].id.vt = VT_LPWSTR;
111     result[4].id.u.pwszVal = strdupAtoW("ColorResolution");
112     result[4].value.vt = VT_UI1;
113     result[4].value.u.bVal = (lsd_data.packed >> 4) & 7;
114
115     result[5].id.vt = VT_LPWSTR;
116     result[5].id.u.pwszVal = strdupAtoW("SortFlag");
117     result[5].value.vt = VT_BOOL;
118     result[5].value.u.boolVal = (lsd_data.packed >> 3) & 1;
119
120     result[6].id.vt = VT_LPWSTR;
121     result[6].id.u.pwszVal = strdupAtoW("GlobalColorTableSize");
122     result[6].value.vt = VT_UI1;
123     result[6].value.u.bVal = lsd_data.packed & 7;
124
125     result[7].id.vt = VT_LPWSTR;
126     result[7].id.u.pwszVal = strdupAtoW("BackgroundColorIndex");
127     result[7].value.vt = VT_UI1;
128     result[7].value.u.bVal = lsd_data.background_color_index;
129
130     result[8].id.vt = VT_LPWSTR;
131     result[8].id.u.pwszVal = strdupAtoW("PixelAspectRatio");
132     result[8].value.vt = VT_UI1;
133     result[8].value.u.bVal = lsd_data.pixel_aspect_ratio;
134
135     *items = result;
136     *count = 9;
137
138     return S_OK;
139 }
140
141 static const MetadataHandlerVtbl LSDReader_Vtbl = {
142     0,
143     &CLSID_WICLSDMetadataReader,
144     load_LSD_metadata
145 };
146
147 HRESULT LSDReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppv)
148 {
149     return MetadataReader_Create(&LSDReader_Vtbl, pUnkOuter, iid, ppv);
150 }
151
152 #include "pshpack1.h"
153 struct image_descriptor
154 {
155     USHORT left;
156     USHORT top;
157     USHORT width;
158     USHORT height;
159     BYTE packed;
160     /* local_color_table_flag : 1;
161      * interlace_flag : 1;
162      * sort_flag : 1;
163      * reserved : 2;
164      * local_color_table_size : 3;
165      */
166 };
167 #include "poppack.h"
168
169 static HRESULT load_IMD_metadata(IStream *stream, const GUID *vendor, DWORD options,
170                                  MetadataItem **items, DWORD *count)
171 {
172     struct image_descriptor imd_data;
173     HRESULT hr;
174     ULONG bytesread, i;
175     MetadataItem *result;
176
177     *items = NULL;
178     *count = 0;
179
180     hr = IStream_Read(stream, &imd_data, sizeof(imd_data), &bytesread);
181     if (FAILED(hr) || bytesread != sizeof(imd_data)) return S_OK;
182
183     result = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataItem) * 8);
184     if (!result) return E_OUTOFMEMORY;
185
186     for (i = 0; i < 8; i++)
187     {
188         PropVariantInit(&result[i].schema);
189         PropVariantInit(&result[i].id);
190         PropVariantInit(&result[i].value);
191     }
192
193     result[0].id.vt = VT_LPWSTR;
194     result[0].id.u.pwszVal = strdupAtoW("Left");
195     result[0].value.vt = VT_UI2;
196     result[0].value.u.uiVal = imd_data.left;
197
198     result[1].id.vt = VT_LPWSTR;
199     result[1].id.u.pwszVal = strdupAtoW("Top");
200     result[1].value.vt = VT_UI2;
201     result[1].value.u.uiVal = imd_data.top;
202
203     result[2].id.vt = VT_LPWSTR;
204     result[2].id.u.pwszVal = strdupAtoW("Width");
205     result[2].value.vt = VT_UI2;
206     result[2].value.u.uiVal = imd_data.width;
207
208     result[3].id.vt = VT_LPWSTR;
209     result[3].id.u.pwszVal = strdupAtoW("Height");
210     result[3].value.vt = VT_UI2;
211     result[3].value.u.uiVal = imd_data.height;
212
213     result[4].id.vt = VT_LPWSTR;
214     result[4].id.u.pwszVal = strdupAtoW("LocalColorTableFlag");
215     result[4].value.vt = VT_BOOL;
216     result[4].value.u.boolVal = (imd_data.packed >> 7) & 1;
217
218     result[5].id.vt = VT_LPWSTR;
219     result[5].id.u.pwszVal = strdupAtoW("InterlaceFlag");
220     result[5].value.vt = VT_BOOL;
221     result[5].value.u.boolVal = (imd_data.packed >> 6) & 1;
222
223     result[6].id.vt = VT_LPWSTR;
224     result[6].id.u.pwszVal = strdupAtoW("SortFlag");
225     result[6].value.vt = VT_BOOL;
226     result[6].value.u.boolVal = (imd_data.packed >> 5) & 1;
227
228     result[7].id.vt = VT_LPWSTR;
229     result[7].id.u.pwszVal = strdupAtoW("LocalColorTableSize");
230     result[7].value.vt = VT_UI1;
231     result[7].value.u.bVal = imd_data.packed & 7;
232
233     *items = result;
234     *count = 8;
235
236     return S_OK;
237 }
238
239 static const MetadataHandlerVtbl IMDReader_Vtbl = {
240     0,
241     &CLSID_WICIMDMetadataReader,
242     load_IMD_metadata
243 };
244
245 HRESULT IMDReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppv)
246 {
247     return MetadataReader_Create(&IMDReader_Vtbl, pUnkOuter, iid, ppv);
248 }
249
250 static HRESULT load_GCE_metadata(IStream *stream, const GUID *vendor, DWORD options,
251                                  MetadataItem **items, DWORD *count)
252 {
253 #include "pshpack1.h"
254     struct graphic_control_extenstion
255     {
256         BYTE packed;
257         /* reservred: 3;
258          * disposal : 3;
259          * user_input_flag : 1;
260          * transparency_flag : 1;
261          */
262          USHORT delay;
263          BYTE transparent_color_index;
264     } gce_data;
265 #include "poppack.h"
266     HRESULT hr;
267     ULONG bytesread, i;
268     MetadataItem *result;
269
270     *items = NULL;
271     *count = 0;
272
273     hr = IStream_Read(stream, &gce_data, sizeof(gce_data), &bytesread);
274     if (FAILED(hr) || bytesread != sizeof(gce_data)) return S_OK;
275
276     result = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataItem) * 5);
277     if (!result) return E_OUTOFMEMORY;
278
279     for (i = 0; i < 5; i++)
280     {
281         PropVariantInit(&result[i].schema);
282         PropVariantInit(&result[i].id);
283         PropVariantInit(&result[i].value);
284     }
285
286     result[0].id.vt = VT_LPWSTR;
287     result[0].id.u.pwszVal = strdupAtoW("Disposal");
288     result[0].value.vt = VT_UI1;
289     result[0].value.u.bVal = (gce_data.packed >> 2) & 7;
290
291     result[1].id.vt = VT_LPWSTR;
292     result[1].id.u.pwszVal = strdupAtoW("UserInputFlag");
293     result[1].value.vt = VT_BOOL;
294     result[1].value.u.boolVal = (gce_data.packed >> 1) & 1;
295
296     result[2].id.vt = VT_LPWSTR;
297     result[2].id.u.pwszVal = strdupAtoW("TransparencyFlag");
298     result[2].value.vt = VT_BOOL;
299     result[2].value.u.boolVal = gce_data.packed & 1;
300
301     result[3].id.vt = VT_LPWSTR;
302     result[3].id.u.pwszVal = strdupAtoW("Delay");
303     result[3].value.vt = VT_UI2;
304     result[3].value.u.uiVal = gce_data.delay;
305
306     result[4].id.vt = VT_LPWSTR;
307     result[4].id.u.pwszVal = strdupAtoW("TransparentColorIndex");
308     result[4].value.vt = VT_UI1;
309     result[4].value.u.bVal = gce_data.transparent_color_index;
310
311     *items = result;
312     *count = 5;
313
314     return S_OK;
315 }
316
317 static const MetadataHandlerVtbl GCEReader_Vtbl = {
318     0,
319     &CLSID_WICGCEMetadataReader,
320     load_GCE_metadata
321 };
322
323 HRESULT GCEReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppv)
324 {
325     return MetadataReader_Create(&GCEReader_Vtbl, pUnkOuter, iid, ppv);
326 }
327
328 static HRESULT load_APE_metadata(IStream *stream, const GUID *vendor, DWORD options,
329                                  MetadataItem **items, DWORD *count)
330 {
331 #include "pshpack1.h"
332     struct application_extenstion
333     {
334         BYTE extension_introducer;
335         BYTE extension_label;
336         BYTE block_size;
337         BYTE application[11];
338     } ape_data;
339 #include "poppack.h"
340     HRESULT hr;
341     ULONG bytesread, data_size, i;
342     MetadataItem *result;
343     BYTE subblock_size;
344     BYTE *data;
345
346     *items = NULL;
347     *count = 0;
348
349     hr = IStream_Read(stream, &ape_data, sizeof(ape_data), &bytesread);
350     if (FAILED(hr) || bytesread != sizeof(ape_data)) return S_OK;
351     if (ape_data.extension_introducer != 0x21 ||
352         ape_data.extension_label != APPLICATION_EXT_FUNC_CODE ||
353         ape_data.block_size != 11)
354         return S_OK;
355
356     data = NULL;
357     data_size = 0;
358
359     for (;;)
360     {
361         hr = IStream_Read(stream, &subblock_size, sizeof(subblock_size), &bytesread);
362         if (FAILED(hr) || bytesread != sizeof(subblock_size))
363         {
364             HeapFree(GetProcessHeap(), 0, data);
365             return S_OK;
366         }
367         if (!subblock_size) break;
368
369         if (!data)
370             data = HeapAlloc(GetProcessHeap(), 0, subblock_size + 1);
371         else
372         {
373             BYTE *new_data = HeapReAlloc(GetProcessHeap(), 0, data, data_size + subblock_size + 1);
374             if (!new_data)
375             {
376                 HeapFree(GetProcessHeap(), 0, data);
377                 return S_OK;
378             }
379             data = new_data;
380         }
381         data[data_size] = subblock_size;
382         hr = IStream_Read(stream, data + data_size + 1, subblock_size, &bytesread);
383         if (FAILED(hr) || bytesread != subblock_size)
384         {
385             HeapFree(GetProcessHeap(), 0, data);
386             return S_OK;
387         }
388         data_size += subblock_size + 1;
389     }
390
391     result = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataItem) * 2);
392     if (!result)
393     {
394         HeapFree(GetProcessHeap(), 0, data);
395         return E_OUTOFMEMORY;
396     }
397
398     for (i = 0; i < 2; i++)
399     {
400         PropVariantInit(&result[i].schema);
401         PropVariantInit(&result[i].id);
402         PropVariantInit(&result[i].value);
403     }
404
405     result[0].id.vt = VT_LPWSTR;
406     result[0].id.u.pwszVal = strdupAtoW("Application");
407     result[0].value.vt = VT_UI1|VT_VECTOR;
408     result[0].value.u.caub.cElems = sizeof(ape_data.application);
409     result[0].value.u.caub.pElems = HeapAlloc(GetProcessHeap(), 0, sizeof(ape_data.application));
410     memcpy(result[0].value.u.caub.pElems, ape_data.application, sizeof(ape_data.application));
411
412     result[1].id.vt = VT_LPWSTR;
413     result[1].id.u.pwszVal = strdupAtoW("Data");
414     result[1].value.vt = VT_UI1|VT_VECTOR;
415     result[1].value.u.caub.cElems = data_size;
416     result[1].value.u.caub.pElems = data;
417
418     *items = result;
419     *count = 2;
420
421     return S_OK;
422 }
423
424 static const MetadataHandlerVtbl APEReader_Vtbl = {
425     0,
426     &CLSID_WICAPEMetadataReader,
427     load_APE_metadata
428 };
429
430 HRESULT APEReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppv)
431 {
432     return MetadataReader_Create(&APEReader_Vtbl, pUnkOuter, iid, ppv);
433 }
434
435 static HRESULT load_GifComment_metadata(IStream *stream, const GUID *vendor, DWORD options,
436                                         MetadataItem **items, DWORD *count)
437 {
438 #include "pshpack1.h"
439     struct gif_extenstion
440     {
441         BYTE extension_introducer;
442         BYTE extension_label;
443     } ext_data;
444 #include "poppack.h"
445     HRESULT hr;
446     ULONG bytesread, data_size;
447     MetadataItem *result;
448     BYTE subblock_size;
449     char *data;
450
451     *items = NULL;
452     *count = 0;
453
454     hr = IStream_Read(stream, &ext_data, sizeof(ext_data), &bytesread);
455     if (FAILED(hr) || bytesread != sizeof(ext_data)) return S_OK;
456     if (ext_data.extension_introducer != 0x21 ||
457         ext_data.extension_label != COMMENT_EXT_FUNC_CODE)
458         return S_OK;
459
460     data = NULL;
461     data_size = 0;
462
463     for (;;)
464     {
465         hr = IStream_Read(stream, &subblock_size, sizeof(subblock_size), &bytesread);
466         if (FAILED(hr) || bytesread != sizeof(subblock_size))
467         {
468             HeapFree(GetProcessHeap(), 0, data);
469             return S_OK;
470         }
471         if (!subblock_size) break;
472
473         if (!data)
474             data = HeapAlloc(GetProcessHeap(), 0, subblock_size + 1);
475         else
476         {
477             char *new_data = HeapReAlloc(GetProcessHeap(), 0, data, data_size + subblock_size + 1);
478             if (!new_data)
479             {
480                 HeapFree(GetProcessHeap(), 0, data);
481                 return S_OK;
482             }
483             data = new_data;
484         }
485         hr = IStream_Read(stream, data + data_size, subblock_size, &bytesread);
486         if (FAILED(hr) || bytesread != subblock_size)
487         {
488             HeapFree(GetProcessHeap(), 0, data);
489             return S_OK;
490         }
491         data_size += subblock_size;
492     }
493
494     data[data_size] = 0;
495
496     result = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataItem));
497     if (!result)
498     {
499         HeapFree(GetProcessHeap(), 0, data);
500         return E_OUTOFMEMORY;
501     }
502
503     PropVariantInit(&result->schema);
504     PropVariantInit(&result->id);
505     PropVariantInit(&result->value);
506
507     result->id.vt = VT_LPWSTR;
508     result->id.u.pwszVal = strdupAtoW("TextEntry");
509     result->value.vt = VT_LPSTR;
510     result->value.u.pszVal = data;
511
512     *items = result;
513     *count = 1;
514
515     return S_OK;
516 }
517
518 static const MetadataHandlerVtbl GifCommentReader_Vtbl = {
519     0,
520     &CLSID_WICGifCommentMetadataReader,
521     load_GifComment_metadata
522 };
523
524 HRESULT GifCommentReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppv)
525 {
526     return MetadataReader_Create(&GifCommentReader_Vtbl, pUnkOuter, iid, ppv);
527 }
528
529 static IStream *create_stream(const void *data, int data_size)
530 {
531     HRESULT hr;
532     IStream *stream;
533     HGLOBAL hdata;
534     void *locked_data;
535
536     hdata = GlobalAlloc(GMEM_MOVEABLE, data_size);
537     if (!hdata) return NULL;
538
539     locked_data = GlobalLock(hdata);
540     memcpy(locked_data, data, data_size);
541     GlobalUnlock(hdata);
542
543     hr = CreateStreamOnHGlobal(hdata, TRUE, &stream);
544     return FAILED(hr) ? NULL : stream;
545 }
546
547 static HRESULT create_metadata_reader(const void *data, int data_size,
548                                       const CLSID *clsid, IWICMetadataReader **reader)
549 {
550     HRESULT hr;
551     IWICMetadataReader *metadata_reader;
552     IWICPersistStream *persist;
553     IStream *stream;
554
555     /* FIXME: Use IWICComponentFactory_CreateMetadataReader once it's implemented */
556
557     hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER,
558                           &IID_IWICMetadataReader, (void **)&metadata_reader);
559     if (FAILED(hr)) return hr;
560
561     hr = IWICMetadataReader_QueryInterface(metadata_reader, &IID_IWICPersistStream, (void **)&persist);
562     if (FAILED(hr))
563     {
564         IWICMetadataReader_Release(metadata_reader);
565         return hr;
566     }
567
568     stream = create_stream(data, data_size);
569     IWICPersistStream_LoadEx(persist, stream, NULL, WICPersistOptionsDefault);
570     IStream_Release(stream);
571
572     IWICPersistStream_Release(persist);
573
574     *reader = metadata_reader;
575     return S_OK;
576 }
577
578 typedef struct {
579     IWICBitmapDecoder IWICBitmapDecoder_iface;
580     IWICMetadataBlockReader IWICMetadataBlockReader_iface;
581     BYTE LSD_data[13]; /* Logical Screen Descriptor */
582     LONG ref;
583     BOOL initialized;
584     GifFileType *gif;
585     UINT current_frame;
586     CRITICAL_SECTION lock;
587 } GifDecoder;
588
589 typedef struct {
590     IWICBitmapFrameDecode IWICBitmapFrameDecode_iface;
591     IWICMetadataBlockReader IWICMetadataBlockReader_iface;
592     LONG ref;
593     SavedImage *frame;
594     GifDecoder *parent;
595 } GifFrameDecode;
596
597 static inline GifDecoder *impl_from_IWICBitmapDecoder(IWICBitmapDecoder *iface)
598 {
599     return CONTAINING_RECORD(iface, GifDecoder, IWICBitmapDecoder_iface);
600 }
601
602 static inline GifDecoder *impl_from_IWICMetadataBlockReader(IWICMetadataBlockReader *iface)
603 {
604     return CONTAINING_RECORD(iface, GifDecoder, IWICMetadataBlockReader_iface);
605 }
606
607 static inline GifFrameDecode *impl_from_IWICBitmapFrameDecode(IWICBitmapFrameDecode *iface)
608 {
609     return CONTAINING_RECORD(iface, GifFrameDecode, IWICBitmapFrameDecode_iface);
610 }
611
612 static inline GifFrameDecode *frame_from_IWICMetadataBlockReader(IWICMetadataBlockReader *iface)
613 {
614     return CONTAINING_RECORD(iface, GifFrameDecode, IWICMetadataBlockReader_iface);
615 }
616
617 static HRESULT WINAPI GifFrameDecode_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
618     void **ppv)
619 {
620     GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
621     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
622
623     if (!ppv) return E_INVALIDARG;
624
625     if (IsEqualIID(&IID_IUnknown, iid) ||
626         IsEqualIID(&IID_IWICBitmapSource, iid) ||
627         IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
628     {
629         *ppv = &This->IWICBitmapFrameDecode_iface;
630     }
631     else if (IsEqualIID(&IID_IWICMetadataBlockReader, iid))
632     {
633         *ppv = &This->IWICMetadataBlockReader_iface;
634     }
635     else
636     {
637         *ppv = NULL;
638         return E_NOINTERFACE;
639     }
640
641     IUnknown_AddRef((IUnknown*)*ppv);
642     return S_OK;
643 }
644
645 static ULONG WINAPI GifFrameDecode_AddRef(IWICBitmapFrameDecode *iface)
646 {
647     GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
648     ULONG ref = InterlockedIncrement(&This->ref);
649
650     TRACE("(%p) refcount=%u\n", iface, ref);
651
652     return ref;
653 }
654
655 static ULONG WINAPI GifFrameDecode_Release(IWICBitmapFrameDecode *iface)
656 {
657     GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
658     ULONG ref = InterlockedDecrement(&This->ref);
659
660     TRACE("(%p) refcount=%u\n", iface, ref);
661
662     if (ref == 0)
663     {
664         IWICBitmapDecoder_Release(&This->parent->IWICBitmapDecoder_iface);
665         HeapFree(GetProcessHeap(), 0, This);
666     }
667
668     return ref;
669 }
670
671 static HRESULT WINAPI GifFrameDecode_GetSize(IWICBitmapFrameDecode *iface,
672     UINT *puiWidth, UINT *puiHeight)
673 {
674     GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
675     TRACE("(%p,%p,%p)\n", iface, puiWidth, puiHeight);
676
677     *puiWidth = This->frame->ImageDesc.Width;
678     *puiHeight = This->frame->ImageDesc.Height;
679
680     return S_OK;
681 }
682
683 static HRESULT WINAPI GifFrameDecode_GetPixelFormat(IWICBitmapFrameDecode *iface,
684     WICPixelFormatGUID *pPixelFormat)
685 {
686     memcpy(pPixelFormat, &GUID_WICPixelFormat8bppIndexed, sizeof(GUID));
687
688     return S_OK;
689 }
690
691 static HRESULT WINAPI GifFrameDecode_GetResolution(IWICBitmapFrameDecode *iface,
692     double *pDpiX, double *pDpiY)
693 {
694     GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
695     const GifWord aspect_word = This->parent->gif->SAspectRatio;
696     const double aspect = (aspect_word > 0) ? ((aspect_word + 15.0) / 64.0) : 1.0;
697     TRACE("(%p,%p,%p)\n", iface, pDpiX, pDpiY);
698
699     *pDpiX = 96.0 / aspect;
700     *pDpiY = 96.0;
701
702     return S_OK;
703 }
704
705 static HRESULT WINAPI GifFrameDecode_CopyPalette(IWICBitmapFrameDecode *iface,
706     IWICPalette *pIPalette)
707 {
708     GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
709     WICColor colors[256];
710     ColorMapObject *cm = This->frame->ImageDesc.ColorMap;
711     int i, trans;
712     ExtensionBlock *eb;
713     TRACE("(%p,%p)\n", iface, pIPalette);
714
715     if (!cm) cm = This->parent->gif->SColorMap;
716
717     if (cm->ColorCount > 256)
718     {
719         ERR("GIF contains %i colors???\n", cm->ColorCount);
720         return E_FAIL;
721     }
722
723     for (i = 0; i < cm->ColorCount; i++) {
724         colors[i] = 0xff000000| /* alpha */
725                     cm->Colors[i].Red << 16|
726                     cm->Colors[i].Green << 8|
727                     cm->Colors[i].Blue;
728     }
729
730     /* look for the transparent color extension */
731     for (i = 0; i < This->frame->Extensions.ExtensionBlockCount; ++i) {
732         eb = This->frame->Extensions.ExtensionBlocks + i;
733         if (eb->Function == GRAPHICS_EXT_FUNC_CODE && eb->ByteCount == 8) {
734             if (eb->Bytes[3] & 1) {
735                 trans = (unsigned char)eb->Bytes[6];
736                 colors[trans] &= 0xffffff; /* set alpha to 0 */
737                 break;
738             }
739         }
740     }
741
742     return IWICPalette_InitializeCustom(pIPalette, colors, cm->ColorCount);
743 }
744
745 static HRESULT copy_interlaced_pixels(const BYTE *srcbuffer,
746     UINT srcwidth, UINT srcheight, INT srcstride, const WICRect *rc,
747     UINT dststride, UINT dstbuffersize, BYTE *dstbuffer)
748 {
749     UINT row_offset; /* number of bytes into the source rows where the data starts */
750     const BYTE *src;
751     BYTE *dst;
752     UINT y;
753     WICRect rect;
754
755     if (!rc)
756     {
757         rect.X = 0;
758         rect.Y = 0;
759         rect.Width = srcwidth;
760         rect.Height = srcheight;
761         rc = &rect;
762     }
763     else
764     {
765         if (rc->X < 0 || rc->Y < 0 || rc->X+rc->Width > srcwidth || rc->Y+rc->Height > srcheight)
766             return E_INVALIDARG;
767     }
768
769     if (dststride < rc->Width)
770         return E_INVALIDARG;
771
772     if ((dststride * rc->Height) > dstbuffersize)
773         return E_INVALIDARG;
774
775     row_offset = rc->X;
776
777     dst = dstbuffer;
778     for (y=rc->Y; y-rc->Y < rc->Height; y++)
779     {
780         if (y%8 == 0)
781             src = srcbuffer + srcstride * (y/8);
782         else if (y%4 == 0)
783             src = srcbuffer + srcstride * ((srcheight+7)/8 + y/8);
784         else if (y%2 == 0)
785             src = srcbuffer + srcstride * ((srcheight+3)/4 + y/4);
786         else /* y%2 == 1 */
787             src = srcbuffer + srcstride * ((srcheight+1)/2 + y/2);
788         src += row_offset;
789         memcpy(dst, src, rc->Width);
790         dst += dststride;
791     }
792     return S_OK;
793 }
794
795 static HRESULT WINAPI GifFrameDecode_CopyPixels(IWICBitmapFrameDecode *iface,
796     const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
797 {
798     GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
799     TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
800
801     if (This->frame->ImageDesc.Interlace)
802     {
803         return copy_interlaced_pixels(This->frame->RasterBits, This->frame->ImageDesc.Width,
804             This->frame->ImageDesc.Height, This->frame->ImageDesc.Width,
805             prc, cbStride, cbBufferSize, pbBuffer);
806     }
807     else
808     {
809         return copy_pixels(8, This->frame->RasterBits, This->frame->ImageDesc.Width,
810             This->frame->ImageDesc.Height, This->frame->ImageDesc.Width,
811             prc, cbStride, cbBufferSize, pbBuffer);
812     }
813 }
814
815 static HRESULT WINAPI GifFrameDecode_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
816     IWICMetadataQueryReader **ppIMetadataQueryReader)
817 {
818     TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
819     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
820 }
821
822 static HRESULT WINAPI GifFrameDecode_GetColorContexts(IWICBitmapFrameDecode *iface,
823     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
824 {
825     TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
826     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
827 }
828
829 static HRESULT WINAPI GifFrameDecode_GetThumbnail(IWICBitmapFrameDecode *iface,
830     IWICBitmapSource **ppIThumbnail)
831 {
832     TRACE("(%p,%p)\n", iface, ppIThumbnail);
833     return WINCODEC_ERR_CODECNOTHUMBNAIL;
834 }
835
836 static const IWICBitmapFrameDecodeVtbl GifFrameDecode_Vtbl = {
837     GifFrameDecode_QueryInterface,
838     GifFrameDecode_AddRef,
839     GifFrameDecode_Release,
840     GifFrameDecode_GetSize,
841     GifFrameDecode_GetPixelFormat,
842     GifFrameDecode_GetResolution,
843     GifFrameDecode_CopyPalette,
844     GifFrameDecode_CopyPixels,
845     GifFrameDecode_GetMetadataQueryReader,
846     GifFrameDecode_GetColorContexts,
847     GifFrameDecode_GetThumbnail
848 };
849
850 static HRESULT WINAPI GifFrameDecode_Block_QueryInterface(IWICMetadataBlockReader *iface,
851     REFIID iid, void **ppv)
852 {
853     GifFrameDecode *This = frame_from_IWICMetadataBlockReader(iface);
854     return IWICBitmapFrameDecode_QueryInterface(&This->IWICBitmapFrameDecode_iface, iid, ppv);
855 }
856
857 static ULONG WINAPI GifFrameDecode_Block_AddRef(IWICMetadataBlockReader *iface)
858 {
859     GifFrameDecode *This = frame_from_IWICMetadataBlockReader(iface);
860     return IWICBitmapFrameDecode_AddRef(&This->IWICBitmapFrameDecode_iface);
861 }
862
863 static ULONG WINAPI GifFrameDecode_Block_Release(IWICMetadataBlockReader *iface)
864 {
865     GifFrameDecode *This = frame_from_IWICMetadataBlockReader(iface);
866     return IWICBitmapFrameDecode_Release(&This->IWICBitmapFrameDecode_iface);
867 }
868
869 static HRESULT WINAPI GifFrameDecode_Block_GetContainerFormat(IWICMetadataBlockReader *iface,
870     GUID *guid)
871 {
872     TRACE("(%p,%p)\n", iface, guid);
873
874     if (!guid) return E_INVALIDARG;
875
876     *guid = GUID_ContainerFormatGif;
877     return S_OK;
878 }
879
880 static HRESULT WINAPI GifFrameDecode_Block_GetCount(IWICMetadataBlockReader *iface,
881     UINT *count)
882 {
883     GifFrameDecode *This = frame_from_IWICMetadataBlockReader(iface);
884
885     TRACE("%p,%p\n", iface, count);
886
887     if (!count) return E_INVALIDARG;
888
889     *count = This->frame->Extensions.ExtensionBlockCount + 1;
890     return S_OK;
891 }
892
893 static HRESULT create_IMD_metadata_reader(GifFrameDecode *This, IWICMetadataReader **reader)
894 {
895     HRESULT hr;
896     IWICMetadataReader *metadata_reader;
897     IWICPersistStream *persist;
898     IStream *stream;
899     struct image_descriptor IMD_data;
900
901     /* FIXME: Use IWICComponentFactory_CreateMetadataReader once it's implemented */
902
903     hr = CoCreateInstance(&CLSID_WICIMDMetadataReader, NULL, CLSCTX_INPROC_SERVER,
904                           &IID_IWICMetadataReader, (void **)&metadata_reader);
905     if (FAILED(hr)) return hr;
906
907     hr = IWICMetadataReader_QueryInterface(metadata_reader, &IID_IWICPersistStream, (void **)&persist);
908     if (FAILED(hr))
909     {
910         IWICMetadataReader_Release(metadata_reader);
911         return hr;
912     }
913
914     /* recreate IMD structure from GIF decoder data */
915     IMD_data.left = This->frame->ImageDesc.Left;
916     IMD_data.top = This->frame->ImageDesc.Top;
917     IMD_data.width = This->frame->ImageDesc.Width;
918     IMD_data.height = This->frame->ImageDesc.Height;
919     IMD_data.packed = 0;
920     /* interlace_flag */
921     IMD_data.packed |= This->frame->ImageDesc.Interlace ? (1 << 6) : 0;
922     if (This->frame->ImageDesc.ColorMap)
923     {
924         /* local_color_table_flag */
925         IMD_data.packed |= 1 << 7;
926         /* local_color_table_size */
927         IMD_data.packed |= This->frame->ImageDesc.ColorMap->BitsPerPixel - 1;
928         /* sort_flag */
929         IMD_data.packed |= This->frame->ImageDesc.ColorMap->SortFlag ? 0x20 : 0;
930     }
931
932     stream = create_stream(&IMD_data, sizeof(IMD_data));
933     IWICPersistStream_LoadEx(persist, stream, NULL, WICPersistOptionsDefault);
934     IStream_Release(stream);
935
936     IWICPersistStream_Release(persist);
937
938     *reader = metadata_reader;
939     return S_OK;
940 }
941
942 static HRESULT WINAPI GifFrameDecode_Block_GetReaderByIndex(IWICMetadataBlockReader *iface,
943     UINT index, IWICMetadataReader **reader)
944 {
945     GifFrameDecode *This = frame_from_IWICMetadataBlockReader(iface);
946     int i, gce_index = -1, gce_skipped = 0;
947
948     TRACE("(%p,%u,%p)\n", iface, index, reader);
949
950     if (!reader) return E_INVALIDARG;
951
952     if (index == 0)
953         return create_IMD_metadata_reader(This, reader);
954
955     if (index >= This->frame->Extensions.ExtensionBlockCount + 1)
956         return E_INVALIDARG;
957
958     for (i = 0; i < This->frame->Extensions.ExtensionBlockCount; i++)
959     {
960         const CLSID *clsid;
961         const void *data;
962         int data_size;
963
964         if (index != i + 1 - gce_skipped) continue;
965
966         if (This->frame->Extensions.ExtensionBlocks[i].Function == GRAPHICS_EXT_FUNC_CODE)
967         {
968             gce_index = i;
969             gce_skipped = 1;
970             continue;
971         }
972         else if (This->frame->Extensions.ExtensionBlocks[i].Function == COMMENT_EXT_FUNC_CODE)
973         {
974             clsid = &CLSID_WICGifCommentMetadataReader;
975             data = This->frame->Extensions.ExtensionBlocks[i].Bytes;
976             data_size = This->frame->Extensions.ExtensionBlocks[i].ByteCount;
977         }
978         else
979         {
980             clsid = &CLSID_WICUnknownMetadataReader;
981             data = This->frame->Extensions.ExtensionBlocks[i].Bytes;
982             data_size = This->frame->Extensions.ExtensionBlocks[i].ByteCount;
983         }
984         return create_metadata_reader(data, data_size, clsid, reader);
985     }
986
987     if (gce_index == -1) return E_INVALIDARG;
988
989     return create_metadata_reader(This->frame->Extensions.ExtensionBlocks[gce_index].Bytes + 3,
990                                   This->frame->Extensions.ExtensionBlocks[gce_index].ByteCount - 4,
991                                   &CLSID_WICGCEMetadataReader, reader);
992 }
993
994 static HRESULT WINAPI GifFrameDecode_Block_GetEnumerator(IWICMetadataBlockReader *iface,
995     IEnumUnknown **enumerator)
996 {
997     FIXME("(%p,%p): stub\n", iface, enumerator);
998     return E_NOTIMPL;
999 }
1000
1001 static const IWICMetadataBlockReaderVtbl GifFrameDecode_BlockVtbl =
1002 {
1003     GifFrameDecode_Block_QueryInterface,
1004     GifFrameDecode_Block_AddRef,
1005     GifFrameDecode_Block_Release,
1006     GifFrameDecode_Block_GetContainerFormat,
1007     GifFrameDecode_Block_GetCount,
1008     GifFrameDecode_Block_GetReaderByIndex,
1009     GifFrameDecode_Block_GetEnumerator
1010 };
1011
1012 static HRESULT WINAPI GifDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid,
1013     void **ppv)
1014 {
1015     GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
1016     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
1017
1018     if (!ppv) return E_INVALIDARG;
1019
1020     if (IsEqualIID(&IID_IUnknown, iid) ||
1021         IsEqualIID(&IID_IWICBitmapDecoder, iid))
1022     {
1023         *ppv = &This->IWICBitmapDecoder_iface;
1024     }
1025     else if (IsEqualIID(&IID_IWICMetadataBlockReader, iid))
1026     {
1027         *ppv = &This->IWICMetadataBlockReader_iface;
1028     }
1029     else
1030     {
1031         *ppv = NULL;
1032         return E_NOINTERFACE;
1033     }
1034
1035     IUnknown_AddRef((IUnknown*)*ppv);
1036     return S_OK;
1037 }
1038
1039 static ULONG WINAPI GifDecoder_AddRef(IWICBitmapDecoder *iface)
1040 {
1041     GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
1042     ULONG ref = InterlockedIncrement(&This->ref);
1043
1044     TRACE("(%p) refcount=%u\n", iface, ref);
1045
1046     return ref;
1047 }
1048
1049 static ULONG WINAPI GifDecoder_Release(IWICBitmapDecoder *iface)
1050 {
1051     GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
1052     ULONG ref = InterlockedDecrement(&This->ref);
1053
1054     TRACE("(%p) refcount=%u\n", iface, ref);
1055
1056     if (ref == 0)
1057     {
1058         This->lock.DebugInfo->Spare[0] = 0;
1059         DeleteCriticalSection(&This->lock);
1060         DGifCloseFile(This->gif);
1061         HeapFree(GetProcessHeap(), 0, This);
1062     }
1063
1064     return ref;
1065 }
1066
1067 static HRESULT WINAPI GifDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *stream,
1068     DWORD *capability)
1069 {
1070     HRESULT hr;
1071
1072     TRACE("(%p,%p,%p)\n", iface, stream, capability);
1073
1074     if (!stream || !capability) return E_INVALIDARG;
1075
1076     hr = IWICBitmapDecoder_Initialize(iface, stream, WICDecodeMetadataCacheOnDemand);
1077     if (hr != S_OK) return hr;
1078
1079     *capability = WICBitmapDecoderCapabilityCanDecodeAllImages |
1080                   WICBitmapDecoderCapabilityCanDecodeSomeImages |
1081                   WICBitmapDecoderCapabilityCanEnumerateMetadata;
1082     return S_OK;
1083 }
1084
1085 static int _gif_inputfunc(GifFileType *gif, GifByteType *data, int len) {
1086     IStream *stream = gif->UserData;
1087     ULONG bytesread;
1088     HRESULT hr;
1089
1090     if (!stream)
1091     {
1092         ERR("attempting to read file after initialization\n");
1093         return 0;
1094     }
1095
1096     hr = IStream_Read(stream, data, len, &bytesread);
1097     if (hr != S_OK) bytesread = 0;
1098     return bytesread;
1099 }
1100
1101 static HRESULT WINAPI GifDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream,
1102     WICDecodeOptions cacheOptions)
1103 {
1104     GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
1105     LARGE_INTEGER seek;
1106     int ret;
1107
1108     TRACE("(%p,%p,%x)\n", iface, pIStream, cacheOptions);
1109
1110     EnterCriticalSection(&This->lock);
1111
1112     if (This->initialized || This->gif)
1113     {
1114         WARN("already initialized\n");
1115         LeaveCriticalSection(&This->lock);
1116         return WINCODEC_ERR_WRONGSTATE;
1117     }
1118
1119     /* seek to start of stream */
1120     seek.QuadPart = 0;
1121     IStream_Seek(pIStream, seek, STREAM_SEEK_SET, NULL);
1122
1123     /* read all data from the stream */
1124     This->gif = DGifOpen((void*)pIStream, _gif_inputfunc);
1125     if (!This->gif)
1126     {
1127         LeaveCriticalSection(&This->lock);
1128         return E_FAIL;
1129     }
1130
1131     ret = DGifSlurp(This->gif);
1132     if (ret == GIF_ERROR)
1133     {
1134         LeaveCriticalSection(&This->lock);
1135         return E_FAIL;
1136     }
1137
1138     /* make sure we don't use the stream after this method returns */
1139     This->gif->UserData = NULL;
1140
1141     seek.QuadPart = 0;
1142     IStream_Seek(pIStream, seek, STREAM_SEEK_SET, NULL);
1143     IStream_Read(pIStream, This->LSD_data, sizeof(This->LSD_data), NULL);
1144
1145     This->initialized = TRUE;
1146
1147     LeaveCriticalSection(&This->lock);
1148
1149     return S_OK;
1150 }
1151
1152 static HRESULT WINAPI GifDecoder_GetContainerFormat(IWICBitmapDecoder *iface,
1153     GUID *pguidContainerFormat)
1154 {
1155     memcpy(pguidContainerFormat, &GUID_ContainerFormatGif, sizeof(GUID));
1156     return S_OK;
1157 }
1158
1159 static HRESULT WINAPI GifDecoder_GetDecoderInfo(IWICBitmapDecoder *iface,
1160     IWICBitmapDecoderInfo **ppIDecoderInfo)
1161 {
1162     HRESULT hr;
1163     IWICComponentInfo *compinfo;
1164
1165     TRACE("(%p,%p)\n", iface, ppIDecoderInfo);
1166
1167     hr = CreateComponentInfo(&CLSID_WICGifDecoder, &compinfo);
1168     if (FAILED(hr)) return hr;
1169
1170     hr = IWICComponentInfo_QueryInterface(compinfo, &IID_IWICBitmapDecoderInfo,
1171         (void**)ppIDecoderInfo);
1172
1173     IWICComponentInfo_Release(compinfo);
1174
1175     return hr;
1176 }
1177
1178 static HRESULT WINAPI GifDecoder_CopyPalette(IWICBitmapDecoder *iface, IWICPalette *palette)
1179 {
1180     GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
1181     WICColor colors[256];
1182     ColorMapObject *cm;
1183     int i, trans;
1184     ExtensionBlock *eb;
1185
1186     TRACE("(%p,%p)\n", iface, palette);
1187
1188     cm = This->gif->SColorMap;
1189     if (!cm) return WINCODEC_ERR_FRAMEMISSING;
1190
1191     if (cm->ColorCount > 256)
1192     {
1193         ERR("GIF contains invalid number of colors: %d\n", cm->ColorCount);
1194         return E_FAIL;
1195     }
1196
1197     for (i = 0; i < cm->ColorCount; i++)
1198     {
1199         colors[i] = 0xff000000 | /* alpha */
1200                     cm->Colors[i].Red << 16 |
1201                     cm->Colors[i].Green << 8 |
1202                     cm->Colors[i].Blue;
1203     }
1204
1205     /* look for the transparent color extension */
1206     for (i = 0; i < This->gif->SavedImages[This->current_frame].Extensions.ExtensionBlockCount; i++)
1207     {
1208         eb = This->gif->SavedImages[This->current_frame].Extensions.ExtensionBlocks + i;
1209         if (eb->Function == GRAPHICS_EXT_FUNC_CODE && eb->ByteCount == 8)
1210         {
1211             if (eb->Bytes[3] & 1)
1212             {
1213                 trans = (unsigned char)eb->Bytes[6];
1214                 colors[trans] &= 0xffffff; /* set alpha to 0 */
1215                 break;
1216             }
1217         }
1218     }
1219
1220     return IWICPalette_InitializeCustom(palette, colors, cm->ColorCount);
1221 }
1222
1223 static HRESULT WINAPI GifDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface,
1224     IWICMetadataQueryReader **ppIMetadataQueryReader)
1225 {
1226     TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
1227     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1228 }
1229
1230 static HRESULT WINAPI GifDecoder_GetPreview(IWICBitmapDecoder *iface,
1231     IWICBitmapSource **ppIBitmapSource)
1232 {
1233     TRACE("(%p,%p)\n", iface, ppIBitmapSource);
1234     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1235 }
1236
1237 static HRESULT WINAPI GifDecoder_GetColorContexts(IWICBitmapDecoder *iface,
1238     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
1239 {
1240     TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
1241     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1242 }
1243
1244 static HRESULT WINAPI GifDecoder_GetThumbnail(IWICBitmapDecoder *iface,
1245     IWICBitmapSource **ppIThumbnail)
1246 {
1247     TRACE("(%p,%p)\n", iface, ppIThumbnail);
1248     return WINCODEC_ERR_CODECNOTHUMBNAIL;
1249 }
1250
1251 static HRESULT WINAPI GifDecoder_GetFrameCount(IWICBitmapDecoder *iface,
1252     UINT *pCount)
1253 {
1254     GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
1255
1256     if (!pCount) return E_INVALIDARG;
1257
1258     EnterCriticalSection(&This->lock);
1259     *pCount = This->gif ? This->gif->ImageCount : 0;
1260     LeaveCriticalSection(&This->lock);
1261
1262     TRACE("(%p) <-- %d\n", iface, *pCount);
1263
1264     return S_OK;
1265 }
1266
1267 static HRESULT WINAPI GifDecoder_GetFrame(IWICBitmapDecoder *iface,
1268     UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
1269 {
1270     GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
1271     GifFrameDecode *result;
1272     TRACE("(%p,%u,%p)\n", iface, index, ppIBitmapFrame);
1273
1274     if (!This->initialized) return WINCODEC_ERR_FRAMEMISSING;
1275
1276     if (index >= This->gif->ImageCount) return E_INVALIDARG;
1277
1278     result = HeapAlloc(GetProcessHeap(), 0, sizeof(GifFrameDecode));
1279     if (!result) return E_OUTOFMEMORY;
1280
1281     result->IWICBitmapFrameDecode_iface.lpVtbl = &GifFrameDecode_Vtbl;
1282     result->IWICMetadataBlockReader_iface.lpVtbl = &GifFrameDecode_BlockVtbl;
1283     result->ref = 1;
1284     result->frame = &This->gif->SavedImages[index];
1285     IWICBitmapDecoder_AddRef(iface);
1286     result->parent = This;
1287     This->current_frame = index;
1288
1289     *ppIBitmapFrame = &result->IWICBitmapFrameDecode_iface;
1290
1291     return S_OK;
1292 }
1293
1294 static const IWICBitmapDecoderVtbl GifDecoder_Vtbl = {
1295     GifDecoder_QueryInterface,
1296     GifDecoder_AddRef,
1297     GifDecoder_Release,
1298     GifDecoder_QueryCapability,
1299     GifDecoder_Initialize,
1300     GifDecoder_GetContainerFormat,
1301     GifDecoder_GetDecoderInfo,
1302     GifDecoder_CopyPalette,
1303     GifDecoder_GetMetadataQueryReader,
1304     GifDecoder_GetPreview,
1305     GifDecoder_GetColorContexts,
1306     GifDecoder_GetThumbnail,
1307     GifDecoder_GetFrameCount,
1308     GifDecoder_GetFrame
1309 };
1310
1311 static HRESULT WINAPI GifDecoder_Block_QueryInterface(IWICMetadataBlockReader *iface,
1312     REFIID iid, void **ppv)
1313 {
1314     GifDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1315     return IWICBitmapDecoder_QueryInterface(&This->IWICBitmapDecoder_iface, iid, ppv);
1316 }
1317
1318 static ULONG WINAPI GifDecoder_Block_AddRef(IWICMetadataBlockReader *iface)
1319 {
1320     GifDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1321     return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
1322 }
1323
1324 static ULONG WINAPI GifDecoder_Block_Release(IWICMetadataBlockReader *iface)
1325 {
1326     GifDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1327     return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
1328 }
1329
1330 static HRESULT WINAPI GifDecoder_Block_GetContainerFormat(IWICMetadataBlockReader *iface,
1331     GUID *guid)
1332 {
1333     TRACE("(%p,%p)\n", iface, guid);
1334
1335     if (!guid) return E_INVALIDARG;
1336
1337     *guid = GUID_ContainerFormatGif;
1338     return S_OK;
1339 }
1340
1341 static HRESULT WINAPI GifDecoder_Block_GetCount(IWICMetadataBlockReader *iface,
1342     UINT *count)
1343 {
1344     GifDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1345
1346     TRACE("%p,%p\n", iface, count);
1347
1348     if (!count) return E_INVALIDARG;
1349
1350     *count = This->gif->Extensions.ExtensionBlockCount + 1;
1351     return S_OK;
1352 }
1353
1354 static HRESULT WINAPI GifDecoder_Block_GetReaderByIndex(IWICMetadataBlockReader *iface,
1355     UINT index, IWICMetadataReader **reader)
1356 {
1357     GifDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1358     int i;
1359
1360     TRACE("(%p,%u,%p)\n", iface, index, reader);
1361
1362     if (!reader) return E_INVALIDARG;
1363
1364     if (index == 0)
1365         return create_metadata_reader(This->LSD_data, sizeof(This->LSD_data),
1366                                       &CLSID_WICLSDMetadataReader, reader);
1367
1368     for (i = 0; i < This->gif->Extensions.ExtensionBlockCount; i++)
1369     {
1370         const CLSID *clsid;
1371
1372         if (index != i + 1) continue;
1373
1374         if (This->gif->Extensions.ExtensionBlocks[i].Function == APPLICATION_EXT_FUNC_CODE)
1375             clsid = &CLSID_WICAPEMetadataReader;
1376         else if (This->gif->Extensions.ExtensionBlocks[i].Function == COMMENT_EXT_FUNC_CODE)
1377             clsid = &CLSID_WICGifCommentMetadataReader;
1378         else
1379             clsid = &CLSID_WICUnknownMetadataReader;
1380
1381         return create_metadata_reader(This->gif->Extensions.ExtensionBlocks[i].Bytes,
1382                                       This->gif->Extensions.ExtensionBlocks[i].ByteCount,
1383                                       clsid, reader);
1384     }
1385
1386     return E_INVALIDARG;
1387 }
1388
1389 static HRESULT WINAPI GifDecoder_Block_GetEnumerator(IWICMetadataBlockReader *iface,
1390     IEnumUnknown **enumerator)
1391 {
1392     FIXME("(%p,%p): stub\n", iface, enumerator);
1393     return E_NOTIMPL;
1394 }
1395
1396 static const IWICMetadataBlockReaderVtbl GifDecoder_BlockVtbl =
1397 {
1398     GifDecoder_Block_QueryInterface,
1399     GifDecoder_Block_AddRef,
1400     GifDecoder_Block_Release,
1401     GifDecoder_Block_GetContainerFormat,
1402     GifDecoder_Block_GetCount,
1403     GifDecoder_Block_GetReaderByIndex,
1404     GifDecoder_Block_GetEnumerator
1405 };
1406
1407 HRESULT GifDecoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
1408 {
1409     GifDecoder *This;
1410     HRESULT ret;
1411
1412     TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
1413
1414     *ppv = NULL;
1415
1416     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
1417
1418     This = HeapAlloc(GetProcessHeap(), 0, sizeof(GifDecoder));
1419     if (!This) return E_OUTOFMEMORY;
1420
1421     This->IWICBitmapDecoder_iface.lpVtbl = &GifDecoder_Vtbl;
1422     This->IWICMetadataBlockReader_iface.lpVtbl = &GifDecoder_BlockVtbl;
1423     This->ref = 1;
1424     This->initialized = FALSE;
1425     This->gif = NULL;
1426     This->current_frame = 0;
1427     InitializeCriticalSection(&This->lock);
1428     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": GifDecoder.lock");
1429
1430     ret = IWICBitmapDecoder_QueryInterface(&This->IWICBitmapDecoder_iface, iid, ppv);
1431     IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
1432
1433     return ret;
1434 }