windowscodecs: Make sure that all GIF metadata blocks are properly packed.
[wine] / dlls / windowscodecs / gifformat.c
1 /*
2  * Copyright 2009 Vincent Povirk for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include "config.h"
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24 #define NONAMELESSUNION
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "objbase.h"
29 #include "wincodec.h"
30 #include "wincodecsdk.h"
31
32 #include "ungif.h"
33
34 #include "wincodecs_private.h"
35
36 #include "wine/debug.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
39
40 static LPWSTR strdupAtoW(const char *src)
41 {
42     int len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
43     LPWSTR dst = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
44     if (dst) MultiByteToWideChar(CP_ACP, 0, src, -1, dst, len);
45     return dst;
46 }
47
48 static HRESULT load_LSD_metadata(IStream *stream, const GUID *vendor, DWORD options,
49                                  MetadataItem **items, DWORD *count)
50 {
51 #include "pshpack1.h"
52     struct logical_screen_descriptor
53     {
54         char signature[6];
55         USHORT width;
56         USHORT height;
57         BYTE packed;
58         /* global_color_table_flag : 1;
59          * color_resolution : 3;
60          * sort_flag : 1;
61          * global_color_table_size : 3;
62          */
63         BYTE background_color_index;
64         BYTE pixel_aspect_ratio;
65     } lsd_data;
66 #include "poppack.h"
67     HRESULT hr;
68     ULONG bytesread, i;
69     MetadataItem *result;
70
71     *items = NULL;
72     *count = 0;
73
74     hr = IStream_Read(stream, &lsd_data, sizeof(lsd_data), &bytesread);
75     if (FAILED(hr) || bytesread != sizeof(lsd_data)) return S_OK;
76
77     result = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataItem) * 9);
78     if (!result) return E_OUTOFMEMORY;
79
80     for (i = 0; i < 9; i++)
81     {
82         PropVariantInit(&result[i].schema);
83         PropVariantInit(&result[i].id);
84         PropVariantInit(&result[i].value);
85     }
86
87     result[0].id.vt = VT_LPWSTR;
88     result[0].id.u.pwszVal = strdupAtoW("Signature");
89     result[0].value.vt = VT_UI1|VT_VECTOR;
90     result[0].value.u.caub.cElems = sizeof(lsd_data.signature);
91     result[0].value.u.caub.pElems = HeapAlloc(GetProcessHeap(), 0, sizeof(lsd_data.signature));
92     memcpy(result[0].value.u.caub.pElems, lsd_data.signature, sizeof(lsd_data.signature));
93
94     result[1].id.vt = VT_LPWSTR;
95     result[1].id.u.pwszVal = strdupAtoW("Width");
96     result[1].value.vt = VT_UI2;
97     result[1].value.u.uiVal = lsd_data.width;
98
99     result[2].id.vt = VT_LPWSTR;
100     result[2].id.u.pwszVal = strdupAtoW("Height");
101     result[2].value.vt = VT_UI2;
102     result[2].value.u.uiVal = lsd_data.height;
103
104     result[3].id.vt = VT_LPWSTR;
105     result[3].id.u.pwszVal = strdupAtoW("GlobalColorTableFlag");
106     result[3].value.vt = VT_BOOL;
107     result[3].value.u.boolVal = (lsd_data.packed >> 7) & 1;
108
109     result[4].id.vt = VT_LPWSTR;
110     result[4].id.u.pwszVal = strdupAtoW("ColorResolution");
111     result[4].value.vt = VT_UI1;
112     result[4].value.u.bVal = (lsd_data.packed >> 4) & 7;
113
114     result[5].id.vt = VT_LPWSTR;
115     result[5].id.u.pwszVal = strdupAtoW("SortFlag");
116     result[5].value.vt = VT_BOOL;
117     result[5].value.u.boolVal = (lsd_data.packed >> 3) & 1;
118
119     result[6].id.vt = VT_LPWSTR;
120     result[6].id.u.pwszVal = strdupAtoW("GlobalColorTableSize");
121     result[6].value.vt = VT_UI1;
122     result[6].value.u.bVal = lsd_data.packed & 7;
123
124     result[7].id.vt = VT_LPWSTR;
125     result[7].id.u.pwszVal = strdupAtoW("BackgroundColorIndex");
126     result[7].value.vt = VT_UI1;
127     result[7].value.u.bVal = lsd_data.background_color_index;
128
129     result[8].id.vt = VT_LPWSTR;
130     result[8].id.u.pwszVal = strdupAtoW("PixelAspectRatio");
131     result[8].value.vt = VT_UI1;
132     result[8].value.u.bVal = lsd_data.pixel_aspect_ratio;
133
134     *items = result;
135     *count = 9;
136
137     return S_OK;
138 }
139
140 static const MetadataHandlerVtbl LSDReader_Vtbl = {
141     0,
142     &CLSID_WICLSDMetadataReader,
143     load_LSD_metadata
144 };
145
146 HRESULT LSDReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppv)
147 {
148     return MetadataReader_Create(&LSDReader_Vtbl, pUnkOuter, iid, ppv);
149 }
150
151 static HRESULT load_IMD_metadata(IStream *stream, const GUID *vendor, DWORD options,
152                                  MetadataItem **items, DWORD *count)
153 {
154 #include "pshpack1.h"
155     struct image_descriptor
156     {
157         USHORT left;
158         USHORT top;
159         USHORT width;
160         USHORT height;
161         BYTE packed;
162         /* local_color_table_flag : 1;
163          * interlace_flag : 1;
164          * sort_flag : 1;
165          * reserved : 2;
166          * local_color_table_size : 3;
167          */
168     } imd_data;
169 #include "poppack.h"
170     HRESULT hr;
171     ULONG bytesread, i;
172     MetadataItem *result;
173
174     *items = NULL;
175     *count = 0;
176
177     hr = IStream_Read(stream, &imd_data, sizeof(imd_data), &bytesread);
178     if (FAILED(hr) || bytesread != sizeof(imd_data)) return S_OK;
179
180     result = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataItem) * 8);
181     if (!result) return E_OUTOFMEMORY;
182
183     for (i = 0; i < 8; i++)
184     {
185         PropVariantInit(&result[i].schema);
186         PropVariantInit(&result[i].id);
187         PropVariantInit(&result[i].value);
188     }
189
190     result[0].id.vt = VT_LPWSTR;
191     result[0].id.u.pwszVal = strdupAtoW("Left");
192     result[0].value.vt = VT_UI2;
193     result[0].value.u.uiVal = imd_data.left;
194
195     result[1].id.vt = VT_LPWSTR;
196     result[1].id.u.pwszVal = strdupAtoW("Top");
197     result[1].value.vt = VT_UI2;
198     result[1].value.u.uiVal = imd_data.top;
199
200     result[2].id.vt = VT_LPWSTR;
201     result[2].id.u.pwszVal = strdupAtoW("Width");
202     result[2].value.vt = VT_UI2;
203     result[2].value.u.uiVal = imd_data.width;
204
205     result[3].id.vt = VT_LPWSTR;
206     result[3].id.u.pwszVal = strdupAtoW("Height");
207     result[3].value.vt = VT_UI2;
208     result[3].value.u.uiVal = imd_data.height;
209
210     result[4].id.vt = VT_LPWSTR;
211     result[4].id.u.pwszVal = strdupAtoW("LocalColorTableFlag");
212     result[4].value.vt = VT_BOOL;
213     result[4].value.u.boolVal = (imd_data.packed >> 7) & 1;
214
215     result[5].id.vt = VT_LPWSTR;
216     result[5].id.u.pwszVal = strdupAtoW("InterlaceFlag");
217     result[5].value.vt = VT_BOOL;
218     result[5].value.u.boolVal = (imd_data.packed >> 6) & 1;
219
220     result[6].id.vt = VT_LPWSTR;
221     result[6].id.u.pwszVal = strdupAtoW("SortFlag");
222     result[6].value.vt = VT_BOOL;
223     result[6].value.u.boolVal = (imd_data.packed >> 5) & 1;
224
225     result[7].id.vt = VT_LPWSTR;
226     result[7].id.u.pwszVal = strdupAtoW("LocalColorTableSize");
227     result[7].value.vt = VT_UI1;
228     result[7].value.u.bVal = imd_data.packed & 7;
229
230     *items = result;
231     *count = 8;
232
233     return S_OK;
234 }
235
236 static const MetadataHandlerVtbl IMDReader_Vtbl = {
237     0,
238     &CLSID_WICIMDMetadataReader,
239     load_IMD_metadata
240 };
241
242 HRESULT IMDReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppv)
243 {
244     return MetadataReader_Create(&IMDReader_Vtbl, pUnkOuter, iid, ppv);
245 }
246
247 static HRESULT load_GCE_metadata(IStream *stream, const GUID *vendor, DWORD options,
248                                  MetadataItem **items, DWORD *count)
249 {
250 #include "pshpack1.h"
251     struct graphic_control_extenstion
252     {
253         BYTE packed;
254         /* reservred: 3;
255          * disposal : 3;
256          * user_input_flag : 1;
257          * transparency_flag : 1;
258          */
259          USHORT delay;
260          BYTE transparent_color_index;
261     } gce_data;
262 #include "poppack.h"
263     HRESULT hr;
264     ULONG bytesread, i;
265     MetadataItem *result;
266
267     *items = NULL;
268     *count = 0;
269
270     hr = IStream_Read(stream, &gce_data, sizeof(gce_data), &bytesread);
271     if (FAILED(hr) || bytesread != sizeof(gce_data)) return S_OK;
272
273     result = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataItem) * 5);
274     if (!result) return E_OUTOFMEMORY;
275
276     for (i = 0; i < 5; i++)
277     {
278         PropVariantInit(&result[i].schema);
279         PropVariantInit(&result[i].id);
280         PropVariantInit(&result[i].value);
281     }
282
283     result[0].id.vt = VT_LPWSTR;
284     result[0].id.u.pwszVal = strdupAtoW("Disposal");
285     result[0].value.vt = VT_UI1;
286     result[0].value.u.bVal = (gce_data.packed >> 2) & 7;
287
288     result[1].id.vt = VT_LPWSTR;
289     result[1].id.u.pwszVal = strdupAtoW("UserInputFlag");
290     result[1].value.vt = VT_BOOL;
291     result[1].value.u.boolVal = (gce_data.packed >> 1) & 1;
292
293     result[2].id.vt = VT_LPWSTR;
294     result[2].id.u.pwszVal = strdupAtoW("TransparencyFlag");
295     result[2].value.vt = VT_BOOL;
296     result[2].value.u.boolVal = gce_data.packed & 1;
297
298     result[3].id.vt = VT_LPWSTR;
299     result[3].id.u.pwszVal = strdupAtoW("Delay");
300     result[3].value.vt = VT_UI2;
301     result[3].value.u.uiVal = gce_data.delay;
302
303     result[4].id.vt = VT_LPWSTR;
304     result[4].id.u.pwszVal = strdupAtoW("TransparentColorIndex");
305     result[4].value.vt = VT_UI1;
306     result[4].value.u.bVal = gce_data.transparent_color_index;
307
308     *items = result;
309     *count = 5;
310
311     return S_OK;
312 }
313
314 static const MetadataHandlerVtbl GCEReader_Vtbl = {
315     0,
316     &CLSID_WICGCEMetadataReader,
317     load_GCE_metadata
318 };
319
320 HRESULT GCEReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppv)
321 {
322     return MetadataReader_Create(&GCEReader_Vtbl, pUnkOuter, iid, ppv);
323 }
324
325 typedef struct {
326     IWICBitmapDecoder IWICBitmapDecoder_iface;
327     LONG ref;
328     BOOL initialized;
329     GifFileType *gif;
330     CRITICAL_SECTION lock;
331 } GifDecoder;
332
333 typedef struct {
334     IWICBitmapFrameDecode IWICBitmapFrameDecode_iface;
335     LONG ref;
336     SavedImage *frame;
337     GifDecoder *parent;
338 } GifFrameDecode;
339
340 static inline GifDecoder *impl_from_IWICBitmapDecoder(IWICBitmapDecoder *iface)
341 {
342     return CONTAINING_RECORD(iface, GifDecoder, IWICBitmapDecoder_iface);
343 }
344
345 static inline GifFrameDecode *impl_from_IWICBitmapFrameDecode(IWICBitmapFrameDecode *iface)
346 {
347     return CONTAINING_RECORD(iface, GifFrameDecode, IWICBitmapFrameDecode_iface);
348 }
349
350 static HRESULT WINAPI GifFrameDecode_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
351     void **ppv)
352 {
353     GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
354     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
355
356     if (!ppv) return E_INVALIDARG;
357
358     if (IsEqualIID(&IID_IUnknown, iid) ||
359         IsEqualIID(&IID_IWICBitmapSource, iid) ||
360         IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
361     {
362         *ppv = &This->IWICBitmapFrameDecode_iface;
363     }
364     else
365     {
366         *ppv = NULL;
367         return E_NOINTERFACE;
368     }
369
370     IUnknown_AddRef((IUnknown*)*ppv);
371     return S_OK;
372 }
373
374 static ULONG WINAPI GifFrameDecode_AddRef(IWICBitmapFrameDecode *iface)
375 {
376     GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
377     ULONG ref = InterlockedIncrement(&This->ref);
378
379     TRACE("(%p) refcount=%u\n", iface, ref);
380
381     return ref;
382 }
383
384 static ULONG WINAPI GifFrameDecode_Release(IWICBitmapFrameDecode *iface)
385 {
386     GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
387     ULONG ref = InterlockedDecrement(&This->ref);
388
389     TRACE("(%p) refcount=%u\n", iface, ref);
390
391     if (ref == 0)
392     {
393         IUnknown_Release((IUnknown*)This->parent);
394         HeapFree(GetProcessHeap(), 0, This);
395     }
396
397     return ref;
398 }
399
400 static HRESULT WINAPI GifFrameDecode_GetSize(IWICBitmapFrameDecode *iface,
401     UINT *puiWidth, UINT *puiHeight)
402 {
403     GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
404     TRACE("(%p,%p,%p)\n", iface, puiWidth, puiHeight);
405
406     *puiWidth = This->frame->ImageDesc.Width;
407     *puiHeight = This->frame->ImageDesc.Height;
408
409     return S_OK;
410 }
411
412 static HRESULT WINAPI GifFrameDecode_GetPixelFormat(IWICBitmapFrameDecode *iface,
413     WICPixelFormatGUID *pPixelFormat)
414 {
415     memcpy(pPixelFormat, &GUID_WICPixelFormat8bppIndexed, sizeof(GUID));
416
417     return S_OK;
418 }
419
420 static HRESULT WINAPI GifFrameDecode_GetResolution(IWICBitmapFrameDecode *iface,
421     double *pDpiX, double *pDpiY)
422 {
423     GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
424     const GifWord aspect_word = This->parent->gif->SAspectRatio;
425     const double aspect = (aspect_word > 0) ? ((aspect_word + 15.0) / 64.0) : 1.0;
426     TRACE("(%p,%p,%p)\n", iface, pDpiX, pDpiY);
427
428     *pDpiX = 96.0 / aspect;
429     *pDpiY = 96.0;
430
431     return S_OK;
432 }
433
434 static HRESULT WINAPI GifFrameDecode_CopyPalette(IWICBitmapFrameDecode *iface,
435     IWICPalette *pIPalette)
436 {
437     GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
438     WICColor colors[256];
439     ColorMapObject *cm = This->frame->ImageDesc.ColorMap;
440     int i, trans;
441     ExtensionBlock *eb;
442     TRACE("(%p,%p)\n", iface, pIPalette);
443
444     if (!cm) cm = This->parent->gif->SColorMap;
445
446     if (cm->ColorCount > 256)
447     {
448         ERR("GIF contains %i colors???\n", cm->ColorCount);
449         return E_FAIL;
450     }
451
452     for (i = 0; i < cm->ColorCount; i++) {
453         colors[i] = 0xff000000| /* alpha */
454                     cm->Colors[i].Red << 16|
455                     cm->Colors[i].Green << 8|
456                     cm->Colors[i].Blue;
457     }
458
459     /* look for the transparent color extension */
460     for (i = 0; i < This->frame->ExtensionBlockCount; ++i) {
461         eb = This->frame->ExtensionBlocks + i;
462         if (eb->Function == 0xF9 && eb->ByteCount == 4) {
463             if ((eb->Bytes[0] & 1) == 1) {
464                 trans = (unsigned char)eb->Bytes[3];
465                 colors[trans] &= 0xffffff; /* set alpha to 0 */
466                 break;
467             }
468         }
469     }
470
471     IWICPalette_InitializeCustom(pIPalette, colors, cm->ColorCount);
472
473     return S_OK;
474 }
475
476 static HRESULT copy_interlaced_pixels(const BYTE *srcbuffer,
477     UINT srcwidth, UINT srcheight, INT srcstride, const WICRect *rc,
478     UINT dststride, UINT dstbuffersize, BYTE *dstbuffer)
479 {
480     UINT row_offset; /* number of bytes into the source rows where the data starts */
481     const BYTE *src;
482     BYTE *dst;
483     UINT y;
484     WICRect rect;
485
486     if (!rc)
487     {
488         rect.X = 0;
489         rect.Y = 0;
490         rect.Width = srcwidth;
491         rect.Height = srcheight;
492         rc = &rect;
493     }
494     else
495     {
496         if (rc->X < 0 || rc->Y < 0 || rc->X+rc->Width > srcwidth || rc->Y+rc->Height > srcheight)
497             return E_INVALIDARG;
498     }
499
500     if (dststride < rc->Width)
501         return E_INVALIDARG;
502
503     if ((dststride * rc->Height) > dstbuffersize)
504         return E_INVALIDARG;
505
506     row_offset = rc->X;
507
508     dst = dstbuffer;
509     for (y=rc->Y; y-rc->Y < rc->Height; y++)
510     {
511         if (y%8 == 0)
512             src = srcbuffer + srcstride * (y/8);
513         else if (y%4 == 0)
514             src = srcbuffer + srcstride * ((srcheight+7)/8 + y/8);
515         else if (y%2 == 0)
516             src = srcbuffer + srcstride * ((srcheight+3)/4 + y/4);
517         else /* y%2 == 1 */
518             src = srcbuffer + srcstride * ((srcheight+1)/2 + y/2);
519         src += row_offset;
520         memcpy(dst, src, rc->Width);
521         dst += dststride;
522     }
523     return S_OK;
524 }
525
526 static HRESULT WINAPI GifFrameDecode_CopyPixels(IWICBitmapFrameDecode *iface,
527     const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
528 {
529     GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
530     TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
531
532     if (This->frame->ImageDesc.Interlace)
533     {
534         return copy_interlaced_pixels(This->frame->RasterBits, This->frame->ImageDesc.Width,
535             This->frame->ImageDesc.Height, This->frame->ImageDesc.Width,
536             prc, cbStride, cbBufferSize, pbBuffer);
537     }
538     else
539     {
540         return copy_pixels(8, This->frame->RasterBits, This->frame->ImageDesc.Width,
541             This->frame->ImageDesc.Height, This->frame->ImageDesc.Width,
542             prc, cbStride, cbBufferSize, pbBuffer);
543     }
544 }
545
546 static HRESULT WINAPI GifFrameDecode_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
547     IWICMetadataQueryReader **ppIMetadataQueryReader)
548 {
549     TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
550     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
551 }
552
553 static HRESULT WINAPI GifFrameDecode_GetColorContexts(IWICBitmapFrameDecode *iface,
554     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
555 {
556     TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
557     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
558 }
559
560 static HRESULT WINAPI GifFrameDecode_GetThumbnail(IWICBitmapFrameDecode *iface,
561     IWICBitmapSource **ppIThumbnail)
562 {
563     TRACE("(%p,%p)\n", iface, ppIThumbnail);
564     return WINCODEC_ERR_CODECNOTHUMBNAIL;
565 }
566
567 static const IWICBitmapFrameDecodeVtbl GifFrameDecode_Vtbl = {
568     GifFrameDecode_QueryInterface,
569     GifFrameDecode_AddRef,
570     GifFrameDecode_Release,
571     GifFrameDecode_GetSize,
572     GifFrameDecode_GetPixelFormat,
573     GifFrameDecode_GetResolution,
574     GifFrameDecode_CopyPalette,
575     GifFrameDecode_CopyPixels,
576     GifFrameDecode_GetMetadataQueryReader,
577     GifFrameDecode_GetColorContexts,
578     GifFrameDecode_GetThumbnail
579 };
580
581 static HRESULT WINAPI GifDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid,
582     void **ppv)
583 {
584     GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
585     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
586
587     if (!ppv) return E_INVALIDARG;
588
589     if (IsEqualIID(&IID_IUnknown, iid) ||
590         IsEqualIID(&IID_IWICBitmapDecoder, iid))
591     {
592         *ppv = &This->IWICBitmapDecoder_iface;
593     }
594     else
595     {
596         *ppv = NULL;
597         return E_NOINTERFACE;
598     }
599
600     IUnknown_AddRef((IUnknown*)*ppv);
601     return S_OK;
602 }
603
604 static ULONG WINAPI GifDecoder_AddRef(IWICBitmapDecoder *iface)
605 {
606     GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
607     ULONG ref = InterlockedIncrement(&This->ref);
608
609     TRACE("(%p) refcount=%u\n", iface, ref);
610
611     return ref;
612 }
613
614 static ULONG WINAPI GifDecoder_Release(IWICBitmapDecoder *iface)
615 {
616     GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
617     ULONG ref = InterlockedDecrement(&This->ref);
618
619     TRACE("(%p) refcount=%u\n", iface, ref);
620
621     if (ref == 0)
622     {
623         This->lock.DebugInfo->Spare[0] = 0;
624         DeleteCriticalSection(&This->lock);
625         DGifCloseFile(This->gif);
626         HeapFree(GetProcessHeap(), 0, This);
627     }
628
629     return ref;
630 }
631
632 static HRESULT WINAPI GifDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *pIStream,
633     DWORD *pdwCapability)
634 {
635     FIXME("(%p,%p,%p): stub\n", iface, pIStream, pdwCapability);
636     return E_NOTIMPL;
637 }
638
639 static int _gif_inputfunc(GifFileType *gif, GifByteType *data, int len) {
640     IStream *stream = gif->UserData;
641     ULONG bytesread;
642     HRESULT hr;
643
644     if (!stream)
645     {
646         ERR("attempting to read file after initialization\n");
647         return 0;
648     }
649
650     hr = IStream_Read(stream, data, len, &bytesread);
651     if (hr != S_OK) bytesread = 0;
652     return bytesread;
653 }
654
655 static HRESULT WINAPI GifDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream,
656     WICDecodeOptions cacheOptions)
657 {
658     GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
659     LARGE_INTEGER seek;
660     int ret;
661
662     TRACE("(%p,%p,%x)\n", iface, pIStream, cacheOptions);
663
664     EnterCriticalSection(&This->lock);
665
666     if (This->initialized || This->gif)
667     {
668         WARN("already initialized\n");
669         LeaveCriticalSection(&This->lock);
670         return WINCODEC_ERR_WRONGSTATE;
671     }
672
673     /* seek to start of stream */
674     seek.QuadPart = 0;
675     IStream_Seek(pIStream, seek, STREAM_SEEK_SET, NULL);
676
677     /* read all data from the stream */
678     This->gif = DGifOpen((void*)pIStream, _gif_inputfunc);
679     if (!This->gif)
680     {
681         LeaveCriticalSection(&This->lock);
682         return E_FAIL;
683     }
684
685     ret = DGifSlurp(This->gif);
686     if (ret == GIF_ERROR)
687     {
688         LeaveCriticalSection(&This->lock);
689         return E_FAIL;
690     }
691
692     /* make sure we don't use the stream after this method returns */
693     This->gif->UserData = NULL;
694
695     This->initialized = TRUE;
696
697     LeaveCriticalSection(&This->lock);
698
699     return S_OK;
700 }
701
702 static HRESULT WINAPI GifDecoder_GetContainerFormat(IWICBitmapDecoder *iface,
703     GUID *pguidContainerFormat)
704 {
705     memcpy(pguidContainerFormat, &GUID_ContainerFormatGif, sizeof(GUID));
706     return S_OK;
707 }
708
709 static HRESULT WINAPI GifDecoder_GetDecoderInfo(IWICBitmapDecoder *iface,
710     IWICBitmapDecoderInfo **ppIDecoderInfo)
711 {
712     HRESULT hr;
713     IWICComponentInfo *compinfo;
714
715     TRACE("(%p,%p)\n", iface, ppIDecoderInfo);
716
717     hr = CreateComponentInfo(&CLSID_WICGifDecoder, &compinfo);
718     if (FAILED(hr)) return hr;
719
720     hr = IWICComponentInfo_QueryInterface(compinfo, &IID_IWICBitmapDecoderInfo,
721         (void**)ppIDecoderInfo);
722
723     IWICComponentInfo_Release(compinfo);
724
725     return hr;
726 }
727
728 static HRESULT WINAPI GifDecoder_CopyPalette(IWICBitmapDecoder *iface,
729     IWICPalette *pIPalette)
730 {
731     TRACE("(%p,%p)\n", iface, pIPalette);
732     return WINCODEC_ERR_PALETTEUNAVAILABLE;
733 }
734
735 static HRESULT WINAPI GifDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface,
736     IWICMetadataQueryReader **ppIMetadataQueryReader)
737 {
738     TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
739     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
740 }
741
742 static HRESULT WINAPI GifDecoder_GetPreview(IWICBitmapDecoder *iface,
743     IWICBitmapSource **ppIBitmapSource)
744 {
745     TRACE("(%p,%p)\n", iface, ppIBitmapSource);
746     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
747 }
748
749 static HRESULT WINAPI GifDecoder_GetColorContexts(IWICBitmapDecoder *iface,
750     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
751 {
752     TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
753     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
754 }
755
756 static HRESULT WINAPI GifDecoder_GetThumbnail(IWICBitmapDecoder *iface,
757     IWICBitmapSource **ppIThumbnail)
758 {
759     TRACE("(%p,%p)\n", iface, ppIThumbnail);
760     return WINCODEC_ERR_CODECNOTHUMBNAIL;
761 }
762
763 static HRESULT WINAPI GifDecoder_GetFrameCount(IWICBitmapDecoder *iface,
764     UINT *pCount)
765 {
766     GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
767     TRACE("(%p,%p)\n", iface, pCount);
768
769     if (!This->initialized) return WINCODEC_ERR_NOTINITIALIZED;
770
771     *pCount = This->gif->ImageCount;
772
773     TRACE("<- %u\n", *pCount);
774
775     return S_OK;
776 }
777
778 static HRESULT WINAPI GifDecoder_GetFrame(IWICBitmapDecoder *iface,
779     UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
780 {
781     GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
782     GifFrameDecode *result;
783     TRACE("(%p,%u,%p)\n", iface, index, ppIBitmapFrame);
784
785     if (!This->initialized) return WINCODEC_ERR_NOTINITIALIZED;
786
787     if (index >= This->gif->ImageCount) return E_INVALIDARG;
788
789     result = HeapAlloc(GetProcessHeap(), 0, sizeof(GifFrameDecode));
790     if (!result) return E_OUTOFMEMORY;
791
792     result->IWICBitmapFrameDecode_iface.lpVtbl = &GifFrameDecode_Vtbl;
793     result->ref = 1;
794     result->frame = &This->gif->SavedImages[index];
795     IWICBitmapDecoder_AddRef(iface);
796     result->parent = This;
797
798     *ppIBitmapFrame = &result->IWICBitmapFrameDecode_iface;
799
800     return S_OK;
801 }
802
803 static const IWICBitmapDecoderVtbl GifDecoder_Vtbl = {
804     GifDecoder_QueryInterface,
805     GifDecoder_AddRef,
806     GifDecoder_Release,
807     GifDecoder_QueryCapability,
808     GifDecoder_Initialize,
809     GifDecoder_GetContainerFormat,
810     GifDecoder_GetDecoderInfo,
811     GifDecoder_CopyPalette,
812     GifDecoder_GetMetadataQueryReader,
813     GifDecoder_GetPreview,
814     GifDecoder_GetColorContexts,
815     GifDecoder_GetThumbnail,
816     GifDecoder_GetFrameCount,
817     GifDecoder_GetFrame
818 };
819
820 HRESULT GifDecoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
821 {
822     GifDecoder *This;
823     HRESULT ret;
824
825     TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
826
827     *ppv = NULL;
828
829     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
830
831     This = HeapAlloc(GetProcessHeap(), 0, sizeof(GifDecoder));
832     if (!This) return E_OUTOFMEMORY;
833
834     This->IWICBitmapDecoder_iface.lpVtbl = &GifDecoder_Vtbl;
835     This->ref = 1;
836     This->initialized = FALSE;
837     This->gif = NULL;
838     InitializeCriticalSection(&This->lock);
839     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": GifDecoder.lock");
840
841     ret = IWICBitmapDecoder_QueryInterface(&This->IWICBitmapDecoder_iface, iid, ppv);
842     IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
843
844     return ret;
845 }