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