wined3d: Use GL_FRAMEBUFFER_SRGB when possible.
[wine/multimedia.git] / dlls / windowscodecs / gifformat.c
blob17de9aa26bbc90b363d5b5a4ac6493f67e431bd5
1 /*
2 * Copyright 2009 Vincent Povirk for CodeWeavers
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.
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.
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
19 #include "config.h"
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "objbase.h"
28 #include "wincodec.h"
30 #include "ungif.h"
32 #include "wincodecs_private.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
38 typedef struct {
39 IWICBitmapDecoder IWICBitmapDecoder_iface;
40 LONG ref;
41 BOOL initialized;
42 GifFileType *gif;
43 CRITICAL_SECTION lock;
44 } GifDecoder;
46 typedef struct {
47 IWICBitmapFrameDecode IWICBitmapFrameDecode_iface;
48 LONG ref;
49 SavedImage *frame;
50 GifDecoder *parent;
51 } GifFrameDecode;
53 static inline GifDecoder *impl_from_IWICBitmapDecoder(IWICBitmapDecoder *iface)
55 return CONTAINING_RECORD(iface, GifDecoder, IWICBitmapDecoder_iface);
58 static inline GifFrameDecode *impl_from_IWICBitmapFrameDecode(IWICBitmapFrameDecode *iface)
60 return CONTAINING_RECORD(iface, GifFrameDecode, IWICBitmapFrameDecode_iface);
63 static HRESULT WINAPI GifFrameDecode_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
64 void **ppv)
66 GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
67 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
69 if (!ppv) return E_INVALIDARG;
71 if (IsEqualIID(&IID_IUnknown, iid) ||
72 IsEqualIID(&IID_IWICBitmapSource, iid) ||
73 IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
75 *ppv = &This->IWICBitmapFrameDecode_iface;
77 else
79 *ppv = NULL;
80 return E_NOINTERFACE;
83 IUnknown_AddRef((IUnknown*)*ppv);
84 return S_OK;
87 static ULONG WINAPI GifFrameDecode_AddRef(IWICBitmapFrameDecode *iface)
89 GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
90 ULONG ref = InterlockedIncrement(&This->ref);
92 TRACE("(%p) refcount=%u\n", iface, ref);
94 return ref;
97 static ULONG WINAPI GifFrameDecode_Release(IWICBitmapFrameDecode *iface)
99 GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
100 ULONG ref = InterlockedDecrement(&This->ref);
102 TRACE("(%p) refcount=%u\n", iface, ref);
104 if (ref == 0)
106 IUnknown_Release((IUnknown*)This->parent);
107 HeapFree(GetProcessHeap(), 0, This);
110 return ref;
113 static HRESULT WINAPI GifFrameDecode_GetSize(IWICBitmapFrameDecode *iface,
114 UINT *puiWidth, UINT *puiHeight)
116 GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
117 TRACE("(%p,%p,%p)\n", iface, puiWidth, puiHeight);
119 *puiWidth = This->frame->ImageDesc.Width;
120 *puiHeight = This->frame->ImageDesc.Height;
122 return S_OK;
125 static HRESULT WINAPI GifFrameDecode_GetPixelFormat(IWICBitmapFrameDecode *iface,
126 WICPixelFormatGUID *pPixelFormat)
128 memcpy(pPixelFormat, &GUID_WICPixelFormat8bppIndexed, sizeof(GUID));
130 return S_OK;
133 static HRESULT WINAPI GifFrameDecode_GetResolution(IWICBitmapFrameDecode *iface,
134 double *pDpiX, double *pDpiY)
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);
141 *pDpiX = 96.0 / aspect;
142 *pDpiY = 96.0;
144 return S_OK;
147 static HRESULT WINAPI GifFrameDecode_CopyPalette(IWICBitmapFrameDecode *iface,
148 IWICPalette *pIPalette)
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);
157 if (!cm) cm = This->parent->gif->SColorMap;
159 if (cm->ColorCount > 256)
161 ERR("GIF contains %i colors???\n", cm->ColorCount);
162 return E_FAIL;
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;
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;
184 IWICPalette_InitializeCustom(pIPalette, colors, cm->ColorCount);
186 return S_OK;
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)
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;
199 if (!rc)
201 rect.X = 0;
202 rect.Y = 0;
203 rect.Width = srcwidth;
204 rect.Height = srcheight;
205 rc = &rect;
207 else
209 if (rc->X < 0 || rc->Y < 0 || rc->X+rc->Width > srcwidth || rc->Y+rc->Height > srcheight)
210 return E_INVALIDARG;
213 if (dststride < rc->Width)
214 return E_INVALIDARG;
216 if ((dststride * rc->Height) > dstbuffersize)
217 return E_INVALIDARG;
219 row_offset = rc->X;
221 dst = dstbuffer;
222 for (y=rc->Y; y-rc->Y < rc->Height; y++)
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;
236 return S_OK;
239 static HRESULT WINAPI GifFrameDecode_CopyPixels(IWICBitmapFrameDecode *iface,
240 const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
242 GifFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
243 TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
245 if (This->frame->ImageDesc.Interlace)
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);
251 else
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);
259 static HRESULT WINAPI GifFrameDecode_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
260 IWICMetadataQueryReader **ppIMetadataQueryReader)
262 TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
263 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
266 static HRESULT WINAPI GifFrameDecode_GetColorContexts(IWICBitmapFrameDecode *iface,
267 UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
269 TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
270 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
273 static HRESULT WINAPI GifFrameDecode_GetThumbnail(IWICBitmapFrameDecode *iface,
274 IWICBitmapSource **ppIThumbnail)
276 TRACE("(%p,%p)\n", iface, ppIThumbnail);
277 return WINCODEC_ERR_CODECNOTHUMBNAIL;
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
294 static HRESULT WINAPI GifDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid,
295 void **ppv)
297 GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
298 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
300 if (!ppv) return E_INVALIDARG;
302 if (IsEqualIID(&IID_IUnknown, iid) ||
303 IsEqualIID(&IID_IWICBitmapDecoder, iid))
305 *ppv = &This->IWICBitmapDecoder_iface;
307 else
309 *ppv = NULL;
310 return E_NOINTERFACE;
313 IUnknown_AddRef((IUnknown*)*ppv);
314 return S_OK;
317 static ULONG WINAPI GifDecoder_AddRef(IWICBitmapDecoder *iface)
319 GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
320 ULONG ref = InterlockedIncrement(&This->ref);
322 TRACE("(%p) refcount=%u\n", iface, ref);
324 return ref;
327 static ULONG WINAPI GifDecoder_Release(IWICBitmapDecoder *iface)
329 GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
330 ULONG ref = InterlockedDecrement(&This->ref);
332 TRACE("(%p) refcount=%u\n", iface, ref);
334 if (ref == 0)
336 This->lock.DebugInfo->Spare[0] = 0;
337 DeleteCriticalSection(&This->lock);
338 DGifCloseFile(This->gif);
339 HeapFree(GetProcessHeap(), 0, This);
342 return ref;
345 static HRESULT WINAPI GifDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *pIStream,
346 DWORD *pdwCapability)
348 FIXME("(%p,%p,%p): stub\n", iface, pIStream, pdwCapability);
349 return E_NOTIMPL;
352 static int _gif_inputfunc(GifFileType *gif, GifByteType *data, int len) {
353 IStream *stream = gif->UserData;
354 ULONG bytesread;
355 HRESULT hr;
357 if (!stream)
359 ERR("attempting to read file after initialization\n");
360 return 0;
363 hr = IStream_Read(stream, data, len, &bytesread);
364 if (hr != S_OK) bytesread = 0;
365 return bytesread;
368 static HRESULT WINAPI GifDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream,
369 WICDecodeOptions cacheOptions)
371 GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
372 LARGE_INTEGER seek;
373 int ret;
375 TRACE("(%p,%p,%x)\n", iface, pIStream, cacheOptions);
377 EnterCriticalSection(&This->lock);
379 if (This->initialized || This->gif)
381 WARN("already initialized\n");
382 LeaveCriticalSection(&This->lock);
383 return WINCODEC_ERR_WRONGSTATE;
386 /* seek to start of stream */
387 seek.QuadPart = 0;
388 IStream_Seek(pIStream, seek, STREAM_SEEK_SET, NULL);
390 /* read all data from the stream */
391 This->gif = DGifOpen((void*)pIStream, _gif_inputfunc);
392 if (!This->gif)
394 LeaveCriticalSection(&This->lock);
395 return E_FAIL;
398 ret = DGifSlurp(This->gif);
399 if (ret == GIF_ERROR)
401 LeaveCriticalSection(&This->lock);
402 return E_FAIL;
405 /* make sure we don't use the stream after this method returns */
406 This->gif->UserData = NULL;
408 This->initialized = TRUE;
410 LeaveCriticalSection(&This->lock);
412 return S_OK;
415 static HRESULT WINAPI GifDecoder_GetContainerFormat(IWICBitmapDecoder *iface,
416 GUID *pguidContainerFormat)
418 memcpy(pguidContainerFormat, &GUID_ContainerFormatGif, sizeof(GUID));
419 return S_OK;
422 static HRESULT WINAPI GifDecoder_GetDecoderInfo(IWICBitmapDecoder *iface,
423 IWICBitmapDecoderInfo **ppIDecoderInfo)
425 HRESULT hr;
426 IWICComponentInfo *compinfo;
428 TRACE("(%p,%p)\n", iface, ppIDecoderInfo);
430 hr = CreateComponentInfo(&CLSID_WICGifDecoder, &compinfo);
431 if (FAILED(hr)) return hr;
433 hr = IWICComponentInfo_QueryInterface(compinfo, &IID_IWICBitmapDecoderInfo,
434 (void**)ppIDecoderInfo);
436 IWICComponentInfo_Release(compinfo);
438 return hr;
441 static HRESULT WINAPI GifDecoder_CopyPalette(IWICBitmapDecoder *iface,
442 IWICPalette *pIPalette)
444 TRACE("(%p,%p)\n", iface, pIPalette);
445 return WINCODEC_ERR_PALETTEUNAVAILABLE;
448 static HRESULT WINAPI GifDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface,
449 IWICMetadataQueryReader **ppIMetadataQueryReader)
451 TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
452 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
455 static HRESULT WINAPI GifDecoder_GetPreview(IWICBitmapDecoder *iface,
456 IWICBitmapSource **ppIBitmapSource)
458 TRACE("(%p,%p)\n", iface, ppIBitmapSource);
459 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
462 static HRESULT WINAPI GifDecoder_GetColorContexts(IWICBitmapDecoder *iface,
463 UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
465 TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
466 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
469 static HRESULT WINAPI GifDecoder_GetThumbnail(IWICBitmapDecoder *iface,
470 IWICBitmapSource **ppIThumbnail)
472 TRACE("(%p,%p)\n", iface, ppIThumbnail);
473 return WINCODEC_ERR_CODECNOTHUMBNAIL;
476 static HRESULT WINAPI GifDecoder_GetFrameCount(IWICBitmapDecoder *iface,
477 UINT *pCount)
479 GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
480 TRACE("(%p,%p)\n", iface, pCount);
482 if (!This->initialized) return WINCODEC_ERR_NOTINITIALIZED;
484 *pCount = This->gif->ImageCount;
486 TRACE("<- %u\n", *pCount);
488 return S_OK;
491 static HRESULT WINAPI GifDecoder_GetFrame(IWICBitmapDecoder *iface,
492 UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
494 GifDecoder *This = impl_from_IWICBitmapDecoder(iface);
495 GifFrameDecode *result;
496 TRACE("(%p,%u,%p)\n", iface, index, ppIBitmapFrame);
498 if (!This->initialized) return WINCODEC_ERR_NOTINITIALIZED;
500 if (index >= This->gif->ImageCount) return E_INVALIDARG;
502 result = HeapAlloc(GetProcessHeap(), 0, sizeof(GifFrameDecode));
503 if (!result) return E_OUTOFMEMORY;
505 result->IWICBitmapFrameDecode_iface.lpVtbl = &GifFrameDecode_Vtbl;
506 result->ref = 1;
507 result->frame = &This->gif->SavedImages[index];
508 IWICBitmapDecoder_AddRef(iface);
509 result->parent = This;
511 *ppIBitmapFrame = &result->IWICBitmapFrameDecode_iface;
513 return S_OK;
516 static const IWICBitmapDecoderVtbl GifDecoder_Vtbl = {
517 GifDecoder_QueryInterface,
518 GifDecoder_AddRef,
519 GifDecoder_Release,
520 GifDecoder_QueryCapability,
521 GifDecoder_Initialize,
522 GifDecoder_GetContainerFormat,
523 GifDecoder_GetDecoderInfo,
524 GifDecoder_CopyPalette,
525 GifDecoder_GetMetadataQueryReader,
526 GifDecoder_GetPreview,
527 GifDecoder_GetColorContexts,
528 GifDecoder_GetThumbnail,
529 GifDecoder_GetFrameCount,
530 GifDecoder_GetFrame
533 HRESULT GifDecoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
535 GifDecoder *This;
536 HRESULT ret;
538 TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
540 *ppv = NULL;
542 if (pUnkOuter) return CLASS_E_NOAGGREGATION;
544 This = HeapAlloc(GetProcessHeap(), 0, sizeof(GifDecoder));
545 if (!This) return E_OUTOFMEMORY;
547 This->IWICBitmapDecoder_iface.lpVtbl = &GifDecoder_Vtbl;
548 This->ref = 1;
549 This->initialized = FALSE;
550 This->gif = NULL;
551 InitializeCriticalSection(&This->lock);
552 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": GifDecoder.lock");
554 ret = IWICBitmapDecoder_QueryInterface(&This->IWICBitmapDecoder_iface, iid, ppv);
555 IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
557 return ret;