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