[PATCH 2/8] windowscodecs: Start loading PNG in unix lib.
Esme Povirk
esme at codeweavers.com
Fri Oct 9 14:56:17 CDT 2020
Signed-off-by: Esme Povirk <esme at codeweavers.com>
---
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 336fbb389b8..f41ef765f9b 100644
--- a/dlls/windowscodecs/libpng.c
+++ b/dlls/windowscodecs/libpng.c
@@ -42,6 +42,67 @@ WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
#ifdef SONAME_LIBPNG
+#include <png.h>
+
+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 3849e24c2a1..2d8b0336f3f 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 cae64a6774c..924ed7f5c0d 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 80321d5b745..fb4400bfe97 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 4592a7ee15b..ddc3d486dcd 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 0085639b2e1..110a0bd510a 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);
};
--
2.17.1
More information about the wine-devel
mailing list