Signed-off-by: Esme Povirk esme@codeweavers.com --- Unfortunately, I don't think I can split this up any further. We can't have multiple encoder implementations splitting up tasks and writing to the same file.
dlls/windowscodecs/Makefile.in | 1 + dlls/windowscodecs/encoder.c | 744 +++++++++++++++++ dlls/windowscodecs/libpng.c | 346 ++++++++ dlls/windowscodecs/main.c | 5 + dlls/windowscodecs/pngformat.c | 1028 +----------------------- dlls/windowscodecs/unix_iface.c | 45 +- dlls/windowscodecs/unix_lib.c | 11 + dlls/windowscodecs/wincodecs_common.h | 30 + dlls/windowscodecs/wincodecs_private.h | 40 + 9 files changed, 1230 insertions(+), 1020 deletions(-) create mode 100644 dlls/windowscodecs/encoder.c
diff --git a/dlls/windowscodecs/Makefile.in b/dlls/windowscodecs/Makefile.in index 94b75b62596..e175359ab9d 100644 --- a/dlls/windowscodecs/Makefile.in +++ b/dlls/windowscodecs/Makefile.in @@ -15,6 +15,7 @@ C_SRCS = \ converter.c \ ddsformat.c \ decoder.c \ + encoder.c \ fliprotate.c \ gifformat.c \ icnsformat.c \ diff --git a/dlls/windowscodecs/encoder.c b/dlls/windowscodecs/encoder.c new file mode 100644 index 00000000000..a051480bbab --- /dev/null +++ b/dlls/windowscodecs/encoder.c @@ -0,0 +1,744 @@ +/* + * Copyright 2020 Esme Povirk + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include <stdarg.h> + +#define NONAMELESSUNION +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" + +#include "wincodecs_private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(wincodecs); + +static const WCHAR wszPngInterlaceOption[] = {'I','n','t','e','r','l','a','c','e','O','p','t','i','o','n',0}; +static const WCHAR wszPngFilterOption[] = {'F','i','l','t','e','r','O','p','t','i','o','n',0}; + +static const PROPBAG2 encoder_option_properties[ENCODER_OPTION_END] = { + { PROPBAG2_TYPE_DATA, VT_BOOL, 0, 0, (LPOLESTR)wszPngInterlaceOption }, + { PROPBAG2_TYPE_DATA, VT_UI1, 0, 0, (LPOLESTR)wszPngFilterOption } +}; + +typedef struct CommonEncoder { + IWICBitmapEncoder IWICBitmapEncoder_iface; + LONG ref; + CRITICAL_SECTION lock; /* must be held when stream or encoder is accessed */ + IStream *stream; + struct encoder *encoder; + struct encoder_info encoder_info; + UINT frame_count; + BOOL uncommitted_frame; + BOOL committed; +} CommonEncoder; + +typedef struct CommonEncoderFrame { + IWICBitmapFrameEncode IWICBitmapFrameEncode_iface; + LONG ref; + CommonEncoder *parent; + struct encoder_frame encoder_frame; + BOOL initialized; + BOOL frame_created; + UINT lines_written; + BOOL committed; +} CommonEncoderFrame; + +static inline CommonEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface) +{ + return CONTAINING_RECORD(iface, CommonEncoder, IWICBitmapEncoder_iface); +} + +static inline CommonEncoderFrame *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface) +{ + return CONTAINING_RECORD(iface, CommonEncoderFrame, IWICBitmapFrameEncode_iface); +} + +static HRESULT WINAPI CommonEncoderFrame_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid, + void **ppv) +{ + CommonEncoderFrame *This = impl_from_IWICBitmapFrameEncode(iface); + TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); + + if (!ppv) return E_INVALIDARG; + + if (IsEqualIID(&IID_IUnknown, iid) || + IsEqualIID(&IID_IWICBitmapFrameEncode, iid)) + { + *ppv = &This->IWICBitmapFrameEncode_iface; + } + else + { + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI CommonEncoderFrame_AddRef(IWICBitmapFrameEncode *iface) +{ + CommonEncoderFrame *This = impl_from_IWICBitmapFrameEncode(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) refcount=%u\n", iface, ref); + + return ref; +} + +static ULONG WINAPI CommonEncoderFrame_Release(IWICBitmapFrameEncode *iface) +{ + CommonEncoderFrame *This = impl_from_IWICBitmapFrameEncode(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) refcount=%u\n", iface, ref); + + if (ref == 0) + { + IWICBitmapEncoder_Release(&This->parent->IWICBitmapEncoder_iface); + HeapFree(GetProcessHeap(), 0, This); + } + + return ref; +} + +static HRESULT WINAPI CommonEncoderFrame_Initialize(IWICBitmapFrameEncode *iface, + IPropertyBag2 *pIEncoderOptions) +{ + CommonEncoderFrame *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr=S_OK; + struct encoder_frame options = {0}; + PROPBAG2 opts[7]= {{0}}; + VARIANT opt_values[7]; + HRESULT opt_hres[7]; + DWORD num_opts, i; + + TRACE("(%p,%p)\n", iface, pIEncoderOptions); + + if (pIEncoderOptions) + { + for (i=0; This->parent->encoder_info.encoder_options[i] != ENCODER_OPTION_END; i++) + opts[i] = encoder_option_properties[This->parent->encoder_info.encoder_options[i]]; + num_opts = i; + + hr = IPropertyBag2_Read(pIEncoderOptions, num_opts, opts, NULL, opt_values, opt_hres); + + if (FAILED(hr)) + return hr; + + for (i=0; This->parent->encoder_info.encoder_options[i] != ENCODER_OPTION_END; i++) + { + VARIANT *val = &opt_values[i]; + + switch (This->parent->encoder_info.encoder_options[i]) + { + case ENCODER_OPTION_INTERLACE: + if (V_VT(val) == VT_EMPTY) + options.interlace = FALSE; + else + options.interlace = (V_BOOL(val) != 0); + break; + case ENCODER_OPTION_FILTER: + options.filter = V_UI1(val); + if (options.filter > WICPngFilterAdaptive) + { + WARN("Unrecognized filter option value %u.\n", options.filter); + options.filter = WICPngFilterUnspecified; + } + break; + default: + break; + } + } + } + else + { + options.interlace = FALSE; + options.filter = WICPngFilterUnspecified; + } + + EnterCriticalSection(&This->parent->lock); + + if (This->initialized) + hr = WINCODEC_ERR_WRONGSTATE; + else + { + This->encoder_frame = options; + This->initialized = TRUE; + } + + LeaveCriticalSection(&This->parent->lock); + + return hr; +} + +static HRESULT WINAPI CommonEncoderFrame_SetSize(IWICBitmapFrameEncode *iface, + UINT uiWidth, UINT uiHeight) +{ + CommonEncoderFrame *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr; + + TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight); + + EnterCriticalSection(&This->parent->lock); + + if (!This->initialized || This->frame_created) + { + hr = WINCODEC_ERR_WRONGSTATE; + } + else + { + This->encoder_frame.width = uiWidth; + This->encoder_frame.height = uiHeight; + hr = S_OK; + } + + LeaveCriticalSection(&This->parent->lock); + + return hr; +} + +static HRESULT WINAPI CommonEncoderFrame_SetResolution(IWICBitmapFrameEncode *iface, + double dpiX, double dpiY) +{ + CommonEncoderFrame *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr; + + TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY); + + EnterCriticalSection(&This->parent->lock); + + if (!This->initialized || This->frame_created) + { + hr = WINCODEC_ERR_WRONGSTATE; + } + else + { + This->encoder_frame.dpix = dpiX; + This->encoder_frame.dpiy = dpiY; + hr = S_OK; + } + + LeaveCriticalSection(&This->parent->lock); + + return hr; +} + +static HRESULT WINAPI CommonEncoderFrame_SetPixelFormat(IWICBitmapFrameEncode *iface, + WICPixelFormatGUID *pPixelFormat) +{ + CommonEncoderFrame *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr; + GUID pixel_format; + DWORD bpp; + BOOL indexed; + + TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat)); + + EnterCriticalSection(&This->parent->lock); + + if (!This->initialized || This->frame_created) + { + hr = WINCODEC_ERR_WRONGSTATE; + } + else + { + pixel_format = *pPixelFormat; + hr = encoder_get_supported_format(This->parent->encoder, &pixel_format, &bpp, &indexed); + } + + if (SUCCEEDED(hr)) + { + TRACE("<-- %s bpp=%i indexed=%i\n", wine_dbgstr_guid(&pixel_format), bpp, indexed); + *pPixelFormat = pixel_format; + This->encoder_frame.pixel_format = pixel_format; + This->encoder_frame.bpp = bpp; + This->encoder_frame.indexed = indexed; + } + + LeaveCriticalSection(&This->parent->lock); + + return hr; +} + +static HRESULT WINAPI CommonEncoderFrame_SetColorContexts(IWICBitmapFrameEncode *iface, + UINT cCount, IWICColorContext **ppIColorContext) +{ + FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext); + return E_NOTIMPL; +} + +static HRESULT WINAPI CommonEncoderFrame_SetPalette(IWICBitmapFrameEncode *iface, + IWICPalette *palette) +{ + CommonEncoderFrame *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr; + + TRACE("(%p,%p)\n", iface, palette); + + if (!palette) + return E_INVALIDARG; + + EnterCriticalSection(&This->parent->lock); + + if (!This->initialized) + hr = WINCODEC_ERR_NOTINITIALIZED; + else if (This->frame_created) + hr = WINCODEC_ERR_WRONGSTATE; + else + hr = IWICPalette_GetColors(palette, 256, This->encoder_frame.palette, + &This->encoder_frame.num_colors); + + LeaveCriticalSection(&This->parent->lock); + + return hr; +} + +static HRESULT WINAPI CommonEncoderFrame_SetThumbnail(IWICBitmapFrameEncode *iface, + IWICBitmapSource *pIThumbnail) +{ + FIXME("(%p,%p): stub\n", iface, pIThumbnail); + return WINCODEC_ERR_UNSUPPORTEDOPERATION; +} + +static HRESULT WINAPI CommonEncoderFrame_WritePixels(IWICBitmapFrameEncode *iface, + UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels) +{ + CommonEncoderFrame *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr=S_OK; + DWORD required_stride; + + TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels); + + EnterCriticalSection(&This->parent->lock); + + if (!This->initialized || !This->encoder_frame.height || !This->encoder_frame.width || + !This->encoder_frame.bpp) + { + LeaveCriticalSection(&This->parent->lock); + return WINCODEC_ERR_WRONGSTATE; + } + + required_stride = (This->encoder_frame.width * This->encoder_frame.bpp + 7)/8; + + if (lineCount == 0 || This->encoder_frame.height - This->lines_written < lineCount || + cbStride < required_stride || cbBufferSize < cbStride * (lineCount - 1) + required_stride || + !pbPixels) + { + LeaveCriticalSection(&This->parent->lock); + return E_INVALIDARG; + } + + if (!This->frame_created) + { + hr = encoder_create_frame(This->parent->encoder, &This->encoder_frame); + if (SUCCEEDED(hr)) + This->frame_created = TRUE; + } + + if (SUCCEEDED(hr)) + { + hr = encoder_write_lines(This->parent->encoder, pbPixels, lineCount, cbStride); + if (SUCCEEDED(hr)) + This->lines_written += lineCount; + } + + LeaveCriticalSection(&This->parent->lock); + + return hr; +} + +static HRESULT WINAPI CommonEncoderFrame_WriteSource(IWICBitmapFrameEncode *iface, + IWICBitmapSource *pIBitmapSource, WICRect *prc) +{ + CommonEncoderFrame *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr; + TRACE("(%p,%p,%s)\n", iface, pIBitmapSource, debug_wic_rect(prc)); + + if (!This->initialized) + return WINCODEC_ERR_WRONGSTATE; + + hr = configure_write_source(iface, pIBitmapSource, prc, + This->encoder_frame.bpp ? &This->encoder_frame.pixel_format : NULL, + This->encoder_frame.width, This->encoder_frame.height, + This->encoder_frame.dpix, This->encoder_frame.dpiy); + + if (SUCCEEDED(hr)) + { + hr = write_source(iface, pIBitmapSource, prc, + &This->encoder_frame.pixel_format, This->encoder_frame.bpp, + !This->encoder_frame.num_colors && This->encoder_frame.indexed, + This->encoder_frame.width, This->encoder_frame.height); + } + + return hr; +} + +static HRESULT WINAPI CommonEncoderFrame_Commit(IWICBitmapFrameEncode *iface) +{ + CommonEncoderFrame *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr; + + TRACE("(%p)\n", iface); + + EnterCriticalSection(&This->parent->lock); + + if (!This->frame_created || This->lines_written != This->encoder_frame.height || + This->committed) + { + hr = WINCODEC_ERR_WRONGSTATE; + } + else + { + hr = encoder_commit_frame(This->parent->encoder); + if (SUCCEEDED(hr)) + { + This->committed = TRUE; + This->parent->uncommitted_frame = FALSE; + } + } + + LeaveCriticalSection(&This->parent->lock); + + return hr; +} + +static HRESULT WINAPI CommonEncoderFrame_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface, + IWICMetadataQueryWriter **ppIMetadataQueryWriter) +{ + FIXME("(%p, %p): stub\n", iface, ppIMetadataQueryWriter); + return E_NOTIMPL; +} + +static const IWICBitmapFrameEncodeVtbl CommonEncoderFrame_Vtbl = { + CommonEncoderFrame_QueryInterface, + CommonEncoderFrame_AddRef, + CommonEncoderFrame_Release, + CommonEncoderFrame_Initialize, + CommonEncoderFrame_SetSize, + CommonEncoderFrame_SetResolution, + CommonEncoderFrame_SetPixelFormat, + CommonEncoderFrame_SetColorContexts, + CommonEncoderFrame_SetPalette, + CommonEncoderFrame_SetThumbnail, + CommonEncoderFrame_WritePixels, + CommonEncoderFrame_WriteSource, + CommonEncoderFrame_Commit, + CommonEncoderFrame_GetMetadataQueryWriter +}; + +static HRESULT WINAPI CommonEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid, + void **ppv) +{ + CommonEncoder *This = impl_from_IWICBitmapEncoder(iface); + TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); + + if (!ppv) return E_INVALIDARG; + + if (IsEqualIID(&IID_IUnknown, iid) || + IsEqualIID(&IID_IWICBitmapEncoder, iid)) + { + *ppv = &This->IWICBitmapEncoder_iface; + } + else + { + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI CommonEncoder_AddRef(IWICBitmapEncoder *iface) +{ + CommonEncoder *This = impl_from_IWICBitmapEncoder(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) refcount=%u\n", iface, ref); + + return ref; +} + +static ULONG WINAPI CommonEncoder_Release(IWICBitmapEncoder *iface) +{ + CommonEncoder *This = impl_from_IWICBitmapEncoder(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) refcount=%u\n", iface, ref); + + if (ref == 0) + { + This->lock.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&This->lock); + if (This->stream) + IStream_Release(This->stream); + encoder_destroy(This->encoder); + HeapFree(GetProcessHeap(), 0, This); + } + + return ref; +} + +static HRESULT WINAPI CommonEncoder_Initialize(IWICBitmapEncoder *iface, + IStream *pIStream, WICBitmapEncoderCacheOption cacheOption) +{ + CommonEncoder *This = impl_from_IWICBitmapEncoder(iface); + HRESULT hr; + + TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption); + + if (!pIStream) + return E_POINTER; + + EnterCriticalSection(&This->lock); + + if (This->stream) + { + LeaveCriticalSection(&This->lock); + return WINCODEC_ERR_WRONGSTATE; + } + + hr = encoder_initialize(This->encoder, pIStream); + + if (SUCCEEDED(hr)) + { + This->stream = pIStream; + IStream_AddRef(This->stream); + } + + LeaveCriticalSection(&This->lock); + + return S_OK; +} + +static HRESULT WINAPI CommonEncoder_GetContainerFormat(IWICBitmapEncoder *iface, GUID *format) +{ + CommonEncoder *This = impl_from_IWICBitmapEncoder(iface); + TRACE("(%p,%p)\n", iface, format); + + if (!format) + return E_INVALIDARG; + + memcpy(format, &This->encoder_info.container_format, sizeof(*format)); + return S_OK; +} + +static HRESULT WINAPI CommonEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **info) +{ + CommonEncoder *This = impl_from_IWICBitmapEncoder(iface); + IWICComponentInfo *comp_info; + HRESULT hr; + + TRACE("%p,%p\n", iface, info); + + if (!info) return E_INVALIDARG; + + hr = CreateComponentInfo(&This->encoder_info.clsid, &comp_info); + if (hr == S_OK) + { + hr = IWICComponentInfo_QueryInterface(comp_info, &IID_IWICBitmapEncoderInfo, (void **)info); + IWICComponentInfo_Release(comp_info); + } + return hr; +} + +static HRESULT WINAPI CommonEncoder_SetColorContexts(IWICBitmapEncoder *iface, + UINT cCount, IWICColorContext **ppIColorContext) +{ + FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext); + return E_NOTIMPL; +} + +static HRESULT WINAPI CommonEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *palette) +{ + CommonEncoder *This = impl_from_IWICBitmapEncoder(iface); + HRESULT hr; + + TRACE("(%p,%p)\n", iface, palette); + + EnterCriticalSection(&This->lock); + + hr = This->stream ? WINCODEC_ERR_UNSUPPORTEDOPERATION : WINCODEC_ERR_NOTINITIALIZED; + + LeaveCriticalSection(&This->lock); + + return hr; +} + +static HRESULT WINAPI CommonEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail) +{ + TRACE("(%p,%p)\n", iface, pIThumbnail); + return WINCODEC_ERR_UNSUPPORTEDOPERATION; +} + +static HRESULT WINAPI CommonEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview) +{ + TRACE("(%p,%p)\n", iface, pIPreview); + return WINCODEC_ERR_UNSUPPORTEDOPERATION; +} + +static HRESULT WINAPI CommonEncoder_CreateNewFrame(IWICBitmapEncoder *iface, + IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions) +{ + CommonEncoder *This = impl_from_IWICBitmapEncoder(iface); + CommonEncoderFrame *result; + HRESULT hr; + DWORD opts_length; + PROPBAG2 opts[6]; + + TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions); + + EnterCriticalSection(&This->lock); + + if (This->frame_count != 0 && !(This->encoder_info.flags & ENCODER_FLAGS_MULTI_FRAME)) + { + LeaveCriticalSection(&This->lock); + return WINCODEC_ERR_UNSUPPORTEDOPERATION; + } + + if (!This->stream || This->committed || This->uncommitted_frame) + { + LeaveCriticalSection(&This->lock); + return WINCODEC_ERR_NOTINITIALIZED; + } + + result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*result)); + if (!result) + { + LeaveCriticalSection(&This->lock); + return E_OUTOFMEMORY; + } + + result->IWICBitmapFrameEncode_iface.lpVtbl = &CommonEncoderFrame_Vtbl; + result->ref = 1; + result->parent = This; + + if (ppIEncoderOptions) + { + for (opts_length = 0; This->encoder_info.encoder_options[opts_length] < ENCODER_OPTION_END; opts_length++) + { + opts[opts_length] = encoder_option_properties[This->encoder_info.encoder_options[opts_length]]; + } + + hr = CreatePropertyBag2(opts, opts_length, ppIEncoderOptions); + if (FAILED(hr)) + { + LeaveCriticalSection(&This->lock); + HeapFree(GetProcessHeap(), 0, result); + return hr; + } + } + + IWICBitmapEncoder_AddRef(iface); + This->frame_count++; + This->uncommitted_frame = TRUE; + + LeaveCriticalSection(&This->lock); + + *ppIFrameEncode = &result->IWICBitmapFrameEncode_iface; + + return S_OK; +} + +static HRESULT WINAPI CommonEncoder_Commit(IWICBitmapEncoder *iface) +{ + CommonEncoder *This = impl_from_IWICBitmapEncoder(iface); + HRESULT hr; + + TRACE("(%p)\n", iface); + + EnterCriticalSection(&This->lock); + + if (This->committed || This->uncommitted_frame) + hr = WINCODEC_ERR_WRONGSTATE; + else + { + hr = encoder_commit_file(This->encoder); + if (SUCCEEDED(hr)) + This->committed = TRUE; + } + + LeaveCriticalSection(&This->lock); + + return hr; +} + +static HRESULT WINAPI CommonEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface, + IWICMetadataQueryWriter **ppIMetadataQueryWriter) +{ + FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter); + return E_NOTIMPL; +} + +static const IWICBitmapEncoderVtbl CommonEncoder_Vtbl = { + CommonEncoder_QueryInterface, + CommonEncoder_AddRef, + CommonEncoder_Release, + CommonEncoder_Initialize, + CommonEncoder_GetContainerFormat, + CommonEncoder_GetEncoderInfo, + CommonEncoder_SetColorContexts, + CommonEncoder_SetPalette, + CommonEncoder_SetThumbnail, + CommonEncoder_SetPreview, + CommonEncoder_CreateNewFrame, + CommonEncoder_Commit, + CommonEncoder_GetMetadataQueryWriter +}; + +HRESULT CommonEncoder_CreateInstance(struct encoder *encoder, + const struct encoder_info *encoder_info, REFIID iid, void** ppv) +{ + CommonEncoder *This; + HRESULT ret; + + TRACE("(%s,%p)\n", debugstr_guid(iid), ppv); + + *ppv = NULL; + + This = HeapAlloc(GetProcessHeap(), 0, sizeof(CommonEncoder)); + if (!This) + { + encoder_destroy(encoder); + return E_OUTOFMEMORY; + } + + This->IWICBitmapEncoder_iface.lpVtbl = &CommonEncoder_Vtbl; + This->ref = 1; + This->stream = NULL; + This->encoder = encoder; + This->encoder_info = *encoder_info; + This->frame_count = 0; + This->uncommitted_frame = FALSE; + This->committed = FALSE; + InitializeCriticalSection(&This->lock); + This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": CommonEncoder.lock"); + + ret = IWICBitmapEncoder_QueryInterface(&This->IWICBitmapEncoder_iface, iid, ppv); + IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface); + + return ret; +} diff --git a/dlls/windowscodecs/libpng.c b/dlls/windowscodecs/libpng.c index fd1ee679ec3..ee02d493f57 100644 --- a/dlls/windowscodecs/libpng.c +++ b/dlls/windowscodecs/libpng.c @@ -50,7 +50,9 @@ static void *libpng_handle; #define MAKE_FUNCPTR(f) static typeof(f) * p##f MAKE_FUNCPTR(png_create_info_struct); MAKE_FUNCPTR(png_create_read_struct); +MAKE_FUNCPTR(png_create_write_struct); MAKE_FUNCPTR(png_destroy_read_struct); +MAKE_FUNCPTR(png_destroy_write_struct); MAKE_FUNCPTR(png_error); MAKE_FUNCPTR(png_get_bit_depth); MAKE_FUNCPTR(png_get_color_type); @@ -67,10 +69,21 @@ MAKE_FUNCPTR(png_read_info); MAKE_FUNCPTR(png_set_bgr); MAKE_FUNCPTR(png_set_crc_action); MAKE_FUNCPTR(png_set_error_fn); +MAKE_FUNCPTR(png_set_filler); +MAKE_FUNCPTR(png_set_filter); MAKE_FUNCPTR(png_set_gray_to_rgb); +MAKE_FUNCPTR(png_set_IHDR); +MAKE_FUNCPTR(png_set_interlace_handling); +MAKE_FUNCPTR(png_set_pHYs); +MAKE_FUNCPTR(png_set_PLTE); MAKE_FUNCPTR(png_set_read_fn); MAKE_FUNCPTR(png_set_swap); +MAKE_FUNCPTR(png_set_tRNS); MAKE_FUNCPTR(png_set_tRNS_to_alpha); +MAKE_FUNCPTR(png_set_write_fn); +MAKE_FUNCPTR(png_write_end); +MAKE_FUNCPTR(png_write_info); +MAKE_FUNCPTR(png_write_rows); #undef MAKE_FUNCPTR
static CRITICAL_SECTION init_png_cs; @@ -99,7 +112,9 @@ static void *load_libpng(void) } LOAD_FUNCPTR(png_create_info_struct); LOAD_FUNCPTR(png_create_read_struct); + LOAD_FUNCPTR(png_create_write_struct); LOAD_FUNCPTR(png_destroy_read_struct); + LOAD_FUNCPTR(png_destroy_write_struct); LOAD_FUNCPTR(png_error); LOAD_FUNCPTR(png_get_bit_depth); LOAD_FUNCPTR(png_get_color_type); @@ -116,10 +131,21 @@ static void *load_libpng(void) LOAD_FUNCPTR(png_set_bgr); LOAD_FUNCPTR(png_set_crc_action); LOAD_FUNCPTR(png_set_error_fn); + LOAD_FUNCPTR(png_set_filler); + LOAD_FUNCPTR(png_set_filter); LOAD_FUNCPTR(png_set_gray_to_rgb); + LOAD_FUNCPTR(png_set_IHDR); + LOAD_FUNCPTR(png_set_interlace_handling); + LOAD_FUNCPTR(png_set_pHYs); + LOAD_FUNCPTR(png_set_PLTE); LOAD_FUNCPTR(png_set_read_fn); LOAD_FUNCPTR(png_set_swap); + LOAD_FUNCPTR(png_set_tRNS); LOAD_FUNCPTR(png_set_tRNS_to_alpha); + LOAD_FUNCPTR(png_set_write_fn); + LOAD_FUNCPTR(png_write_end); + LOAD_FUNCPTR(png_write_info); + LOAD_FUNCPTR(png_write_rows);
#undef LOAD_FUNCPTR } @@ -611,9 +637,45 @@ HRESULT CDECL png_decoder_create(struct decoder_info *info, struct decoder **res return S_OK; }
+struct png_pixelformat { + const WICPixelFormatGUID *guid; + UINT bpp; + int bit_depth; + int color_type; + BOOL remove_filler; + BOOL swap_rgb; +}; + +static const struct png_pixelformat formats[] = { + {&GUID_WICPixelFormat32bppBGRA, 32, 8, PNG_COLOR_TYPE_RGB_ALPHA, 0, 1}, + {&GUID_WICPixelFormat24bppBGR, 24, 8, PNG_COLOR_TYPE_RGB, 0, 1}, + {&GUID_WICPixelFormatBlackWhite, 1, 1, PNG_COLOR_TYPE_GRAY, 0, 0}, + {&GUID_WICPixelFormat2bppGray, 2, 2, PNG_COLOR_TYPE_GRAY, 0, 0}, + {&GUID_WICPixelFormat4bppGray, 4, 4, PNG_COLOR_TYPE_GRAY, 0, 0}, + {&GUID_WICPixelFormat8bppGray, 8, 8, PNG_COLOR_TYPE_GRAY, 0, 0}, + {&GUID_WICPixelFormat16bppGray, 16, 16, PNG_COLOR_TYPE_GRAY, 0, 0}, + {&GUID_WICPixelFormat32bppBGR, 32, 8, PNG_COLOR_TYPE_RGB, 1, 1}, + {&GUID_WICPixelFormat48bppRGB, 48, 16, PNG_COLOR_TYPE_RGB, 0, 0}, + {&GUID_WICPixelFormat64bppRGBA, 64, 16, PNG_COLOR_TYPE_RGB_ALPHA, 0, 0}, + {&GUID_WICPixelFormat1bppIndexed, 1, 1, PNG_COLOR_TYPE_PALETTE, 0, 0}, + {&GUID_WICPixelFormat2bppIndexed, 2, 2, PNG_COLOR_TYPE_PALETTE, 0, 0}, + {&GUID_WICPixelFormat4bppIndexed, 4, 4, PNG_COLOR_TYPE_PALETTE, 0, 0}, + {&GUID_WICPixelFormat8bppIndexed, 8, 8, PNG_COLOR_TYPE_PALETTE, 0, 0}, + {NULL}, +}; + struct png_encoder { struct encoder encoder; + IStream *stream; + png_structp png_ptr; + png_infop info_ptr; + struct encoder_frame encoder_frame; + const struct png_pixelformat *format; + BYTE *data; + UINT stride; + UINT passes; + UINT lines_written; };
static inline struct png_encoder *impl_from_encoder(struct encoder* iface) @@ -621,13 +683,294 @@ static inline struct png_encoder *impl_from_encoder(struct encoder* iface) return CONTAINING_RECORD(iface, struct png_encoder, encoder); }
+static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + struct png_encoder *This = ppng_get_io_ptr(png_ptr); + HRESULT hr; + ULONG byteswritten; + + hr = stream_write(This->stream, data, length, &byteswritten); + if (FAILED(hr) || byteswritten != length) + { + ppng_error(png_ptr, "failed writing data"); + } +} + +static void user_flush(png_structp png_ptr) +{ +} + +static HRESULT CDECL png_encoder_initialize(struct encoder *encoder, IStream *stream) +{ + struct png_encoder *This = impl_from_encoder(encoder); + jmp_buf jmpbuf; + + TRACE("(%p,%p)\n", encoder, stream); + + /* initialize libpng */ + This->png_ptr = ppng_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!This->png_ptr) + return E_FAIL; + + This->info_ptr = ppng_create_info_struct(This->png_ptr); + if (!This->info_ptr) + { + ppng_destroy_write_struct(&This->png_ptr, NULL); + This->png_ptr = NULL; + return E_FAIL; + } + + This->stream = stream; + + /* set up setjmp/longjmp error handling */ + if (setjmp(jmpbuf)) + { + ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr); + This->png_ptr = NULL; + This->stream = NULL; + return E_FAIL; + } + ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn); + + /* set up custom i/o handling */ + ppng_set_write_fn(This->png_ptr, This, user_write_data, user_flush); + + return S_OK; +} + +HRESULT CDECL png_encoder_get_supported_format(struct encoder* iface, GUID *pixel_format, DWORD *bpp, BOOL *indexed) +{ + int i; + + for (i=0; formats[i].guid; i++) + { + if (memcmp(formats[i].guid, pixel_format, sizeof(GUID)) == 0) + break; + } + + if (!formats[i].guid) + i = 0; + + *pixel_format = *formats[i].guid; + *bpp = formats[i].bpp; + *indexed = (formats[i].color_type == PNG_COLOR_TYPE_PALETTE); + + return S_OK; +} + +static HRESULT CDECL png_encoder_create_frame(struct encoder *encoder, const struct encoder_frame *encoder_frame) +{ + struct png_encoder *This = impl_from_encoder(encoder); + jmp_buf jmpbuf; + int i; + + for (i=0; formats[i].guid; i++) + { + if (memcmp(formats[i].guid, &encoder_frame->pixel_format, sizeof(GUID)) == 0) + { + This->format = &formats[i]; + break; + } + } + + if (!formats[i].guid) + { + ERR("invalid pixel format %s\n", wine_dbgstr_guid(&encoder_frame->pixel_format)); + return E_FAIL; + } + + /* set up setjmp/longjmp error handling */ + if (setjmp(jmpbuf)) + return E_FAIL; + + ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn); + + This->encoder_frame = *encoder_frame; + This->lines_written = 0; + + if (encoder_frame->interlace) + { + /* libpng requires us to write all data multiple times in this case. */ + This->stride = (This->format->bpp * encoder_frame->width + 7)/8; + This->data = malloc(encoder_frame->height * This->stride); + if (!This->data) + return E_OUTOFMEMORY; + } + + /* Tell PNG we need to byte swap if writing a >8-bpp image */ + if (This->format->bit_depth > 8) + ppng_set_swap(This->png_ptr); + + ppng_set_IHDR(This->png_ptr, This->info_ptr, encoder_frame->width, encoder_frame->height, + This->format->bit_depth, This->format->color_type, + encoder_frame->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + if (encoder_frame->dpix != 0.0 && encoder_frame->dpiy != 0.0) + { + ppng_set_pHYs(This->png_ptr, This->info_ptr, (encoder_frame->dpix+0.0127) / 0.0254, + (encoder_frame->dpiy+0.0127) / 0.0254, PNG_RESOLUTION_METER); + } + + if (This->format->color_type == PNG_COLOR_TYPE_PALETTE && encoder_frame->num_colors) + { + png_color png_palette[256]; + png_byte trans[256]; + UINT i, num_trans = 0, colors; + + /* Newer libpng versions don't accept larger palettes than the declared + * bit depth, so we need to generate the palette of the correct length. + */ + colors = min(encoder_frame->num_colors, 1 << This->format->bit_depth); + + for (i = 0; i < colors; i++) + { + png_palette[i].red = (encoder_frame->palette[i] >> 16) & 0xff; + png_palette[i].green = (encoder_frame->palette[i] >> 8) & 0xff; + png_palette[i].blue = encoder_frame->palette[i] & 0xff; + trans[i] = (encoder_frame->palette[i] >> 24) & 0xff; + if (trans[i] != 0xff) + num_trans = i+1; + } + + ppng_set_PLTE(This->png_ptr, This->info_ptr, png_palette, colors); + + if (num_trans) + ppng_set_tRNS(This->png_ptr, This->info_ptr, trans, num_trans, NULL); + } + + ppng_write_info(This->png_ptr, This->info_ptr); + + if (This->format->remove_filler) + ppng_set_filler(This->png_ptr, 0, PNG_FILLER_AFTER); + + if (This->format->swap_rgb) + ppng_set_bgr(This->png_ptr); + + if (encoder_frame->interlace) + This->passes = ppng_set_interlace_handling(This->png_ptr); + + if (encoder_frame->filter != WICPngFilterUnspecified) + { + static const int png_filter_map[] = + { + /* WICPngFilterUnspecified */ PNG_NO_FILTERS, + /* WICPngFilterNone */ PNG_FILTER_NONE, + /* WICPngFilterSub */ PNG_FILTER_SUB, + /* WICPngFilterUp */ PNG_FILTER_UP, + /* WICPngFilterAverage */ PNG_FILTER_AVG, + /* WICPngFilterPaeth */ PNG_FILTER_PAETH, + /* WICPngFilterAdaptive */ PNG_ALL_FILTERS, + }; + + ppng_set_filter(This->png_ptr, 0, png_filter_map[encoder_frame->filter]); + } + + return S_OK; +} + +HRESULT CDECL png_encoder_write_lines(struct encoder* encoder, BYTE *data, DWORD line_count, DWORD stride) +{ + struct png_encoder *This = impl_from_encoder(encoder); + jmp_buf jmpbuf; + png_byte **row_pointers=NULL; + UINT i; + + if (This->encoder_frame.interlace) + { + /* Just store the data so we can write it in multiple passes in Commit. */ + for (i=0; i<line_count; i++) + memcpy(This->data + This->stride * (This->lines_written + i), + data + stride * i, + This->stride); + + This->lines_written += line_count; + + return S_OK; + } + + /* set up setjmp/longjmp error handling */ + if (setjmp(jmpbuf)) + { + free(row_pointers); + return E_FAIL; + } + + ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn); + + row_pointers = malloc(line_count * sizeof(png_byte*)); + if (!row_pointers) + return E_OUTOFMEMORY; + + for (i=0; i<line_count; i++) + row_pointers[i] = data + stride * i; + + ppng_write_rows(This->png_ptr, row_pointers, line_count); + This->lines_written += line_count; + + free(row_pointers); + + return S_OK; +} + +static HRESULT CDECL png_encoder_commit_frame(struct encoder *encoder) +{ + struct png_encoder *This = impl_from_encoder(encoder); + jmp_buf jmpbuf; + png_byte **row_pointers=NULL; + + /* set up setjmp/longjmp error handling */ + if (setjmp(jmpbuf)) + { + free(row_pointers); + return E_FAIL; + } + + ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn); + + if (This->encoder_frame.interlace) + { + int i; + + row_pointers = malloc(This->encoder_frame.height * sizeof(png_byte*)); + if (!row_pointers) + return E_OUTOFMEMORY; + + for (i=0; i<This->encoder_frame.height; i++) + row_pointers[i] = This->data + This->stride * i; + + for (i=0; i<This->passes; i++) + ppng_write_rows(This->png_ptr, row_pointers, This->encoder_frame.height); + } + + ppng_write_end(This->png_ptr, This->info_ptr); + + free(row_pointers); + + return S_OK; +} + +static HRESULT CDECL png_encoder_commit_file(struct encoder *encoder) +{ + return S_OK; +} + static void CDECL png_encoder_destroy(struct encoder *encoder) { struct png_encoder *This = impl_from_encoder(encoder); + if (This->png_ptr) + ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr); + free(This->data); RtlFreeHeap(GetProcessHeap(), 0, This); }
static const struct encoder_funcs png_encoder_vtable = { + png_encoder_initialize, + png_encoder_get_supported_format, + png_encoder_create_frame, + png_encoder_write_lines, + png_encoder_commit_frame, + png_encoder_commit_file, png_encoder_destroy };
@@ -649,6 +992,9 @@ HRESULT CDECL png_encoder_create(struct encoder_info *info, struct encoder **res }
This->encoder.vtable = &png_encoder_vtable; + This->png_ptr = NULL; + This->info_ptr = NULL; + This->data = NULL; *result = &This->encoder;
info->container_format = GUID_ContainerFormatPng; diff --git a/dlls/windowscodecs/main.c b/dlls/windowscodecs/main.c index 7976b96a393..3817d23eb4c 100644 --- a/dlls/windowscodecs/main.c +++ b/dlls/windowscodecs/main.c @@ -223,6 +223,11 @@ HRESULT CDECL stream_seek(IStream *stream, LONGLONG ofs, DWORD origin, ULONGLONG return hr; }
+HRESULT CDECL stream_write(IStream *stream, const void *buffer, ULONG write, ULONG *bytes_written) +{ + return IStream_Write(stream, buffer, write, bytes_written); +} + HRESULT get_pixelformat_bpp(const GUID *pixelformat, UINT *bpp) { HRESULT hr; diff --git a/dlls/windowscodecs/pngformat.c b/dlls/windowscodecs/pngformat.c index f0ac83dae1c..b5ea9b48861 100644 --- a/dlls/windowscodecs/pngformat.c +++ b/dlls/windowscodecs/pngformat.c @@ -22,10 +22,6 @@
#include <stdarg.h>
-#ifdef SONAME_LIBPNG -#include <png.h> -#endif - #define NONAMELESSUNION #define COBJMACROS
@@ -37,8 +33,6 @@
#include "wine/debug.h"
-WINE_DEFAULT_DEBUG_CHANNEL(wincodecs); - static inline ULONG read_ulong_be(BYTE* data) { return data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; @@ -255,1034 +249,30 @@ HRESULT PngChrmReader_CreateInstance(REFIID iid, void** ppv) return MetadataReader_Create(&ChrmReader_Vtbl, iid, ppv); }
-#ifdef SONAME_LIBPNG - -static void *libpng_handle; -#define MAKE_FUNCPTR(f) static typeof(f) * p##f -MAKE_FUNCPTR(png_create_read_struct); -MAKE_FUNCPTR(png_create_info_struct); -MAKE_FUNCPTR(png_create_write_struct); -MAKE_FUNCPTR(png_destroy_read_struct); -MAKE_FUNCPTR(png_destroy_write_struct); -MAKE_FUNCPTR(png_error); -MAKE_FUNCPTR(png_get_bit_depth); -MAKE_FUNCPTR(png_get_color_type); -MAKE_FUNCPTR(png_get_error_ptr); -MAKE_FUNCPTR(png_get_iCCP); -MAKE_FUNCPTR(png_get_image_height); -MAKE_FUNCPTR(png_get_image_width); -MAKE_FUNCPTR(png_get_io_ptr); -MAKE_FUNCPTR(png_get_pHYs); -MAKE_FUNCPTR(png_get_PLTE); -MAKE_FUNCPTR(png_get_tRNS); -MAKE_FUNCPTR(png_set_bgr); -MAKE_FUNCPTR(png_set_crc_action); -MAKE_FUNCPTR(png_set_error_fn); -MAKE_FUNCPTR(png_set_filler); -MAKE_FUNCPTR(png_set_filter); -MAKE_FUNCPTR(png_set_gray_to_rgb); -MAKE_FUNCPTR(png_set_interlace_handling); -MAKE_FUNCPTR(png_set_IHDR); -MAKE_FUNCPTR(png_set_pHYs); -MAKE_FUNCPTR(png_set_PLTE); -MAKE_FUNCPTR(png_set_read_fn); -MAKE_FUNCPTR(png_set_strip_16); -MAKE_FUNCPTR(png_set_tRNS); -MAKE_FUNCPTR(png_set_tRNS_to_alpha); -MAKE_FUNCPTR(png_set_write_fn); -MAKE_FUNCPTR(png_set_swap); -MAKE_FUNCPTR(png_read_end); -MAKE_FUNCPTR(png_read_image); -MAKE_FUNCPTR(png_read_info); -MAKE_FUNCPTR(png_write_end); -MAKE_FUNCPTR(png_write_info); -MAKE_FUNCPTR(png_write_rows); -#undef MAKE_FUNCPTR - -static CRITICAL_SECTION init_png_cs; -static CRITICAL_SECTION_DEBUG init_png_cs_debug = -{ - 0, 0, &init_png_cs, - { &init_png_cs_debug.ProcessLocksList, - &init_png_cs_debug.ProcessLocksList }, - 0, 0, { (DWORD_PTR)(__FILE__ ": init_png_cs") } -}; -static CRITICAL_SECTION init_png_cs = { &init_png_cs_debug, -1, 0, 0, 0, 0 }; - -static void *load_libpng(void) -{ - void *result; - - EnterCriticalSection(&init_png_cs); - - if(!libpng_handle && (libpng_handle = dlopen(SONAME_LIBPNG, RTLD_NOW)) != NULL) { - -#define LOAD_FUNCPTR(f) \ - if((p##f = dlsym(libpng_handle, #f)) == NULL) { \ - libpng_handle = NULL; \ - LeaveCriticalSection(&init_png_cs); \ - return NULL; \ - } - LOAD_FUNCPTR(png_create_read_struct); - LOAD_FUNCPTR(png_create_info_struct); - LOAD_FUNCPTR(png_create_write_struct); - LOAD_FUNCPTR(png_destroy_read_struct); - LOAD_FUNCPTR(png_destroy_write_struct); - LOAD_FUNCPTR(png_error); - LOAD_FUNCPTR(png_get_bit_depth); - LOAD_FUNCPTR(png_get_color_type); - LOAD_FUNCPTR(png_get_error_ptr); - LOAD_FUNCPTR(png_get_iCCP); - LOAD_FUNCPTR(png_get_image_height); - LOAD_FUNCPTR(png_get_image_width); - LOAD_FUNCPTR(png_get_io_ptr); - LOAD_FUNCPTR(png_get_pHYs); - LOAD_FUNCPTR(png_get_PLTE); - LOAD_FUNCPTR(png_get_tRNS); - LOAD_FUNCPTR(png_set_bgr); - LOAD_FUNCPTR(png_set_crc_action); - LOAD_FUNCPTR(png_set_error_fn); - LOAD_FUNCPTR(png_set_filler); - LOAD_FUNCPTR(png_set_filter); - LOAD_FUNCPTR(png_set_gray_to_rgb); - LOAD_FUNCPTR(png_set_interlace_handling); - LOAD_FUNCPTR(png_set_IHDR); - LOAD_FUNCPTR(png_set_pHYs); - LOAD_FUNCPTR(png_set_PLTE); - LOAD_FUNCPTR(png_set_read_fn); - LOAD_FUNCPTR(png_set_strip_16); - LOAD_FUNCPTR(png_set_tRNS); - LOAD_FUNCPTR(png_set_tRNS_to_alpha); - LOAD_FUNCPTR(png_set_write_fn); - LOAD_FUNCPTR(png_set_swap); - LOAD_FUNCPTR(png_read_end); - LOAD_FUNCPTR(png_read_image); - LOAD_FUNCPTR(png_read_info); - LOAD_FUNCPTR(png_write_end); - LOAD_FUNCPTR(png_write_info); - LOAD_FUNCPTR(png_write_rows); - -#undef LOAD_FUNCPTR - } - - result = libpng_handle; - - LeaveCriticalSection(&init_png_cs); - - return result; -} - -static void user_error_fn(png_structp png_ptr, png_const_charp error_message) -{ - jmp_buf *pjmpbuf; - - /* This uses setjmp/longjmp just like the default. We can't use the - * default because there's no way to access the jmp buffer in the png_struct - * that works in 1.2 and 1.4 and allows us to dynamically load libpng. */ - WARN("PNG error: %s\n", debugstr_a(error_message)); - pjmpbuf = ppng_get_error_ptr(png_ptr); - longjmp(*pjmpbuf, 1); -} - -static void user_warning_fn(png_structp png_ptr, png_const_charp warning_message) -{ - WARN("PNG warning: %s\n", debugstr_a(warning_message)); -} - -static const WCHAR wszPngInterlaceOption[] = {'I','n','t','e','r','l','a','c','e','O','p','t','i','o','n',0}; -static const WCHAR wszPngFilterOption[] = {'F','i','l','t','e','r','O','p','t','i','o','n',0}; - -static const PROPBAG2 encoder_option_properties[ENCODER_OPTION_END] = { - { PROPBAG2_TYPE_DATA, VT_BOOL, 0, 0, (LPOLESTR)wszPngInterlaceOption }, - { PROPBAG2_TYPE_DATA, VT_UI1, 0, 0, (LPOLESTR)wszPngFilterOption } -}; - -struct png_pixelformat { - const WICPixelFormatGUID *guid; - UINT bpp; - int bit_depth; - int color_type; - BOOL remove_filler; - BOOL swap_rgb; -}; - -static const struct png_pixelformat formats[] = { - {&GUID_WICPixelFormat32bppBGRA, 32, 8, PNG_COLOR_TYPE_RGB_ALPHA, 0, 1}, - {&GUID_WICPixelFormat24bppBGR, 24, 8, PNG_COLOR_TYPE_RGB, 0, 1}, - {&GUID_WICPixelFormatBlackWhite, 1, 1, PNG_COLOR_TYPE_GRAY, 0, 0}, - {&GUID_WICPixelFormat2bppGray, 2, 2, PNG_COLOR_TYPE_GRAY, 0, 0}, - {&GUID_WICPixelFormat4bppGray, 4, 4, PNG_COLOR_TYPE_GRAY, 0, 0}, - {&GUID_WICPixelFormat8bppGray, 8, 8, PNG_COLOR_TYPE_GRAY, 0, 0}, - {&GUID_WICPixelFormat16bppGray, 16, 16, PNG_COLOR_TYPE_GRAY, 0, 0}, - {&GUID_WICPixelFormat32bppBGR, 32, 8, PNG_COLOR_TYPE_RGB, 1, 1}, - {&GUID_WICPixelFormat48bppRGB, 48, 16, PNG_COLOR_TYPE_RGB, 0, 0}, - {&GUID_WICPixelFormat64bppRGBA, 64, 16, PNG_COLOR_TYPE_RGB_ALPHA, 0, 0}, - {&GUID_WICPixelFormat1bppIndexed, 1, 1, PNG_COLOR_TYPE_PALETTE, 0, 0}, - {&GUID_WICPixelFormat2bppIndexed, 2, 2, PNG_COLOR_TYPE_PALETTE, 0, 0}, - {&GUID_WICPixelFormat4bppIndexed, 4, 4, PNG_COLOR_TYPE_PALETTE, 0, 0}, - {&GUID_WICPixelFormat8bppIndexed, 8, 8, PNG_COLOR_TYPE_PALETTE, 0, 0}, - {NULL}, -}; - -typedef struct PngEncoder { - IWICBitmapEncoder IWICBitmapEncoder_iface; - IWICBitmapFrameEncode IWICBitmapFrameEncode_iface; - LONG ref; - IStream *stream; - png_structp png_ptr; - png_infop info_ptr; - struct encoder *encoder; - struct encoder_info encoder_info; - UINT frame_count; - BOOL frame_initialized; - const struct png_pixelformat *format; - BOOL info_written; - UINT width, height; - double xres, yres; - UINT lines_written; - BOOL frame_committed; - BOOL committed; - CRITICAL_SECTION lock; - BOOL interlace; - WICPngFilterOption filter; - BYTE *data; - UINT stride; - UINT passes; - WICColor palette[256]; - UINT colors; -} PngEncoder; - -static inline PngEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface) -{ - return CONTAINING_RECORD(iface, PngEncoder, IWICBitmapEncoder_iface); -} - -static inline PngEncoder *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface) -{ - return CONTAINING_RECORD(iface, PngEncoder, IWICBitmapFrameEncode_iface); -} - -static HRESULT WINAPI PngFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid, - void **ppv) -{ - PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); - - if (!ppv) return E_INVALIDARG; - - if (IsEqualIID(&IID_IUnknown, iid) || - IsEqualIID(&IID_IWICBitmapFrameEncode, iid)) - { - *ppv = &This->IWICBitmapFrameEncode_iface; - } - else - { - *ppv = NULL; - return E_NOINTERFACE; - } - - IUnknown_AddRef((IUnknown*)*ppv); - return S_OK; -} - -static ULONG WINAPI PngFrameEncode_AddRef(IWICBitmapFrameEncode *iface) -{ - PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - return IWICBitmapEncoder_AddRef(&This->IWICBitmapEncoder_iface); -} - -static ULONG WINAPI PngFrameEncode_Release(IWICBitmapFrameEncode *iface) -{ - PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - return IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface); -} - -static HRESULT WINAPI PngFrameEncode_Initialize(IWICBitmapFrameEncode *iface, - IPropertyBag2 *pIEncoderOptions) -{ - PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - WICPngFilterOption filter; - BOOL interlace; - PROPBAG2 opts[2]= {{0}}; - VARIANT opt_values[2]; - HRESULT opt_hres[2]; - HRESULT hr; - - TRACE("(%p,%p)\n", iface, pIEncoderOptions); - - opts[0].pstrName = (LPOLESTR)wszPngInterlaceOption; - opts[0].vt = VT_BOOL; - opts[1].pstrName = (LPOLESTR)wszPngFilterOption; - opts[1].vt = VT_UI1; - - if (pIEncoderOptions) - { - hr = IPropertyBag2_Read(pIEncoderOptions, ARRAY_SIZE(opts), opts, NULL, opt_values, opt_hres); - - if (FAILED(hr)) - return hr; - - if (V_VT(&opt_values[0]) == VT_EMPTY) - interlace = FALSE; - else - interlace = (V_BOOL(&opt_values[0]) != 0); - - filter = V_UI1(&opt_values[1]); - if (filter > WICPngFilterAdaptive) - { - WARN("Unrecognized filter option value %u.\n", filter); - filter = WICPngFilterUnspecified; - } - } - else - { - interlace = FALSE; - filter = WICPngFilterUnspecified; - } - - EnterCriticalSection(&This->lock); - - if (This->frame_initialized) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_WRONGSTATE; - } - - This->interlace = interlace; - This->filter = filter; - - This->frame_initialized = TRUE; - - LeaveCriticalSection(&This->lock); - - return S_OK; -} - -static HRESULT WINAPI PngFrameEncode_SetSize(IWICBitmapFrameEncode *iface, - UINT uiWidth, UINT uiHeight) -{ - PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight); - - EnterCriticalSection(&This->lock); - - if (!This->frame_initialized || This->info_written) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_WRONGSTATE; - } - - This->width = uiWidth; - This->height = uiHeight; - - LeaveCriticalSection(&This->lock); - - return S_OK; -} - -static HRESULT WINAPI PngFrameEncode_SetResolution(IWICBitmapFrameEncode *iface, - double dpiX, double dpiY) -{ - PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY); - - EnterCriticalSection(&This->lock); - - if (!This->frame_initialized || This->info_written) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_WRONGSTATE; - } - - This->xres = dpiX; - This->yres = dpiY; - - LeaveCriticalSection(&This->lock); - - return S_OK; -} - -static HRESULT WINAPI PngFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface, - WICPixelFormatGUID *pPixelFormat) -{ - PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - int i; - TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat)); - - EnterCriticalSection(&This->lock); - - if (!This->frame_initialized || This->info_written) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_WRONGSTATE; - } - - for (i=0; formats[i].guid; i++) - { - if (memcmp(formats[i].guid, pPixelFormat, sizeof(GUID)) == 0) - break; - } - - if (!formats[i].guid) i = 0; - - This->format = &formats[i]; - memcpy(pPixelFormat, This->format->guid, sizeof(GUID)); - - LeaveCriticalSection(&This->lock); - - return S_OK; -} - -static HRESULT WINAPI PngFrameEncode_SetColorContexts(IWICBitmapFrameEncode *iface, - UINT cCount, IWICColorContext **ppIColorContext) -{ - FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext); - return E_NOTIMPL; -} - -static HRESULT WINAPI PngFrameEncode_SetPalette(IWICBitmapFrameEncode *iface, - IWICPalette *palette) -{ - PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - HRESULT hr; - - TRACE("(%p,%p)\n", iface, palette); - - if (!palette) return E_INVALIDARG; - - EnterCriticalSection(&This->lock); - - if (This->frame_initialized) - hr = IWICPalette_GetColors(palette, 256, This->palette, &This->colors); - else - hr = WINCODEC_ERR_NOTINITIALIZED; - - LeaveCriticalSection(&This->lock); - return hr; -} - -static HRESULT WINAPI PngFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface, - IWICBitmapSource *pIThumbnail) -{ - FIXME("(%p,%p): stub\n", iface, pIThumbnail); - return WINCODEC_ERR_UNSUPPORTEDOPERATION; -} - -static HRESULT WINAPI PngFrameEncode_WritePixels(IWICBitmapFrameEncode *iface, - UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels) -{ - PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - png_byte **row_pointers=NULL; - UINT i; - jmp_buf jmpbuf; - TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels); - - EnterCriticalSection(&This->lock); - - if (!This->frame_initialized || !This->width || !This->height || !This->format) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_WRONGSTATE; - } - - if (lineCount == 0 || lineCount + This->lines_written > This->height) - { - LeaveCriticalSection(&This->lock); - return E_INVALIDARG; - } - - /* set up setjmp/longjmp error handling */ - if (setjmp(jmpbuf)) - { - LeaveCriticalSection(&This->lock); - HeapFree(GetProcessHeap(), 0, row_pointers); - return E_FAIL; - } - ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn); - - if (!This->info_written) - { - if (This->interlace) - { - /* libpng requires us to write all data multiple times in this case. */ - This->stride = (This->format->bpp * This->width + 7)/8; - This->data = HeapAlloc(GetProcessHeap(), 0, This->height * This->stride); - if (!This->data) - { - LeaveCriticalSection(&This->lock); - return E_OUTOFMEMORY; - } - } - - /* Tell PNG we need to byte swap if writing a >8-bpp image */ - if (This->format->bit_depth > 8) - ppng_set_swap(This->png_ptr); - - ppng_set_IHDR(This->png_ptr, This->info_ptr, This->width, This->height, - This->format->bit_depth, This->format->color_type, - This->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - - if (This->xres != 0.0 && This->yres != 0.0) - { - ppng_set_pHYs(This->png_ptr, This->info_ptr, (This->xres+0.0127) / 0.0254, - (This->yres+0.0127) / 0.0254, PNG_RESOLUTION_METER); - } - - if (This->format->color_type == PNG_COLOR_TYPE_PALETTE && This->colors) - { - png_color png_palette[256]; - png_byte trans[256]; - UINT i, num_trans = 0, colors; - - /* Newer libpng versions don't accept larger palettes than the declared - * bit depth, so we need to generate the palette of the correct length. - */ - colors = min(This->colors, 1 << This->format->bit_depth); - - for (i = 0; i < colors; i++) - { - png_palette[i].red = (This->palette[i] >> 16) & 0xff; - png_palette[i].green = (This->palette[i] >> 8) & 0xff; - png_palette[i].blue = This->palette[i] & 0xff; - trans[i] = (This->palette[i] >> 24) & 0xff; - if (trans[i] != 0xff) - num_trans = i+1; - } - - ppng_set_PLTE(This->png_ptr, This->info_ptr, png_palette, colors); - - if (num_trans) - ppng_set_tRNS(This->png_ptr, This->info_ptr, trans, num_trans, NULL); - } - - ppng_write_info(This->png_ptr, This->info_ptr); - - if (This->format->remove_filler) - ppng_set_filler(This->png_ptr, 0, PNG_FILLER_AFTER); - - if (This->format->swap_rgb) - ppng_set_bgr(This->png_ptr); - - if (This->interlace) - This->passes = ppng_set_interlace_handling(This->png_ptr); - - if (This->filter != WICPngFilterUnspecified) - { - static const int png_filter_map[] = - { - /* WICPngFilterUnspecified */ PNG_NO_FILTERS, - /* WICPngFilterNone */ PNG_FILTER_NONE, - /* WICPngFilterSub */ PNG_FILTER_SUB, - /* WICPngFilterUp */ PNG_FILTER_UP, - /* WICPngFilterAverage */ PNG_FILTER_AVG, - /* WICPngFilterPaeth */ PNG_FILTER_PAETH, - /* WICPngFilterAdaptive */ PNG_ALL_FILTERS, - }; - - ppng_set_filter(This->png_ptr, 0, png_filter_map[This->filter]); - } - - This->info_written = TRUE; - } - - if (This->interlace) - { - /* Just store the data so we can write it in multiple passes in Commit. */ - for (i=0; i<lineCount; i++) - memcpy(This->data + This->stride * (This->lines_written + i), - pbPixels + cbStride * i, - This->stride); - - This->lines_written += lineCount; - - LeaveCriticalSection(&This->lock); - return S_OK; - } - - row_pointers = HeapAlloc(GetProcessHeap(), 0, lineCount * sizeof(png_byte*)); - if (!row_pointers) - { - LeaveCriticalSection(&This->lock); - return E_OUTOFMEMORY; - } - - for (i=0; i<lineCount; i++) - row_pointers[i] = pbPixels + cbStride * i; - - ppng_write_rows(This->png_ptr, row_pointers, lineCount); - This->lines_written += lineCount; - - LeaveCriticalSection(&This->lock); - - HeapFree(GetProcessHeap(), 0, row_pointers); - - return S_OK; -} - -static HRESULT WINAPI PngFrameEncode_WriteSource(IWICBitmapFrameEncode *iface, - IWICBitmapSource *pIBitmapSource, WICRect *prc) +HRESULT PngDecoder_CreateInstance(REFIID iid, void** ppv) { - PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface); HRESULT hr; - TRACE("(%p,%p,%s)\n", iface, pIBitmapSource, debug_wic_rect(prc)); - - if (!This->frame_initialized) - return WINCODEC_ERR_WRONGSTATE; + struct decoder *decoder; + struct decoder_info decoder_info;
- hr = configure_write_source(iface, pIBitmapSource, prc, - This->format ? This->format->guid : NULL, This->width, This->height, - This->xres, This->yres); + hr = get_unix_decoder(&CLSID_WICPngDecoder, &decoder_info, &decoder);
if (SUCCEEDED(hr)) - { - hr = write_source(iface, pIBitmapSource, prc, - This->format->guid, This->format->bpp, - !This->colors && This->format->color_type == PNG_COLOR_TYPE_PALETTE, - This->width, This->height); - } - - return hr; -} - -static HRESULT WINAPI PngFrameEncode_Commit(IWICBitmapFrameEncode *iface) -{ - PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - png_byte **row_pointers=NULL; - jmp_buf jmpbuf; - TRACE("(%p)\n", iface); - - EnterCriticalSection(&This->lock); - - if (!This->info_written || This->lines_written != This->height || This->frame_committed) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_WRONGSTATE; - } - - /* set up setjmp/longjmp error handling */ - if (setjmp(jmpbuf)) - { - LeaveCriticalSection(&This->lock); - HeapFree(GetProcessHeap(), 0, row_pointers); - return E_FAIL; - } - ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn); - - if (This->interlace) - { - int i; - - row_pointers = HeapAlloc(GetProcessHeap(), 0, This->height * sizeof(png_byte*)); - if (!row_pointers) - { - LeaveCriticalSection(&This->lock); - return E_OUTOFMEMORY; - } - - for (i=0; i<This->height; i++) - row_pointers[i] = This->data + This->stride * i; - - for (i=0; i<This->passes; i++) - ppng_write_rows(This->png_ptr, row_pointers, This->height); - } - - ppng_write_end(This->png_ptr, This->info_ptr); - - This->frame_committed = TRUE; - - HeapFree(GetProcessHeap(), 0, row_pointers); - - LeaveCriticalSection(&This->lock); - - return S_OK; -} - -static HRESULT WINAPI PngFrameEncode_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface, - IWICMetadataQueryWriter **ppIMetadataQueryWriter) -{ - FIXME("(%p, %p): stub\n", iface, ppIMetadataQueryWriter); - return E_NOTIMPL; -} - -static const IWICBitmapFrameEncodeVtbl PngEncoder_FrameVtbl = { - PngFrameEncode_QueryInterface, - PngFrameEncode_AddRef, - PngFrameEncode_Release, - PngFrameEncode_Initialize, - PngFrameEncode_SetSize, - PngFrameEncode_SetResolution, - PngFrameEncode_SetPixelFormat, - PngFrameEncode_SetColorContexts, - PngFrameEncode_SetPalette, - PngFrameEncode_SetThumbnail, - PngFrameEncode_WritePixels, - PngFrameEncode_WriteSource, - PngFrameEncode_Commit, - PngFrameEncode_GetMetadataQueryWriter -}; - -static HRESULT WINAPI PngEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid, - void **ppv) -{ - PngEncoder *This = impl_from_IWICBitmapEncoder(iface); - TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); - - if (!ppv) return E_INVALIDARG; - - if (IsEqualIID(&IID_IUnknown, iid) || - IsEqualIID(&IID_IWICBitmapEncoder, iid)) - { - *ppv = &This->IWICBitmapEncoder_iface; - } - else - { - *ppv = NULL; - return E_NOINTERFACE; - } - - IUnknown_AddRef((IUnknown*)*ppv); - return S_OK; -} - -static ULONG WINAPI PngEncoder_AddRef(IWICBitmapEncoder *iface) -{ - PngEncoder *This = impl_from_IWICBitmapEncoder(iface); - ULONG ref = InterlockedIncrement(&This->ref); - - TRACE("(%p) refcount=%u\n", iface, ref); - - return ref; -} - -static ULONG WINAPI PngEncoder_Release(IWICBitmapEncoder *iface) -{ - PngEncoder *This = impl_from_IWICBitmapEncoder(iface); - ULONG ref = InterlockedDecrement(&This->ref); - - TRACE("(%p) refcount=%u\n", iface, ref); - - if (ref == 0) - { - This->lock.DebugInfo->Spare[0] = 0; - DeleteCriticalSection(&This->lock); - if (This->png_ptr) - ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr); - if (This->stream) - IStream_Release(This->stream); - HeapFree(GetProcessHeap(), 0, This->data); - encoder_destroy(This->encoder); - HeapFree(GetProcessHeap(), 0, This); - } - - return ref; -} - -static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length) -{ - PngEncoder *This = ppng_get_io_ptr(png_ptr); - HRESULT hr; - ULONG byteswritten; - - hr = IStream_Write(This->stream, data, length, &byteswritten); - if (FAILED(hr) || byteswritten != length) - { - ppng_error(png_ptr, "failed writing data"); - } -} - -static void user_flush(png_structp png_ptr) -{ -} - -static HRESULT WINAPI PngEncoder_Initialize(IWICBitmapEncoder *iface, - IStream *pIStream, WICBitmapEncoderCacheOption cacheOption) -{ - PngEncoder *This = impl_from_IWICBitmapEncoder(iface); - jmp_buf jmpbuf; - - TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption); - - EnterCriticalSection(&This->lock); - - if (This->png_ptr) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_WRONGSTATE; - } - - /* initialize libpng */ - This->png_ptr = ppng_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!This->png_ptr) - { - LeaveCriticalSection(&This->lock); - return E_FAIL; - } - - This->info_ptr = ppng_create_info_struct(This->png_ptr); - if (!This->info_ptr) - { - ppng_destroy_write_struct(&This->png_ptr, NULL); - This->png_ptr = NULL; - LeaveCriticalSection(&This->lock); - return E_FAIL; - } - - IStream_AddRef(pIStream); - This->stream = pIStream; - - /* set up setjmp/longjmp error handling */ - if (setjmp(jmpbuf)) - { - ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr); - This->png_ptr = NULL; - IStream_Release(This->stream); - This->stream = NULL; - LeaveCriticalSection(&This->lock); - return E_FAIL; - } - ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn); - - /* set up custom i/o handling */ - ppng_set_write_fn(This->png_ptr, This, user_write_data, user_flush); - - LeaveCriticalSection(&This->lock); - - return S_OK; -} - -static HRESULT WINAPI PngEncoder_GetContainerFormat(IWICBitmapEncoder *iface, GUID *format) -{ - PngEncoder *This = impl_from_IWICBitmapEncoder(iface); - TRACE("(%p,%p)\n", iface, format); - - if (!format) - return E_INVALIDARG; - - memcpy(format, &This->encoder_info.container_format, sizeof(*format)); - return S_OK; -} - -static HRESULT WINAPI PngEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **info) -{ - PngEncoder *This = impl_from_IWICBitmapEncoder(iface); - IWICComponentInfo *comp_info; - HRESULT hr; - - TRACE("%p,%p\n", iface, info); - - if (!info) return E_INVALIDARG; - - hr = CreateComponentInfo(&This->encoder_info.clsid, &comp_info); - if (hr == S_OK) - { - hr = IWICComponentInfo_QueryInterface(comp_info, &IID_IWICBitmapEncoderInfo, (void **)info); - IWICComponentInfo_Release(comp_info); - } - return hr; -} - -static HRESULT WINAPI PngEncoder_SetColorContexts(IWICBitmapEncoder *iface, - UINT cCount, IWICColorContext **ppIColorContext) -{ - FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext); - return E_NOTIMPL; -} - -static HRESULT WINAPI PngEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *palette) -{ - PngEncoder *This = impl_from_IWICBitmapEncoder(iface); - HRESULT hr; - - TRACE("(%p,%p)\n", iface, palette); - - EnterCriticalSection(&This->lock); - - hr = This->stream ? WINCODEC_ERR_UNSUPPORTEDOPERATION : WINCODEC_ERR_NOTINITIALIZED; - - LeaveCriticalSection(&This->lock); + hr = CommonDecoder_CreateInstance(decoder, &decoder_info, iid, ppv);
return hr; }
-static HRESULT WINAPI PngEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail) -{ - TRACE("(%p,%p)\n", iface, pIThumbnail); - return WINCODEC_ERR_UNSUPPORTEDOPERATION; -} - -static HRESULT WINAPI PngEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview) -{ - TRACE("(%p,%p)\n", iface, pIPreview); - return WINCODEC_ERR_UNSUPPORTEDOPERATION; -} - -static HRESULT WINAPI PngEncoder_CreateNewFrame(IWICBitmapEncoder *iface, - IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions) -{ - PngEncoder *This = impl_from_IWICBitmapEncoder(iface); - HRESULT hr; - DWORD opts_length; - PROPBAG2 opts[6]; - - TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions); - - EnterCriticalSection(&This->lock); - - if (This->frame_count != 0) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_UNSUPPORTEDOPERATION; - } - - if (!This->stream) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_NOTINITIALIZED; - } - - if (ppIEncoderOptions) - { - for (opts_length = 0; This->encoder_info.encoder_options[opts_length] < ENCODER_OPTION_END; opts_length++) - { - opts[opts_length] = encoder_option_properties[This->encoder_info.encoder_options[opts_length]]; - } - - hr = CreatePropertyBag2(opts, opts_length, ppIEncoderOptions); - if (FAILED(hr)) - { - LeaveCriticalSection(&This->lock); - return hr; - } - } - - This->frame_count = 1; - - LeaveCriticalSection(&This->lock); - - IWICBitmapEncoder_AddRef(iface); - *ppIFrameEncode = &This->IWICBitmapFrameEncode_iface; - - return S_OK; -} - -static HRESULT WINAPI PngEncoder_Commit(IWICBitmapEncoder *iface) -{ - PngEncoder *This = impl_from_IWICBitmapEncoder(iface); - TRACE("(%p)\n", iface); - - EnterCriticalSection(&This->lock); - - if (!This->frame_committed || This->committed) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_WRONGSTATE; - } - - This->committed = TRUE; - - LeaveCriticalSection(&This->lock); - - return S_OK; -} - -static HRESULT WINAPI PngEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface, - IWICMetadataQueryWriter **ppIMetadataQueryWriter) -{ - FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter); - return E_NOTIMPL; -} - -static const IWICBitmapEncoderVtbl PngEncoder_Vtbl = { - PngEncoder_QueryInterface, - PngEncoder_AddRef, - PngEncoder_Release, - PngEncoder_Initialize, - PngEncoder_GetContainerFormat, - PngEncoder_GetEncoderInfo, - PngEncoder_SetColorContexts, - PngEncoder_SetPalette, - PngEncoder_SetThumbnail, - PngEncoder_SetPreview, - PngEncoder_CreateNewFrame, - PngEncoder_Commit, - PngEncoder_GetMetadataQueryWriter -}; - HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv) -{ - PngEncoder *This; - HRESULT ret; - - TRACE("(%s,%p)\n", debugstr_guid(iid), ppv); - - *ppv = NULL; - - if (!load_libpng()) - { - ERR("Failed writing PNG because unable to find %s\n",SONAME_LIBPNG); - return E_FAIL; - } - - This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngEncoder)); - if (!This) return E_OUTOFMEMORY; - - ret = get_unix_encoder(&CLSID_WICPngEncoder, &This->encoder_info, &This->encoder); - - if (FAILED(ret)) - { - HeapFree(GetProcessHeap(), 0, This); - return ret; - } - - This->IWICBitmapEncoder_iface.lpVtbl = &PngEncoder_Vtbl; - This->IWICBitmapFrameEncode_iface.lpVtbl = &PngEncoder_FrameVtbl; - This->ref = 1; - This->png_ptr = NULL; - This->info_ptr = NULL; - This->stream = NULL; - This->frame_count = 0; - This->frame_initialized = FALSE; - This->format = NULL; - This->info_written = FALSE; - This->width = 0; - This->height = 0; - This->xres = 0.0; - This->yres = 0.0; - This->lines_written = 0; - This->frame_committed = FALSE; - This->committed = FALSE; - This->data = NULL; - This->colors = 0; - InitializeCriticalSection(&This->lock); - This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngEncoder.lock"); - - ret = IWICBitmapEncoder_QueryInterface(&This->IWICBitmapEncoder_iface, iid, ppv); - IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface); - - return ret; -} - -#else /* !SONAME_LIBPNG */ - -HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv) -{ - ERR("Trying to save PNG picture, but PNG support is not compiled in.\n"); - return E_FAIL; -} - -#endif - -HRESULT PngDecoder_CreateInstance(REFIID iid, void** ppv) { HRESULT hr; - struct decoder *decoder; - struct decoder_info decoder_info; + struct encoder *encoder; + struct encoder_info encoder_info;
- hr = get_unix_decoder(&CLSID_WICPngDecoder, &decoder_info, &decoder); + hr = get_unix_encoder(&CLSID_WICPngEncoder, &encoder_info, &encoder);
if (SUCCEEDED(hr)) - hr = CommonDecoder_CreateInstance(decoder, &decoder_info, iid, ppv); + hr = CommonEncoder_CreateInstance(encoder, &encoder_info, iid, ppv);
return hr; } diff --git a/dlls/windowscodecs/unix_iface.c b/dlls/windowscodecs/unix_iface.c index 80769b5d560..1f5dec9a6c4 100644 --- a/dlls/windowscodecs/unix_iface.c +++ b/dlls/windowscodecs/unix_iface.c @@ -42,7 +42,8 @@ static const struct unix_funcs *unix_funcs; static const struct win32_funcs win32_funcs = { stream_getsize, stream_read, - stream_seek + stream_seek, + stream_write };
static BOOL WINAPI load_unixlib( INIT_ONCE *once, void *param, void **context ) @@ -155,6 +156,42 @@ static inline struct encoder_wrapper *impl_from_encoder(struct encoder* iface) return CONTAINING_RECORD(iface, struct encoder_wrapper, win32_encoder); }
+HRESULT CDECL encoder_wrapper_initialize(struct encoder* iface, IStream* stream) +{ + struct encoder_wrapper* This = impl_from_encoder(iface); + return unix_funcs->encoder_initialize(This->unix_encoder, stream); +} + +HRESULT CDECL encoder_wrapper_get_supported_format(struct encoder* iface, GUID *pixel_format, DWORD *bpp, BOOL *indexed) +{ + struct encoder_wrapper* This = impl_from_encoder(iface); + return unix_funcs->encoder_get_supported_format(This->unix_encoder, pixel_format, bpp, indexed); +} + +HRESULT CDECL encoder_wrapper_create_frame(struct encoder* iface, const struct encoder_frame *frame) +{ + struct encoder_wrapper* This = impl_from_encoder(iface); + return unix_funcs->encoder_create_frame(This->unix_encoder, frame); +} + +HRESULT CDECL encoder_wrapper_write_lines(struct encoder* iface, BYTE *data, DWORD line_count, DWORD stride) +{ + struct encoder_wrapper* This = impl_from_encoder(iface); + return unix_funcs->encoder_write_lines(This->unix_encoder, data, line_count, stride); +} + +HRESULT CDECL encoder_wrapper_commit_frame(struct encoder* iface) +{ + struct encoder_wrapper* This = impl_from_encoder(iface); + return unix_funcs->encoder_commit_frame(This->unix_encoder); +} + +HRESULT CDECL encoder_wrapper_commit_file(struct encoder* iface) +{ + struct encoder_wrapper* This = impl_from_encoder(iface); + return unix_funcs->encoder_commit_file(This->unix_encoder); +} + void CDECL encoder_wrapper_destroy(struct encoder* iface) { struct encoder_wrapper* This = impl_from_encoder(iface); @@ -163,6 +200,12 @@ void CDECL encoder_wrapper_destroy(struct encoder* iface) }
static const struct encoder_funcs encoder_wrapper_vtable = { + encoder_wrapper_initialize, + encoder_wrapper_get_supported_format, + encoder_wrapper_create_frame, + encoder_wrapper_write_lines, + encoder_wrapper_commit_frame, + encoder_wrapper_commit_file, encoder_wrapper_destroy };
diff --git a/dlls/windowscodecs/unix_lib.c b/dlls/windowscodecs/unix_lib.c index b0c6b37bfce..7fc5f388697 100644 --- a/dlls/windowscodecs/unix_lib.c +++ b/dlls/windowscodecs/unix_lib.c @@ -62,6 +62,11 @@ HRESULT CDECL stream_seek(IStream *stream, LONGLONG ofs, DWORD origin, ULONGLONG return win32_funcs->stream_seek(stream, ofs, origin, new_position); }
+HRESULT CDECL stream_write(IStream *stream, const void *buffer, ULONG write, ULONG *bytes_written) +{ + return win32_funcs->stream_write(stream, buffer, write, bytes_written); +} + HRESULT CDECL decoder_create(const CLSID *decoder_clsid, struct decoder_info *info, struct decoder **result) { if (IsEqualGUID(decoder_clsid, &CLSID_WICPngDecoder)) @@ -93,6 +98,12 @@ static const struct unix_funcs unix_funcs = { decoder_get_color_context, decoder_destroy, encoder_create, + encoder_initialize, + encoder_get_supported_format, + encoder_create_frame, + encoder_write_lines, + encoder_commit_frame, + encoder_commit_file, encoder_destroy };
diff --git a/dlls/windowscodecs/wincodecs_common.h b/dlls/windowscodecs/wincodecs_common.h index 303c2d9f38b..75af544fac7 100644 --- a/dlls/windowscodecs/wincodecs_common.h +++ b/dlls/windowscodecs/wincodecs_common.h @@ -48,6 +48,36 @@ void CDECL decoder_destroy(struct decoder *decoder) decoder->vtable->destroy(decoder); }
+HRESULT CDECL encoder_initialize(struct encoder *encoder, IStream *stream) +{ + return encoder->vtable->initialize(encoder, stream); +} + +HRESULT CDECL encoder_get_supported_format(struct encoder* encoder, GUID *pixel_format, DWORD *bpp, BOOL *indexed) +{ + return encoder->vtable->get_supported_format(encoder, pixel_format, bpp, indexed); +} + +HRESULT CDECL encoder_create_frame(struct encoder* encoder, const struct encoder_frame *frame) +{ + return encoder->vtable->create_frame(encoder, frame); +} + +HRESULT CDECL encoder_write_lines(struct encoder* encoder, BYTE *data, DWORD line_count, DWORD stride) +{ + return encoder->vtable->write_lines(encoder, data, line_count, stride); +} + +HRESULT CDECL encoder_commit_frame(struct encoder* encoder) +{ + return encoder->vtable->commit_frame(encoder); +} + +HRESULT CDECL encoder_commit_file(struct encoder* encoder) +{ + return encoder->vtable->commit_file(encoder); +} + void CDECL encoder_destroy(struct encoder *encoder) { encoder->vtable->destroy(encoder); diff --git a/dlls/windowscodecs/wincodecs_private.h b/dlls/windowscodecs/wincodecs_private.h index 8495ea73521..a3d5f5591b0 100644 --- a/dlls/windowscodecs/wincodecs_private.h +++ b/dlls/windowscodecs/wincodecs_private.h @@ -313,12 +313,14 @@ struct decoder_funcs HRESULT CDECL stream_getsize(IStream *stream, ULONGLONG *size); HRESULT CDECL stream_read(IStream *stream, void *buffer, ULONG read, ULONG *bytes_read); HRESULT CDECL stream_seek(IStream *stream, LONGLONG ofs, DWORD origin, ULONGLONG *new_position); +HRESULT CDECL stream_write(IStream *stream, const void *buffer, ULONG write, ULONG *bytes_written);
struct win32_funcs { HRESULT (CDECL *stream_getsize)(IStream *stream, ULONGLONG *size); HRESULT (CDECL *stream_read)(IStream *stream, void *buffer, ULONG read, ULONG *bytes_read); HRESULT (CDECL *stream_seek)(IStream *stream, LONGLONG ofs, DWORD origin, ULONGLONG *new_position); + HRESULT (CDECL *stream_write)(IStream *stream, const void *buffer, ULONG write, ULONG *bytes_written); };
HRESULT CDECL decoder_create(const CLSID *decoder_clsid, struct decoder_info *info, struct decoder **result); @@ -342,13 +344,30 @@ enum encoder_option ENCODER_OPTION_END };
+#define ENCODER_FLAGS_MULTI_FRAME 0x1 + struct encoder_info { + DWORD flags; GUID container_format; CLSID clsid; DWORD encoder_options[7]; };
+struct encoder_frame +{ + GUID pixel_format; + UINT width, height; + UINT bpp; + BOOL indexed; + double dpix, dpiy; + DWORD num_colors; + WICColor palette[256]; + /* encoder options */ + BOOL interlace; + DWORD filter; +}; + struct encoder { const struct encoder_funcs *vtable; @@ -356,9 +375,21 @@ struct encoder
struct encoder_funcs { + HRESULT (CDECL *initialize)(struct encoder* This, IStream *stream); + HRESULT (CDECL *get_supported_format)(struct encoder* This, GUID *pixel_format, DWORD *bpp, BOOL *indexed); + HRESULT (CDECL *create_frame)(struct encoder* This, const struct encoder_frame *frame); + HRESULT (CDECL *write_lines)(struct encoder* This, BYTE *data, DWORD line_count, DWORD stride); + HRESULT (CDECL *commit_frame)(struct encoder* This); + HRESULT (CDECL *commit_file)(struct encoder* This); void (CDECL *destroy)(struct encoder* This); };
+HRESULT CDECL encoder_initialize(struct encoder* This, IStream *stream); +HRESULT CDECL encoder_get_supported_format(struct encoder* This, GUID *pixel_format, DWORD *bpp, BOOL *indexed); +HRESULT CDECL encoder_create_frame(struct encoder* This, const struct encoder_frame *frame); +HRESULT CDECL encoder_write_lines(struct encoder* This, BYTE *data, DWORD line_count, DWORD stride); +HRESULT CDECL encoder_commit_frame(struct encoder* This); +HRESULT CDECL encoder_commit_file(struct encoder* This); void CDECL encoder_destroy(struct encoder* This);
HRESULT CDECL png_decoder_create(struct decoder_info *info, struct decoder **result); @@ -380,6 +411,12 @@ struct unix_funcs BYTE **data, DWORD *datasize); void (CDECL *decoder_destroy)(struct decoder* This); HRESULT (CDECL *encoder_create)(const CLSID *encoder_clsid, struct encoder_info *info, struct encoder **result); + HRESULT (CDECL *encoder_initialize)(struct encoder* This, IStream *stream); + HRESULT (CDECL *encoder_get_supported_format)(struct encoder* This, GUID *pixel_format, DWORD *bpp, BOOL *indexed); + HRESULT (CDECL *encoder_create_frame)(struct encoder* This, const struct encoder_frame *frame); + HRESULT (CDECL *encoder_write_lines)(struct encoder* This, BYTE *data, DWORD line_count, DWORD stride); + HRESULT (CDECL *encoder_commit_frame)(struct encoder* This); + HRESULT (CDECL *encoder_commit_file)(struct encoder* This); void (CDECL *encoder_destroy)(struct encoder* This); };
@@ -389,4 +426,7 @@ HRESULT get_unix_encoder(const CLSID *encoder_clsid, struct encoder_info *info, extern HRESULT CommonDecoder_CreateInstance(struct decoder *decoder, const struct decoder_info *decoder_info, REFIID iid, void** ppv) DECLSPEC_HIDDEN;
+extern HRESULT CommonEncoder_CreateInstance(struct encoder *encoder, + const struct encoder_info *encoder_info, REFIID iid, void** ppv) DECLSPEC_HIDDEN; + #endif /* WINCODECS_PRIVATE_H */