windowscodecs: Implemented GifFrameDecode_GetResolution.
[wine] / dlls / windowscodecs / icoformat.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
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "objbase.h"
29 #include "wincodec.h"
30
31 #include "wincodecs_private.h"
32
33 #include "wine/debug.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
36
37 #include "pshpack1.h"
38
39 typedef struct {
40     BYTE bWidth;
41     BYTE bHeight;
42     BYTE bColorCount;
43     BYTE bReserved;
44     WORD wPlanes;
45     WORD wBitCount;
46     DWORD dwDIBSize;
47     DWORD dwDIBOffset;
48 } ICONDIRENTRY;
49
50 typedef struct
51 {
52     WORD idReserved;
53     WORD idType;
54     WORD idCount;
55 } ICONHEADER;
56
57 #include "poppack.h"
58
59 typedef struct {
60     const IWICBitmapDecoderVtbl *lpVtbl;
61     LONG ref;
62     BOOL initialized;
63     IStream *stream;
64     ICONHEADER header;
65     CRITICAL_SECTION lock; /* must be held when accessing stream */
66 } IcoDecoder;
67
68 typedef struct {
69     const IWICBitmapFrameDecodeVtbl *lpVtbl;
70     LONG ref;
71     UINT width, height;
72     BYTE *bits;
73 } IcoFrameDecode;
74
75 static HRESULT WINAPI IcoFrameDecode_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
76     void **ppv)
77 {
78     IcoFrameDecode *This = (IcoFrameDecode*)iface;
79     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
80
81     if (!ppv) return E_INVALIDARG;
82
83     if (IsEqualIID(&IID_IUnknown, iid) ||
84         IsEqualIID(&IID_IWICBitmapSource, iid) ||
85         IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
86     {
87         *ppv = This;
88     }
89     else
90     {
91         *ppv = NULL;
92         return E_NOINTERFACE;
93     }
94
95     IUnknown_AddRef((IUnknown*)*ppv);
96     return S_OK;
97 }
98
99 static ULONG WINAPI IcoFrameDecode_AddRef(IWICBitmapFrameDecode *iface)
100 {
101     IcoFrameDecode *This = (IcoFrameDecode*)iface;
102     ULONG ref = InterlockedIncrement(&This->ref);
103
104     TRACE("(%p) refcount=%u\n", iface, ref);
105
106     return ref;
107 }
108
109 static ULONG WINAPI IcoFrameDecode_Release(IWICBitmapFrameDecode *iface)
110 {
111     IcoFrameDecode *This = (IcoFrameDecode*)iface;
112     ULONG ref = InterlockedDecrement(&This->ref);
113
114     TRACE("(%p) refcount=%u\n", iface, ref);
115
116     if (ref == 0)
117     {
118         HeapFree(GetProcessHeap(), 0, This->bits);
119         HeapFree(GetProcessHeap(), 0, This);
120     }
121
122     return ref;
123 }
124
125 static HRESULT WINAPI IcoFrameDecode_GetSize(IWICBitmapFrameDecode *iface,
126     UINT *puiWidth, UINT *puiHeight)
127 {
128     IcoFrameDecode *This = (IcoFrameDecode*)iface;
129
130     *puiWidth = This->width;
131     *puiHeight = This->height;
132
133     TRACE("(%p) -> (%i,%i)\n", iface, *puiWidth, *puiHeight);
134
135     return S_OK;
136 }
137
138 static HRESULT WINAPI IcoFrameDecode_GetPixelFormat(IWICBitmapFrameDecode *iface,
139     WICPixelFormatGUID *pPixelFormat)
140 {
141     memcpy(pPixelFormat, &GUID_WICPixelFormat32bppBGRA, sizeof(GUID));
142     return S_OK;
143 }
144
145 static HRESULT WINAPI IcoFrameDecode_GetResolution(IWICBitmapFrameDecode *iface,
146     double *pDpiX, double *pDpiY)
147 {
148     FIXME("(%p,%p,%p): stub\n", iface, pDpiX, pDpiY);
149     return E_NOTIMPL;
150 }
151
152 static HRESULT WINAPI IcoFrameDecode_CopyPalette(IWICBitmapFrameDecode *iface,
153     IWICPalette *pIPalette)
154 {
155     TRACE("(%p,%p)\n", iface, pIPalette);
156     return WINCODEC_ERR_PALETTEUNAVAILABLE;
157 }
158
159 static HRESULT WINAPI IcoFrameDecode_CopyPixels(IWICBitmapFrameDecode *iface,
160     const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
161 {
162     IcoFrameDecode *This = (IcoFrameDecode*)iface;
163     TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
164
165     return copy_pixels(32, This->bits, This->width, This->height, This->width * 4,
166         prc, cbStride, cbBufferSize, pbBuffer);
167 }
168
169 static HRESULT WINAPI IcoFrameDecode_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
170     IWICMetadataQueryReader **ppIMetadataQueryReader)
171 {
172     TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
173     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
174 }
175
176 static HRESULT WINAPI IcoFrameDecode_GetColorContexts(IWICBitmapFrameDecode *iface,
177     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
178 {
179     TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
180     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
181 }
182
183 static HRESULT WINAPI IcoFrameDecode_GetThumbnail(IWICBitmapFrameDecode *iface,
184     IWICBitmapSource **ppIThumbnail)
185 {
186     TRACE("(%p,%p)\n", iface, ppIThumbnail);
187     return WINCODEC_ERR_CODECNOTHUMBNAIL;
188 }
189
190 static const IWICBitmapFrameDecodeVtbl IcoFrameDecode_Vtbl = {
191     IcoFrameDecode_QueryInterface,
192     IcoFrameDecode_AddRef,
193     IcoFrameDecode_Release,
194     IcoFrameDecode_GetSize,
195     IcoFrameDecode_GetPixelFormat,
196     IcoFrameDecode_GetResolution,
197     IcoFrameDecode_CopyPalette,
198     IcoFrameDecode_CopyPixels,
199     IcoFrameDecode_GetMetadataQueryReader,
200     IcoFrameDecode_GetColorContexts,
201     IcoFrameDecode_GetThumbnail
202 };
203
204 static inline void pixel_set_trans(DWORD* pixel, BOOL transparent)
205 {
206     if (transparent) *pixel = 0;
207     else *pixel |= 0xff000000;
208 }
209
210 static HRESULT ReadIcoDib(IStream *stream, IcoFrameDecode *result)
211 {
212     HRESULT hr;
213     IWICBitmapDecoder *decoder;
214     IWICBitmapFrameDecode *framedecode;
215     WICPixelFormatGUID pixelformat;
216     IWICBitmapSource *source;
217     int has_alpha=FALSE; /* if TRUE, alpha data might be in the image data */
218     WICRect rc;
219
220     hr = IcoDibDecoder_CreateInstance(NULL, &IID_IWICBitmapDecoder, (void**)&decoder);
221     if (SUCCEEDED(hr))
222     {
223         hr = IWICBitmapDecoder_Initialize(decoder, stream, WICDecodeMetadataCacheOnLoad);
224
225         if (SUCCEEDED(hr))
226             hr = IWICBitmapDecoder_GetFrame(decoder, 0, &framedecode);
227
228         if (SUCCEEDED(hr))
229         {
230             hr = IWICBitmapFrameDecode_GetSize(framedecode, &result->width, &result->height);
231
232             if (SUCCEEDED(hr))
233             {
234                 result->bits = HeapAlloc(GetProcessHeap(), 0, result->width * result->height * 4);
235                 if (!result->bits) hr = E_OUTOFMEMORY;
236             }
237
238             if (SUCCEEDED(hr))
239                 hr = IWICBitmapFrameDecode_GetPixelFormat(framedecode, &pixelformat);
240
241             if (IsEqualGUID(&pixelformat, &GUID_WICPixelFormat32bppBGR) ||
242                 IsEqualGUID(&pixelformat, &GUID_WICPixelFormat32bppBGRA))
243             {
244                 source = (IWICBitmapSource*)framedecode;
245                 IWICBitmapSource_AddRef(source);
246                 has_alpha = TRUE;
247             }
248             else
249             {
250                 hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA,
251                     (IWICBitmapSource*)framedecode, &source);
252                 has_alpha = FALSE;
253             }
254
255             if (SUCCEEDED(hr))
256             {
257                 rc.X = 0;
258                 rc.Y = 0;
259                 rc.Width = result->width;
260                 rc.Height = result->height;
261                 hr = IWICBitmapSource_CopyPixels(source, &rc, result->width * 4,
262                     result->width * result->height * 4, result->bits);
263
264                 IWICBitmapSource_Release(source);
265             }
266
267             IWICBitmapFrameDecode_Release(framedecode);
268         }
269
270         if (SUCCEEDED(hr) && !has_alpha)
271         {
272             /* set alpha data based on the AND mask */
273             UINT andBytesPerRow = (result->width+31)/32*4;
274             UINT andBytes = andBytesPerRow * result->height;
275             INT andStride;
276             BYTE *tempdata=NULL;
277             BYTE *andRow;
278             BYTE *bitsRow;
279             UINT bitsStride = result->width * 4;
280             UINT x, y;
281             ULONG offset;
282             ULONG bytesread;
283             LARGE_INTEGER seek;
284             int topdown;
285
286             BmpDecoder_FindIconMask(decoder, &offset, &topdown);
287
288             if (offset)
289             {
290                 seek.QuadPart = offset;
291
292                 hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, 0);
293
294                 if (SUCCEEDED(hr))
295                 {
296                     tempdata = HeapAlloc(GetProcessHeap(), 0, andBytes);
297                     if (!tempdata) hr = E_OUTOFMEMORY;
298                 }
299
300                 if (SUCCEEDED(hr))
301                     hr = IStream_Read(stream, tempdata, andBytes, &bytesread);
302
303                 if (SUCCEEDED(hr) && bytesread == andBytes)
304                 {
305                     if (topdown)
306                     {
307                         andStride = andBytesPerRow;
308                         andRow = tempdata;
309                     }
310                     else
311                     {
312                         andStride = -andBytesPerRow;
313                         andRow = tempdata + (result->height-1)*andBytesPerRow;
314                     }
315
316                     bitsRow = result->bits;
317                     for (y=0; y<result->height; y++) {
318                         BYTE *andByte=andRow;
319                         DWORD *bitsPixel=(DWORD*)bitsRow;
320                         for (x=0; x<result->width; x+=8) {
321                             BYTE andVal=*andByte++;
322                             pixel_set_trans(bitsPixel++, andVal>>7&1);
323                             if (x+1 < result->width) pixel_set_trans(bitsPixel++, andVal>>6&1);
324                             if (x+2 < result->width) pixel_set_trans(bitsPixel++, andVal>>5&1);
325                             if (x+3 < result->width) pixel_set_trans(bitsPixel++, andVal>>4&1);
326                             if (x+4 < result->width) pixel_set_trans(bitsPixel++, andVal>>3&1);
327                             if (x+5 < result->width) pixel_set_trans(bitsPixel++, andVal>>2&1);
328                             if (x+6 < result->width) pixel_set_trans(bitsPixel++, andVal>>1&1);
329                             if (x+7 < result->width) pixel_set_trans(bitsPixel++, andVal&1);
330                         }
331                         andRow += andStride;
332                         bitsRow += bitsStride;
333                     }
334                 }
335
336                 HeapFree(GetProcessHeap(), 0, tempdata);
337             }
338         }
339
340         IWICBitmapDecoder_Release(decoder);
341     }
342
343     return hr;
344 }
345
346 static HRESULT ReadIcoPng(IStream *stream, IcoFrameDecode *result)
347 {
348     IWICBitmapDecoder *decoder = NULL;
349     IWICBitmapFrameDecode *sourceFrame = NULL;
350     IWICBitmapSource *sourceBitmap = NULL;
351     WICRect rect;
352     HRESULT hr;
353
354     hr = PngDecoder_CreateInstance(NULL, &IID_IWICBitmapDecoder, (void**)&decoder);
355     if (FAILED(hr))
356         goto end;
357     hr = IWICBitmapDecoder_Initialize(decoder, stream, WICDecodeMetadataCacheOnLoad);
358     if (FAILED(hr))
359         goto end;
360     hr = IWICBitmapDecoder_GetFrame(decoder, 0, &sourceFrame);
361     if (FAILED(hr))
362         goto end;
363     hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, (IWICBitmapSource*)sourceFrame, &sourceBitmap);
364     if (FAILED(hr))
365         goto end;
366     hr = IWICBitmapFrameDecode_GetSize(sourceFrame, &result->width, &result->height);
367     if (FAILED(hr))
368         goto end;
369     result->bits = HeapAlloc(GetProcessHeap(), 0, 4 * result->width * result->height);
370     if (result->bits == NULL)
371     {
372         hr = E_OUTOFMEMORY;
373         goto end;
374     }
375     rect.X = 0;
376     rect.Y = 0;
377     rect.Width = result->width;
378     rect.Height = result->height;
379     hr = IWICBitmapSource_CopyPixels(sourceBitmap, &rect, 4*result->width,
380                                      4*result->width*result->height, result->bits);
381
382 end:
383     if (decoder != NULL)
384         IWICBitmapDecoder_Release(decoder);
385     if (sourceFrame != NULL)
386         IWICBitmapFrameDecode_Release(sourceFrame);
387     if (sourceBitmap != NULL)
388         IWICBitmapSource_Release(sourceBitmap);
389     return hr;
390 }
391
392 static HRESULT WINAPI IcoDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid,
393     void **ppv)
394 {
395     IcoDecoder *This = (IcoDecoder*)iface;
396     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
397
398     if (!ppv) return E_INVALIDARG;
399
400     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IWICBitmapDecoder, iid))
401     {
402         *ppv = This;
403     }
404     else
405     {
406         *ppv = NULL;
407         return E_NOINTERFACE;
408     }
409
410     IUnknown_AddRef((IUnknown*)*ppv);
411     return S_OK;
412 }
413
414 static ULONG WINAPI IcoDecoder_AddRef(IWICBitmapDecoder *iface)
415 {
416     IcoDecoder *This = (IcoDecoder*)iface;
417     ULONG ref = InterlockedIncrement(&This->ref);
418
419     TRACE("(%p) refcount=%u\n", iface, ref);
420
421     return ref;
422 }
423
424 static ULONG WINAPI IcoDecoder_Release(IWICBitmapDecoder *iface)
425 {
426     IcoDecoder *This = (IcoDecoder*)iface;
427     ULONG ref = InterlockedDecrement(&This->ref);
428
429     TRACE("(%p) refcount=%u\n", iface, ref);
430
431     if (ref == 0)
432     {
433         This->lock.DebugInfo->Spare[0] = 0;
434         DeleteCriticalSection(&This->lock);
435         if (This->stream) IStream_Release(This->stream);
436         HeapFree(GetProcessHeap(), 0, This);
437     }
438
439     return ref;
440 }
441
442 static HRESULT WINAPI IcoDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *pIStream,
443     DWORD *pdwCapability)
444 {
445     FIXME("(%p,%p,%p): stub\n", iface, pIStream, pdwCapability);
446     return E_NOTIMPL;
447 }
448
449 static HRESULT WINAPI IcoDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream,
450     WICDecodeOptions cacheOptions)
451 {
452     IcoDecoder *This = (IcoDecoder*)iface;
453     LARGE_INTEGER seek;
454     HRESULT hr;
455     ULONG bytesread;
456     TRACE("(%p,%p,%x)\n", iface, pIStream, cacheOptions);
457
458     EnterCriticalSection(&This->lock);
459
460     if (This->initialized)
461     {
462         hr = WINCODEC_ERR_WRONGSTATE;
463         goto end;
464     }
465
466     seek.QuadPart = 0;
467     hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, NULL);
468     if (FAILED(hr)) goto end;
469
470     hr = IStream_Read(pIStream, &This->header, sizeof(ICONHEADER), &bytesread);
471     if (FAILED(hr)) goto end;
472     if (bytesread != sizeof(ICONHEADER) ||
473         This->header.idReserved != 0 ||
474         This->header.idType != 1)
475     {
476         hr = E_FAIL;
477         goto end;
478     }
479
480     This->initialized = TRUE;
481     This->stream = pIStream;
482     IStream_AddRef(pIStream);
483
484 end:
485
486     LeaveCriticalSection(&This->lock);
487
488     return hr;
489 }
490
491 static HRESULT WINAPI IcoDecoder_GetContainerFormat(IWICBitmapDecoder *iface,
492     GUID *pguidContainerFormat)
493 {
494     FIXME("(%p,%p): stub\n", iface, pguidContainerFormat);
495     return E_NOTIMPL;
496 }
497
498 static HRESULT WINAPI IcoDecoder_GetDecoderInfo(IWICBitmapDecoder *iface,
499     IWICBitmapDecoderInfo **ppIDecoderInfo)
500 {
501     FIXME("(%p,%p): stub\n", iface, ppIDecoderInfo);
502     return E_NOTIMPL;
503 }
504
505 static HRESULT WINAPI IcoDecoder_CopyPalette(IWICBitmapDecoder *iface,
506     IWICPalette *pIPalette)
507 {
508     TRACE("(%p,%p)\n", iface, pIPalette);
509     return WINCODEC_ERR_PALETTEUNAVAILABLE;
510 }
511
512 static HRESULT WINAPI IcoDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface,
513     IWICMetadataQueryReader **ppIMetadataQueryReader)
514 {
515     TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
516     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
517 }
518
519 static HRESULT WINAPI IcoDecoder_GetPreview(IWICBitmapDecoder *iface,
520     IWICBitmapSource **ppIBitmapSource)
521 {
522     TRACE("(%p,%p)\n", iface, ppIBitmapSource);
523     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
524 }
525
526 static HRESULT WINAPI IcoDecoder_GetColorContexts(IWICBitmapDecoder *iface,
527     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
528 {
529     TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
530     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
531 }
532
533 static HRESULT WINAPI IcoDecoder_GetThumbnail(IWICBitmapDecoder *iface,
534     IWICBitmapSource **ppIThumbnail)
535 {
536     TRACE("(%p,%p)\n", iface, ppIThumbnail);
537     return WINCODEC_ERR_CODECNOTHUMBNAIL;
538 }
539
540 static HRESULT WINAPI IcoDecoder_GetFrameCount(IWICBitmapDecoder *iface,
541     UINT *pCount)
542 {
543     IcoDecoder *This = (IcoDecoder*)iface;
544     TRACE("(%p,%p)\n", iface, pCount);
545
546     if (!This->initialized) return WINCODEC_ERR_NOTINITIALIZED;
547
548     *pCount = This->header.idCount;
549     TRACE("<-- %u\n", *pCount);
550
551     return S_OK;
552 }
553
554 static HRESULT WINAPI IcoDecoder_GetFrame(IWICBitmapDecoder *iface,
555     UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
556 {
557     IcoDecoder *This = (IcoDecoder*)iface;
558     IcoFrameDecode *result=NULL;
559     LARGE_INTEGER seek;
560     ULARGE_INTEGER offset, length;
561     HRESULT hr;
562     ULONG bytesread;
563     ICONDIRENTRY entry;
564     IWICStream *substream=NULL;
565     DWORD magic;
566     TRACE("(%p,%u,%p)\n", iface, index, ppIBitmapFrame);
567
568     EnterCriticalSection(&This->lock);
569
570     if (!This->initialized)
571     {
572         hr = WINCODEC_ERR_NOTINITIALIZED;
573         goto fail;
574     }
575
576     if (This->header.idCount < index)
577     {
578         hr = E_INVALIDARG;
579         goto fail;
580     }
581
582     result = HeapAlloc(GetProcessHeap(), 0, sizeof(IcoFrameDecode));
583     if (!result)
584     {
585         hr = E_OUTOFMEMORY;
586         goto fail;
587     }
588
589     result->lpVtbl = &IcoFrameDecode_Vtbl;
590     result->ref = 1;
591     result->bits = NULL;
592
593     /* read the icon entry */
594     seek.QuadPart = sizeof(ICONHEADER) + sizeof(ICONDIRENTRY) * index;
595     hr = IStream_Seek(This->stream, seek, STREAM_SEEK_SET, 0);
596     if (FAILED(hr)) goto fail;
597
598     hr = IStream_Read(This->stream, &entry, sizeof(ICONDIRENTRY), &bytesread);
599     if (FAILED(hr) || bytesread != sizeof(ICONDIRENTRY)) goto fail;
600
601     /* create a stream object for this icon */
602     hr = StreamImpl_Create(&substream);
603     if (FAILED(hr)) goto fail;
604
605     offset.QuadPart = entry.dwDIBOffset;
606     length.QuadPart = entry.dwDIBSize;
607     hr = IWICStream_InitializeFromIStreamRegion(substream, This->stream, offset, length);
608     if (FAILED(hr)) goto fail;
609
610     /* read the bitmapinfo size or magic number */
611     hr = IWICStream_Read(substream, &magic, sizeof(magic), &bytesread);
612     if (FAILED(hr) || bytesread != sizeof(magic)) goto fail;
613
614     /* forward to the appropriate decoding function based on the magic number */
615     switch (magic)
616     {
617     case sizeof(BITMAPCOREHEADER):
618     case 64: /* sizeof(BITMAPCOREHEADER2) */
619     case sizeof(BITMAPINFOHEADER):
620     case sizeof(BITMAPV4HEADER):
621     case sizeof(BITMAPV5HEADER):
622         hr = ReadIcoDib((IStream*)substream, result);
623         break;
624     case 0x474e5089:
625         hr = ReadIcoPng((IStream*)substream, result);
626         break;
627     default:
628         FIXME("Unrecognized ICO frame magic: %x\n", magic);
629         hr = E_FAIL;
630         break;
631     }
632     if (FAILED(hr)) goto fail;
633
634     *ppIBitmapFrame = (IWICBitmapFrameDecode*)result;
635
636     LeaveCriticalSection(&This->lock);
637
638     return S_OK;
639
640 fail:
641     LeaveCriticalSection(&This->lock);
642     HeapFree(GetProcessHeap(), 0, result);
643     if (substream) IStream_Release(substream);
644     if (SUCCEEDED(hr)) hr = E_FAIL;
645     TRACE("<-- %x\n", hr);
646     return hr;
647 }
648
649 static const IWICBitmapDecoderVtbl IcoDecoder_Vtbl = {
650     IcoDecoder_QueryInterface,
651     IcoDecoder_AddRef,
652     IcoDecoder_Release,
653     IcoDecoder_QueryCapability,
654     IcoDecoder_Initialize,
655     IcoDecoder_GetContainerFormat,
656     IcoDecoder_GetDecoderInfo,
657     IcoDecoder_CopyPalette,
658     IcoDecoder_GetMetadataQueryReader,
659     IcoDecoder_GetPreview,
660     IcoDecoder_GetColorContexts,
661     IcoDecoder_GetThumbnail,
662     IcoDecoder_GetFrameCount,
663     IcoDecoder_GetFrame
664 };
665
666 HRESULT IcoDecoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
667 {
668     IcoDecoder *This;
669     HRESULT ret;
670
671     TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
672
673     *ppv = NULL;
674
675     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
676
677     This = HeapAlloc(GetProcessHeap(), 0, sizeof(IcoDecoder));
678     if (!This) return E_OUTOFMEMORY;
679
680     This->lpVtbl = &IcoDecoder_Vtbl;
681     This->ref = 1;
682     This->stream = NULL;
683     This->initialized = FALSE;
684     InitializeCriticalSection(&This->lock);
685     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": IcoDecoder.lock");
686
687     ret = IUnknown_QueryInterface((IUnknown*)This, iid, ppv);
688     IUnknown_Release((IUnknown*)This);
689
690     return ret;
691 }