diff --git a/patches/patchinstall.sh b/patches/patchinstall.sh index cb3a247d..6d93f40e 100755 --- a/patches/patchinstall.sh +++ b/patches/patchinstall.sh @@ -349,6 +349,7 @@ patch_enable_all () enable_wbemdisp_Printer="$1" enable_widl_SLTG_Typelib_Support="$1" enable_windowscodecs_32bppGrayFloat="$1" + enable_windowscodecs_GIF_Encoder="$1" enable_windowscodecs_IMILBitmapSource="$1" enable_windowscodecs_IWICPalette_InitializeFromBitmap="$1" enable_windowscodecs_Palette_Images="$1" @@ -1229,6 +1230,9 @@ patch_enable () windowscodecs-32bppGrayFloat) enable_windowscodecs_32bppGrayFloat="$2" ;; + windowscodecs-GIF_Encoder) + enable_windowscodecs_GIF_Encoder="$2" + ;; windowscodecs-IMILBitmapSource) enable_windowscodecs_IMILBitmapSource="$2" ;; @@ -7197,6 +7201,34 @@ if test "$enable_windowscodecs_32bppGrayFloat" -eq 1; then ) >> "$patchlist" fi +# Patchset windowscodecs-GIF_Encoder +# | +# | Modified files: +# | * dlls/windowscodecs/bmpencode.c, dlls/windowscodecs/clsfactory.c, dlls/windowscodecs/gifformat.c, +# | dlls/windowscodecs/info.c, dlls/windowscodecs/jpegformat.c, dlls/windowscodecs/regsvr.c, +# | dlls/windowscodecs/tiffformat.c, dlls/windowscodecs/wincodecs_private.h +# | +if test "$enable_windowscodecs_GIF_Encoder" -eq 1; then + patch_apply windowscodecs-GIF_Encoder/0001-windowscodecs-Implement-IWICBitmapEncoder-GetEncoder.patch + patch_apply windowscodecs-GIF_Encoder/0002-windowscodecs-Implement-IWICBitmapEncoderInfo-GetFil.patch + patch_apply windowscodecs-GIF_Encoder/0003-windowscodecs-Implement-IWICBitmapFrameEncode-SetPal.patch + patch_apply windowscodecs-GIF_Encoder/0004-windowscodecs-Implement-IWICBitmapEncoder-GetEncoder.patch + patch_apply windowscodecs-GIF_Encoder/0005-windowscodecs-Avoid-crashing-if-no-IPropertyBag2-was.patch + patch_apply windowscodecs-GIF_Encoder/0006-windowscodecs-Implement-IWICBitmapEncoder-GetEncoder.patch + patch_apply windowscodecs-GIF_Encoder/0007-windowscodecs-Avoid-crashing-if-no-IPropertyBag2-was.patch + patch_apply windowscodecs-GIF_Encoder/0008-windowscodecs-Add-initial-implementation-of-the-GIF-.patch + ( + echo '+ { "Dmitry Timoshkov", "windowscodecs: Implement IWICBitmapEncoder::GetEncoderInfo in BMP encoder.", 1 },'; + echo '+ { "Dmitry Timoshkov", "windowscodecs: Implement IWICBitmapEncoderInfo::GetFileExtensions.", 1 },'; + echo '+ { "Dmitry Timoshkov", "windowscodecs: Implement IWICBitmapFrameEncode::SetPalette in JPEG encoder,.", 1 },'; + echo '+ { "Dmitry Timoshkov", "windowscodecs: Implement IWICBitmapEncoder::GetEncoderInfo in JPEG encoder.", 1 },'; + echo '+ { "Dmitry Timoshkov", "windowscodecs: Avoid crashing if no IPropertyBag2 was passed to IWICBitmapEncoder::CreateNewFrame in JPEG encoder.", 1 },'; + echo '+ { "Dmitry Timoshkov", "windowscodecs: Implement IWICBitmapEncoder::GetEncoderInfo in TIFF encoder.", 1 },'; + echo '+ { "Dmitry Timoshkov", "windowscodecs: Avoid crashing if no IPropertyBag2 was passed to IWICBitmapEncoder::CreateNewFrame in TIFF encoder.", 1 },'; + echo '+ { "Dmitry Timoshkov", "windowscodecs: Add initial implementation of the GIF encoder.", 1 },'; + ) >> "$patchlist" +fi + # Patchset windowscodecs-IMILBitmapSource # | # | This patchset fixes the following Wine bugs: diff --git a/patches/windowscodecs-GIF_Encoder/0001-windowscodecs-Implement-IWICBitmapEncoder-GetEncoder.patch b/patches/windowscodecs-GIF_Encoder/0001-windowscodecs-Implement-IWICBitmapEncoder-GetEncoder.patch new file mode 100644 index 00000000..c50950f5 --- /dev/null +++ b/patches/windowscodecs-GIF_Encoder/0001-windowscodecs-Implement-IWICBitmapEncoder-GetEncoder.patch @@ -0,0 +1,44 @@ +From c79b0e20ed84142dd3652d4f1e08abc8b13a9f8b Mon Sep 17 00:00:00 2001 +From: Dmitry Timoshkov +Date: Sun, 16 Oct 2016 21:06:32 +0800 +Subject: windowscodecs: Implement IWICBitmapEncoder::GetEncoderInfo in BMP + encoder. + +--- + dlls/windowscodecs/bmpencode.c | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +diff --git a/dlls/windowscodecs/bmpencode.c b/dlls/windowscodecs/bmpencode.c +index 3dce8bb..8bb5c10 100644 +--- a/dlls/windowscodecs/bmpencode.c ++++ b/dlls/windowscodecs/bmpencode.c +@@ -451,11 +451,22 @@ static HRESULT WINAPI BmpEncoder_GetContainerFormat(IWICBitmapEncoder *iface, + return S_OK; + } + +-static HRESULT WINAPI BmpEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, +- IWICBitmapEncoderInfo **ppIEncoderInfo) ++static HRESULT WINAPI BmpEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **info) + { +- FIXME("(%p,%p): stub\n", iface, ppIEncoderInfo); +- return E_NOTIMPL; ++ IWICComponentInfo *comp_info; ++ HRESULT hr; ++ ++ TRACE("%p,%p\n", iface, info); ++ ++ if (!info) return E_INVALIDARG; ++ ++ hr = CreateComponentInfo(&CLSID_WICBmpEncoder, &comp_info); ++ if (hr == S_OK) ++ { ++ hr = IWICComponentInfo_QueryInterface(comp_info, &IID_IWICBitmapEncoderInfo, (void **)info); ++ IWICComponentInfo_Release(comp_info); ++ } ++ return hr; + } + + static HRESULT WINAPI BmpEncoder_SetColorContexts(IWICBitmapEncoder *iface, +-- +2.9.0 + diff --git a/patches/windowscodecs-GIF_Encoder/0002-windowscodecs-Implement-IWICBitmapEncoderInfo-GetFil.patch b/patches/windowscodecs-GIF_Encoder/0002-windowscodecs-Implement-IWICBitmapEncoderInfo-GetFil.patch new file mode 100644 index 00000000..19ee502a --- /dev/null +++ b/patches/windowscodecs-GIF_Encoder/0002-windowscodecs-Implement-IWICBitmapEncoderInfo-GetFil.patch @@ -0,0 +1,31 @@ +From 1977ebd81ff7f7023daf00ae7eecee1c8f2e7260 Mon Sep 17 00:00:00 2001 +From: Dmitry Timoshkov +Date: Sun, 16 Oct 2016 17:30:39 +0800 +Subject: windowscodecs: Implement IWICBitmapEncoderInfo::GetFileExtensions. + +--- + dlls/windowscodecs/info.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/dlls/windowscodecs/info.c b/dlls/windowscodecs/info.c +index 84b80bc..1a93491 100644 +--- a/dlls/windowscodecs/info.c ++++ b/dlls/windowscodecs/info.c +@@ -868,8 +868,12 @@ static HRESULT WINAPI BitmapEncoderInfo_GetMimeTypes(IWICBitmapEncoderInfo *ifac + static HRESULT WINAPI BitmapEncoderInfo_GetFileExtensions(IWICBitmapEncoderInfo *iface, + UINT cchFileExtensions, WCHAR *wzFileExtensions, UINT *pcchActual) + { +- FIXME("(%p,%u,%p,%p): stub\n", iface, cchFileExtensions, wzFileExtensions, pcchActual); +- return E_NOTIMPL; ++ BitmapEncoderInfo *This = impl_from_IWICBitmapEncoderInfo(iface); ++ ++ TRACE("(%p,%u,%p,%p)\n", iface, cchFileExtensions, wzFileExtensions, pcchActual); ++ ++ return ComponentInfo_GetStringValue(This->classkey, fileextensions_valuename, ++ cchFileExtensions, wzFileExtensions, pcchActual); + } + + static HRESULT WINAPI BitmapEncoderInfo_DoesSupportAnimation(IWICBitmapEncoderInfo *iface, +-- +2.9.0 + diff --git a/patches/windowscodecs-GIF_Encoder/0003-windowscodecs-Implement-IWICBitmapFrameEncode-SetPal.patch b/patches/windowscodecs-GIF_Encoder/0003-windowscodecs-Implement-IWICBitmapFrameEncode-SetPal.patch new file mode 100644 index 00000000..2b36b13e --- /dev/null +++ b/patches/windowscodecs-GIF_Encoder/0003-windowscodecs-Implement-IWICBitmapFrameEncode-SetPal.patch @@ -0,0 +1,62 @@ +From 69942a5f16dff9ea123fd9e8819cde00c7fbf3ae Mon Sep 17 00:00:00 2001 +From: Dmitry Timoshkov +Date: Sun, 16 Oct 2016 17:32:48 +0800 +Subject: windowscodecs: Implement IWICBitmapFrameEncode::SetPalette in JPEG + encoder, + +--- + dlls/windowscodecs/jpegformat.c | 23 ++++++++++++++++++++--- + 1 file changed, 20 insertions(+), 3 deletions(-) + +diff --git a/dlls/windowscodecs/jpegformat.c b/dlls/windowscodecs/jpegformat.c +index b7998be..9ec64e7 100644 +--- a/dlls/windowscodecs/jpegformat.c ++++ b/dlls/windowscodecs/jpegformat.c +@@ -861,6 +861,8 @@ typedef struct JpegEncoder { + double xres, yres; + const jpeg_compress_format *format; + IStream *stream; ++ WICColor palette[256]; ++ UINT colors; + CRITICAL_SECTION lock; + BYTE dest_buffer[1024]; + } JpegEncoder; +@@ -1063,10 +1065,24 @@ static HRESULT WINAPI JpegEncoder_Frame_SetColorContexts(IWICBitmapFrameEncode * + } + + static HRESULT WINAPI JpegEncoder_Frame_SetPalette(IWICBitmapFrameEncode *iface, +- IWICPalette *pIPalette) ++ IWICPalette *palette) + { +- FIXME("(%p,%p): stub\n", iface, pIPalette); +- return WINCODEC_ERR_UNSUPPORTEDOPERATION; ++ JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface); ++ HRESULT hr; ++ ++ TRACE("(%p,%p)\n", iface, palette); ++ ++ if (!palette) return E_INVALIDARG; ++ ++ EnterCriticalSection(&This->lock); ++ ++ if (This->initialized) ++ hr = IWICPalette_GetColors(palette, 256, This->palette, &This->colors); ++ else ++ hr = WINCODEC_ERR_NOTINITIALIZED; ++ ++ LeaveCriticalSection(&This->lock); ++ return hr; + } + + static HRESULT WINAPI JpegEncoder_Frame_SetThumbnail(IWICBitmapFrameEncode *iface, +@@ -1516,6 +1532,7 @@ HRESULT JpegEncoder_CreateInstance(REFIID iid, void** ppv) + This->xres = This->yres = 0.0; + This->format = NULL; + This->stream = NULL; ++ This->colors = 0; + InitializeCriticalSection(&This->lock); + This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JpegEncoder.lock"); + +-- +2.9.0 + diff --git a/patches/windowscodecs-GIF_Encoder/0004-windowscodecs-Implement-IWICBitmapEncoder-GetEncoder.patch b/patches/windowscodecs-GIF_Encoder/0004-windowscodecs-Implement-IWICBitmapEncoder-GetEncoder.patch new file mode 100644 index 00000000..c2a43a41 --- /dev/null +++ b/patches/windowscodecs-GIF_Encoder/0004-windowscodecs-Implement-IWICBitmapEncoder-GetEncoder.patch @@ -0,0 +1,44 @@ +From 7e6eb1a23949bddd3538447d5393d4b66d061f1b Mon Sep 17 00:00:00 2001 +From: Dmitry Timoshkov +Date: Sun, 16 Oct 2016 17:34:21 +0800 +Subject: windowscodecs: Implement IWICBitmapEncoder::GetEncoderInfo in JPEG + encoder. + +--- + dlls/windowscodecs/jpegformat.c | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +diff --git a/dlls/windowscodecs/jpegformat.c b/dlls/windowscodecs/jpegformat.c +index 9ec64e7..7d8beab 100644 +--- a/dlls/windowscodecs/jpegformat.c ++++ b/dlls/windowscodecs/jpegformat.c +@@ -1386,11 +1386,22 @@ static HRESULT WINAPI JpegEncoder_GetContainerFormat(IWICBitmapEncoder *iface, + return E_NOTIMPL; + } + +-static HRESULT WINAPI JpegEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, +- IWICBitmapEncoderInfo **ppIEncoderInfo) ++static HRESULT WINAPI JpegEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **info) + { +- FIXME("(%p,%p): stub\n", iface, ppIEncoderInfo); +- return E_NOTIMPL; ++ IWICComponentInfo *comp_info; ++ HRESULT hr; ++ ++ TRACE("%p,%p\n", iface, info); ++ ++ if (!info) return E_INVALIDARG; ++ ++ hr = CreateComponentInfo(&CLSID_WICJpegEncoder, &comp_info); ++ if (hr == S_OK) ++ { ++ hr = IWICComponentInfo_QueryInterface(comp_info, &IID_IWICBitmapEncoderInfo, (void **)info); ++ IWICComponentInfo_Release(comp_info); ++ } ++ return hr; + } + + static HRESULT WINAPI JpegEncoder_SetColorContexts(IWICBitmapEncoder *iface, +-- +2.9.0 + diff --git a/patches/windowscodecs-GIF_Encoder/0005-windowscodecs-Avoid-crashing-if-no-IPropertyBag2-was.patch b/patches/windowscodecs-GIF_Encoder/0005-windowscodecs-Avoid-crashing-if-no-IPropertyBag2-was.patch new file mode 100644 index 00000000..cb824570 --- /dev/null +++ b/patches/windowscodecs-GIF_Encoder/0005-windowscodecs-Avoid-crashing-if-no-IPropertyBag2-was.patch @@ -0,0 +1,36 @@ +From 6a2b1a19e6f4b6eec02097d3ba55958ec58a7146 Mon Sep 17 00:00:00 2001 +From: Dmitry Timoshkov +Date: Sun, 16 Oct 2016 17:36:32 +0800 +Subject: windowscodecs: Avoid crashing if no IPropertyBag2 was passed to + IWICBitmapEncoder::CreateNewFrame in JPEG encoder. + +--- + dlls/windowscodecs/jpegformat.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/dlls/windowscodecs/jpegformat.c b/dlls/windowscodecs/jpegformat.c +index 7d8beab..45bb88f 100644 +--- a/dlls/windowscodecs/jpegformat.c ++++ b/dlls/windowscodecs/jpegformat.c +@@ -1451,11 +1451,14 @@ static HRESULT WINAPI JpegEncoder_CreateNewFrame(IWICBitmapEncoder *iface, + return WINCODEC_ERR_NOTINITIALIZED; + } + +- hr = CreatePropertyBag2(NULL, 0, ppIEncoderOptions); +- if (FAILED(hr)) ++ if (ppIEncoderOptions) + { +- LeaveCriticalSection(&This->lock); +- return hr; ++ hr = CreatePropertyBag2(NULL, 0, ppIEncoderOptions); ++ if (FAILED(hr)) ++ { ++ LeaveCriticalSection(&This->lock); ++ return hr; ++ } + } + + This->frame_count = 1; +-- +2.9.0 + diff --git a/patches/windowscodecs-GIF_Encoder/0006-windowscodecs-Implement-IWICBitmapEncoder-GetEncoder.patch b/patches/windowscodecs-GIF_Encoder/0006-windowscodecs-Implement-IWICBitmapEncoder-GetEncoder.patch new file mode 100644 index 00000000..0e385e3e --- /dev/null +++ b/patches/windowscodecs-GIF_Encoder/0006-windowscodecs-Implement-IWICBitmapEncoder-GetEncoder.patch @@ -0,0 +1,44 @@ +From ac87acc7cb36198ed28d092b0278ea17ff6006b7 Mon Sep 17 00:00:00 2001 +From: Dmitry Timoshkov +Date: Sun, 16 Oct 2016 17:38:39 +0800 +Subject: windowscodecs: Implement IWICBitmapEncoder::GetEncoderInfo in TIFF + encoder. + +--- + dlls/windowscodecs/tiffformat.c | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +diff --git a/dlls/windowscodecs/tiffformat.c b/dlls/windowscodecs/tiffformat.c +index 1dacd87..4946e4d 100644 +--- a/dlls/windowscodecs/tiffformat.c ++++ b/dlls/windowscodecs/tiffformat.c +@@ -1873,11 +1873,22 @@ static HRESULT WINAPI TiffEncoder_GetContainerFormat(IWICBitmapEncoder *iface, + return S_OK; + } + +-static HRESULT WINAPI TiffEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, +- IWICBitmapEncoderInfo **ppIEncoderInfo) ++static HRESULT WINAPI TiffEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **info) + { +- FIXME("(%p,%p): stub\n", iface, ppIEncoderInfo); +- return E_NOTIMPL; ++ IWICComponentInfo *comp_info; ++ HRESULT hr; ++ ++ TRACE("%p,%p\n", iface, info); ++ ++ if (!info) return E_INVALIDARG; ++ ++ hr = CreateComponentInfo(&CLSID_WICTiffEncoder, &comp_info); ++ if (hr == S_OK) ++ { ++ hr = IWICComponentInfo_QueryInterface(comp_info, &IID_IWICBitmapEncoderInfo, (void **)info); ++ IWICComponentInfo_Release(comp_info); ++ } ++ return hr; + } + + static HRESULT WINAPI TiffEncoder_SetColorContexts(IWICBitmapEncoder *iface, +-- +2.9.0 + diff --git a/patches/windowscodecs-GIF_Encoder/0007-windowscodecs-Avoid-crashing-if-no-IPropertyBag2-was.patch b/patches/windowscodecs-GIF_Encoder/0007-windowscodecs-Avoid-crashing-if-no-IPropertyBag2-was.patch new file mode 100644 index 00000000..f1e840f4 --- /dev/null +++ b/patches/windowscodecs-GIF_Encoder/0007-windowscodecs-Avoid-crashing-if-no-IPropertyBag2-was.patch @@ -0,0 +1,35 @@ +From c89c3a6950bb3514bef044df6488000861a261e4 Mon Sep 17 00:00:00 2001 +From: Dmitry Timoshkov +Date: Sun, 16 Oct 2016 17:40:14 +0800 +Subject: windowscodecs: Avoid crashing if no IPropertyBag2 was passed to + IWICBitmapEncoder::CreateNewFrame in TIFF encoder. + +--- + dlls/windowscodecs/tiffformat.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/windowscodecs/tiffformat.c b/dlls/windowscodecs/tiffformat.c +index 4946e4d..5071147 100644 +--- a/dlls/windowscodecs/tiffformat.c ++++ b/dlls/windowscodecs/tiffformat.c +@@ -1938,7 +1938,7 @@ static HRESULT WINAPI TiffEncoder_CreateNewFrame(IWICBitmapEncoder *iface, + hr = E_FAIL; + } + +- if (SUCCEEDED(hr)) ++ if (SUCCEEDED(hr) && ppIEncoderOptions) + { + PROPBAG2 opts[2]= {{0}}; + opts[0].pstrName = (LPOLESTR)wszTiffCompressionMethod; +@@ -1997,7 +1997,7 @@ static HRESULT WINAPI TiffEncoder_CreateNewFrame(IWICBitmapEncoder *iface, + else + hr = E_OUTOFMEMORY; + +- if (FAILED(hr)) ++ if (FAILED(hr) && ppIEncoderOptions) + { + IPropertyBag2_Release(*ppIEncoderOptions); + *ppIEncoderOptions = NULL; +-- +2.9.0 + diff --git a/patches/windowscodecs-GIF_Encoder/0008-windowscodecs-Add-initial-implementation-of-the-GIF-.patch b/patches/windowscodecs-GIF_Encoder/0008-windowscodecs-Add-initial-implementation-of-the-GIF-.patch new file mode 100644 index 00000000..fc5183fe --- /dev/null +++ b/patches/windowscodecs-GIF_Encoder/0008-windowscodecs-Add-initial-implementation-of-the-GIF-.patch @@ -0,0 +1,1094 @@ +From 0c50bf1c5e19330493e0cd108a703fd135f7541a Mon Sep 17 00:00:00 2001 +From: Dmitry Timoshkov +Date: Sun, 16 Oct 2016 17:44:31 +0800 +Subject: windowscodecs: Add initial implementation of the GIF encoder. + +--- + dlls/windowscodecs/clsfactory.c | 1 + + dlls/windowscodecs/gifformat.c | 999 +++++++++++++++++++++++++++++++-- + dlls/windowscodecs/regsvr.c | 10 + + dlls/windowscodecs/wincodecs_private.h | 1 + + 4 files changed, 977 insertions(+), 34 deletions(-) + +diff --git a/dlls/windowscodecs/clsfactory.c b/dlls/windowscodecs/clsfactory.c +index 77eeedc..98938eb 100644 +--- a/dlls/windowscodecs/clsfactory.c ++++ b/dlls/windowscodecs/clsfactory.c +@@ -49,6 +49,7 @@ static const classinfo wic_classes[] = { + {&CLSID_WICPngEncoder, PngEncoder_CreateInstance}, + {&CLSID_WICBmpEncoder, BmpEncoder_CreateInstance}, + {&CLSID_WICGifDecoder, GifDecoder_CreateInstance}, ++ {&CLSID_WICGifEncoder, GifEncoder_CreateInstance}, + {&CLSID_WICIcoDecoder, IcoDecoder_CreateInstance}, + {&CLSID_WICJpegDecoder, JpegDecoder_CreateInstance}, + {&CLSID_WICJpegEncoder, JpegEncoder_CreateInstance}, +diff --git a/dlls/windowscodecs/gifformat.c b/dlls/windowscodecs/gifformat.c +index ddff88b..b5c56aa 100644 +--- a/dlls/windowscodecs/gifformat.c ++++ b/dlls/windowscodecs/gifformat.c +@@ -1,6 +1,6 @@ + /* + * Copyright 2009 Vincent Povirk for CodeWeavers +- * Copyright 2012 Dmitry Timoshkov ++ * Copyright 2012,2016 Dmitry Timoshkov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -36,6 +36,40 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(wincodecs); + ++#include "pshpack1.h" ++ ++struct logical_screen_descriptor ++{ ++ char signature[6]; ++ USHORT width; ++ USHORT height; ++ BYTE packed; ++ /* global_color_table_flag : 1; ++ * color_resolution : 3; ++ * sort_flag : 1; ++ * global_color_table_size : 3; ++ */ ++ BYTE background_color_index; ++ BYTE pixel_aspect_ratio; ++}; ++ ++struct image_descriptor ++{ ++ USHORT left; ++ USHORT top; ++ USHORT width; ++ USHORT height; ++ BYTE packed; ++ /* local_color_table_flag : 1; ++ * interlace_flag : 1; ++ * sort_flag : 1; ++ * reserved : 2; ++ * local_color_table_size : 3; ++ */ ++}; ++ ++#include "poppack.h" ++ + static LPWSTR strdupAtoW(const char *src) + { + int len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0); +@@ -47,22 +81,7 @@ static LPWSTR strdupAtoW(const char *src) + static HRESULT load_LSD_metadata(IStream *stream, const GUID *vendor, DWORD options, + MetadataItem **items, DWORD *count) + { +-#include "pshpack1.h" +- struct logical_screen_descriptor +- { +- char signature[6]; +- USHORT width; +- USHORT height; +- BYTE packed; +- /* global_color_table_flag : 1; +- * color_resolution : 3; +- * sort_flag : 1; +- * global_color_table_size : 3; +- */ +- BYTE background_color_index; +- BYTE pixel_aspect_ratio; +- } lsd_data; +-#include "poppack.h" ++ struct logical_screen_descriptor lsd_data; + HRESULT hr; + ULONG bytesread, i; + MetadataItem *result; +@@ -147,23 +166,6 @@ HRESULT LSDReader_CreateInstance(REFIID iid, void **ppv) + return MetadataReader_Create(&LSDReader_Vtbl, iid, ppv); + } + +-#include "pshpack1.h" +-struct image_descriptor +-{ +- USHORT left; +- USHORT top; +- USHORT width; +- USHORT height; +- BYTE packed; +- /* local_color_table_flag : 1; +- * interlace_flag : 1; +- * sort_flag : 1; +- * reserved : 2; +- * local_color_table_size : 3; +- */ +-}; +-#include "poppack.h" +- + static HRESULT load_IMD_metadata(IStream *stream, const GUID *vendor, DWORD options, + MetadataItem **items, DWORD *count) + { +@@ -1455,3 +1457,932 @@ HRESULT GifDecoder_CreateInstance(REFIID iid, void** ppv) + + return ret; + } ++ ++typedef struct GifEncoder ++{ ++ IWICBitmapEncoder IWICBitmapEncoder_iface; ++ LONG ref; ++ IStream *stream; ++ CRITICAL_SECTION lock; ++ BOOL initialized, info_written, committed; ++ UINT n_frames; ++ WICColor palette[256]; ++ UINT colors; ++} GifEncoder; ++ ++static inline GifEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface) ++{ ++ return CONTAINING_RECORD(iface, GifEncoder, IWICBitmapEncoder_iface); ++} ++ ++typedef struct GifFrameEncode ++{ ++ IWICBitmapFrameEncode IWICBitmapFrameEncode_iface; ++ LONG ref; ++ GifEncoder *encoder; ++ BOOL initialized, interlace, committed; ++ UINT width, height, lines; ++ double xres, yres; ++ WICColor palette[256]; ++ UINT colors; ++ BYTE *image_data; ++} GifFrameEncode; ++ ++static inline GifFrameEncode *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface) ++{ ++ return CONTAINING_RECORD(iface, GifFrameEncode, IWICBitmapFrameEncode_iface); ++} ++ ++static HRESULT WINAPI GifFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid, void **ppv) ++{ ++ TRACE("%p,%s,%p\n", iface, debugstr_guid(iid), ppv); ++ ++ if (!ppv) return E_INVALIDARG; ++ ++ if (IsEqualIID(&IID_IUnknown, iid) || ++ IsEqualIID(&IID_IWICBitmapFrameEncode, iid)) ++ { ++ IWICBitmapFrameEncode_AddRef(iface); ++ *ppv = iface; ++ return S_OK; ++ } ++ ++ *ppv = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG WINAPI GifFrameEncode_AddRef(IWICBitmapFrameEncode *iface) ++{ ++ GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); ++ ULONG ref = InterlockedIncrement(&This->ref); ++ ++ TRACE("%p -> %u\n", iface, ref); ++ return ref; ++} ++ ++static ULONG WINAPI GifFrameEncode_Release(IWICBitmapFrameEncode *iface) ++{ ++ GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); ++ ULONG ref = InterlockedDecrement(&This->ref); ++ ++ TRACE("%p -> %u\n", iface, ref); ++ ++ if (!ref) ++ { ++ IWICBitmapEncoder_Release(&This->encoder->IWICBitmapEncoder_iface); ++ HeapFree(GetProcessHeap(), 0, This->image_data); ++ HeapFree(GetProcessHeap(), 0, This); ++ } ++ ++ return ref; ++} ++ ++static HRESULT WINAPI GifFrameEncode_Initialize(IWICBitmapFrameEncode *iface, IPropertyBag2 *options) ++{ ++ GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); ++ HRESULT hr; ++ ++ TRACE("%p,%p\n", iface, options); ++ ++ EnterCriticalSection(&This->encoder->lock); ++ ++ if (!This->initialized) ++ { ++ This->initialized = TRUE; ++ hr = S_OK; ++ } ++ else ++ hr = WINCODEC_ERR_WRONGSTATE; ++ ++ LeaveCriticalSection(&This->encoder->lock); ++ ++ return hr; ++} ++ ++static HRESULT WINAPI GifFrameEncode_SetSize(IWICBitmapFrameEncode *iface, UINT width, UINT height) ++{ ++ GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); ++ HRESULT hr; ++ ++ TRACE("%p,%u,%u\n", iface, width, height); ++ ++ if (!width || !height) return E_INVALIDARG; ++ ++ EnterCriticalSection(&This->encoder->lock); ++ ++ if (This->initialized) ++ { ++ HeapFree(GetProcessHeap(), 0, This->image_data); ++ ++ This->image_data = HeapAlloc(GetProcessHeap(), 0, width * height); ++ if (This->image_data) ++ { ++ This->width = width; ++ This->height = height; ++ hr = S_OK; ++ } ++ else ++ hr = E_OUTOFMEMORY; ++ } ++ else ++ hr = WINCODEC_ERR_WRONGSTATE; ++ ++ LeaveCriticalSection(&This->encoder->lock); ++ ++ return hr; ++} ++ ++static HRESULT WINAPI GifFrameEncode_SetResolution(IWICBitmapFrameEncode *iface, double xres, double yres) ++{ ++ GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); ++ HRESULT hr; ++ ++ TRACE("%p,%f,%f\n", iface, xres, yres); ++ ++ EnterCriticalSection(&This->encoder->lock); ++ ++ if (This->initialized) ++ { ++ This->xres = xres; ++ This->yres = yres; ++ hr = S_OK; ++ } ++ else ++ hr = WINCODEC_ERR_WRONGSTATE; ++ ++ LeaveCriticalSection(&This->encoder->lock); ++ ++ return hr; ++} ++ ++static HRESULT WINAPI GifFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface, WICPixelFormatGUID *format) ++{ ++ GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); ++ HRESULT hr; ++ ++ TRACE("%p,%s\n", iface, debugstr_guid(format)); ++ ++ if (!format) return E_INVALIDARG; ++ ++ EnterCriticalSection(&This->encoder->lock); ++ ++ if (This->initialized) ++ { ++ *format = GUID_WICPixelFormat8bppIndexed; ++ hr = S_OK; ++ } ++ else ++ hr = WINCODEC_ERR_WRONGSTATE; ++ ++ LeaveCriticalSection(&This->encoder->lock); ++ ++ return hr; ++} ++ ++static HRESULT WINAPI GifFrameEncode_SetColorContexts(IWICBitmapFrameEncode *iface, UINT count, IWICColorContext **context) ++{ ++ FIXME("%p,%u,%p: stub\n", iface, count, context); ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI GifFrameEncode_SetPalette(IWICBitmapFrameEncode *iface, IWICPalette *palette) ++{ ++ GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); ++ HRESULT hr; ++ ++ TRACE("%p,%p\n", iface, palette); ++ ++ if (!palette) return E_INVALIDARG; ++ ++ EnterCriticalSection(&This->encoder->lock); ++ ++ if (This->initialized) ++ hr = IWICPalette_GetColors(palette, 256, This->palette, &This->colors); ++ else ++ hr = WINCODEC_ERR_NOTINITIALIZED; ++ ++ LeaveCriticalSection(&This->encoder->lock); ++ return hr; ++} ++ ++static HRESULT WINAPI GifFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface, IWICBitmapSource *thumbnail) ++{ ++ FIXME("%p,%p: stub\n", iface, thumbnail); ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI GifFrameEncode_WritePixels(IWICBitmapFrameEncode *iface, UINT lines, UINT stride, UINT size, BYTE *pixels) ++{ ++ GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); ++ HRESULT hr; ++ ++ TRACE("%p,%u,%u,%u,%p\n", iface, lines, stride, size, pixels); ++ ++ if (!pixels) return E_INVALIDARG; ++ ++ EnterCriticalSection(&This->encoder->lock); ++ ++ if (This->initialized && This->image_data) ++ { ++ if (This->lines + lines <= This->height) ++ { ++ UINT i; ++ BYTE *src, *dst; ++ ++ src = pixels; ++ dst = This->image_data + This->lines * This->width; ++ ++ for (i = 0; i < lines; i++) ++ { ++ memcpy(dst, src, This->width); ++ src += stride; ++ dst += This->width; ++ } ++ ++ This->lines += lines; ++ hr = S_OK; ++ } ++ else ++ hr = E_INVALIDARG; ++ } ++ else ++ hr = WINCODEC_ERR_WRONGSTATE; ++ ++ LeaveCriticalSection(&This->encoder->lock); ++ return hr; ++} ++ ++static HRESULT WINAPI GifFrameEncode_WriteSource(IWICBitmapFrameEncode *iface, IWICBitmapSource *source, WICRect *rc) ++{ ++ GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); ++ HRESULT hr; ++ ++ TRACE("%p,%p,%p\n", iface, source, rc); ++ ++ if (!source) return E_INVALIDARG; ++ ++ EnterCriticalSection(&This->encoder->lock); ++ ++ if (This->initialized) ++ { ++ const GUID *format = &GUID_WICPixelFormat8bppIndexed; ++ ++ hr = configure_write_source(iface, source, rc, format, ++ This->width, This->height, This->xres, This->yres); ++ if (hr == S_OK) ++ hr = write_source(iface, source, rc, format, 8, This->width, This->height); ++ } ++ else ++ hr = WINCODEC_ERR_WRONGSTATE; ++ ++ LeaveCriticalSection(&This->encoder->lock); ++ return hr; ++} ++ ++#define LZW_DICT_SIZE (1 << 12) ++ ++struct lzw_dict ++{ ++ short prefix[LZW_DICT_SIZE]; ++ unsigned char suffix[LZW_DICT_SIZE]; ++}; ++ ++struct lzw_state ++{ ++ struct lzw_dict dict; ++ short init_code_bits, code_bits, next_code, clear_code, eof_code; ++ unsigned bits_buf; ++ int bits_count; ++ int (*user_write_data)(void *user_ptr, void *data, int length); ++ void *user_ptr; ++}; ++ ++struct input_stream ++{ ++ unsigned len; ++ const BYTE *in; ++}; ++ ++struct output_stream ++{ ++ struct ++ { ++ unsigned char len; ++ char data[255]; ++ } gif_block; ++ IStream *out; ++}; ++ ++static int lzw_output_code(struct lzw_state *state, short code) ++{ ++ state->bits_buf |= code << state->bits_count; ++ state->bits_count += state->code_bits; ++ ++ while (state->bits_count >= 8) ++ { ++ unsigned char byte = (unsigned char)state->bits_buf; ++ if (state->user_write_data(state->user_ptr, &byte, 1) != 1) ++ return 0; ++ state->bits_buf >>= 8; ++ state->bits_count -= 8; ++ } ++ ++ return 1; ++} ++ ++static inline int lzw_output_clear_code(struct lzw_state *state) ++{ ++ return lzw_output_code(state, state->clear_code); ++} ++ ++static inline int lzw_output_eof_code(struct lzw_state *state) ++{ ++ return lzw_output_code(state, state->eof_code); ++} ++ ++static int lzw_flush_bits(struct lzw_state *state) ++{ ++ unsigned char byte; ++ ++ while (state->bits_count >= 8) ++ { ++ byte = (unsigned char)state->bits_buf; ++ if (state->user_write_data(state->user_ptr, &byte, 1) != 1) ++ return 0; ++ state->bits_buf >>= 8; ++ state->bits_count -= 8; ++ } ++ ++ if (state->bits_count) ++ { ++ static const char mask[8] = { 0x00,0x01,0x03,0x07,0x0f,0x1f,0x3f,0x7f }; ++ ++ byte = (unsigned char)state->bits_buf & mask[state->bits_count]; ++ if (state->user_write_data(state->user_ptr, &byte, 1) != 1) ++ return 0; ++ } ++ ++ state->bits_buf = 0; ++ state->bits_count = 0; ++ ++ return 1; ++} ++ ++static void lzw_dict_reset(struct lzw_state *state) ++{ ++ int i; ++ ++ state->code_bits = state->init_code_bits + 1; ++ state->next_code = (1 << state->init_code_bits) + 2; ++ ++ for(i = 0; i < LZW_DICT_SIZE; i++) ++ { ++ state->dict.prefix[i] = 1 << 12; /* impossible LZW code value */ ++ state->dict.suffix[i] = 0; ++ } ++} ++ ++static void lzw_state_init(struct lzw_state *state, short init_code_bits, void *user_write_data, void *user_ptr) ++{ ++ state->init_code_bits = init_code_bits; ++ state->clear_code = 1 << init_code_bits; ++ state->eof_code = state->clear_code + 1; ++ state->bits_buf = 0; ++ state->bits_count = 0; ++ state->user_write_data = user_write_data; ++ state->user_ptr = user_ptr; ++ ++ lzw_dict_reset(state); ++} ++ ++static int lzw_dict_add(struct lzw_state *state, short prefix, unsigned char suffix) ++{ ++ if (state->next_code < LZW_DICT_SIZE) ++ { ++ state->dict.prefix[state->next_code] = prefix; ++ state->dict.suffix[state->next_code] = suffix; ++ ++ if ((state->next_code & (state->next_code - 1)) == 0) ++ state->code_bits++; ++ ++ state->next_code++; ++ return state->next_code; ++ } ++ ++ return -1; ++} ++ ++static short lzw_dict_lookup(const struct lzw_state *state, short prefix, unsigned char suffix) ++{ ++ short i; ++ ++ for (i = 0; i < state->next_code; i++) ++ { ++ if (state->dict.prefix[i] == prefix && state->dict.suffix[i] == suffix) ++ return i; ++ } ++ ++ return -1; ++} ++ ++static inline int write_byte(struct output_stream *out, char byte) ++{ ++ if (out->gif_block.len == 255) ++ { ++ if (IStream_Write(out->out, &out->gif_block, sizeof(out->gif_block), NULL) != S_OK) ++ return 0; ++ ++ out->gif_block.len = 0; ++ } ++ ++ out->gif_block.data[out->gif_block.len++] = byte; ++ ++ return 1; ++} ++ ++static int write_data(void *user_ptr, void *user_data, int length) ++{ ++ unsigned char *data = user_data; ++ struct output_stream *out = user_ptr; ++ int len = length; ++ ++ while (len-- > 0) ++ { ++ if (!write_byte(out, *data++)) return 0; ++ } ++ ++ return length; ++} ++ ++static int flush_output_data(void *user_ptr) ++{ ++ struct output_stream *out = user_ptr; ++ ++ if (out->gif_block.len) ++ { ++ if (IStream_Write(out->out, &out->gif_block, out->gif_block.len + sizeof(out->gif_block.len), NULL) != S_OK) ++ return 0; ++ } ++ ++ /* write GIF block terminator */ ++ out->gif_block.len = 0; ++ return IStream_Write(out->out, &out->gif_block, sizeof(out->gif_block.len), NULL) == S_OK; ++} ++ ++static inline int read_byte(struct input_stream *in, unsigned char *byte) ++{ ++ if (in->len) ++ { ++ in->len--; ++ *byte = *in->in++; ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static HRESULT gif_compress(IStream *out_stream, const BYTE *in_data, ULONG in_size) ++{ ++ struct input_stream in; ++ struct output_stream out; ++ struct lzw_state state; ++ short init_code_bits, prefix, code; ++ unsigned char suffix; ++ ++ in.in = in_data; ++ in.len = in_size; ++ ++ out.gif_block.len = 0; ++ out.out = out_stream; ++ ++ init_code_bits = suffix = 8; ++ if (IStream_Write(out.out, &suffix, sizeof(suffix), NULL) != S_OK) ++ return E_FAIL; ++ ++ lzw_state_init(&state, init_code_bits, write_data, &out); ++ ++ if (!lzw_output_clear_code(&state)) ++ return E_FAIL; ++ ++ if (read_byte(&in, &suffix)) ++ { ++ prefix = suffix; ++ ++ while (read_byte(&in, &suffix)) ++ { ++ code = lzw_dict_lookup(&state, prefix, suffix); ++ if (code == -1) ++ { ++ if (!lzw_output_code(&state, prefix)) ++ return E_FAIL; ++ ++ if (lzw_dict_add(&state, prefix, suffix) == -1) ++ { ++ if (!lzw_output_clear_code(&state)) ++ return E_FAIL; ++ lzw_dict_reset(&state); ++ } ++ ++ prefix = suffix; ++ } ++ else ++ prefix = code; ++ } ++ ++ if (!lzw_output_code(&state, prefix)) ++ return E_FAIL; ++ if (!lzw_output_eof_code(&state)) ++ return E_FAIL; ++ if (!lzw_flush_bits(&state)) ++ return E_FAIL; ++ } ++ ++ return flush_output_data(&out) ? S_OK : E_FAIL; ++} ++ ++static HRESULT WINAPI GifFrameEncode_Commit(IWICBitmapFrameEncode *iface) ++{ ++ GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); ++ HRESULT hr; ++ ++ TRACE("%p\n", iface); ++ ++ EnterCriticalSection(&This->encoder->lock); ++ ++ if (This->image_data && This->lines == This->height && !This->committed) ++ { ++ BYTE gif_palette[256][3]; ++ ++ hr = S_OK; ++ ++ if (!This->encoder->info_written) ++ { ++ struct logical_screen_descriptor lsd; ++ ++ /* Logical Screen Descriptor */ ++ memcpy(lsd.signature, "GIF89a", 6); ++ lsd.width = This->width; ++ lsd.height = This->height; ++ lsd.packed = 0; ++ if (This->encoder->colors) ++ lsd.packed |= 0x80; /* global color table flag */ ++ lsd.packed |= 0x07 << 4; /* color resolution */ ++ lsd.packed |= 0x07; /* global color table size */ ++ lsd.background_color_index = 0; /* FIXME */ ++ lsd.pixel_aspect_ratio = 0; ++ hr = IStream_Write(This->encoder->stream, &lsd, sizeof(lsd), NULL); ++ if (hr == S_OK && This->encoder->colors) ++ { ++ UINT i; ++ ++ /* Global Color Table */ ++ memset(gif_palette, 0, sizeof(gif_palette)); ++ for (i = 0; i < This->encoder->colors; i++) ++ { ++ gif_palette[i][0] = (This->encoder->palette[i] >> 16) & 0xff; ++ gif_palette[i][1] = (This->encoder->palette[i] >> 8) & 0xff; ++ gif_palette[i][2] = This->encoder->palette[i] & 0xff; ++ } ++ hr = IStream_Write(This->encoder->stream, gif_palette, sizeof(gif_palette), NULL); ++ if (hr == S_OK) ++ This->encoder->info_written = TRUE; ++ } ++ ++ /* FIXME: write GCE, APE, etc. GIF extensions */ ++ } ++ ++ if (hr == S_OK) ++ { ++ char image_separator = 0x2c; ++ ++ hr = IStream_Write(This->encoder->stream, &image_separator, sizeof(image_separator), NULL); ++ if (hr == S_OK) ++ { ++ struct image_descriptor imd; ++ ++ /* Image Descriptor */ ++ imd.left = 0; ++ imd.top = 0; ++ imd.width = This->width; ++ imd.height = This->height; ++ imd.packed = 0; ++ if (This->colors) ++ imd.packed |= 0x80; /* local color table flag */ ++ /* FIXME: interlace flag */ ++ imd.packed |= 0x07; /* local color table size */ ++ hr = IStream_Write(This->encoder->stream, &imd, sizeof(imd), NULL); ++ if (hr == S_OK && This->colors) ++ { ++ UINT i; ++ ++ /* Local Color Table */ ++ memset(gif_palette, 0, sizeof(gif_palette)); ++ for (i = 0; i < This->colors; i++) ++ { ++ gif_palette[i][0] = (This->palette[i] >> 16) & 0xff; ++ gif_palette[i][1] = (This->palette[i] >> 8) & 0xff; ++ gif_palette[i][2] = This->palette[i] & 0xff; ++ } ++ hr = IStream_Write(This->encoder->stream, gif_palette, sizeof(gif_palette), NULL); ++ if (hr == S_OK) ++ { ++ /* Image Data */ ++ hr = gif_compress(This->encoder->stream, This->image_data, This->width * This->height); ++ if (hr == S_OK) ++ This->committed = TRUE; ++ } ++ } ++ } ++ } ++ } ++ else ++ hr = WINCODEC_ERR_WRONGSTATE; ++ ++ LeaveCriticalSection(&This->encoder->lock); ++ return hr; ++} ++ ++static HRESULT WINAPI GifFrameEncode_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface, IWICMetadataQueryWriter **writer) ++{ ++ FIXME("%p, %p: stub\n", iface, writer); ++ return E_NOTIMPL; ++} ++ ++static const IWICBitmapFrameEncodeVtbl GifFrameEncode_Vtbl = ++{ ++ GifFrameEncode_QueryInterface, ++ GifFrameEncode_AddRef, ++ GifFrameEncode_Release, ++ GifFrameEncode_Initialize, ++ GifFrameEncode_SetSize, ++ GifFrameEncode_SetResolution, ++ GifFrameEncode_SetPixelFormat, ++ GifFrameEncode_SetColorContexts, ++ GifFrameEncode_SetPalette, ++ GifFrameEncode_SetThumbnail, ++ GifFrameEncode_WritePixels, ++ GifFrameEncode_WriteSource, ++ GifFrameEncode_Commit, ++ GifFrameEncode_GetMetadataQueryWriter ++}; ++ ++static HRESULT WINAPI GifEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid, void **ppv) ++{ ++ TRACE("%p,%s,%p\n", iface, debugstr_guid(iid), ppv); ++ ++ if (!ppv) return E_INVALIDARG; ++ ++ if (IsEqualIID(&IID_IUnknown, iid) || ++ IsEqualIID(&IID_IWICBitmapEncoder, iid)) ++ { ++ IWICBitmapEncoder_AddRef(iface); ++ *ppv = iface; ++ return S_OK; ++ } ++ ++ *ppv = NULL; ++ return E_NOINTERFACE; ++} ++ ++static ULONG WINAPI GifEncoder_AddRef(IWICBitmapEncoder *iface) ++{ ++ GifEncoder *This = impl_from_IWICBitmapEncoder(iface); ++ ULONG ref = InterlockedIncrement(&This->ref); ++ ++ TRACE("%p -> %u\n", iface, ref); ++ return ref; ++} ++ ++static ULONG WINAPI GifEncoder_Release(IWICBitmapEncoder *iface) ++{ ++ GifEncoder *This = impl_from_IWICBitmapEncoder(iface); ++ ULONG ref = InterlockedDecrement(&This->ref); ++ ++ TRACE("%p -> %u\n", iface, ref); ++ ++ if (!ref) ++ { ++ if (This->stream) IStream_Release(This->stream); ++ This->lock.DebugInfo->Spare[0] = 0; ++ DeleteCriticalSection(&This->lock); ++ HeapFree(GetProcessHeap(), 0, This); ++ } ++ ++ return ref; ++} ++ ++static HRESULT WINAPI GifEncoder_Initialize(IWICBitmapEncoder *iface, IStream *stream, WICBitmapEncoderCacheOption option) ++{ ++ GifEncoder *This = impl_from_IWICBitmapEncoder(iface); ++ HRESULT hr; ++ ++ TRACE("%p,%p,%#x\n", iface, stream, option); ++ ++ if (!stream) return E_INVALIDARG; ++ ++ EnterCriticalSection(&This->lock); ++ ++ if (!This->initialized) ++ { ++ IStream_AddRef(stream); ++ This->stream = stream; ++ This->initialized = TRUE; ++ hr = S_OK; ++ } ++ else ++ hr = WINCODEC_ERR_WRONGSTATE; ++ ++ LeaveCriticalSection(&This->lock); ++ ++ return hr; ++} ++ ++static HRESULT WINAPI GifEncoder_GetContainerFormat(IWICBitmapEncoder *iface, GUID *format) ++{ ++ if (!format) return E_INVALIDARG; ++ ++ *format = GUID_ContainerFormatGif; ++ return S_OK; ++} ++ ++static HRESULT WINAPI GifEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **info) ++{ ++ IWICComponentInfo *comp_info; ++ HRESULT hr; ++ ++ TRACE("%p,%p\n", iface, info); ++ ++ if (!info) return E_INVALIDARG; ++ ++ hr = CreateComponentInfo(&CLSID_WICGifEncoder, &comp_info); ++ if (hr == S_OK) ++ { ++ hr = IWICComponentInfo_QueryInterface(comp_info, &IID_IWICBitmapEncoderInfo, (void **)info); ++ IWICComponentInfo_Release(comp_info); ++ } ++ return hr; ++} ++ ++static HRESULT WINAPI GifEncoder_SetColorContexts(IWICBitmapEncoder *iface, UINT count, IWICColorContext **context) ++{ ++ FIXME("%p,%u,%p: stub\n", iface, count, context); ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI GifEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *palette) ++{ ++ GifEncoder *This = impl_from_IWICBitmapEncoder(iface); ++ HRESULT hr; ++ ++ TRACE("%p,%p\n", iface, palette); ++ ++ if (!palette) return E_INVALIDARG; ++ ++ EnterCriticalSection(&This->lock); ++ ++ if (This->initialized) ++ hr = IWICPalette_GetColors(palette, 256, This->palette, &This->colors); ++ else ++ hr = WINCODEC_ERR_NOTINITIALIZED; ++ ++ LeaveCriticalSection(&This->lock); ++ return hr; ++} ++ ++static HRESULT WINAPI GifEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *thumbnail) ++{ ++ TRACE("%p,%p\n", iface, thumbnail); ++ return WINCODEC_ERR_UNSUPPORTEDOPERATION; ++} ++ ++static HRESULT WINAPI GifEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *preview) ++{ ++ TRACE("%p,%p\n", iface, preview); ++ return WINCODEC_ERR_UNSUPPORTEDOPERATION; ++} ++ ++static HRESULT WINAPI GifEncoder_CreateNewFrame(IWICBitmapEncoder *iface, IWICBitmapFrameEncode **frame, IPropertyBag2 **options) ++{ ++ GifEncoder *This = impl_from_IWICBitmapEncoder(iface); ++ HRESULT hr; ++ ++ TRACE("%p,%p,%p\n", iface, frame, options); ++ ++ if (!frame) return E_INVALIDARG; ++ ++ EnterCriticalSection(&This->lock); ++ ++ if (This->initialized && !This->committed) ++ { ++ GifFrameEncode *ret = HeapAlloc(GetProcessHeap(), 0, sizeof(*ret)); ++ if (ret) ++ { ++ This->n_frames++; ++ ++ ret->IWICBitmapFrameEncode_iface.lpVtbl = &GifFrameEncode_Vtbl; ++ ret->ref = 1; ++ ret->encoder = This; ++ ret->initialized = FALSE; ++ ret->interlace = FALSE; /* FIXME: read from the properties */ ++ ret->committed = FALSE; ++ ret->width = 0; ++ ret->height = 0; ++ ret->lines = 0; ++ ret->xres = 0.0; ++ ret->yres = 0.0; ++ ret->colors = 0; ++ ret->image_data = NULL; ++ IWICBitmapEncoder_AddRef(iface); ++ *frame = &ret->IWICBitmapFrameEncode_iface; ++ ++ hr = S_OK; ++ } ++ else ++ hr = E_OUTOFMEMORY; ++ } ++ else ++ hr = WINCODEC_ERR_WRONGSTATE; ++ ++ LeaveCriticalSection(&This->lock); ++ ++ return hr; ++ ++} ++ ++static HRESULT WINAPI GifEncoder_Commit(IWICBitmapEncoder *iface) ++{ ++ GifEncoder *This = impl_from_IWICBitmapEncoder(iface); ++ HRESULT hr; ++ ++ TRACE("%p\n", iface); ++ ++ EnterCriticalSection(&This->lock); ++ ++ if (This->initialized && !This->committed) ++ { ++ char gif_trailer = 0x3b; ++ ++ /* FIXME: write text, comment GIF extensions */ ++ ++ hr = IStream_Write(This->stream, &gif_trailer, sizeof(gif_trailer), NULL); ++ if (hr == S_OK) ++ This->committed = TRUE; ++ } ++ else ++ hr = WINCODEC_ERR_WRONGSTATE; ++ ++ LeaveCriticalSection(&This->lock); ++ return hr; ++} ++ ++static HRESULT WINAPI GifEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface, IWICMetadataQueryWriter **writer) ++{ ++ FIXME("%p,%p: stub\n", iface, writer); ++ return E_NOTIMPL; ++} ++ ++static const IWICBitmapEncoderVtbl GifEncoder_Vtbl = ++{ ++ GifEncoder_QueryInterface, ++ GifEncoder_AddRef, ++ GifEncoder_Release, ++ GifEncoder_Initialize, ++ GifEncoder_GetContainerFormat, ++ GifEncoder_GetEncoderInfo, ++ GifEncoder_SetColorContexts, ++ GifEncoder_SetPalette, ++ GifEncoder_SetThumbnail, ++ GifEncoder_SetPreview, ++ GifEncoder_CreateNewFrame, ++ GifEncoder_Commit, ++ GifEncoder_GetMetadataQueryWriter ++}; ++ ++HRESULT GifEncoder_CreateInstance(REFIID iid, void **ppv) ++{ ++ GifEncoder *This; ++ HRESULT ret; ++ ++ TRACE("%s,%p\n", debugstr_guid(iid), ppv); ++ ++ *ppv = NULL; ++ ++ This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); ++ if (!This) return E_OUTOFMEMORY; ++ ++ This->IWICBitmapEncoder_iface.lpVtbl = &GifEncoder_Vtbl; ++ This->ref = 1; ++ This->stream = NULL; ++ InitializeCriticalSection(&This->lock); ++ This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": GifEncoder.lock"); ++ This->initialized = FALSE; ++ This->info_written = FALSE; ++ This->committed = FALSE; ++ This->n_frames = 0; ++ This->colors = 0; ++ ++ ret = IWICBitmapEncoder_QueryInterface(&This->IWICBitmapEncoder_iface, iid, ppv); ++ IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface); ++ ++ return ret; ++} +diff --git a/dlls/windowscodecs/regsvr.c b/dlls/windowscodecs/regsvr.c +index 10a6c03..ab249b0 100644 +--- a/dlls/windowscodecs/regsvr.c ++++ b/dlls/windowscodecs/regsvr.c +@@ -1391,6 +1391,16 @@ static struct regsvr_encoder const encoder_list[] = { + ".bmp,.dib,.rle", + bmp_encode_formats + }, ++ { &CLSID_WICGifEncoder, ++ "The Wine Project", ++ "GIF Encoder", ++ "1.0.0.0", ++ &GUID_VendorMicrosoft, ++ &GUID_ContainerFormatGif, ++ "image/gif", ++ ".gif", ++ gif_formats ++ }, + { &CLSID_WICJpegEncoder, + "The Wine Project", + "JPEG Encoder", +diff --git a/dlls/windowscodecs/wincodecs_private.h b/dlls/windowscodecs/wincodecs_private.h +index a8af0d6..b60db8c 100644 +--- a/dlls/windowscodecs/wincodecs_private.h ++++ b/dlls/windowscodecs/wincodecs_private.h +@@ -81,6 +81,7 @@ extern HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv) DECLSPEC_HIDDEN + extern HRESULT BmpEncoder_CreateInstance(REFIID iid, void** ppv) DECLSPEC_HIDDEN; + extern HRESULT DibDecoder_CreateInstance(REFIID iid, void** ppv) DECLSPEC_HIDDEN; + extern HRESULT GifDecoder_CreateInstance(REFIID riid, void** ppv) DECLSPEC_HIDDEN; ++extern HRESULT GifEncoder_CreateInstance(REFIID iid, void** ppv) DECLSPEC_HIDDEN; + extern HRESULT IcoDecoder_CreateInstance(REFIID iid, void** ppv) DECLSPEC_HIDDEN; + extern HRESULT JpegDecoder_CreateInstance(REFIID iid, void** ppv) DECLSPEC_HIDDEN; + extern HRESULT JpegEncoder_CreateInstance(REFIID iid, void** ppv) DECLSPEC_HIDDEN; +-- +2.9.0 + diff --git a/patches/windowscodecs-GIF_Encoder/definition b/patches/windowscodecs-GIF_Encoder/definition new file mode 100644 index 00000000..1eb8618b --- /dev/null +++ b/patches/windowscodecs-GIF_Encoder/definition @@ -0,0 +1 @@ +Fixes: Add initial implementation of GIF encoder to windowscodecs