From 6aa8e179759ddba104b5682984048a61922f85f7 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Fri, 9 Oct 2020 14:56:17 -0500 Subject: [PATCH] windowscodecs: Start loading PNG in unix lib. Signed-off-by: Esme Povirk Signed-off-by: Alexandre Julliard --- dlls/windowscodecs/libpng.c | 159 ++++++++++++++++++++++++- dlls/windowscodecs/main.c | 19 +++ dlls/windowscodecs/pngformat.c | 21 +++- dlls/windowscodecs/unix_iface.c | 14 ++- dlls/windowscodecs/unix_lib.c | 15 +++ dlls/windowscodecs/wincodecs_common.h | 5 + dlls/windowscodecs/wincodecs_private.h | 20 ++++ 7 files changed, 247 insertions(+), 6 deletions(-) diff --git a/dlls/windowscodecs/libpng.c b/dlls/windowscodecs/libpng.c index 0f00946be83..d232382209e 100644 --- a/dlls/windowscodecs/libpng.c +++ b/dlls/windowscodecs/libpng.c @@ -42,6 +42,67 @@ WINE_DEFAULT_DEBUG_CHANNEL(wincodecs); #ifdef SONAME_LIBPNG +#include + +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_destroy_read_struct); +MAKE_FUNCPTR(png_error); +MAKE_FUNCPTR(png_get_error_ptr); +MAKE_FUNCPTR(png_get_io_ptr); +MAKE_FUNCPTR(png_read_info); +MAKE_FUNCPTR(png_set_crc_action); +MAKE_FUNCPTR(png_set_error_fn); +MAKE_FUNCPTR(png_set_read_fn); +#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; + + RtlEnterCriticalSection(&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; \ + RtlLeaveCriticalSection(&init_png_cs); \ + return NULL; \ + } + LOAD_FUNCPTR(png_create_info_struct); + LOAD_FUNCPTR(png_create_read_struct); + LOAD_FUNCPTR(png_destroy_read_struct); + LOAD_FUNCPTR(png_error); + LOAD_FUNCPTR(png_get_error_ptr); + LOAD_FUNCPTR(png_get_io_ptr); + LOAD_FUNCPTR(png_read_info); + LOAD_FUNCPTR(png_set_crc_action); + LOAD_FUNCPTR(png_set_error_fn); + LOAD_FUNCPTR(png_set_read_fn); + +#undef LOAD_FUNCPTR + } + + result = libpng_handle; + + RtlLeaveCriticalSection(&init_png_cs); + + return result; +} + struct png_decoder { struct decoder decoder; @@ -52,6 +113,97 @@ static inline struct png_decoder *impl_from_decoder(struct decoder* iface) return CONTAINING_RECORD(iface, struct png_decoder, decoder); } +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 void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + IStream *stream = ppng_get_io_ptr(png_ptr); + HRESULT hr; + ULONG bytesread; + + hr = stream_read(stream, data, length, &bytesread); + if (FAILED(hr) || bytesread != length) + { + ppng_error(png_ptr, "failed reading data"); + } +} + +HRESULT CDECL png_decoder_initialize(struct decoder *iface, IStream *stream, struct decoder_stat *st) +{ + png_structp png_ptr; + png_infop info_ptr; + png_infop end_info; + jmp_buf jmpbuf; + HRESULT hr = E_FAIL; + + png_ptr = ppng_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + return E_FAIL; + } + + info_ptr = ppng_create_info_struct(png_ptr); + if (!info_ptr) + { + ppng_destroy_read_struct(&png_ptr, NULL, NULL); + return E_FAIL; + } + + end_info = ppng_create_info_struct(png_ptr); + if (!end_info) + { + ppng_destroy_read_struct(&png_ptr, &info_ptr, NULL); + return E_FAIL; + } + + /* set up setjmp/longjmp error handling */ + if (setjmp(jmpbuf)) + { + hr = WINCODEC_ERR_UNKNOWNIMAGEFORMAT; + goto end; + } + ppng_set_error_fn(png_ptr, jmpbuf, user_error_fn, user_warning_fn); + ppng_set_crc_action(png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); + + /* seek to the start of the stream */ + hr = stream_seek(stream, 0, STREAM_SEEK_SET, NULL); + if (FAILED(hr)) + { + goto end; + } + + /* set up custom i/o handling */ + ppng_set_read_fn(png_ptr, stream, user_read_data); + + /* read the header */ + ppng_read_info(png_ptr, info_ptr); + + st->flags = WICBitmapDecoderCapabilityCanDecodeAllImages | + WICBitmapDecoderCapabilityCanDecodeSomeImages | + WICBitmapDecoderCapabilityCanEnumerateMetadata; + st->frame_count = 1; + hr = S_OK; + +end: + ppng_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + return hr; +} + void CDECL png_decoder_destroy(struct decoder* iface) { struct png_decoder *This = impl_from_decoder(iface); @@ -60,6 +212,7 @@ void CDECL png_decoder_destroy(struct decoder* iface) } static const struct decoder_funcs png_decoder_vtable = { + png_decoder_initialize, png_decoder_destroy }; @@ -67,7 +220,11 @@ HRESULT CDECL png_decoder_create(struct decoder_info *info, struct decoder **res { struct png_decoder *This; - TRACE("\n"); + if (!load_libpng()) + { + ERR("Failed reading PNG because unable to find %s\n",SONAME_LIBPNG); + return E_FAIL; + } This = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(*This)); diff --git a/dlls/windowscodecs/main.c b/dlls/windowscodecs/main.c index f747aea8e65..9ec5abe5c88 100644 --- a/dlls/windowscodecs/main.c +++ b/dlls/windowscodecs/main.c @@ -255,6 +255,25 @@ HRESULT write_source(IWICBitmapFrameEncode *iface, return hr; } +HRESULT CDECL stream_read(IStream *stream, void *buffer, ULONG read, ULONG *bytes_read) +{ + return IStream_Read(stream, buffer, read, bytes_read); +} + +HRESULT CDECL stream_seek(IStream *stream, LONGLONG ofs, DWORD origin, ULONGLONG *new_position) +{ + HRESULT hr; + LARGE_INTEGER ofs_large; + ULARGE_INTEGER pos_large; + + ofs_large.QuadPart = ofs; + hr = IStream_Seek(stream, ofs_large, origin, &pos_large); + if (new_position) + *new_position = pos_large.QuadPart; + + return hr; +} + void reverse_bgr8(UINT bytesperpixel, LPBYTE bits, UINT width, UINT height, INT stride) { UINT x, y; diff --git a/dlls/windowscodecs/pngformat.c b/dlls/windowscodecs/pngformat.c index 3b326c1266f..5a4d94ae4f3 100644 --- a/dlls/windowscodecs/pngformat.c +++ b/dlls/windowscodecs/pngformat.c @@ -446,6 +446,7 @@ typedef struct { IStream *stream; struct decoder *png_decoder; struct decoder_info decoder_info; + struct decoder_stat file_info; png_structp png_ptr; png_infop info_ptr; png_infop end_info; @@ -543,6 +544,7 @@ static ULONG WINAPI PngDecoder_Release(IWICBitmapDecoder *iface) static HRESULT WINAPI PngDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *stream, DWORD *capability) { + PngDecoder *This = impl_from_IWICBitmapDecoder(iface); HRESULT hr; TRACE("(%p,%p,%p)\n", iface, stream, capability); @@ -552,9 +554,7 @@ static HRESULT WINAPI PngDecoder_QueryCapability(IWICBitmapDecoder *iface, IStre hr = IWICBitmapDecoder_Initialize(iface, stream, WICDecodeMetadataCacheOnDemand); if (hr != S_OK) return hr; - *capability = WICBitmapDecoderCapabilityCanDecodeAllImages | - WICBitmapDecoderCapabilityCanDecodeSomeImages; - /* FIXME: WICBitmapDecoderCapabilityCanEnumerateMetadata */ + *capability = (This->file_info.flags & DECODER_FLAGS_CAPABILITY_MASK); return S_OK; } @@ -595,6 +595,16 @@ static HRESULT WINAPI PngDecoder_Initialize(IWICBitmapDecoder *iface, IStream *p EnterCriticalSection(&This->lock); + if (This->initialized) + { + hr = WINCODEC_ERR_WRONGSTATE; + goto end; + } + + hr = decoder_initialize(This->png_decoder, pIStream, &This->file_info); + if (FAILED(hr)) + goto end; + /* initialize libpng */ This->png_ptr = ppng_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!This->png_ptr) @@ -896,9 +906,12 @@ static HRESULT WINAPI PngDecoder_GetThumbnail(IWICBitmapDecoder *iface, static HRESULT WINAPI PngDecoder_GetFrameCount(IWICBitmapDecoder *iface, UINT *pCount) { + PngDecoder *This = impl_from_IWICBitmapDecoder(iface); if (!pCount) return E_INVALIDARG; - *pCount = 1; + if (!This->initialized) return WINCODEC_ERR_WRONGSTATE; + + *pCount = This->file_info.frame_count; return S_OK; } diff --git a/dlls/windowscodecs/unix_iface.c b/dlls/windowscodecs/unix_iface.c index 28f12d842bf..811e45f692e 100644 --- a/dlls/windowscodecs/unix_iface.c +++ b/dlls/windowscodecs/unix_iface.c @@ -39,9 +39,14 @@ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; static const struct unix_funcs *unix_funcs; +static const struct win32_funcs win32_funcs = { + stream_read, + stream_seek +}; + static BOOL WINAPI load_unixlib( INIT_ONCE *once, void *param, void **context ) { - __wine_init_unix_lib( windowscodecs_module, DLL_PROCESS_ATTACH, NULL, &unix_funcs ); + __wine_init_unix_lib( windowscodecs_module, DLL_PROCESS_ATTACH, &win32_funcs, &unix_funcs ); return TRUE; } @@ -61,6 +66,12 @@ static inline struct decoder_wrapper *impl_from_decoder(struct decoder* iface) return CONTAINING_RECORD(iface, struct decoder_wrapper, win32_decoder); } +HRESULT CDECL decoder_wrapper_initialize(struct decoder* iface, IStream* stream, struct decoder_stat *st) +{ + struct decoder_wrapper* This = impl_from_decoder(iface); + return unix_funcs->decoder_initialize(This->unix_decoder, stream, st); +} + void CDECL decoder_wrapper_destroy(struct decoder* iface) { struct decoder_wrapper* This = impl_from_decoder(iface); @@ -69,6 +80,7 @@ void CDECL decoder_wrapper_destroy(struct decoder* iface) } static const struct decoder_funcs decoder_wrapper_vtable = { + decoder_wrapper_initialize, decoder_wrapper_destroy }; diff --git a/dlls/windowscodecs/unix_lib.c b/dlls/windowscodecs/unix_lib.c index ef9fd0341c5..5a7477bdeb1 100644 --- a/dlls/windowscodecs/unix_lib.c +++ b/dlls/windowscodecs/unix_lib.c @@ -43,6 +43,18 @@ #include "wincodecs_common.h" +static const struct win32_funcs *win32_funcs; + +HRESULT CDECL stream_read(IStream *stream, void *buffer, ULONG read, ULONG *bytes_read) +{ + return win32_funcs->stream_read(stream, buffer, read, bytes_read); +} + +HRESULT CDECL stream_seek(IStream *stream, LONGLONG ofs, DWORD origin, ULONGLONG *new_position) +{ + return win32_funcs->stream_seek(stream, ofs, origin, new_position); +} + HRESULT CDECL decoder_create(const CLSID *decoder_clsid, struct decoder_info *info, struct decoder **result) { if (IsEqualGUID(decoder_clsid, &CLSID_WICPngDecoder)) @@ -53,6 +65,7 @@ HRESULT CDECL decoder_create(const CLSID *decoder_clsid, struct decoder_info *in static const struct unix_funcs unix_funcs = { decoder_create, + decoder_initialize, decoder_destroy }; @@ -60,6 +73,8 @@ NTSTATUS CDECL __wine_init_unix_lib( HMODULE module, DWORD reason, const void *p { if (reason != DLL_PROCESS_ATTACH) return STATUS_SUCCESS; + win32_funcs = ptr_in; + *(const struct unix_funcs **)ptr_out = &unix_funcs; return STATUS_SUCCESS; } diff --git a/dlls/windowscodecs/wincodecs_common.h b/dlls/windowscodecs/wincodecs_common.h index c33037ee597..dade447140d 100644 --- a/dlls/windowscodecs/wincodecs_common.h +++ b/dlls/windowscodecs/wincodecs_common.h @@ -16,6 +16,11 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +HRESULT CDECL decoder_initialize(struct decoder *decoder, IStream *stream, struct decoder_stat *st) +{ + return decoder->vtable->initialize(decoder, stream, st); +} + void CDECL decoder_destroy(struct decoder *decoder) { decoder->vtable->destroy(decoder); diff --git a/dlls/windowscodecs/wincodecs_private.h b/dlls/windowscodecs/wincodecs_private.h index 17c326a1a1c..b82c671f88d 100644 --- a/dlls/windowscodecs/wincodecs_private.h +++ b/dlls/windowscodecs/wincodecs_private.h @@ -259,6 +259,14 @@ struct decoder_info CLSID clsid; }; +#define DECODER_FLAGS_CAPABILITY_MASK 0x1f + +struct decoder_stat +{ + DWORD flags; + DWORD frame_count; +}; + struct decoder { const struct decoder_funcs *vtable; @@ -266,10 +274,21 @@ struct decoder struct decoder_funcs { + HRESULT (CDECL *initialize)(struct decoder* This, IStream *stream, struct decoder_stat *st); void (CDECL *destroy)(struct decoder* This); }; +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); + +struct win32_funcs +{ + 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 decoder_create(const CLSID *decoder_clsid, struct decoder_info *info, struct decoder **result); +HRESULT CDECL decoder_initialize(struct decoder *This, IStream *stream, struct decoder_stat *st); void CDECL decoder_destroy(struct decoder *This); HRESULT CDECL png_decoder_create(struct decoder_info *info, struct decoder **result); @@ -277,6 +296,7 @@ HRESULT CDECL png_decoder_create(struct decoder_info *info, struct decoder **res struct unix_funcs { HRESULT (CDECL *decoder_create)(const CLSID *decoder_clsid, struct decoder_info *info, struct decoder **result); + HRESULT (CDECL *decoder_initialize)(struct decoder *This, IStream *stream, struct decoder_stat *st); void (CDECL *decoder_destroy)(struct decoder* This); };