windowscodecs: Implement GetDecoderInfo for the GIF decoder.
[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
25 #include "windef.h"
26 #include "winbase.h"
27 #include "objbase.h"
28 #include "wincodec.h"
29
30 #include "ungif.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     const IWICBitmapDecoderVtbl *lpVtbl;
40     LONG ref;
41     BOOL initialized;
42     GifFileType *gif;
43 } GifDecoder;
44
45 typedef struct {
46     const IWICBitmapFrameDecodeVtbl *lpVtbl;
47     LONG ref;
48     SavedImage *frame;
49     GifDecoder *parent;
50 } GifFrameDecode;
51
52 static HRESULT WINAPI GifFrameDecode_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
53     void **ppv)
54 {
55     GifFrameDecode *This = (GifFrameDecode*)iface;
56     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
57
58     if (!ppv) return E_INVALIDARG;
59
60     if (IsEqualIID(&IID_IUnknown, iid) ||
61         IsEqualIID(&IID_IWICBitmapSource, iid) ||
62         IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
63     {
64         *ppv = This;
65     }
66     else
67     {
68         *ppv = NULL;
69         return E_NOINTERFACE;
70     }
71
72     IUnknown_AddRef((IUnknown*)*ppv);
73     return S_OK;
74 }
75
76 static ULONG WINAPI GifFrameDecode_AddRef(IWICBitmapFrameDecode *iface)
77 {
78     GifFrameDecode *This = (GifFrameDecode*)iface;
79     ULONG ref = InterlockedIncrement(&This->ref);
80
81     TRACE("(%p) refcount=%u\n", iface, ref);
82
83     return ref;
84 }
85
86 static ULONG WINAPI GifFrameDecode_Release(IWICBitmapFrameDecode *iface)
87 {
88     GifFrameDecode *This = (GifFrameDecode*)iface;
89     ULONG ref = InterlockedDecrement(&This->ref);
90
91     TRACE("(%p) refcount=%u\n", iface, ref);
92
93     if (ref == 0)
94     {
95         IUnknown_Release((IUnknown*)This->parent);
96         HeapFree(GetProcessHeap(), 0, This);
97     }
98
99     return ref;
100 }
101
102 static HRESULT WINAPI GifFrameDecode_GetSize(IWICBitmapFrameDecode *iface,
103     UINT *puiWidth, UINT *puiHeight)
104 {
105     GifFrameDecode *This = (GifFrameDecode*)iface;
106     TRACE("(%p,%p,%p)\n", iface, puiWidth, puiHeight);
107
108     *puiWidth = This->frame->ImageDesc.Width;
109     *puiHeight = This->frame->ImageDesc.Height;
110
111     return S_OK;
112 }
113
114 static HRESULT WINAPI GifFrameDecode_GetPixelFormat(IWICBitmapFrameDecode *iface,
115     WICPixelFormatGUID *pPixelFormat)
116 {
117     memcpy(pPixelFormat, &GUID_WICPixelFormat8bppIndexed, sizeof(GUID));
118
119     return S_OK;
120 }
121
122 static HRESULT WINAPI GifFrameDecode_GetResolution(IWICBitmapFrameDecode *iface,
123     double *pDpiX, double *pDpiY)
124 {
125     FIXME("(%p,%p,%p): stub\n", iface, pDpiX, pDpiY);
126     return E_NOTIMPL;
127 }
128
129 static HRESULT WINAPI GifFrameDecode_CopyPalette(IWICBitmapFrameDecode *iface,
130     IWICPalette *pIPalette)
131 {
132     GifFrameDecode *This = (GifFrameDecode*)iface;
133     WICColor colors[256];
134     ColorMapObject *cm = This->frame->ImageDesc.ColorMap;
135     int i, trans;
136     ExtensionBlock *eb;
137     TRACE("(%p,%p)\n", iface, pIPalette);
138
139     if (!cm) cm = This->parent->gif->SColorMap;
140
141     if (cm->ColorCount > 256)
142     {
143         ERR("GIF contains %i colors???\n", cm->ColorCount);
144         return E_FAIL;
145     }
146
147     for (i = 0; i < cm->ColorCount; i++) {
148         colors[i] = 0xff000000| /* alpha */
149                     cm->Colors[i].Red << 16|
150                     cm->Colors[i].Green << 8|
151                     cm->Colors[i].Blue;
152     }
153
154     /* look for the transparent color extension */
155     for (i = 0; i < This->frame->ExtensionBlockCount; ++i) {
156         eb = This->frame->ExtensionBlocks + i;
157         if (eb->Function == 0xF9 && eb->ByteCount == 4) {
158             if ((eb->Bytes[0] & 1) == 1) {
159                 trans = (unsigned char)eb->Bytes[3];
160                 colors[trans] &= 0xffffff; /* set alpha to 0 */
161                 break;
162             }
163         }
164     }
165
166     IWICPalette_InitializeCustom(pIPalette, colors, cm->ColorCount);
167
168     return S_OK;
169 }
170
171 static HRESULT copy_interlaced_pixels(const BYTE *srcbuffer,
172     UINT srcwidth, UINT srcheight, INT srcstride, const WICRect *rc,
173     UINT dststride, UINT dstbuffersize, BYTE *dstbuffer)
174 {
175     UINT row_offset; /* number of bytes into the source rows where the data starts */
176     const BYTE *src;
177     BYTE *dst;
178     UINT y;
179
180     if (rc->X < 0 || rc->Y < 0 || rc->X+rc->Width > srcwidth || rc->Y+rc->Height > srcheight)
181         return E_INVALIDARG;
182
183     if (dststride < rc->Width)
184         return E_INVALIDARG;
185
186     if ((dststride * rc->Height) > dstbuffersize)
187         return E_INVALIDARG;
188
189     row_offset = rc->X;
190
191     dst = dstbuffer;
192     for (y=rc->Y; y-rc->Y < rc->Height; y++)
193     {
194         if (y%8 == 0)
195             src = srcbuffer + srcstride * (y/8);
196         else if (y%4 == 0)
197             src = srcbuffer + srcstride * ((srcheight+7)/8 + y/8);
198         else if (y%2 == 0)
199             src = srcbuffer + srcstride * ((srcheight+3)/4 + y/4);
200         else /* y%2 == 1 */
201             src = srcbuffer + srcstride * ((srcheight+1)/2 + y/2);
202         src += row_offset;
203         memcpy(dst, src, rc->Width);
204         dst += dststride;
205     }
206     return S_OK;
207 }
208
209 static HRESULT WINAPI GifFrameDecode_CopyPixels(IWICBitmapFrameDecode *iface,
210     const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
211 {
212     GifFrameDecode *This = (GifFrameDecode*)iface;
213     TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
214
215     if (This->frame->ImageDesc.Interlace)
216     {
217         return copy_interlaced_pixels(This->frame->RasterBits, This->frame->ImageDesc.Width,
218             This->frame->ImageDesc.Height, This->frame->ImageDesc.Width,
219             prc, cbStride, cbBufferSize, pbBuffer);
220     }
221     else
222     {
223         return copy_pixels(8, This->frame->RasterBits, This->frame->ImageDesc.Width,
224             This->frame->ImageDesc.Height, This->frame->ImageDesc.Width,
225             prc, cbStride, cbBufferSize, pbBuffer);
226     }
227 }
228
229 static HRESULT WINAPI GifFrameDecode_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
230     IWICMetadataQueryReader **ppIMetadataQueryReader)
231 {
232     TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
233     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
234 }
235
236 static HRESULT WINAPI GifFrameDecode_GetColorContexts(IWICBitmapFrameDecode *iface,
237     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
238 {
239     TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
240     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
241 }
242
243 static HRESULT WINAPI GifFrameDecode_GetThumbnail(IWICBitmapFrameDecode *iface,
244     IWICBitmapSource **ppIThumbnail)
245 {
246     TRACE("(%p,%p)\n", iface, ppIThumbnail);
247     return WINCODEC_ERR_CODECNOTHUMBNAIL;
248 }
249
250 static const IWICBitmapFrameDecodeVtbl GifFrameDecode_Vtbl = {
251     GifFrameDecode_QueryInterface,
252     GifFrameDecode_AddRef,
253     GifFrameDecode_Release,
254     GifFrameDecode_GetSize,
255     GifFrameDecode_GetPixelFormat,
256     GifFrameDecode_GetResolution,
257     GifFrameDecode_CopyPalette,
258     GifFrameDecode_CopyPixels,
259     GifFrameDecode_GetMetadataQueryReader,
260     GifFrameDecode_GetColorContexts,
261     GifFrameDecode_GetThumbnail
262 };
263
264 static HRESULT WINAPI GifDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid,
265     void **ppv)
266 {
267     GifDecoder *This = (GifDecoder*)iface;
268     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
269
270     if (!ppv) return E_INVALIDARG;
271
272     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IWICBitmapDecoder, iid))
273     {
274         *ppv = This;
275     }
276     else
277     {
278         *ppv = NULL;
279         return E_NOINTERFACE;
280     }
281
282     IUnknown_AddRef((IUnknown*)*ppv);
283     return S_OK;
284 }
285
286 static ULONG WINAPI GifDecoder_AddRef(IWICBitmapDecoder *iface)
287 {
288     GifDecoder *This = (GifDecoder*)iface;
289     ULONG ref = InterlockedIncrement(&This->ref);
290
291     TRACE("(%p) refcount=%u\n", iface, ref);
292
293     return ref;
294 }
295
296 static ULONG WINAPI GifDecoder_Release(IWICBitmapDecoder *iface)
297 {
298     GifDecoder *This = (GifDecoder*)iface;
299     ULONG ref = InterlockedDecrement(&This->ref);
300
301     TRACE("(%p) refcount=%u\n", iface, ref);
302
303     if (ref == 0)
304     {
305         DGifCloseFile(This->gif);
306         HeapFree(GetProcessHeap(), 0, This);
307     }
308
309     return ref;
310 }
311
312 static HRESULT WINAPI GifDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *pIStream,
313     DWORD *pdwCapability)
314 {
315     FIXME("(%p,%p,%p): stub\n", iface, pIStream, pdwCapability);
316     return E_NOTIMPL;
317 }
318
319 static int _gif_inputfunc(GifFileType *gif, GifByteType *data, int len) {
320     IStream *stream = gif->UserData;
321     ULONG bytesread;
322     HRESULT hr;
323
324     if (!stream)
325     {
326         ERR("attempting to read file after initialization\n");
327         return 0;
328     }
329
330     hr = IStream_Read(stream, data, len, &bytesread);
331     if (hr != S_OK) bytesread = 0;
332     return bytesread;
333 }
334
335 static HRESULT WINAPI GifDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream,
336     WICDecodeOptions cacheOptions)
337 {
338     GifDecoder *This = (GifDecoder*)iface;
339     LARGE_INTEGER seek;
340     int ret;
341
342     TRACE("(%p,%p,%x)\n", iface, pIStream, cacheOptions);
343
344     if (This->initialized || This->gif)
345     {
346         WARN("already initialized\n");
347         return WINCODEC_ERR_WRONGSTATE;
348     }
349
350     /* seek to start of stream */
351     seek.QuadPart = 0;
352     IStream_Seek(pIStream, seek, STREAM_SEEK_SET, NULL);
353
354     /* read all data from the stream */
355     This->gif = DGifOpen((void*)pIStream, _gif_inputfunc);
356     if (!This->gif) return E_FAIL;
357
358     ret = DGifSlurp(This->gif);
359     if (ret == GIF_ERROR) return E_FAIL;
360
361     /* make sure we don't use the stream after this method returns */
362     This->gif->UserData = NULL;
363
364     This->initialized = TRUE;
365
366     return S_OK;
367 }
368
369 static HRESULT WINAPI GifDecoder_GetContainerFormat(IWICBitmapDecoder *iface,
370     GUID *pguidContainerFormat)
371 {
372     memcpy(pguidContainerFormat, &GUID_ContainerFormatGif, sizeof(GUID));
373     return S_OK;
374 }
375
376 static HRESULT WINAPI GifDecoder_GetDecoderInfo(IWICBitmapDecoder *iface,
377     IWICBitmapDecoderInfo **ppIDecoderInfo)
378 {
379     HRESULT hr;
380     IWICComponentInfo *compinfo;
381
382     TRACE("(%p,%p)\n", iface, ppIDecoderInfo);
383
384     hr = CreateComponentInfo(&CLSID_WICGifDecoder, &compinfo);
385     if (FAILED(hr)) return hr;
386
387     hr = IWICComponentInfo_QueryInterface(compinfo, &IID_IWICBitmapDecoderInfo,
388         (void**)ppIDecoderInfo);
389
390     IWICComponentInfo_Release(compinfo);
391
392     return hr;
393 }
394
395 static HRESULT WINAPI GifDecoder_CopyPalette(IWICBitmapDecoder *iface,
396     IWICPalette *pIPalette)
397 {
398     TRACE("(%p,%p)\n", iface, pIPalette);
399     return WINCODEC_ERR_PALETTEUNAVAILABLE;
400 }
401
402 static HRESULT WINAPI GifDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface,
403     IWICMetadataQueryReader **ppIMetadataQueryReader)
404 {
405     TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
406     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
407 }
408
409 static HRESULT WINAPI GifDecoder_GetPreview(IWICBitmapDecoder *iface,
410     IWICBitmapSource **ppIBitmapSource)
411 {
412     TRACE("(%p,%p)\n", iface, ppIBitmapSource);
413     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
414 }
415
416 static HRESULT WINAPI GifDecoder_GetColorContexts(IWICBitmapDecoder *iface,
417     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
418 {
419     TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
420     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
421 }
422
423 static HRESULT WINAPI GifDecoder_GetThumbnail(IWICBitmapDecoder *iface,
424     IWICBitmapSource **ppIThumbnail)
425 {
426     TRACE("(%p,%p)\n", iface, ppIThumbnail);
427     return WINCODEC_ERR_CODECNOTHUMBNAIL;
428 }
429
430 static HRESULT WINAPI GifDecoder_GetFrameCount(IWICBitmapDecoder *iface,
431     UINT *pCount)
432 {
433     GifDecoder *This = (GifDecoder*)iface;
434     TRACE("(%p,%p)\n", iface, pCount);
435
436     if (!This->initialized) return WINCODEC_ERR_NOTINITIALIZED;
437
438     *pCount = This->gif->ImageCount;
439
440     TRACE("<- %u\n", *pCount);
441
442     return S_OK;
443 }
444
445 static HRESULT WINAPI GifDecoder_GetFrame(IWICBitmapDecoder *iface,
446     UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
447 {
448     GifDecoder *This = (GifDecoder*)iface;
449     GifFrameDecode *result;
450     TRACE("(%p,%u,%p)\n", iface, index, ppIBitmapFrame);
451
452     if (!This->initialized) return WINCODEC_ERR_NOTINITIALIZED;
453
454     if (index >= This->gif->ImageCount) return E_INVALIDARG;
455
456     result = HeapAlloc(GetProcessHeap(), 0, sizeof(GifFrameDecode));
457     if (!result) return E_OUTOFMEMORY;
458
459     result->lpVtbl = &GifFrameDecode_Vtbl;
460     result->ref = 1;
461     result->frame = &This->gif->SavedImages[index];
462     IWICBitmapDecoder_AddRef(iface);
463     result->parent = This;
464
465     *ppIBitmapFrame = (IWICBitmapFrameDecode*)result;
466
467     return S_OK;
468 }
469
470 static const IWICBitmapDecoderVtbl GifDecoder_Vtbl = {
471     GifDecoder_QueryInterface,
472     GifDecoder_AddRef,
473     GifDecoder_Release,
474     GifDecoder_QueryCapability,
475     GifDecoder_Initialize,
476     GifDecoder_GetContainerFormat,
477     GifDecoder_GetDecoderInfo,
478     GifDecoder_CopyPalette,
479     GifDecoder_GetMetadataQueryReader,
480     GifDecoder_GetPreview,
481     GifDecoder_GetColorContexts,
482     GifDecoder_GetThumbnail,
483     GifDecoder_GetFrameCount,
484     GifDecoder_GetFrame
485 };
486
487 HRESULT GifDecoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
488 {
489     GifDecoder *This;
490     HRESULT ret;
491
492     TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
493
494     *ppv = NULL;
495
496     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
497
498     This = HeapAlloc(GetProcessHeap(), 0, sizeof(GifDecoder));
499     if (!This) return E_OUTOFMEMORY;
500
501     This->lpVtbl = &GifDecoder_Vtbl;
502     This->ref = 1;
503     This->initialized = FALSE;
504     This->gif = NULL;
505
506     ret = IUnknown_QueryInterface((IUnknown*)This, iid, ppv);
507     IUnknown_Release((IUnknown*)This);
508
509     return ret;
510 }