[PATCH v5 7/7] wmphoto: Implement WmpDecoder using jxrlib.

Rémi Bernon rbernon at codeweavers.com
Thu Sep 24 13:10:54 CDT 2020


Only a single frame is supported for now, but it should not be too hard
to extend it to support multiple frames if there's any need for it.

Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---

v5: Move part of the implementation to the unix side, mostly the
    WMPStream to IStream bridge. The unix interface follows the jxrlib
    decoder interface otherwise, with a few adaptations to make it
    simpler to use. This should make it fairly easy to move to a full
    PE build if we want/need it at some point.

    Don't keep reference on jxrlib decoder anymore, create one on
    Initialize to get the frame count, and on frame request to decode
    the frame, then release it as soon as it's not needed.

 dlls/d3dx10_43/tests/d3dx10.c        |   3 +-
 dlls/windowscodecs/Makefile.in       |   1 +
 dlls/windowscodecs/copy_pixels.c     |  90 +++++++++
 dlls/windowscodecs/main.c            |  65 -------
 dlls/windowscodecs/tests/wmpformat.c |   6 +-
 dlls/wmphoto/Makefile.in             |   1 +
 dlls/wmphoto/jxrlib.c                | 195 +++++++++++++++++++
 dlls/wmphoto/main.c                  | 275 ++++++++++++++++++++++++++-
 dlls/wmphoto/wmphoto_private.h       |  16 ++
 9 files changed, 578 insertions(+), 74 deletions(-)
 create mode 100644 dlls/windowscodecs/copy_pixels.c

diff --git a/dlls/d3dx10_43/tests/d3dx10.c b/dlls/d3dx10_43/tests/d3dx10.c
index 640e83fcd0f..fb5eb79e4e4 100644
--- a/dlls/d3dx10_43/tests/d3dx10.c
+++ b/dlls/d3dx10_43/tests/d3dx10.c
@@ -1376,8 +1376,7 @@ static void test_get_image_info(void)
     for (i = 0; i < ARRAY_SIZE(test_image); ++i)
     {
         hr = D3DX10GetImageInfoFromMemory(test_image[i].data, test_image[i].size, NULL, &image_info, NULL);
-        todo_wine_if(test_image[i].expected.ImageFileFormat == D3DX10_IFF_WMP)
-            ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
+        ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
         if (hr != S_OK)
             continue;
         check_image_info(&image_info, i, __LINE__);
diff --git a/dlls/windowscodecs/Makefile.in b/dlls/windowscodecs/Makefile.in
index fac90344fa3..56fa821939f 100644
--- a/dlls/windowscodecs/Makefile.in
+++ b/dlls/windowscodecs/Makefile.in
@@ -13,6 +13,7 @@ C_SRCS = \
 	colorcontext.c \
 	colortransform.c \
 	converter.c \
+	copy_pixels.c \
 	ddsformat.c \
 	fliprotate.c \
 	gifformat.c \
diff --git a/dlls/windowscodecs/copy_pixels.c b/dlls/windowscodecs/copy_pixels.c
new file mode 100644
index 00000000000..da905557b3f
--- /dev/null
+++ b/dlls/windowscodecs/copy_pixels.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2009 Vincent Povirk for CodeWeavers
+ *
+ * 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 <stdarg.h>
+
+#include "wincodecsdk.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
+
+HRESULT copy_pixels(UINT bpp, const BYTE *srcbuffer,
+    UINT srcwidth, UINT srcheight, INT srcstride,
+    const WICRect *rc, UINT dststride, UINT dstbuffersize, BYTE *dstbuffer)
+{
+    UINT bytesperrow;
+    UINT row_offset; /* number of bits into the source rows where the data starts */
+    WICRect rect;
+
+    if (!rc)
+    {
+        rect.X = 0;
+        rect.Y = 0;
+        rect.Width = srcwidth;
+        rect.Height = srcheight;
+        rc = ▭
+    }
+    else
+    {
+        if (rc->X < 0 || rc->Y < 0 || rc->X+rc->Width > srcwidth || rc->Y+rc->Height > srcheight)
+            return E_INVALIDARG;
+    }
+
+    bytesperrow = ((bpp * rc->Width)+7)/8;
+
+    if (dststride < bytesperrow)
+        return E_INVALIDARG;
+
+    if ((dststride * (rc->Height-1)) + bytesperrow > dstbuffersize)
+        return E_INVALIDARG;
+
+    /* if the whole bitmap is copied and the buffer format matches then it's a matter of a single memcpy */
+    if (rc->X == 0 && rc->Y == 0 && rc->Width == srcwidth && rc->Height == srcheight &&
+        srcstride == dststride && srcstride == bytesperrow)
+    {
+        memcpy(dstbuffer, srcbuffer, srcstride * srcheight);
+        return S_OK;
+    }
+
+    row_offset = rc->X * bpp;
+
+    if (row_offset % 8 == 0)
+    {
+        /* everything lines up on a byte boundary */
+        INT row;
+        const BYTE *src;
+        BYTE *dst;
+
+        src = srcbuffer + (row_offset / 8) + srcstride * rc->Y;
+        dst = dstbuffer;
+        for (row=0; row < rc->Height; row++)
+        {
+            memcpy(dst, src, bytesperrow);
+            src += srcstride;
+            dst += dststride;
+        }
+        return S_OK;
+    }
+    else
+    {
+        /* we have to do a weird bitwise copy. eww. */
+        FIXME("cannot reliably copy bitmap data if bpp < 8\n");
+        return E_FAIL;
+    }
+}
diff --git a/dlls/windowscodecs/main.c b/dlls/windowscodecs/main.c
index 7e03112a63c..ca708d26fd4 100644
--- a/dlls/windowscodecs/main.c
+++ b/dlls/windowscodecs/main.c
@@ -55,71 +55,6 @@ HRESULT WINAPI DllCanUnloadNow(void)
     return S_FALSE;
 }
 
-HRESULT copy_pixels(UINT bpp, const BYTE *srcbuffer,
-    UINT srcwidth, UINT srcheight, INT srcstride,
-    const WICRect *rc, UINT dststride, UINT dstbuffersize, BYTE *dstbuffer)
-{
-    UINT bytesperrow;
-    UINT row_offset; /* number of bits into the source rows where the data starts */
-    WICRect rect;
-
-    if (!rc)
-    {
-        rect.X = 0;
-        rect.Y = 0;
-        rect.Width = srcwidth;
-        rect.Height = srcheight;
-        rc = ▭
-    }
-    else
-    {
-        if (rc->X < 0 || rc->Y < 0 || rc->X+rc->Width > srcwidth || rc->Y+rc->Height > srcheight)
-            return E_INVALIDARG;
-    }
-
-    bytesperrow = ((bpp * rc->Width)+7)/8;
-
-    if (dststride < bytesperrow)
-        return E_INVALIDARG;
-
-    if ((dststride * (rc->Height-1)) + bytesperrow > dstbuffersize)
-        return E_INVALIDARG;
-
-    /* if the whole bitmap is copied and the buffer format matches then it's a matter of a single memcpy */
-    if (rc->X == 0 && rc->Y == 0 && rc->Width == srcwidth && rc->Height == srcheight &&
-        srcstride == dststride && srcstride == bytesperrow)
-    {
-        memcpy(dstbuffer, srcbuffer, srcstride * srcheight);
-        return S_OK;
-    }
-
-    row_offset = rc->X * bpp;
-
-    if (row_offset % 8 == 0)
-    {
-        /* everything lines up on a byte boundary */
-        INT row;
-        const BYTE *src;
-        BYTE *dst;
-
-        src = srcbuffer + (row_offset / 8) + srcstride * rc->Y;
-        dst = dstbuffer;
-        for (row=0; row < rc->Height; row++)
-        {
-            memcpy(dst, src, bytesperrow);
-            src += srcstride;
-            dst += dststride;
-        }
-        return S_OK;
-    }
-    else
-    {
-        /* we have to do a weird bitwise copy. eww. */
-        FIXME("cannot reliably copy bitmap data if bpp < 8\n");
-        return E_FAIL;
-    }
-}
-
 HRESULT configure_write_source(IWICBitmapFrameEncode *iface,
     IWICBitmapSource *source, const WICRect *prc,
     const WICPixelFormatGUID *format,
diff --git a/dlls/windowscodecs/tests/wmpformat.c b/dlls/windowscodecs/tests/wmpformat.c
index 12bc1275919..e808e6fee53 100644
--- a/dlls/windowscodecs/tests/wmpformat.c
+++ b/dlls/windowscodecs/tests/wmpformat.c
@@ -119,8 +119,8 @@ static void test_decode(void)
        "unexpected container format\n");
 
     hr = IWICBitmapDecoder_GetFrameCount(decoder, &count);
-    todo_wine ok(SUCCEEDED(hr), "GetFrameCount failed, hr=%x\n", hr);
-    todo_wine ok(count == 1, "unexpected count %u\n", count);
+    ok(SUCCEEDED(hr), "GetFrameCount failed, hr=%x\n", hr);
+    ok(count == 1, "unexpected count %u\n", count);
 
     hr = IWICBitmapDecoder_GetFrame(decoder, 0, NULL);
     ok(hr == E_INVALIDARG, "GetFrame(NULL) returned hr=%x\n", hr);
@@ -128,7 +128,7 @@ static void test_decode(void)
     for (j = 2; j > 0; --j)
     {
         hr = IWICBitmapDecoder_GetFrame(decoder, 0, &framedecode);
-        todo_wine ok(SUCCEEDED(hr), "GetFrame failed, hr=%x\n", hr);
+        ok(SUCCEEDED(hr), "GetFrame failed, hr=%x\n", hr);
 
         if (FAILED(hr))
         {
diff --git a/dlls/wmphoto/Makefile.in b/dlls/wmphoto/Makefile.in
index 37c879aed49..a9dce70a4de 100644
--- a/dlls/wmphoto/Makefile.in
+++ b/dlls/wmphoto/Makefile.in
@@ -6,6 +6,7 @@ EXTRADLLFLAGS = -mno-cygwin
 EXTRAINCL = $(JXRLIB_CFLAGS)
 
 C_SRCS = \
+	copy_pixels.c \
 	jxrlib.c \
 	main.c
 
diff --git a/dlls/wmphoto/jxrlib.c b/dlls/wmphoto/jxrlib.c
index 3fc78bee9de..b5f24e46c2e 100644
--- a/dlls/wmphoto/jxrlib.c
+++ b/dlls/wmphoto/jxrlib.c
@@ -28,12 +28,21 @@
 #include <string.h>
 #include <stdlib.h>
 
+#define COBJMACROS
+
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
 #include "windef.h"
 #include "winbase.h"
 #include "winerror.h"
 #include "winternl.h"
+#include "objidl.h"
+
+#ifdef SONAME_LIBJXRGLUE
+#define ERR JXR_ERR
+#include <JXRGlue.h>
+#undef ERR
+#endif
 
 #include "wine/debug.h"
 
@@ -43,10 +52,190 @@ WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
 
 #ifdef SONAME_LIBJXRGLUE
 static void *libjxrglue;
+static typeof(PKImageDecode_Create_WMP) *pPKImageDecode_Create_WMP;
+
+struct decoder_proxy
+{
+    struct jxrlib_decoder decoder_iface;
+    struct WMPStream WMPStream_iface;
+    PKImageDecode *decoder;
+    IStream *stream;
+};
+
+static inline struct decoder_proxy *impl_from_jxrlib_decoder(struct jxrlib_decoder *iface)
+{
+    return CONTAINING_RECORD(iface, struct decoder_proxy, decoder_iface);
+}
+
+static inline struct decoder_proxy *impl_from_WMPStream(struct WMPStream *iface)
+{
+    return CONTAINING_RECORD(iface, struct decoder_proxy, WMPStream_iface);
+}
+
+static JXR_ERR wmp_stream_Close(struct WMPStream **piface)
+{
+    TRACE("iface %p.\n", piface);
+    return WMP_errSuccess;
+}
+
+static Bool wmp_stream_EOS(struct WMPStream *iface)
+{
+    FIXME("iface %p, stub!\n", iface);
+    return FALSE;
+}
+
+static JXR_ERR wmp_stream_Read(struct WMPStream *iface, void *buf, size_t len)
+{
+    struct decoder_proxy *This = impl_from_WMPStream(iface);
+    ULONG count;
+
+    TRACE("iface %p, buf %p, len %zx.\n", iface, buf, len);
+
+    if (FAILED(IStream_Read(This->stream, buf, len, &count)) || count < len) return WMP_errFileIO;
+    return WMP_errSuccess;
+}
+
+static JXR_ERR wmp_stream_Write(struct WMPStream *iface, const void *buf, size_t len)
+{
+    struct decoder_proxy *This = impl_from_WMPStream(iface);
+    ULONG count;
+
+    TRACE("iface %p, buf %p, len %zx.\n", iface, buf, len);
+
+    if (FAILED(IStream_Write(This->stream, buf, len, &count)) || count < len) return WMP_errFileIO;
+    return WMP_errSuccess;
+}
+
+static JXR_ERR wmp_stream_SetPos(struct WMPStream *iface, size_t pos)
+{
+    struct decoder_proxy *This = impl_from_WMPStream(iface);
+    LARGE_INTEGER move;
+
+    TRACE("iface %p, pos %zx.\n", iface, pos);
+
+    move.QuadPart = pos;
+    if (FAILED(IStream_Seek(This->stream, move, STREAM_SEEK_SET, NULL))) return WMP_errFileIO;
+    return WMP_errSuccess;
+}
+
+static JXR_ERR wmp_stream_GetPos(struct WMPStream *iface, size_t *pos)
+{
+    struct decoder_proxy *This = impl_from_WMPStream(iface);
+    ULARGE_INTEGER curr;
+    LARGE_INTEGER move;
+
+    TRACE("iface %p, pos %p.\n", iface, pos);
+
+    move.QuadPart = 0;
+    if (FAILED(IStream_Seek(This->stream, move, STREAM_SEEK_CUR, &curr))) return WMP_errFileIO;
+    *pos = curr.QuadPart;
+
+    return WMP_errSuccess;
+}
+
+static JXR_ERR CDECL decoder_Initialize(struct jxrlib_decoder *iface, IStream *stream)
+{
+    struct decoder_proxy *This = impl_from_jxrlib_decoder(iface);
+    if (This->stream) return WMP_errFail;
+    This->stream = stream;
+    return This->decoder->Initialize(This->decoder, &This->WMPStream_iface);
+}
+
+static JXR_ERR CDECL decoder_GetPixelFormat(struct jxrlib_decoder *iface, GUID *guid)
+{
+    struct decoder_proxy *This = impl_from_jxrlib_decoder(iface);
+    return This->decoder->GetPixelFormat(This->decoder, guid);
+}
+
+static JXR_ERR CDECL decoder_GetSize(struct jxrlib_decoder *iface, INT32 *width, INT32 *height)
+{
+    struct decoder_proxy *This = impl_from_jxrlib_decoder(iface);
+    return This->decoder->GetSize(This->decoder, width, height);
+}
+
+static JXR_ERR CDECL decoder_GetResolution(struct jxrlib_decoder *iface, FLOAT *resx, FLOAT *resy)
+{
+    struct decoder_proxy *This = impl_from_jxrlib_decoder(iface);
+    return This->decoder->GetResolution(This->decoder, resx, resy);
+}
+
+static JXR_ERR CDECL decoder_Copy(struct jxrlib_decoder *iface, const WICRect *rect, UINT8 *buffer, UINT32 stride)
+{
+    struct decoder_proxy *This = impl_from_jxrlib_decoder(iface);
+    PKRect pkrect;
+    if (!rect) return WMP_errFail;
+    pkrect.X = rect->X;
+    pkrect.Y = rect->Y;
+    pkrect.Width = rect->Width;
+    pkrect.Height = rect->Height;
+    return This->decoder->Copy(This->decoder, &pkrect, buffer, stride);
+}
+
+static JXR_ERR CDECL decoder_GetFrameCount(struct jxrlib_decoder *iface, UINT32 *count)
+{
+    struct decoder_proxy *This = impl_from_jxrlib_decoder(iface);
+    return This->decoder->GetFrameCount(This->decoder, count);
+}
+
+static JXR_ERR CDECL decoder_SelectFrame(struct jxrlib_decoder *iface, UINT32 index)
+{
+    struct decoder_proxy *This = impl_from_jxrlib_decoder(iface);
+    return This->decoder->SelectFrame(This->decoder, index);
+}
+
+static JXR_ERR CDECL decoder_Release(struct jxrlib_decoder **iface)
+{
+    struct decoder_proxy *This = impl_from_jxrlib_decoder(*iface);
+    JXR_ERR err;
+
+    if ((err = This->decoder->Release(&This->decoder))) return err;
+
+    free(This);
+    *iface = NULL;
+
+    return err;
+}
+
+static HRESULT CDECL jxrlib_decoder_create(struct jxrlib_decoder **out)
+{
+    struct decoder_proxy *This;
+    PKImageDecode *decoder;
+    if (pPKImageDecode_Create_WMP(&decoder)) return E_FAIL;
+    if (!(This = malloc(sizeof(*This)))) return E_OUTOFMEMORY;
+
+    This->decoder_iface.Initialize = &decoder_Initialize;
+    This->decoder_iface.GetPixelFormat = &decoder_GetPixelFormat;
+    This->decoder_iface.GetSize = &decoder_GetSize;
+    This->decoder_iface.GetResolution = &decoder_GetResolution;
+    This->decoder_iface.Copy = &decoder_Copy;
+    This->decoder_iface.GetFrameCount = &decoder_GetFrameCount;
+    This->decoder_iface.SelectFrame = &decoder_SelectFrame;
+    This->decoder_iface.Release = &decoder_Release;
+
+    This->WMPStream_iface.Close = &wmp_stream_Close;
+    This->WMPStream_iface.EOS = &wmp_stream_EOS;
+    This->WMPStream_iface.Read = &wmp_stream_Read;
+    This->WMPStream_iface.Write = &wmp_stream_Write;
+    This->WMPStream_iface.SetPos = &wmp_stream_SetPos;
+    This->WMPStream_iface.GetPos = &wmp_stream_GetPos;
+
+    This->decoder = decoder;
+    This->stream = NULL;
+
+    *out = &This->decoder_iface;
+    return S_OK;
+}
+#else
+static HRESULT CDECL jxrlib_decoder_create(struct jxrlib_decoder **out)
+{
+    FIXME("JPEG-XR support not compiled in!\n");
+    return E_NOTIMPL;
+}
 #endif
 
 static const struct jxrlib_funcs jxrlib_funcs =
 {
+    jxrlib_decoder_create,
 };
 
 NTSTATUS CDECL __wine_init_unix_lib(HMODULE module, DWORD reason, const void *ptr_in, void *ptr_out)
@@ -61,6 +250,12 @@ NTSTATUS CDECL __wine_init_unix_lib(HMODULE module, DWORD reason, const void *pt
         WARN("failed to load %s\n", SONAME_LIBJXRGLUE);
         return STATUS_DLL_NOT_FOUND;
     }
+
+    if (!(pPKImageDecode_Create_WMP = dlsym(libjxrglue, "PKImageDecode_Create_WMP")))
+    {
+        ERR("unable to find PKImageDecode_Create_WMP in %s!\n", SONAME_LIBJXRGLUE);
+        return FALSE;
+    }
 #endif
 
     *(const struct jxrlib_funcs **)ptr_out = &jxrlib_funcs;
diff --git a/dlls/wmphoto/main.c b/dlls/wmphoto/main.c
index 83b5627f1af..4fe3714a6da 100644
--- a/dlls/wmphoto/main.c
+++ b/dlls/wmphoto/main.c
@@ -39,11 +39,252 @@ WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
 
 static const struct jxrlib_funcs *jxrlib_funcs;
 
+static inline const char *debug_wic_rect(const WICRect *rect)
+{
+    if (!rect) return "(null)";
+    return wine_dbg_sprintf("(%u,%u)-(%u,%u)", rect->X, rect->Y, rect->Width, rect->Height);
+}
+
+extern HRESULT copy_pixels(UINT bpp, const BYTE *srcbuffer, UINT srcwidth, UINT srcheight, INT srcstride,
+                           const WICRect *rc, UINT dststride, UINT dstbuffersize, BYTE *dstbuffer) DECLSPEC_HIDDEN;
+
+static const struct
+{
+    const WICPixelFormatGUID *format;
+    UINT bpp;
+} pixel_format_bpp[] =
+{
+    {&GUID_WICPixelFormat128bppRGBAFixedPoint, 128},
+    {&GUID_WICPixelFormat128bppRGBAFloat, 128},
+    {&GUID_WICPixelFormat128bppRGBFloat, 128},
+    {&GUID_WICPixelFormat16bppBGR555, 16},
+    {&GUID_WICPixelFormat16bppBGR565, 16},
+    {&GUID_WICPixelFormat16bppGray, 16},
+    {&GUID_WICPixelFormat16bppGrayFixedPoint, 16},
+    {&GUID_WICPixelFormat16bppGrayHalf, 16},
+    {&GUID_WICPixelFormat24bppBGR, 24},
+    {&GUID_WICPixelFormat24bppRGB, 24},
+    {&GUID_WICPixelFormat32bppBGR, 32},
+    {&GUID_WICPixelFormat32bppBGR101010, 32},
+    {&GUID_WICPixelFormat32bppBGRA, 32},
+    {&GUID_WICPixelFormat32bppCMYK, 32},
+    {&GUID_WICPixelFormat32bppGrayFixedPoint, 32},
+    {&GUID_WICPixelFormat32bppGrayFloat, 32},
+    {&GUID_WICPixelFormat32bppRGBE, 32},
+    {&GUID_WICPixelFormat40bppCMYKAlpha, 40},
+    {&GUID_WICPixelFormat48bppRGB, 48},
+    {&GUID_WICPixelFormat48bppRGBFixedPoint, 48},
+    {&GUID_WICPixelFormat48bppRGBHalf, 48},
+    {&GUID_WICPixelFormat64bppCMYK, 64},
+    {&GUID_WICPixelFormat64bppRGBA, 64},
+    {&GUID_WICPixelFormat64bppRGBAFixedPoint, 64},
+    {&GUID_WICPixelFormat64bppRGBAHalf, 64},
+    {&GUID_WICPixelFormat80bppCMYKAlpha, 80},
+    {&GUID_WICPixelFormat8bppGray, 8},
+    {&GUID_WICPixelFormat96bppRGBFixedPoint, 96},
+    {&GUID_WICPixelFormatBlackWhite, 1},
+};
+
+static inline UINT pixel_format_get_bpp(const WICPixelFormatGUID *format)
+{
+    int i;
+    for (i = 0; i < ARRAY_SIZE(pixel_format_bpp); ++i)
+        if (IsEqualGUID(format, pixel_format_bpp[i].format)) return pixel_format_bpp[i].bpp;
+    return 0;
+}
+
+struct wmp_decoder_frame
+{
+    IWICBitmapFrameDecode IWICBitmapFrameDecode_iface;
+    LONG ref;
+    WICPixelFormatGUID format;
+    UINT bpp;
+    UINT stride;
+    WICRect rect;
+    float resx, resy;
+    void *image_data;
+};
+
+static inline struct wmp_decoder_frame *impl_from_IWICBitmapFrameDecode(IWICBitmapFrameDecode *iface)
+{
+    return CONTAINING_RECORD(iface, struct wmp_decoder_frame, IWICBitmapFrameDecode_iface);
+}
+
+static HRESULT WINAPI wmp_decoder_frame_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid, void **out)
+{
+    struct wmp_decoder_frame *This = impl_from_IWICBitmapFrameDecode(iface);
+
+    TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out);
+
+    if (!out) return E_INVALIDARG;
+
+    *out = NULL;
+    if (!IsEqualIID(&IID_IUnknown, iid) &&
+        !IsEqualIID(&IID_IWICBitmapSource, iid) &&
+        !IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
+        return E_NOINTERFACE;
+
+    *out = &This->IWICBitmapFrameDecode_iface;
+    IUnknown_AddRef((IUnknown *)*out);
+    return S_OK;
+}
+
+static ULONG WINAPI wmp_decoder_frame_AddRef(IWICBitmapFrameDecode *iface)
+{
+    struct wmp_decoder_frame *This = impl_from_IWICBitmapFrameDecode(iface);
+    ULONG ref = InterlockedIncrement(&This->ref);
+    TRACE("iface %p -> ref %u.\n", iface, ref);
+    return ref;
+}
+
+static ULONG WINAPI wmp_decoder_frame_Release(IWICBitmapFrameDecode *iface)
+{
+    struct wmp_decoder_frame *This = impl_from_IWICBitmapFrameDecode(iface);
+    ULONG ref = InterlockedDecrement(&This->ref);
+    TRACE("iface %p -> ref %u.\n", iface, ref);
+
+    if (ref == 0)
+    {
+        HeapFree(GetProcessHeap(), 0, This->image_data);
+        HeapFree(GetProcessHeap(), 0, This);
+    }
+
+    return ref;
+}
+
+static HRESULT WINAPI wmp_decoder_frame_GetSize(IWICBitmapFrameDecode *iface, UINT *width, UINT *height)
+{
+    struct wmp_decoder_frame *This = impl_from_IWICBitmapFrameDecode(iface);
+    TRACE("iface %p, width %p, height %p.\n", iface, width, height);
+    *width = This->rect.Width;
+    *height = This->rect.Height;
+    return S_OK;
+}
+
+static HRESULT WINAPI wmp_decoder_frame_GetPixelFormat(IWICBitmapFrameDecode *iface, WICPixelFormatGUID *format)
+{
+    struct wmp_decoder_frame *This = impl_from_IWICBitmapFrameDecode(iface);
+    TRACE("iface %p, format %p.\n", iface, format);
+    *format = This->format;
+    return S_OK;
+}
+
+static HRESULT WINAPI wmp_decoder_frame_GetResolution(IWICBitmapFrameDecode *iface, double *dpix, double *dpiy)
+{
+    struct wmp_decoder_frame *This = impl_from_IWICBitmapFrameDecode(iface);
+    TRACE("iface %p, dpix %p, dpiy %p.\n", iface, dpix, dpiy);
+    *dpix = This->resx;
+    *dpiy = This->resy;
+    return S_OK;
+}
+
+static HRESULT WINAPI wmp_decoder_frame_CopyPalette(IWICBitmapFrameDecode *iface, IWICPalette *palette)
+{
+    TRACE("iface %p, palette %p.\n", iface, palette);
+    return WINCODEC_ERR_PALETTEUNAVAILABLE;
+}
+
+static HRESULT WINAPI wmp_decoder_frame_CopyPixels(IWICBitmapFrameDecode *iface, const WICRect *rect,
+                                                   UINT stride, UINT bufsize, BYTE *buffer)
+{
+    struct wmp_decoder_frame *This = impl_from_IWICBitmapFrameDecode(iface);
+    TRACE("iface %p, rect %p, stride %u, bufsize %u, buffer %p.\n", iface, debug_wic_rect(rect),
+          stride, bufsize, buffer);
+    return copy_pixels(This->bpp, This->image_data, This->rect.Width, This->rect.Height,
+                       This->stride, rect, stride, bufsize, buffer);
+}
+
+static HRESULT WINAPI wmp_decoder_frame_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
+                                                               IWICMetadataQueryReader **reader)
+{
+    FIXME("iface %p, reader %p, stub!\n", iface, reader);
+    if (!reader) return E_INVALIDARG;
+    *reader = NULL;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI wmp_decoder_frame_GetColorContexts(IWICBitmapFrameDecode *iface, UINT maxcount,
+                                                         IWICColorContext **contexts, UINT *count)
+{
+    FIXME("iface %p, maxcount %u, contexts %p, count %p, stub\n", iface, maxcount, contexts, count);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI wmp_decoder_frame_GetThumbnail(IWICBitmapFrameDecode *iface, IWICBitmapSource **thumbnail)
+{
+    FIXME("iface %p, thumbnail %p, stub!\n", iface, thumbnail);
+    return WINCODEC_ERR_CODECNOTHUMBNAIL;
+}
+
+static const IWICBitmapFrameDecodeVtbl wmp_decoder_frame_vtbl =
+{
+    /* IUnknown methods */
+    wmp_decoder_frame_QueryInterface,
+    wmp_decoder_frame_AddRef,
+    wmp_decoder_frame_Release,
+    /* IWICBitmapSource methods */
+    wmp_decoder_frame_GetSize,
+    wmp_decoder_frame_GetPixelFormat,
+    wmp_decoder_frame_GetResolution,
+    wmp_decoder_frame_CopyPalette,
+    wmp_decoder_frame_CopyPixels,
+    /* IWICBitmapFrameDecode methods */
+    wmp_decoder_frame_GetMetadataQueryReader,
+    wmp_decoder_frame_GetColorContexts,
+    wmp_decoder_frame_GetThumbnail
+};
+
+static HRESULT wmp_decoder_frame_create(IStream *stream, UINT frame, IWICBitmapFrameDecode **out)
+{
+    struct wmp_decoder_frame *This = NULL;
+    struct jxrlib_decoder *decoder = NULL;
+    LARGE_INTEGER seek;
+    HRESULT hr;
+
+    TRACE("stream %p, frame %u, out %p.\n", stream, frame, out);
+
+    *out = NULL;
+    seek.QuadPart = 0;
+    if (FAILED(hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL))) goto failed;
+    if (FAILED(hr = jxrlib_funcs->decoder_create(&decoder))) goto failed;
+
+    hr = E_FAIL;
+    if (decoder->Initialize(decoder, stream)) goto failed;
+    if (decoder->SelectFrame(decoder, frame)) goto failed;
+
+    hr = E_OUTOFMEMORY;
+    if (!(This = HeapAlloc(GetProcessHeap(), 0, sizeof(struct wmp_decoder_frame)))) goto failed;
+    This->IWICBitmapFrameDecode_iface.lpVtbl = &wmp_decoder_frame_vtbl;
+    This->ref = 1;
+
+    hr = E_FAIL;
+    if (decoder->GetPixelFormat(decoder, &This->format)) goto failed;
+    if (decoder->GetSize(decoder, &This->rect.Width, &This->rect.Height)) goto failed;
+    if (decoder->GetResolution(decoder, &This->resx, &This->resy)) goto failed;
+
+    if (!(This->bpp = pixel_format_get_bpp(&This->format))) goto failed;
+    This->stride = (This->rect.Width * This->bpp + 7) / 8;
+    if (!(This->image_data = HeapAlloc(GetProcessHeap(), 0, This->rect.Height * This->stride))) goto failed;
+    if (decoder->Copy(decoder, &This->rect, This->image_data, This->stride)) goto failed;
+
+    *out = &This->IWICBitmapFrameDecode_iface;
+    return S_OK;
+
+failed:
+    if (This && This->image_data) HeapFree(GetProcessHeap(), 0, This->image_data);
+    if (This) HeapFree(GetProcessHeap(), 0, This);
+    if (decoder) decoder->Release(&decoder);
+    return hr;
+}
+
 struct wmp_decoder
 {
     IWICBitmapDecoder IWICBitmapDecoder_iface;
+    IWICBitmapFrameDecode IWICBitmapFrameDecode_iface;
     LONG ref;
     IStream *stream;
+    UINT frame_count;
+    IWICBitmapFrameDecode *frame;
     CRITICAL_SECTION lock;
 };
 
@@ -87,6 +328,7 @@ static ULONG WINAPI wmp_decoder_Release(IWICBitmapDecoder *iface)
     {
         This->lock.DebugInfo->Spare[0] = 0;
         DeleteCriticalSection(&This->lock);
+        if (This->frame) IWICBitmapFrameDecode_Release(This->frame);
         if (This->stream) IStream_Release(This->stream);
         HeapFree(GetProcessHeap(), 0, This);
     }
@@ -103,6 +345,7 @@ static HRESULT WINAPI wmp_decoder_QueryCapability(IWICBitmapDecoder *iface, IStr
 static HRESULT WINAPI wmp_decoder_Initialize(IWICBitmapDecoder *iface, IStream *stream, WICDecodeOptions options)
 {
     struct wmp_decoder *This = impl_from_IWICBitmapDecoder(iface);
+    struct jxrlib_decoder *decoder = NULL;
     LARGE_INTEGER seek;
     HRESULT hr;
 
@@ -115,6 +358,12 @@ static HRESULT WINAPI wmp_decoder_Initialize(IWICBitmapDecoder *iface, IStream *
     {
         seek.QuadPart = 0;
         if (FAILED(hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL))) goto done;
+        if (FAILED(hr = jxrlib_funcs->decoder_create(&decoder))) goto done;
+
+        hr = E_FAIL;
+        if (decoder->Initialize(decoder, stream)) goto done;
+        if (decoder->GetFrameCount(decoder, &This->frame_count)) goto done;
+        if (This->frame_count > 1) FIXME("multi frame JPEG-XR not implemented\n");
 
         IStream_AddRef(stream);
         This->stream = stream;
@@ -122,6 +371,7 @@ static HRESULT WINAPI wmp_decoder_Initialize(IWICBitmapDecoder *iface, IStream *
     }
 
 done:
+    if (decoder) decoder->Release(&decoder);
     LeaveCriticalSection(&This->lock);
     return hr;
 }
@@ -175,16 +425,31 @@ static HRESULT WINAPI wmp_decoder_GetFrameCount(IWICBitmapDecoder *iface, UINT *
 {
     TRACE("iface %p, count %p.\n", iface, count);
     if (!count) return E_INVALIDARG;
-    *count = 0;
-    return E_NOTIMPL;
+    *count = 1;
+    return S_OK;
 }
 
 static HRESULT WINAPI wmp_decoder_GetFrame(IWICBitmapDecoder *iface, UINT index, IWICBitmapFrameDecode **frame)
 {
+    struct wmp_decoder *This = impl_from_IWICBitmapDecoder(iface);
+    HRESULT hr;
+
     TRACE("iface %p, index %u, frame %p.\n", iface, index, frame);
+
     if (!frame) return E_INVALIDARG;
-    *frame = NULL;
-    return E_NOTIMPL;
+
+    EnterCriticalSection(&This->lock);
+    if (!This->stream) hr = WINCODEC_ERR_WRONGSTATE;
+    else if (index >= This->frame_count) hr = E_INVALIDARG;
+    else if (index >= 1) hr = E_NOTIMPL; /* FIXME: Add support for multiple frames */
+    else if (This->frame) hr = S_OK;
+    else hr = wmp_decoder_frame_create(This->stream, index, &This->frame);
+
+    if (This->frame) IWICBitmapFrameDecode_AddRef(This->frame);
+    *frame = This->frame;
+    LeaveCriticalSection(&This->lock);
+
+    return hr;
 }
 
 static const IWICBitmapDecoderVtbl wmp_decoder_vtbl =
@@ -218,8 +483,10 @@ static HRESULT CDECL wmp_decoder_create(IUnknown *outer, IUnknown **out)
     if (!This) return E_OUTOFMEMORY;
 
     This->IWICBitmapDecoder_iface.lpVtbl = &wmp_decoder_vtbl;
+    This->IWICBitmapFrameDecode_iface.lpVtbl = &wmp_decoder_frame_vtbl;
     This->ref = 1;
     This->stream = NULL;
+    This->frame = NULL;
     InitializeCriticalSection(&This->lock);
     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": wmp_decoder.lock");
 
diff --git a/dlls/wmphoto/wmphoto_private.h b/dlls/wmphoto/wmphoto_private.h
index dce7f018039..a2148b6c13b 100644
--- a/dlls/wmphoto/wmphoto_private.h
+++ b/dlls/wmphoto/wmphoto_private.h
@@ -23,9 +23,25 @@
 
 #include "windef.h"
 #include "winbase.h"
+#include "wincodecsdk.h"
+
+typedef long JXR_ERR;
+
+struct jxrlib_decoder
+{
+    JXR_ERR (CDECL *Initialize)(struct jxrlib_decoder *iface, IStream *stream);
+    JXR_ERR (CDECL *GetPixelFormat)(struct jxrlib_decoder *iface, GUID *guid);
+    JXR_ERR (CDECL *GetSize)(struct jxrlib_decoder *iface, INT32 *width, INT32 *height);
+    JXR_ERR (CDECL *GetResolution)(struct jxrlib_decoder *iface, FLOAT *resx, FLOAT *resy);
+    JXR_ERR (CDECL *Copy)(struct jxrlib_decoder *iface, const WICRect *rect, UINT8 *buffer, UINT32 stride);
+    JXR_ERR (CDECL *GetFrameCount)(struct jxrlib_decoder *iface, UINT32 *count);
+    JXR_ERR (CDECL *SelectFrame)(struct jxrlib_decoder *iface, UINT32 index);
+    JXR_ERR (CDECL *Release)(struct jxrlib_decoder **iface);
+};
 
 struct jxrlib_funcs
 {
+    HRESULT (CDECL *decoder_create)(struct jxrlib_decoder **out);
 };
 
 #endif /* WMPHOTO_PRIVATE_H */
-- 
2.28.0




More information about the wine-devel mailing list