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