wined3d: Fix typo in checkGLcall.
[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     ICONDIRENTRY entry;
72     IcoDecoder *parent;
73     BYTE *bits;
74 } IcoFrameDecode;
75
76 static HRESULT WINAPI IcoFrameDecode_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
77     void **ppv)
78 {
79     IcoFrameDecode *This = (IcoFrameDecode*)iface;
80     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
81
82     if (!ppv) return E_INVALIDARG;
83
84     if (IsEqualIID(&IID_IUnknown, iid) ||
85         IsEqualIID(&IID_IWICBitmapSource, iid) ||
86         IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
87     {
88         *ppv = This;
89     }
90     else
91     {
92         *ppv = NULL;
93         return E_NOINTERFACE;
94     }
95
96     IUnknown_AddRef((IUnknown*)*ppv);
97     return S_OK;
98 }
99
100 static ULONG WINAPI IcoFrameDecode_AddRef(IWICBitmapFrameDecode *iface)
101 {
102     IcoFrameDecode *This = (IcoFrameDecode*)iface;
103     ULONG ref = InterlockedIncrement(&This->ref);
104
105     TRACE("(%p) refcount=%u\n", iface, ref);
106
107     return ref;
108 }
109
110 static ULONG WINAPI IcoFrameDecode_Release(IWICBitmapFrameDecode *iface)
111 {
112     IcoFrameDecode *This = (IcoFrameDecode*)iface;
113     ULONG ref = InterlockedDecrement(&This->ref);
114
115     TRACE("(%p) refcount=%u\n", iface, ref);
116
117     if (ref == 0)
118     {
119         IUnknown_Release((IUnknown*)This->parent);
120         HeapFree(GetProcessHeap(), 0, This->bits);
121         HeapFree(GetProcessHeap(), 0, This);
122     }
123
124     return ref;
125 }
126
127 static HRESULT WINAPI IcoFrameDecode_GetSize(IWICBitmapFrameDecode *iface,
128     UINT *puiWidth, UINT *puiHeight)
129 {
130     IcoFrameDecode *This = (IcoFrameDecode*)iface;
131
132     *puiWidth = This->entry.bWidth ? This->entry.bWidth : 256;
133     *puiHeight = This->entry.bHeight ? This->entry.bHeight : 256;
134
135     TRACE("(%p) -> (%i,%i)\n", iface, *puiWidth, *puiHeight);
136
137     return S_OK;
138 }
139
140 static HRESULT WINAPI IcoFrameDecode_GetPixelFormat(IWICBitmapFrameDecode *iface,
141     WICPixelFormatGUID *pPixelFormat)
142 {
143     memcpy(pPixelFormat, &GUID_WICPixelFormat32bppBGRA, sizeof(GUID));
144     return S_OK;
145 }
146
147 static HRESULT WINAPI IcoFrameDecode_GetResolution(IWICBitmapFrameDecode *iface,
148     double *pDpiX, double *pDpiY)
149 {
150     FIXME("(%p,%p,%p): stub\n", iface, pDpiX, pDpiY);
151     return E_NOTIMPL;
152 }
153
154 static HRESULT WINAPI IcoFrameDecode_CopyPalette(IWICBitmapFrameDecode *iface,
155     IWICPalette *pIPalette)
156 {
157     TRACE("(%p,%p)\n", iface, pIPalette);
158     return WINCODEC_ERR_PALETTEUNAVAILABLE;
159 }
160
161 static inline void pixel_set_trans(DWORD* pixel, BOOL transparent)
162 {
163     if (transparent) *pixel = 0;
164     else *pixel |= 0xff000000;
165 }
166
167 static HRESULT IcoFrameDecode_ReadPixels(IcoFrameDecode *This)
168 {
169     BITMAPINFOHEADER bih;
170     DWORD colors[256];
171     UINT colorcount=0;
172     LARGE_INTEGER seek;
173     ULONG bytesread;
174     HRESULT hr;
175     BYTE *tempdata = NULL;
176     BYTE *bits = NULL;
177     UINT bitsStride;
178     UINT bitsSize;
179     UINT width, height;
180
181     width = This->entry.bWidth ? This->entry.bWidth : 256;
182     height = This->entry.bHeight ? This->entry.bHeight : 256;
183
184     /* read the BITMAPINFOHEADER */
185     seek.QuadPart = This->entry.dwDIBOffset;
186     hr = IStream_Seek(This->parent->stream, seek, STREAM_SEEK_SET, NULL);
187     if (FAILED(hr)) goto fail;
188
189     hr = IStream_Read(This->parent->stream, &bih, sizeof(BITMAPINFOHEADER), &bytesread);
190     if (FAILED(hr) || bytesread != sizeof(BITMAPINFOHEADER)) goto fail;
191
192     if (bih.biBitCount <= 8)
193     {
194         /* read the palette */
195         colorcount = bih.biClrUsed ? bih.biClrUsed : 1 << bih.biBitCount;
196
197         hr = IStream_Read(This->parent->stream, colors, sizeof(RGBQUAD)*colorcount, &bytesread);
198         if (FAILED(hr) || bytesread != sizeof(RGBQUAD)*colorcount) goto fail;
199     }
200
201     bitsStride = width * 4;
202     bitsSize = bitsStride * height;
203
204     /* read the XOR data */
205     switch (bih.biBitCount)
206     {
207     case 1:
208     {
209         UINT xorBytesPerRow = (width+31)/32*4;
210         UINT xorBytes = xorBytesPerRow * height;
211         INT xorStride;
212         BYTE *xorRow;
213         BYTE *bitsRow;
214         UINT x, y;
215
216         tempdata = HeapAlloc(GetProcessHeap(), 0, xorBytes);
217         if (!tempdata)
218         {
219             hr = E_OUTOFMEMORY;
220             goto fail;
221         }
222
223         hr = IStream_Read(This->parent->stream, tempdata, xorBytes, &bytesread);
224         if (FAILED(hr) || bytesread != xorBytes) goto fail;
225
226         if (bih.biHeight > 0) /* bottom-up DIB */
227         {
228             xorStride = -xorBytesPerRow;
229             xorRow = tempdata + (height-1)*xorBytesPerRow;
230         }
231         else /* top-down DIB */
232         {
233             xorStride = xorBytesPerRow;
234             xorRow = tempdata;
235         }
236
237         bits = HeapAlloc(GetProcessHeap(), 0, bitsSize);
238
239         /* palette-map the 1-bit data */
240         bitsRow = bits;
241         for (y=0; y<height; y++) {
242             BYTE *xorByte=xorRow;
243             DWORD *bitsPixel=(DWORD*)bitsRow;
244             for (x=0; x<width; x+=8) {
245                 BYTE xorVal;
246                 xorVal=*xorByte++;
247                 *bitsPixel++ = colors[xorVal>>7];
248                 if (x+1 < width) *bitsPixel++ = colors[xorVal>>6&1];
249                 if (x+2 < width) *bitsPixel++ = colors[xorVal>>5&1];
250                 if (x+3 < width) *bitsPixel++ = colors[xorVal>>4&1];
251                 if (x+4 < width) *bitsPixel++ = colors[xorVal>>3&1];
252                 if (x+5 < width) *bitsPixel++ = colors[xorVal>>2&1];
253                 if (x+6 < width) *bitsPixel++ = colors[xorVal>>1&1];
254                 if (x+7 < width) *bitsPixel++ = colors[xorVal&1];
255             }
256             xorRow += xorStride;
257             bitsRow += bitsStride;
258         }
259
260         HeapFree(GetProcessHeap(), 0, tempdata);
261         break;
262     }
263     case 4:
264     {
265         UINT xorBytesPerRow = (width+7)/8*4;
266         UINT xorBytes = xorBytesPerRow * height;
267         INT xorStride;
268         BYTE *xorRow;
269         BYTE *bitsRow;
270         UINT x, y;
271
272         tempdata = HeapAlloc(GetProcessHeap(), 0, xorBytes);
273         if (!tempdata)
274         {
275             hr = E_OUTOFMEMORY;
276             goto fail;
277         }
278
279         hr = IStream_Read(This->parent->stream, tempdata, xorBytes, &bytesread);
280         if (FAILED(hr) || bytesread != xorBytes) goto fail;
281
282         if (bih.biHeight > 0) /* bottom-up DIB */
283         {
284             xorStride = -xorBytesPerRow;
285             xorRow = tempdata + (height-1)*xorBytesPerRow;
286         }
287         else /* top-down DIB */
288         {
289             xorStride = xorBytesPerRow;
290             xorRow = tempdata;
291         }
292
293         bits = HeapAlloc(GetProcessHeap(), 0, bitsSize);
294
295         /* palette-map the 4-bit data */
296         bitsRow = bits;
297         for (y=0; y<height; y++) {
298             BYTE *xorByte=xorRow;
299             DWORD *bitsPixel=(DWORD*)bitsRow;
300             for (x=0; x<width; x+=2) {
301                 BYTE xorVal;
302                 xorVal=*xorByte++;
303                 *bitsPixel++ = colors[xorVal>>4];
304                 if (x+1 < width) *bitsPixel++ = colors[xorVal&0xf];
305             }
306             xorRow += xorStride;
307             bitsRow += bitsStride;
308         }
309
310         HeapFree(GetProcessHeap(), 0, tempdata);
311         break;
312     }
313     case 8:
314     {
315         UINT xorBytesPerRow = (width+3)/4*4;
316         UINT xorBytes = xorBytesPerRow * height;
317         INT xorStride;
318         BYTE *xorRow;
319         BYTE *bitsRow;
320         UINT x, y;
321
322         tempdata = HeapAlloc(GetProcessHeap(), 0, xorBytes);
323         if (!tempdata)
324         {
325             hr = E_OUTOFMEMORY;
326             goto fail;
327         }
328
329         hr = IStream_Read(This->parent->stream, tempdata, xorBytes, &bytesread);
330         if (FAILED(hr) || bytesread != xorBytes) goto fail;
331
332         if (bih.biHeight > 0) /* bottom-up DIB */
333         {
334             xorStride = -xorBytesPerRow;
335             xorRow = tempdata + (height-1)*xorBytesPerRow;
336         }
337         else /* top-down DIB */
338         {
339             xorStride = xorBytesPerRow;
340             xorRow = tempdata;
341         }
342
343         bits = HeapAlloc(GetProcessHeap(), 0, bitsSize);
344
345         /* palette-map the 8-bit data */
346         bitsRow = bits;
347         for (y=0; y<height; y++) {
348             BYTE *xorByte=xorRow;
349             DWORD *bitsPixel=(DWORD*)bitsRow;
350             for (x=0; x<width; x++)
351                 *bitsPixel++ = colors[*xorByte++];
352             xorRow += xorStride;
353             bitsRow += bitsStride;
354         }
355
356         HeapFree(GetProcessHeap(), 0, tempdata);
357         break;
358     }
359     case 24:
360     {
361         UINT xorBytesPerRow = (width*3+3)/4*4;
362         UINT xorBytes = xorBytesPerRow * height;
363         INT xorStride;
364         BYTE *xorRow;
365         BYTE *bitsRow;
366         UINT x, y;
367
368         tempdata = HeapAlloc(GetProcessHeap(), 0, xorBytes);
369         if (!tempdata)
370         {
371             hr = E_OUTOFMEMORY;
372             goto fail;
373         }
374
375         hr = IStream_Read(This->parent->stream, tempdata, xorBytes, &bytesread);
376         if (FAILED(hr) || bytesread != xorBytes) goto fail;
377
378         if (bih.biHeight > 0) /* bottom-up DIB */
379         {
380             xorStride = -xorBytesPerRow;
381             xorRow = tempdata + (height-1)*xorBytesPerRow;
382         }
383         else /* top-down DIB */
384         {
385             xorStride = xorBytesPerRow;
386             xorRow = tempdata;
387         }
388
389         bits = HeapAlloc(GetProcessHeap(), 0, bitsSize);
390
391         /* copy BGR->BGRA */
392         bitsRow = bits;
393         for (y=0; y<height; y++) {
394             BYTE *xorByte=xorRow;
395             BYTE *bitsByte=bitsRow;
396             for (x=0; x<width; x++)
397             {
398                 *bitsByte++ = *xorByte++; /* blue */
399                 *bitsByte++ = *xorByte++; /* green */
400                 *bitsByte++ = *xorByte++; /* red */
401                 bitsByte++; /* alpha */
402             }
403             xorRow += xorStride;
404             bitsRow += bitsStride;
405         }
406
407         HeapFree(GetProcessHeap(), 0, tempdata);
408         break;
409     }
410     case 32:
411     {
412         UINT xorBytesPerRow = width*4;
413         UINT xorBytes = xorBytesPerRow * height;
414
415         bits = HeapAlloc(GetProcessHeap(), 0, xorBytes);
416         if (!bits)
417         {
418             hr = E_OUTOFMEMORY;
419             goto fail;
420         }
421
422         if (bih.biHeight > 0) /* bottom-up DIB */
423         {
424             /* read the rows backwards so we get a top-down DIB */
425             UINT i;
426             BYTE *xorRow = bits + xorBytesPerRow * (height-1);
427
428             for (i=0; i<height; i++)
429             {
430                 hr = IStream_Read(This->parent->stream, xorRow, xorBytesPerRow, &bytesread);
431                 if (FAILED(hr) || bytesread != xorBytesPerRow) goto fail;
432                 xorRow -= xorBytesPerRow;
433             }
434         }
435         else /* top-down DIB */
436         {
437             hr = IStream_Read(This->parent->stream, bits, xorBytes, &bytesread);
438             if (FAILED(hr) || bytesread != xorBytes) goto fail;
439         }
440         break;
441     }
442     default:
443         FIXME("unsupported bitcount: %u\n", bih.biBitCount);
444         goto fail;
445     }
446
447     if (bih.biBitCount < 32)
448     {
449         /* set alpha data based on the AND mask */
450         UINT andBytesPerRow = (width+31)/32*4;
451         UINT andBytes = andBytesPerRow * height;
452         INT andStride;
453         BYTE *andRow;
454         BYTE *bitsRow;
455         UINT x, y;
456
457         tempdata = HeapAlloc(GetProcessHeap(), 0, andBytes);
458         if (!tempdata)
459         {
460             hr = E_OUTOFMEMORY;
461             goto fail;
462         }
463
464         hr = IStream_Read(This->parent->stream, tempdata, andBytes, &bytesread);
465         if (FAILED(hr) || bytesread != andBytes) goto fail;
466
467         if (bih.biHeight > 0) /* bottom-up DIB */
468         {
469             andStride = -andBytesPerRow;
470             andRow = tempdata + (height-1)*andBytesPerRow;
471         }
472         else /* top-down DIB */
473         {
474             andStride = andBytesPerRow;
475             andRow = tempdata;
476         }
477
478         bitsRow = bits;
479         for (y=0; y<height; y++) {
480             BYTE *andByte=andRow;
481             DWORD *bitsPixel=(DWORD*)bitsRow;
482             for (x=0; x<width; x+=8) {
483                 BYTE andVal=*andByte++;
484                 pixel_set_trans(bitsPixel++, andVal>>7&1);
485                 if (x+1 < width) pixel_set_trans(bitsPixel++, andVal>>6&1);
486                 if (x+2 < width) pixel_set_trans(bitsPixel++, andVal>>5&1);
487                 if (x+3 < width) pixel_set_trans(bitsPixel++, andVal>>4&1);
488                 if (x+4 < width) pixel_set_trans(bitsPixel++, andVal>>3&1);
489                 if (x+5 < width) pixel_set_trans(bitsPixel++, andVal>>2&1);
490                 if (x+6 < width) pixel_set_trans(bitsPixel++, andVal>>1&1);
491                 if (x+7 < width) pixel_set_trans(bitsPixel++, andVal&1);
492             }
493             andRow += andStride;
494             bitsRow += bitsStride;
495         }
496
497         HeapFree(GetProcessHeap(), 0, tempdata);
498     }
499
500     This->bits = bits;
501
502     return S_OK;
503
504 fail:
505     HeapFree(GetProcessHeap(), 0, tempdata);
506     HeapFree(GetProcessHeap(), 0, bits);
507     if (SUCCEEDED(hr)) hr = E_FAIL;
508     TRACE("<-- %x\n", hr);
509     return hr;
510 }
511
512 static HRESULT WINAPI IcoFrameDecode_CopyPixels(IWICBitmapFrameDecode *iface,
513     const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
514 {
515     IcoFrameDecode *This = (IcoFrameDecode*)iface;
516     HRESULT hr=S_OK;
517     UINT width, height, stride;
518     TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
519
520     EnterCriticalSection(&This->parent->lock);
521     if (!This->bits)
522     {
523         hr = IcoFrameDecode_ReadPixels(This);
524     }
525     LeaveCriticalSection(&This->parent->lock);
526     if (FAILED(hr)) return hr;
527
528     width = This->entry.bWidth ? This->entry.bWidth : 256;
529     height = This->entry.bHeight ? This->entry.bHeight : 256;
530     stride = width * 4;
531
532     return copy_pixels(32, This->bits, width, height, stride,
533         prc, cbStride, cbBufferSize, pbBuffer);
534 }
535
536 static HRESULT WINAPI IcoFrameDecode_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
537     IWICMetadataQueryReader **ppIMetadataQueryReader)
538 {
539     TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
540     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
541 }
542
543 static HRESULT WINAPI IcoFrameDecode_GetColorContexts(IWICBitmapFrameDecode *iface,
544     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
545 {
546     TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
547     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
548 }
549
550 static HRESULT WINAPI IcoFrameDecode_GetThumbnail(IWICBitmapFrameDecode *iface,
551     IWICBitmapSource **ppIThumbnail)
552 {
553     TRACE("(%p,%p)\n", iface, ppIThumbnail);
554     return WINCODEC_ERR_CODECNOTHUMBNAIL;
555 }
556
557 static const IWICBitmapFrameDecodeVtbl IcoFrameDecode_Vtbl = {
558     IcoFrameDecode_QueryInterface,
559     IcoFrameDecode_AddRef,
560     IcoFrameDecode_Release,
561     IcoFrameDecode_GetSize,
562     IcoFrameDecode_GetPixelFormat,
563     IcoFrameDecode_GetResolution,
564     IcoFrameDecode_CopyPalette,
565     IcoFrameDecode_CopyPixels,
566     IcoFrameDecode_GetMetadataQueryReader,
567     IcoFrameDecode_GetColorContexts,
568     IcoFrameDecode_GetThumbnail
569 };
570
571 static HRESULT WINAPI IcoDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid,
572     void **ppv)
573 {
574     IcoDecoder *This = (IcoDecoder*)iface;
575     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
576
577     if (!ppv) return E_INVALIDARG;
578
579     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IWICBitmapDecoder, iid))
580     {
581         *ppv = This;
582     }
583     else
584     {
585         *ppv = NULL;
586         return E_NOINTERFACE;
587     }
588
589     IUnknown_AddRef((IUnknown*)*ppv);
590     return S_OK;
591 }
592
593 static ULONG WINAPI IcoDecoder_AddRef(IWICBitmapDecoder *iface)
594 {
595     IcoDecoder *This = (IcoDecoder*)iface;
596     ULONG ref = InterlockedIncrement(&This->ref);
597
598     TRACE("(%p) refcount=%u\n", iface, ref);
599
600     return ref;
601 }
602
603 static ULONG WINAPI IcoDecoder_Release(IWICBitmapDecoder *iface)
604 {
605     IcoDecoder *This = (IcoDecoder*)iface;
606     ULONG ref = InterlockedDecrement(&This->ref);
607
608     TRACE("(%p) refcount=%u\n", iface, ref);
609
610     if (ref == 0)
611     {
612         This->lock.DebugInfo->Spare[0] = 0;
613         DeleteCriticalSection(&This->lock);
614         if (This->stream) IStream_Release(This->stream);
615         HeapFree(GetProcessHeap(), 0, This);
616     }
617
618     return ref;
619 }
620
621 static HRESULT WINAPI IcoDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *pIStream,
622     DWORD *pdwCapability)
623 {
624     FIXME("(%p,%p,%p): stub\n", iface, pIStream, pdwCapability);
625     return E_NOTIMPL;
626 }
627
628 static HRESULT WINAPI IcoDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream,
629     WICDecodeOptions cacheOptions)
630 {
631     IcoDecoder *This = (IcoDecoder*)iface;
632     LARGE_INTEGER seek;
633     HRESULT hr;
634     ULONG bytesread;
635     TRACE("(%p,%p,%x)\n", iface, pIStream, cacheOptions);
636
637     EnterCriticalSection(&This->lock);
638
639     if (This->initialized)
640     {
641         hr = WINCODEC_ERR_WRONGSTATE;
642         goto end;
643     }
644
645     seek.QuadPart = 0;
646     hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, NULL);
647     if (FAILED(hr)) goto end;
648
649     hr = IStream_Read(pIStream, &This->header, sizeof(ICONHEADER), &bytesread);
650     if (FAILED(hr)) goto end;
651     if (bytesread != sizeof(ICONHEADER) ||
652         This->header.idReserved != 0 ||
653         This->header.idType != 1)
654     {
655         hr = E_FAIL;
656         goto end;
657     }
658
659     This->initialized = TRUE;
660     This->stream = pIStream;
661     IStream_AddRef(pIStream);
662
663 end:
664
665     LeaveCriticalSection(&This->lock);
666
667     return hr;
668 }
669
670 static HRESULT WINAPI IcoDecoder_GetContainerFormat(IWICBitmapDecoder *iface,
671     GUID *pguidContainerFormat)
672 {
673     FIXME("(%p,%p): stub\n", iface, pguidContainerFormat);
674     return E_NOTIMPL;
675 }
676
677 static HRESULT WINAPI IcoDecoder_GetDecoderInfo(IWICBitmapDecoder *iface,
678     IWICBitmapDecoderInfo **ppIDecoderInfo)
679 {
680     FIXME("(%p,%p): stub\n", iface, ppIDecoderInfo);
681     return E_NOTIMPL;
682 }
683
684 static HRESULT WINAPI IcoDecoder_CopyPalette(IWICBitmapDecoder *iface,
685     IWICPalette *pIPalette)
686 {
687     TRACE("(%p,%p)\n", iface, pIPalette);
688     return WINCODEC_ERR_PALETTEUNAVAILABLE;
689 }
690
691 static HRESULT WINAPI IcoDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface,
692     IWICMetadataQueryReader **ppIMetadataQueryReader)
693 {
694     TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
695     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
696 }
697
698 static HRESULT WINAPI IcoDecoder_GetPreview(IWICBitmapDecoder *iface,
699     IWICBitmapSource **ppIBitmapSource)
700 {
701     TRACE("(%p,%p)\n", iface, ppIBitmapSource);
702     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
703 }
704
705 static HRESULT WINAPI IcoDecoder_GetColorContexts(IWICBitmapDecoder *iface,
706     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
707 {
708     TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
709     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
710 }
711
712 static HRESULT WINAPI IcoDecoder_GetThumbnail(IWICBitmapDecoder *iface,
713     IWICBitmapSource **ppIThumbnail)
714 {
715     TRACE("(%p,%p)\n", iface, ppIThumbnail);
716     return WINCODEC_ERR_CODECNOTHUMBNAIL;
717 }
718
719 static HRESULT WINAPI IcoDecoder_GetFrameCount(IWICBitmapDecoder *iface,
720     UINT *pCount)
721 {
722     IcoDecoder *This = (IcoDecoder*)iface;
723     TRACE("(%p,%p)\n", iface, pCount);
724
725     if (!This->initialized) return WINCODEC_ERR_NOTINITIALIZED;
726
727     *pCount = This->header.idCount;
728     TRACE("<-- %u\n", *pCount);
729
730     return S_OK;
731 }
732
733 static HRESULT WINAPI IcoDecoder_GetFrame(IWICBitmapDecoder *iface,
734     UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
735 {
736     IcoDecoder *This = (IcoDecoder*)iface;
737     IcoFrameDecode *result=NULL;
738     LARGE_INTEGER seek;
739     HRESULT hr;
740     ULONG bytesread;
741     TRACE("(%p,%u,%p)\n", iface, index, ppIBitmapFrame);
742
743     EnterCriticalSection(&This->lock);
744
745     if (!This->initialized)
746     {
747         hr = WINCODEC_ERR_NOTINITIALIZED;
748         goto fail;
749     }
750
751     if (This->header.idCount < index)
752     {
753         hr = E_INVALIDARG;
754         goto fail;
755     }
756
757     result = HeapAlloc(GetProcessHeap(), 0, sizeof(IcoFrameDecode));
758     if (!result)
759     {
760         hr = E_OUTOFMEMORY;
761         goto fail;
762     }
763
764     result->lpVtbl = &IcoFrameDecode_Vtbl;
765     result->ref = 1;
766     result->parent = This;
767     result->bits = NULL;
768
769     /* read the icon entry */
770     seek.QuadPart = sizeof(ICONHEADER) + sizeof(ICONDIRENTRY) * index;
771     hr = IStream_Seek(This->stream, seek, STREAM_SEEK_SET, 0);
772     if (FAILED(hr)) goto fail;
773
774     hr = IStream_Read(This->stream, &result->entry, sizeof(ICONDIRENTRY), &bytesread);
775     if (FAILED(hr) || bytesread != sizeof(ICONDIRENTRY)) goto fail;
776
777     IWICBitmapDecoder_AddRef(iface);
778
779     *ppIBitmapFrame = (IWICBitmapFrameDecode*)result;
780
781     LeaveCriticalSection(&This->lock);
782
783     return S_OK;
784
785 fail:
786     LeaveCriticalSection(&This->lock);
787     HeapFree(GetProcessHeap(), 0, result);
788     if (SUCCEEDED(hr)) hr = E_FAIL;
789     TRACE("<-- %x\n", hr);
790     return hr;
791 }
792
793 static const IWICBitmapDecoderVtbl IcoDecoder_Vtbl = {
794     IcoDecoder_QueryInterface,
795     IcoDecoder_AddRef,
796     IcoDecoder_Release,
797     IcoDecoder_QueryCapability,
798     IcoDecoder_Initialize,
799     IcoDecoder_GetContainerFormat,
800     IcoDecoder_GetDecoderInfo,
801     IcoDecoder_CopyPalette,
802     IcoDecoder_GetMetadataQueryReader,
803     IcoDecoder_GetPreview,
804     IcoDecoder_GetColorContexts,
805     IcoDecoder_GetThumbnail,
806     IcoDecoder_GetFrameCount,
807     IcoDecoder_GetFrame
808 };
809
810 HRESULT IcoDecoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
811 {
812     IcoDecoder *This;
813     HRESULT ret;
814
815     TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
816
817     *ppv = NULL;
818
819     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
820
821     This = HeapAlloc(GetProcessHeap(), 0, sizeof(IcoDecoder));
822     if (!This) return E_OUTOFMEMORY;
823
824     This->lpVtbl = &IcoDecoder_Vtbl;
825     This->ref = 1;
826     This->stream = NULL;
827     This->initialized = FALSE;
828     InitializeCriticalSection(&This->lock);
829     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": IcoDecoder.lock");
830
831     ret = IUnknown_QueryInterface((IUnknown*)This, iid, ppv);
832     IUnknown_Release((IUnknown*)This);
833
834     return ret;
835 }