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