crypt32: Make sure we show Unicode characters (Dutch translation).
[wine] / dlls / windowscodecs / bmpdecode.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 "winreg.h"
28 #include "wingdi.h"
29 #include "objbase.h"
30 #include "wincodec.h"
31
32 #include "wincodecs_private.h"
33
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
37
38 typedef struct {
39     DWORD bc2Size;
40     DWORD bc2Width;
41     DWORD bc2Height;
42     WORD  bc2Planes;
43     WORD  bc2BitCount;
44     DWORD bc2Compression;
45     DWORD bc2SizeImage;
46     DWORD bc2XRes;
47     DWORD bc2YRes;
48     DWORD bc2ClrUsed;
49     DWORD bc2ClrImportant;
50     /* same as BITMAPINFOHEADER until this point */
51     WORD  bc2ResUnit;
52     WORD  bc2Reserved;
53     WORD  bc2Orientation;
54     WORD  bc2Halftoning;
55     DWORD bc2HalftoneSize1;
56     DWORD bc2HalftoneSize2;
57     DWORD bc2ColorSpace;
58     DWORD bc2AppData;
59 } BITMAPCOREHEADER2;
60
61 struct BmpFrameDecode;
62 typedef HRESULT (*ReadDataFunc)(struct BmpFrameDecode* This);
63
64 typedef struct BmpFrameDecode {
65     const IWICBitmapFrameDecodeVtbl *lpVtbl;
66     LONG ref;
67     IStream *stream;
68     BITMAPFILEHEADER bfh;
69     BITMAPV5HEADER bih;
70     const WICPixelFormatGUID *pixelformat;
71     int bitsperpixel;
72     ReadDataFunc read_data_func;
73     INT stride;
74     BYTE *imagedata;
75     BYTE *imagedatastart;
76 } BmpFrameDecode;
77
78 static HRESULT WINAPI BmpFrameDecode_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
79     void **ppv)
80 {
81     BmpFrameDecode *This = (BmpFrameDecode*)iface;
82     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
83
84     if (!ppv) return E_INVALIDARG;
85
86     if (IsEqualIID(&IID_IUnknown, iid) ||
87         IsEqualIID(&IID_IWICBitmapSource, iid) ||
88         IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
89     {
90         *ppv = This;
91     }
92     else
93     {
94         *ppv = NULL;
95         return E_NOINTERFACE;
96     }
97
98     IUnknown_AddRef((IUnknown*)*ppv);
99     return S_OK;
100 }
101
102 static ULONG WINAPI BmpFrameDecode_AddRef(IWICBitmapFrameDecode *iface)
103 {
104     BmpFrameDecode *This = (BmpFrameDecode*)iface;
105     ULONG ref = InterlockedIncrement(&This->ref);
106
107     TRACE("(%p) refcount=%u\n", iface, ref);
108
109     return ref;
110 }
111
112 static ULONG WINAPI BmpFrameDecode_Release(IWICBitmapFrameDecode *iface)
113 {
114     BmpFrameDecode *This = (BmpFrameDecode*)iface;
115     ULONG ref = InterlockedDecrement(&This->ref);
116
117     TRACE("(%p) refcount=%u\n", iface, ref);
118
119     if (ref == 0)
120     {
121         IStream_Release(This->stream);
122         HeapFree(GetProcessHeap(), 0, This->imagedata);
123         HeapFree(GetProcessHeap(), 0, This);
124     }
125
126     return ref;
127 }
128
129 static HRESULT WINAPI BmpFrameDecode_GetSize(IWICBitmapFrameDecode *iface,
130     UINT *puiWidth, UINT *puiHeight)
131 {
132     BmpFrameDecode *This = (BmpFrameDecode*)iface;
133     TRACE("(%p,%p,%p)\n", iface, puiWidth, puiHeight);
134
135     if (This->bih.bV5Size == sizeof(BITMAPCOREHEADER))
136     {
137         BITMAPCOREHEADER *bch = (BITMAPCOREHEADER*)&This->bih;
138         *puiWidth = bch->bcWidth;
139         *puiHeight = bch->bcHeight;
140     }
141     else
142     {
143         *puiWidth = This->bih.bV5Width;
144         *puiHeight = abs(This->bih.bV5Height);
145     }
146     return S_OK;
147 }
148
149 static HRESULT WINAPI BmpFrameDecode_GetPixelFormat(IWICBitmapFrameDecode *iface,
150     WICPixelFormatGUID *pPixelFormat)
151 {
152     BmpFrameDecode *This = (BmpFrameDecode*)iface;
153     TRACE("(%p,%p)\n", iface, pPixelFormat);
154
155     memcpy(pPixelFormat, This->pixelformat, sizeof(GUID));
156
157     return S_OK;
158 }
159
160 static HRESULT BmpHeader_GetResolution(BITMAPV5HEADER *bih, double *pDpiX, double *pDpiY)
161 {
162     switch (bih->bV5Size)
163     {
164     case sizeof(BITMAPCOREHEADER):
165         *pDpiX = 96.0;
166         *pDpiY = 96.0;
167         return S_OK;
168     case sizeof(BITMAPCOREHEADER2):
169     case sizeof(BITMAPINFOHEADER):
170     case sizeof(BITMAPV4HEADER):
171     case sizeof(BITMAPV5HEADER):
172         *pDpiX = bih->bV5XPelsPerMeter * 0.0254;
173         *pDpiY = bih->bV5YPelsPerMeter * 0.0254;
174         return S_OK;
175     default:
176         return E_FAIL;
177     }
178 }
179
180 static HRESULT WINAPI BmpFrameDecode_GetResolution(IWICBitmapFrameDecode *iface,
181     double *pDpiX, double *pDpiY)
182 {
183     BmpFrameDecode *This = (BmpFrameDecode*)iface;
184     TRACE("(%p,%p,%p)\n", iface, pDpiX, pDpiY);
185
186     return BmpHeader_GetResolution(&This->bih, pDpiX, pDpiY);
187 }
188
189 static HRESULT WINAPI BmpFrameDecode_CopyPalette(IWICBitmapFrameDecode *iface,
190     IWICPalette *pIPalette)
191 {
192     FIXME("(%p,%p): stub\n", iface, pIPalette);
193     return E_NOTIMPL;
194 }
195
196 static HRESULT WINAPI BmpFrameDecode_CopyPixels(IWICBitmapFrameDecode *iface,
197     const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
198 {
199     BmpFrameDecode *This = (BmpFrameDecode*)iface;
200     HRESULT hr;
201     UINT width, height;
202     TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
203
204     if (!This->imagedata)
205     {
206         hr = This->read_data_func(This);
207         if (FAILED(hr)) return hr;
208     }
209
210     hr = BmpFrameDecode_GetSize(iface, &width, &height);
211     if (FAILED(hr)) return hr;
212
213     return copy_pixels(This->bitsperpixel, This->imagedatastart,
214         width, height, This->stride,
215         prc, cbStride, cbBufferSize, pbBuffer);
216 }
217
218 static HRESULT WINAPI BmpFrameDecode_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
219     IWICMetadataQueryReader **ppIMetadataQueryReader)
220 {
221     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryReader);
222     return E_NOTIMPL;
223 }
224
225 static HRESULT WINAPI BmpFrameDecode_GetColorContexts(IWICBitmapFrameDecode *iface,
226     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
227 {
228     FIXME("(%p,%u,%p,%p): stub\n", iface, cCount, ppIColorContexts, pcActualCount);
229     return E_NOTIMPL;
230 }
231
232 static HRESULT WINAPI BmpFrameDecode_GetThumbnail(IWICBitmapFrameDecode *iface,
233     IWICBitmapSource **ppIThumbnail)
234 {
235     FIXME("(%p,%p): stub\n", iface, ppIThumbnail);
236     return E_NOTIMPL;
237 }
238
239 static HRESULT BmpFrameDecode_ReadUncompressed(BmpFrameDecode* This)
240 {
241     UINT bytesperrow;
242     UINT width, height;
243     UINT datasize;
244     int bottomup;
245     HRESULT hr;
246     LARGE_INTEGER offbits;
247     ULONG bytesread;
248
249     if (This->bih.bV5Size == sizeof(BITMAPCOREHEADER))
250     {
251         BITMAPCOREHEADER *bch = (BITMAPCOREHEADER*)&This->bih;
252         width = bch->bcWidth;
253         height = bch->bcHeight;
254         bottomup = 1;
255     }
256     else
257     {
258         width = This->bih.bV5Width;
259         height = abs(This->bih.bV5Height);
260         bottomup = (This->bih.bV5Height > 0);
261     }
262
263     /* row sizes in BMP files must be divisible by 4 bytes */
264     bytesperrow = (((width * This->bitsperpixel)+31)/32)*4;
265     datasize = bytesperrow * height;
266
267     This->imagedata = HeapAlloc(GetProcessHeap(), 0, datasize);
268     if (!This->imagedata) return E_OUTOFMEMORY;
269
270     offbits.QuadPart = This->bfh.bfOffBits;
271     hr = IStream_Seek(This->stream, offbits, STREAM_SEEK_SET, NULL);
272     if (FAILED(hr)) goto fail;
273
274     hr = IStream_Read(This->stream, This->imagedata, datasize, &bytesread);
275     if (FAILED(hr) || bytesread != datasize) goto fail;
276
277     if (bottomup)
278     {
279         This->imagedatastart = This->imagedata + (height-1) * bytesperrow;
280         This->stride = -bytesperrow;
281     }
282     else
283     {
284         This->imagedatastart = This->imagedata;
285         This->stride = bytesperrow;
286     }
287     return S_OK;
288
289 fail:
290     HeapFree(GetProcessHeap(), 0, This->imagedata);
291     This->imagedata = NULL;
292     if (SUCCEEDED(hr)) hr = E_FAIL;
293     return hr;
294 }
295
296 static HRESULT BmpFrameDecode_ReadUnsupported(BmpFrameDecode* This)
297 {
298     return E_FAIL;
299 }
300
301 static const IWICBitmapFrameDecodeVtbl BmpFrameDecode_Vtbl = {
302     BmpFrameDecode_QueryInterface,
303     BmpFrameDecode_AddRef,
304     BmpFrameDecode_Release,
305     BmpFrameDecode_GetSize,
306     BmpFrameDecode_GetPixelFormat,
307     BmpFrameDecode_GetResolution,
308     BmpFrameDecode_CopyPalette,
309     BmpFrameDecode_CopyPixels,
310     BmpFrameDecode_GetMetadataQueryReader,
311     BmpFrameDecode_GetColorContexts,
312     BmpFrameDecode_GetThumbnail
313 };
314
315 typedef struct {
316     const IWICBitmapDecoderVtbl *lpVtbl;
317     LONG ref;
318     BOOL initialized;
319     IStream *stream;
320     BITMAPFILEHEADER bfh;
321     BITMAPV5HEADER bih;
322     BmpFrameDecode *framedecode;
323     const WICPixelFormatGUID *pixelformat;
324     int bitsperpixel;
325     ReadDataFunc read_data_func;
326 } BmpDecoder;
327
328 static HRESULT BmpDecoder_ReadHeaders(BmpDecoder* This, IStream *stream)
329 {
330     HRESULT hr;
331     ULONG bytestoread, bytesread;
332     LARGE_INTEGER seek;
333
334     if (This->initialized) return WINCODEC_ERR_WRONGSTATE;
335
336     seek.QuadPart = 0;
337     hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
338     if (FAILED(hr)) return hr;
339
340     hr = IStream_Read(stream, &This->bfh, sizeof(BITMAPFILEHEADER), &bytesread);
341     if (FAILED(hr)) return hr;
342     if (bytesread != sizeof(BITMAPFILEHEADER) ||
343         This->bfh.bfType != 0x4d42 /* "BM" */) return E_FAIL;
344
345     hr = IStream_Read(stream, &This->bih.bV5Size, sizeof(DWORD), &bytesread);
346     if (FAILED(hr)) return hr;
347     if (bytesread != sizeof(DWORD) ||
348         (This->bih.bV5Size != sizeof(BITMAPCOREHEADER) &&
349          This->bih.bV5Size != sizeof(BITMAPCOREHEADER2) &&
350          This->bih.bV5Size != sizeof(BITMAPINFOHEADER) &&
351          This->bih.bV5Size != sizeof(BITMAPV4HEADER) &&
352          This->bih.bV5Size != sizeof(BITMAPV5HEADER))) return E_FAIL;
353
354     bytestoread = This->bih.bV5Size-sizeof(DWORD);
355     hr = IStream_Read(stream, &This->bih.bV5Width, bytestoread, &bytesread);
356     if (FAILED(hr)) return hr;
357     if (bytestoread != bytesread) return E_FAIL;
358
359     /* decide what kind of bitmap this is and how/if we can read it */
360     if (This->bih.bV5Size == sizeof(BITMAPCOREHEADER))
361     {
362         BITMAPCOREHEADER *bch = (BITMAPCOREHEADER*)&This->bih;
363         TRACE("BITMAPCOREHEADER with depth=%i\n", bch->bcBitCount);
364         This->bitsperpixel = bch->bcBitCount;
365         This->read_data_func = BmpFrameDecode_ReadUncompressed;
366         switch(bch->bcBitCount)
367         {
368         case 1:
369             This->pixelformat = &GUID_WICPixelFormat1bppIndexed;
370             break;
371         case 2:
372             This->pixelformat = &GUID_WICPixelFormat2bppIndexed;
373             break;
374         case 4:
375             This->pixelformat = &GUID_WICPixelFormat4bppIndexed;
376             break;
377         case 8:
378             This->pixelformat = &GUID_WICPixelFormat8bppIndexed;
379             break;
380         case 24:
381             This->pixelformat = &GUID_WICPixelFormat24bppBGR;
382             break;
383         default:
384             This->pixelformat = &GUID_WICPixelFormatUndefined;
385             WARN("unsupported bit depth %i for BITMAPCOREHEADER\n", bch->bcBitCount);
386             break;
387         }
388     }
389     else /* struct is compatible with BITMAPINFOHEADER */
390     {
391         TRACE("bitmap header=%i compression=%i depth=%i\n", This->bih.bV5Size, This->bih.bV5Compression, This->bih.bV5BitCount);
392         switch(This->bih.bV5Compression)
393         {
394         case BI_RGB:
395             This->bitsperpixel = This->bih.bV5BitCount;
396             This->read_data_func = BmpFrameDecode_ReadUncompressed;
397             switch(This->bih.bV5BitCount)
398             {
399             case 1:
400                 This->pixelformat = &GUID_WICPixelFormat1bppIndexed;
401                 break;
402             case 2:
403                 This->pixelformat = &GUID_WICPixelFormat2bppIndexed;
404                 break;
405             case 4:
406                 This->pixelformat = &GUID_WICPixelFormat4bppIndexed;
407                 break;
408             case 8:
409                 This->pixelformat = &GUID_WICPixelFormat8bppIndexed;
410                 break;
411             case 16:
412                 This->pixelformat = &GUID_WICPixelFormat16bppBGR555;
413                 break;
414             case 24:
415                 This->pixelformat = &GUID_WICPixelFormat24bppBGR;
416                 break;
417             case 32:
418                 This->pixelformat = &GUID_WICPixelFormat32bppBGR;
419                 break;
420             default:
421                 This->pixelformat = &GUID_WICPixelFormatUndefined;
422                 FIXME("unsupported bit depth %i for uncompressed RGB\n", This->bih.bV5BitCount);
423             }
424             break;
425         default:
426             This->bitsperpixel = 0;
427             This->read_data_func = BmpFrameDecode_ReadUnsupported;
428             This->pixelformat = &GUID_WICPixelFormatUndefined;
429             FIXME("unsupported bitmap type header=%i compression=%i depth=%i\n", This->bih.bV5Size, This->bih.bV5Compression, This->bih.bV5BitCount);
430             break;
431         }
432     }
433
434     This->initialized = TRUE;
435
436     return S_OK;
437 }
438
439 static HRESULT WINAPI BmpDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid,
440     void **ppv)
441 {
442     BmpDecoder *This = (BmpDecoder*)iface;
443     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
444
445     if (!ppv) return E_INVALIDARG;
446
447     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IWICBitmapDecoder, iid))
448     {
449         *ppv = This;
450     }
451     else
452     {
453         *ppv = NULL;
454         return E_NOINTERFACE;
455     }
456
457     IUnknown_AddRef((IUnknown*)*ppv);
458     return S_OK;
459 }
460
461 static ULONG WINAPI BmpDecoder_AddRef(IWICBitmapDecoder *iface)
462 {
463     BmpDecoder *This = (BmpDecoder*)iface;
464     ULONG ref = InterlockedIncrement(&This->ref);
465
466     TRACE("(%p) refcount=%u\n", iface, ref);
467
468     return ref;
469 }
470
471 static ULONG WINAPI BmpDecoder_Release(IWICBitmapDecoder *iface)
472 {
473     BmpDecoder *This = (BmpDecoder*)iface;
474     ULONG ref = InterlockedDecrement(&This->ref);
475
476     TRACE("(%p) refcount=%u\n", iface, ref);
477
478     if (ref == 0)
479     {
480         if (This->stream) IStream_Release(This->stream);
481         if (This->framedecode) IUnknown_Release((IUnknown*)This->framedecode);
482         HeapFree(GetProcessHeap(), 0, This);
483     }
484
485     return ref;
486 }
487
488 static HRESULT WINAPI BmpDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *pIStream,
489     DWORD *pdwCapability)
490 {
491     HRESULT hr;
492     BmpDecoder *This = (BmpDecoder*)iface;
493
494     hr = BmpDecoder_ReadHeaders(This, pIStream);
495     if (FAILED(hr)) return hr;
496
497     if (This->read_data_func == BmpFrameDecode_ReadUnsupported)
498         *pdwCapability = 0;
499     else
500         *pdwCapability = WICBitmapDecoderCapabilityCanDecodeAllImages;
501
502     return S_OK;
503 }
504
505 static HRESULT WINAPI BmpDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream,
506     WICDecodeOptions cacheOptions)
507 {
508     HRESULT hr;
509     BmpDecoder *This = (BmpDecoder*)iface;
510
511     hr = BmpDecoder_ReadHeaders(This, pIStream);
512
513     if (SUCCEEDED(hr))
514     {
515         This->stream = pIStream;
516         IStream_AddRef(pIStream);
517     }
518
519     return hr;
520 }
521
522 static HRESULT WINAPI BmpDecoder_GetContainerFormat(IWICBitmapDecoder *iface,
523     GUID *pguidContainerFormat)
524 {
525     memcpy(pguidContainerFormat, &GUID_ContainerFormatBmp, sizeof(GUID));
526     return S_OK;
527 }
528
529 static HRESULT WINAPI BmpDecoder_GetDecoderInfo(IWICBitmapDecoder *iface,
530     IWICBitmapDecoderInfo **ppIDecoderInfo)
531 {
532     FIXME("(%p,%p): stub\n", iface, ppIDecoderInfo);
533     return E_NOTIMPL;
534 }
535
536 static HRESULT WINAPI BmpDecoder_CopyPalette(IWICBitmapDecoder *iface,
537     IWICPalette *pIPalette)
538 {
539     FIXME("(%p,%p): stub\n", iface, pIPalette);
540     return E_NOTIMPL;
541 }
542
543 static HRESULT WINAPI BmpDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface,
544     IWICMetadataQueryReader **ppIMetadataQueryReader)
545 {
546     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryReader);
547     return E_NOTIMPL;
548 }
549
550 static HRESULT WINAPI BmpDecoder_GetPreview(IWICBitmapDecoder *iface,
551     IWICBitmapSource **ppIBitmapSource)
552 {
553     FIXME("(%p,%p): stub\n", iface, ppIBitmapSource);
554     return E_NOTIMPL;
555 }
556
557 static HRESULT WINAPI BmpDecoder_GetColorContexts(IWICBitmapDecoder *iface,
558     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
559 {
560     FIXME("(%p,%u,%p,%p): stub\n", iface, cCount, ppIColorContexts, pcActualCount);
561     return E_NOTIMPL;
562 }
563
564 static HRESULT WINAPI BmpDecoder_GetThumbnail(IWICBitmapDecoder *iface,
565     IWICBitmapSource **ppIThumbnail)
566 {
567     FIXME("(%p,%p): stub\n", iface, ppIThumbnail);
568     return E_NOTIMPL;
569 }
570
571 static HRESULT WINAPI BmpDecoder_GetFrameCount(IWICBitmapDecoder *iface,
572     UINT *pCount)
573 {
574     *pCount = 1;
575     return S_OK;
576 }
577
578 static HRESULT WINAPI BmpDecoder_GetFrame(IWICBitmapDecoder *iface,
579     UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
580 {
581     BmpDecoder *This = (BmpDecoder*)iface;
582
583     if (index != 0) return E_INVALIDARG;
584
585     if (!This->stream) return WINCODEC_ERR_WRONGSTATE;
586
587     if (!This->framedecode)
588     {
589         This->framedecode = HeapAlloc(GetProcessHeap(), 0, sizeof(BmpFrameDecode));
590         if (!This->framedecode) return E_OUTOFMEMORY;
591
592         This->framedecode->lpVtbl = &BmpFrameDecode_Vtbl;
593         This->framedecode->ref = 1;
594         This->framedecode->stream = This->stream;
595         IStream_AddRef(This->stream);
596         This->framedecode->bfh = This->bfh;
597         This->framedecode->bih = This->bih;
598         This->framedecode->pixelformat = This->pixelformat;
599         This->framedecode->bitsperpixel = This->bitsperpixel;
600         This->framedecode->read_data_func = This->read_data_func;
601         This->framedecode->imagedata = NULL;
602     }
603
604     *ppIBitmapFrame = (IWICBitmapFrameDecode*)This->framedecode;
605     IWICBitmapFrameDecode_AddRef((IWICBitmapFrameDecode*)This->framedecode);
606
607     return S_OK;
608 }
609
610 static const IWICBitmapDecoderVtbl BmpDecoder_Vtbl = {
611     BmpDecoder_QueryInterface,
612     BmpDecoder_AddRef,
613     BmpDecoder_Release,
614     BmpDecoder_QueryCapability,
615     BmpDecoder_Initialize,
616     BmpDecoder_GetContainerFormat,
617     BmpDecoder_GetDecoderInfo,
618     BmpDecoder_CopyPalette,
619     BmpDecoder_GetMetadataQueryReader,
620     BmpDecoder_GetPreview,
621     BmpDecoder_GetColorContexts,
622     BmpDecoder_GetThumbnail,
623     BmpDecoder_GetFrameCount,
624     BmpDecoder_GetFrame
625 };
626
627 HRESULT BmpDecoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
628 {
629     BmpDecoder *This;
630     HRESULT ret;
631
632     TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
633
634     *ppv = NULL;
635
636     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
637
638     This = HeapAlloc(GetProcessHeap(), 0, sizeof(BmpDecoder));
639     if (!This) return E_OUTOFMEMORY;
640
641     This->lpVtbl = &BmpDecoder_Vtbl;
642     This->ref = 1;
643     This->initialized = FALSE;
644     This->stream = NULL;
645     This->framedecode = NULL;
646
647     ret = IUnknown_QueryInterface((IUnknown*)This, iid, ppv);
648     IUnknown_Release((IUnknown*)This);
649
650     return ret;
651 }