[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