[WINDOWSCODECS_WINETEST]
authorAmine Khaldi <amine.khaldi@reactos.org>
Thu, 13 Dec 2012 11:15:23 +0000 (11:15 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Thu, 13 Dec 2012 11:15:23 +0000 (11:15 +0000)
* Import from Wine 1.5.19.

svn path=/trunk/; revision=57900

14 files changed:
rostests/winetests/CMakeLists.txt
rostests/winetests/windowscodecs/CMakeLists.txt [new file with mode: 0644]
rostests/winetests/windowscodecs/bitmap.c [new file with mode: 0644]
rostests/winetests/windowscodecs/bmpformat.c [new file with mode: 0644]
rostests/winetests/windowscodecs/converter.c [new file with mode: 0644]
rostests/winetests/windowscodecs/gifformat.c [new file with mode: 0644]
rostests/winetests/windowscodecs/icoformat.c [new file with mode: 0644]
rostests/winetests/windowscodecs/info.c [new file with mode: 0644]
rostests/winetests/windowscodecs/metadata.c [new file with mode: 0644]
rostests/winetests/windowscodecs/palette.c [new file with mode: 0644]
rostests/winetests/windowscodecs/pngformat.c [new file with mode: 0644]
rostests/winetests/windowscodecs/stream.c [new file with mode: 0644]
rostests/winetests/windowscodecs/testlist.c [new file with mode: 0644]
rostests/winetests/windowscodecs/tiffformat.c [new file with mode: 0644]

index 0daeb5c..95275ca 100644 (file)
@@ -87,6 +87,7 @@ add_subdirectory(userenv)
 add_subdirectory(usp10)
 add_subdirectory(uxtheme)
 add_subdirectory(version)
 add_subdirectory(usp10)
 add_subdirectory(uxtheme)
 add_subdirectory(version)
+add_subdirectory(windowscodecs)
 add_subdirectory(winhttp)
 add_subdirectory(wininet)
 add_subdirectory(winmm)
 add_subdirectory(winhttp)
 add_subdirectory(wininet)
 add_subdirectory(winmm)
diff --git a/rostests/winetests/windowscodecs/CMakeLists.txt b/rostests/winetests/windowscodecs/CMakeLists.txt
new file mode 100644 (file)
index 0000000..237f05d
--- /dev/null
@@ -0,0 +1,22 @@
+
+add_definitions(-D__ROS_LONG64__)
+
+list(APPEND SOURCE
+    bitmap.c
+    bmpformat.c
+    converter.c
+    gifformat.c
+    icoformat.c
+    info.c
+    metadata.c
+    palette.c
+    pngformat.c
+    stream.c
+    testlist.c
+    tiffformat.c)
+
+add_executable(windowscodecs_winetest ${SOURCE})
+target_link_libraries(windowscodecs_winetest wine)
+set_module_type(windowscodecs_winetest win32cui)
+add_importlibs(windowscodecs_winetest windowscodecs ole32 msvcrt kernel32 ntdll)
+add_cd_file(TARGET version_winetest DESTINATION reactos/bin FOR all)
diff --git a/rostests/winetests/windowscodecs/bitmap.c b/rostests/winetests/windowscodecs/bitmap.c
new file mode 100644 (file)
index 0000000..c2ce1aa
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ * Copyright 2012 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 <math.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "objbase.h"
+#include "wincodec.h"
+#include "wine/test.h"
+
+static IWICImagingFactory *factory;
+
+static void test_createbitmap(void)
+{
+    HRESULT hr;
+    IWICBitmap *bitmap;
+    IWICPalette *palette;
+    IWICBitmapLock *lock, *lock2;
+    WICBitmapPaletteType palettetype;
+    int i;
+    WICRect rc;
+    const BYTE bitmap_data[27] = {
+        128,128,255, 128,128,128, 128,255,128,
+        128,128,128, 128,128,128, 255,255,255,
+        255,128,128, 255,255,255, 255,255,255};
+    BYTE returned_data[27] = {0};
+    BYTE *lock_buffer=NULL, *base_lock_buffer=NULL;
+    UINT lock_buffer_size=0;
+    UINT lock_buffer_stride=0;
+    WICPixelFormatGUID pixelformat = {0};
+    UINT width=0, height=0;
+    double dpix=10.0, dpiy=10.0;
+    int can_lock_null = 1;
+
+    hr = IWICImagingFactory_CreateBitmap(factory, 3, 3, &GUID_WICPixelFormat24bppBGR,
+        WICBitmapCacheOnLoad, &bitmap);
+    ok(hr == S_OK, "IWICImagingFactory_CreateBitmap failed hr=%x\n", hr);
+
+    if (FAILED(hr))
+        return;
+
+    hr = IWICImagingFactory_CreatePalette(factory, &palette);
+    ok(hr == S_OK, "IWICImagingFactory_CreatePalette failed hr=%x\n", hr);
+
+    /* Palette is unavailable until explicitly set */
+    hr = IWICBitmap_CopyPalette(bitmap, palette);
+    ok(hr == WINCODEC_ERR_PALETTEUNAVAILABLE, "IWICBitmap_CopyPalette failed hr=%x\n", hr);
+
+    hr = IWICPalette_InitializePredefined(palette, WICBitmapPaletteTypeFixedGray256, FALSE);
+    ok(hr == S_OK, "IWICPalette_InitializePredefined failed hr=%x\n", hr);
+
+    hr = IWICBitmap_SetPalette(bitmap, palette);
+    ok(hr == S_OK, "IWICBitmap_SetPalette failed hr=%x\n", hr);
+
+    hr = IWICPalette_InitializePredefined(palette, WICBitmapPaletteTypeFixedGray4, FALSE);
+    ok(hr == S_OK, "IWICPalette_InitializePredefined failed hr=%x\n", hr);
+
+    hr = IWICBitmap_CopyPalette(bitmap, palette);
+    ok(hr == S_OK, "IWICBitmap_CopyPalette failed hr=%x\n", hr);
+
+    hr = IWICPalette_GetType(palette, &palettetype);
+    ok(hr == S_OK, "IWICPalette_GetType failed hr=%x\n", hr);
+    ok(palettetype == WICBitmapPaletteTypeFixedGray256,
+        "expected WICBitmapPaletteTypeFixedGray256, got %x\n", palettetype);
+
+    IWICPalette_Release(palette);
+
+    /* pixel data is initially zeroed */
+    hr = IWICBitmap_CopyPixels(bitmap, NULL, 9, 27, returned_data);
+    ok(hr == S_OK, "IWICBitmap_CopyPixels failed hr=%x\n", hr);
+
+    for (i=0; i<27; i++)
+        ok(returned_data[i] == 0, "returned_data[%i] == %i\n", i, returned_data[i]);
+
+    /* Invalid lock rects */
+    rc.X = rc.Y = 0;
+    rc.Width = 4;
+    rc.Height = 3;
+    hr = IWICBitmap_Lock(bitmap, &rc, WICBitmapLockRead, &lock);
+    ok(hr == E_INVALIDARG, "IWICBitmap_Lock failed hr=%x\n", hr);
+    if (SUCCEEDED(hr)) IWICBitmapLock_Release(lock);
+
+    rc.Width = 3;
+    rc.Height = 4;
+    hr = IWICBitmap_Lock(bitmap, &rc, WICBitmapLockRead, &lock);
+    ok(hr == E_INVALIDARG, "IWICBitmap_Lock failed hr=%x\n", hr);
+    if (SUCCEEDED(hr)) IWICBitmapLock_Release(lock);
+
+    rc.Height = 3;
+    rc.X = 4;
+    hr = IWICBitmap_Lock(bitmap, &rc, WICBitmapLockRead, &lock);
+    ok(hr == E_INVALIDARG, "IWICBitmap_Lock failed hr=%x\n", hr);
+    if (SUCCEEDED(hr)) IWICBitmapLock_Release(lock);
+
+    rc.X = 0;
+    rc.Y = 4;
+    hr = IWICBitmap_Lock(bitmap, &rc, WICBitmapLockRead, &lock);
+    ok(hr == E_INVALIDARG, "IWICBitmap_Lock failed hr=%x\n", hr);
+    if (SUCCEEDED(hr)) IWICBitmapLock_Release(lock);
+
+    /* NULL lock rect */
+    hr = IWICBitmap_Lock(bitmap, NULL, WICBitmapLockRead, &lock);
+    ok(hr == S_OK || broken(hr == E_INVALIDARG) /* winxp */, "IWICBitmap_Lock failed hr=%x\n", hr);
+
+    if (SUCCEEDED(hr))
+    {
+        /* entire bitmap is locked */
+        hr = IWICBitmapLock_GetSize(lock, &width, &height);
+        ok(hr == S_OK, "IWICBitmapLock_GetSize failed hr=%x\n", hr);
+        ok(width == 3, "got %d, expected 3\n", width);
+        ok(height == 3, "got %d, expected 3\n", height);
+
+        IWICBitmapLock_Release(lock);
+    }
+    else
+        can_lock_null = 0;
+
+    /* lock with a valid rect */
+    rc.Y = 0;
+    hr = IWICBitmap_Lock(bitmap, &rc, WICBitmapLockRead, &lock);
+    ok(hr == S_OK, "IWICBitmap_Lock failed hr=%x\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICBitmapLock_GetStride(lock, &lock_buffer_stride);
+        ok(hr == S_OK, "IWICBitmapLock_GetStride failed hr=%x\n", hr);
+        /* stride is divisible by 4 */
+        ok(lock_buffer_stride == 12, "got %i, expected 12\n", lock_buffer_stride);
+
+        hr = IWICBitmapLock_GetDataPointer(lock, &lock_buffer_size, &lock_buffer);
+        ok(hr == S_OK, "IWICBitmapLock_GetDataPointer failed hr=%x\n", hr);
+        /* buffer size does not include padding from the last row */
+        ok(lock_buffer_size == 33, "got %i, expected 33\n", lock_buffer_size);
+        ok(lock_buffer != NULL, "got NULL data pointer\n");
+        base_lock_buffer = lock_buffer;
+
+        hr = IWICBitmapLock_GetPixelFormat(lock, &pixelformat);
+        ok(hr == S_OK, "IWICBitmapLock_GetPixelFormat failed hr=%x\n", hr);
+        ok(IsEqualGUID(&pixelformat, &GUID_WICPixelFormat24bppBGR), "unexpected pixel format\n");
+
+        hr = IWICBitmapLock_GetSize(lock, &width, &height);
+        ok(hr == S_OK, "IWICBitmapLock_GetSize failed hr=%x\n", hr);
+        ok(width == 3, "got %d, expected 3\n", width);
+        ok(height == 3, "got %d, expected 3\n", height);
+
+        /* We can have multiple simultaneous read locks */
+        hr = IWICBitmap_Lock(bitmap, &rc, WICBitmapLockRead, &lock2);
+        ok(hr == S_OK, "IWICBitmap_Lock failed hr=%x\n", hr);
+
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICBitmapLock_GetDataPointer(lock2, &lock_buffer_size, &lock_buffer);
+            ok(hr == S_OK, "IWICBitmapLock_GetDataPointer failed hr=%x\n", hr);
+            ok(lock_buffer_size == 33, "got %i, expected 33\n", lock_buffer_size);
+            ok(lock_buffer == base_lock_buffer, "got %p, expected %p\n", lock_buffer, base_lock_buffer);
+
+            IWICBitmapLock_Release(lock2);
+        }
+
+        if (can_lock_null) /* this hangs on xp/vista */
+        {
+            /* But not a read and a write lock */
+            hr = IWICBitmap_Lock(bitmap, &rc, WICBitmapLockWrite, &lock2);
+            ok(hr == WINCODEC_ERR_ALREADYLOCKED, "IWICBitmap_Lock failed hr=%x\n", hr);
+        }
+
+        /* But we don't need a write lock to write */
+        if (base_lock_buffer)
+        {
+            for (i=0; i<3; i++)
+                memcpy(base_lock_buffer + lock_buffer_stride*i, bitmap_data + i*9, 9);
+        }
+
+        IWICBitmapLock_Release(lock);
+    }
+
+    /* test that the data we wrote is returned by CopyPixels */
+    hr = IWICBitmap_CopyPixels(bitmap, NULL, 9, 27, returned_data);
+    ok(hr == S_OK, "IWICBitmap_CopyPixels failed hr=%x\n", hr);
+
+    for (i=0; i<27; i++)
+        ok(returned_data[i] == bitmap_data[i], "returned_data[%i] == %i\n", i, returned_data[i]);
+
+    /* try a valid partial rect, and write mode */
+    rc.X = 2;
+    rc.Y = 0;
+    rc.Width = 1;
+    rc.Height = 2;
+    hr = IWICBitmap_Lock(bitmap, &rc, WICBitmapLockWrite, &lock);
+    ok(hr == S_OK, "IWICBitmap_Lock failed hr=%x\n", hr);
+
+    if (SUCCEEDED(hr))
+    {
+        if (can_lock_null) /* this hangs on xp/vista */
+        {
+            /* Can't lock again while locked for writing */
+            hr = IWICBitmap_Lock(bitmap, &rc, WICBitmapLockWrite, &lock2);
+            ok(hr == WINCODEC_ERR_ALREADYLOCKED, "IWICBitmap_Lock failed hr=%x\n", hr);
+
+            hr = IWICBitmap_Lock(bitmap, &rc, WICBitmapLockRead, &lock2);
+            ok(hr == WINCODEC_ERR_ALREADYLOCKED, "IWICBitmap_Lock failed hr=%x\n", hr);
+        }
+
+        hr = IWICBitmapLock_GetStride(lock, &lock_buffer_stride);
+        ok(hr == S_OK, "IWICBitmapLock_GetStride failed hr=%x\n", hr);
+        ok(lock_buffer_stride == 12, "got %i, expected 12\n", lock_buffer_stride);
+
+        hr = IWICBitmapLock_GetDataPointer(lock, &lock_buffer_size, &lock_buffer);
+        ok(hr == S_OK, "IWICBitmapLock_GetDataPointer failed hr=%x\n", hr);
+        ok(lock_buffer_size == 15, "got %i, expected 15\n", lock_buffer_size);
+        ok(lock_buffer == base_lock_buffer+6, "got %p, expected %p+6\n", lock_buffer, base_lock_buffer);
+
+        hr = IWICBitmapLock_GetPixelFormat(lock, &pixelformat);
+        ok(hr == S_OK, "IWICBitmapLock_GetPixelFormat failed hr=%x\n", hr);
+        ok(IsEqualGUID(&pixelformat, &GUID_WICPixelFormat24bppBGR), "unexpected pixel format\n");
+
+        hr = IWICBitmapLock_GetSize(lock, &width, &height);
+        ok(hr == S_OK, "IWICBitmapLock_GetSize failed hr=%x\n", hr);
+        ok(width == 1, "got %d, expected 1\n", width);
+        ok(height == 2, "got %d, expected 2\n", height);
+
+        IWICBitmapLock_Release(lock);
+    }
+
+    hr = IWICBitmap_GetPixelFormat(bitmap, &pixelformat);
+    ok(hr == S_OK, "IWICBitmap_GetPixelFormat failed hr=%x\n", hr);
+    ok(IsEqualGUID(&pixelformat, &GUID_WICPixelFormat24bppBGR), "unexpected pixel format\n");
+
+    hr = IWICBitmap_GetResolution(bitmap, &dpix, &dpiy);
+    ok(hr == S_OK, "IWICBitmap_GetResolution failed hr=%x\n", hr);
+    ok(dpix == 0.0, "got %f, expected 0.0\n", dpix);
+    ok(dpiy == 0.0, "got %f, expected 0.0\n", dpiy);
+
+    hr = IWICBitmap_SetResolution(bitmap, 12.0, 34.0);
+    ok(hr == S_OK, "IWICBitmap_SetResolution failed hr=%x\n", hr);
+
+    hr = IWICBitmap_GetResolution(bitmap, &dpix, &dpiy);
+    ok(hr == S_OK, "IWICBitmap_GetResolution failed hr=%x\n", hr);
+    ok(dpix == 12.0, "got %f, expected 12.0\n", dpix);
+    ok(dpiy == 34.0, "got %f, expected 34.0\n", dpiy);
+
+    hr = IWICBitmap_GetSize(bitmap, &width, &height);
+    ok(hr == S_OK, "IWICBitmap_GetSize failed hr=%x\n", hr);
+    ok(width == 3, "got %d, expected 3\n", width);
+    ok(height == 3, "got %d, expected 3\n", height);
+
+    IWICBitmap_Release(bitmap);
+}
+
+static void test_createbitmapfromsource(void)
+{
+    HRESULT hr;
+    IWICBitmap *bitmap, *bitmap2;
+    IWICPalette *palette;
+    IWICBitmapLock *lock;
+    int i;
+    WICRect rc;
+    const BYTE bitmap_data[27] = {
+        128,128,255, 128,128,128, 128,255,128,
+        128,128,128, 128,128,128, 255,255,255,
+        255,128,128, 255,255,255, 255,255,255};
+    BYTE returned_data[27] = {0};
+    BYTE *lock_buffer=NULL;
+    UINT lock_buffer_stride=0;
+    UINT lock_buffer_size=0;
+    WICPixelFormatGUID pixelformat = {0};
+    UINT width=0, height=0;
+    double dpix=10.0, dpiy=10.0;
+    UINT count;
+    WICBitmapPaletteType palette_type;
+
+    hr = IWICImagingFactory_CreateBitmap(factory, 3, 3, &GUID_WICPixelFormat24bppBGR,
+        WICBitmapCacheOnLoad, &bitmap);
+    ok(hr == S_OK, "IWICImagingFactory_CreateBitmap failed hr=%x\n", hr);
+
+    if (FAILED(hr))
+        return;
+
+    hr = IWICImagingFactory_CreatePalette(factory, &palette);
+    ok(hr == S_OK, "IWICImagingFactory_CreatePalette failed hr=%x\n", hr);
+
+    hr = IWICPalette_InitializePredefined(palette, WICBitmapPaletteTypeFixedGray256, FALSE);
+    ok(hr == S_OK, "IWICPalette_InitializePredefined failed hr=%x\n", hr);
+
+    hr = IWICBitmap_SetPalette(bitmap, palette);
+    ok(hr == S_OK, "IWICBitmap_SetPalette failed hr=%x\n", hr);
+
+    IWICPalette_Release(palette);
+
+    rc.X = rc.Y = 0;
+    rc.Width = 3;
+    rc.Height = 3;
+    hr = IWICBitmap_Lock(bitmap, &rc, WICBitmapLockWrite, &lock);
+    ok(hr == S_OK, "IWICBitmap_Lock failed hr=%x\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICBitmapLock_GetStride(lock, &lock_buffer_stride);
+        ok(hr == S_OK, "IWICBitmapLock_GetStride failed hr=%x\n", hr);
+        ok(lock_buffer_stride == 12, "got %i, expected 12\n", lock_buffer_stride);
+
+        hr = IWICBitmapLock_GetDataPointer(lock, &lock_buffer_size, &lock_buffer);
+        ok(hr == S_OK, "IWICBitmapLock_GetDataPointer failed hr=%x\n", hr);
+        ok(lock_buffer_size == 33, "got %i, expected 33\n", lock_buffer_size);
+        ok(lock_buffer != NULL, "got NULL data pointer\n");
+
+        for (i=0; i<3; i++)
+            memcpy(lock_buffer + lock_buffer_stride*i, bitmap_data + i*9, 9);
+
+        IWICBitmapLock_Release(lock);
+    }
+
+    hr = IWICBitmap_SetResolution(bitmap, 12.0, 34.0);
+    ok(hr == S_OK, "IWICBitmap_SetResolution failed hr=%x\n", hr);
+
+    hr = IWICImagingFactory_CreateBitmapFromSource(factory, (IWICBitmapSource*)bitmap,
+        WICBitmapCacheOnLoad, &bitmap2);
+    ok(hr == S_OK, "IWICImagingFactory_CreateBitmapFromSource failed hr=%x\n", hr);
+
+    IWICBitmap_Release(bitmap);
+
+    if (FAILED(hr)) return;
+
+    hr = IWICImagingFactory_CreatePalette(factory, &palette);
+    ok(hr == S_OK, "IWICImagingFactory_CreatePalette failed hr=%x\n", hr);
+
+    /* palette isn't copied for non-indexed formats? */
+    hr = IWICBitmap_CopyPalette(bitmap2, palette);
+    ok(hr == WINCODEC_ERR_PALETTEUNAVAILABLE, "IWICBitmap_CopyPalette failed hr=%x\n", hr);
+
+    IWICPalette_Release(palette);
+
+    hr = IWICBitmap_CopyPixels(bitmap2, NULL, 9, 27, returned_data);
+    ok(hr == S_OK, "IWICBitmap_CopyPixels failed hr=%x\n", hr);
+
+    for (i=0; i<27; i++)
+        ok(returned_data[i] == bitmap_data[i], "returned_data[%i] == %i\n", i, returned_data[i]);
+
+    hr = IWICBitmap_GetPixelFormat(bitmap2, &pixelformat);
+    ok(hr == S_OK, "IWICBitmap_GetPixelFormat failed hr=%x\n", hr);
+    ok(IsEqualGUID(&pixelformat, &GUID_WICPixelFormat24bppBGR), "unexpected pixel format\n");
+
+    hr = IWICBitmap_GetResolution(bitmap2, &dpix, &dpiy);
+    ok(hr == S_OK, "IWICBitmap_GetResolution failed hr=%x\n", hr);
+    ok(dpix == 12.0, "got %f, expected 12.0\n", dpix);
+    ok(dpiy == 34.0, "got %f, expected 34.0\n", dpiy);
+
+    hr = IWICBitmap_GetSize(bitmap2, &width, &height);
+    ok(hr == S_OK, "IWICBitmap_GetSize failed hr=%x\n", hr);
+    ok(width == 3, "got %d, expected 3\n", width);
+    ok(height == 3, "got %d, expected 3\n", height);
+
+    IWICBitmap_Release(bitmap2);
+
+    /* Ensure palette is copied for indexed formats */
+    hr = IWICImagingFactory_CreateBitmap(factory, 3, 3, &GUID_WICPixelFormat4bppIndexed,
+        WICBitmapCacheOnLoad, &bitmap);
+    ok(hr == S_OK, "IWICImagingFactory_CreateBitmap failed hr=%x\n", hr);
+
+    hr = IWICImagingFactory_CreatePalette(factory, &palette);
+    ok(hr == S_OK, "IWICImagingFactory_CreatePalette failed hr=%x\n", hr);
+
+    hr = IWICPalette_InitializePredefined(palette, WICBitmapPaletteTypeFixedGray256, FALSE);
+    ok(hr == S_OK, "IWICPalette_InitializePredefined failed hr=%x\n", hr);
+
+    hr = IWICBitmap_SetPalette(bitmap, palette);
+    ok(hr == S_OK, "IWICBitmap_SetPalette failed hr=%x\n", hr);
+
+    IWICPalette_Release(palette);
+
+    hr = IWICImagingFactory_CreateBitmapFromSource(factory, (IWICBitmapSource*)bitmap,
+        WICBitmapCacheOnLoad, &bitmap2);
+    ok(hr == S_OK, "IWICImagingFactory_CreateBitmapFromSource failed hr=%x\n", hr);
+
+    IWICBitmap_Release(bitmap);
+
+    hr = IWICImagingFactory_CreatePalette(factory, &palette);
+    ok(hr == S_OK, "IWICImagingFactory_CreatePalette failed hr=%x\n", hr);
+
+    hr = IWICBitmap_CopyPalette(bitmap2, palette);
+    ok(hr == S_OK, "IWICBitmap_CopyPalette failed hr=%x\n", hr);
+
+    hr = IWICPalette_GetColorCount(palette, &count);
+    ok(hr == S_OK, "IWICPalette_GetColorCount failed hr=%x\n", hr);
+    ok(count == 256, "unexpected count %d\n", count);
+
+    hr = IWICPalette_GetType(palette, &palette_type);
+    ok(hr == S_OK, "IWICPalette_GetType failed hr=%x\n", hr);
+    ok(palette_type == WICBitmapPaletteTypeFixedGray256, "unexpected palette type %d\n", palette_type);
+
+    IWICPalette_Release(palette);
+
+    hr = IWICBitmap_GetPixelFormat(bitmap2, &pixelformat);
+    ok(hr == S_OK, "IWICBitmap_GetPixelFormat failed hr=%x\n", hr);
+    ok(IsEqualGUID(&pixelformat, &GUID_WICPixelFormat4bppIndexed), "unexpected pixel format\n");
+
+    hr = IWICBitmap_GetSize(bitmap2, &width, &height);
+    ok(hr == S_OK, "IWICBitmap_GetSize failed hr=%x\n", hr);
+    ok(width == 3, "got %d, expected 3\n", width);
+    ok(height == 3, "got %d, expected 3\n", height);
+
+    IWICBitmap_Release(bitmap2);
+}
+
+START_TEST(bitmap)
+{
+    HRESULT hr;
+
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+
+    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICImagingFactory, (void**)&factory);
+    ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+
+    test_createbitmap();
+    test_createbitmapfromsource();
+
+    IWICImagingFactory_Release(factory);
+
+    CoUninitialize();
+}
diff --git a/rostests/winetests/windowscodecs/bmpformat.c b/rostests/winetests/windowscodecs/bmpformat.c
new file mode 100644 (file)
index 0000000..39820b3
--- /dev/null
@@ -0,0 +1,1164 @@
+/*
+ * 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 <math.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "initguid.h"
+#include "objbase.h"
+#include "wincodec.h"
+#include "wincodecsdk.h"
+#include "wine/test.h"
+
+static const char testbmp_24bpp[] = {
+    /* BITMAPFILEHEADER */
+    66,77, /* "BM" */
+    50,0,0,0, /* file size */
+    0,0,0,0, /* reserved */
+    26,0,0,0, /* offset to bits */
+    /* BITMAPCOREHEADER */
+    12,0,0,0, /* header size */
+    2,0, /* width */
+    3,0, /* height */
+    1,0, /* planes */
+    24,0, /* bit count */
+    /* bits */
+    0,0,0,     0,255,0,     0,0,
+    255,0,0,   255,255,0,   0,0,
+    255,0,255, 255,255,255, 0,0
+};
+
+static void test_decode_24bpp(void)
+{
+    IWICBitmapDecoder *decoder, *decoder2;
+    IWICBitmapFrameDecode *framedecode;
+    IWICMetadataQueryReader *queryreader;
+    IWICColorContext *colorcontext;
+    IWICBitmapSource *thumbnail;
+    HRESULT hr;
+    HGLOBAL hbmpdata;
+    char *bmpdata;
+    IStream *bmpstream;
+    DWORD capability=0;
+    GUID guidresult;
+    UINT count=0, width=0, height=0;
+    double dpiX, dpiY;
+    BYTE imagedata[36] = {1};
+    const BYTE expected_imagedata[36] = {
+        255,0,255, 255,255,255,
+        255,0,0,   255,255,0,
+        0,0,0,     0,255,0};
+    WICRect rc;
+
+    hr = CoCreateInstance(&CLSID_WICBmpDecoder, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICBitmapDecoder, (void**)&decoder);
+    ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+    if (FAILED(hr)) return;
+
+    hbmpdata = GlobalAlloc(GMEM_MOVEABLE, sizeof(testbmp_24bpp));
+    ok(hbmpdata != 0, "GlobalAlloc failed\n");
+    if (hbmpdata)
+    {
+        bmpdata = GlobalLock(hbmpdata);
+        memcpy(bmpdata, testbmp_24bpp, sizeof(testbmp_24bpp));
+        GlobalUnlock(hbmpdata);
+
+        hr = CreateStreamOnHGlobal(hbmpdata, FALSE, &bmpstream);
+        ok(SUCCEEDED(hr), "CreateStreamOnHGlobal failed, hr=%x\n", hr);
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICBitmapDecoder_Initialize(decoder, bmpstream, WICDecodeMetadataCacheOnLoad);
+            ok(hr == S_OK, "Initialize failed, hr=%x\n", hr);
+
+            hr = IWICBitmapDecoder_GetContainerFormat(decoder, &guidresult);
+            ok(SUCCEEDED(hr), "GetContainerFormat failed, hr=%x\n", hr);
+            ok(IsEqualGUID(&guidresult, &GUID_ContainerFormatBmp), "unexpected container format\n");
+
+            hr = IWICBitmapDecoder_GetMetadataQueryReader(decoder, &queryreader);
+            ok(hr == WINCODEC_ERR_UNSUPPORTEDOPERATION, "expected WINCODEC_ERR_UNSUPPORTEDOPERATION, got %x\n", hr);
+
+            hr = IWICBitmapDecoder_GetColorContexts(decoder, 1, &colorcontext, &count);
+            ok(hr == WINCODEC_ERR_UNSUPPORTEDOPERATION, "expected WINCODEC_ERR_UNSUPPORTEDOPERATION, got %x\n", hr);
+
+            hr = IWICBitmapDecoder_GetThumbnail(decoder, &thumbnail);
+            ok(hr == WINCODEC_ERR_CODECNOTHUMBNAIL, "expected WINCODEC_ERR_CODECNOTHUMBNAIL, got %x\n", hr);
+
+            hr = IWICBitmapDecoder_GetPreview(decoder, &thumbnail);
+            ok(hr == WINCODEC_ERR_UNSUPPORTEDOPERATION, "expected WINCODEC_ERR_UNSUPPORTEDOPERATION, got %x\n", hr);
+
+            hr = IWICBitmapDecoder_GetFrameCount(decoder, &count);
+            ok(SUCCEEDED(hr), "GetFrameCount failed, hr=%x\n", hr);
+            ok(count == 1, "unexpected count %u\n", count);
+
+            hr = IWICBitmapDecoder_GetFrame(decoder, 1, &framedecode);
+            ok(hr == E_INVALIDARG || hr == WINCODEC_ERR_FRAMEMISSING, "GetFrame returned %x\n", hr);
+
+            hr = IWICBitmapDecoder_GetFrame(decoder, 0, &framedecode);
+            ok(SUCCEEDED(hr), "GetFrame failed, hr=%x\n", hr);
+            if (SUCCEEDED(hr))
+            {
+                IWICImagingFactory *factory;
+                IWICPalette *palette;
+
+                hr = IWICBitmapFrameDecode_GetSize(framedecode, &width, &height);
+                ok(SUCCEEDED(hr), "GetSize failed, hr=%x\n", hr);
+                ok(width == 2, "expected width=2, got %u\n", width);
+                ok(height == 3, "expected height=2, got %u\n", height);
+
+                hr = IWICBitmapFrameDecode_GetResolution(framedecode, &dpiX, &dpiY);
+                ok(SUCCEEDED(hr), "GetResolution failed, hr=%x\n", hr);
+                ok(dpiX == 96.0, "expected dpiX=96.0, got %f\n", dpiX);
+                ok(dpiY == 96.0, "expected dpiY=96.0, got %f\n", dpiY);
+
+                hr = IWICBitmapFrameDecode_GetPixelFormat(framedecode, &guidresult);
+                ok(SUCCEEDED(hr), "GetPixelFormat failed, hr=%x\n", hr);
+                ok(IsEqualGUID(&guidresult, &GUID_WICPixelFormat24bppBGR), "unexpected pixel format\n");
+
+                hr = IWICBitmapFrameDecode_GetMetadataQueryReader(framedecode, &queryreader);
+                ok(hr == WINCODEC_ERR_UNSUPPORTEDOPERATION, "expected WINCODEC_ERR_UNSUPPORTEDOPERATION, got %x\n", hr);
+
+                hr = IWICBitmapFrameDecode_GetColorContexts(framedecode, 1, &colorcontext, &count);
+                ok(hr == WINCODEC_ERR_UNSUPPORTEDOPERATION, "expected WINCODEC_ERR_UNSUPPORTEDOPERATION, got %x\n", hr);
+
+                hr = IWICBitmapFrameDecode_GetThumbnail(framedecode, &thumbnail);
+                ok(hr == WINCODEC_ERR_CODECNOTHUMBNAIL, "expected WINCODEC_ERR_CODECNOTHUMBNAIL, got %x\n", hr);
+
+                hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+                    &IID_IWICImagingFactory, (void**)&factory);
+                ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+                if (SUCCEEDED(hr))
+                {
+                    hr = IWICImagingFactory_CreatePalette(factory, &palette);
+                    ok(SUCCEEDED(hr), "CreatePalette failed, hr=%x\n", hr);
+                    if (SUCCEEDED(hr))
+                    {
+                        hr = IWICBitmapDecoder_CopyPalette(decoder, palette);
+                        ok(hr == WINCODEC_ERR_PALETTEUNAVAILABLE, "expected WINCODEC_ERR_PALETTEUNAVAILABLE, got %x\n", hr);
+
+                        hr = IWICBitmapFrameDecode_CopyPalette(framedecode, palette);
+                        ok(hr == WINCODEC_ERR_PALETTEUNAVAILABLE, "expected WINCODEC_ERR_PALETTEUNAVAILABLE, got %x\n", hr);
+
+                        IWICPalette_Release(palette);
+                    }
+
+                    IWICImagingFactory_Release(factory);
+                }
+
+                rc.X = 0;
+                rc.Y = 0;
+                rc.Width = 3;
+                rc.Height = 3;
+                hr = IWICBitmapFrameDecode_CopyPixels(framedecode, &rc, 6, sizeof(imagedata), imagedata);
+                ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %x\n", hr);
+
+                rc.X = -1;
+                rc.Y = 0;
+                rc.Width = 2;
+                rc.Height = 3;
+                hr = IWICBitmapFrameDecode_CopyPixels(framedecode, &rc, 6, sizeof(imagedata), imagedata);
+                ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %x\n", hr);
+
+                rc.X = 0;
+                rc.Y = 0;
+                rc.Width = 2;
+                rc.Height = 3;
+                hr = IWICBitmapFrameDecode_CopyPixels(framedecode, &rc, 4, sizeof(imagedata), imagedata);
+                ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %x\n", hr);
+
+                rc.X = 0;
+                rc.Y = 0;
+                rc.Width = 2;
+                rc.Height = 3;
+                hr = IWICBitmapFrameDecode_CopyPixels(framedecode, &rc, 4, 5, imagedata);
+                ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %x\n", hr);
+
+                rc.X = 0;
+                rc.Y = 0;
+                rc.Width = 2;
+                rc.Height = 3;
+                hr = IWICBitmapFrameDecode_CopyPixels(framedecode, &rc, 6, sizeof(imagedata), imagedata);
+                ok(SUCCEEDED(hr), "CopyPixels failed, hr=%x\n", hr);
+                ok(!memcmp(imagedata, expected_imagedata, sizeof(imagedata)), "unexpected image data\n");
+
+                hr = IWICBitmapFrameDecode_CopyPixels(framedecode, NULL, 6, sizeof(imagedata), imagedata);
+                ok(SUCCEEDED(hr), "CopyPixels(rect=NULL) failed, hr=%x\n", hr);
+                ok(!memcmp(imagedata, expected_imagedata, sizeof(imagedata)), "unexpected image data\n");
+
+                IWICBitmapFrameDecode_Release(framedecode);
+            }
+
+            /* cannot initialize twice */
+            hr = IWICBitmapDecoder_Initialize(decoder, bmpstream, WICDecodeMetadataCacheOnLoad);
+            ok(hr == WINCODEC_ERR_WRONGSTATE, "expected WINCODEC_ERR_WRONGSTATE, hr=%x\n", hr);
+
+            /* cannot querycapability after initialize */
+            hr = IWICBitmapDecoder_QueryCapability(decoder, bmpstream, &capability);
+            ok(hr == WINCODEC_ERR_WRONGSTATE, "expected WINCODEC_ERR_WRONGSTATE, hr=%x\n", hr);
+
+            hr = CoCreateInstance(&CLSID_WICBmpDecoder, NULL, CLSCTX_INPROC_SERVER,
+                &IID_IWICBitmapDecoder, (void**)&decoder2);
+            ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+            if (SUCCEEDED(hr))
+            {
+                hr = IWICBitmapDecoder_QueryCapability(decoder2, bmpstream, &capability);
+                ok(hr == S_OK, "QueryCapability failed, hr=%x\n", hr);
+                ok(capability == (WICBitmapDecoderCapabilityCanDecodeAllImages),
+                    "unexpected capabilities: %x\n", capability);
+
+                /* cannot initialize after querycapability */
+                hr = IWICBitmapDecoder_Initialize(decoder2, bmpstream, WICDecodeMetadataCacheOnLoad);
+                ok(hr == WINCODEC_ERR_WRONGSTATE, "expected WINCODEC_ERR_WRONGSTATE, hr=%x\n", hr);
+
+                /* cannot querycapability twice */
+                hr = IWICBitmapDecoder_QueryCapability(decoder2, bmpstream, &capability);
+                ok(hr == WINCODEC_ERR_WRONGSTATE, "expected WINCODEC_ERR_WRONGSTATE, hr=%x\n", hr);
+
+                IWICBitmapDecoder_Release(decoder2);
+            }
+
+            IStream_Release(bmpstream);
+        }
+
+        GlobalFree(hbmpdata);
+    }
+
+    IWICBitmapDecoder_Release(decoder);
+}
+
+static const char testbmp_1bpp[] = {
+    /* BITMAPFILEHEADER */
+    66,77, /* "BM" */
+    40,0,0,0, /* file size */
+    0,0,0,0, /* reserved */
+    32,0,0,0, /* offset to bits */
+    /* BITMAPCOREHEADER */
+    12,0,0,0, /* header size */
+    2,0, /* width */
+    2,0, /* height */
+    1,0, /* planes */
+    1,0, /* bit count */
+    /* color table */
+    255,0,0,
+    0,255,0,
+    /* bits */
+    0xc0,0,0,0,
+    0x80,0,0,0
+};
+
+static void test_decode_1bpp(void)
+{
+    IWICBitmapDecoder *decoder, *decoder2;
+    IWICBitmapFrameDecode *framedecode;
+    HRESULT hr;
+    HGLOBAL hbmpdata;
+    char *bmpdata;
+    IStream *bmpstream;
+    DWORD capability=0;
+    GUID guidresult;
+    UINT count=0, width=0, height=0;
+    double dpiX, dpiY;
+    BYTE imagedata[2] = {1};
+    const BYTE expected_imagedata[2] = {0x80,0xc0};
+    WICColor palettedata[2] = {1};
+    const WICColor expected_palettedata[2] = {0xff0000ff,0xff00ff00};
+    WICRect rc;
+
+    hr = CoCreateInstance(&CLSID_WICBmpDecoder, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICBitmapDecoder, (void**)&decoder);
+    ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+    if (FAILED(hr)) return;
+
+    hbmpdata = GlobalAlloc(GMEM_MOVEABLE, sizeof(testbmp_1bpp));
+    ok(hbmpdata != 0, "GlobalAlloc failed\n");
+    if (hbmpdata)
+    {
+        bmpdata = GlobalLock(hbmpdata);
+        memcpy(bmpdata, testbmp_1bpp, sizeof(testbmp_1bpp));
+        GlobalUnlock(hbmpdata);
+
+        hr = CreateStreamOnHGlobal(hbmpdata, FALSE, &bmpstream);
+        ok(SUCCEEDED(hr), "CreateStreamOnHGlobal failed, hr=%x\n", hr);
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICBitmapDecoder_Initialize(decoder, bmpstream, WICDecodeMetadataCacheOnLoad);
+            ok(hr == S_OK, "Initialize failed, hr=%x\n", hr);
+
+            hr = IWICBitmapDecoder_GetContainerFormat(decoder, &guidresult);
+            ok(SUCCEEDED(hr), "GetContainerFormat failed, hr=%x\n", hr);
+            ok(IsEqualGUID(&guidresult, &GUID_ContainerFormatBmp), "unexpected container format\n");
+
+            hr = IWICBitmapDecoder_GetFrameCount(decoder, &count);
+            ok(SUCCEEDED(hr), "GetFrameCount failed, hr=%x\n", hr);
+            ok(count == 1, "unexpected count %u\n", count);
+
+            hr = IWICBitmapDecoder_GetFrame(decoder, 0, &framedecode);
+            ok(SUCCEEDED(hr), "GetFrame failed, hr=%x\n", hr);
+            if (SUCCEEDED(hr))
+            {
+                IWICImagingFactory *factory;
+                IWICPalette *palette;
+
+                hr = IWICBitmapFrameDecode_GetSize(framedecode, &width, &height);
+                ok(SUCCEEDED(hr), "GetSize failed, hr=%x\n", hr);
+                ok(width == 2, "expected width=2, got %u\n", width);
+                ok(height == 2, "expected height=2, got %u\n", height);
+
+                hr = IWICBitmapFrameDecode_GetResolution(framedecode, &dpiX, &dpiY);
+                ok(SUCCEEDED(hr), "GetResolution failed, hr=%x\n", hr);
+                ok(dpiX == 96.0, "expected dpiX=96.0, got %f\n", dpiX);
+                ok(dpiY == 96.0, "expected dpiY=96.0, got %f\n", dpiY);
+
+                hr = IWICBitmapFrameDecode_GetPixelFormat(framedecode, &guidresult);
+                ok(SUCCEEDED(hr), "GetPixelFormat failed, hr=%x\n", hr);
+                ok(IsEqualGUID(&guidresult, &GUID_WICPixelFormat1bppIndexed), "unexpected pixel format\n");
+
+                hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+                    &IID_IWICImagingFactory, (void**)&factory);
+                ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+                if (SUCCEEDED(hr))
+                {
+                    hr = IWICImagingFactory_CreatePalette(factory, &palette);
+                    ok(SUCCEEDED(hr), "CreatePalette failed, hr=%x\n", hr);
+                    if (SUCCEEDED(hr))
+                    {
+                        hr = IWICBitmapDecoder_CopyPalette(decoder, palette);
+                        ok(hr == WINCODEC_ERR_PALETTEUNAVAILABLE, "expected WINCODEC_ERR_PALETTEUNAVAILABLE, got %x\n", hr);
+
+                        hr = IWICBitmapFrameDecode_CopyPalette(framedecode, palette);
+                        ok(SUCCEEDED(hr), "CopyPalette failed, hr=%x\n", hr);
+
+                        hr = IWICPalette_GetColorCount(palette, &count);
+                        ok(SUCCEEDED(hr), "GetColorCount failed, hr=%x\n", hr);
+                        ok(count == 2, "expected count=2, got %u\n", count);
+
+                        hr = IWICPalette_GetColors(palette, 2, palettedata, &count);
+                        ok(SUCCEEDED(hr), "GetColorCount failed, hr=%x\n", hr);
+                        ok(count == 2, "expected count=2, got %u\n", count);
+                        ok(!memcmp(palettedata, expected_palettedata, sizeof(palettedata)), "unexpected palette data\n");
+
+                        IWICPalette_Release(palette);
+                    }
+
+                    IWICImagingFactory_Release(factory);
+                }
+
+                rc.X = 0;
+                rc.Y = 0;
+                rc.Width = 2;
+                rc.Height = 2;
+                hr = IWICBitmapFrameDecode_CopyPixels(framedecode, &rc, 1, sizeof(imagedata), imagedata);
+                ok(SUCCEEDED(hr), "CopyPixels failed, hr=%x\n", hr);
+                ok(!memcmp(imagedata, expected_imagedata, sizeof(imagedata)), "unexpected image data\n");
+
+                IWICBitmapFrameDecode_Release(framedecode);
+            }
+
+            hr = CoCreateInstance(&CLSID_WICBmpDecoder, NULL, CLSCTX_INPROC_SERVER,
+                &IID_IWICBitmapDecoder, (void**)&decoder2);
+            ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+            if (SUCCEEDED(hr))
+            {
+                hr = IWICBitmapDecoder_QueryCapability(decoder2, bmpstream, &capability);
+                ok(hr == S_OK, "QueryCapability failed, hr=%x\n", hr);
+                ok(capability == (WICBitmapDecoderCapabilityCanDecodeAllImages),
+                    "unexpected capabilities: %x\n", capability);
+                IWICBitmapDecoder_Release(decoder2);
+            }
+
+            IStream_Release(bmpstream);
+        }
+
+        GlobalFree(hbmpdata);
+    }
+
+    IWICBitmapDecoder_Release(decoder);
+}
+
+static const char testbmp_4bpp[] = {
+    /* BITMAPFILEHEADER */
+    66,77, /* "BM" */
+    82,0,0,0, /* file size */
+    0,0,0,0, /* reserved */
+    74,0,0,0, /* offset to bits */
+    /* BITMAPINFOHEADER */
+    40,0,0,0, /* header size */
+    2,0,0,0, /* width */
+    254,255,255,255, /* height = -2 */
+    1,0, /* planes */
+    4,0, /* bit count */
+    0,0,0,0, /* compression = BI_RGB */
+    0,0,0,0, /* image size = 0 */
+    16,39,0,0, /* X pixels per meter = 10000 */
+    32,78,0,0, /* Y pixels per meter = 20000 */
+    5,0,0,0, /* colors used */
+    5,0,0,0, /* colors important */
+    /* color table */
+    255,0,0,0,
+    0,255,0,255,
+    0,0,255,23,
+    128,0,128,1,
+    255,255,255,0,
+    /* bits */
+    0x01,0,0,0,
+    0x23,0,0,0,
+};
+
+static void test_decode_4bpp(void)
+{
+    IWICBitmapDecoder *decoder, *decoder2;
+    IWICBitmapFrameDecode *framedecode;
+    HRESULT hr;
+    HGLOBAL hbmpdata;
+    char *bmpdata;
+    IStream *bmpstream;
+    DWORD capability=0;
+    GUID guidresult;
+    UINT count=0, width=0, height=0;
+    double dpiX, dpiY;
+    BYTE imagedata[2] = {1};
+    const BYTE expected_imagedata[2] = {0x01,0x23};
+    WICColor palettedata[5] = {1};
+    const WICColor expected_palettedata[5] =
+        {0xff0000ff,0xff00ff00,0xffff0000,0xff800080,0xffffffff};
+    WICRect rc;
+
+    hr = CoCreateInstance(&CLSID_WICBmpDecoder, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICBitmapDecoder, (void**)&decoder);
+    ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+    if (FAILED(hr)) return;
+
+    hbmpdata = GlobalAlloc(GMEM_MOVEABLE, sizeof(testbmp_4bpp));
+    ok(hbmpdata != 0, "GlobalAlloc failed\n");
+    if (hbmpdata)
+    {
+        bmpdata = GlobalLock(hbmpdata);
+        memcpy(bmpdata, testbmp_4bpp, sizeof(testbmp_4bpp));
+        GlobalUnlock(hbmpdata);
+
+        hr = CreateStreamOnHGlobal(hbmpdata, FALSE, &bmpstream);
+        ok(SUCCEEDED(hr), "CreateStreamOnHGlobal failed, hr=%x\n", hr);
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICBitmapDecoder_Initialize(decoder, bmpstream, WICDecodeMetadataCacheOnLoad);
+            ok(hr == S_OK, "Initialize failed, hr=%x\n", hr);
+
+            hr = IWICBitmapDecoder_GetContainerFormat(decoder, &guidresult);
+            ok(SUCCEEDED(hr), "GetContainerFormat failed, hr=%x\n", hr);
+            ok(IsEqualGUID(&guidresult, &GUID_ContainerFormatBmp), "unexpected container format\n");
+
+            hr = IWICBitmapDecoder_GetFrameCount(decoder, &count);
+            ok(SUCCEEDED(hr), "GetFrameCount failed, hr=%x\n", hr);
+            ok(count == 1, "unexpected count %u\n", count);
+
+            hr = IWICBitmapDecoder_GetFrame(decoder, 0, &framedecode);
+            ok(SUCCEEDED(hr), "GetFrame failed, hr=%x\n", hr);
+            if (SUCCEEDED(hr))
+            {
+                IWICImagingFactory *factory;
+                IWICPalette *palette;
+
+                hr = IWICBitmapFrameDecode_GetSize(framedecode, &width, &height);
+                ok(SUCCEEDED(hr), "GetSize failed, hr=%x\n", hr);
+                ok(width == 2, "expected width=2, got %u\n", width);
+                ok(height == 2, "expected height=2, got %u\n", height);
+
+                hr = IWICBitmapFrameDecode_GetResolution(framedecode, &dpiX, &dpiY);
+                ok(SUCCEEDED(hr), "GetResolution failed, hr=%x\n", hr);
+                ok(fabs(dpiX - 254.0) < 0.01, "expected dpiX=96.0, got %f\n", dpiX);
+                ok(fabs(dpiY - 508.0) < 0.01, "expected dpiY=96.0, got %f\n", dpiY);
+
+                hr = IWICBitmapFrameDecode_GetPixelFormat(framedecode, &guidresult);
+                ok(SUCCEEDED(hr), "GetPixelFormat failed, hr=%x\n", hr);
+                ok(IsEqualGUID(&guidresult, &GUID_WICPixelFormat4bppIndexed), "unexpected pixel format\n");
+
+                hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+                    &IID_IWICImagingFactory, (void**)&factory);
+                ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+                if (SUCCEEDED(hr))
+                {
+                    hr = IWICImagingFactory_CreatePalette(factory, &palette);
+                    ok(SUCCEEDED(hr), "CreatePalette failed, hr=%x\n", hr);
+                    if (SUCCEEDED(hr))
+                    {
+                        hr = IWICBitmapDecoder_CopyPalette(decoder, palette);
+                        ok(hr == WINCODEC_ERR_PALETTEUNAVAILABLE, "expected WINCODEC_ERR_PALETTEUNAVAILABLE, got %x\n", hr);
+
+                        hr = IWICBitmapFrameDecode_CopyPalette(framedecode, palette);
+                        ok(SUCCEEDED(hr), "CopyPalette failed, hr=%x\n", hr);
+
+                        hr = IWICPalette_GetColorCount(palette, &count);
+                        ok(SUCCEEDED(hr), "GetColorCount failed, hr=%x\n", hr);
+                        ok(count == 5, "expected count=5, got %u\n", count);
+
+                        hr = IWICPalette_GetColors(palette, 5, palettedata, &count);
+                        ok(SUCCEEDED(hr), "GetColorCount failed, hr=%x\n", hr);
+                        ok(count == 5, "expected count=5, got %u\n", count);
+                        ok(!memcmp(palettedata, expected_palettedata, sizeof(palettedata)), "unexpected palette data\n");
+
+                        IWICPalette_Release(palette);
+                    }
+
+                    IWICImagingFactory_Release(factory);
+                }
+
+                rc.X = 0;
+                rc.Y = 0;
+                rc.Width = 2;
+                rc.Height = 2;
+                hr = IWICBitmapFrameDecode_CopyPixels(framedecode, &rc, 1, sizeof(imagedata), imagedata);
+                ok(SUCCEEDED(hr), "CopyPixels failed, hr=%x\n", hr);
+                ok(!memcmp(imagedata, expected_imagedata, sizeof(imagedata)), "unexpected image data\n");
+
+                IWICBitmapFrameDecode_Release(framedecode);
+            }
+
+            hr = CoCreateInstance(&CLSID_WICBmpDecoder, NULL, CLSCTX_INPROC_SERVER,
+                &IID_IWICBitmapDecoder, (void**)&decoder2);
+            ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+            if (SUCCEEDED(hr))
+            {
+                hr = IWICBitmapDecoder_QueryCapability(decoder2, bmpstream, &capability);
+                ok(hr == S_OK, "QueryCapability failed, hr=%x\n", hr);
+                ok(capability == (WICBitmapDecoderCapabilityCanDecodeAllImages),
+                    "unexpected capabilities: %x\n", capability);
+                IWICBitmapDecoder_Release(decoder2);
+            }
+
+            IStream_Release(bmpstream);
+        }
+
+        GlobalFree(hbmpdata);
+    }
+
+    IWICBitmapDecoder_Release(decoder);
+}
+
+static const char testbmp_rle8[] = {
+    /* BITMAPFILEHEADER */
+    66,77, /* "BM" */
+    202,0,0,0, /* file size */
+    0,0,0,0, /* reserved */
+    122,0,0,0, /* offset to bits */
+    /* BITMAPINFOHEADER */
+    40,0,0,0, /* header size */
+    8,0,0,0, /* width */
+    8,0,0,0, /* height */
+    1,0, /* planes */
+    8,0, /* bit count */
+    1,0,0,0, /* compression = BI_RLE8 */
+    80,0,0,0, /* image size */
+    19,11,0,0, /* X pixels per meter */
+    19,11,0,0, /* Y pixels per meter */
+    17,0,0,0, /* colors used */
+    17,0,0,0, /* colors important */
+    /* color table */
+    0,0,0,0,
+    17,17,17,0,
+    255,0,0,0,
+    34,34,34,0,
+    0,0,204,0,
+    0,0,221,0,
+    0,0,238,0,
+    51,51,51,0,
+    0,0,255,0,
+    68,68,68,0,
+    255,0,255,0,
+    85,85,85,0,
+    0,204,0,0,
+    0,221,0,0,
+    0,238,0,0,
+    0,255,0,0,
+    255,255,255,0,
+    /* bits */
+    4,15,0,4,11,9,9,0,0,0,4,14,0,4,3,10,10,7,0,0,4,13,0,4,3,10,10,7,0,0,4,12,0,4,0,1,1,11,0,0,0,4,16,2,16,2,4,4,0,0,0,4,2,16,2,16,4,5,0,0,0,4,16,2,16,2,4,6,0,0,0,4,2,16,2,16,4,8,0,1
+};
+
+static void test_decode_rle8(void)
+{
+    IWICBitmapDecoder *decoder, *decoder2;
+    IWICBitmapFrameDecode *framedecode;
+    HRESULT hr;
+    HGLOBAL hbmpdata;
+    char *bmpdata;
+    IStream *bmpstream;
+    DWORD capability=0;
+    GUID guidresult;
+    UINT count=0, width=0, height=0;
+    double dpiX, dpiY;
+    DWORD imagedata[64] = {1};
+    const DWORD expected_imagedata[64] = {
+        0x0000ff,0xffffff,0x0000ff,0xffffff,0xff0000,0xff0000,0xff0000,0xff0000,
+        0xffffff,0x0000ff,0xffffff,0x0000ff,0xee0000,0xee0000,0xee0000,0xee0000,
+        0x0000ff,0xffffff,0x0000ff,0xffffff,0xdd0000,0xdd0000,0xdd0000,0xdd0000,
+        0xffffff,0x0000ff,0xffffff,0x0000ff,0xcc0000,0xcc0000,0xcc0000,0xcc0000,
+        0x00cc00,0x00cc00,0x00cc00,0x00cc00,0x000000,0x111111,0x111111,0x555555,
+        0x00dd00,0x00dd00,0x00dd00,0x00dd00,0x222222,0xff00ff,0xff00ff,0x333333,
+        0x00ee00,0x00ee00,0x00ee00,0x00ee00,0x222222,0xff00ff,0xff00ff,0x333333,
+        0x00ff00,0x00ff00,0x00ff00,0x00ff00,0x555555,0x444444,0x444444,0x000000};
+    WICColor palettedata[17] = {1};
+    const WICColor expected_palettedata[17] = {
+        0xff000000,0xff111111,0xff0000ff,0xff222222,0xffcc0000,0xffdd0000,
+        0xffee0000,0xff333333,0xffff0000,0xff444444,0xffff00ff,0xff555555,
+        0xff00cc00,0xff00dd00,0xff00ee00,0xff00ff00,0xffffffff};
+    WICRect rc;
+
+    hr = CoCreateInstance(&CLSID_WICBmpDecoder, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICBitmapDecoder, (void**)&decoder);
+    ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+    if (FAILED(hr)) return;
+
+    hbmpdata = GlobalAlloc(GMEM_MOVEABLE, sizeof(testbmp_rle8));
+    ok(hbmpdata != 0, "GlobalAlloc failed\n");
+    if (hbmpdata)
+    {
+        bmpdata = GlobalLock(hbmpdata);
+        memcpy(bmpdata, testbmp_rle8, sizeof(testbmp_rle8));
+        GlobalUnlock(hbmpdata);
+
+        hr = CreateStreamOnHGlobal(hbmpdata, FALSE, &bmpstream);
+        ok(SUCCEEDED(hr), "CreateStreamOnHGlobal failed, hr=%x\n", hr);
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICBitmapDecoder_Initialize(decoder, bmpstream, WICDecodeMetadataCacheOnLoad);
+            ok(hr == S_OK, "Initialize failed, hr=%x\n", hr);
+
+            hr = IWICBitmapDecoder_GetContainerFormat(decoder, &guidresult);
+            ok(SUCCEEDED(hr), "GetContainerFormat failed, hr=%x\n", hr);
+            ok(IsEqualGUID(&guidresult, &GUID_ContainerFormatBmp), "unexpected container format\n");
+
+            hr = IWICBitmapDecoder_GetFrameCount(decoder, &count);
+            ok(SUCCEEDED(hr), "GetFrameCount failed, hr=%x\n", hr);
+            ok(count == 1, "unexpected count %u\n", count);
+
+            hr = IWICBitmapDecoder_GetFrame(decoder, 0, &framedecode);
+            ok(SUCCEEDED(hr), "GetFrame failed, hr=%x\n", hr);
+            if (SUCCEEDED(hr))
+            {
+                IWICImagingFactory *factory;
+                IWICPalette *palette;
+
+                hr = IWICBitmapFrameDecode_GetSize(framedecode, &width, &height);
+                ok(SUCCEEDED(hr), "GetSize failed, hr=%x\n", hr);
+                ok(width == 8, "expected width=8, got %u\n", width);
+                ok(height == 8, "expected height=8, got %u\n", height);
+
+                hr = IWICBitmapFrameDecode_GetResolution(framedecode, &dpiX, &dpiY);
+                ok(SUCCEEDED(hr), "GetResolution failed, hr=%x\n", hr);
+                ok(fabs(dpiX - 72.0) < 0.01, "expected dpiX=96.0, got %f\n", dpiX);
+                ok(fabs(dpiY - 72.0) < 0.01, "expected dpiY=96.0, got %f\n", dpiY);
+
+                hr = IWICBitmapFrameDecode_GetPixelFormat(framedecode, &guidresult);
+                ok(SUCCEEDED(hr), "GetPixelFormat failed, hr=%x\n", hr);
+                ok(IsEqualGUID(&guidresult, &GUID_WICPixelFormat32bppBGR), "unexpected pixel format\n");
+
+                hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+                    &IID_IWICImagingFactory, (void**)&factory);
+                ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+                if (SUCCEEDED(hr))
+                {
+                    hr = IWICImagingFactory_CreatePalette(factory, &palette);
+                    ok(SUCCEEDED(hr), "CreatePalette failed, hr=%x\n", hr);
+                    if (SUCCEEDED(hr))
+                    {
+                        hr = IWICBitmapDecoder_CopyPalette(decoder, palette);
+                        ok(hr == WINCODEC_ERR_PALETTEUNAVAILABLE, "expected WINCODEC_ERR_PALETTEUNAVAILABLE, got %x\n", hr);
+
+                        hr = IWICBitmapFrameDecode_CopyPalette(framedecode, palette);
+                        ok(SUCCEEDED(hr), "CopyPalette failed, hr=%x\n", hr);
+
+                        hr = IWICPalette_GetColorCount(palette, &count);
+                        ok(SUCCEEDED(hr), "GetColorCount failed, hr=%x\n", hr);
+                        ok(count == 17, "expected count=17, got %u\n", count);
+
+                        hr = IWICPalette_GetColors(palette, 17, palettedata, &count);
+                        ok(SUCCEEDED(hr), "GetColorCount failed, hr=%x\n", hr);
+                        ok(count == 17, "expected count=17, got %u\n", count);
+                        ok(!memcmp(palettedata, expected_palettedata, sizeof(palettedata)), "unexpected palette data\n");
+
+                        IWICPalette_Release(palette);
+                    }
+
+                    IWICImagingFactory_Release(factory);
+                }
+
+                rc.X = 0;
+                rc.Y = 0;
+                rc.Width = 8;
+                rc.Height = 8;
+                hr = IWICBitmapFrameDecode_CopyPixels(framedecode, &rc, 32, sizeof(imagedata), (BYTE*)imagedata);
+                ok(SUCCEEDED(hr), "CopyPixels failed, hr=%x\n", hr);
+                ok(!memcmp(imagedata, expected_imagedata, sizeof(imagedata)), "unexpected image data\n");
+
+                IWICBitmapFrameDecode_Release(framedecode);
+            }
+
+            hr = CoCreateInstance(&CLSID_WICBmpDecoder, NULL, CLSCTX_INPROC_SERVER,
+                &IID_IWICBitmapDecoder, (void**)&decoder2);
+            ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+            if (SUCCEEDED(hr))
+            {
+                hr = IWICBitmapDecoder_QueryCapability(decoder2, bmpstream, &capability);
+                ok(hr == S_OK, "QueryCapability failed, hr=%x\n", hr);
+                ok(capability == (WICBitmapDecoderCapabilityCanDecodeAllImages),
+                    "unexpected capabilities: %x\n", capability);
+                IWICBitmapDecoder_Release(decoder2);
+            }
+
+            IStream_Release(bmpstream);
+        }
+
+        GlobalFree(hbmpdata);
+    }
+
+    IWICBitmapDecoder_Release(decoder);
+}
+
+static const char testbmp_rle4[] = {
+    /* BITMAPFILEHEADER */
+    66,77, /* "BM" */
+    142,0,0,0, /* file size */
+    0,0,0,0, /* reserved */
+    78,0,0,0, /* offset to bits */
+    /* BITMAPINFOHEADER */
+    40,0,0,0, /* header size */
+    8,0,0,0, /* width */
+    8,0,0,0, /* height */
+    1,0, /* planes */
+    4,0, /* bit count */
+    2,0,0,0, /* compression = BI_RLE4 */
+    64,0,0,0, /* image size */
+    19,11,0,0, /* X pixels per meter */
+    19,11,0,0, /* Y pixels per meter */
+    6,0,0,0, /* colors used */
+    6,0,0,0, /* colors important */
+    /* color table */
+    0,0,0,0,
+    255,0,0,0,
+    0,0,255,0,
+    255,0,255,0,
+    0,255,0,0,
+    255,255,255,0,
+    /* bits */
+    0,8,68,68,0,0,0,0,0,8,68,68,3,48,0,0,0,8,68,68,3,48,0,0,0,8,68,68,0,0,0,0,0,8,81,81,34,34,0,0,0,8,21,21,34,34,0,0,0,8,81,81,34,34,0,0,0,8,21,21,34,34,0,1
+};
+
+static void test_decode_rle4(void)
+{
+    IWICBitmapDecoder *decoder, *decoder2;
+    IWICBitmapFrameDecode *framedecode;
+    HRESULT hr;
+    HGLOBAL hbmpdata;
+    char *bmpdata;
+    IStream *bmpstream;
+    DWORD capability=0;
+    GUID guidresult;
+    UINT count=0, width=0, height=0;
+    double dpiX, dpiY;
+    DWORD imagedata[64] = {1};
+    const DWORD expected_imagedata[64] = {
+        0x0000ff,0xffffff,0x0000ff,0xffffff,0xff0000,0xff0000,0xff0000,0xff0000,
+        0xffffff,0x0000ff,0xffffff,0x0000ff,0xff0000,0xff0000,0xff0000,0xff0000,
+        0x0000ff,0xffffff,0x0000ff,0xffffff,0xff0000,0xff0000,0xff0000,0xff0000,
+        0xffffff,0x0000ff,0xffffff,0x0000ff,0xff0000,0xff0000,0xff0000,0xff0000,
+        0x00ff00,0x00ff00,0x00ff00,0x00ff00,0x000000,0x000000,0x000000,0x000000,
+        0x00ff00,0x00ff00,0x00ff00,0x00ff00,0x000000,0xff00ff,0xff00ff,0x000000,
+        0x00ff00,0x00ff00,0x00ff00,0x00ff00,0x000000,0xff00ff,0xff00ff,0x000000,
+        0x00ff00,0x00ff00,0x00ff00,0x00ff00,0x000000,0x000000,0x000000,0x000000};
+    WICColor palettedata[6] = {1};
+    const WICColor expected_palettedata[6] = {
+        0xff000000,0xff0000ff,0xffff0000,0xffff00ff,0xff00ff00,0xffffffff};
+    WICRect rc;
+
+    hr = CoCreateInstance(&CLSID_WICBmpDecoder, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICBitmapDecoder, (void**)&decoder);
+    ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+    if (FAILED(hr)) return;
+
+    hbmpdata = GlobalAlloc(GMEM_MOVEABLE, sizeof(testbmp_rle4));
+    ok(hbmpdata != 0, "GlobalAlloc failed\n");
+    if (hbmpdata)
+    {
+        bmpdata = GlobalLock(hbmpdata);
+        memcpy(bmpdata, testbmp_rle4, sizeof(testbmp_rle4));
+        GlobalUnlock(hbmpdata);
+
+        hr = CreateStreamOnHGlobal(hbmpdata, FALSE, &bmpstream);
+        ok(SUCCEEDED(hr), "CreateStreamOnHGlobal failed, hr=%x\n", hr);
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICBitmapDecoder_Initialize(decoder, bmpstream, WICDecodeMetadataCacheOnLoad);
+            ok(hr == S_OK, "Initialize failed, hr=%x\n", hr);
+
+            hr = IWICBitmapDecoder_GetContainerFormat(decoder, &guidresult);
+            ok(SUCCEEDED(hr), "GetContainerFormat failed, hr=%x\n", hr);
+            ok(IsEqualGUID(&guidresult, &GUID_ContainerFormatBmp), "unexpected container format\n");
+
+            hr = IWICBitmapDecoder_GetFrameCount(decoder, &count);
+            ok(SUCCEEDED(hr), "GetFrameCount failed, hr=%x\n", hr);
+            ok(count == 1, "unexpected count %u\n", count);
+
+            hr = IWICBitmapDecoder_GetFrame(decoder, 0, &framedecode);
+            ok(SUCCEEDED(hr), "GetFrame failed, hr=%x\n", hr);
+            if (SUCCEEDED(hr))
+            {
+                IWICImagingFactory *factory;
+                IWICPalette *palette;
+
+                hr = IWICBitmapFrameDecode_GetSize(framedecode, &width, &height);
+                ok(SUCCEEDED(hr), "GetSize failed, hr=%x\n", hr);
+                ok(width == 8, "expected width=8, got %u\n", width);
+                ok(height == 8, "expected height=8, got %u\n", height);
+
+                hr = IWICBitmapFrameDecode_GetResolution(framedecode, &dpiX, &dpiY);
+                ok(SUCCEEDED(hr), "GetResolution failed, hr=%x\n", hr);
+                ok(fabs(dpiX - 72.0) < 0.01, "expected dpiX=96.0, got %f\n", dpiX);
+                ok(fabs(dpiY - 72.0) < 0.01, "expected dpiY=96.0, got %f\n", dpiY);
+
+                hr = IWICBitmapFrameDecode_GetPixelFormat(framedecode, &guidresult);
+                ok(SUCCEEDED(hr), "GetPixelFormat failed, hr=%x\n", hr);
+                ok(IsEqualGUID(&guidresult, &GUID_WICPixelFormat32bppBGR), "unexpected pixel format\n");
+
+                hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+                    &IID_IWICImagingFactory, (void**)&factory);
+                ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+                if (SUCCEEDED(hr))
+                {
+                    hr = IWICImagingFactory_CreatePalette(factory, &palette);
+                    ok(SUCCEEDED(hr), "CreatePalette failed, hr=%x\n", hr);
+                    if (SUCCEEDED(hr))
+                    {
+                        hr = IWICBitmapDecoder_CopyPalette(decoder, palette);
+                        ok(hr == WINCODEC_ERR_PALETTEUNAVAILABLE, "expected WINCODEC_ERR_PALETTEUNAVAILABLE, got %x\n", hr);
+
+                        hr = IWICBitmapFrameDecode_CopyPalette(framedecode, palette);
+                        ok(SUCCEEDED(hr), "CopyPalette failed, hr=%x\n", hr);
+
+                        hr = IWICPalette_GetColorCount(palette, &count);
+                        ok(SUCCEEDED(hr), "GetColorCount failed, hr=%x\n", hr);
+                        ok(count == 6, "expected count=6, got %u\n", count);
+
+                        hr = IWICPalette_GetColors(palette, 6, palettedata, &count);
+                        ok(SUCCEEDED(hr), "GetColorCount failed, hr=%x\n", hr);
+                        ok(count == 6, "expected count=6, got %u\n", count);
+                        ok(!memcmp(palettedata, expected_palettedata, sizeof(palettedata)), "unexpected palette data\n");
+
+                        IWICPalette_Release(palette);
+                    }
+
+                    IWICImagingFactory_Release(factory);
+                }
+
+                rc.X = 0;
+                rc.Y = 0;
+                rc.Width = 8;
+                rc.Height = 8;
+                hr = IWICBitmapFrameDecode_CopyPixels(framedecode, &rc, 32, sizeof(imagedata), (BYTE*)imagedata);
+                ok(SUCCEEDED(hr), "CopyPixels failed, hr=%x\n", hr);
+                ok(!memcmp(imagedata, expected_imagedata, sizeof(imagedata)), "unexpected image data\n");
+
+                IWICBitmapFrameDecode_Release(framedecode);
+            }
+
+            hr = CoCreateInstance(&CLSID_WICBmpDecoder, NULL, CLSCTX_INPROC_SERVER,
+                &IID_IWICBitmapDecoder, (void**)&decoder2);
+            ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+            if (SUCCEEDED(hr))
+            {
+                hr = IWICBitmapDecoder_QueryCapability(decoder2, bmpstream, &capability);
+                ok(hr == S_OK, "QueryCapability failed, hr=%x\n", hr);
+                ok(capability == (WICBitmapDecoderCapabilityCanDecodeAllImages),
+                    "unexpected capabilities: %x\n", capability);
+                IWICBitmapDecoder_Release(decoder2);
+            }
+
+            IStream_Release(bmpstream);
+        }
+
+        GlobalFree(hbmpdata);
+    }
+
+    IWICBitmapDecoder_Release(decoder);
+}
+
+static void test_componentinfo(void)
+{
+    IWICImagingFactory *factory;
+    IWICComponentInfo *info;
+    IWICBitmapDecoderInfo *decoderinfo;
+    IWICBitmapDecoder *decoder;
+    HRESULT hr;
+    WICBitmapPattern *patterns;
+    UINT pattern_count, pattern_size;
+    WICComponentType type;
+    GUID guidresult;
+    HGLOBAL hbmpdata;
+    char *bmpdata;
+    IStream *bmpstream;
+    BOOL boolresult;
+
+    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICImagingFactory, (void**)&factory);
+    ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICImagingFactory_CreateComponentInfo(factory, &CLSID_WICBmpDecoder, &info);
+        ok(SUCCEEDED(hr), "CreateComponentInfo failed, hr=%x\n", hr);
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICComponentInfo_GetComponentType(info, &type);
+            ok(SUCCEEDED(hr), "GetComponentType failed, hr=%x\n", hr);
+            ok(type == WICDecoder, "got %i, expected WICDecoder\n", type);
+
+            hr = IWICComponentInfo_QueryInterface(info, &IID_IWICBitmapDecoderInfo, (void**)&decoderinfo);
+            ok(SUCCEEDED(hr), "QueryInterface failed, hr=%x\n", hr);
+            if (SUCCEEDED(hr))
+            {
+                pattern_count = 0;
+                pattern_size = 0;
+                hr = IWICBitmapDecoderInfo_GetPatterns(decoderinfo, 0, NULL, &pattern_count, &pattern_size);
+                ok(SUCCEEDED(hr), "GetPatterns failed, hr=%x\n", hr);
+                ok(pattern_count != 0, "pattern count is 0\n");
+                ok(pattern_size > pattern_count * sizeof(WICBitmapPattern), "size=%i, count=%i\n", pattern_size, pattern_count);
+
+                patterns = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pattern_size);
+                hr = IWICBitmapDecoderInfo_GetPatterns(decoderinfo, pattern_size, patterns, &pattern_count, &pattern_size);
+                ok(SUCCEEDED(hr), "GetPatterns failed, hr=%x\n", hr);
+                ok(pattern_count != 0, "pattern count is 0\n");
+                ok(pattern_size > pattern_count * sizeof(WICBitmapPattern), "size=%i, count=%i\n", pattern_size, pattern_count);
+                ok(patterns[0].Length != 0, "pattern length is 0\n");
+                ok(patterns[0].Pattern != NULL, "pattern is NULL\n");
+                ok(patterns[0].Mask != NULL, "mask is NULL\n");
+
+                pattern_size -= 1;
+                hr = IWICBitmapDecoderInfo_GetPatterns(decoderinfo, pattern_size, patterns, &pattern_count, &pattern_size);
+                ok(hr == WINCODEC_ERR_INSUFFICIENTBUFFER, "GetPatterns returned %x, expected WINCODEC_ERR_INSUFFICIENTBUFFER\n", hr);
+
+                HeapFree(GetProcessHeap(), 0, patterns);
+
+                hr = IWICBitmapDecoderInfo_CreateInstance(decoderinfo, &decoder);
+                ok(SUCCEEDED(hr), "CreateInstance failed, hr=%x\n", hr);
+                if (SUCCEEDED(hr))
+                {
+                    hr = IWICBitmapDecoder_GetContainerFormat(decoder, &guidresult);
+                    ok(SUCCEEDED(hr), "GetContainerFormat failed, hr=%x\n", hr);
+                    ok(IsEqualGUID(&guidresult, &GUID_ContainerFormatBmp), "unexpected container format\n");
+
+                    IWICBitmapDecoder_Release(decoder);
+                }
+
+                hbmpdata = GlobalAlloc(GMEM_MOVEABLE, sizeof(testbmp_rle4));
+                ok(hbmpdata != 0, "GlobalAlloc failed\n");
+                if (hbmpdata)
+                {
+                    bmpdata = GlobalLock(hbmpdata);
+                    memcpy(bmpdata, testbmp_rle4, sizeof(testbmp_rle4));
+                    GlobalUnlock(hbmpdata);
+
+                    hr = CreateStreamOnHGlobal(hbmpdata, FALSE, &bmpstream);
+                    ok(SUCCEEDED(hr), "CreateStreamOnHGlobal failed, hr=%x\n", hr);
+                    if (SUCCEEDED(hr))
+                    {
+                        boolresult = 0;
+                        hr = IWICBitmapDecoderInfo_MatchesPattern(decoderinfo, bmpstream, &boolresult);
+                        ok(SUCCEEDED(hr), "MatchesPattern failed, hr=%x\n", hr);
+                        ok(boolresult, "pattern not matched\n");
+
+                        IStream_Release(bmpstream);
+                    }
+
+                    GlobalFree(hbmpdata);
+                }
+
+                IWICBitmapDecoderInfo_Release(decoderinfo);
+            }
+
+            IWICComponentInfo_Release(info);
+        }
+
+        IWICImagingFactory_Release(factory);
+    }
+}
+
+static void test_createfromstream(void)
+{
+    IWICBitmapDecoder *decoder;
+    IWICImagingFactory *factory;
+    HRESULT hr;
+    HGLOBAL hbmpdata;
+    char *bmpdata;
+    IStream *bmpstream;
+    GUID guidresult;
+
+    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICImagingFactory, (void**)&factory);
+    ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+    if (FAILED(hr)) return;
+
+    hbmpdata = GlobalAlloc(GMEM_MOVEABLE, sizeof(testbmp_1bpp));
+    ok(hbmpdata != 0, "GlobalAlloc failed\n");
+    if (hbmpdata)
+    {
+        bmpdata = GlobalLock(hbmpdata);
+        memcpy(bmpdata, testbmp_1bpp, sizeof(testbmp_1bpp));
+        GlobalUnlock(hbmpdata);
+
+        hr = CreateStreamOnHGlobal(hbmpdata, FALSE, &bmpstream);
+        ok(SUCCEEDED(hr), "CreateStreamOnHGlobal failed, hr=%x\n", hr);
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICImagingFactory_CreateDecoderFromStream(factory, bmpstream,
+                NULL, WICDecodeMetadataCacheOnDemand, &decoder);
+            ok(SUCCEEDED(hr), "CreateDecoderFromStream failed, hr=%x\n", hr);
+            if (SUCCEEDED(hr))
+            {
+                hr = IWICBitmapDecoder_GetContainerFormat(decoder, &guidresult);
+                ok(SUCCEEDED(hr), "GetContainerFormat failed, hr=%x\n", hr);
+                ok(IsEqualGUID(&guidresult, &GUID_ContainerFormatBmp), "unexpected container format\n");
+
+                IWICBitmapDecoder_Release(decoder);
+            }
+
+            IStream_Release(bmpstream);
+        }
+
+        GlobalFree(hbmpdata);
+    }
+
+    IWICImagingFactory_Release(factory);
+}
+
+/* 1x1 pixel gif, missing trailer */
+static unsigned char gifimage_notrailer[] = {
+0x47,0x49,0x46,0x38,0x37,0x61,0x01,0x00,0x01,0x00,0x80,0x00,0x71,0xff,0xff,0xff,
+0xff,0xff,0xff,0x2c,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x02,0x02,0x44,
+0x01,0x00
+};
+
+static void test_gif_notrailer(void)
+{
+    IWICBitmapDecoder *decoder;
+    IWICImagingFactory *factory;
+    HRESULT hr;
+    IWICStream *gifstream;
+    IWICBitmapFrameDecode *framedecode;
+    double dpiX = 0.0, dpiY = 0.0;
+    UINT framecount;
+
+    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICImagingFactory, (void**)&factory);
+    ok(hr == S_OK, "CoCreateInstance failed, hr=%x\n", hr);
+    if (FAILED(hr)) return;
+
+    hr = IWICImagingFactory_CreateStream(factory, &gifstream);
+    ok(hr == S_OK, "CreateStream failed, hr=%x\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICStream_InitializeFromMemory(gifstream, gifimage_notrailer,
+            sizeof(gifimage_notrailer));
+        ok(hr == S_OK, "InitializeFromMemory failed, hr=%x\n", hr);
+
+        if (SUCCEEDED(hr))
+        {
+            hr = CoCreateInstance(&CLSID_WICGifDecoder, NULL, CLSCTX_INPROC_SERVER,
+                &IID_IWICBitmapDecoder, (void**)&decoder);
+            ok(hr == S_OK, "CoCreateInstance failed, hr=%x\n", hr);
+        }
+
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICBitmapDecoder_Initialize(decoder, (IStream*)gifstream,
+                WICDecodeMetadataCacheOnDemand);
+            ok(hr == S_OK, "Initialize failed, hr=%x\n", hr);
+
+            if (SUCCEEDED(hr))
+            {
+                hr = IWICBitmapDecoder_GetFrame(decoder, 0, &framedecode);
+                ok(hr == S_OK, "GetFrame failed, hr=%x\n", hr);
+                if (SUCCEEDED(hr))
+                {
+                    hr = IWICBitmapFrameDecode_GetResolution(framedecode, &dpiX, &dpiY);
+                    ok(SUCCEEDED(hr), "GetResolution failed, hr=%x\n", hr);
+                    ok(dpiX == 48.0, "expected dpiX=48.0, got %f\n", dpiX);
+                    ok(dpiY == 96.0, "expected dpiY=96.0, got %f\n", dpiY);
+
+                    IWICBitmapFrameDecode_Release(framedecode);
+                }
+            }
+
+            if (SUCCEEDED(hr))
+            {
+                hr = IWICBitmapDecoder_GetFrameCount(decoder, &framecount);
+                ok(hr == S_OK, "GetFrameCount failed, hr=%x\n", hr);
+                ok(framecount == 1, "framecount=%u\n", framecount);
+            }
+
+            IWICBitmapDecoder_Release(decoder);
+        }
+
+        IWICStream_Release(gifstream);
+    }
+
+    IWICImagingFactory_Release(factory);
+}
+
+static void test_create_decoder(void)
+{
+    IWICBitmapDecoder *decoder;
+    IWICImagingFactory *factory;
+    HRESULT hr;
+
+    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICImagingFactory, (void **)&factory);
+    ok(hr == S_OK, "CoCreateInstance error %#x\n", hr);
+
+    hr = IWICImagingFactory_CreateDecoder(factory, NULL, NULL, NULL);
+    ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+
+    hr = IWICImagingFactory_CreateDecoder(factory, NULL, NULL, &decoder);
+    ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+
+    hr = IWICImagingFactory_CreateDecoder(factory, &GUID_ContainerFormatBmp, NULL, &decoder);
+    ok(hr == S_OK, "CreateDecoder error %#x\n", hr);
+    IWICBitmapDecoder_Release(decoder);
+
+    hr = IWICImagingFactory_CreateDecoder(factory, &GUID_ContainerFormatBmp, &GUID_VendorMicrosoft, &decoder);
+    ok(hr == S_OK, "CreateDecoder error %#x\n", hr);
+    IWICBitmapDecoder_Release(decoder);
+
+    IWICImagingFactory_Release(factory);
+}
+
+START_TEST(bmpformat)
+{
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+
+    test_decode_24bpp();
+    test_decode_1bpp();
+    test_decode_4bpp();
+    test_decode_rle8();
+    test_decode_rle4();
+    test_componentinfo();
+    test_createfromstream();
+    test_gif_notrailer();
+    test_create_decoder();
+
+    CoUninitialize();
+}
diff --git a/rostests/winetests/windowscodecs/converter.c b/rostests/winetests/windowscodecs/converter.c
new file mode 100644 (file)
index 0000000..de44709
--- /dev/null
@@ -0,0 +1,518 @@
+/*
+ * Copyright 2009 Vincent 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 <stdarg.h>
+#include <math.h>
+
+#define COBJMACROS
+#define CONST_VTABLE
+
+#include "windef.h"
+#include "objbase.h"
+#include "wincodec.h"
+#include "wine/test.h"
+
+typedef struct bitmap_data {
+    const WICPixelFormatGUID *format;
+    UINT bpp;
+    const BYTE *bits;
+    UINT width;
+    UINT height;
+    double xres;
+    double yres;
+} bitmap_data;
+
+typedef struct BitmapTestSrc {
+    IWICBitmapSource IWICBitmapSource_iface;
+    LONG ref;
+    const bitmap_data *data;
+} BitmapTestSrc;
+
+static inline BitmapTestSrc *impl_from_IWICBitmapSource(IWICBitmapSource *iface)
+{
+    return CONTAINING_RECORD(iface, BitmapTestSrc, IWICBitmapSource_iface);
+}
+
+static HRESULT WINAPI BitmapTestSrc_QueryInterface(IWICBitmapSource *iface, REFIID iid,
+    void **ppv)
+{
+    if (!ppv) return E_INVALIDARG;
+
+    if (IsEqualIID(&IID_IUnknown, iid) ||
+        IsEqualIID(&IID_IWICBitmapSource, iid))
+        *ppv = iface;
+    else
+        return E_NOINTERFACE;
+
+    IUnknown_AddRef((IUnknown*)*ppv);
+    return S_OK;
+}
+
+static ULONG WINAPI BitmapTestSrc_AddRef(IWICBitmapSource *iface)
+{
+    BitmapTestSrc *This = impl_from_IWICBitmapSource(iface);
+    ULONG ref = InterlockedIncrement(&This->ref);
+    return ref;
+}
+
+static ULONG WINAPI BitmapTestSrc_Release(IWICBitmapSource *iface)
+{
+    BitmapTestSrc *This = impl_from_IWICBitmapSource(iface);
+    ULONG ref = InterlockedDecrement(&This->ref);
+    return ref;
+}
+
+static HRESULT WINAPI BitmapTestSrc_GetSize(IWICBitmapSource *iface,
+    UINT *puiWidth, UINT *puiHeight)
+{
+    BitmapTestSrc *This = impl_from_IWICBitmapSource(iface);
+    *puiWidth = This->data->width;
+    *puiHeight = This->data->height;
+    return S_OK;
+}
+
+static HRESULT WINAPI BitmapTestSrc_GetPixelFormat(IWICBitmapSource *iface,
+    WICPixelFormatGUID *pPixelFormat)
+{
+    BitmapTestSrc *This = impl_from_IWICBitmapSource(iface);
+    memcpy(pPixelFormat, This->data->format, sizeof(GUID));
+    return S_OK;
+}
+
+static HRESULT WINAPI BitmapTestSrc_GetResolution(IWICBitmapSource *iface,
+    double *pDpiX, double *pDpiY)
+{
+    BitmapTestSrc *This = impl_from_IWICBitmapSource(iface);
+    *pDpiX = This->data->xres;
+    *pDpiY = This->data->yres;
+    return S_OK;
+}
+
+static HRESULT WINAPI BitmapTestSrc_CopyPalette(IWICBitmapSource *iface,
+    IWICPalette *pIPalette)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI BitmapTestSrc_CopyPixels(IWICBitmapSource *iface,
+    const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
+{
+    BitmapTestSrc *This = impl_from_IWICBitmapSource(iface);
+    UINT bytesperrow;
+    UINT srcstride;
+    UINT row_offset;
+    WICRect rc;
+
+    if (!prc)
+    {
+        rc.X = 0;
+        rc.Y = 0;
+        rc.Width = This->data->width;
+        rc.Height = This->data->height;
+        prc = &rc;
+    }
+    else
+    {
+        if (prc->X < 0 || prc->Y < 0 || prc->X+prc->Width > This->data->width || prc->Y+prc->Height > This->data->height)
+            return E_INVALIDARG;
+    }
+
+    bytesperrow = ((This->data->bpp * prc->Width)+7)/8;
+    srcstride = ((This->data->bpp * This->data->width)+7)/8;
+
+    if (cbStride < bytesperrow)
+        return E_INVALIDARG;
+
+    if ((cbStride * prc->Height) > cbBufferSize)
+        return E_INVALIDARG;
+
+    row_offset = prc->X * This->data->bpp;
+
+    if (row_offset % 8 == 0)
+    {
+        UINT row;
+        const BYTE *src;
+        BYTE *dst;
+
+        src = This->data->bits + (row_offset / 8) + prc->Y * srcstride;
+        dst = pbBuffer;
+        for (row=0; row < prc->Height; row++)
+        {
+            memcpy(dst, src, bytesperrow);
+            src += srcstride;
+            dst += cbStride;
+        }
+        return S_OK;
+    }
+    else
+    {
+        ok(0, "bitmap %p was asked to copy pixels not aligned on a byte boundary\n", iface);
+        return E_FAIL;
+    }
+}
+
+static const IWICBitmapSourceVtbl BitmapTestSrc_Vtbl = {
+    BitmapTestSrc_QueryInterface,
+    BitmapTestSrc_AddRef,
+    BitmapTestSrc_Release,
+    BitmapTestSrc_GetSize,
+    BitmapTestSrc_GetPixelFormat,
+    BitmapTestSrc_GetResolution,
+    BitmapTestSrc_CopyPalette,
+    BitmapTestSrc_CopyPixels
+};
+
+static void CreateTestBitmap(const bitmap_data *data, BitmapTestSrc **This)
+{
+    *This = HeapAlloc(GetProcessHeap(), 0, sizeof(**This));
+
+    if (*This)
+    {
+        (*This)->IWICBitmapSource_iface.lpVtbl = &BitmapTestSrc_Vtbl;
+        (*This)->ref = 1;
+        (*This)->data = data;
+    }
+}
+
+static void DeleteTestBitmap(BitmapTestSrc *This)
+{
+    ok(This->IWICBitmapSource_iface.lpVtbl == &BitmapTestSrc_Vtbl, "test bitmap %p deleted with incorrect vtable\n", This);
+    ok(This->ref == 1, "test bitmap %p deleted with %i references instead of 1\n", This, This->ref);
+    HeapFree(GetProcessHeap(), 0, This);
+}
+
+static void compare_bitmap_data(const struct bitmap_data *expect, IWICBitmapSource *source, const char *name)
+{
+    BYTE *converted_bits;
+    UINT width, height;
+    double xres, yres;
+    WICRect prc;
+    UINT stride, buffersize;
+    GUID dst_pixelformat;
+    HRESULT hr;
+
+    hr = IWICBitmapSource_GetSize(source, &width, &height);
+    ok(SUCCEEDED(hr), "GetSize(%s) failed, hr=%x\n", name, hr);
+    ok(width == expect->width, "expecting %u, got %u (%s)\n", expect->width, width, name);
+    ok(height == expect->height, "expecting %u, got %u (%s)\n", expect->height, height, name);
+
+    hr = IWICBitmapSource_GetResolution(source, &xres, &yres);
+    ok(SUCCEEDED(hr), "GetResolution(%s) failed, hr=%x\n", name, hr);
+    ok(fabs(xres - expect->xres) < 0.02, "expecting %0.2f, got %0.2f (%s)\n", expect->xres, xres, name);
+    ok(fabs(yres - expect->yres) < 0.02, "expecting %0.2f, got %0.2f (%s)\n", expect->yres, yres, name);
+
+    hr = IWICBitmapSource_GetPixelFormat(source, &dst_pixelformat);
+    ok(SUCCEEDED(hr), "GetPixelFormat(%s) failed, hr=%x\n", name, hr);
+    ok(IsEqualGUID(&dst_pixelformat, expect->format), "got unexpected pixel format (%s)\n", name);
+
+    prc.X = 0;
+    prc.Y = 0;
+    prc.Width = expect->width;
+    prc.Height = expect->height;
+
+    stride = (expect->bpp * expect->width + 7) / 8;
+    buffersize = stride * expect->height;
+
+    converted_bits = HeapAlloc(GetProcessHeap(), 0, buffersize);
+    hr = IWICBitmapSource_CopyPixels(source, &prc, stride, buffersize, converted_bits);
+    ok(SUCCEEDED(hr), "CopyPixels(%s) failed, hr=%x\n", name, hr);
+    if (IsEqualGUID(expect->format, &GUID_WICPixelFormat32bppBGR))
+    {
+        /* ignore the padding byte when comparing data */
+        UINT i;
+        BOOL equal=TRUE;
+        const DWORD *a=(const DWORD*)expect->bits, *b=(const DWORD*)converted_bits;
+        for (i=0; i<(buffersize/4); i++)
+            if ((a[i]&0xffffff) != (b[i]&0xffffff))
+            {
+                equal = FALSE;
+                break;
+            }
+        ok(equal, "unexpected pixel data (%s)\n", name);
+    }
+    else
+        ok(memcmp(expect->bits, converted_bits, buffersize) == 0, "unexpected pixel data (%s)\n", name);
+
+    /* Test with NULL rectangle - should copy the whole bitmap */
+    hr = IWICBitmapSource_CopyPixels(source, NULL, stride, buffersize, converted_bits);
+    ok(SUCCEEDED(hr), "CopyPixels(%s,rc=NULL) failed, hr=%x\n", name, hr);
+    if (IsEqualGUID(expect->format, &GUID_WICPixelFormat32bppBGR))
+    {
+        /* ignore the padding byte when comparing data */
+        UINT i;
+        BOOL equal=TRUE;
+        const DWORD *a=(const DWORD*)expect->bits, *b=(const DWORD*)converted_bits;
+        for (i=0; i<(buffersize/4); i++)
+            if ((a[i]&0xffffff) != (b[i]&0xffffff))
+            {
+                equal = FALSE;
+                break;
+            }
+        ok(equal, "unexpected pixel data with rc=NULL (%s)\n", name);
+    }
+    else
+        ok(memcmp(expect->bits, converted_bits, buffersize) == 0, "unexpected pixel data with rc=NULL (%s)\n", name);
+
+    HeapFree(GetProcessHeap(), 0, converted_bits);
+}
+
+static const BYTE bits_24bppBGR[] = {
+    255,0,0, 0,255,0, 0,0,255, 0,0,0,
+    0,255,255, 255,0,255, 255,255,0, 255,255,255};
+static const struct bitmap_data testdata_24bppBGR = {
+    &GUID_WICPixelFormat24bppBGR, 24, bits_24bppBGR, 4, 2, 96.0, 96.0};
+
+static const BYTE bits_32bppBGR[] = {
+    255,0,0,80, 0,255,0,80, 0,0,255,80, 0,0,0,80,
+    0,255,255,80, 255,0,255,80, 255,255,0,80, 255,255,255,80};
+static const struct bitmap_data testdata_32bppBGR = {
+    &GUID_WICPixelFormat32bppBGR, 32, bits_32bppBGR, 4, 2, 96.0, 96.0};
+
+static const BYTE bits_32bppBGRA[] = {
+    255,0,0,255, 0,255,0,255, 0,0,255,255, 0,0,0,255,
+    0,255,255,255, 255,0,255,255, 255,255,0,255, 255,255,255,255};
+static const struct bitmap_data testdata_32bppBGRA = {
+    &GUID_WICPixelFormat32bppBGRA, 32, bits_32bppBGRA, 4, 2, 96.0, 96.0};
+
+static void test_conversion(const struct bitmap_data *src, const struct bitmap_data *dst, const char *name, BOOL todo)
+{
+    BitmapTestSrc *src_obj;
+    IWICBitmapSource *dst_bitmap;
+    HRESULT hr;
+
+    CreateTestBitmap(src, &src_obj);
+
+    hr = WICConvertBitmapSource(dst->format, &src_obj->IWICBitmapSource_iface, &dst_bitmap);
+    if (todo)
+        todo_wine ok(SUCCEEDED(hr), "WICConvertBitmapSource(%s) failed, hr=%x\n", name, hr);
+    else
+        ok(SUCCEEDED(hr), "WICConvertBitmapSource(%s) failed, hr=%x\n", name, hr);
+
+    if (SUCCEEDED(hr))
+    {
+        compare_bitmap_data(dst, dst_bitmap, name);
+
+        IWICBitmapSource_Release(dst_bitmap);
+    }
+
+    DeleteTestBitmap(src_obj);
+}
+
+static void test_invalid_conversion(void)
+{
+    BitmapTestSrc *src_obj;
+    IWICBitmapSource *dst_bitmap;
+    HRESULT hr;
+
+    CreateTestBitmap(&testdata_32bppBGRA, &src_obj);
+
+    /* convert to a non-pixel-format GUID */
+    hr = WICConvertBitmapSource(&GUID_VendorMicrosoft, &src_obj->IWICBitmapSource_iface, &dst_bitmap);
+    ok(hr == WINCODEC_ERR_COMPONENTNOTFOUND, "WICConvertBitmapSource returned %x\n", hr);
+
+    DeleteTestBitmap(src_obj);
+}
+
+static void test_default_converter(void)
+{
+    BitmapTestSrc *src_obj;
+    IWICFormatConverter *converter;
+    BOOL can_convert=1;
+    HRESULT hr;
+
+    CreateTestBitmap(&testdata_32bppBGRA, &src_obj);
+
+    hr = CoCreateInstance(&CLSID_WICDefaultFormatConverter, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICFormatConverter, (void**)&converter);
+    ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICFormatConverter_CanConvert(converter, &GUID_WICPixelFormat32bppBGRA,
+            &GUID_WICPixelFormat32bppBGR, &can_convert);
+        ok(SUCCEEDED(hr), "CanConvert returned %x\n", hr);
+        ok(can_convert, "expected TRUE, got %i\n", can_convert);
+
+        hr = IWICFormatConverter_Initialize(converter, &src_obj->IWICBitmapSource_iface,
+            &GUID_WICPixelFormat32bppBGR, WICBitmapDitherTypeNone, NULL, 0.0,
+            WICBitmapPaletteTypeCustom);
+        ok(SUCCEEDED(hr), "Initialize returned %x\n", hr);
+
+        if (SUCCEEDED(hr))
+            compare_bitmap_data(&testdata_32bppBGR, (IWICBitmapSource*)converter, "default converter");
+
+        IWICFormatConverter_Release(converter);
+    }
+
+    DeleteTestBitmap(src_obj);
+}
+
+static void test_multi_encoder(const struct bitmap_data **srcs, const CLSID* clsid_encoder,
+    const struct bitmap_data **dsts, const CLSID *clsid_decoder, const char *name)
+{
+    HRESULT hr;
+    IWICBitmapEncoder *encoder;
+    BitmapTestSrc *src_obj;
+    HGLOBAL hglobal;
+    IStream *stream;
+    IWICBitmapFrameEncode *frameencode;
+    IPropertyBag2 *options=NULL;
+    IWICBitmapDecoder *decoder;
+    IWICBitmapFrameDecode *framedecode;
+    WICPixelFormatGUID pixelformat;
+    int i;
+
+    hr = CoCreateInstance(clsid_encoder, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICBitmapEncoder, (void**)&encoder);
+    ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        hglobal = GlobalAlloc(GMEM_MOVEABLE, 0);
+        ok(hglobal != NULL, "GlobalAlloc failed\n");
+        if (hglobal)
+        {
+            hr = CreateStreamOnHGlobal(hglobal, TRUE, &stream);
+            ok(SUCCEEDED(hr), "CreateStreamOnHGlobal failed, hr=%x\n", hr);
+        }
+
+        if (hglobal && SUCCEEDED(hr))
+        {
+            hr = IWICBitmapEncoder_Initialize(encoder, stream, WICBitmapEncoderNoCache);
+            ok(SUCCEEDED(hr), "Initialize failed, hr=%x\n", hr);
+
+            i=0;
+            while (SUCCEEDED(hr) && srcs[i])
+            {
+                CreateTestBitmap(srcs[i], &src_obj);
+
+                hr = IWICBitmapEncoder_CreateNewFrame(encoder, &frameencode, &options);
+                ok(SUCCEEDED(hr), "CreateFrame failed, hr=%x\n", hr);
+                if (SUCCEEDED(hr))
+                {
+                    hr = IWICBitmapFrameEncode_Initialize(frameencode, options);
+                    ok(SUCCEEDED(hr), "Initialize failed, hr=%x\n", hr);
+
+                    memcpy(&pixelformat, srcs[i]->format, sizeof(GUID));
+                    hr = IWICBitmapFrameEncode_SetPixelFormat(frameencode, &pixelformat);
+                    ok(SUCCEEDED(hr), "SetPixelFormat failed, hr=%x\n", hr);
+                    ok(IsEqualGUID(&pixelformat, srcs[i]->format), "SetPixelFormat changed the format\n");
+
+                    hr = IWICBitmapFrameEncode_SetSize(frameencode, srcs[i]->width, srcs[i]->height);
+                    ok(SUCCEEDED(hr), "SetSize failed, hr=%x\n", hr);
+
+                    hr = IWICBitmapFrameEncode_WriteSource(frameencode, &src_obj->IWICBitmapSource_iface, NULL);
+                    ok(SUCCEEDED(hr), "WriteSource failed, hr=%x\n", hr);
+
+                    hr = IWICBitmapFrameEncode_Commit(frameencode);
+                    ok(SUCCEEDED(hr), "Commit failed, hr=%x\n", hr);
+
+                    IWICBitmapFrameEncode_Release(frameencode);
+                    IPropertyBag2_Release(options);
+                }
+
+                DeleteTestBitmap(src_obj);
+
+                i++;
+            }
+
+            if (SUCCEEDED(hr))
+            {
+                hr = IWICBitmapEncoder_Commit(encoder);
+                ok(SUCCEEDED(hr), "Commit failed, hr=%x\n", hr);
+            }
+
+            if (SUCCEEDED(hr))
+            {
+                hr = CoCreateInstance(clsid_decoder, NULL, CLSCTX_INPROC_SERVER,
+                    &IID_IWICBitmapDecoder, (void**)&decoder);
+                ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+            }
+
+            if (SUCCEEDED(hr))
+            {
+                hr = IWICBitmapDecoder_Initialize(decoder, stream, WICDecodeMetadataCacheOnDemand);
+                ok(SUCCEEDED(hr), "Initialize failed, hr=%x\n", hr);
+
+                i=0;
+                while (SUCCEEDED(hr) && dsts[i])
+                {
+                    hr = IWICBitmapDecoder_GetFrame(decoder, i, &framedecode);
+                    ok(SUCCEEDED(hr), "GetFrame failed, hr=%x\n", hr);
+
+                    if (SUCCEEDED(hr))
+                    {
+                        compare_bitmap_data(dsts[i], (IWICBitmapSource*)framedecode, name);
+
+                        IWICBitmapFrameDecode_Release(framedecode);
+                    }
+
+                    i++;
+                }
+
+                IWICBitmapDecoder_Release(decoder);
+            }
+
+            IStream_Release(stream);
+        }
+
+        IWICBitmapEncoder_Release(encoder);
+    }
+}
+
+static void test_encoder(const struct bitmap_data *src, const CLSID* clsid_encoder,
+    const struct bitmap_data *dst, const CLSID *clsid_decoder, const char *name)
+{
+    const struct bitmap_data *srcs[2];
+    const struct bitmap_data *dsts[2];
+
+    srcs[0] = src;
+    srcs[1] = NULL;
+    dsts[0] = dst;
+    dsts[1] = NULL;
+
+    test_multi_encoder(srcs, clsid_encoder, dsts, clsid_decoder, name);
+}
+
+static const struct bitmap_data *multiple_frames[3] = {
+    &testdata_24bppBGR,
+    &testdata_24bppBGR,
+    NULL};
+
+START_TEST(converter)
+{
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+
+    test_conversion(&testdata_32bppBGRA, &testdata_32bppBGR, "BGRA -> BGR", 0);
+    test_conversion(&testdata_32bppBGR, &testdata_32bppBGRA, "BGR -> BGRA", 0);
+    test_conversion(&testdata_32bppBGRA, &testdata_32bppBGRA, "BGRA -> BGRA", 0);
+    test_invalid_conversion();
+    test_default_converter();
+
+    test_encoder(&testdata_32bppBGR, &CLSID_WICBmpEncoder,
+                 &testdata_32bppBGR, &CLSID_WICBmpDecoder, "BMP encoder 32bppBGR");
+
+    test_encoder(&testdata_24bppBGR, &CLSID_WICPngEncoder,
+                 &testdata_24bppBGR, &CLSID_WICPngDecoder, "PNG encoder 24bppBGR");
+
+    test_encoder(&testdata_24bppBGR, &CLSID_WICTiffEncoder,
+                 &testdata_24bppBGR, &CLSID_WICTiffDecoder, "TIFF encoder 24bppBGR");
+
+    test_multi_encoder(multiple_frames, &CLSID_WICTiffEncoder,
+                       multiple_frames, &CLSID_WICTiffDecoder, "TIFF encoder multi-frame");
+
+    CoUninitialize();
+}
diff --git a/rostests/winetests/windowscodecs/gifformat.c b/rostests/winetests/windowscodecs/gifformat.c
new file mode 100644 (file)
index 0000000..9ff1d90
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2012 Dmitry Timoshkov
+ *
+ * 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 <stdio.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "wincodec.h"
+#include "wine/test.h"
+
+static const char gif_global_palette[] = {
+/* LSD */'G','I','F','8','7','a',0x01,0x00,0x01,0x00,0xa1,0x02,0x00,
+/* palette */0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,
+/* GCE */0x21,0xf9,0x04,0x01,0x05,0x00,0x01,0x00, /* index 1 */
+/* IMD */0x2c,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,
+0x02,0x02,0x44,0x01,0x00,0x3b
+};
+
+/* frame 0, GCE transparent index 1
+ * frame 1, GCE transparent index 2
+ */
+static const char gif_global_palette_2frames[] = {
+/* LSD */'G','I','F','8','9','a',0x01,0x00,0x01,0x00,0xa1,0x02,0x00,
+/* palette */0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,
+/* GCE */0x21,0xf9,0x04,0x01,0x05,0x00,0x01,0x00, /* index 1 */
+/* IMD */0x2c,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,
+0x02,0x02,0x44,0x01,0x00,
+/* GCE */0x21,0xf9,0x04,0x01,0x05,0x00,0x02,0x00, /* index 2 */
+/* IMD */0x2c,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,
+0x02,0x02,0x44,0x01,0x00,0x3b
+};
+
+static const char gif_local_palette[] = {
+/* LSD */'G','I','F','8','7','a',0x01,0x00,0x01,0x00,0x27,0x02,0x00,
+/* GCE */0x21,0xf9,0x04,0x01,0x05,0x00,0x01,0x00, /* index 1 */
+/* IMD */0x2c,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x81,
+/* palette */0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,
+0x02,0x02,0x44,0x01,0x00,0x3b
+};
+
+static IWICImagingFactory *factory;
+
+static const char *debugstr_guid(const GUID *guid)
+{
+    static char buf[50];
+
+    if (!guid) return "(null)";
+    sprintf(buf, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
+            guid->Data1, guid->Data2, guid->Data3, guid->Data4[0],
+            guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4],
+            guid->Data4[5], guid->Data4[6], guid->Data4[7]);
+    return buf;
+}
+
+static IWICBitmapDecoder *create_decoder(const void *image_data, UINT image_size)
+{
+    HGLOBAL hmem;
+    BYTE *data;
+    HRESULT hr;
+    IWICBitmapDecoder *decoder = NULL;
+    IStream *stream;
+    GUID format;
+
+    hmem = GlobalAlloc(0, image_size);
+    data = GlobalLock(hmem);
+    memcpy(data, image_data, image_size);
+    GlobalUnlock(hmem);
+
+    hr = CreateStreamOnHGlobal(hmem, TRUE, &stream);
+    ok(hr == S_OK, "CreateStreamOnHGlobal error %#x\n", hr);
+
+    hr = IWICImagingFactory_CreateDecoderFromStream(factory, stream, NULL, 0, &decoder);
+    ok(hr == S_OK, "CreateDecoderFromStream error %#x\n", hr);
+
+    hr = IWICBitmapDecoder_GetContainerFormat(decoder, &format);
+    ok(hr == S_OK, "GetContainerFormat error %#x\n", hr);
+    ok(IsEqualGUID(&format, &GUID_ContainerFormatGif),
+       "wrong container format %s\n", debugstr_guid(&format));
+
+    IStream_Release(stream);
+
+    return decoder;
+}
+
+static void test_global_gif_palette(void)
+{
+    HRESULT hr;
+    IWICBitmapDecoder *decoder;
+    IWICBitmapFrameDecode *frame;
+    IWICPalette *palette;
+    GUID format;
+    UINT count, ret;
+    WICColor color[256];
+
+    decoder = create_decoder(gif_global_palette, sizeof(gif_global_palette));
+    ok(decoder != 0, "Failed to load GIF image data\n");
+
+    hr = IWICImagingFactory_CreatePalette(factory, &palette);
+    ok(hr == S_OK, "CreatePalette error %#x\n", hr);
+
+    /* global palette */
+    hr = IWICBitmapDecoder_CopyPalette(decoder, palette);
+    ok(hr == S_OK, "CopyPalette error %#x\n", hr);
+
+    hr = IWICPalette_GetColorCount(palette, &count);
+    ok(hr == S_OK, "GetColorCount error %#x\n", hr);
+    ok(count == 4, "expected 4, got %u\n", count);
+
+    hr = IWICPalette_GetColors(palette, count, color, &ret);
+    ok(hr == S_OK, "GetColors error %#x\n", hr);
+    ok(ret == count, "expected %u, got %u\n", count, ret);
+    ok(color[0] == 0xff010203, "expected 0xff010203, got %#x\n", color[0]);
+    ok(color[1] == 0x00040506, "expected 0x00040506, got %#x\n", color[1]);
+    ok(color[2] == 0xff070809, "expected 0xff070809, got %#x\n", color[2]);
+    ok(color[3] == 0xff0a0b0c, "expected 0xff0a0b0c, got %#x\n", color[3]);
+
+    /* frame palette */
+    hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
+    ok(hr == S_OK, "GetFrame error %#x\n", hr);
+
+    hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &format);
+    ok(hr == S_OK, "GetPixelFormat error %#x\n", hr);
+    ok(IsEqualGUID(&format, &GUID_WICPixelFormat8bppIndexed),
+       "wrong pixel format %s\n", debugstr_guid(&format));
+
+    hr = IWICBitmapFrameDecode_CopyPalette(frame, palette);
+    ok(hr == S_OK, "CopyPalette error %#x\n", hr);
+
+    hr = IWICPalette_GetColorCount(palette, &count);
+    ok(hr == S_OK, "GetColorCount error %#x\n", hr);
+    ok(count == 4, "expected 4, got %u\n", count);
+
+    hr = IWICPalette_GetColors(palette, count, color, &ret);
+    ok(hr == S_OK, "GetColors error %#x\n", hr);
+    ok(ret == count, "expected %u, got %u\n", count, ret);
+    ok(color[0] == 0xff010203, "expected 0xff010203, got %#x\n", color[0]);
+    ok(color[1] == 0x00040506, "expected 0x00040506, got %#x\n", color[1]);
+    ok(color[2] == 0xff070809, "expected 0xff070809, got %#x\n", color[2]);
+    ok(color[3] == 0xff0a0b0c, "expected 0xff0a0b0c, got %#x\n", color[3]);
+
+    IWICPalette_Release(palette);
+    IWICBitmapFrameDecode_Release(frame);
+    IWICBitmapDecoder_Release(decoder);
+}
+
+static void test_global_gif_palette_2frames(void)
+{
+    HRESULT hr;
+    IWICBitmapDecoder *decoder;
+    IWICBitmapFrameDecode *frame;
+    IWICPalette *palette;
+    GUID format;
+    UINT count, ret;
+    WICColor color[256];
+
+    decoder = create_decoder(gif_global_palette_2frames, sizeof(gif_global_palette_2frames));
+    ok(decoder != 0, "Failed to load GIF image data\n");
+
+    /* active frame 0, GCE transparent index 1 */
+    hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
+    ok(hr == S_OK, "GetFrame error %#x\n", hr);
+
+    hr = IWICImagingFactory_CreatePalette(factory, &palette);
+    ok(hr == S_OK, "CreatePalette error %#x\n", hr);
+
+    /* global palette */
+    hr = IWICBitmapDecoder_CopyPalette(decoder, palette);
+    ok(hr == S_OK, "CopyPalette error %#x\n", hr);
+
+    hr = IWICPalette_GetColorCount(palette, &count);
+    ok(hr == S_OK, "GetColorCount error %#x\n", hr);
+    ok(count == 4, "expected 4, got %u\n", count);
+
+    hr = IWICPalette_GetColors(palette, count, color, &ret);
+    ok(hr == S_OK, "GetColors error %#x\n", hr);
+    ok(ret == count, "expected %u, got %u\n", count, ret);
+    ok(color[0] == 0xff010203, "expected 0xff010203, got %#x\n", color[0]);
+    ok(color[1] == 0x00040506, "expected 0x00040506, got %#x\n", color[1]);
+    ok(color[2] == 0xff070809, "expected 0xff070809, got %#x\n", color[2]);
+    ok(color[3] == 0xff0a0b0c, "expected 0xff0a0b0c, got %#x\n", color[3]);
+
+    /* frame 0 palette */
+    hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &format);
+    ok(hr == S_OK, "GetPixelFormat error %#x\n", hr);
+    ok(IsEqualGUID(&format, &GUID_WICPixelFormat8bppIndexed),
+       "wrong pixel format %s\n", debugstr_guid(&format));
+
+    hr = IWICBitmapFrameDecode_CopyPalette(frame, palette);
+    ok(hr == S_OK, "CopyPalette error %#x\n", hr);
+
+    hr = IWICPalette_GetColorCount(palette, &count);
+    ok(hr == S_OK, "GetColorCount error %#x\n", hr);
+    ok(count == 4, "expected 4, got %u\n", count);
+
+    hr = IWICPalette_GetColors(palette, count, color, &ret);
+    ok(hr == S_OK, "GetColors error %#x\n", hr);
+    ok(ret == count, "expected %u, got %u\n", count, ret);
+    ok(color[0] == 0xff010203, "expected 0xff010203, got %#x\n", color[0]);
+    ok(color[1] == 0x00040506, "expected 0x00040506, got %#x\n", color[1]);
+    ok(color[2] == 0xff070809, "expected 0xff070809, got %#x\n", color[2]);
+    ok(color[3] == 0xff0a0b0c, "expected 0xff0a0b0c, got %#x\n", color[3]);
+
+    IWICBitmapFrameDecode_Release(frame);
+
+    /* active frame 1, GCE transparent index 2 */
+    hr = IWICBitmapDecoder_GetFrame(decoder, 1, &frame);
+    ok(hr == S_OK, "GetFrame error %#x\n", hr);
+
+    /* global palette */
+    hr = IWICBitmapDecoder_CopyPalette(decoder, palette);
+    ok(hr == S_OK, "CopyPalette error %#x\n", hr);
+
+    hr = IWICPalette_GetColorCount(palette, &count);
+    ok(hr == S_OK, "GetColorCount error %#x\n", hr);
+    ok(count == 4, "expected 4, got %u\n", count);
+
+    hr = IWICPalette_GetColors(palette, count, color, &ret);
+    ok(hr == S_OK, "GetColors error %#x\n", hr);
+    ok(ret == count, "expected %u, got %u\n", count, ret);
+    ok(color[0] == 0xff010203, "expected 0xff010203, got %#x\n", color[0]);
+    ok(color[1] == 0xff040506 || broken(color[1] == 0x00040506) /* XP */, "expected 0xff040506, got %#x\n", color[1]);
+    ok(color[2] == 0x00070809 || broken(color[2] == 0xff070809) /* XP */, "expected 0x00070809, got %#x\n", color[2]);
+    ok(color[3] == 0xff0a0b0c, "expected 0xff0a0b0c, got %#x\n", color[3]);
+
+    /* frame 1 palette */
+    hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &format);
+    ok(hr == S_OK, "GetPixelFormat error %#x\n", hr);
+    ok(IsEqualGUID(&format, &GUID_WICPixelFormat8bppIndexed),
+       "wrong pixel format %s\n", debugstr_guid(&format));
+
+    hr = IWICBitmapFrameDecode_CopyPalette(frame, palette);
+    ok(hr == S_OK, "CopyPalette error %#x\n", hr);
+
+    hr = IWICPalette_GetColorCount(palette, &count);
+    ok(hr == S_OK, "GetColorCount error %#x\n", hr);
+    ok(count == 4, "expected 4, got %u\n", count);
+
+    hr = IWICPalette_GetColors(palette, count, color, &ret);
+    ok(hr == S_OK, "GetColors error %#x\n", hr);
+    ok(ret == count, "expected %u, got %u\n", count, ret);
+    ok(color[0] == 0xff010203, "expected 0xff010203, got %#x\n", color[0]);
+    ok(color[1] == 0xff040506, "expected 0xff040506, got %#x\n", color[1]);
+    ok(color[2] == 0x00070809, "expected 0x00070809, got %#x\n", color[2]);
+    ok(color[3] == 0xff0a0b0c, "expected 0xff0a0b0c, got %#x\n", color[3]);
+
+    IWICPalette_Release(palette);
+    IWICBitmapFrameDecode_Release(frame);
+    IWICBitmapDecoder_Release(decoder);
+}
+
+static void test_local_gif_palette(void)
+{
+    HRESULT hr;
+    IWICBitmapDecoder *decoder;
+    IWICBitmapFrameDecode *frame;
+    IWICPalette *palette;
+    GUID format;
+    UINT count, ret;
+    WICColor color[256];
+
+    decoder = create_decoder(gif_local_palette, sizeof(gif_local_palette));
+    ok(decoder != 0, "Failed to load GIF image data\n");
+
+    hr = IWICImagingFactory_CreatePalette(factory, &palette);
+    ok(hr == S_OK, "CreatePalette error %#x\n", hr);
+
+    /* global palette */
+    hr = IWICBitmapDecoder_CopyPalette(decoder, palette);
+    ok(hr == WINCODEC_ERR_FRAMEMISSING,
+       "expected WINCODEC_ERR_FRAMEMISSING, got %#x\n", hr);
+
+    /* frame palette */
+    hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
+    ok(hr == S_OK, "GetFrame error %#x\n", hr);
+
+    hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &format);
+    ok(hr == S_OK, "GetPixelFormat error %#x\n", hr);
+    ok(IsEqualGUID(&format, &GUID_WICPixelFormat8bppIndexed),
+       "wrong pixel format %s\n", debugstr_guid(&format));
+
+    hr = IWICBitmapFrameDecode_CopyPalette(frame, palette);
+    ok(hr == S_OK, "CopyPalette error %#x\n", hr);
+
+    hr = IWICPalette_GetColorCount(palette, &count);
+    ok(hr == S_OK, "GetColorCount error %#x\n", hr);
+    ok(count == 4, "expected 4, got %u\n", count);
+
+    hr = IWICPalette_GetColors(palette, count, color, &ret);
+    ok(hr == S_OK, "GetColors error %#x\n", hr);
+    ok(ret == count, "expected %u, got %u\n", count, ret);
+    ok(color[0] == 0xff010203, "expected 0xff010203, got %#x\n", color[0]);
+    ok(color[1] == 0x00040506, "expected 0x00040506, got %#x\n", color[1]);
+    ok(color[2] == 0xff070809, "expected 0xff070809, got %#x\n", color[2]);
+    ok(color[3] == 0xff0a0b0c, "expected 0xff0a0b0c, got %#x\n", color[3]);
+
+    IWICPalette_Release(palette);
+    IWICBitmapFrameDecode_Release(frame);
+    IWICBitmapDecoder_Release(decoder);
+}
+
+START_TEST(gifformat)
+{
+    HRESULT hr;
+
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_IWICImagingFactory, (void **)&factory);
+    ok(hr == S_OK, "CoCreateInstance error %#x\n", hr);
+    if (FAILED(hr)) return;
+
+    test_global_gif_palette();
+    test_global_gif_palette_2frames();
+    test_local_gif_palette();
+
+    IWICImagingFactory_Release(factory);
+    CoUninitialize();
+}
diff --git a/rostests/winetests/windowscodecs/icoformat.c b/rostests/winetests/windowscodecs/icoformat.c
new file mode 100644 (file)
index 0000000..e9c0a48
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2010 Damjan Jovanovic
+ *
+ * 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 <math.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "objbase.h"
+#include "wincodec.h"
+#include "wine/test.h"
+
+static unsigned char testico_bad_icondirentry_size[] = {
+    /* ICONDIR */
+    0, 0, /* reserved */
+    1, 0, /* type */
+    1, 0, /* count */
+    /* ICONDIRENTRY */
+    2, /* width */
+    2, /* height */
+    2, /* colorCount */
+    0, /* reserved */
+    1,0, /* planes */
+    8,0, /* bitCount */
+    (40+2*4+16*16+16*4) & 0xFF,((40+2*4+16*16+16*4) >> 8) & 0xFF,0,0, /* bytesInRes */
+    22,0,0,0, /* imageOffset */
+    /* BITMAPINFOHEADER */
+    40,0,0,0, /* header size */
+    16,0,0,0, /* width */
+    2*16,0,0,0, /* height (XOR+AND rows) */
+    1,0, /* planes */
+    8,0, /* bit count */
+    0,0,0,0, /* compression */
+    0,0,0,0, /* sizeImage */
+    0,0,0,0, /* x pels per meter */
+    0,0,0,0, /* y pels per meter */
+    2,0,0,0, /* clrUsed */
+    0,0,0,0, /* clrImportant */
+    /* palette */
+    0,0,0,0,
+    0xFF,0xFF,0xFF,0,
+    /* XOR mask */
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,
+    0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,
+    0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,
+    0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,
+    0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,
+    0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,
+    0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,
+    0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,
+    0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,
+    0,0,0,0,1,0,1,0,1,0,1,0,0,0,0,0,
+    0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    /* AND mask */
+    0,0,0,0,
+    0,0,0,0,
+    0,0,0,0,
+    0,0,0,0,
+    0,0,0,0,
+    0,0,0,0,
+    0,0,0,0,
+    0,0,0,0,
+    0,0,0,0,
+    0,0,0,0,
+    0,0,0,0,
+    0,0,0,0,
+    0,0,0,0,
+    0,0,0,0,
+    0,0,0,0,
+    0,0,0,0
+};
+
+static void test_bad_icondirentry_size(void)
+{
+    IWICBitmapDecoder *decoder;
+    IWICImagingFactory *factory;
+    HRESULT hr;
+    IWICStream *icostream;
+    IWICBitmapFrameDecode *framedecode = NULL;
+
+    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICImagingFactory, (void**)&factory);
+    ok(hr == S_OK, "CoCreateInstance failed, hr=%x\n", hr);
+    if (FAILED(hr)) return;
+
+    hr = IWICImagingFactory_CreateStream(factory, &icostream);
+    ok(hr == S_OK, "CreateStream failed, hr=%x\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICStream_InitializeFromMemory(icostream, testico_bad_icondirentry_size,
+            sizeof(testico_bad_icondirentry_size));
+        ok(hr == S_OK, "InitializeFromMemory failed, hr=%x\n", hr);
+
+        if (SUCCEEDED(hr))
+        {
+            hr = CoCreateInstance(&CLSID_WICIcoDecoder, NULL, CLSCTX_INPROC_SERVER,
+                &IID_IWICBitmapDecoder, (void**)&decoder);
+            ok(hr == S_OK, "CoCreateInstance failed, hr=%x\n", hr);
+        }
+
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICBitmapDecoder_Initialize(decoder, (IStream*)icostream,
+                WICDecodeMetadataCacheOnDemand);
+            ok(hr == S_OK, "Initialize failed, hr=%x\n", hr);
+
+            if (SUCCEEDED(hr))
+            {
+                hr = IWICBitmapDecoder_GetFrame(decoder, 0, &framedecode);
+                ok(hr == S_OK, "GetFrame failed, hr=%x\n", hr);
+            }
+
+            if (SUCCEEDED(hr))
+            {
+                UINT width = 0, height = 0;
+                IWICBitmapSource *thumbnail = NULL;
+
+                hr = IWICBitmapFrameDecode_GetSize(framedecode, &width, &height);
+                ok(hr == S_OK, "GetFrameSize failed, hr=%x\n", hr);
+                ok(width == 16 && height == 16, "framesize=%ux%u\n", width, height);
+
+                hr = IWICBitmapFrameDecode_GetThumbnail(framedecode, &thumbnail);
+                todo_wine ok(hr == S_OK, "GetThumbnail failed, hr=%x\n", hr);
+
+                if (thumbnail) IWICBitmapSource_Release(thumbnail);
+                IWICBitmapFrameDecode_Release(framedecode);
+            }
+
+            IWICBitmapDecoder_Release(decoder);
+        }
+
+        IWICStream_Release(icostream);
+    }
+
+    IWICImagingFactory_Release(factory);
+}
+
+START_TEST(icoformat)
+{
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+
+    test_bad_icondirentry_size();
+
+    CoUninitialize();
+}
diff --git a/rostests/winetests/windowscodecs/info.c b/rostests/winetests/windowscodecs/info.c
new file mode 100644 (file)
index 0000000..d9159b1
--- /dev/null
@@ -0,0 +1,549 @@
+/*
+ * 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 <stdio.h>
+#include <stdarg.h>
+#include <math.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "objbase.h"
+#include "wincodec.h"
+#include "wincodecsdk.h"
+#include "wine/test.h"
+
+#include "initguid.h"
+DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
+
+static const char *debugstr_guid(GUID *guid)
+{
+    static char buf[50];
+
+    if(!guid)
+        return "(null)";
+
+    sprintf(buf, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
+            guid->Data1, guid->Data2, guid->Data3, guid->Data4[0],
+            guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4],
+            guid->Data4[5], guid->Data4[6], guid->Data4[7]);
+
+    return buf;
+}
+
+static HRESULT get_component_info(const GUID *clsid, IWICComponentInfo **result)
+{
+    IWICImagingFactory *factory;
+    HRESULT hr;
+
+    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICImagingFactory, (void**)&factory);
+    ok(hr == S_OK, "CoCreateInstance failed, hr=%x\n", hr);
+    if (FAILED(hr)) return hr;
+
+    hr = IWICImagingFactory_CreateComponentInfo(factory, clsid, result);
+
+    IWICImagingFactory_Release(factory);
+
+    return hr;
+}
+
+static int is_pixelformat(GUID *format)
+{
+    IWICComponentInfo *info;
+    HRESULT hr;
+    WICComponentType componenttype;
+
+    hr = get_component_info(format, &info);
+    if (FAILED(hr))
+        return FALSE;
+
+    hr = IWICComponentInfo_GetComponentType(info, &componenttype);
+
+    IWICComponentInfo_Release(info);
+
+    return SUCCEEDED(hr) && componenttype == WICPixelFormat;
+}
+
+static void test_decoder_info(void)
+{
+    IWICComponentInfo *info;
+    IWICBitmapDecoderInfo *decoder_info;
+    HRESULT hr;
+    ULONG len;
+    WCHAR value[256];
+    const WCHAR expected_mimetype[] = {'i','m','a','g','e','/','b','m','p',0};
+    const WCHAR expected_extensions[] = {'.','b','m','p',',','.','d','i','b',',','.','r','l','e',0};
+    CLSID clsid;
+    GUID pixelformats[20];
+    UINT num_formats, count;
+    int i;
+
+    hr = get_component_info(&CLSID_WICBmpDecoder, &info);
+
+    hr = IWICComponentInfo_QueryInterface(info, &IID_IWICBitmapDecoderInfo, (void**)&decoder_info);
+    ok(hr == S_OK, "QueryInterface failed, hr=%x\n", hr);
+
+    hr = IWICBitmapDecoderInfo_GetCLSID(decoder_info, NULL);
+    ok(hr == E_INVALIDARG, "GetCLSID failed, hr=%x\n", hr);
+
+    hr = IWICBitmapDecoderInfo_GetCLSID(decoder_info, &clsid);
+    ok(hr == S_OK, "GetCLSID failed, hr=%x\n", hr);
+    ok(IsEqualGUID(&CLSID_WICBmpDecoder, &clsid), "GetCLSID returned wrong result\n");
+
+    hr = IWICBitmapDecoderInfo_GetMimeTypes(decoder_info, 0, NULL, NULL);
+    ok(hr == E_INVALIDARG, "GetMimeType failed, hr=%x\n", hr);
+
+    hr = IWICBitmapDecoderInfo_GetMimeTypes(decoder_info, 1, NULL, &len);
+    ok(hr == E_INVALIDARG, "GetMimeType failed, hr=%x\n", hr);
+    ok(len == lstrlenW(expected_mimetype)+1, "GetMimeType returned wrong len %i\n", len);
+
+    hr = IWICBitmapDecoderInfo_GetMimeTypes(decoder_info, len, value, NULL);
+    ok(hr == E_INVALIDARG, "GetMimeType failed, hr=%x\n", hr);
+
+    hr = IWICBitmapDecoderInfo_GetMimeTypes(decoder_info, 0, NULL, &len);
+    ok(hr == S_OK, "GetMimeType failed, hr=%x\n", hr);
+    ok(len == lstrlenW(expected_mimetype)+1, "GetMimeType returned wrong len %i\n", len);
+
+    value[0] = 0;
+    hr = IWICBitmapDecoderInfo_GetMimeTypes(decoder_info, len, value, &len);
+    ok(hr == S_OK, "GetMimeType failed, hr=%x\n", hr);
+    ok(lstrcmpW(value, expected_mimetype) == 0, "GetMimeType returned wrong value %s\n", wine_dbgstr_w(value));
+    ok(len == lstrlenW(expected_mimetype)+1, "GetMimeType returned wrong len %i\n", len);
+
+    hr = IWICBitmapDecoderInfo_GetMimeTypes(decoder_info, 1, value, &len);
+    ok(hr == WINCODEC_ERR_INSUFFICIENTBUFFER, "GetMimeType failed, hr=%x\n", hr);
+    ok(len == lstrlenW(expected_mimetype)+1, "GetMimeType returned wrong len %i\n", len);
+
+    hr = IWICBitmapDecoderInfo_GetMimeTypes(decoder_info, 256, value, &len);
+    ok(hr == S_OK, "GetMimeType failed, hr=%x\n", hr);
+    ok(lstrcmpW(value, expected_mimetype) == 0, "GetMimeType returned wrong value %s\n", wine_dbgstr_w(value));
+    ok(len == lstrlenW(expected_mimetype)+1, "GetMimeType returned wrong len %i\n", len);
+
+    num_formats = 0xdeadbeef;
+    hr = IWICBitmapDecoderInfo_GetPixelFormats(decoder_info, 0, NULL, &num_formats);
+    ok(hr == S_OK, "GetPixelFormats failed, hr=%x\n", hr);
+    ok(num_formats < 20 && num_formats > 1, "got %d formats\n", num_formats);
+
+    hr = IWICBitmapDecoderInfo_GetPixelFormats(decoder_info, 0, NULL, NULL);
+    ok(hr == E_INVALIDARG, "GetPixelFormats failed, hr=%x\n", hr);
+
+    count = 0xdeadbeef;
+    hr = IWICBitmapDecoderInfo_GetPixelFormats(decoder_info, 0, pixelformats, &count);
+    ok(hr == S_OK, "GetPixelFormats failed, hr=%x\n", hr);
+    ok(count == 0, "got %d formats\n", count);
+
+    count = 0xdeadbeef;
+    hr = IWICBitmapDecoderInfo_GetPixelFormats(decoder_info, 1, pixelformats, &count);
+    ok(hr == S_OK, "GetPixelFormats failed, hr=%x\n", hr);
+    ok(count == 1, "got %d formats\n", count);
+    ok(is_pixelformat(&pixelformats[0]), "got invalid pixel format\n");
+
+    count = 0xdeadbeef;
+    hr = IWICBitmapDecoderInfo_GetPixelFormats(decoder_info, num_formats, pixelformats, &count);
+    ok(hr == S_OK, "GetPixelFormats failed, hr=%x\n", hr);
+    ok(count == num_formats, "got %d formats, expected %d\n", count, num_formats);
+    for (i=0; i<num_formats; i++)
+        ok(is_pixelformat(&pixelformats[i]), "got invalid pixel format\n");
+
+    hr = IWICBitmapDecoderInfo_GetPixelFormats(decoder_info, num_formats, pixelformats, NULL);
+    ok(hr == E_INVALIDARG, "GetPixelFormats failed, hr=%x\n", hr);
+
+    count = 0xdeadbeef;
+    hr = IWICBitmapDecoderInfo_GetPixelFormats(decoder_info, 20, pixelformats, &count);
+    ok(hr == S_OK, "GetPixelFormats failed, hr=%x\n", hr);
+    ok(count == num_formats, "got %d formats, expected %d\n", count, num_formats);
+
+    hr = IWICBitmapDecoderInfo_GetFileExtensions(decoder_info, 0, NULL, NULL);
+    ok(hr == E_INVALIDARG, "GetFileExtensions failed, hr=%x\n", hr);
+
+    hr = IWICBitmapDecoderInfo_GetFileExtensions(decoder_info, 1, NULL, &len);
+    ok(hr == E_INVALIDARG, "GetFileExtensions failed, hr=%x\n", hr);
+    ok(len == lstrlenW(expected_extensions)+1, "GetFileExtensions returned wrong len %i\n", len);
+
+    hr = IWICBitmapDecoderInfo_GetFileExtensions(decoder_info, len, value, NULL);
+    ok(hr == E_INVALIDARG, "GetFileExtensions failed, hr=%x\n", hr);
+
+    hr = IWICBitmapDecoderInfo_GetFileExtensions(decoder_info, 0, NULL, &len);
+    ok(hr == S_OK, "GetFileExtensions failed, hr=%x\n", hr);
+    ok(len == lstrlenW(expected_extensions)+1, "GetFileExtensions returned wrong len %i\n", len);
+
+    value[0] = 0;
+    hr = IWICBitmapDecoderInfo_GetFileExtensions(decoder_info, len, value, &len);
+    ok(hr == S_OK, "GetFileExtensions failed, hr=%x\n", hr);
+    ok(lstrcmpW(value, expected_extensions) == 0, "GetFileExtensions returned wrong value %s\n", wine_dbgstr_w(value));
+    ok(len == lstrlenW(expected_extensions)+1, "GetFileExtensions returned wrong len %i\n", len);
+
+    hr = IWICBitmapDecoderInfo_GetFileExtensions(decoder_info, 1, value, &len);
+    ok(hr == WINCODEC_ERR_INSUFFICIENTBUFFER, "GetFileExtensions failed, hr=%x\n", hr);
+    ok(len == lstrlenW(expected_extensions)+1, "GetFileExtensions returned wrong len %i\n", len);
+
+    hr = IWICBitmapDecoderInfo_GetFileExtensions(decoder_info, 256, value, &len);
+    ok(hr == S_OK, "GetFileExtensions failed, hr=%x\n", hr);
+    ok(lstrcmpW(value, expected_extensions) == 0, "GetFileExtensions returned wrong value %s\n", wine_dbgstr_w(value));
+    ok(len == lstrlenW(expected_extensions)+1, "GetFileExtensions returned wrong len %i\n", len);
+
+    IWICBitmapDecoderInfo_Release(decoder_info);
+
+    IWICComponentInfo_Release(info);
+}
+
+static void test_pixelformat_info(void)
+{
+    IWICComponentInfo *info;
+    IWICPixelFormatInfo *pixelformat_info;
+    IWICPixelFormatInfo2 *pixelformat_info2;
+    HRESULT hr;
+    ULONG len, known_len;
+    WCHAR value[256];
+    GUID guid;
+    WICComponentType componenttype;
+    WICPixelFormatNumericRepresentation numericrepresentation;
+    DWORD signing;
+    UINT uiresult;
+    BYTE abbuffer[256];
+    BOOL supportstransparency;
+
+    hr = get_component_info(&GUID_WICPixelFormat32bppBGRA, &info);
+    ok(hr == S_OK, "CreateComponentInfo failed, hr=%x\n", hr);
+
+    if (FAILED(hr))
+        return;
+
+    hr = IWICComponentInfo_GetAuthor(info, 0, NULL, 0);
+    ok(hr == E_INVALIDARG, "GetAuthor failed, hr=%x\n", hr);
+
+    len = 0xdeadbeef;
+    hr = IWICComponentInfo_GetAuthor(info, 0, NULL, &len);
+    ok(hr == S_OK, "GetAuthor failed, hr=%x\n", hr);
+    ok(len < 255 && len > 0, "invalid length 0x%x\n", len);
+    known_len = len;
+
+    memset(value, 0xaa, 256 * sizeof(WCHAR));
+    hr = IWICComponentInfo_GetAuthor(info, len-1, value, NULL);
+    ok(hr == E_INVALIDARG, "GetAuthor failed, hr=%x\n", hr);
+    ok(value[0] = 0xaaaa, "string modified\n");
+
+    len = 0xdeadbeef;
+    memset(value, 0xaa, 256 * sizeof(WCHAR));
+    hr = IWICComponentInfo_GetAuthor(info, known_len-1, value, &len);
+    ok(hr == WINCODEC_ERR_INSUFFICIENTBUFFER, "GetAuthor failed, hr=%x\n", hr);
+    ok(len == known_len, "got length of 0x%x, expected 0x%x\n", len, known_len);
+    ok(value[known_len-1] == 0xaaaa, "string modified past given length\n");
+    ok(value[0] == 0xaaaa, "string modified\n");
+
+    len = 0xdeadbeef;
+    memset(value, 0xaa, 256 * sizeof(WCHAR));
+    hr = IWICComponentInfo_GetAuthor(info, known_len, value, &len);
+    ok(hr == S_OK, "GetAuthor failed, hr=%x\n", hr);
+    ok(len == known_len, "got length of 0x%x, expected 0x%x\n", len, known_len);
+    ok(value[known_len-1] == 0, "string not terminated at expected length\n");
+    ok(value[known_len-2] != 0xaaaa, "string not modified at given length\n");
+
+    len = 0xdeadbeef;
+    memset(value, 0xaa, 256 * sizeof(WCHAR));
+    hr = IWICComponentInfo_GetAuthor(info, known_len+1, value, &len);
+    ok(hr == S_OK, "GetAuthor failed, hr=%x\n", hr);
+    ok(len == known_len, "got length of 0x%x, expected 0x%x\n", len, known_len);
+    ok(value[known_len] == 0xaaaa, "string modified past end\n");
+    ok(value[known_len-1] == 0, "string not terminated at expected length\n");
+    ok(value[known_len-2] != 0xaaaa, "string not modified at given length\n");
+
+    hr = IWICComponentInfo_GetCLSID(info, NULL);
+    ok(hr == E_INVALIDARG, "GetCLSID failed, hr=%x\n", hr);
+
+    memset(&guid, 0xaa, sizeof(guid));
+    hr = IWICComponentInfo_GetCLSID(info, &guid);
+    ok(hr == S_OK, "GetCLSID failed, hr=%x\n", hr);
+    ok(IsEqualGUID(&guid, &GUID_WICPixelFormat32bppBGRA), "unexpected CLSID %s\n", debugstr_guid(&guid));
+
+    hr = IWICComponentInfo_GetComponentType(info, NULL);
+    ok(hr == E_INVALIDARG, "GetComponentType failed, hr=%x\n", hr);
+
+    hr = IWICComponentInfo_GetComponentType(info, &componenttype);
+    ok(hr == S_OK, "GetComponentType failed, hr=%x\n", hr);
+    ok(componenttype == WICPixelFormat, "unexpected component type 0x%x\n", componenttype);
+
+    len = 0xdeadbeef;
+    hr = IWICComponentInfo_GetFriendlyName(info, 0, NULL, &len);
+    ok(hr == S_OK, "GetFriendlyName failed, hr=%x\n", hr);
+    ok(len < 255 && len > 0, "invalid length 0x%x\n", len);
+
+    hr = IWICComponentInfo_GetSigningStatus(info, NULL);
+    ok(hr == E_INVALIDARG, "GetSigningStatus failed, hr=%x\n", hr);
+
+    hr = IWICComponentInfo_GetSigningStatus(info, &signing);
+    ok(hr == S_OK, "GetSigningStatus failed, hr=%x\n", hr);
+    ok(signing == WICComponentSigned, "unexpected signing status 0x%x\n", signing);
+
+    len = 0xdeadbeef;
+    hr = IWICComponentInfo_GetSpecVersion(info, 0, NULL, &len);
+    ok(hr == S_OK, "GetSpecVersion failed, hr=%x\n", hr);
+    ok(len == 0, "invalid length 0x%x\n", len); /* spec version does not apply to pixel formats */
+
+    memset(&guid, 0xaa, sizeof(guid));
+    hr = IWICComponentInfo_GetVendorGUID(info, &guid);
+    ok(hr == S_OK, "GetVendorGUID failed, hr=%x\n", hr);
+    ok(IsEqualGUID(&guid, &GUID_VendorMicrosoft) ||
+       broken(IsEqualGUID(&guid, &GUID_NULL)) /* XP */, "unexpected GUID %s\n", debugstr_guid(&guid));
+
+    len = 0xdeadbeef;
+    hr = IWICComponentInfo_GetVersion(info, 0, NULL, &len);
+    ok(hr == S_OK, "GetVersion failed, hr=%x\n", hr);
+    ok(len == 0, "invalid length 0x%x\n", len); /* version does not apply to pixel formats */
+
+    hr = IWICComponentInfo_QueryInterface(info, &IID_IWICPixelFormatInfo, (void**)&pixelformat_info);
+    ok(hr == S_OK, "QueryInterface failed, hr=%x\n", hr);
+
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICPixelFormatInfo_GetBitsPerPixel(pixelformat_info, NULL);
+        ok(hr == E_INVALIDARG, "GetBitsPerPixel failed, hr=%x\n", hr);
+
+        hr = IWICPixelFormatInfo_GetBitsPerPixel(pixelformat_info, &uiresult);
+        ok(hr == S_OK, "GetBitsPerPixel failed, hr=%x\n", hr);
+        ok(uiresult == 32, "unexpected bpp %i\n", uiresult);
+
+        hr = IWICPixelFormatInfo_GetChannelCount(pixelformat_info, &uiresult);
+        ok(hr == S_OK, "GetChannelCount failed, hr=%x\n", hr);
+        ok(uiresult == 4, "unexpected channel count %i\n", uiresult);
+
+        hr = IWICPixelFormatInfo_GetChannelMask(pixelformat_info, 0, 0, NULL, NULL);
+        ok(hr == E_INVALIDARG, "GetChannelMask failed, hr=%x\n", hr);
+
+        uiresult = 0xdeadbeef;
+        hr = IWICPixelFormatInfo_GetChannelMask(pixelformat_info, 0, 0, NULL, &uiresult);
+        ok(hr == S_OK, "GetChannelMask failed, hr=%x\n", hr);
+        ok(uiresult == 4, "unexpected length %i\n", uiresult);
+
+        memset(abbuffer, 0xaa, sizeof(abbuffer));
+        hr = IWICPixelFormatInfo_GetChannelMask(pixelformat_info, 0, known_len, abbuffer, NULL);
+        ok(hr == E_INVALIDARG, "GetChannelMask failed, hr=%x\n", hr);
+        ok(abbuffer[0] == 0xaa, "buffer modified\n");
+
+        uiresult = 0xdeadbeef;
+        memset(abbuffer, 0xaa, sizeof(abbuffer));
+        hr = IWICPixelFormatInfo_GetChannelMask(pixelformat_info, 0, 3, abbuffer, &uiresult);
+        ok(hr == E_INVALIDARG, "GetChannelMask failed, hr=%x\n", hr);
+        ok(abbuffer[0] == 0xaa, "buffer modified\n");
+        ok(uiresult == 4, "unexpected length %i\n", uiresult);
+
+        memset(abbuffer, 0xaa, sizeof(abbuffer));
+        hr = IWICPixelFormatInfo_GetChannelMask(pixelformat_info, 0, 4, abbuffer, &uiresult);
+        ok(hr == S_OK, "GetChannelMask failed, hr=%x\n", hr);
+        ok(*((ULONG*)abbuffer) == 0xff, "unexpected mask 0x%x\n", *((ULONG*)abbuffer));
+        ok(uiresult == 4, "unexpected length %i\n", uiresult);
+
+        memset(abbuffer, 0xaa, sizeof(abbuffer));
+        hr = IWICPixelFormatInfo_GetChannelMask(pixelformat_info, 0, 5, abbuffer, &uiresult);
+        ok(hr == S_OK, "GetChannelMask failed, hr=%x\n", hr);
+        ok(*((ULONG*)abbuffer) == 0xff, "unexpected mask 0x%x\n", *((ULONG*)abbuffer));
+        ok(abbuffer[4] == 0xaa, "buffer modified past actual length\n");
+        ok(uiresult == 4, "unexpected length %i\n", uiresult);
+
+        memset(&guid, 0xaa, sizeof(guid));
+        hr = IWICPixelFormatInfo_GetFormatGUID(pixelformat_info, &guid);
+        ok(hr == S_OK, "GetFormatGUID failed, hr=%x\n", hr);
+        ok(IsEqualGUID(&guid, &GUID_WICPixelFormat32bppBGRA), "unexpected GUID %s\n", debugstr_guid(&guid));
+
+        IWICPixelFormatInfo_Release(pixelformat_info);
+    }
+
+    hr = IWICComponentInfo_QueryInterface(info, &IID_IWICPixelFormatInfo2, (void**)&pixelformat_info2);
+
+    if (FAILED(hr))
+        win_skip("IWICPixelFormatInfo2 not supported\n");
+    else
+    {
+        hr = IWICPixelFormatInfo2_GetNumericRepresentation(pixelformat_info2, NULL);
+        ok(hr == E_INVALIDARG, "GetNumericRepresentation failed, hr=%x\n", hr);
+
+        numericrepresentation = 0xdeadbeef;
+        hr = IWICPixelFormatInfo2_GetNumericRepresentation(pixelformat_info2, &numericrepresentation);
+        ok(hr == S_OK, "GetNumericRepresentation failed, hr=%x\n", hr);
+        ok(numericrepresentation == WICPixelFormatNumericRepresentationUnsignedInteger, "unexpected numeric representation %i\n", numericrepresentation);
+
+        hr = IWICPixelFormatInfo2_SupportsTransparency(pixelformat_info2, NULL);
+        ok(hr == E_INVALIDARG, "SupportsTransparency failed, hr=%x\n", hr);
+
+        supportstransparency = 0xdeadbeef;
+        hr = IWICPixelFormatInfo2_SupportsTransparency(pixelformat_info2, &supportstransparency);
+        ok(hr == S_OK, "SupportsTransparency failed, hr=%x\n", hr);
+        ok(supportstransparency == 1, "unexpected value %i\n", supportstransparency);
+
+        IWICPixelFormatInfo2_Release(pixelformat_info2);
+    }
+
+    IWICComponentInfo_Release(info);
+}
+
+static void test_reader_info(void)
+{
+    IWICImagingFactory *factory;
+    IWICComponentInfo *info;
+    IWICMetadataReaderInfo *reader_info;
+    HRESULT hr;
+    CLSID clsid;
+    GUID container_formats[10];
+    UINT count, size;
+    WICMetadataPattern *patterns;
+
+    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICImagingFactory, (void**)&factory);
+    ok(hr == S_OK, "CoCreateInstance failed, hr=%x\n", hr);
+    if (FAILED(hr)) return;
+
+    hr = IWICImagingFactory_CreateComponentInfo(factory, &CLSID_WICUnknownMetadataReader, &info);
+    ok(hr == S_OK, "CreateComponentInfo failed, hr=%x\n", hr);
+
+    hr = IWICComponentInfo_QueryInterface(info, &IID_IWICMetadataReaderInfo, (void**)&reader_info);
+    ok(hr == S_OK, "QueryInterface failed, hr=%x\n", hr);
+
+    hr = IWICMetadataReaderInfo_GetCLSID(reader_info, NULL);
+    ok(hr == E_INVALIDARG, "GetCLSID failed, hr=%x\n", hr);
+
+    hr = IWICMetadataReaderInfo_GetCLSID(reader_info, &clsid);
+    ok(hr == S_OK, "GetCLSID failed, hr=%x\n", hr);
+    ok(IsEqualGUID(&CLSID_WICUnknownMetadataReader, &clsid), "GetCLSID returned wrong result\n");
+
+    hr = IWICMetadataReaderInfo_GetMetadataFormat(reader_info, &clsid);
+    ok(hr == S_OK, "GetMetadataFormat failed, hr=%x\n", hr);
+    ok(IsEqualGUID(&GUID_MetadataFormatUnknown, &clsid), "GetMetadataFormat returned wrong result\n");
+
+    hr = IWICMetadataReaderInfo_GetContainerFormats(reader_info, 0, NULL, NULL);
+    ok(hr == E_INVALIDARG, "GetContainerFormats failed, hr=%x\n", hr);
+
+    count = 0xdeadbeef;
+    hr = IWICMetadataReaderInfo_GetContainerFormats(reader_info, 0, NULL, &count);
+todo_wine
+    ok(hr == S_OK, "GetContainerFormats failed, hr=%x\n", hr);
+todo_wine
+    ok(count == 0, "unexpected count %d\n", count);
+
+    hr = IWICMetadataReaderInfo_GetPatterns(reader_info, &GUID_ContainerFormatPng,
+        0, NULL, NULL, NULL);
+    ok(hr == E_INVALIDARG, "GetPatterns failed, hr=%x\n", hr);
+
+    count = size = 0xdeadbeef;
+    hr = IWICMetadataReaderInfo_GetPatterns(reader_info, &GUID_ContainerFormatPng,
+        0, NULL, &count, &size);
+todo_wine
+    ok(hr == WINCODEC_ERR_COMPONENTNOTFOUND || broken(hr == S_OK) /* Windows XP */,
+        "GetPatterns failed, hr=%x\n", hr);
+    ok(count == 0xdeadbeef, "unexpected count %d\n", count);
+    ok(size == 0xdeadbeef, "unexpected size %d\n", size);
+
+    IWICMetadataReaderInfo_Release(reader_info);
+
+    IWICComponentInfo_Release(info);
+
+    hr = IWICImagingFactory_CreateComponentInfo(factory, &CLSID_WICXMPStructMetadataReader, &info);
+todo_wine
+    ok(hr == S_OK, "CreateComponentInfo failed, hr=%x\n", hr);
+
+    if (FAILED(hr))
+    {
+        IWICImagingFactory_Release(factory);
+        return;
+    }
+
+    hr = IWICComponentInfo_QueryInterface(info, &IID_IWICMetadataReaderInfo, (void**)&reader_info);
+    ok(hr == S_OK, "QueryInterface failed, hr=%x\n", hr);
+
+    hr = IWICMetadataReaderInfo_GetCLSID(reader_info, NULL);
+    ok(hr == E_INVALIDARG, "GetCLSID failed, hr=%x\n", hr);
+
+    hr = IWICMetadataReaderInfo_GetCLSID(reader_info, &clsid);
+    ok(hr == S_OK, "GetCLSID failed, hr=%x\n", hr);
+    ok(IsEqualGUID(&CLSID_WICXMPStructMetadataReader, &clsid), "GetCLSID returned wrong result\n");
+
+    hr = IWICMetadataReaderInfo_GetMetadataFormat(reader_info, &clsid);
+    ok(hr == S_OK, "GetMetadataFormat failed, hr=%x\n", hr);
+    ok(IsEqualGUID(&GUID_MetadataFormatXMPStruct, &clsid), "GetMetadataFormat returned wrong result\n");
+
+    hr = IWICMetadataReaderInfo_GetContainerFormats(reader_info, 0, NULL, NULL);
+    ok(hr == E_INVALIDARG, "GetContainerFormats failed, hr=%x\n", hr);
+
+    count = 0xdeadbeef;
+    hr = IWICMetadataReaderInfo_GetContainerFormats(reader_info, 0, NULL, &count);
+    ok(hr == S_OK, "GetContainerFormats failed, hr=%x\n", hr);
+    ok(count >= 2, "unexpected count %d\n", count);
+
+    count = 0xdeadbeef;
+    hr = IWICMetadataReaderInfo_GetContainerFormats(reader_info, 1, container_formats, &count);
+    ok(hr == S_OK, "GetContainerFormats failed, hr=%x\n", hr);
+    ok(count == 1, "unexpected count %d\n", count);
+
+    count = 0xdeadbeef;
+    hr = IWICMetadataReaderInfo_GetContainerFormats(reader_info, 10, container_formats, &count);
+    ok(hr == S_OK, "GetContainerFormats failed, hr=%x\n", hr);
+    ok(count == min(count, 10), "unexpected count %d\n", count);
+
+    count = size = 0xdeadbeef;
+    hr = IWICMetadataReaderInfo_GetPatterns(reader_info, &GUID_ContainerFormatPng,
+        0, NULL, &count, &size);
+    ok(hr == WINCODEC_ERR_COMPONENTNOTFOUND || broken(hr == S_OK) /* Windows XP */,
+        "GetPatterns failed, hr=%x\n", hr);
+    ok(count == 0xdeadbeef, "unexpected count %d\n", count);
+    ok(size == 0xdeadbeef, "unexpected size %d\n", size);
+
+    count = size = 0xdeadbeef;
+    hr = IWICMetadataReaderInfo_GetPatterns(reader_info, &GUID_MetadataFormatXMP,
+        0, NULL, &count, &size);
+    ok(hr == S_OK, "GetPatterns failed, hr=%x\n", hr);
+    ok(count == 1, "unexpected count %d\n", count);
+    ok(size > sizeof(WICMetadataPattern), "unexpected size %d\n", size);
+
+    if (hr == S_OK)
+    {
+        patterns = HeapAlloc(GetProcessHeap(), 0, size);
+
+        count = size = 0xdeadbeef;
+        hr = IWICMetadataReaderInfo_GetPatterns(reader_info, &GUID_MetadataFormatXMP,
+            size-1, patterns, &count, &size);
+        ok(hr == S_OK, "GetPatterns failed, hr=%x\n", hr);
+        ok(count == 1, "unexpected count %d\n", count);
+        ok(size > sizeof(WICMetadataPattern), "unexpected size %d\n", size);
+
+        count = size = 0xdeadbeef;
+        hr = IWICMetadataReaderInfo_GetPatterns(reader_info, &GUID_MetadataFormatXMP,
+            size, patterns, &count, &size);
+        ok(hr == S_OK, "GetPatterns failed, hr=%x\n", hr);
+        ok(count == 1, "unexpected count %d\n", count);
+        ok(size == sizeof(WICMetadataPattern) + patterns->Length * 2, "unexpected size %d\n", size);
+
+        HeapFree(GetProcessHeap(), 0, patterns);
+    }
+
+    IWICMetadataReaderInfo_Release(reader_info);
+
+    IWICComponentInfo_Release(info);
+
+    IWICImagingFactory_Release(factory);
+}
+
+START_TEST(info)
+{
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+
+    test_decoder_info();
+    test_reader_info();
+    test_pixelformat_info();
+
+    CoUninitialize();
+}
diff --git a/rostests/winetests/windowscodecs/metadata.c b/rostests/winetests/windowscodecs/metadata.c
new file mode 100644 (file)
index 0000000..be700ad
--- /dev/null
@@ -0,0 +1,1811 @@
+/*
+ * Copyright 2011 Vincent Povirk for CodeWeavers
+ * Copyright 2012 Dmitry Timoshkov
+ *
+ * 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 <stdio.h>
+#include <stdarg.h>
+#include <math.h>
+#include <assert.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "objbase.h"
+#include "wincodec.h"
+#include "wincodecsdk.h"
+#include "wine/test.h"
+
+#define expect_blob(propvar, data, length) do { \
+    ok((propvar).vt == VT_BLOB, "unexpected vt: %i\n", (propvar).vt); \
+    if ((propvar).vt == VT_BLOB) { \
+        ok(U(propvar).blob.cbSize == (length), "expected size %u, got %u\n", (ULONG)(length), U(propvar).blob.cbSize); \
+        if (U(propvar).blob.cbSize == (length)) { \
+            ok(!memcmp(U(propvar).blob.pBlobData, (data), (length)), "unexpected data\n"); \
+        } \
+    } \
+} while (0)
+
+#define IFD_BYTE 1
+#define IFD_ASCII 2
+#define IFD_SHORT 3
+#define IFD_LONG 4
+#define IFD_RATIONAL 5
+#define IFD_SBYTE 6
+#define IFD_UNDEFINED 7
+#define IFD_SSHORT 8
+#define IFD_SLONG 9
+#define IFD_SRATIONAL 10
+#define IFD_FLOAT 11
+#define IFD_DOUBLE 12
+#define IFD_IFD 13
+
+#include "pshpack2.h"
+struct IFD_entry
+{
+    SHORT id;
+    SHORT type;
+    ULONG count;
+    LONG  value;
+};
+
+struct IFD_rational
+{
+    LONG numerator;
+    LONG denominator;
+};
+
+static const struct ifd_data
+{
+    USHORT number_of_entries;
+    struct IFD_entry entry[40];
+    ULONG next_IFD;
+    struct IFD_rational xres;
+    DOUBLE double_val;
+    struct IFD_rational srational_val;
+    char string[14];
+    SHORT short_val[4];
+    LONG long_val[2];
+    FLOAT float_val[2];
+    struct IFD_rational rational[3];
+} IFD_data =
+{
+    28,
+    {
+        { 0xfe,  IFD_SHORT, 1, 1 }, /* NEWSUBFILETYPE */
+        { 0x100, IFD_LONG, 1, 222 }, /* IMAGEWIDTH */
+        { 0x101, IFD_LONG, 1, 333 }, /* IMAGELENGTH */
+        { 0x102, IFD_SHORT, 1, 24 }, /* BITSPERSAMPLE */
+        { 0x103, IFD_LONG, 1, 32773 }, /* COMPRESSION: packbits */
+        { 0x11a, IFD_RATIONAL, 1, FIELD_OFFSET(struct ifd_data, xres) },
+        { 0xf001, IFD_BYTE, 1, 0x11223344 },
+        { 0xf002, IFD_BYTE, 4, 0x11223344 },
+        { 0xf003, IFD_SBYTE, 1, 0x11223344 },
+        { 0xf004, IFD_SSHORT, 1, 0x11223344 },
+        { 0xf005, IFD_SSHORT, 2, 0x11223344 },
+        { 0xf006, IFD_SLONG, 1, 0x11223344 },
+        { 0xf007, IFD_FLOAT, 1, 0x11223344 },
+        { 0xf008, IFD_DOUBLE, 1, FIELD_OFFSET(struct ifd_data, double_val) },
+        { 0xf009, IFD_SRATIONAL, 1, FIELD_OFFSET(struct ifd_data, srational_val) },
+        { 0xf00a, IFD_BYTE, 13, FIELD_OFFSET(struct ifd_data, string) },
+        { 0xf00b, IFD_SSHORT, 4, FIELD_OFFSET(struct ifd_data, short_val) },
+        { 0xf00c, IFD_SLONG, 2, FIELD_OFFSET(struct ifd_data, long_val) },
+        { 0xf00d, IFD_FLOAT, 2, FIELD_OFFSET(struct ifd_data, float_val) },
+        { 0xf00e, IFD_ASCII, 13, FIELD_OFFSET(struct ifd_data, string) },
+        { 0xf00f, IFD_ASCII, 4, 'a' | 'b' << 8 | 'c' << 16 | 'd' << 24 },
+        { 0xf010, IFD_UNDEFINED, 13, FIELD_OFFSET(struct ifd_data, string) },
+        { 0xf011, IFD_UNDEFINED, 4, 'a' | 'b' << 8 | 'c' << 16 | 'd' << 24 },
+        { 0xf012, IFD_BYTE, 0, 0x11223344 },
+        { 0xf013, IFD_SHORT, 0, 0x11223344 },
+        { 0xf014, IFD_LONG, 0, 0x11223344 },
+        { 0xf015, IFD_FLOAT, 0, 0x11223344 },
+        { 0xf016, IFD_SRATIONAL, 3, FIELD_OFFSET(struct ifd_data, rational) },
+    },
+    0,
+    { 900, 3 },
+    1234567890.0987654321,
+    { 0x1a2b3c4d, 0x5a6b7c8d },
+    "Hello World!",
+    { 0x0101, 0x0202, 0x0303, 0x0404 },
+    { 0x11223344, 0x55667788 },
+    { (FLOAT)1234.5678, (FLOAT)8765.4321 },
+    { { 0x01020304, 0x05060708 }, { 0x10203040, 0x50607080 }, { 0x11223344, 0x55667788 } },
+};
+#include "poppack.h"
+
+static const char metadata_unknown[] = "lalala";
+
+static const char metadata_tEXt[] = {
+    0,0,0,14, /* chunk length */
+    't','E','X','t', /* chunk type */
+    'w','i','n','e','t','e','s','t',0, /* keyword */
+    'v','a','l','u','e', /* text */
+    0x3f,0x64,0x19,0xf3 /* chunk CRC */
+};
+
+static const char pngimage[285] = {
+0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52,
+0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x08,0x02,0x00,0x00,0x00,0x90,0x77,0x53,
+0xde,0x00,0x00,0x00,0x09,0x70,0x48,0x59,0x73,0x00,0x00,0x0b,0x13,0x00,0x00,0x0b,
+0x13,0x01,0x00,0x9a,0x9c,0x18,0x00,0x00,0x00,0x07,0x74,0x49,0x4d,0x45,0x07,0xd5,
+0x06,0x03,0x0f,0x07,0x2d,0x12,0x10,0xf0,0xfd,0x00,0x00,0x00,0x0c,0x49,0x44,0x41,
+0x54,0x08,0xd7,0x63,0xf8,0xff,0xff,0x3f,0x00,0x05,0xfe,0x02,0xfe,0xdc,0xcc,0x59,
+0xe7,0x00,0x00,0x00,0x00,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82
+};
+
+/* 1x1 pixel gif */
+static const char gifimage[35] = {
+0x47,0x49,0x46,0x38,0x37,0x61,0x01,0x00,0x01,0x00,0x80,0x00,0x00,0xff,0xff,0xff,
+0xff,0xff,0xff,0x2c,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x02,0x02,0x44,
+0x01,0x00,0x3b
+};
+
+/* 1x1 pixel gif, 2 frames; first frame is white, second is black */
+static const char animatedgif[] = {
+'G','I','F','8','9','a',0x01,0x00,0x01,0x00,0xA1,0x00,0x00,
+0x6F,0x6F,0x6F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*0x21,0xFF,0x0B,'N','E','T','S','C','A','P','E','2','.','0',*/
+0x21,0xFF,0x0B,'A','N','I','M','E','X','T','S','1','.','0',
+0x03,0x01,0x05,0x00,0x00,
+0x21,0xFE,0x0C,'H','e','l','l','o',' ','W','o','r','l','d','!',0x00,
+0x21,0x01,0x0D,'a','n','i','m','a','t','i','o','n','.','g','i','f',0x00,
+0x21,0xF9,0x04,0x00,0x0A,0x00,0xFF,0x00,0x2C,
+0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x81,
+0xDE,0xDE,0xDE,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02,0x4C,0x01,0x00,
+0x21,0xFE,0x08,'i','m','a','g','e',' ','#','1',0x00,
+0x21,0x01,0x0C,'p','l','a','i','n','t','e','x','t',' ','#','1',0x00,
+0x21,0xF9,0x04,0x01,0x0A,0x00,0x01,0x00,0x2C,
+0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x81,
+0x4D,0x4D,0x4D,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02,0x44,0x01,0x00,
+0x21,0xFE,0x08,'i','m','a','g','e',' ','#','2',0x00,
+0x21,0x01,0x0C,'p','l','a','i','n','t','e','x','t',' ','#','2',0x00,0x3B
+};
+
+static const char *debugstr_guid(REFIID riid)
+{
+    static char buf[50];
+
+    if(!riid)
+        return "(null)";
+
+    sprintf(buf, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
+            riid->Data1, riid->Data2, riid->Data3, riid->Data4[0],
+            riid->Data4[1], riid->Data4[2], riid->Data4[3], riid->Data4[4],
+            riid->Data4[5], riid->Data4[6], riid->Data4[7]);
+
+    return buf;
+}
+
+static IStream *create_stream(const char *data, int data_size)
+{
+    HRESULT hr;
+    IStream *stream;
+    HGLOBAL hdata;
+    void *locked_data;
+
+    hdata = GlobalAlloc(GMEM_MOVEABLE, data_size);
+    ok(hdata != 0, "GlobalAlloc failed\n");
+    if (!hdata) return NULL;
+
+    locked_data = GlobalLock(hdata);
+    memcpy(locked_data, data, data_size);
+    GlobalUnlock(hdata);
+
+    hr = CreateStreamOnHGlobal(hdata, TRUE, &stream);
+    ok(hr == S_OK, "CreateStreamOnHGlobal failed, hr=%x\n", hr);
+
+    return stream;
+}
+
+static void load_stream(IUnknown *reader, const char *data, int data_size, DWORD persist_options)
+{
+    HRESULT hr;
+    IWICPersistStream *persist;
+    IStream *stream;
+    LARGE_INTEGER pos;
+    ULARGE_INTEGER cur_pos;
+
+    stream = create_stream(data, data_size);
+    if (!stream)
+        return;
+
+    hr = IUnknown_QueryInterface(reader, &IID_IWICPersistStream, (void**)&persist);
+    ok(hr == S_OK, "QueryInterface failed, hr=%x\n", hr);
+
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICPersistStream_LoadEx(persist, stream, NULL, persist_options);
+        ok(hr == S_OK, "LoadEx failed, hr=%x\n", hr);
+
+        IWICPersistStream_Release(persist);
+    }
+
+    pos.QuadPart = 0;
+    hr = IStream_Seek(stream, pos, SEEK_CUR, &cur_pos);
+    ok(hr == S_OK, "IStream_Seek error %#x\n", hr);
+    /* IFD metadata reader doesn't rewind the stream to the start */
+    ok(cur_pos.QuadPart == 0 || cur_pos.QuadPart <= data_size,
+       "current stream pos is at %x/%x, data size %x\n", cur_pos.u.LowPart, cur_pos.u.HighPart, data_size);
+
+    IStream_Release(stream);
+}
+
+static void test_metadata_unknown(void)
+{
+    HRESULT hr;
+    IWICMetadataReader *reader;
+    IWICEnumMetadataItem *enumerator;
+    IWICMetadataBlockReader *blockreader;
+    PROPVARIANT schema, id, value;
+    ULONG items_returned;
+
+    hr = CoCreateInstance(&CLSID_WICUnknownMetadataReader, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICMetadataReader, (void**)&reader);
+    ok(hr == S_OK, "CoCreateInstance failed, hr=%x\n", hr);
+    if (FAILED(hr)) return;
+
+    load_stream((IUnknown*)reader, metadata_unknown, sizeof(metadata_unknown), WICPersistOptionsDefault);
+
+    hr = IWICMetadataReader_GetEnumerator(reader, &enumerator);
+    ok(hr == S_OK, "GetEnumerator failed, hr=%x\n", hr);
+
+    if (SUCCEEDED(hr))
+    {
+        PropVariantInit(&schema);
+        PropVariantInit(&id);
+        PropVariantInit(&value);
+
+        hr = IWICEnumMetadataItem_Next(enumerator, 1, &schema, &id, &value, &items_returned);
+        ok(hr == S_OK, "Next failed, hr=%x\n", hr);
+        ok(items_returned == 1, "unexpected item count %i\n", items_returned);
+
+        if (hr == S_OK && items_returned == 1)
+        {
+            ok(schema.vt == VT_EMPTY, "unexpected vt: %i\n", schema.vt);
+            ok(id.vt == VT_EMPTY, "unexpected vt: %i\n", id.vt);
+            expect_blob(value, metadata_unknown, sizeof(metadata_unknown));
+
+            PropVariantClear(&schema);
+            PropVariantClear(&id);
+            PropVariantClear(&value);
+        }
+
+        hr = IWICEnumMetadataItem_Next(enumerator, 1, &schema, &id, &value, &items_returned);
+        ok(hr == S_FALSE, "Next failed, hr=%x\n", hr);
+        ok(items_returned == 0, "unexpected item count %i\n", items_returned);
+
+        IWICEnumMetadataItem_Release(enumerator);
+    }
+
+    hr = IWICMetadataReader_QueryInterface(reader, &IID_IWICMetadataBlockReader, (void**)&blockreader);
+    ok(hr == E_NOINTERFACE, "QueryInterface failed, hr=%x\n", hr);
+
+    if (SUCCEEDED(hr))
+        IWICMetadataBlockReader_Release(blockreader);
+
+    IWICMetadataReader_Release(reader);
+}
+
+static void test_metadata_tEXt(void)
+{
+    HRESULT hr;
+    IWICMetadataReader *reader;
+    IWICEnumMetadataItem *enumerator;
+    PROPVARIANT schema, id, value;
+    ULONG items_returned, count;
+    GUID format;
+
+    PropVariantInit(&schema);
+    PropVariantInit(&id);
+    PropVariantInit(&value);
+
+    hr = CoCreateInstance(&CLSID_WICPngTextMetadataReader, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICMetadataReader, (void**)&reader);
+    ok(hr == S_OK, "CoCreateInstance failed, hr=%x\n", hr);
+    if (FAILED(hr)) return;
+
+    hr = IWICMetadataReader_GetCount(reader, NULL);
+    ok(hr == E_INVALIDARG, "GetCount failed, hr=%x\n", hr);
+
+    hr = IWICMetadataReader_GetCount(reader, &count);
+    ok(hr == S_OK, "GetCount failed, hr=%x\n", hr);
+    ok(count == 0, "unexpected count %i\n", count);
+
+    load_stream((IUnknown*)reader, metadata_tEXt, sizeof(metadata_tEXt), WICPersistOptionsDefault);
+
+    hr = IWICMetadataReader_GetCount(reader, &count);
+    ok(hr == S_OK, "GetCount failed, hr=%x\n", hr);
+    ok(count == 1, "unexpected count %i\n", count);
+
+    hr = IWICMetadataReader_GetEnumerator(reader, NULL);
+    ok(hr == E_INVALIDARG, "GetEnumerator failed, hr=%x\n", hr);
+
+    hr = IWICMetadataReader_GetEnumerator(reader, &enumerator);
+    ok(hr == S_OK, "GetEnumerator failed, hr=%x\n", hr);
+
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICEnumMetadataItem_Next(enumerator, 1, &schema, &id, &value, &items_returned);
+        ok(hr == S_OK, "Next failed, hr=%x\n", hr);
+        ok(items_returned == 1, "unexpected item count %i\n", items_returned);
+
+        if (hr == S_OK && items_returned == 1)
+        {
+            ok(schema.vt == VT_EMPTY, "unexpected vt: %i\n", schema.vt);
+            ok(id.vt == VT_LPSTR, "unexpected vt: %i\n", id.vt);
+            ok(!strcmp(U(id).pszVal, "winetest"), "unexpected id: %s\n", U(id).pszVal);
+            ok(value.vt == VT_LPSTR, "unexpected vt: %i\n", value.vt);
+            ok(!strcmp(U(value).pszVal, "value"), "unexpected value: %s\n", U(value).pszVal);
+
+            PropVariantClear(&schema);
+            PropVariantClear(&id);
+            PropVariantClear(&value);
+        }
+
+        hr = IWICEnumMetadataItem_Next(enumerator, 1, &schema, &id, &value, &items_returned);
+        ok(hr == S_FALSE, "Next failed, hr=%x\n", hr);
+        ok(items_returned == 0, "unexpected item count %i\n", items_returned);
+
+        IWICEnumMetadataItem_Release(enumerator);
+    }
+
+    hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+    ok(hr == S_OK, "GetMetadataFormat failed, hr=%x\n", hr);
+    ok(IsEqualGUID(&format, &GUID_MetadataFormatChunktEXt), "unexpected format %s\n", debugstr_guid(&format));
+
+    hr = IWICMetadataReader_GetMetadataFormat(reader, NULL);
+    ok(hr == E_INVALIDARG, "GetMetadataFormat failed, hr=%x\n", hr);
+
+    id.vt = VT_LPSTR;
+    U(id).pszVal = CoTaskMemAlloc(strlen("winetest") + 1);
+    strcpy(U(id).pszVal, "winetest");
+
+    hr = IWICMetadataReader_GetValue(reader, NULL, &id, NULL);
+    ok(hr == S_OK, "GetValue failed, hr=%x\n", hr);
+
+    hr = IWICMetadataReader_GetValue(reader, &schema, NULL, &value);
+    ok(hr == E_INVALIDARG, "GetValue failed, hr=%x\n", hr);
+
+    hr = IWICMetadataReader_GetValue(reader, &schema, &id, &value);
+    ok(hr == S_OK, "GetValue failed, hr=%x\n", hr);
+    ok(value.vt == VT_LPSTR, "unexpected vt: %i\n", id.vt);
+    ok(!strcmp(U(value).pszVal, "value"), "unexpected value: %s\n", U(value).pszVal);
+    PropVariantClear(&value);
+
+    strcpy(U(id).pszVal, "test");
+
+    hr = IWICMetadataReader_GetValue(reader, &schema, &id, &value);
+    ok(hr == WINCODEC_ERR_PROPERTYNOTFOUND, "GetValue failed, hr=%x\n", hr);
+
+    PropVariantClear(&id);
+
+    hr = IWICMetadataReader_GetValueByIndex(reader, 0, NULL, NULL, NULL);
+    ok(hr == S_OK, "GetValueByIndex failed, hr=%x\n", hr);
+
+    hr = IWICMetadataReader_GetValueByIndex(reader, 0, &schema, NULL, NULL);
+    ok(hr == S_OK, "GetValueByIndex failed, hr=%x\n", hr);
+    ok(schema.vt == VT_EMPTY, "unexpected vt: %i\n", schema.vt);
+
+    hr = IWICMetadataReader_GetValueByIndex(reader, 0, NULL, &id, NULL);
+    ok(hr == S_OK, "GetValueByIndex failed, hr=%x\n", hr);
+    ok(id.vt == VT_LPSTR, "unexpected vt: %i\n", id.vt);
+    ok(!strcmp(U(id).pszVal, "winetest"), "unexpected id: %s\n", U(id).pszVal);
+    PropVariantClear(&id);
+
+    hr = IWICMetadataReader_GetValueByIndex(reader, 0, NULL, NULL, &value);
+    ok(hr == S_OK, "GetValueByIndex failed, hr=%x\n", hr);
+    ok(value.vt == VT_LPSTR, "unexpected vt: %i\n", value.vt);
+    ok(!strcmp(U(value).pszVal, "value"), "unexpected value: %s\n", U(value).pszVal);
+    PropVariantClear(&value);
+
+    hr = IWICMetadataReader_GetValueByIndex(reader, 1, NULL, NULL, NULL);
+    ok(hr == E_INVALIDARG, "GetValueByIndex failed, hr=%x\n", hr);
+
+    IWICMetadataReader_Release(reader);
+}
+
+static inline USHORT ushort_bswap(USHORT s)
+{
+    return (s >> 8) | (s << 8);
+}
+
+static inline ULONG ulong_bswap(ULONG l)
+{
+    return ((ULONG)ushort_bswap((USHORT)l) << 16) | ushort_bswap((USHORT)(l >> 16));
+}
+
+static inline ULONGLONG ulonglong_bswap(ULONGLONG ll)
+{
+    return ((ULONGLONG)ulong_bswap((ULONG)ll) << 32) | ulong_bswap((ULONG)(ll >> 32));
+}
+
+static void byte_swap_ifd_data(char *data)
+{
+    USHORT number_of_entries, i;
+    struct IFD_entry *entry;
+    char *data_start = data;
+
+    number_of_entries = *(USHORT *)data;
+    *(USHORT *)data = ushort_bswap(*(USHORT *)data);
+    data += sizeof(USHORT);
+
+    for (i = 0; i < number_of_entries; i++)
+    {
+        entry = (struct IFD_entry *)data;
+
+        switch (entry->type)
+        {
+        case IFD_BYTE:
+        case IFD_SBYTE:
+        case IFD_ASCII:
+        case IFD_UNDEFINED:
+            if (entry->count > 4)
+                entry->value = ulong_bswap(entry->value);
+            break;
+
+        case IFD_SHORT:
+        case IFD_SSHORT:
+            if (entry->count > 2)
+            {
+                ULONG j, count = entry->count;
+                USHORT *us = (USHORT *)(data_start + entry->value);
+                if (!count) count = 1;
+                for (j = 0; j < count; j++)
+                    us[j] = ushort_bswap(us[j]);
+
+                entry->value = ulong_bswap(entry->value);
+            }
+            else
+            {
+                ULONG j, count = entry->count;
+                USHORT *us = (USHORT *)&entry->value;
+                if (!count) count = 1;
+                for (j = 0; j < count; j++)
+                    us[j] = ushort_bswap(us[j]);
+            }
+            break;
+
+        case IFD_LONG:
+        case IFD_SLONG:
+        case IFD_FLOAT:
+            if (entry->count > 1)
+            {
+                ULONG j, count = entry->count;
+                ULONG *ul = (ULONG *)(data_start + entry->value);
+                if (!count) count = 1;
+                for (j = 0; j < count; j++)
+                    ul[j] = ulong_bswap(ul[j]);
+            }
+            entry->value = ulong_bswap(entry->value);
+            break;
+
+        case IFD_RATIONAL:
+        case IFD_SRATIONAL:
+            {
+                ULONG j;
+                ULONG *ul = (ULONG *)(data_start + entry->value);
+                for (j = 0; j < entry->count * 2; j++)
+                    ul[j] = ulong_bswap(ul[j]);
+            }
+            entry->value = ulong_bswap(entry->value);
+            break;
+
+        case IFD_DOUBLE:
+            {
+                ULONG j;
+                ULONGLONG *ull = (ULONGLONG *)(data_start + entry->value);
+                for (j = 0; j < entry->count; j++)
+                    ull[j] = ulonglong_bswap(ull[j]);
+            }
+            entry->value = ulong_bswap(entry->value);
+            break;
+
+        default:
+            assert(0);
+            break;
+        }
+
+        entry->id = ushort_bswap(entry->id);
+        entry->type = ushort_bswap(entry->type);
+        entry->count = ulong_bswap(entry->count);
+        data += sizeof(*entry);
+    }
+}
+
+struct test_data
+{
+    ULONG type, id;
+    int count; /* if VT_VECTOR */
+    LONGLONG value[13];
+    const char *string;
+    const WCHAR id_string[32];
+};
+
+static void compare_metadata(IWICMetadataReader *reader, const struct test_data *td, ULONG count)
+{
+    HRESULT hr;
+    IWICEnumMetadataItem *enumerator;
+    PROPVARIANT schema, id, value;
+    ULONG items_returned, i;
+
+    hr = IWICMetadataReader_GetEnumerator(reader, NULL);
+    ok(hr == E_INVALIDARG, "GetEnumerator error %#x\n", hr);
+
+    hr = IWICMetadataReader_GetEnumerator(reader, &enumerator);
+    ok(hr == S_OK, "GetEnumerator error %#x\n", hr);
+
+    PropVariantInit(&schema);
+    PropVariantInit(&id);
+    PropVariantInit(&value);
+
+    for (i = 0; i < count; i++)
+    {
+        hr = IWICEnumMetadataItem_Next(enumerator, 1, &schema, &id, &value, &items_returned);
+        ok(hr == S_OK, "Next error %#x\n", hr);
+        ok(items_returned == 1, "unexpected item count %u\n", items_returned);
+
+        ok(schema.vt == VT_EMPTY, "%u: unexpected vt: %u\n", i, schema.vt);
+        ok(id.vt == VT_UI2 || id.vt == VT_LPWSTR || id.vt == VT_EMPTY, "%u: unexpected vt: %u\n", i, id.vt);
+        if (id.vt == VT_UI2)
+            ok(U(id).uiVal == td[i].id, "%u: expected id %#x, got %#x\n", i, td[i].id, U(id).uiVal);
+        else if (id.vt == VT_LPWSTR)
+            ok(!lstrcmpW(td[i].id_string, U(id).pwszVal),
+               "%u: expected %s, got %s\n", i, wine_dbgstr_w(td[i].id_string), wine_dbgstr_w(U(id).pwszVal));
+
+        ok(value.vt == td[i].type, "%u: expected vt %#x, got %#x\n", i, td[i].type, value.vt);
+        if (value.vt & VT_VECTOR)
+        {
+            ULONG j;
+            switch (value.vt & ~VT_VECTOR)
+            {
+            case VT_I1:
+            case VT_UI1:
+                ok(td[i].count == U(value).caub.cElems, "%u: expected cElems %d, got %d\n", i, td[i].count, U(value).caub.cElems);
+                for (j = 0; j < U(value).caub.cElems; j++)
+                    ok(td[i].value[j] == U(value).caub.pElems[j], "%u: expected value[%d] %#x/%#x, got %#x\n", i, j, (ULONG)td[i].value[j], (ULONG)(td[i].value[j] >> 32), U(value).caub.pElems[j]);
+                break;
+            case VT_I2:
+            case VT_UI2:
+                ok(td[i].count == U(value).caui.cElems, "%u: expected cElems %d, got %d\n", i, td[i].count, U(value).caui.cElems);
+                for (j = 0; j < U(value).caui.cElems; j++)
+                    ok(td[i].value[j] == U(value).caui.pElems[j], "%u: expected value[%d] %#x/%#x, got %#x\n", i, j, (ULONG)td[i].value[j], (ULONG)(td[i].value[j] >> 32), U(value).caui.pElems[j]);
+                break;
+            case VT_I4:
+            case VT_UI4:
+            case VT_R4:
+                ok(td[i].count == U(value).caul.cElems, "%u: expected cElems %d, got %d\n", i, td[i].count, U(value).caul.cElems);
+                for (j = 0; j < U(value).caul.cElems; j++)
+                    ok(td[i].value[j] == U(value).caul.pElems[j], "%u: expected value[%d] %#x/%#x, got %#x\n", i, j, (ULONG)td[i].value[j], (ULONG)(td[i].value[j] >> 32), U(value).caul.pElems[j]);
+                break;
+            case VT_I8:
+            case VT_UI8:
+            case VT_R8:
+                ok(td[i].count == U(value).cauh.cElems, "%u: expected cElems %d, got %d\n", i, td[i].count, U(value).cauh.cElems);
+                for (j = 0; j < U(value).cauh.cElems; j++)
+                    ok(td[i].value[j] == U(value).cauh.pElems[j].QuadPart, "%u: expected value[%d] %08x/%08x, got %08x/%08x\n", i, j, (ULONG)td[i].value[j], (ULONG)(td[i].value[j] >> 32), U(value).cauh.pElems[j].u.LowPart, U(value).cauh.pElems[j].u.HighPart);
+                break;
+            case VT_LPSTR:
+                ok(td[i].count == U(value).calpstr.cElems, "%u: expected cElems %d, got %d\n", i, td[i].count, U(value).caub.cElems);
+                for (j = 0; j < U(value).calpstr.cElems; j++)
+                    trace("%u: %s\n", j, U(value).calpstr.pElems[j]);
+                /* fall through to not handled message */
+            default:
+                ok(0, "%u: array of type %d is not handled\n", i, value.vt & ~VT_VECTOR);
+                break;
+            }
+        }
+        else if (value.vt == VT_LPSTR)
+        {
+            ok(td[i].count == strlen(U(value).pszVal) ||
+               broken(td[i].count == strlen(U(value).pszVal) + 1), /* before Win7 */
+               "%u: expected count %d, got %d\n", i, td[i].count, lstrlenA(U(value).pszVal));
+            if (td[i].count == strlen(U(value).pszVal))
+                ok(!strcmp(td[i].string, U(value).pszVal),
+                   "%u: expected %s, got %s\n", i, td[i].string, U(value).pszVal);
+        }
+        else if (value.vt == VT_BLOB)
+        {
+            ok(td[i].count == U(value).blob.cbSize, "%u: expected count %d, got %d\n", i, td[i].count, U(value).blob.cbSize);
+            ok(!memcmp(td[i].string, U(value).blob.pBlobData, td[i].count), "%u: expected %s, got %s\n", i, td[i].string, U(value).blob.pBlobData);
+        }
+        else
+            ok(U(value).uhVal.QuadPart == td[i].value[0], "%u: expected value %#x/%#x got %#x/%#x\n",
+               i, (UINT)td[i].value[0], (UINT)(td[i].value[0] >> 32), U(value).uhVal.u.LowPart, U(value).uhVal.u.HighPart);
+
+        PropVariantClear(&schema);
+        PropVariantClear(&id);
+        PropVariantClear(&value);
+    }
+
+    hr = IWICEnumMetadataItem_Next(enumerator, 1, &schema, &id, &value, &items_returned);
+    ok(hr == S_FALSE, "Next should fail\n");
+    ok(items_returned == 0, "unexpected item count %u\n", items_returned);
+
+    IWICEnumMetadataItem_Release(enumerator);
+}
+
+static void test_metadata_IFD(void)
+{
+    static const struct test_data td[28] =
+    {
+        { VT_UI2, 0xfe, 0, { 1 } },
+        { VT_UI4, 0x100, 0, { 222 } },
+        { VT_UI4, 0x101, 0, { 333 } },
+        { VT_UI2, 0x102, 0, { 24 } },
+        { VT_UI4, 0x103, 0, { 32773 } },
+        { VT_UI8, 0x11a, 0, { ((LONGLONG)3 << 32) | 900 } },
+        { VT_UI1, 0xf001, 0, { 0x44 } },
+        { VT_UI1|VT_VECTOR, 0xf002, 4, { 0x44, 0x33, 0x22, 0x11 } },
+        { VT_I1, 0xf003, 0, { 0x44 } },
+        { VT_I2, 0xf004, 0, { 0x3344 } },
+        { VT_I2|VT_VECTOR, 0xf005, 2, { 0x3344, 0x1122 } },
+        { VT_I4, 0xf006, 0, { 0x11223344 } },
+        { VT_R4, 0xf007, 0, { 0x11223344 } },
+        { VT_R8, 0xf008, 0, { ((LONGLONG)0x41d26580 << 32) | 0xb486522c } },
+        { VT_I8, 0xf009, 0, { ((LONGLONG)0x5a6b7c8d << 32) | 0x1a2b3c4d } },
+        { VT_UI1|VT_VECTOR, 0xf00a, 13, { 'H','e','l','l','o',' ','W','o','r','l','d','!',0 } },
+        { VT_I2|VT_VECTOR, 0xf00b, 4, { 0x0101, 0x0202, 0x0303, 0x0404 } },
+        { VT_I4|VT_VECTOR, 0xf00c, 2, { 0x11223344, 0x55667788 } },
+        { VT_R4|VT_VECTOR, 0xf00d, 2, { 0x449a522b, 0x4608f5ba } },
+        { VT_LPSTR, 0xf00e, 12, { 0 }, "Hello World!" },
+        { VT_LPSTR, 0xf00f, 4, { 0 }, "abcd" },
+        { VT_BLOB, 0xf010, 13, { 0 }, "Hello World!" },
+        { VT_BLOB, 0xf011, 4, { 0 }, "abcd" },
+        { VT_UI1, 0xf012, 0, { 0x44 } },
+        { VT_UI2, 0xf013, 0, { 0x3344 } },
+        { VT_UI4, 0xf014, 0, { 0x11223344 } },
+        { VT_R4, 0xf015, 0, { 0x11223344 } },
+        { VT_I8|VT_VECTOR, 0xf016, 3,
+          { ((LONGLONG)0x05060708 << 32) | 0x01020304,
+            ((LONGLONG)0x50607080 << 32) | 0x10203040,
+            ((LONGLONG)0x55667788 << 32) | 0x11223344 } },
+    };
+    HRESULT hr;
+    IWICMetadataReader *reader;
+    IWICMetadataBlockReader *blockreader;
+    PROPVARIANT schema, id, value;
+    ULONG count;
+    GUID format;
+    char *IFD_data_swapped;
+#ifdef WORDS_BIGENDIAN
+    DWORD persist_options = WICPersistOptionsBigEndian;
+#else
+    DWORD persist_options = WICPersistOptionsLittleEndian;
+#endif
+
+    hr = CoCreateInstance(&CLSID_WICIfdMetadataReader, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICMetadataReader, (void**)&reader);
+    ok(hr == S_OK, "CoCreateInstance error %#x\n", hr);
+
+    hr = IWICMetadataReader_GetCount(reader, NULL);
+    ok(hr == E_INVALIDARG, "GetCount error %#x\n", hr);
+
+    hr = IWICMetadataReader_GetCount(reader, &count);
+    ok(hr == S_OK, "GetCount error %#x\n", hr);
+    ok(count == 0, "unexpected count %u\n", count);
+
+    load_stream((IUnknown*)reader, (const char *)&IFD_data, sizeof(IFD_data), persist_options);
+
+    hr = IWICMetadataReader_GetCount(reader, &count);
+    ok(hr == S_OK, "GetCount error %#x\n", hr);
+    ok(count == sizeof(td)/sizeof(td[0]), "unexpected count %u\n", count);
+
+    compare_metadata(reader, td, count);
+
+    /* test IFD data with different endianness */
+    if (persist_options == WICPersistOptionsLittleEndian)
+        persist_options = WICPersistOptionsBigEndian;
+    else
+        persist_options = WICPersistOptionsLittleEndian;
+
+    IFD_data_swapped = HeapAlloc(GetProcessHeap(), 0, sizeof(IFD_data));
+    memcpy(IFD_data_swapped, &IFD_data, sizeof(IFD_data));
+    byte_swap_ifd_data(IFD_data_swapped);
+    load_stream((IUnknown *)reader, IFD_data_swapped, sizeof(IFD_data), persist_options);
+    hr = IWICMetadataReader_GetCount(reader, &count);
+    ok(hr == S_OK, "GetCount error %#x\n", hr);
+    ok(count == sizeof(td)/sizeof(td[0]), "unexpected count %u\n", count);
+    compare_metadata(reader, td, count);
+    HeapFree(GetProcessHeap(), 0, IFD_data_swapped);
+
+    hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+    ok(hr == S_OK, "GetMetadataFormat error %#x\n", hr);
+    ok(IsEqualGUID(&format, &GUID_MetadataFormatIfd), "unexpected format %s\n", debugstr_guid(&format));
+
+    hr = IWICMetadataReader_GetMetadataFormat(reader, NULL);
+    ok(hr == E_INVALIDARG, "GetMetadataFormat should fail\n");
+
+    hr = IWICMetadataReader_GetValueByIndex(reader, 0, NULL, NULL, NULL);
+    ok(hr == S_OK, "GetValueByIndex error %#x\n", hr);
+
+    PropVariantInit(&schema);
+    PropVariantInit(&id);
+    PropVariantInit(&value);
+
+    hr = IWICMetadataReader_GetValueByIndex(reader, count - 1, NULL, NULL, NULL);
+    ok(hr == S_OK, "GetValueByIndex error %#x\n", hr);
+
+    hr = IWICMetadataReader_GetValueByIndex(reader, 0, &schema, NULL, NULL);
+    ok(hr == S_OK, "GetValueByIndex error %#x\n", hr);
+    ok(schema.vt == VT_EMPTY, "unexpected vt: %u\n", schema.vt);
+
+    hr = IWICMetadataReader_GetValueByIndex(reader, count - 1, &schema, NULL, NULL);
+    ok(hr == S_OK, "GetValueByIndex error %#x\n", hr);
+    ok(schema.vt == VT_EMPTY, "unexpected vt: %u\n", schema.vt);
+
+    hr = IWICMetadataReader_GetValueByIndex(reader, 0, NULL, &id, NULL);
+    ok(hr == S_OK, "GetValueByIndex error %#x\n", hr);
+    ok(id.vt == VT_UI2, "unexpected vt: %u\n", id.vt);
+    ok(U(id).uiVal == 0xfe, "unexpected id: %#x\n", U(id).uiVal);
+    PropVariantClear(&id);
+
+    hr = IWICMetadataReader_GetValueByIndex(reader, 0, NULL, NULL, &value);
+    ok(hr == S_OK, "GetValueByIndex error %#x\n", hr);
+    ok(value.vt == VT_UI2, "unexpected vt: %u\n", value.vt);
+    ok(U(value).uiVal == 1, "unexpected id: %#x\n", U(value).uiVal);
+    PropVariantClear(&value);
+
+    hr = IWICMetadataReader_GetValueByIndex(reader, count, &schema, NULL, NULL);
+    ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+
+    PropVariantInit(&schema);
+    PropVariantInit(&id);
+    PropVariantInit(&value);
+
+    hr = IWICMetadataReader_GetValue(reader, &schema, &id, &value);
+    ok(hr == WINCODEC_ERR_PROPERTYNOTFOUND, "expected WINCODEC_ERR_PROPERTYNOTFOUND, got %#x\n", hr);
+
+    hr = IWICMetadataReader_GetValue(reader, NULL, &id, NULL);
+    ok(hr == WINCODEC_ERR_PROPERTYNOTFOUND, "expected WINCODEC_ERR_PROPERTYNOTFOUND, got %#x\n", hr);
+
+    hr = IWICMetadataReader_GetValue(reader, &schema, NULL, NULL);
+    ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+
+    hr = IWICMetadataReader_GetValue(reader, &schema, &id, NULL);
+    ok(hr == WINCODEC_ERR_PROPERTYNOTFOUND, "expected WINCODEC_ERR_PROPERTYNOTFOUND, got %#x\n", hr);
+
+    hr = IWICMetadataReader_GetValue(reader, &schema, NULL, &value);
+    ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+
+    id.vt = VT_UI2;
+    U(id).uiVal = 0xf00e;
+    hr = IWICMetadataReader_GetValue(reader, NULL, &id, NULL);
+    ok(hr == S_OK, "GetValue error %#x\n", hr);
+
+    /* schema is ignored by Ifd metadata reader */
+    schema.vt = VT_UI4;
+    U(schema).ulVal = 0xdeadbeef;
+    hr = IWICMetadataReader_GetValue(reader, &schema, &id, &value);
+    ok(hr == S_OK, "GetValue error %#x\n", hr);
+    ok(value.vt == VT_LPSTR, "unexpected vt: %i\n", id.vt);
+    ok(!strcmp(U(value).pszVal, "Hello World!"), "unexpected value: %s\n", U(value).pszVal);
+    PropVariantClear(&value);
+
+    hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
+    ok(hr == S_OK, "GetValue error %#x\n", hr);
+    ok(value.vt == VT_LPSTR, "unexpected vt: %i\n", id.vt);
+    ok(!strcmp(U(value).pszVal, "Hello World!"), "unexpected value: %s\n", U(value).pszVal);
+    PropVariantClear(&value);
+
+    hr = IWICMetadataReader_QueryInterface(reader, &IID_IWICMetadataBlockReader, (void**)&blockreader);
+    ok(hr == E_NOINTERFACE, "QueryInterface failed, hr=%x\n", hr);
+
+    if (SUCCEEDED(hr))
+        IWICMetadataBlockReader_Release(blockreader);
+
+    IWICMetadataReader_Release(reader);
+}
+
+static void test_metadata_Exif(void)
+{
+    HRESULT hr;
+    IWICMetadataReader *reader;
+    IWICMetadataBlockReader *blockreader;
+    UINT count=0;
+
+    hr = CoCreateInstance(&CLSID_WICExifMetadataReader, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICMetadataReader, (void**)&reader);
+    todo_wine ok(hr == S_OK, "CoCreateInstance error %#x\n", hr);
+    if (FAILED(hr)) return;
+
+    hr = IWICMetadataReader_GetCount(reader, NULL);
+    ok(hr == E_INVALIDARG, "GetCount error %#x\n", hr);
+
+    hr = IWICMetadataReader_GetCount(reader, &count);
+    ok(hr == S_OK, "GetCount error %#x\n", hr);
+    ok(count == 0, "unexpected count %u\n", count);
+
+    hr = IWICMetadataReader_QueryInterface(reader, &IID_IWICMetadataBlockReader, (void**)&blockreader);
+    ok(hr == E_NOINTERFACE, "QueryInterface failed, hr=%x\n", hr);
+
+    if (SUCCEEDED(hr))
+        IWICMetadataBlockReader_Release(blockreader);
+
+    IWICMetadataReader_Release(reader);
+}
+
+static void test_create_reader(void)
+{
+    HRESULT hr;
+    IWICComponentFactory *factory;
+    IStream *stream;
+    IWICMetadataReader *reader;
+    UINT count=0;
+    GUID format;
+
+    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICComponentFactory, (void**)&factory);
+    ok(hr == S_OK, "CoCreateInstance failed, hr=%x\n", hr);
+
+    stream = create_stream(metadata_tEXt, sizeof(metadata_tEXt));
+
+    hr = IWICComponentFactory_CreateMetadataReaderFromContainer(factory,
+        &GUID_ContainerFormatPng, NULL, WICPersistOptionsDefault,
+        stream, &reader);
+todo_wine
+    ok(hr == S_OK, "CreateMetadataReaderFromContainer failed, hr=%x\n", hr);
+    if (FAILED(hr)) return;
+
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICMetadataReader_GetCount(reader, &count);
+        ok(hr == S_OK, "GetCount failed, hr=%x\n", hr);
+        ok(count == 1, "unexpected count %i\n", count);
+
+        hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+        ok(hr == S_OK, "GetMetadataFormat failed, hr=%x\n", hr);
+        ok(IsEqualGUID(&format, &GUID_MetadataFormatChunktEXt), "unexpected format %s\n", debugstr_guid(&format));
+
+        IWICMetadataReader_Release(reader);
+    }
+
+    hr = IWICComponentFactory_CreateMetadataReaderFromContainer(factory,
+        &GUID_ContainerFormatWmp, NULL, WICPersistOptionsDefault,
+        stream, &reader);
+    ok(hr == S_OK, "CreateMetadataReaderFromContainer failed, hr=%x\n", hr);
+
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICMetadataReader_GetCount(reader, &count);
+        ok(hr == S_OK, "GetCount failed, hr=%x\n", hr);
+        ok(count == 1, "unexpected count %i\n", count);
+
+        hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+        ok(hr == S_OK, "GetMetadataFormat failed, hr=%x\n", hr);
+        ok(IsEqualGUID(&format, &GUID_MetadataFormatUnknown), "unexpected format %s\n", debugstr_guid(&format));
+
+        IWICMetadataReader_Release(reader);
+    }
+
+    IStream_Release(stream);
+
+    IWICComponentFactory_Release(factory);
+}
+
+static void test_metadata_png(void)
+{
+    static const struct test_data td[6] =
+    {
+        { VT_UI2, 0, 0, { 2005 }, NULL, { 'Y','e','a','r',0 } },
+        { VT_UI1, 0, 0, { 6 }, NULL, { 'M','o','n','t','h',0 } },
+        { VT_UI1, 0, 0, { 3 }, NULL, { 'D','a','y',0 } },
+        { VT_UI1, 0, 0, { 15 }, NULL, { 'H','o','u','r',0 } },
+        { VT_UI1, 0, 0, { 7 }, NULL, { 'M','i','n','u','t','e',0 } },
+        { VT_UI1, 0, 0, { 45 }, NULL, { 'S','e','c','o','n','d',0 } }
+    };
+    IStream *stream;
+    IWICBitmapDecoder *decoder;
+    IWICBitmapFrameDecode *frame;
+    IWICMetadataBlockReader *blockreader;
+    IWICMetadataReader *reader;
+    GUID containerformat;
+    HRESULT hr;
+    UINT count;
+
+    hr = CoCreateInstance(&CLSID_WICPngDecoder, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICBitmapDecoder, (void**)&decoder);
+    ok(hr == S_OK, "CoCreateInstance failed, hr=%x\n", hr);
+
+    if (FAILED(hr)) return;
+
+    stream = create_stream(pngimage, sizeof(pngimage));
+
+    hr = IWICBitmapDecoder_Initialize(decoder, stream, WICDecodeMetadataCacheOnLoad);
+    ok(hr == S_OK, "Initialize failed, hr=%x\n", hr);
+
+    hr = IWICBitmapDecoder_QueryInterface(decoder, &IID_IWICMetadataBlockReader, (void**)&blockreader);
+    ok(hr == E_NOINTERFACE, "QueryInterface failed, hr=%x\n", hr);
+
+    hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
+    ok(hr == S_OK, "GetFrame failed, hr=%x\n", hr);
+
+    hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void**)&blockreader);
+    ok(hr == S_OK, "QueryInterface failed, hr=%x\n", hr);
+
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICMetadataBlockReader_GetContainerFormat(blockreader, NULL);
+        ok(hr == E_INVALIDARG, "GetContainerFormat failed, hr=%x\n", hr);
+
+        hr = IWICMetadataBlockReader_GetContainerFormat(blockreader, &containerformat);
+        ok(hr == S_OK, "GetContainerFormat failed, hr=%x\n", hr);
+        ok(IsEqualGUID(&containerformat, &GUID_ContainerFormatPng), "unexpected container format\n");
+
+        hr = IWICMetadataBlockReader_GetCount(blockreader, NULL);
+        todo_wine ok(hr == E_INVALIDARG, "GetCount failed, hr=%x\n", hr);
+
+        hr = IWICMetadataBlockReader_GetCount(blockreader, &count);
+        todo_wine ok(hr == S_OK, "GetCount failed, hr=%x\n", hr);
+        todo_wine ok(count == 1, "unexpected count %d\n", count);
+
+        if (0)
+        {
+            /* Crashes on Windows XP */
+            hr = IWICMetadataBlockReader_GetReaderByIndex(blockreader, 0, NULL);
+            ok(hr == E_INVALIDARG, "GetReaderByIndex failed, hr=%x\n", hr);
+        }
+
+        hr = IWICMetadataBlockReader_GetReaderByIndex(blockreader, 0, &reader);
+        todo_wine ok(hr == S_OK, "GetReaderByIndex failed, hr=%x\n", hr);
+
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICMetadataReader_GetMetadataFormat(reader, &containerformat);
+            ok(IsEqualGUID(&containerformat, &GUID_MetadataFormatChunktIME) ||
+               broken(IsEqualGUID(&containerformat, &GUID_MetadataFormatUnknown)) /* Windows XP */,
+               "unexpected container format\n");
+
+            hr = IWICMetadataReader_GetCount(reader, &count);
+            ok(hr == S_OK, "GetCount error %#x\n", hr);
+            ok(count == 6 || broken(count == 1) /* XP */, "expected 6, got %u\n", count);
+            if (count == 6)
+                compare_metadata(reader, td, count);
+
+            IWICMetadataReader_Release(reader);
+        }
+
+        hr = IWICMetadataBlockReader_GetReaderByIndex(blockreader, 1, &reader);
+        todo_wine ok(hr == WINCODEC_ERR_VALUEOUTOFRANGE, "GetReaderByIndex failed, hr=%x\n", hr);
+
+        IWICMetadataBlockReader_Release(blockreader);
+    }
+
+    IWICBitmapFrameDecode_Release(frame);
+
+    IWICBitmapDecoder_Release(decoder);
+
+    IStream_Release(stream);
+}
+
+static void test_metadata_gif(void)
+{
+    static const struct test_data gif_LSD[9] =
+    {
+        { VT_UI1|VT_VECTOR, 0, 6, {'G','I','F','8','7','a'}, NULL, { 'S','i','g','n','a','t','u','r','e',0 } },
+        { VT_UI2, 0, 0, { 1 }, NULL, { 'W','i','d','t','h',0 } },
+        { VT_UI2, 0, 0, { 1 }, NULL, { 'H','e','i','g','h','t',0 } },
+        { VT_BOOL, 0, 0, { 1 }, NULL, { 'G','l','o','b','a','l','C','o','l','o','r','T','a','b','l','e','F','l','a','g',0 } },
+        { VT_UI1, 0, 0, { 0 }, NULL, { 'C','o','l','o','r','R','e','s','o','l','u','t','i','o','n',0 } },
+        { VT_BOOL, 0, 0, { 0 }, NULL, { 'S','o','r','t','F','l','a','g',0 } },
+        { VT_UI1, 0, 0, { 0 }, NULL, { 'G','l','o','b','a','l','C','o','l','o','r','T','a','b','l','e','S','i','z','e',0 } },
+        { VT_UI1, 0, 0, { 0 }, NULL, { 'B','a','c','k','g','r','o','u','n','d','C','o','l','o','r','I','n','d','e','x',0 } },
+        { VT_UI1, 0, 0, { 0 }, NULL, { 'P','i','x','e','l','A','s','p','e','c','t','R','a','t','i','o',0 } }
+    };
+    static const struct test_data gif_IMD[8] =
+    {
+        { VT_UI2, 0, 0, { 0 }, NULL, { 'L','e','f','t',0 } },
+        { VT_UI2, 0, 0, { 0 }, NULL, { 'T','o','p',0 } },
+        { VT_UI2, 0, 0, { 1 }, NULL, { 'W','i','d','t','h',0 } },
+        { VT_UI2, 0, 0, { 1 }, NULL, { 'H','e','i','g','h','t',0 } },
+        { VT_BOOL, 0, 0, { 0 }, NULL, { 'L','o','c','a','l','C','o','l','o','r','T','a','b','l','e','F','l','a','g',0 } },
+        { VT_BOOL, 0, 0, { 0 }, NULL, { 'I','n','t','e','r','l','a','c','e','F','l','a','g',0 } },
+        { VT_BOOL, 0, 0, { 0 }, NULL, { 'S','o','r','t','F','l','a','g',0 } },
+        { VT_UI1, 0, 0, { 0 }, NULL, { 'L','o','c','a','l','C','o','l','o','r','T','a','b','l','e','S','i','z','e',0 } }
+    };
+    static const struct test_data animated_gif_LSD[9] =
+    {
+        { VT_UI1|VT_VECTOR, 0, 6, {'G','I','F','8','9','a'}, NULL, { 'S','i','g','n','a','t','u','r','e',0 } },
+        { VT_UI2, 0, 0, { 1 }, NULL, { 'W','i','d','t','h',0 } },
+        { VT_UI2, 0, 0, { 1 }, NULL, { 'H','e','i','g','h','t',0 } },
+        { VT_BOOL, 0, 0, { 1 }, NULL, { 'G','l','o','b','a','l','C','o','l','o','r','T','a','b','l','e','F','l','a','g',0 } },
+        { VT_UI1, 0, 0, { 2 }, NULL, { 'C','o','l','o','r','R','e','s','o','l','u','t','i','o','n',0 } },
+        { VT_BOOL, 0, 0, { 0 }, NULL, { 'S','o','r','t','F','l','a','g',0 } },
+        { VT_UI1, 0, 0, { 1 }, NULL, { 'G','l','o','b','a','l','C','o','l','o','r','T','a','b','l','e','S','i','z','e',0 } },
+        { VT_UI1, 0, 0, { 0 }, NULL, { 'B','a','c','k','g','r','o','u','n','d','C','o','l','o','r','I','n','d','e','x',0 } },
+        { VT_UI1, 0, 0, { 0 }, NULL, { 'P','i','x','e','l','A','s','p','e','c','t','R','a','t','i','o',0 } }
+    };
+    static const struct test_data animated_gif_IMD[8] =
+    {
+        { VT_UI2, 0, 0, { 0 }, NULL, { 'L','e','f','t',0 } },
+        { VT_UI2, 0, 0, { 0 }, NULL, { 'T','o','p',0 } },
+        { VT_UI2, 0, 0, { 1 }, NULL, { 'W','i','d','t','h',0 } },
+        { VT_UI2, 0, 0, { 1 }, NULL, { 'H','e','i','g','h','t',0 } },
+        { VT_BOOL, 0, 0, { 1 }, NULL, { 'L','o','c','a','l','C','o','l','o','r','T','a','b','l','e','F','l','a','g',0 } },
+        { VT_BOOL, 0, 0, { 0 }, NULL, { 'I','n','t','e','r','l','a','c','e','F','l','a','g',0 } },
+        { VT_BOOL, 0, 0, { 0 }, NULL, { 'S','o','r','t','F','l','a','g',0 } },
+        { VT_UI1, 0, 0, { 1 }, NULL, { 'L','o','c','a','l','C','o','l','o','r','T','a','b','l','e','S','i','z','e',0 } }
+    };
+    static const struct test_data animated_gif_GCE[5] =
+    {
+        { VT_UI1, 0, 0, { 0 }, NULL, { 'D','i','s','p','o','s','a','l',0 } },
+        { VT_BOOL, 0, 0, { 0 }, NULL, { 'U','s','e','r','I','n','p','u','t','F','l','a','g',0 } },
+        { VT_BOOL, 0, 0, { 1 }, NULL, { 'T','r','a','n','s','p','a','r','e','n','c','y','F','l','a','g',0 } },
+        { VT_UI2, 0, 0, { 10 }, NULL, { 'D','e','l','a','y',0 } },
+        { VT_UI1, 0, 0, { 1 }, NULL, { 'T','r','a','n','s','p','a','r','e','n','t','C','o','l','o','r','I','n','d','e','x',0 } }
+    };
+    static const struct test_data animated_gif_APE[2] =
+    {
+        { VT_UI1|VT_VECTOR, 0, 11, { 'A','N','I','M','E','X','T','S','1','.','0' }, NULL, { 'A','p','p','l','i','c','a','t','i','o','n',0 } },
+        { VT_UI1|VT_VECTOR, 0, 4, { 0x03,0x01,0x05,0x00 }, NULL, { 'D','a','t','a',0 } }
+    };
+    static const struct test_data animated_gif_comment_1[1] =
+    {
+        { VT_LPSTR, 0, 12, { 0 }, "Hello World!", { 'T','e','x','t','E','n','t','r','y',0 } }
+    };
+    static const struct test_data animated_gif_comment_2[1] =
+    {
+        { VT_LPSTR, 0, 8, { 0 }, "image #1", { 'T','e','x','t','E','n','t','r','y',0 } }
+    };
+    static const struct test_data animated_gif_plain_1[1] =
+    {
+        { VT_BLOB, 0, 17, { 0 }, "\x21\x01\x0d\x61nimation.gif" }
+    };
+    static const struct test_data animated_gif_plain_2[1] =
+    {
+        { VT_BLOB, 0, 16, { 0 }, "\x21\x01\x0cplaintext #1" }
+    };
+    IStream *stream;
+    IWICBitmapDecoder *decoder;
+    IWICBitmapFrameDecode *frame;
+    IWICMetadataBlockReader *blockreader;
+    IWICMetadataReader *reader;
+    GUID format;
+    HRESULT hr;
+    UINT count;
+
+    /* 1x1 pixel gif */
+    stream = create_stream(gifimage, sizeof(gifimage));
+
+    hr = CoCreateInstance(&CLSID_WICGifDecoder, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_IWICBitmapDecoder, (void **)&decoder);
+    ok(hr == S_OK, "CoCreateInstance error %#x\n", hr);
+    hr = IWICBitmapDecoder_Initialize(decoder, stream, WICDecodeMetadataCacheOnLoad);
+    ok(hr == S_OK, "Initialize error %#x\n", hr);
+
+    IStream_Release(stream);
+
+    /* global metadata block */
+    hr = IWICBitmapDecoder_QueryInterface(decoder, &IID_IWICMetadataBlockReader, (void **)&blockreader);
+    ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* before Win7 */, "QueryInterface error %#x\n", hr);
+
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICMetadataBlockReader_GetContainerFormat(blockreader, &format);
+        ok(hr == S_OK, "GetContainerFormat error %#x\n", hr);
+        ok(IsEqualGUID(&format, &GUID_ContainerFormatGif),
+           "wrong container format %s\n", debugstr_guid(&format));
+
+        hr = IWICMetadataBlockReader_GetCount(blockreader, &count);
+        ok(hr == S_OK, "GetCount error %#x\n", hr);
+        ok(count == 1, "expected 1, got %u\n", count);
+
+        hr = IWICMetadataBlockReader_GetReaderByIndex(blockreader, 0, &reader);
+        ok(hr == S_OK, "GetReaderByIndex error %#x\n", hr);
+
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+            ok(IsEqualGUID(&format, &GUID_MetadataFormatLSD), /* Logical Screen Descriptor */
+               "wrong metadata format %s\n", debugstr_guid(&format));
+
+            hr = IWICMetadataReader_GetCount(reader, &count);
+            ok(hr == S_OK, "GetCount error %#x\n", hr);
+            ok(count == sizeof(gif_LSD)/sizeof(gif_LSD[0]), "unexpected count %u\n", count);
+
+            compare_metadata(reader, gif_LSD, count);
+
+            IWICMetadataReader_Release(reader);
+        }
+
+        hr = IWICMetadataBlockReader_GetReaderByIndex(blockreader, 1, &reader);
+        ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+
+        IWICMetadataBlockReader_Release(blockreader);
+    }
+
+    /* frame metadata block */
+    hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
+    ok(hr == S_OK, "GetFrame error %#x\n", hr);
+
+    hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&blockreader);
+    ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* before Win7 */, "QueryInterface error %#x\n", hr);
+
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICMetadataBlockReader_GetContainerFormat(blockreader, NULL);
+        ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+
+        hr = IWICMetadataBlockReader_GetContainerFormat(blockreader, &format);
+        ok(hr == S_OK, "GetContainerFormat error %#x\n", hr);
+        ok(IsEqualGUID(&format, &GUID_ContainerFormatGif),
+           "wrong container format %s\n", debugstr_guid(&format));
+
+        hr = IWICMetadataBlockReader_GetCount(blockreader, NULL);
+        ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+
+        hr = IWICMetadataBlockReader_GetCount(blockreader, &count);
+        ok(hr == S_OK, "GetCount error %#x\n", hr);
+        ok(count == 1, "expected 1, got %u\n", count);
+
+        hr = IWICMetadataBlockReader_GetReaderByIndex(blockreader, 0, &reader);
+        ok(hr == S_OK, "GetReaderByIndex error %#x\n", hr);
+
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+            ok(IsEqualGUID(&format, &GUID_MetadataFormatIMD), /* Image Descriptor */
+               "wrong metadata format %s\n", debugstr_guid(&format));
+
+            hr = IWICMetadataReader_GetCount(reader, &count);
+            ok(hr == S_OK, "GetCount error %#x\n", hr);
+            ok(count == sizeof(gif_IMD)/sizeof(gif_IMD[0]), "unexpected count %u\n", count);
+
+            compare_metadata(reader, gif_IMD, count);
+
+            IWICMetadataReader_Release(reader);
+        }
+
+        hr = IWICMetadataBlockReader_GetReaderByIndex(blockreader, 1, &reader);
+        ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+
+        IWICMetadataBlockReader_Release(blockreader);
+    }
+
+    IWICBitmapFrameDecode_Release(frame);
+    IWICBitmapDecoder_Release(decoder);
+
+    /* 1x1 pixel gif, 2 frames */
+    stream = create_stream(animatedgif, sizeof(animatedgif));
+
+    hr = CoCreateInstance(&CLSID_WICGifDecoder, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_IWICBitmapDecoder, (void **)&decoder);
+    ok(hr == S_OK, "CoCreateInstance error %#x\n", hr);
+    hr = IWICBitmapDecoder_Initialize(decoder, stream, WICDecodeMetadataCacheOnLoad);
+    ok(hr == S_OK, "Initialize error %#x\n", hr);
+
+    IStream_Release(stream);
+
+    /* global metadata block */
+    hr = IWICBitmapDecoder_QueryInterface(decoder, &IID_IWICMetadataBlockReader, (void **)&blockreader);
+    ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* before Win7 */, "QueryInterface error %#x\n", hr);
+
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICMetadataBlockReader_GetContainerFormat(blockreader, &format);
+        ok(hr == S_OK, "GetContainerFormat error %#x\n", hr);
+        ok(IsEqualGUID(&format, &GUID_ContainerFormatGif),
+           "wrong container format %s\n", debugstr_guid(&format));
+
+        hr = IWICMetadataBlockReader_GetCount(blockreader, &count);
+        ok(hr == S_OK, "GetCount error %#x\n", hr);
+        ok(count == 4, "expected 4, got %u\n", count);
+
+        hr = IWICMetadataBlockReader_GetReaderByIndex(blockreader, 0, &reader);
+        ok(hr == S_OK, "GetReaderByIndex error %#x\n", hr);
+
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+            ok(IsEqualGUID(&format, &GUID_MetadataFormatLSD), /* Logical Screen Descriptor */
+               "wrong metadata format %s\n", debugstr_guid(&format));
+
+            hr = IWICMetadataReader_GetCount(reader, &count);
+            ok(hr == S_OK, "GetCount error %#x\n", hr);
+            ok(count == sizeof(animated_gif_LSD)/sizeof(animated_gif_LSD[0]), "unexpected count %u\n", count);
+
+            compare_metadata(reader, animated_gif_LSD, count);
+
+            IWICMetadataReader_Release(reader);
+        }
+
+        hr = IWICMetadataBlockReader_GetReaderByIndex(blockreader, 1, &reader);
+        ok(hr == S_OK, "GetReaderByIndex error %#x\n", hr);
+
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+            ok(IsEqualGUID(&format, &GUID_MetadataFormatAPE), /* Application Extension */
+               "wrong metadata format %s\n", debugstr_guid(&format));
+
+            hr = IWICMetadataReader_GetCount(reader, &count);
+            ok(hr == S_OK, "GetCount error %#x\n", hr);
+            ok(count == sizeof(animated_gif_APE)/sizeof(animated_gif_APE[0]), "unexpected count %u\n", count);
+
+            compare_metadata(reader, animated_gif_APE, count);
+
+            IWICMetadataReader_Release(reader);
+        }
+
+        hr = IWICMetadataBlockReader_GetReaderByIndex(blockreader, 2, &reader);
+        ok(hr == S_OK, "GetReaderByIndex error %#x\n", hr);
+
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+            ok(IsEqualGUID(&format, &GUID_MetadataFormatGifComment), /* Comment Extension */
+               "wrong metadata format %s\n", debugstr_guid(&format));
+
+            hr = IWICMetadataReader_GetCount(reader, &count);
+            ok(hr == S_OK, "GetCount error %#x\n", hr);
+            ok(count == sizeof(animated_gif_comment_1)/sizeof(animated_gif_comment_1[0]), "unexpected count %u\n", count);
+
+            compare_metadata(reader, animated_gif_comment_1, count);
+
+            IWICMetadataReader_Release(reader);
+        }
+
+        hr = IWICMetadataBlockReader_GetReaderByIndex(blockreader, 3, &reader);
+        ok(hr == S_OK, "GetReaderByIndex error %#x\n", hr);
+
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+            ok(IsEqualGUID(&format, &GUID_MetadataFormatUnknown),
+               "wrong metadata format %s\n", debugstr_guid(&format));
+
+            hr = IWICMetadataReader_GetCount(reader, &count);
+            ok(hr == S_OK, "GetCount error %#x\n", hr);
+            ok(count == sizeof(animated_gif_plain_1)/sizeof(animated_gif_plain_1[0]), "unexpected count %u\n", count);
+
+            compare_metadata(reader, animated_gif_plain_1, count);
+
+            IWICMetadataReader_Release(reader);
+        }
+
+        hr = IWICMetadataBlockReader_GetReaderByIndex(blockreader, 4, &reader);
+        ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+
+        IWICMetadataBlockReader_Release(blockreader);
+    }
+
+    /* frame metadata block */
+    hr = IWICBitmapDecoder_GetFrame(decoder, 1, &frame);
+    ok(hr == S_OK, "GetFrame error %#x\n", hr);
+
+    hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&blockreader);
+    ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* before Win7 */, "QueryInterface error %#x\n", hr);
+
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICMetadataBlockReader_GetContainerFormat(blockreader, NULL);
+        ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+
+        hr = IWICMetadataBlockReader_GetContainerFormat(blockreader, &format);
+        ok(hr == S_OK, "GetContainerFormat error %#x\n", hr);
+        ok(IsEqualGUID(&format, &GUID_ContainerFormatGif),
+           "wrong container format %s\n", debugstr_guid(&format));
+
+        hr = IWICMetadataBlockReader_GetCount(blockreader, NULL);
+        ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+
+        hr = IWICMetadataBlockReader_GetCount(blockreader, &count);
+        ok(hr == S_OK, "GetCount error %#x\n", hr);
+        ok(count == 4, "expected 4, got %u\n", count);
+
+        hr = IWICMetadataBlockReader_GetReaderByIndex(blockreader, 0, &reader);
+        ok(hr == S_OK, "GetReaderByIndex error %#x\n", hr);
+
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+            ok(IsEqualGUID(&format, &GUID_MetadataFormatIMD), /* Image Descriptor */
+               "wrong metadata format %s\n", debugstr_guid(&format));
+
+            hr = IWICMetadataReader_GetCount(reader, &count);
+            ok(hr == S_OK, "GetCount error %#x\n", hr);
+            ok(count == sizeof(animated_gif_IMD)/sizeof(animated_gif_IMD[0]), "unexpected count %u\n", count);
+
+            compare_metadata(reader, animated_gif_IMD, count);
+
+            IWICMetadataReader_Release(reader);
+        }
+
+        hr = IWICMetadataBlockReader_GetReaderByIndex(blockreader, 1, &reader);
+        ok(hr == S_OK, "GetReaderByIndex error %#x\n", hr);
+
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+            ok(IsEqualGUID(&format, &GUID_MetadataFormatGifComment), /* Comment Extension */
+                "wrong metadata format %s\n", debugstr_guid(&format));
+
+            hr = IWICMetadataReader_GetCount(reader, &count);
+            ok(hr == S_OK, "GetCount error %#x\n", hr);
+            ok(count == sizeof(animated_gif_comment_2)/sizeof(animated_gif_comment_2[0]), "unexpected count %u\n", count);
+
+            if (count == 1)
+            compare_metadata(reader, animated_gif_comment_2, count);
+
+            IWICMetadataReader_Release(reader);
+        }
+
+        hr = IWICMetadataBlockReader_GetReaderByIndex(blockreader, 2, &reader);
+        ok(hr == S_OK, "GetReaderByIndex error %#x\n", hr);
+
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+            ok(IsEqualGUID(&format, &GUID_MetadataFormatUnknown),
+               "wrong metadata format %s\n", debugstr_guid(&format));
+
+            hr = IWICMetadataReader_GetCount(reader, &count);
+            ok(hr == S_OK, "GetCount error %#x\n", hr);
+            ok(count == sizeof(animated_gif_plain_2)/sizeof(animated_gif_plain_2[0]), "unexpected count %u\n", count);
+
+            compare_metadata(reader, animated_gif_plain_2, count);
+
+            IWICMetadataReader_Release(reader);
+        }
+
+        hr = IWICMetadataBlockReader_GetReaderByIndex(blockreader, 3, &reader);
+        ok(hr == S_OK, "GetReaderByIndex error %#x\n", hr);
+
+        if (SUCCEEDED(hr))
+        {
+            hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+            ok(IsEqualGUID(&format, &GUID_MetadataFormatGCE), /* Graphic Control Extension */
+               "wrong metadata format %s\n", debugstr_guid(&format));
+
+            hr = IWICMetadataReader_GetCount(reader, &count);
+            ok(hr == S_OK, "GetCount error %#x\n", hr);
+            ok(count == sizeof(animated_gif_GCE)/sizeof(animated_gif_GCE[0]), "unexpected count %u\n", count);
+
+            compare_metadata(reader, animated_gif_GCE, count);
+
+            IWICMetadataReader_Release(reader);
+        }
+
+        hr = IWICMetadataBlockReader_GetReaderByIndex(blockreader, 4, &reader);
+        ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+
+        IWICMetadataBlockReader_Release(blockreader);
+    }
+
+    IWICBitmapFrameDecode_Release(frame);
+    IWICBitmapDecoder_Release(decoder);
+}
+
+static void test_metadata_LSD(void)
+{
+    static const WCHAR LSD_name[] = {'L','o','g','i','c','a','l',' ','S','c','r','e','e','n',' ','D','e','s','c','r','i','p','t','o','r',' ','R','e','a','d','e','r',0};
+    static const char LSD_data[] = "hello world!\x1\x2\x3\x4\xab\x6\x7\x8\x9\xa\xb\xc\xd\xe\xf";
+    static const struct test_data td[9] =
+    {
+        { VT_UI1|VT_VECTOR, 0, 6, {'w','o','r','l','d','!'}, NULL, { 'S','i','g','n','a','t','u','r','e',0 } },
+        { VT_UI2, 0, 0, { 0x201 }, NULL, { 'W','i','d','t','h',0 } },
+        { VT_UI2, 0, 0, { 0x403 }, NULL, { 'H','e','i','g','h','t',0 } },
+        { VT_BOOL, 0, 0, { 1 }, NULL, { 'G','l','o','b','a','l','C','o','l','o','r','T','a','b','l','e','F','l','a','g',0 } },
+        { VT_UI1, 0, 0, { 2 }, NULL, { 'C','o','l','o','r','R','e','s','o','l','u','t','i','o','n',0 } },
+        { VT_BOOL, 0, 0, { 1 }, NULL, { 'S','o','r','t','F','l','a','g',0 } },
+        { VT_UI1, 0, 0, { 3 }, NULL, { 'G','l','o','b','a','l','C','o','l','o','r','T','a','b','l','e','S','i','z','e',0 } },
+        { VT_UI1, 0, 0, { 6 }, NULL, { 'B','a','c','k','g','r','o','u','n','d','C','o','l','o','r','I','n','d','e','x',0 } },
+        { VT_UI1, 0, 0, { 7 }, NULL, { 'P','i','x','e','l','A','s','p','e','c','t','R','a','t','i','o',0 } }
+    };
+    LARGE_INTEGER pos;
+    HRESULT hr;
+    IStream *stream;
+    IWICPersistStream *persist;
+    IWICMetadataReader *reader;
+    IWICMetadataHandlerInfo *info;
+    WCHAR name[64];
+    UINT count, dummy;
+    GUID format;
+    CLSID id;
+
+    hr = CoCreateInstance(&CLSID_WICLSDMetadataReader, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_IWICMetadataReader, (void **)&reader);
+    ok(hr == S_OK || broken(hr == E_NOINTERFACE || hr == REGDB_E_CLASSNOTREG) /* before Win7 */,
+       "CoCreateInstance error %#x\n", hr);
+
+    stream = create_stream(LSD_data, sizeof(LSD_data));
+
+    if (SUCCEEDED(hr))
+    {
+        pos.QuadPart = 6;
+        hr = IStream_Seek(stream, pos, SEEK_SET, NULL);
+        ok(hr == S_OK, "IStream_Seek error %#x\n", hr);
+
+        hr = IUnknown_QueryInterface(reader, &IID_IWICPersistStream, (void **)&persist);
+        ok(hr == S_OK, "QueryInterface error %#x\n", hr);
+
+        hr = IWICPersistStream_Load(persist, stream);
+        ok(hr == S_OK, "Load error %#x\n", hr);
+
+        IWICPersistStream_Release(persist);
+    }
+
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICMetadataReader_GetCount(reader, &count);
+        ok(hr == S_OK, "GetCount error %#x\n", hr);
+        ok(count == sizeof(td)/sizeof(td[0]), "unexpected count %u\n", count);
+
+        compare_metadata(reader, td, count);
+
+        hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+        ok(hr == S_OK, "GetMetadataFormat error %#x\n", hr);
+        ok(IsEqualGUID(&format, &GUID_MetadataFormatLSD), "wrong format %s\n", debugstr_guid(&format));
+
+        hr = IWICMetadataReader_GetMetadataHandlerInfo(reader, &info);
+        ok(hr == S_OK, "GetMetadataHandlerInfo error %#x\n", hr);
+
+        hr = IWICMetadataHandlerInfo_GetCLSID(info, &id);
+        ok(hr == S_OK, "GetCLSID error %#x\n", hr);
+        ok(IsEqualGUID(&id, &CLSID_WICLSDMetadataReader), "wrong CLSID %s\n", debugstr_guid(&id));
+
+        hr = IWICMetadataHandlerInfo_GetFriendlyName(info, 64, name, &dummy);
+        ok(hr == S_OK, "GetFriendlyName error %#x\n", hr);
+        ok(lstrcmpW(name, LSD_name) == 0, "wrong LSD reader name %s\n", wine_dbgstr_w(name));
+
+        IWICMetadataHandlerInfo_Release(info);
+        IWICMetadataReader_Release(reader);
+    }
+
+    IStream_Release(stream);
+}
+
+static void test_metadata_IMD(void)
+{
+    static const WCHAR IMD_name[] = {'I','m','a','g','e',' ','D','e','s','c','r','i','p','t','o','r',' ','R','e','a','d','e','r',0};
+    static const char IMD_data[] = "hello world!\x1\x2\x3\x4\x5\x6\x7\x8\xed\xa\xb\xc\xd\xe\xf";
+    static const struct test_data td[8] =
+    {
+        { VT_UI2, 0, 0, { 0x201 }, NULL, { 'L','e','f','t',0 } },
+        { VT_UI2, 0, 0, { 0x403 }, NULL, { 'T','o','p',0 } },
+        { VT_UI2, 0, 0, { 0x605 }, NULL, { 'W','i','d','t','h',0 } },
+        { VT_UI2, 0, 0, { 0x807 }, NULL, { 'H','e','i','g','h','t',0 } },
+        { VT_BOOL, 0, 0, { 1 }, NULL, { 'L','o','c','a','l','C','o','l','o','r','T','a','b','l','e','F','l','a','g',0 } },
+        { VT_BOOL, 0, 0, { 1 }, NULL, { 'I','n','t','e','r','l','a','c','e','F','l','a','g',0 } },
+        { VT_BOOL, 0, 0, { 1 }, NULL, { 'S','o','r','t','F','l','a','g',0 } },
+        { VT_UI1, 0, 0, { 5 }, NULL, { 'L','o','c','a','l','C','o','l','o','r','T','a','b','l','e','S','i','z','e',0 } }
+    };
+    LARGE_INTEGER pos;
+    HRESULT hr;
+    IStream *stream;
+    IWICPersistStream *persist;
+    IWICMetadataReader *reader;
+    IWICMetadataHandlerInfo *info;
+    WCHAR name[64];
+    UINT count, dummy;
+    GUID format;
+    CLSID id;
+
+    hr = CoCreateInstance(&CLSID_WICIMDMetadataReader, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_IWICMetadataReader, (void **)&reader);
+    ok(hr == S_OK || broken(hr == E_NOINTERFACE || hr == REGDB_E_CLASSNOTREG) /* before Win7 */,
+       "CoCreateInstance error %#x\n", hr);
+
+    stream = create_stream(IMD_data, sizeof(IMD_data));
+
+    if (SUCCEEDED(hr))
+    {
+        pos.QuadPart = 12;
+        hr = IStream_Seek(stream, pos, SEEK_SET, NULL);
+        ok(hr == S_OK, "IStream_Seek error %#x\n", hr);
+
+        hr = IUnknown_QueryInterface(reader, &IID_IWICPersistStream, (void **)&persist);
+        ok(hr == S_OK, "QueryInterface error %#x\n", hr);
+
+        hr = IWICPersistStream_Load(persist, stream);
+        ok(hr == S_OK, "Load error %#x\n", hr);
+
+        IWICPersistStream_Release(persist);
+    }
+
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICMetadataReader_GetCount(reader, &count);
+        ok(hr == S_OK, "GetCount error %#x\n", hr);
+        ok(count == sizeof(td)/sizeof(td[0]), "unexpected count %u\n", count);
+
+        compare_metadata(reader, td, count);
+
+        hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+        ok(hr == S_OK, "GetMetadataFormat error %#x\n", hr);
+        ok(IsEqualGUID(&format, &GUID_MetadataFormatIMD), "wrong format %s\n", debugstr_guid(&format));
+
+        hr = IWICMetadataReader_GetMetadataHandlerInfo(reader, &info);
+        ok(hr == S_OK, "GetMetadataHandlerInfo error %#x\n", hr);
+
+        hr = IWICMetadataHandlerInfo_GetCLSID(info, &id);
+        ok(hr == S_OK, "GetCLSID error %#x\n", hr);
+        ok(IsEqualGUID(&id, &CLSID_WICIMDMetadataReader), "wrong CLSID %s\n", debugstr_guid(&id));
+
+        hr = IWICMetadataHandlerInfo_GetFriendlyName(info, 64, name, &dummy);
+        ok(hr == S_OK, "GetFriendlyName error %#x\n", hr);
+        ok(lstrcmpW(name, IMD_name) == 0, "wrong IMD reader name %s\n", wine_dbgstr_w(name));
+
+        IWICMetadataHandlerInfo_Release(info);
+        IWICMetadataReader_Release(reader);
+    }
+
+    IStream_Release(stream);
+}
+
+static void test_metadata_GCE(void)
+{
+    static const WCHAR GCE_name[] = {'G','r','a','p','h','i','c',' ','C','o','n','t','r','o','l',' ','E','x','t','e','n','s','i','o','n',' ','R','e','a','d','e','r',0};
+    static const char GCE_data[] = "hello world!\xa\x2\x3\x4\x5\x6\x7\x8\xed\xa\xb\xc\xd\xe\xf";
+    static const struct test_data td[5] =
+    {
+        { VT_UI1, 0, 0, { 2 }, NULL, { 'D','i','s','p','o','s','a','l',0 } },
+        { VT_BOOL, 0, 0, { 1 }, NULL, { 'U','s','e','r','I','n','p','u','t','F','l','a','g',0 } },
+        { VT_BOOL, 0, 0, { 0 }, NULL, { 'T','r','a','n','s','p','a','r','e','n','c','y','F','l','a','g',0 } },
+        { VT_UI2, 0, 0, { 0x302 }, NULL, { 'D','e','l','a','y',0 } },
+        { VT_UI1, 0, 0, { 4 }, NULL, { 'T','r','a','n','s','p','a','r','e','n','t','C','o','l','o','r','I','n','d','e','x',0 } }
+    };
+    LARGE_INTEGER pos;
+    HRESULT hr;
+    IStream *stream;
+    IWICPersistStream *persist;
+    IWICMetadataReader *reader;
+    IWICMetadataHandlerInfo *info;
+    WCHAR name[64];
+    UINT count, dummy;
+    GUID format;
+    CLSID id;
+
+    hr = CoCreateInstance(&CLSID_WICGCEMetadataReader, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_IWICMetadataReader, (void **)&reader);
+    ok(hr == S_OK || broken(hr == E_NOINTERFACE || hr == REGDB_E_CLASSNOTREG) /* before Win7 */,
+       "CoCreateInstance error %#x\n", hr);
+
+    stream = create_stream(GCE_data, sizeof(GCE_data));
+
+    if (SUCCEEDED(hr))
+    {
+        pos.QuadPart = 12;
+        hr = IStream_Seek(stream, pos, SEEK_SET, NULL);
+        ok(hr == S_OK, "IStream_Seek error %#x\n", hr);
+
+        hr = IUnknown_QueryInterface(reader, &IID_IWICPersistStream, (void **)&persist);
+        ok(hr == S_OK, "QueryInterface error %#x\n", hr);
+
+        hr = IWICPersistStream_Load(persist, stream);
+        ok(hr == S_OK, "Load error %#x\n", hr);
+
+        IWICPersistStream_Release(persist);
+    }
+
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICMetadataReader_GetCount(reader, &count);
+        ok(hr == S_OK, "GetCount error %#x\n", hr);
+        ok(count == sizeof(td)/sizeof(td[0]), "unexpected count %u\n", count);
+
+        compare_metadata(reader, td, count);
+
+        hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+        ok(hr == S_OK, "GetMetadataFormat error %#x\n", hr);
+        ok(IsEqualGUID(&format, &GUID_MetadataFormatGCE), "wrong format %s\n", debugstr_guid(&format));
+
+        hr = IWICMetadataReader_GetMetadataHandlerInfo(reader, &info);
+        ok(hr == S_OK, "GetMetadataHandlerInfo error %#x\n", hr);
+
+        hr = IWICMetadataHandlerInfo_GetCLSID(info, &id);
+        ok(hr == S_OK, "GetCLSID error %#x\n", hr);
+        ok(IsEqualGUID(&id, &CLSID_WICGCEMetadataReader), "wrong CLSID %s\n", debugstr_guid(&id));
+
+        hr = IWICMetadataHandlerInfo_GetFriendlyName(info, 64, name, &dummy);
+        ok(hr == S_OK, "GetFriendlyName error %#x\n", hr);
+        ok(lstrcmpW(name, GCE_name) == 0, "wrong GCE reader name %s\n", wine_dbgstr_w(name));
+
+        IWICMetadataHandlerInfo_Release(info);
+        IWICMetadataReader_Release(reader);
+    }
+
+    IStream_Release(stream);
+}
+
+static void test_metadata_APE(void)
+{
+    static const WCHAR APE_name[] = {'A','p','p','l','i','c','a','t','i','o','n',' ','E','x','t','e','n','s','i','o','n',' ','R','e','a','d','e','r',0};
+    static const char APE_data[] = { 0x21,0xff,0x0b,'H','e','l','l','o',' ','W','o','r','l','d',
+                                     /*sub-block*/1,0x11,
+                                     /*sub-block*/2,0x22,0x33,
+                                     /*sub-block*/4,0x44,0x55,0x66,0x77,
+                                     /*terminator*/0 };
+    static const struct test_data td[2] =
+    {
+        { VT_UI1|VT_VECTOR, 0, 11, { 'H','e','l','l','o',' ','W','o','r','l','d' }, NULL, { 'A','p','p','l','i','c','a','t','i','o','n',0 } },
+        { VT_UI1|VT_VECTOR, 0, 10, { 1,0x11,2,0x22,0x33,4,0x44,0x55,0x66,0x77 }, NULL, { 'D','a','t','a',0 } }
+    };
+    WCHAR dataW[] = { 'd','a','t','a',0 };
+    HRESULT hr;
+    IStream *stream;
+    IWICPersistStream *persist;
+    IWICMetadataReader *reader;
+    IWICMetadataHandlerInfo *info;
+    WCHAR name[64];
+    UINT count, dummy, i;
+    GUID format;
+    CLSID clsid;
+    PROPVARIANT id, value;
+
+    hr = CoCreateInstance(&CLSID_WICAPEMetadataReader, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_IWICMetadataReader, (void **)&reader);
+    ok(hr == S_OK || broken(hr == E_NOINTERFACE || hr == REGDB_E_CLASSNOTREG) /* before Win7 */,
+       "CoCreateInstance error %#x\n", hr);
+
+    stream = create_stream(APE_data, sizeof(APE_data));
+
+    if (SUCCEEDED(hr))
+    {
+        hr = IUnknown_QueryInterface(reader, &IID_IWICPersistStream, (void **)&persist);
+        ok(hr == S_OK, "QueryInterface error %#x\n", hr);
+
+        hr = IWICPersistStream_Load(persist, stream);
+        ok(hr == S_OK, "Load error %#x\n", hr);
+
+        IWICPersistStream_Release(persist);
+    }
+
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICMetadataReader_GetCount(reader, &count);
+        ok(hr == S_OK, "GetCount error %#x\n", hr);
+        ok(count == sizeof(td)/sizeof(td[0]), "unexpected count %u\n", count);
+
+        compare_metadata(reader, td, count);
+
+        hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+        ok(hr == S_OK, "GetMetadataFormat error %#x\n", hr);
+        ok(IsEqualGUID(&format, &GUID_MetadataFormatAPE), "wrong format %s\n", debugstr_guid(&format));
+
+        PropVariantInit(&value);
+        id.vt = VT_LPWSTR;
+        U(id).pwszVal = dataW;
+
+        hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
+        ok(hr == S_OK, "GetValue error %#x\n", hr);
+        ok(value.vt == (VT_UI1|VT_VECTOR), "unexpected vt: %i\n", id.vt);
+        ok(td[1].count == U(value).caub.cElems, "expected cElems %d, got %d\n", td[1].count, U(value).caub.cElems);
+        for (i = 0; i < U(value).caub.cElems; i++)
+            ok(td[1].value[i] == U(value).caub.pElems[i], "%u: expected value %#x/%#x, got %#x\n", i, (ULONG)td[1].value[i], (ULONG)(td[1].value[i] >> 32), U(value).caub.pElems[i]);
+        PropVariantClear(&value);
+
+        hr = IWICMetadataReader_GetMetadataHandlerInfo(reader, &info);
+        ok(hr == S_OK, "GetMetadataHandlerInfo error %#x\n", hr);
+
+        hr = IWICMetadataHandlerInfo_GetCLSID(info, &clsid);
+        ok(hr == S_OK, "GetCLSID error %#x\n", hr);
+        ok(IsEqualGUID(&clsid, &CLSID_WICAPEMetadataReader), "wrong CLSID %s\n", debugstr_guid(&clsid));
+
+        hr = IWICMetadataHandlerInfo_GetFriendlyName(info, 64, name, &dummy);
+        ok(hr == S_OK, "GetFriendlyName error %#x\n", hr);
+        ok(lstrcmpW(name, APE_name) == 0, "wrong APE reader name %s\n", wine_dbgstr_w(name));
+
+        IWICMetadataHandlerInfo_Release(info);
+        IWICMetadataReader_Release(reader);
+    }
+
+    IStream_Release(stream);
+}
+
+static void test_metadata_GIF_comment(void)
+{
+    static const WCHAR GIF_comment_name[] = {'C','o','m','m','e','n','t',' ','E','x','t','e','n','s','i','o','n',' ','R','e','a','d','e','r',0};
+    static const char GIF_comment_data[] = { 0x21,0xfe,
+                                             /*sub-block*/5,'H','e','l','l','o',
+                                             /*sub-block*/1,' ',
+                                             /*sub-block*/6,'W','o','r','l','d','!',
+                                             /*terminator*/0 };
+    static const struct test_data td[1] =
+    {
+        { VT_LPSTR, 0, 12, { 0 }, "Hello World!", { 'T','e','x','t','E','n','t','r','y',0 } }
+    };
+    WCHAR text_entryW[] = { 'T','E','X','T','E','N','T','R','Y',0 };
+    HRESULT hr;
+    IStream *stream;
+    IWICPersistStream *persist;
+    IWICMetadataReader *reader;
+    IWICMetadataHandlerInfo *info;
+    WCHAR name[64];
+    UINT count, dummy;
+    GUID format;
+    CLSID clsid;
+    PROPVARIANT id, value;
+
+    hr = CoCreateInstance(&CLSID_WICGifCommentMetadataReader, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_IWICMetadataReader, (void **)&reader);
+    ok(hr == S_OK || broken(hr == E_NOINTERFACE || hr == REGDB_E_CLASSNOTREG) /* before Win7 */,
+       "CoCreateInstance error %#x\n", hr);
+
+    stream = create_stream(GIF_comment_data, sizeof(GIF_comment_data));
+
+    if (SUCCEEDED(hr))
+    {
+        hr = IUnknown_QueryInterface(reader, &IID_IWICPersistStream, (void **)&persist);
+        ok(hr == S_OK, "QueryInterface error %#x\n", hr);
+
+        hr = IWICPersistStream_Load(persist, stream);
+        ok(hr == S_OK, "Load error %#x\n", hr);
+
+        IWICPersistStream_Release(persist);
+    }
+
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICMetadataReader_GetCount(reader, &count);
+        ok(hr == S_OK, "GetCount error %#x\n", hr);
+        ok(count == sizeof(td)/sizeof(td[0]), "unexpected count %u\n", count);
+
+        compare_metadata(reader, td, count);
+
+        hr = IWICMetadataReader_GetMetadataFormat(reader, &format);
+        ok(hr == S_OK, "GetMetadataFormat error %#x\n", hr);
+        ok(IsEqualGUID(&format, &GUID_MetadataFormatGifComment), "wrong format %s\n", debugstr_guid(&format));
+
+        PropVariantInit(&value);
+        id.vt = VT_LPWSTR;
+        U(id).pwszVal = text_entryW;
+
+        hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
+        ok(hr == S_OK, "GetValue error %#x\n", hr);
+        ok(value.vt == VT_LPSTR, "unexpected vt: %i\n", id.vt);
+        ok(!strcmp(U(value).pszVal, "Hello World!"), "unexpected value: %s\n", U(value).pszVal);
+        PropVariantClear(&value);
+
+        hr = IWICMetadataReader_GetMetadataHandlerInfo(reader, &info);
+        ok(hr == S_OK, "GetMetadataHandlerInfo error %#x\n", hr);
+
+        hr = IWICMetadataHandlerInfo_GetCLSID(info, &clsid);
+        ok(hr == S_OK, "GetCLSID error %#x\n", hr);
+        ok(IsEqualGUID(&clsid, &CLSID_WICGifCommentMetadataReader), "wrong CLSID %s\n", debugstr_guid(&clsid));
+
+        hr = IWICMetadataHandlerInfo_GetFriendlyName(info, 64, name, &dummy);
+        ok(hr == S_OK, "GetFriendlyName error %#x\n", hr);
+        ok(lstrcmpW(name, GIF_comment_name) == 0, "wrong APE reader name %s\n", wine_dbgstr_w(name));
+
+        IWICMetadataHandlerInfo_Release(info);
+        IWICMetadataReader_Release(reader);
+    }
+
+    IStream_Release(stream);
+}
+
+START_TEST(metadata)
+{
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+
+    test_metadata_unknown();
+    test_metadata_tEXt();
+    test_metadata_IFD();
+    test_metadata_Exif();
+    test_create_reader();
+    test_metadata_png();
+    test_metadata_gif();
+    test_metadata_LSD();
+    test_metadata_IMD();
+    test_metadata_GCE();
+    test_metadata_APE();
+    test_metadata_GIF_comment();
+
+    CoUninitialize();
+}
diff --git a/rostests/winetests/windowscodecs/palette.c b/rostests/winetests/windowscodecs/palette.c
new file mode 100644 (file)
index 0000000..e3b9a31
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ * Copyright 2009 Vincent Povirk for CodeWeavers
+ * Copyright 2012 Dmitry Timoshkov
+ *
+ * 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 <assert.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "objbase.h"
+#include "wincodec.h"
+#include "wine/test.h"
+
+static void test_custom_palette(void)
+{
+    IWICImagingFactory *factory;
+    IWICPalette *palette, *palette2;
+    HRESULT hr;
+    WICBitmapPaletteType type=0xffffffff;
+    UINT count=1;
+    const WICColor initcolors[4]={0xff000000,0xff0000ff,0xffffff00,0xffffffff};
+    WICColor colors[4];
+    BOOL boolresult;
+
+    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICImagingFactory, (void**)&factory);
+    ok(SUCCEEDED(hr), "CoCreateInstance failed, hr=%x\n", hr);
+    if (FAILED(hr)) return;
+
+    hr = IWICImagingFactory_CreatePalette(factory, &palette);
+    ok(SUCCEEDED(hr), "CreatePalette failed, hr=%x\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICPalette_GetType(palette, &type);
+        ok(SUCCEEDED(hr), "GetType failed, hr=%x\n", hr);
+        ok(type == WICBitmapPaletteTypeCustom, "expected WICBitmapPaletteTypeCustom, got %x\n", type);
+
+        hr = IWICPalette_GetColorCount(palette, &count);
+        ok(SUCCEEDED(hr), "GetColorCount failed, hr=%x\n", hr);
+        ok(count == 0, "expected 0, got %u\n", count);
+
+        hr = IWICPalette_GetColors(palette, 0, colors, &count);
+        ok(SUCCEEDED(hr), "GetColors failed, hr=%x\n", hr);
+        ok(count == 0, "expected 0, got %u\n", count);
+
+        hr = IWICPalette_GetColors(palette, 4, colors, &count);
+        ok(SUCCEEDED(hr), "GetColors failed, hr=%x\n", hr);
+        ok(count == 0, "expected 0, got %u\n", count);
+
+        memcpy(colors, initcolors, sizeof(initcolors));
+        hr = IWICPalette_InitializeCustom(palette, colors, 4);
+        ok(SUCCEEDED(hr), "InitializeCustom failed, hr=%x\n", hr);
+
+        hr = IWICPalette_GetType(palette, &type);
+        ok(SUCCEEDED(hr), "GetType failed, hr=%x\n", hr);
+        ok(type == WICBitmapPaletteTypeCustom, "expected WICBitmapPaletteTypeCustom, got %x\n", type);
+
+        hr = IWICPalette_GetColorCount(palette, &count);
+        ok(SUCCEEDED(hr), "GetColorCount failed, hr=%x\n", hr);
+        ok(count == 4, "expected 4, got %u\n", count);
+
+        memset(colors, 0, sizeof(colors));
+        count = 0;
+        hr = IWICPalette_GetColors(palette, 4, colors, &count);
+        ok(SUCCEEDED(hr), "GetColors failed, hr=%x\n", hr);
+        ok(count == 4, "expected 4, got %u\n", count);
+        ok(!memcmp(colors, initcolors, sizeof(colors)), "got unexpected palette data\n");
+
+        memset(colors, 0, sizeof(colors));
+        count = 0;
+        hr = IWICPalette_GetColors(palette, 2, colors, &count);
+        ok(SUCCEEDED(hr), "GetColors failed, hr=%x\n", hr);
+        ok(count == 2, "expected 2, got %u\n", count);
+        ok(!memcmp(colors, initcolors, sizeof(WICColor)*2), "got unexpected palette data\n");
+
+        count = 0;
+        hr = IWICPalette_GetColors(palette, 6, colors, &count);
+        ok(SUCCEEDED(hr), "GetColors failed, hr=%x\n", hr);
+        ok(count == 4, "expected 4, got %u\n", count);
+
+        hr = IWICPalette_HasAlpha(palette, &boolresult);
+        ok(SUCCEEDED(hr), "HasAlpha failed, hr=%x\n", hr);
+        ok(!boolresult, "expected FALSE, got TRUE\n");
+
+        hr = IWICPalette_IsBlackWhite(palette, &boolresult);
+        ok(SUCCEEDED(hr), "IsBlackWhite failed, hr=%x\n", hr);
+        ok(!boolresult, "expected FALSE, got TRUE\n");
+
+        hr = IWICPalette_IsGrayscale(palette, &boolresult);
+        ok(SUCCEEDED(hr), "IsGrayscale failed, hr=%x\n", hr);
+        ok(!boolresult, "expected FALSE, got TRUE\n");
+
+        hr = IWICImagingFactory_CreatePalette(factory, &palette2);
+        ok(SUCCEEDED(hr), "CreatePalette failed, hr=%x\n", hr);
+
+        hr = IWICPalette_InitializeFromPalette(palette2, palette);
+        ok(SUCCEEDED(hr), "InitializeFromPalette failed, hr=%x\n", hr);
+
+        type = 0xdeadbeef;
+        hr = IWICPalette_GetType(palette2, &type);
+        ok(SUCCEEDED(hr), "GetType failed, hr=%x\n", hr);
+        ok(type == WICBitmapPaletteTypeCustom, "expected WICBitmapPaletteTypeCustom, got %x\n", type);
+
+        count = 0xdeadbeef;
+        hr = IWICPalette_GetColorCount(palette2, &count);
+        ok(SUCCEEDED(hr), "GetColorCount failed, hr=%x\n", hr);
+        ok(count == 4, "expected 4, got %u\n", count);
+
+        memset(colors, 0, sizeof(colors));
+        count = 0xdeadbeef;
+        hr = IWICPalette_GetColors(palette2, 4, colors, &count);
+        ok(SUCCEEDED(hr), "GetColors failed, hr=%x\n", hr);
+        ok(count == 4, "expected 4, got %u\n", count);
+        ok(!memcmp(colors, initcolors, sizeof(colors)), "got unexpected palette data\n");
+
+        /* try a palette with some alpha in it */
+        colors[2] = 0x80ffffff;
+        hr = IWICPalette_InitializeCustom(palette, colors, 4);
+        ok(SUCCEEDED(hr), "InitializeCustom failed, hr=%x\n", hr);
+
+        hr = IWICPalette_HasAlpha(palette, &boolresult);
+        ok(SUCCEEDED(hr), "HasAlpha failed, hr=%x\n", hr);
+        ok(boolresult, "expected TRUE, got FALSE\n");
+
+        /* setting to a 0-color palette is acceptable */
+        hr = IWICPalette_InitializeCustom(palette, NULL, 0);
+        ok(SUCCEEDED(hr), "InitializeCustom failed, hr=%x\n", hr);
+
+        type = 0xdeadbeef;
+        hr = IWICPalette_GetType(palette, &type);
+        ok(SUCCEEDED(hr), "GetType failed, hr=%x\n", hr);
+        ok(type == WICBitmapPaletteTypeCustom, "expected WICBitmapPaletteTypeCustom, got %x\n", type);
+
+        count = 0xdeadbeef;
+        hr = IWICPalette_GetColorCount(palette, &count);
+        ok(SUCCEEDED(hr), "GetColorCount failed, hr=%x\n", hr);
+        ok(count == 0, "expected 0, got %u\n", count);
+
+        count = 0xdeadbeef;
+        hr = IWICPalette_GetColors(palette, 4, colors, &count);
+        ok(SUCCEEDED(hr), "GetColors failed, hr=%x\n", hr);
+        ok(count == 0, "expected 0, got %u\n", count);
+
+        hr = IWICPalette_InitializeFromPalette(palette2, palette);
+        ok(SUCCEEDED(hr), "InitializeFromPalette failed, hr=%x\n", hr);
+
+        type = 0xdeadbeef;
+        hr = IWICPalette_GetType(palette2, &type);
+        ok(SUCCEEDED(hr), "GetType failed, hr=%x\n", hr);
+        ok(type == WICBitmapPaletteTypeCustom, "expected WICBitmapPaletteTypeCustom, got %x\n", type);
+
+        count = 0xdeadbeef;
+        hr = IWICPalette_GetColorCount(palette2, &count);
+        ok(SUCCEEDED(hr), "GetColorCount failed, hr=%x\n", hr);
+        ok(count == 0, "expected 0, got %u\n", count);
+
+        memset(colors, 0, sizeof(colors));
+        count = 0xdeadbeef;
+        hr = IWICPalette_GetColors(palette2, 4, colors, &count);
+        ok(SUCCEEDED(hr), "GetColors failed, hr=%x\n", hr);
+        ok(count == 0, "expected 0, got %u\n", count);
+
+        /* IWICPalette is paranoid about NULL pointers */
+        hr = IWICPalette_GetType(palette, NULL);
+        ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %x\n", hr);
+
+        hr = IWICPalette_GetColorCount(palette, NULL);
+        ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %x\n", hr);
+
+        hr = IWICPalette_InitializeCustom(palette, NULL, 4);
+        ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %x\n", hr);
+
+        hr = IWICPalette_GetColors(palette, 4, NULL, &count);
+        ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %x\n", hr);
+
+        hr = IWICPalette_GetColors(palette, 4, colors, NULL);
+        ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %x\n", hr);
+
+        hr = IWICPalette_HasAlpha(palette, NULL);
+        ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %x\n", hr);
+
+        hr = IWICPalette_IsBlackWhite(palette, NULL);
+        ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %x\n", hr);
+
+        hr = IWICPalette_IsGrayscale(palette, NULL);
+        ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %x\n", hr);
+
+        hr = IWICPalette_InitializeFromPalette(palette, NULL);
+        ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %x\n", hr);
+
+        IWICPalette_Release(palette2);
+        IWICPalette_Release(palette);
+    }
+
+    IWICImagingFactory_Release(factory);
+}
+
+static void generate_gray16_palette(DWORD *entries, UINT count)
+{
+    UINT i;
+
+    assert(count == 16);
+
+    for (i = 0; i < 16; i++)
+    {
+        entries[i] = 0xff000000;
+        entries[i] |= (i << 20) | (i << 16) | (i << 12) | (i << 8) | (i << 4) | i;
+    }
+}
+
+static void generate_gray256_palette(DWORD *entries, UINT count)
+{
+    UINT i;
+
+    assert(count == 256);
+
+    for (i = 0; i < 256; i++)
+    {
+        entries[i] = 0xff000000;
+        entries[i] |= (i << 16) | (i << 8) | i;
+    }
+}
+
+static void generate_halftone8_palette(DWORD *entries, UINT count, BOOL add_transparent)
+{
+    UINT i;
+
+    if (add_transparent)
+        ok(count == 17, "expected 17, got %u\n", count);
+    else
+        ok(count == 16, "expected 16, got %u\n", count);
+
+    for (i = 0; i < 8; i++)
+    {
+        entries[i] = 0xff000000;
+        if (i & 1) entries[i] |= 0xff;
+        if (i & 2) entries[i] |= 0xff00;
+        if (i & 4) entries[i] |= 0xff0000;
+    }
+
+    for (i = 8; i < 16; i++)
+    {
+        static const DWORD halftone[8] = { 0xc0c0c0, 0x808080, 0x800000, 0x008000,
+                                           0x000080, 0x808000, 0x800080, 0x008080 };
+        entries[i] = 0xff000000;
+        entries[i] |= halftone[i-8];
+    }
+
+    if (add_transparent)
+        entries[i] = 0;
+}
+
+static void generate_halftone27_palette(DWORD *entries, UINT count, BOOL add_transparent)
+{
+    static const BYTE halftone_values[4] = { 0x00,0x80,0xff };
+    UINT i;
+
+    if (add_transparent)
+        ok(count == 29, "expected 29, got %u\n", count);
+    else
+        ok(count == 28, "expected 28, got %u\n", count);
+
+    for (i = 0; i < 27; i++)
+    {
+        entries[i] = 0xff000000;
+        entries[i] |= halftone_values[i%3];
+        entries[i] |= halftone_values[(i/3)%3] << 8;
+        entries[i] |= halftone_values[(i/9)%3] << 16;
+    }
+
+    entries[i++] = 0xffc0c0c0;
+    if (add_transparent)
+        entries[i] = 0;
+}
+
+static void generate_halftone64_palette(DWORD *entries, UINT count, BOOL add_transparent)
+{
+    static const BYTE halftone_values[4] = { 0x00,0x55,0xaa,0xff };
+    UINT i;
+
+    if (add_transparent)
+        ok(count == 73, "expected 73, got %u\n", count);
+    else
+        ok(count == 72, "expected 72, got %u\n", count);
+
+    for (i = 0; i < 64; i++)
+    {
+        entries[i] = 0xff000000;
+        entries[i] |= halftone_values[i%4];
+        entries[i] |= halftone_values[(i/4)%4] << 8;
+        entries[i] |= halftone_values[(i/16)%4] << 16;
+    }
+
+    for (i = 64; i < 72; i++)
+    {
+        static const DWORD halftone[8] = { 0xc0c0c0, 0x808080, 0x800000, 0x008000,
+                                           0x000080, 0x808000, 0x800080, 0x008080 };
+        entries[i] = 0xff000000;
+        entries[i] |= halftone[i-64];
+    }
+
+    if (add_transparent)
+        entries[i] = 0;
+}
+
+static void generate_halftone125_palette(DWORD *entries, UINT count, BOOL add_transparent)
+{
+    static const BYTE halftone_values[5] = { 0x00, 0x40, 0x80, 0xbf, 0xff };
+    UINT i;
+
+    if (add_transparent)
+        ok(count == 127, "expected 127, got %u\n", count);
+    else
+        ok(count == 126, "expected 126, got %u\n", count);
+
+    for (i = 0; i < 125; i++)
+    {
+        entries[i] = 0xff000000;
+        entries[i] |= halftone_values[i%5];
+        entries[i] |= halftone_values[(i/5)%5] << 8;
+        entries[i] |= halftone_values[(i/25)%5] << 16;
+    }
+
+    entries[i++] = 0xffc0c0c0;
+    if (add_transparent)
+        entries[i] = 0;
+}
+
+static void generate_halftone216_palette(DWORD *entries, UINT count, BOOL add_transparent)
+{
+    static const BYTE halftone_values[6] = { 0x00,0x33,0x66,0x99,0xcc,0xff };
+    UINT i;
+
+    if (add_transparent)
+        ok(count == 225, "expected 225, got %u\n", count);
+    else
+        ok(count == 224, "expected 224, got %u\n", count);
+
+    for (i = 0; i < 216; i++)
+    {
+        entries[i] = 0xff000000;
+        entries[i] |= halftone_values[i%6];
+        entries[i] |= halftone_values[(i/6)%6] << 8;
+        entries[i] |= halftone_values[(i/36)%6] << 16;
+    }
+
+    for (i = 216; i < 224; i++)
+    {
+        static const DWORD halftone[8] = { 0xc0c0c0, 0x808080, 0x800000, 0x008000,
+                                           0x000080, 0x808000, 0x800080, 0x008080 };
+        entries[i] = 0xff000000;
+        entries[i] |= halftone[i-216];
+    }
+
+    if (add_transparent)
+        entries[i] = 0;
+}
+
+static void generate_halftone252_palette(DWORD *entries, UINT count, BOOL add_transparent)
+{
+    static const BYTE halftone_values_rb[6] = { 0x00,0x33,0x66,0x99,0xcc,0xff };
+    static const BYTE halftone_values_g[7] = { 0x00,0x2b,0x55,0x80,0xaa,0xd5,0xff };
+    UINT i;
+
+    if (add_transparent)
+        ok(count == 253, "expected 253, got %u\n", count);
+    else
+        ok(count == 252, "expected 252, got %u\n", count);
+
+    for (i = 0; i < 252; i++)
+    {
+        entries[i] = 0xff000000;
+        entries[i] |= halftone_values_rb[i%6];
+        entries[i] |= halftone_values_g[(i/6)%7] << 8;
+        entries[i] |= halftone_values_rb[(i/42)%6] << 16;
+    }
+
+    if (add_transparent)
+        entries[i] = 0;
+}
+
+static void generate_halftone256_palette(DWORD *entries, UINT count, BOOL add_transparent)
+{
+    static const BYTE halftone_values_b[4] = { 0x00,0x55,0xaa,0xff };
+    static const BYTE halftone_values_gr[8] = { 0x00,0x24,0x49,0x6d,0x92,0xb6,0xdb,0xff };
+    UINT i;
+
+    assert(count == 256);
+
+    for (i = 0; i < 256; i++)
+    {
+        entries[i] = 0xff000000;
+        entries[i] |= halftone_values_b[i%4];
+        entries[i] |= halftone_values_gr[(i/4)%8] << 8;
+        entries[i] |= halftone_values_gr[(i/32)%8] << 16;
+    }
+
+    if (add_transparent)
+        entries[255] = 0;
+}
+
+static void test_predefined_palette(void)
+{
+    static struct test_data
+    {
+        WICBitmapPaletteType type;
+        BOOL is_bw, is_gray;
+        UINT count;
+        WICColor color[256];
+        BOOL add_transparent;
+    } td[] =
+    {
+        { WICBitmapPaletteTypeFixedBW, 1, 1, 2, { 0xff000000, 0xffffffff } },
+        { WICBitmapPaletteTypeFixedBW, 1, 1, 2, { 0xff000000, 0xffffffff }, 1 },
+        { WICBitmapPaletteTypeFixedGray4, 0, 1, 4,
+          { 0xff000000, 0xff555555, 0xffaaaaaa, 0xffffffff } },
+        { WICBitmapPaletteTypeFixedGray4, 0, 1, 4,
+          { 0xff000000, 0xff555555, 0xffaaaaaa, 0xffffffff }, 1 },
+        { WICBitmapPaletteTypeFixedGray16, 0, 1, 16, { 0 } },
+        { WICBitmapPaletteTypeFixedGray16, 0, 1, 16, { 0 }, 1 },
+        { WICBitmapPaletteTypeFixedGray256, 0, 1, 256, { 0 } },
+        { WICBitmapPaletteTypeFixedGray256, 0, 1, 256, { 0 }, 1 },
+        { WICBitmapPaletteTypeFixedHalftone8, 0, 0, 16, { 0 } },
+        { WICBitmapPaletteTypeFixedHalftone8, 0, 0, 17, { 0 }, 1 },
+        { WICBitmapPaletteTypeFixedHalftone27, 0, 0, 28, { 0 } },
+        { WICBitmapPaletteTypeFixedHalftone27, 0, 0, 29, { 0 }, 1 },
+        { WICBitmapPaletteTypeFixedHalftone64, 0, 0, 72, { 0 } },
+        { WICBitmapPaletteTypeFixedHalftone64, 0, 0, 73, { 0 }, 1 },
+        { WICBitmapPaletteTypeFixedHalftone125, 0, 0, 126, { 0 } },
+        { WICBitmapPaletteTypeFixedHalftone125, 0, 0, 127, { 0 }, 1 },
+        { WICBitmapPaletteTypeFixedHalftone216, 0, 0, 224, { 0 } },
+        { WICBitmapPaletteTypeFixedHalftone216, 0, 0, 225, { 0 }, 1 },
+        { WICBitmapPaletteTypeFixedHalftone252, 0, 0, 252, { 0 } },
+        { WICBitmapPaletteTypeFixedHalftone252, 0, 0, 253, { 0 }, 1 },
+        { WICBitmapPaletteTypeFixedHalftone256, 0, 0, 256, { 0 } },
+        { WICBitmapPaletteTypeFixedHalftone256, 0, 0, 256, { 0 }, 1 }
+    };
+    IWICImagingFactory *factory;
+    IWICPalette *palette;
+    HRESULT hr;
+    WICBitmapPaletteType type;
+    UINT count, i, ret;
+    BOOL bret;
+    WICColor color[256];
+
+    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_IWICImagingFactory, (void **)&factory);
+    ok(hr == S_OK, "CoCreateInstance error %#x\n", hr);
+
+    hr = IWICImagingFactory_CreatePalette(factory, &palette);
+    ok(hr == S_OK, "CreatePalette error %#x\n", hr);
+    hr = IWICPalette_InitializePredefined(palette, WICBitmapPaletteTypeCustom, FALSE);
+    ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+    hr = IWICPalette_InitializePredefined(palette, WICBitmapPaletteTypeMedianCut, FALSE);
+    ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+    hr = IWICPalette_InitializePredefined(palette, 0x0f, FALSE);
+    ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+    IWICPalette_Release(palette);
+
+    for (i = 0; i < sizeof(td)/sizeof(td[0]); i++)
+    {
+        hr = IWICImagingFactory_CreatePalette(factory, &palette);
+        ok(hr == S_OK, "%u: CreatePalette error %#x\n", i, hr);
+
+        hr = IWICPalette_InitializePredefined(palette, td[i].type, td[i].add_transparent);
+        ok(hr == S_OK, "%u: InitializePredefined error %#x\n", i, hr);
+
+        bret = -1;
+        hr = IWICPalette_IsBlackWhite(palette, &bret);
+        ok(hr == S_OK, "%u: IsBlackWhite error %#x\n", i, hr);
+        ok(bret == td[i].is_bw ||
+           broken(td[i].type == WICBitmapPaletteTypeFixedBW && bret != td[i].is_bw), /* XP */
+           "%u: expected %d, got %d\n",i, td[i].is_bw, bret);
+
+        bret = -1;
+        hr = IWICPalette_IsGrayscale(palette, &bret);
+        ok(hr == S_OK, "%u: IsGrayscale error %#x\n", i, hr);
+        ok(bret == td[i].is_gray, "%u: expected %d, got %d\n", i, td[i].is_gray, bret);
+
+        type = -1;
+        hr = IWICPalette_GetType(palette, &type);
+        ok(hr == S_OK, "%u: GetType error %#x\n", i, hr);
+        ok(type == td[i].type, "%u: expected %#x, got %#x\n", i, td[i].type, type);
+
+        count = 0xdeadbeef;
+        hr = IWICPalette_GetColorCount(palette, &count);
+        ok(hr == S_OK, "%u: GetColorCount error %#x\n", i, hr);
+        ok(count == td[i].count, "%u: expected %u, got %u\n", i, td[i].count, count);
+
+        hr = IWICPalette_GetColors(palette, count, color, &ret);
+        ok(hr == S_OK, "%u: GetColors error %#x\n", i, hr);
+        ok(ret == count, "%u: expected %u, got %u\n", i, count, ret);
+        if (ret == td[i].count)
+        {
+            UINT j;
+
+            if (td[i].type == WICBitmapPaletteTypeFixedGray16)
+                generate_gray16_palette(td[i].color, td[i].count);
+            else if (td[i].type == WICBitmapPaletteTypeFixedGray256)
+                generate_gray256_palette(td[i].color, td[i].count);
+            else if (td[i].type == WICBitmapPaletteTypeFixedHalftone8)
+                generate_halftone8_palette(td[i].color, td[i].count, td[i].add_transparent);
+            else if (td[i].type == WICBitmapPaletteTypeFixedHalftone27)
+                generate_halftone27_palette(td[i].color, td[i].count, td[i].add_transparent);
+            else if (td[i].type == WICBitmapPaletteTypeFixedHalftone64)
+                generate_halftone64_palette(td[i].color, td[i].count, td[i].add_transparent);
+            else if (td[i].type == WICBitmapPaletteTypeFixedHalftone125)
+                generate_halftone125_palette(td[i].color, td[i].count, td[i].add_transparent);
+            else if (td[i].type == WICBitmapPaletteTypeFixedHalftone216)
+                generate_halftone216_palette(td[i].color, td[i].count, td[i].add_transparent);
+            else if (td[i].type == WICBitmapPaletteTypeFixedHalftone252)
+                generate_halftone252_palette(td[i].color, td[i].count, td[i].add_transparent);
+            else if (td[i].type == WICBitmapPaletteTypeFixedHalftone256)
+                generate_halftone256_palette(td[i].color, td[i].count, td[i].add_transparent);
+
+            for (j = 0; j < count; j++)
+            {
+                ok(color[j] == td[i].color[j], "%u:[%u]: expected %#x, got %#x\n",
+                   i, j, td[i].color[j], color[j]);
+            }
+        }
+
+        IWICPalette_Release(palette);
+    }
+
+    IWICImagingFactory_Release(factory);
+}
+
+START_TEST(palette)
+{
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+
+    test_custom_palette();
+    test_predefined_palette();
+
+    CoUninitialize();
+}
diff --git a/rostests/winetests/windowscodecs/pngformat.c b/rostests/winetests/windowscodecs/pngformat.c
new file mode 100644 (file)
index 0000000..3f4e855
--- /dev/null
@@ -0,0 +1,490 @@
+/*
+ * Copyright 2012 Dmitry Timoshkov
+ * Copyright 2012 Hans Leidekker 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 <stdio.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "wincodec.h"
+#include "wine/test.h"
+
+/* 1x1 pixel PNG image */
+static const char png_no_color_profile[] = {
+  0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
+  0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
+  0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xde, 0x00, 0x00, 0x00,
+  0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b,
+  0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49,
+  0x4d, 0x45, 0x07, 0xdc, 0x0b, 0x0e, 0x0e, 0x22, 0x17, 0x10, 0xd8, 0xde,
+  0x3b, 0x00, 0x00, 0x00, 0x0c, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63,
+  0xf8, 0xff, 0xff, 0x3f, 0x00, 0x05, 0xfe, 0x02, 0xfe, 0xdc, 0xcc, 0x59,
+  0xe7, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60,
+  0x82
+};
+
+/* 1x1 pixel PNG image with embedded sRGB profile */
+static const char png_color_profile[] = {
+  0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
+  0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
+  0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xde, 0x00, 0x00, 0x0a,
+  0x43, 0x69, 0x43, 0x43, 0x50, 0x49, 0x43, 0x43, 0x20, 0x70, 0x72, 0x6f,
+  0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x78, 0xda, 0x9d, 0x53, 0x77, 0x58,
+  0x93, 0xf7, 0x16, 0x3e, 0xdf, 0xf7, 0x65, 0x0f, 0x56, 0x42, 0xd8, 0xf0,
+  0xb1, 0x97, 0x6c, 0x81, 0x00, 0x22, 0x23, 0xac, 0x08, 0xc8, 0x10, 0x59,
+  0xa2, 0x10, 0x92, 0x00, 0x61, 0x84, 0x10, 0x12, 0x40, 0xc5, 0x85, 0x88,
+  0x0a, 0x56, 0x14, 0x15, 0x11, 0x9c, 0x48, 0x55, 0xc4, 0x82, 0xd5, 0x0a,
+  0x48, 0x9d, 0x88, 0xe2, 0xa0, 0x28, 0xb8, 0x67, 0x41, 0x8a, 0x88, 0x5a,
+  0x8b, 0x55, 0x5c, 0x38, 0xee, 0x1f, 0xdc, 0xa7, 0xb5, 0x7d, 0x7a, 0xef,
+  0xed, 0xed, 0xfb, 0xd7, 0xfb, 0xbc, 0xe7, 0x9c, 0xe7, 0xfc, 0xce, 0x79,
+  0xcf, 0x0f, 0x80, 0x11, 0x12, 0x26, 0x91, 0xe6, 0xa2, 0x6a, 0x00, 0x39,
+  0x52, 0x85, 0x3c, 0x3a, 0xd8, 0x1f, 0x8f, 0x4f, 0x48, 0xc4, 0xc9, 0xbd,
+  0x80, 0x02, 0x15, 0x48, 0xe0, 0x04, 0x20, 0x10, 0xe6, 0xcb, 0xc2, 0x67,
+  0x05, 0xc5, 0x00, 0x00, 0xf0, 0x03, 0x79, 0x78, 0x7e, 0x74, 0xb0, 0x3f,
+  0xfc, 0x01, 0xaf, 0x6f, 0x00, 0x02, 0x00, 0x70, 0xd5, 0x2e, 0x24, 0x12,
+  0xc7, 0xe1, 0xff, 0x83, 0xba, 0x50, 0x26, 0x57, 0x00, 0x20, 0x91, 0x00,
+  0xe0, 0x22, 0x12, 0xe7, 0x0b, 0x01, 0x90, 0x52, 0x00, 0xc8, 0x2e, 0x54,
+  0xc8, 0x14, 0x00, 0xc8, 0x18, 0x00, 0xb0, 0x53, 0xb3, 0x64, 0x0a, 0x00,
+  0x94, 0x00, 0x00, 0x6c, 0x79, 0x7c, 0x42, 0x22, 0x00, 0xaa, 0x0d, 0x00,
+  0xec, 0xf4, 0x49, 0x3e, 0x05, 0x00, 0xd8, 0xa9, 0x93, 0xdc, 0x17, 0x00,
+  0xd8, 0xa2, 0x1c, 0xa9, 0x08, 0x00, 0x8d, 0x01, 0x00, 0x99, 0x28, 0x47,
+  0x24, 0x02, 0x40, 0xbb, 0x00, 0x60, 0x55, 0x81, 0x52, 0x2c, 0x02, 0xc0,
+  0xc2, 0x00, 0xa0, 0xac, 0x40, 0x22, 0x2e, 0x04, 0xc0, 0xae, 0x01, 0x80,
+  0x59, 0xb6, 0x32, 0x47, 0x02, 0x80, 0xbd, 0x05, 0x00, 0x76, 0x8e, 0x58,
+  0x90, 0x0f, 0x40, 0x60, 0x00, 0x80, 0x99, 0x42, 0x2c, 0xcc, 0x00, 0x20,
+  0x38, 0x02, 0x00, 0x43, 0x1e, 0x13, 0xcd, 0x03, 0x20, 0x4c, 0x03, 0xa0,
+  0x30, 0xd2, 0xbf, 0xe0, 0xa9, 0x5f, 0x70, 0x85, 0xb8, 0x48, 0x01, 0x00,
+  0xc0, 0xcb, 0x95, 0xcd, 0x97, 0x4b, 0xd2, 0x33, 0x14, 0xb8, 0x95, 0xd0,
+  0x1a, 0x77, 0xf2, 0xf0, 0xe0, 0xe2, 0x21, 0xe2, 0xc2, 0x6c, 0xb1, 0x42,
+  0x61, 0x17, 0x29, 0x10, 0x66, 0x09, 0xe4, 0x22, 0x9c, 0x97, 0x9b, 0x23,
+  0x13, 0x48, 0xe7, 0x03, 0x4c, 0xce, 0x0c, 0x00, 0x00, 0x1a, 0xf9, 0xd1,
+  0xc1, 0xfe, 0x38, 0x3f, 0x90, 0xe7, 0xe6, 0xe4, 0xe1, 0xe6, 0x66, 0xe7,
+  0x6c, 0xef, 0xf4, 0xc5, 0xa2, 0xfe, 0x6b, 0xf0, 0x6f, 0x22, 0x3e, 0x21,
+  0xf1, 0xdf, 0xfe, 0xbc, 0x8c, 0x02, 0x04, 0x00, 0x10, 0x4e, 0xcf, 0xef,
+  0xda, 0x5f, 0xe5, 0xe5, 0xd6, 0x03, 0x70, 0xc7, 0x01, 0xb0, 0x75, 0xbf,
+  0x6b, 0xa9, 0x5b, 0x00, 0xda, 0x56, 0x00, 0x68, 0xdf, 0xf9, 0x5d, 0x33,
+  0xdb, 0x09, 0xa0, 0x5a, 0x0a, 0xd0, 0x7a, 0xf9, 0x8b, 0x79, 0x38, 0xfc,
+  0x40, 0x1e, 0x9e, 0xa1, 0x50, 0xc8, 0x3c, 0x1d, 0x1c, 0x0a, 0x0b, 0x0b,
+  0xed, 0x25, 0x62, 0xa1, 0xbd, 0x30, 0xe3, 0x8b, 0x3e, 0xff, 0x33, 0xe1,
+  0x6f, 0xe0, 0x8b, 0x7e, 0xf6, 0xfc, 0x40, 0x1e, 0xfe, 0xdb, 0x7a, 0xf0,
+  0x00, 0x71, 0x9a, 0x40, 0x99, 0xad, 0xc0, 0xa3, 0x83, 0xfd, 0x71, 0x61,
+  0x6e, 0x76, 0xae, 0x52, 0x8e, 0xe7, 0xcb, 0x04, 0x42, 0x31, 0x6e, 0xf7,
+  0xe7, 0x23, 0xfe, 0xc7, 0x85, 0x7f, 0xfd, 0x8e, 0x29, 0xd1, 0xe2, 0x34,
+  0xb1, 0x5c, 0x2c, 0x15, 0x8a, 0xf1, 0x58, 0x89, 0xb8, 0x50, 0x22, 0x4d,
+  0xc7, 0x79, 0xb9, 0x52, 0x91, 0x44, 0x21, 0xc9, 0x95, 0xe2, 0x12, 0xe9,
+  0x7f, 0x32, 0xf1, 0x1f, 0x96, 0xfd, 0x09, 0x93, 0x77, 0x0d, 0x00, 0xac,
+  0x86, 0x4f, 0xc0, 0x4e, 0xb6, 0x07, 0xb5, 0xcb, 0x6c, 0xc0, 0x7e, 0xee,
+  0x01, 0x02, 0x8b, 0x0e, 0x58, 0xd2, 0x76, 0x00, 0x40, 0x7e, 0xf3, 0x2d,
+  0x8c, 0x1a, 0x0b, 0x91, 0x00, 0x10, 0x67, 0x34, 0x32, 0x79, 0xf7, 0x00,
+  0x00, 0x93, 0xbf, 0xf9, 0x8f, 0x40, 0x2b, 0x01, 0x00, 0xcd, 0x97, 0xa4,
+  0xe3, 0x00, 0x00, 0xbc, 0xe8, 0x18, 0x5c, 0xa8, 0x94, 0x17, 0x4c, 0xc6,
+  0x08, 0x00, 0x00, 0x44, 0xa0, 0x81, 0x2a, 0xb0, 0x41, 0x07, 0x0c, 0xc1,
+  0x14, 0xac, 0xc0, 0x0e, 0x9c, 0xc1, 0x1d, 0xbc, 0xc0, 0x17, 0x02, 0x61,
+  0x06, 0x44, 0x40, 0x0c, 0x24, 0xc0, 0x3c, 0x10, 0x42, 0x06, 0xe4, 0x80,
+  0x1c, 0x0a, 0xa1, 0x18, 0x96, 0x41, 0x19, 0x54, 0xc0, 0x3a, 0xd8, 0x04,
+  0xb5, 0xb0, 0x03, 0x1a, 0xa0, 0x11, 0x9a, 0xe1, 0x10, 0xb4, 0xc1, 0x31,
+  0x38, 0x0d, 0xe7, 0xe0, 0x12, 0x5c, 0x81, 0xeb, 0x70, 0x17, 0x06, 0x60,
+  0x18, 0x9e, 0xc2, 0x18, 0xbc, 0x86, 0x09, 0x04, 0x41, 0xc8, 0x08, 0x13,
+  0x61, 0x21, 0x3a, 0x88, 0x11, 0x62, 0x8e, 0xd8, 0x22, 0xce, 0x08, 0x17,
+  0x99, 0x8e, 0x04, 0x22, 0x61, 0x48, 0x34, 0x92, 0x80, 0xa4, 0x20, 0xe9,
+  0x88, 0x14, 0x51, 0x22, 0xc5, 0xc8, 0x72, 0xa4, 0x02, 0xa9, 0x42, 0x6a,
+  0x91, 0x5d, 0x48, 0x23, 0xf2, 0x2d, 0x72, 0x14, 0x39, 0x8d, 0x5c, 0x40,
+  0xfa, 0x90, 0xdb, 0xc8, 0x20, 0x32, 0x8a, 0xfc, 0x8a, 0xbc, 0x47, 0x31,
+  0x94, 0x81, 0xb2, 0x51, 0x03, 0xd4, 0x02, 0x75, 0x40, 0xb9, 0xa8, 0x1f,
+  0x1a, 0x8a, 0xc6, 0xa0, 0x73, 0xd1, 0x74, 0x34, 0x0f, 0x5d, 0x80, 0x96,
+  0xa2, 0x6b, 0xd1, 0x1a, 0xb4, 0x1e, 0x3d, 0x80, 0xb6, 0xa2, 0xa7, 0xd1,
+  0x4b, 0xe8, 0x75, 0x74, 0x00, 0x7d, 0x8a, 0x8e, 0x63, 0x80, 0xd1, 0x31,
+  0x0e, 0x66, 0x8c, 0xd9, 0x61, 0x5c, 0x8c, 0x87, 0x45, 0x60, 0x89, 0x58,
+  0x1a, 0x26, 0xc7, 0x16, 0x63, 0xe5, 0x58, 0x35, 0x56, 0x8f, 0x35, 0x63,
+  0x1d, 0x58, 0x37, 0x76, 0x15, 0x1b, 0xc0, 0x9e, 0x61, 0xef, 0x08, 0x24,
+  0x02, 0x8b, 0x80, 0x13, 0xec, 0x08, 0x5e, 0x84, 0x10, 0xc2, 0x6c, 0x82,
+  0x90, 0x90, 0x47, 0x58, 0x4c, 0x58, 0x43, 0xa8, 0x25, 0xec, 0x23, 0xb4,
+  0x12, 0xba, 0x08, 0x57, 0x09, 0x83, 0x84, 0x31, 0xc2, 0x27, 0x22, 0x93,
+  0xa8, 0x4f, 0xb4, 0x25, 0x7a, 0x12, 0xf9, 0xc4, 0x78, 0x62, 0x3a, 0xb1,
+  0x90, 0x58, 0x46, 0xac, 0x26, 0xee, 0x21, 0x1e, 0x21, 0x9e, 0x25, 0x5e,
+  0x27, 0x0e, 0x13, 0x5f, 0x93, 0x48, 0x24, 0x0e, 0xc9, 0x92, 0xe4, 0x4e,
+  0x0a, 0x21, 0x25, 0x90, 0x32, 0x49, 0x0b, 0x49, 0x6b, 0x48, 0xdb, 0x48,
+  0x2d, 0xa4, 0x53, 0xa4, 0x3e, 0xd2, 0x10, 0x69, 0x9c, 0x4c, 0x26, 0xeb,
+  0x90, 0x6d, 0xc9, 0xde, 0xe4, 0x08, 0xb2, 0x80, 0xac, 0x20, 0x97, 0x91,
+  0xb7, 0x90, 0x0f, 0x90, 0x4f, 0x92, 0xfb, 0xc9, 0xc3, 0xe4, 0xb7, 0x14,
+  0x3a, 0xc5, 0x88, 0xe2, 0x4c, 0x09, 0xa2, 0x24, 0x52, 0xa4, 0x94, 0x12,
+  0x4a, 0x35, 0x65, 0x3f, 0xe5, 0x04, 0xa5, 0x9f, 0x32, 0x42, 0x99, 0xa0,
+  0xaa, 0x51, 0xcd, 0xa9, 0x9e, 0xd4, 0x08, 0xaa, 0x88, 0x3a, 0x9f, 0x5a,
+  0x49, 0x6d, 0xa0, 0x76, 0x50, 0x2f, 0x53, 0x87, 0xa9, 0x13, 0x34, 0x75,
+  0x9a, 0x25, 0xcd, 0x9b, 0x16, 0x43, 0xcb, 0xa4, 0x2d, 0xa3, 0xd5, 0xd0,
+  0x9a, 0x69, 0x67, 0x69, 0xf7, 0x68, 0x2f, 0xe9, 0x74, 0xba, 0x09, 0xdd,
+  0x83, 0x1e, 0x45, 0x97, 0xd0, 0x97, 0xd2, 0x6b, 0xe8, 0x07, 0xe9, 0xe7,
+  0xe9, 0x83, 0xf4, 0x77, 0x0c, 0x0d, 0x86, 0x0d, 0x83, 0xc7, 0x48, 0x62,
+  0x28, 0x19, 0x6b, 0x19, 0x7b, 0x19, 0xa7, 0x18, 0xb7, 0x19, 0x2f, 0x99,
+  0x4c, 0xa6, 0x05, 0xd3, 0x97, 0x99, 0xc8, 0x54, 0x30, 0xd7, 0x32, 0x1b,
+  0x99, 0x67, 0x98, 0x0f, 0x98, 0x6f, 0x55, 0x58, 0x2a, 0xf6, 0x2a, 0x7c,
+  0x15, 0x91, 0xca, 0x12, 0x95, 0x3a, 0x95, 0x56, 0x95, 0x7e, 0x95, 0xe7,
+  0xaa, 0x54, 0x55, 0x73, 0x55, 0x3f, 0xd5, 0x79, 0xaa, 0x0b, 0x54, 0xab,
+  0x55, 0x0f, 0xab, 0x5e, 0x56, 0x7d, 0xa6, 0x46, 0x55, 0xb3, 0x50, 0xe3,
+  0xa9, 0x09, 0xd4, 0x16, 0xab, 0xd5, 0xa9, 0x1d, 0x55, 0xbb, 0xa9, 0x36,
+  0xae, 0xce, 0x52, 0x77, 0x52, 0x8f, 0x50, 0xcf, 0x51, 0x5f, 0xa3, 0xbe,
+  0x5f, 0xfd, 0x82, 0xfa, 0x63, 0x0d, 0xb2, 0x86, 0x85, 0x46, 0xa0, 0x86,
+  0x48, 0xa3, 0x54, 0x63, 0xb7, 0xc6, 0x19, 0x8d, 0x21, 0x16, 0xc6, 0x32,
+  0x65, 0xf1, 0x58, 0x42, 0xd6, 0x72, 0x56, 0x03, 0xeb, 0x2c, 0x6b, 0x98,
+  0x4d, 0x62, 0x5b, 0xb2, 0xf9, 0xec, 0x4c, 0x76, 0x05, 0xfb, 0x1b, 0x76,
+  0x2f, 0x7b, 0x4c, 0x53, 0x43, 0x73, 0xaa, 0x66, 0xac, 0x66, 0x91, 0x66,
+  0x9d, 0xe6, 0x71, 0xcd, 0x01, 0x0e, 0xc6, 0xb1, 0xe0, 0xf0, 0x39, 0xd9,
+  0x9c, 0x4a, 0xce, 0x21, 0xce, 0x0d, 0xce, 0x7b, 0x2d, 0x03, 0x2d, 0x3f,
+  0x2d, 0xb1, 0xd6, 0x6a, 0xad, 0x66, 0xad, 0x7e, 0xad, 0x37, 0xda, 0x7a,
+  0xda, 0xbe, 0xda, 0x62, 0xed, 0x72, 0xed, 0x16, 0xed, 0xeb, 0xda, 0xef,
+  0x75, 0x70, 0x9d, 0x40, 0x9d, 0x2c, 0x9d, 0xf5, 0x3a, 0x6d, 0x3a, 0xf7,
+  0x75, 0x09, 0xba, 0x36, 0xba, 0x51, 0xba, 0x85, 0xba, 0xdb, 0x75, 0xcf,
+  0xea, 0x3e, 0xd3, 0x63, 0xeb, 0x79, 0xe9, 0x09, 0xf5, 0xca, 0xf5, 0x0e,
+  0xe9, 0xdd, 0xd1, 0x47, 0xf5, 0x6d, 0xf4, 0xa3, 0xf5, 0x17, 0xea, 0xef,
+  0xd6, 0xef, 0xd1, 0x1f, 0x37, 0x30, 0x34, 0x08, 0x36, 0x90, 0x19, 0x6c,
+  0x31, 0x38, 0x63, 0xf0, 0xcc, 0x90, 0x63, 0xe8, 0x6b, 0x98, 0x69, 0xb8,
+  0xd1, 0xf0, 0x84, 0xe1, 0xa8, 0x11, 0xcb, 0x68, 0xba, 0x91, 0xc4, 0x68,
+  0xa3, 0xd1, 0x49, 0xa3, 0x27, 0xb8, 0x26, 0xee, 0x87, 0x67, 0xe3, 0x35,
+  0x78, 0x17, 0x3e, 0x66, 0xac, 0x6f, 0x1c, 0x62, 0xac, 0x34, 0xde, 0x65,
+  0xdc, 0x6b, 0x3c, 0x61, 0x62, 0x69, 0x32, 0xdb, 0xa4, 0xc4, 0xa4, 0xc5,
+  0xe4, 0xbe, 0x29, 0xcd, 0x94, 0x6b, 0x9a, 0x66, 0xba, 0xd1, 0xb4, 0xd3,
+  0x74, 0xcc, 0xcc, 0xc8, 0x2c, 0xdc, 0xac, 0xd8, 0xac, 0xc9, 0xec, 0x8e,
+  0x39, 0xd5, 0x9c, 0x6b, 0x9e, 0x61, 0xbe, 0xd9, 0xbc, 0xdb, 0xfc, 0x8d,
+  0x85, 0xa5, 0x45, 0x9c, 0xc5, 0x4a, 0x8b, 0x36, 0x8b, 0xc7, 0x96, 0xda,
+  0x96, 0x7c, 0xcb, 0x05, 0x96, 0x4d, 0x96, 0xf7, 0xac, 0x98, 0x56, 0x3e,
+  0x56, 0x79, 0x56, 0xf5, 0x56, 0xd7, 0xac, 0x49, 0xd6, 0x5c, 0xeb, 0x2c,
+  0xeb, 0x6d, 0xd6, 0x57, 0x6c, 0x50, 0x1b, 0x57, 0x9b, 0x0c, 0x9b, 0x3a,
+  0x9b, 0xcb, 0xb6, 0xa8, 0xad, 0x9b, 0xad, 0xc4, 0x76, 0x9b, 0x6d, 0xdf,
+  0x14, 0xe2, 0x14, 0x8f, 0x29, 0xd2, 0x29, 0xf5, 0x53, 0x6e, 0xda, 0x31,
+  0xec, 0xfc, 0xec, 0x0a, 0xec, 0x9a, 0xec, 0x06, 0xed, 0x39, 0xf6, 0x61,
+  0xf6, 0x25, 0xf6, 0x6d, 0xf6, 0xcf, 0x1d, 0xcc, 0x1c, 0x12, 0x1d, 0xd6,
+  0x3b, 0x74, 0x3b, 0x7c, 0x72, 0x74, 0x75, 0xcc, 0x76, 0x6c, 0x70, 0xbc,
+  0xeb, 0xa4, 0xe1, 0x34, 0xc3, 0xa9, 0xc4, 0xa9, 0xc3, 0xe9, 0x57, 0x67,
+  0x1b, 0x67, 0xa1, 0x73, 0x9d, 0xf3, 0x35, 0x17, 0xa6, 0x4b, 0x90, 0xcb,
+  0x12, 0x97, 0x76, 0x97, 0x17, 0x53, 0x6d, 0xa7, 0x8a, 0xa7, 0x6e, 0x9f,
+  0x7a, 0xcb, 0x95, 0xe5, 0x1a, 0xee, 0xba, 0xd2, 0xb5, 0xd3, 0xf5, 0xa3,
+  0x9b, 0xbb, 0x9b, 0xdc, 0xad, 0xd9, 0x6d, 0xd4, 0xdd, 0xcc, 0x3d, 0xc5,
+  0x7d, 0xab, 0xfb, 0x4d, 0x2e, 0x9b, 0x1b, 0xc9, 0x5d, 0xc3, 0x3d, 0xef,
+  0x41, 0xf4, 0xf0, 0xf7, 0x58, 0xe2, 0x71, 0xcc, 0xe3, 0x9d, 0xa7, 0x9b,
+  0xa7, 0xc2, 0xf3, 0x90, 0xe7, 0x2f, 0x5e, 0x76, 0x5e, 0x59, 0x5e, 0xfb,
+  0xbd, 0x1e, 0x4f, 0xb3, 0x9c, 0x26, 0x9e, 0xd6, 0x30, 0x6d, 0xc8, 0xdb,
+  0xc4, 0x5b, 0xe0, 0xbd, 0xcb, 0x7b, 0x60, 0x3a, 0x3e, 0x3d, 0x65, 0xfa,
+  0xce, 0xe9, 0x03, 0x3e, 0xc6, 0x3e, 0x02, 0x9f, 0x7a, 0x9f, 0x87, 0xbe,
+  0xa6, 0xbe, 0x22, 0xdf, 0x3d, 0xbe, 0x23, 0x7e, 0xd6, 0x7e, 0x99, 0x7e,
+  0x07, 0xfc, 0x9e, 0xfb, 0x3b, 0xfa, 0xcb, 0xfd, 0x8f, 0xf8, 0xbf, 0xe1,
+  0x79, 0xf2, 0x16, 0xf1, 0x4e, 0x05, 0x60, 0x01, 0xc1, 0x01, 0xe5, 0x01,
+  0xbd, 0x81, 0x1a, 0x81, 0xb3, 0x03, 0x6b, 0x03, 0x1f, 0x04, 0x99, 0x04,
+  0xa5, 0x07, 0x35, 0x05, 0x8d, 0x05, 0xbb, 0x06, 0x2f, 0x0c, 0x3e, 0x15,
+  0x42, 0x0c, 0x09, 0x0d, 0x59, 0x1f, 0x72, 0x93, 0x6f, 0xc0, 0x17, 0xf2,
+  0x1b, 0xf9, 0x63, 0x33, 0xdc, 0x67, 0x2c, 0x9a, 0xd1, 0x15, 0xca, 0x08,
+  0x9d, 0x15, 0x5a, 0x1b, 0xfa, 0x30, 0xcc, 0x26, 0x4c, 0x1e, 0xd6, 0x11,
+  0x8e, 0x86, 0xcf, 0x08, 0xdf, 0x10, 0x7e, 0x6f, 0xa6, 0xf9, 0x4c, 0xe9,
+  0xcc, 0xb6, 0x08, 0x88, 0xe0, 0x47, 0x6c, 0x88, 0xb8, 0x1f, 0x69, 0x19,
+  0x99, 0x17, 0xf9, 0x7d, 0x14, 0x29, 0x2a, 0x32, 0xaa, 0x2e, 0xea, 0x51,
+  0xb4, 0x53, 0x74, 0x71, 0x74, 0xf7, 0x2c, 0xd6, 0xac, 0xe4, 0x59, 0xfb,
+  0x67, 0xbd, 0x8e, 0xf1, 0x8f, 0xa9, 0x8c, 0xb9, 0x3b, 0xdb, 0x6a, 0xb6,
+  0x72, 0x76, 0x67, 0xac, 0x6a, 0x6c, 0x52, 0x6c, 0x63, 0xec, 0x9b, 0xb8,
+  0x80, 0xb8, 0xaa, 0xb8, 0x81, 0x78, 0x87, 0xf8, 0x45, 0xf1, 0x97, 0x12,
+  0x74, 0x13, 0x24, 0x09, 0xed, 0x89, 0xe4, 0xc4, 0xd8, 0xc4, 0x3d, 0x89,
+  0xe3, 0x73, 0x02, 0xe7, 0x6c, 0x9a, 0x33, 0x9c, 0xe4, 0x9a, 0x54, 0x96,
+  0x74, 0x63, 0xae, 0xe5, 0xdc, 0xa2, 0xb9, 0x17, 0xe6, 0xe9, 0xce, 0xcb,
+  0x9e, 0x77, 0x3c, 0x59, 0x35, 0x59, 0x90, 0x7c, 0x38, 0x85, 0x98, 0x12,
+  0x97, 0xb2, 0x3f, 0xe5, 0x83, 0x20, 0x42, 0x50, 0x2f, 0x18, 0x4f, 0xe5,
+  0xa7, 0x6e, 0x4d, 0x1d, 0x13, 0xf2, 0x84, 0x9b, 0x85, 0x4f, 0x45, 0xbe,
+  0xa2, 0x8d, 0xa2, 0x51, 0xb1, 0xb7, 0xb8, 0x4a, 0x3c, 0x92, 0xe6, 0x9d,
+  0x56, 0x95, 0xf6, 0x38, 0xdd, 0x3b, 0x7d, 0x43, 0xfa, 0x68, 0x86, 0x4f,
+  0x46, 0x75, 0xc6, 0x33, 0x09, 0x4f, 0x52, 0x2b, 0x79, 0x91, 0x19, 0x92,
+  0xb9, 0x23, 0xf3, 0x4d, 0x56, 0x44, 0xd6, 0xde, 0xac, 0xcf, 0xd9, 0x71,
+  0xd9, 0x2d, 0x39, 0x94, 0x9c, 0x94, 0x9c, 0xa3, 0x52, 0x0d, 0x69, 0x96,
+  0xb4, 0x2b, 0xd7, 0x30, 0xb7, 0x28, 0xb7, 0x4f, 0x66, 0x2b, 0x2b, 0x93,
+  0x0d, 0xe4, 0x79, 0xe6, 0x6d, 0xca, 0x1b, 0x93, 0x87, 0xca, 0xf7, 0xe4,
+  0x23, 0xf9, 0x73, 0xf3, 0xdb, 0x15, 0x6c, 0x85, 0x4c, 0xd1, 0xa3, 0xb4,
+  0x52, 0xae, 0x50, 0x0e, 0x16, 0x4c, 0x2f, 0xa8, 0x2b, 0x78, 0x5b, 0x18,
+  0x5b, 0x78, 0xb8, 0x48, 0xbd, 0x48, 0x5a, 0xd4, 0x33, 0xdf, 0x66, 0xfe,
+  0xea, 0xf9, 0x23, 0x0b, 0x82, 0x16, 0x7c, 0xbd, 0x90, 0xb0, 0x50, 0xb8,
+  0xb0, 0xb3, 0xd8, 0xb8, 0x78, 0x59, 0xf1, 0xe0, 0x22, 0xbf, 0x45, 0xbb,
+  0x16, 0x23, 0x8b, 0x53, 0x17, 0x77, 0x2e, 0x31, 0x5d, 0x52, 0xba, 0x64,
+  0x78, 0x69, 0xf0, 0xd2, 0x7d, 0xcb, 0x68, 0xcb, 0xb2, 0x96, 0xfd, 0x50,
+  0xe2, 0x58, 0x52, 0x55, 0xf2, 0x6a, 0x79, 0xdc, 0xf2, 0x8e, 0x52, 0x83,
+  0xd2, 0xa5, 0xa5, 0x43, 0x2b, 0x82, 0x57, 0x34, 0x95, 0xa9, 0x94, 0xc9,
+  0xcb, 0x6e, 0xae, 0xf4, 0x5a, 0xb9, 0x63, 0x15, 0x61, 0x95, 0x64, 0x55,
+  0xef, 0x6a, 0x97, 0xd5, 0x5b, 0x56, 0x7f, 0x2a, 0x17, 0x95, 0x5f, 0xac,
+  0x70, 0xac, 0xa8, 0xae, 0xf8, 0xb0, 0x46, 0xb8, 0xe6, 0xe2, 0x57, 0x4e,
+  0x5f, 0xd5, 0x7c, 0xf5, 0x79, 0x6d, 0xda, 0xda, 0xde, 0x4a, 0xb7, 0xca,
+  0xed, 0xeb, 0x48, 0xeb, 0xa4, 0xeb, 0x6e, 0xac, 0xf7, 0x59, 0xbf, 0xaf,
+  0x4a, 0xbd, 0x6a, 0x41, 0xd5, 0xd0, 0x86, 0xf0, 0x0d, 0xad, 0x1b, 0xf1,
+  0x8d, 0xe5, 0x1b, 0x5f, 0x6d, 0x4a, 0xde, 0x74, 0xa1, 0x7a, 0x6a, 0xf5,
+  0x8e, 0xcd, 0xb4, 0xcd, 0xca, 0xcd, 0x03, 0x35, 0x61, 0x35, 0xed, 0x5b,
+  0xcc, 0xb6, 0xac, 0xdb, 0xf2, 0xa1, 0x36, 0xa3, 0xf6, 0x7a, 0x9d, 0x7f,
+  0x5d, 0xcb, 0x56, 0xfd, 0xad, 0xab, 0xb7, 0xbe, 0xd9, 0x26, 0xda, 0xd6,
+  0xbf, 0xdd, 0x77, 0x7b, 0xf3, 0x0e, 0x83, 0x1d, 0x15, 0x3b, 0xde, 0xef,
+  0x94, 0xec, 0xbc, 0xb5, 0x2b, 0x78, 0x57, 0x6b, 0xbd, 0x45, 0x7d, 0xf5,
+  0x6e, 0xd2, 0xee, 0x82, 0xdd, 0x8f, 0x1a, 0x62, 0x1b, 0xba, 0xbf, 0xe6,
+  0x7e, 0xdd, 0xb8, 0x47, 0x77, 0x4f, 0xc5, 0x9e, 0x8f, 0x7b, 0xa5, 0x7b,
+  0x07, 0xf6, 0x45, 0xef, 0xeb, 0x6a, 0x74, 0x6f, 0x6c, 0xdc, 0xaf, 0xbf,
+  0xbf, 0xb2, 0x09, 0x6d, 0x52, 0x36, 0x8d, 0x1e, 0x48, 0x3a, 0x70, 0xe5,
+  0x9b, 0x80, 0x6f, 0xda, 0x9b, 0xed, 0x9a, 0x77, 0xb5, 0x70, 0x5a, 0x2a,
+  0x0e, 0xc2, 0x41, 0xe5, 0xc1, 0x27, 0xdf, 0xa6, 0x7c, 0x7b, 0xe3, 0x50,
+  0xe8, 0xa1, 0xce, 0xc3, 0xdc, 0xc3, 0xcd, 0xdf, 0x99, 0x7f, 0xb7, 0xf5,
+  0x08, 0xeb, 0x48, 0x79, 0x2b, 0xd2, 0x3a, 0xbf, 0x75, 0xac, 0x2d, 0xa3,
+  0x6d, 0xa0, 0x3d, 0xa1, 0xbd, 0xef, 0xe8, 0x8c, 0xa3, 0x9d, 0x1d, 0x5e,
+  0x1d, 0x47, 0xbe, 0xb7, 0xff, 0x7e, 0xef, 0x31, 0xe3, 0x63, 0x75, 0xc7,
+  0x35, 0x8f, 0x57, 0x9e, 0xa0, 0x9d, 0x28, 0x3d, 0xf1, 0xf9, 0xe4, 0x82,
+  0x93, 0xe3, 0xa7, 0x64, 0xa7, 0x9e, 0x9d, 0x4e, 0x3f, 0x3d, 0xd4, 0x99,
+  0xdc, 0x79, 0xf7, 0x4c, 0xfc, 0x99, 0x6b, 0x5d, 0x51, 0x5d, 0xbd, 0x67,
+  0x43, 0xcf, 0x9e, 0x3f, 0x17, 0x74, 0xee, 0x4c, 0xb7, 0x5f, 0xf7, 0xc9,
+  0xf3, 0xde, 0xe7, 0x8f, 0x5d, 0xf0, 0xbc, 0x70, 0xf4, 0x22, 0xf7, 0x62,
+  0xdb, 0x25, 0xb7, 0x4b, 0xad, 0x3d, 0xae, 0x3d, 0x47, 0x7e, 0x70, 0xfd,
+  0xe1, 0x48, 0xaf, 0x5b, 0x6f, 0xeb, 0x65, 0xf7, 0xcb, 0xed, 0x57, 0x3c,
+  0xae, 0x74, 0xf4, 0x4d, 0xeb, 0x3b, 0xd1, 0xef, 0xd3, 0x7f, 0xfa, 0x6a,
+  0xc0, 0xd5, 0x73, 0xd7, 0xf8, 0xd7, 0x2e, 0x5d, 0x9f, 0x79, 0xbd, 0xef,
+  0xc6, 0xec, 0x1b, 0xb7, 0x6e, 0x26, 0xdd, 0x1c, 0xb8, 0x25, 0xba, 0xf5,
+  0xf8, 0x76, 0xf6, 0xed, 0x17, 0x77, 0x0a, 0xee, 0x4c, 0xdc, 0x5d, 0x7a,
+  0x8f, 0x78, 0xaf, 0xfc, 0xbe, 0xda, 0xfd, 0xea, 0x07, 0xfa, 0x0f, 0xea,
+  0x7f, 0xb4, 0xfe, 0xb1, 0x65, 0xc0, 0x6d, 0xe0, 0xf8, 0x60, 0xc0, 0x60,
+  0xcf, 0xc3, 0x59, 0x0f, 0xef, 0x0e, 0x09, 0x87, 0x9e, 0xfe, 0x94, 0xff,
+  0xd3, 0x87, 0xe1, 0xd2, 0x47, 0xcc, 0x47, 0xd5, 0x23, 0x46, 0x23, 0x8d,
+  0x8f, 0x9d, 0x1f, 0x1f, 0x1b, 0x0d, 0x1a, 0xbd, 0xf2, 0x64, 0xce, 0x93,
+  0xe1, 0xa7, 0xb2, 0xa7, 0x13, 0xcf, 0xca, 0x7e, 0x56, 0xff, 0x79, 0xeb,
+  0x73, 0xab, 0xe7, 0xdf, 0xfd, 0xe2, 0xfb, 0x4b, 0xcf, 0x58, 0xfc, 0xd8,
+  0xf0, 0x0b, 0xf9, 0x8b, 0xcf, 0xbf, 0xae, 0x79, 0xa9, 0xf3, 0x72, 0xef,
+  0xab, 0xa9, 0xaf, 0x3a, 0xc7, 0x23, 0xc7, 0x1f, 0xbc, 0xce, 0x79, 0x3d,
+  0xf1, 0xa6, 0xfc, 0xad, 0xce, 0xdb, 0x7d, 0xef, 0xb8, 0xef, 0xba, 0xdf,
+  0xc7, 0xbd, 0x1f, 0x99, 0x28, 0xfc, 0x40, 0xfe, 0x50, 0xf3, 0xd1, 0xfa,
+  0x63, 0xc7, 0xa7, 0xd0, 0x4f, 0xf7, 0x3e, 0xe7, 0x7c, 0xfe, 0xfc, 0x2f,
+  0xf7, 0x84, 0xf3, 0xfb, 0x80, 0x39, 0x25, 0x11, 0x00, 0x00, 0x00, 0x09,
+  0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13,
+  0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d,
+  0x45, 0x07, 0xdc, 0x0b, 0x0e, 0x0e, 0x25, 0x16, 0x28, 0x9e, 0x78, 0x6a,
+  0x00, 0x00, 0x00, 0x0c, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8,
+  0xff, 0xff, 0x3f, 0x00, 0x05, 0xfe, 0x02, 0xfe, 0xdc, 0xcc, 0x59, 0xe7,
+  0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
+static IWICImagingFactory *factory;
+
+static const char *debugstr_guid(const GUID *guid)
+{
+    static char buf[50];
+
+    if (!guid) return "(null)";
+    sprintf(buf, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
+            guid->Data1, guid->Data2, guid->Data3, guid->Data4[0],
+            guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4],
+            guid->Data4[5], guid->Data4[6], guid->Data4[7]);
+    return buf;
+}
+
+static IWICBitmapDecoder *create_decoder(const void *image_data, UINT image_size)
+{
+    HGLOBAL hmem;
+    BYTE *data;
+    HRESULT hr;
+    IWICBitmapDecoder *decoder = NULL;
+    IStream *stream;
+    GUID format;
+
+    hmem = GlobalAlloc(0, image_size);
+    data = GlobalLock(hmem);
+    memcpy(data, image_data, image_size);
+    GlobalUnlock(hmem);
+
+    hr = CreateStreamOnHGlobal(hmem, TRUE, &stream);
+    ok(hr == S_OK, "CreateStreamOnHGlobal error %#x\n", hr);
+
+    hr = IWICImagingFactory_CreateDecoderFromStream(factory, stream, NULL, 0, &decoder);
+    ok(hr == S_OK, "CreateDecoderFromStream error %#x\n", hr);
+
+    hr = IWICBitmapDecoder_GetContainerFormat(decoder, &format);
+    ok(hr == S_OK, "GetContainerFormat error %#x\n", hr);
+    ok(IsEqualGUID(&format, &GUID_ContainerFormatPng),
+       "wrong container format %s\n", debugstr_guid(&format));
+
+    IStream_Release(stream);
+
+    return decoder;
+}
+
+static void test_color_contexts(void)
+{
+    HRESULT hr;
+    IWICBitmapDecoder *decoder;
+    IWICBitmapFrameDecode *frame;
+    IWICColorContext *context;
+    WICColorContextType type;
+    UINT count, colorspace, size;
+    BYTE *buffer;
+
+    decoder = create_decoder(png_no_color_profile, sizeof(png_no_color_profile));
+    ok(decoder != 0, "Failed to load PNG image data\n");
+
+    /* global color context */
+    hr = IWICBitmapDecoder_GetColorContexts(decoder, 0, NULL, NULL);
+    ok(hr == WINCODEC_ERR_UNSUPPORTEDOPERATION, "GetColorContexts error %#x\n", hr);
+
+    count = 0xdeadbeef;
+    hr = IWICBitmapDecoder_GetColorContexts(decoder, 0, NULL, &count);
+    ok(hr == WINCODEC_ERR_UNSUPPORTEDOPERATION, "GetColorContexts error %#x\n", hr);
+    ok(count == 0xdeadbeef, "unexpected count %u\n", count);
+
+    /* frame color context */
+    hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
+    ok(hr == S_OK, "GetFrame error %#x\n", hr);
+
+    hr = IWICBitmapFrameDecode_GetColorContexts(frame, 0, NULL, NULL);
+    ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+
+    count = 0xdeadbeef;
+    hr = IWICBitmapFrameDecode_GetColorContexts(frame, 0, NULL, &count);
+    ok(hr == S_OK, "GetColorContexts error %#x\n", hr);
+    ok(!count, "unexpected count %u\n", count);
+
+    IWICBitmapFrameDecode_Release(frame);
+    IWICBitmapDecoder_Release(decoder);
+
+    decoder = create_decoder(png_color_profile, sizeof(png_color_profile));
+    ok(decoder != 0, "Failed to load PNG image data\n");
+
+    /* global color context */
+    count = 0xdeadbeef;
+    hr = IWICBitmapDecoder_GetColorContexts(decoder, 0, NULL, &count);
+    ok(hr == WINCODEC_ERR_UNSUPPORTEDOPERATION, "GetColorContexts error %#x\n", hr);
+    ok(count == 0xdeadbeef, "unexpected count %u\n", count);
+
+    /* frame color context */
+    hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
+    ok(hr == S_OK, "GetFrame error %#x\n", hr);
+
+    count = 0xdeadbeef;
+    hr = IWICBitmapFrameDecode_GetColorContexts(frame, 0, NULL, &count);
+    ok(hr == S_OK, "GetColorContexts error %#x\n", hr);
+    ok(count == 1, "unexpected count %u\n", count);
+
+    hr = IWICImagingFactory_CreateColorContext(factory, NULL);
+    ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+
+    hr = IWICImagingFactory_CreateColorContext(factory, &context);
+    ok(hr == S_OK, "CreateColorContext error %#x\n", hr);
+
+    hr = IWICColorContext_GetType(context, NULL);
+    ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+
+    type = 0xdeadbeef;
+    hr = IWICColorContext_GetType(context, &type);
+    ok(hr == S_OK, "GetType error %#x\n", hr);
+    ok(type == WICColorContextUninitialized, "unexpected type %u\n", type);
+
+    hr = IWICColorContext_GetProfileBytes(context, 0, NULL, NULL);
+    ok(hr == WINCODEC_ERR_NOTINITIALIZED, "GetProfileBytes error %#x\n", hr);
+
+    size = 0;
+    hr = IWICColorContext_GetProfileBytes(context, 0, NULL, &size);
+    ok(hr == WINCODEC_ERR_NOTINITIALIZED, "GetProfileBytes error %#x\n", hr);
+    ok(!size, "unexpected size %u\n", size);
+
+    hr = IWICColorContext_GetExifColorSpace(context, NULL);
+    ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+
+    colorspace = 0xdeadbeef;
+    hr = IWICColorContext_GetExifColorSpace(context, &colorspace);
+    ok(hr == S_OK, "GetExifColorSpace error %#x\n", hr);
+    ok(colorspace == 0xffffffff, "unexpected color space %u\n", colorspace);
+
+    hr = IWICColorContext_InitializeFromExifColorSpace(context, 0);
+    ok(hr == S_OK, "InitializeFromExifColorSpace error %#x\n", hr);
+
+    hr = IWICColorContext_InitializeFromExifColorSpace(context, 1);
+    ok(hr == S_OK, "InitializeFromExifColorSpace error %#x\n", hr);
+
+    hr = IWICColorContext_InitializeFromExifColorSpace(context, 2);
+    ok(hr == S_OK, "InitializeFromExifColorSpace error %#x\n", hr);
+
+    colorspace = 0xdeadbeef;
+    hr = IWICColorContext_GetExifColorSpace(context, &colorspace);
+    ok(hr == S_OK, "GetExifColorSpace error %#x\n", hr);
+    ok(colorspace == 2, "unexpected color space %u\n", colorspace);
+
+    size = 0;
+    hr = IWICColorContext_GetProfileBytes(context, 0, NULL, &size);
+    ok(hr == WINCODEC_ERR_NOTINITIALIZED, "GetProfileBytes error %#x\n", hr);
+    ok(!size, "unexpected size %u\n", size);
+
+    type = 0xdeadbeef;
+    hr = IWICColorContext_GetType(context, &type);
+    ok(hr == S_OK, "GetType error %#x\n", hr);
+    ok(type == WICColorContextExifColorSpace, "unexpected type %u\n", type);
+
+    hr = IWICBitmapFrameDecode_GetColorContexts(frame, count, &context, &count);
+    ok(hr == WINCODEC_ERR_WRONGSTATE, "GetColorContexts error %#x\n", hr);
+
+    IWICColorContext_Release(context);
+    IWICBitmapFrameDecode_Release(frame);
+
+    hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
+    ok(hr == S_OK, "GetFrame error %#x\n", hr);
+
+    hr = IWICImagingFactory_CreateColorContext(factory, &context);
+    ok(hr == S_OK, "CreateColorContext error %#x\n", hr);
+
+    count = 1;
+    hr = IWICBitmapFrameDecode_GetColorContexts(frame, count, &context, &count);
+    ok(hr == S_OK, "GetColorContexts error %#x\n", hr);
+
+    hr = IWICColorContext_GetProfileBytes(context, 0, NULL, NULL);
+    ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got %#x\n", hr);
+
+    size = 0;
+    hr = IWICColorContext_GetProfileBytes(context, 0, NULL, &size);
+    ok(hr == S_OK, "GetProfileBytes error %#x\n", hr);
+    ok(size, "unexpected size %u\n", size);
+
+    buffer = HeapAlloc(GetProcessHeap(), 0, size);
+    hr = IWICColorContext_GetProfileBytes(context, size, buffer, &size);
+    ok(hr == S_OK, "GetProfileBytes error %#x\n", hr);
+    HeapFree(GetProcessHeap(), 0, buffer);
+
+    type = 0xdeadbeef;
+    hr = IWICColorContext_GetType(context, &type);
+    ok(hr == S_OK, "GetType error %#x\n", hr);
+    ok(type == WICColorContextProfile, "unexpected type %u\n", type);
+
+    colorspace = 0xdeadbeef;
+    hr = IWICColorContext_GetExifColorSpace(context, &colorspace);
+    ok(hr == S_OK, "GetExifColorSpace error %#x\n", hr);
+    ok(colorspace == 0xffffffff, "unexpected color space %u\n", colorspace);
+
+    hr = IWICColorContext_InitializeFromExifColorSpace(context, 1);
+    ok(hr == WINCODEC_ERR_WRONGSTATE, "InitializeFromExifColorSpace error %#x\n", hr);
+
+    IWICColorContext_Release(context);
+    IWICBitmapFrameDecode_Release(frame);
+    IWICBitmapDecoder_Release(decoder);
+}
+
+START_TEST(pngformat)
+{
+    HRESULT hr;
+
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_IWICImagingFactory, (void **)&factory);
+    ok(hr == S_OK, "CoCreateInstance error %#x\n", hr);
+    if (FAILED(hr)) return;
+
+    test_color_contexts();
+
+    IWICImagingFactory_Release(factory);
+    CoUninitialize();
+}
diff --git a/rostests/winetests/windowscodecs/stream.c b/rostests/winetests/windowscodecs/stream.c
new file mode 100644 (file)
index 0000000..5f311b8
--- /dev/null
@@ -0,0 +1,744 @@
+/*
+ * Copyright 2009 Tony Wasserka
+ *
+ * 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 "wine/test.h"
+
+#define COBJMACROS
+#include "wincodec.h"
+
+static void test_StreamOnMemory(void)
+{
+    IWICImagingFactory *pFactory;
+    IWICStream *pStream, *pBufStream;
+    const BYTE CmpMem[] = {
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+        0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+    };
+    const BYTE CmpMemOverlap[] = {
+        0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+        0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+    };
+    const BYTE ZeroMem[10] = {0};
+    BYTE Memory[64], MemBuf[64];
+    LARGE_INTEGER LargeNull, LargeInt, SeekPos;
+    ULARGE_INTEGER uLargeNull, uNewPos;
+    ULONG uBytesRead, uBytesWritten;
+    HRESULT hr;
+    STATSTG Stats;
+
+    LargeNull.QuadPart = 0;
+    uLargeNull.QuadPart = 0;
+    SeekPos.QuadPart = 5;
+
+    memcpy(Memory, CmpMem, sizeof(CmpMem));
+
+    CoInitialize(NULL);
+
+    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, &IID_IWICImagingFactory, (void**)&pFactory);
+    if(FAILED(hr)) {
+        skip("CoCreateInstance returned with %#x, expected %#x\n", hr, S_OK);
+        return;
+    }
+
+    hr = IWICImagingFactory_CreateStream(pFactory, &pStream);
+    ok(hr == S_OK, "CreateStream returned with %#x, expected %#x\n", hr, S_OK);
+    if(FAILED(hr)) {
+        skip("Failed to create stream\n");
+        return;
+    }
+
+    /* InitializeFromMemory */
+    hr = IWICStream_InitializeFromMemory(pStream, NULL, sizeof(Memory));   /* memory = NULL */
+    ok(hr == E_INVALIDARG, "InitializeFromMemory returned with %#x, expected %#x\n", hr, E_INVALIDARG);
+
+    hr = IWICStream_InitializeFromMemory(pStream, Memory, 0);   /* size = 0 */
+    ok(hr == S_OK, "InitializeFromMemory returned with %#x, expected %#x\n", hr, S_OK);
+
+    hr = IWICStream_InitializeFromMemory(pStream, Memory, sizeof(Memory));   /* stream already initialized */
+    ok(hr == WINCODEC_ERR_WRONGSTATE, "InitializeFromMemory returned with %#x, expected %#x\n", hr, WINCODEC_ERR_WRONGSTATE);
+
+    /* recreate stream */
+    IWICStream_Release(pStream);
+    hr = IWICImagingFactory_CreateStream(pFactory, &pStream);
+    ok(hr == S_OK, "CreateStream failed with %#x\n", hr);
+
+    hr = IWICStream_InitializeFromMemory(pStream, Memory, sizeof(Memory));
+    ok(hr == S_OK, "InitializeFromMemory returned with %#x, expected %#x\n", hr, S_OK);
+
+    /* IWICStream does not maintain an independent copy of the backing memory buffer. */
+    memcpy(Memory, ZeroMem, sizeof(ZeroMem));
+    hr = IWICStream_Read(pStream, MemBuf, sizeof(ZeroMem), &uBytesRead);
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+    if(SUCCEEDED(hr)) {
+        ok(uBytesRead == sizeof(ZeroMem), "Read %u bytes\n", uBytesRead);
+        ok(memcmp(MemBuf, ZeroMem, sizeof(ZeroMem)) == 0, "Read returned invalid data!\n");
+    }
+
+    IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_SET, NULL);
+
+    hr = IWICStream_Write(pStream, CmpMem, sizeof(CmpMem), &uBytesWritten);
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+
+    /* Seek */
+    hr = IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_SET, &uNewPos);
+    ok(hr == S_OK, "Seek returned with %#x, expected %#x\n", hr, S_OK);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 0, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, 0);
+
+    hr = IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_SET, NULL);
+    ok(hr == S_OK, "Seek returned with %#x, expected %#x\n", hr, S_OK);
+
+    LargeInt.u.HighPart = 1;
+    LargeInt.u.LowPart = 0;
+    uNewPos.u.HighPart = 0xdeadbeef;
+    uNewPos.u.LowPart = 0xdeadbeef;
+    hr = IWICStream_Seek(pStream, LargeInt, STREAM_SEEK_SET, &uNewPos);
+    ok(hr == HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW), "Seek returned with %#x, expected %#x\n", hr, HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW));
+    ok(uNewPos.u.HighPart == 0xdeadbeef && uNewPos.u.LowPart == 0xdeadbeef, "Seek cursor initialized to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0xdeadbeef, 0xdeadbeef);
+    hr = IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+    ok(hr == S_OK, "Seek returned with %#x, expected %#x\n", hr, S_OK);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 0, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, 0);
+
+    LargeInt.QuadPart = sizeof(Memory) + 10;
+    uNewPos.u.HighPart = 0xdeadbeef;
+    uNewPos.u.LowPart = 0xdeadbeef;
+    hr = IWICStream_Seek(pStream, LargeInt, STREAM_SEEK_SET, &uNewPos);
+    ok(hr == E_INVALIDARG, "Seek returned with %#x, expected %#x\n", hr, E_INVALIDARG);
+    ok(uNewPos.u.HighPart == 0xdeadbeef && uNewPos.u.LowPart == 0xdeadbeef, "Seek cursor initialized to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0xdeadbeef, 0xdeadbeef);
+    hr = IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+    ok(hr == S_OK, "Seek returned with %#x, expected %#x\n", hr, S_OK);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 0, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, 0);
+
+    LargeInt.QuadPart = 1;
+    uNewPos.u.HighPart = 0xdeadbeef;
+    uNewPos.u.LowPart = 0xdeadbeef;
+    hr = IWICStream_Seek(pStream, LargeInt, STREAM_SEEK_END, &uNewPos);
+    ok(hr == E_INVALIDARG, "Seek returned with %#x, expected %#x\n", hr, E_INVALIDARG);
+    ok(uNewPos.u.HighPart == 0xdeadbeef && uNewPos.u.LowPart == 0xdeadbeef, "Seek cursor initialized to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0xdeadbeef, 0xdeadbeef);
+    hr = IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+    ok(hr == S_OK, "Seek returned with %#x, expected %#x\n", hr, S_OK);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 0, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, 0);
+
+    LargeInt.QuadPart = -1;
+    hr = IWICStream_Seek(pStream, LargeInt, STREAM_SEEK_END, &uNewPos);
+    ok(hr == S_OK, "Seek returned with %#x, expected %#x\n", hr, S_OK);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == sizeof(Memory) - 1, "bSeek cursor moved to position (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart);
+
+    IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_SET, &uNewPos); /* reset seek pointer */
+    LargeInt.QuadPart = -(LONGLONG)sizeof(Memory) - 5;
+    uNewPos.u.HighPart = 0xdeadbeef;
+    uNewPos.u.LowPart = 0xdeadbeef;
+    hr = IWICStream_Seek(pStream, LargeInt, STREAM_SEEK_END, &uNewPos);
+    ok(hr == HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW),
+       "Seek returned with %#x, expected %#x\n", hr, HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW));
+    ok(uNewPos.u.HighPart == 0xdeadbeef && uNewPos.u.LowPart == 0xdeadbeef, "Seek cursor initialized to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0xdeadbeef, 0xdeadbeef);
+    hr = IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+    ok(hr == S_OK, "Seek returned with %#x, expected %#x\n", hr, S_OK);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 0, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, 0); /* remains unchanged */
+    IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_SET, NULL);
+
+
+    /* Read */
+    hr = IWICStream_Read(pStream, MemBuf, 12, &uBytesRead);
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+    if(SUCCEEDED(hr)) {
+        ok(uBytesRead == 12, "Read %u bytes, expected %u\n", uBytesRead, 12);
+        ok(memcmp(MemBuf, CmpMem, 12) == 0, "Read returned invalid data!\n");
+
+        /* check whether the seek pointer has moved correctly */
+        IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+        ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == uBytesRead, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, uBytesRead);
+    }
+
+    IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_SET, NULL);
+
+    hr = IWICStream_Read(pStream, Memory, 10, &uBytesRead);   /* source = dest */
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+    if(SUCCEEDED(hr)) {
+        ok(uBytesRead == 10, "Read %u bytes, expected %u\n", uBytesRead, 10);
+        ok(memcmp(Memory, CmpMem, uBytesRead) == 0, "Read returned invalid data!\n");
+    }
+
+    IWICStream_Seek(pStream, SeekPos, STREAM_SEEK_SET, NULL);
+
+    hr = IWICStream_Read(pStream, Memory, 10, &uBytesRead);   /* source and dest overlap */
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+    if(SUCCEEDED(hr)) {
+        ok(uBytesRead == 10, "Read %u bytes, expected %u\n", uBytesRead, 10);
+        ok(memcmp(Memory, CmpMemOverlap, uBytesRead) == 0, "Read returned invalid data!\n");
+    }
+
+    memcpy(Memory, CmpMem, sizeof(CmpMem));
+
+    IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_SET, NULL);
+
+    hr = IWICStream_Read(pStream, Memory, sizeof(Memory) + 10, &uBytesRead);   /* request too many bytes */
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+    if(SUCCEEDED(hr)) {
+        ok(uBytesRead == sizeof(Memory), "Read %u bytes\n", uBytesRead);
+        ok(memcmp(Memory, CmpMem, uBytesRead) == 0, "Read returned invalid data!\n");
+    }
+
+    hr = IWICStream_Read(pStream, NULL, 1, &uBytesRead);    /* destination buffer = NULL */
+    ok(hr == E_INVALIDARG, "Read returned with %#x, expected %#x\n", hr, E_INVALIDARG);
+
+    hr = IWICStream_Read(pStream, MemBuf, 0, &uBytesRead);    /* read 0 bytes */
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+
+    hr = IWICStream_Read(pStream, NULL, 0, &uBytesRead);
+    ok(hr == E_INVALIDARG, "Read returned with %#x, expected %#x\n", hr, E_INVALIDARG);
+
+    hr = IWICStream_Read(pStream, NULL, 0, NULL);
+    ok(hr == E_INVALIDARG, "Read returned with %#x, expected %#x\n", hr, E_INVALIDARG);
+
+    hr = IWICStream_Read(pStream, MemBuf, 1, NULL);
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+
+    IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_SET, NULL);
+    ZeroMemory(MemBuf, sizeof(MemBuf));
+    hr = IWICStream_Read(pStream, MemBuf, sizeof(Memory) + 10, &uBytesRead);
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+    if(SUCCEEDED(hr)) {
+        ok(uBytesRead == sizeof(Memory), "Read %u bytes\n", uBytesRead);
+        ok(memcmp(Memory, CmpMem, 64) == 0, "Read returned invalid data!\n");
+    }
+    IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_SET, NULL);
+
+
+    /* Write */
+    MemBuf[0] = CmpMem[0] + 1;
+    MemBuf[1] = CmpMem[1] + 1;
+    MemBuf[2] = CmpMem[2] + 1;
+    hr = IWICStream_Write(pStream, MemBuf, 3, &uBytesWritten);
+    ok(hr == S_OK, "Write returned with %#x, expected %#x\n", hr, S_OK);
+    if(SUCCEEDED(hr)) {
+        ok(uBytesWritten == 3, "Wrote %u bytes, expected %u\n", uBytesWritten, 3);
+        ok(memcmp(MemBuf, Memory, 3) == 0, "Wrote returned invalid data!\n"); /* make sure we're writing directly */
+
+        /* check whether the seek pointer has moved correctly */
+        IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+        ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == uBytesWritten, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, uBytesWritten);
+    }
+    IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_SET, NULL);
+
+    hr = IWICStream_Write(pStream, MemBuf, 0, &uBytesWritten);
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+
+    /* Restore the original contents of the memory stream. */
+    hr = IWICStream_Write(pStream, CmpMem, sizeof(CmpMem), &uBytesWritten);
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+
+    IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_SET, NULL);
+
+    /* Source and destination overlap. */
+    hr = IWICStream_Write(pStream, Memory + 5, 10, &uBytesWritten);
+    ok(hr == S_OK, "Write returned with %#x, expected %#x\n", hr, S_OK);
+    if(SUCCEEDED(hr)) {
+        ok(uBytesWritten == 10, "Wrote %u bytes, expected %u\n", uBytesWritten, 10);
+        ok(memcmp(CmpMemOverlap, Memory, sizeof(CmpMemOverlap)) == 0, "Wrote returned invalid data!\n");
+    }
+
+    IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_SET, NULL);
+
+    uBytesWritten = 0xdeadbeef;
+    hr = IWICStream_Write(pStream, NULL, 3, &uBytesWritten);
+    ok(hr == E_INVALIDARG, "Write returned with %#x, expected %#x\n", hr, E_INVALIDARG);
+    ok(uBytesWritten == 0xdeadbeef, "Expected uBytesWritten to be unchanged, got %u\n", uBytesWritten);
+    IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 0, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, 0);
+
+    uBytesWritten = 0xdeadbeef;
+    hr = IWICStream_Write(pStream, NULL, 0, &uBytesWritten);
+    ok(hr == E_INVALIDARG, "Write returned with %#x, expected %#x\n", hr, E_INVALIDARG);
+    ok(uBytesWritten == 0xdeadbeef, "Expected uBytesWritten to be unchanged, got %u\n", uBytesWritten);
+    IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 0, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, 0);
+
+    uBytesWritten = 0xdeadbeef;
+    hr = IWICStream_Write(pStream, CmpMem, sizeof(Memory) + 10, &uBytesWritten);
+    ok(hr == STG_E_MEDIUMFULL, "Write returned with %#x, expected %#x\n", hr, STG_E_MEDIUMFULL);
+    ok(uBytesWritten == 0xdeadbeef, "Expected uBytesWritten to be unchanged, got %u\n", uBytesWritten);
+    IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 0, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, 0);
+    IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_SET, NULL);
+
+
+    /* SetSize */
+    uNewPos.u.HighPart = 0;
+    uNewPos.u.LowPart = sizeof(Memory) + 10;
+    hr = IWICStream_SetSize(pStream, uNewPos);
+    ok(hr == E_NOTIMPL, "SetSize returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    uNewPos.u.HighPart = 0;
+    uNewPos.u.LowPart = sizeof(Memory);
+    hr = IWICStream_SetSize(pStream, uNewPos);
+    ok(hr == E_NOTIMPL, "SetSize returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    uNewPos.u.HighPart = 0;
+    uNewPos.u.LowPart = sizeof(Memory) - 10;
+    hr = IWICStream_SetSize(pStream, uNewPos);
+    ok(hr == E_NOTIMPL, "SetSize returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    uNewPos.u.HighPart = 0;
+    uNewPos.u.LowPart = 0;
+    hr = IWICStream_SetSize(pStream, uNewPos);
+    ok(hr == E_NOTIMPL, "SetSize returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    uNewPos.QuadPart = -10;
+    hr = IWICStream_SetSize(pStream, uNewPos);
+    ok(hr == E_NOTIMPL, "SetSize returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+
+    /* CopyTo */
+    uNewPos.u.HighPart = 0;
+    uNewPos.u.LowPart = 5;
+    hr = IWICStream_CopyTo(pStream, NULL, uNewPos, NULL, NULL);
+    ok(hr == E_NOTIMPL, "CopyTo returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    hr = IWICImagingFactory_CreateStream(pFactory, &pBufStream);
+    ok(hr == S_OK, "CreateStream failed with %#x\n", hr);
+
+    hr = IWICStream_InitializeFromMemory(pBufStream, Memory, sizeof(Memory));
+    ok(hr == S_OK, "InitializeFromMemory returned with %#x, expected %#x\n", hr, S_OK);
+
+    hr = IWICStream_CopyTo(pStream, (IStream*)pBufStream, uNewPos, NULL, NULL);
+    ok(hr == E_NOTIMPL, "CopyTo returned %#x, expected %#x\n", hr, E_NOTIMPL);
+    IWICStream_Release(pBufStream);
+
+
+    /* Commit */
+    hr = IWICStream_Commit(pStream, STGC_DEFAULT);
+    ok(hr == E_NOTIMPL, "Commit returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    hr = IWICStream_Commit(pStream, STGC_OVERWRITE);
+    ok(hr == E_NOTIMPL, "Commit returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    hr = IWICStream_Commit(pStream, STGC_ONLYIFCURRENT);
+    ok(hr == E_NOTIMPL, "Commit returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    hr = IWICStream_Commit(pStream, STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE);
+    ok(hr == E_NOTIMPL, "Commit returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    hr = IWICStream_Commit(pStream, STGC_CONSOLIDATE);
+    ok(hr == E_NOTIMPL, "Commit returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+
+    /* Revert */
+    IWICStream_Write(pStream, &MemBuf[5], 6, NULL);
+    hr = IWICStream_Revert(pStream);
+    ok(hr == E_NOTIMPL, "Revert returned %#x, expected %#x\n", hr, E_NOTIMPL);
+    memcpy(Memory, CmpMem, sizeof(Memory));
+
+
+    /* LockRegion/UnlockRegion */
+    hr = IWICStream_LockRegion(pStream, uLargeNull, uLargeNull, 0);
+    ok(hr == E_NOTIMPL, "LockRegion returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    hr = IWICStream_UnlockRegion(pStream, uLargeNull, uLargeNull, 0);
+    ok(hr == E_NOTIMPL, "UnlockRegion returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+
+    /* Stat */
+    hr = IWICStream_Stat(pStream, NULL, 0);
+    ok(hr == E_INVALIDARG, "Stat returned %#x, expected %#x\n", hr, E_INVALIDARG);
+
+    hr = IWICStream_Stat(pStream, &Stats, 0);
+    ok(hr == S_OK, "Stat returned %#x, expected %#x\n", hr, S_OK);
+    ok(Stats.pwcsName == NULL, "Stat returned name %p, expected %p\n", Stats.pwcsName, NULL);
+    ok(Stats.type == STGTY_STREAM, "Stat returned type %d, expected %d\n", Stats.type, STGTY_STREAM);
+    ok(Stats.cbSize.u.HighPart == 0 && Stats.cbSize.u.LowPart == sizeof(Memory), "Stat returned size (%u;%u)\n", Stats.cbSize.u.HighPart, Stats.cbSize.u.LowPart);
+    ok(Stats.mtime.dwHighDateTime == 0 && Stats.mtime.dwLowDateTime == 0, "Stat returned mtime (%u;%u), expected (%u;%u)\n", Stats.mtime.dwHighDateTime, Stats.mtime.dwLowDateTime, 0, 0);
+    ok(Stats.ctime.dwHighDateTime == 0 && Stats.ctime.dwLowDateTime == 0, "Stat returned ctime (%u;%u), expected (%u;%u)\n", Stats.ctime.dwHighDateTime, Stats.ctime.dwLowDateTime, 0, 0);
+    ok(Stats.atime.dwHighDateTime == 0 && Stats.atime.dwLowDateTime == 0, "Stat returned atime (%u;%u), expected (%u;%u)\n", Stats.atime.dwHighDateTime, Stats.atime.dwLowDateTime, 0, 0);
+    ok(Stats.grfMode == 0, "Stat returned access mode %d, expected %d\n", Stats.grfMode, 0);
+    ok(Stats.grfLocksSupported == 0, "Stat returned supported locks %#x, expected %#x\n", Stats.grfLocksSupported, 0);
+    ok(Stats.grfStateBits == 0, "Stat returned state bits %#x, expected %#x\n", Stats.grfStateBits, 0);
+
+
+    /* Clone */
+    hr = IWICStream_Clone(pStream, (IStream**)&pBufStream);
+    ok(hr == E_NOTIMPL, "UnlockRegion returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+
+    IWICStream_Release(pStream);
+    IWICImagingFactory_Release(pFactory);
+    CoUninitialize();
+}
+
+static void test_StreamOnStreamRange(void)
+{
+    IWICImagingFactory *pFactory;
+    IWICStream *pStream, *pSubStream;
+    IStream *CopyStream;
+    const BYTE CmpMem[] = {
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+        0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+    };
+    BYTE Memory[64], MemBuf[64];
+    LARGE_INTEGER LargeNull, LargeInt;
+    ULARGE_INTEGER uLargeNull, uNewPos, uSize;
+    ULONG uBytesRead, uBytesWritten;
+    HRESULT hr;
+    STATSTG Stats;
+
+    LargeNull.QuadPart = 0;
+    uLargeNull.QuadPart = 0;
+
+    memcpy(Memory, CmpMem, sizeof(CmpMem));
+
+    CoInitialize(NULL);
+
+    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, &IID_IWICImagingFactory, (void**)&pFactory);
+    if(FAILED(hr)) {
+        skip("CoCreateInstance returned with %#x, expected %#x\n", hr, S_OK);
+        return;
+    }
+
+    hr = IWICImagingFactory_CreateStream(pFactory, &pStream);
+    ok(hr == S_OK, "CreateStream returned with %#x, expected %#x\n", hr, S_OK);
+    if(FAILED(hr)) {
+        skip("Failed to create stream\n");
+        return;
+    }
+
+    hr = IWICStream_InitializeFromMemory(pStream, Memory, sizeof(Memory));
+    ok(hr == S_OK, "InitializeFromMemory returned with %#x, expected %#x\n", hr, S_OK);
+
+    hr = IWICImagingFactory_CreateStream(pFactory, &pSubStream);
+    ok(hr == S_OK, "CreateStream returned with %#x, expected %#x\n", hr, S_OK);
+
+    uNewPos.QuadPart = 20;
+    uSize.QuadPart = 20;
+    hr = IWICStream_InitializeFromIStreamRegion(pSubStream, (IStream*)pStream, uNewPos, uSize);
+    ok(hr == S_OK, "InitializeFromIStreamRegion returned with %#x, expected %#x\n", hr, S_OK);
+    if(FAILED(hr)) {
+        skip("InitializeFromIStreamRegion unimplemented\n");
+        IWICStream_Release(pSubStream);
+        IWICStream_Release(pStream);
+        IWICImagingFactory_Release(pFactory);
+        CoUninitialize();
+        return;
+    }
+
+    /* Seek */
+    hr = IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_END, &uNewPos);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 20, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, 20);
+    ok(hr == S_OK, "Seek returned with %#x, expected %#x\n", hr, S_OK);
+
+    hr = IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_SET, &uNewPos);
+    ok(hr == S_OK, "Seek returned with %#x, expected %#x\n", hr, S_OK);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 0, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, 0);
+
+    hr = IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_SET, NULL);
+    ok(hr == S_OK, "Seek returned with %#x, expected %#x\n", hr, S_OK);
+
+    LargeInt.u.HighPart = 1;
+    LargeInt.u.LowPart = 0;
+    uNewPos.u.HighPart = 0xdeadbeef;
+    uNewPos.u.LowPart = 0xdeadbeef;
+    hr = IWICStream_Seek(pSubStream, LargeInt, STREAM_SEEK_SET, &uNewPos);
+    ok(hr == WINCODEC_ERR_VALUEOUTOFRANGE, "Seek returned with %#x, expected %#x\n", hr, WINCODEC_ERR_VALUEOUTOFRANGE);
+    ok(uNewPos.u.HighPart == 0xdeadbeef && uNewPos.u.LowPart == 0xdeadbeef, "Seek cursor initialized to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0xdeadbeef, 0xdeadbeef);
+    hr = IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+    ok(hr == S_OK, "Seek returned with %#x, expected %#x\n", hr, S_OK);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 0, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, 0);
+
+    LargeInt.QuadPart = 30;
+    uNewPos.u.HighPart = 0xdeadbeef;
+    uNewPos.u.LowPart = 0xdeadbeef;
+    hr = IWICStream_Seek(pSubStream, LargeInt, STREAM_SEEK_SET, &uNewPos);
+    ok(hr == WINCODEC_ERR_VALUEOUTOFRANGE, "Seek returned with %#x, expected %#x\n", hr, WINCODEC_ERR_VALUEOUTOFRANGE);
+    ok(uNewPos.u.HighPart == 0xdeadbeef && uNewPos.u.LowPart == 0xdeadbeef, "Seek cursor initialized to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0xdeadbeef, 0xdeadbeef);
+    hr = IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+    ok(hr == S_OK, "Seek returned with %#x, expected %#x\n", hr, S_OK);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 0, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, 0);
+
+    LargeInt.QuadPart = 1;
+    uNewPos.u.HighPart = 0xdeadbeef;
+    uNewPos.u.LowPart = 0xdeadbeef;
+    hr = IWICStream_Seek(pSubStream, LargeInt, STREAM_SEEK_END, &uNewPos);
+    ok(hr == WINCODEC_ERR_VALUEOUTOFRANGE, "Seek returned with %#x, expected %#x\n", hr, WINCODEC_ERR_VALUEOUTOFRANGE);
+    ok(uNewPos.u.HighPart == 0xdeadbeef && uNewPos.u.LowPart == 0xdeadbeef, "Seek cursor initialized to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0xdeadbeef, 0xdeadbeef);
+    hr = IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+    ok(hr == S_OK, "Seek returned with %#x, expected %#x\n", hr, S_OK);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 0, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, 0);
+
+    LargeInt.QuadPart = -1;
+    hr = IWICStream_Seek(pSubStream, LargeInt, STREAM_SEEK_END, &uNewPos);
+    ok(hr == S_OK, "Seek returned with %#x, expected %#x\n", hr, S_OK);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 19, "bSeek cursor moved to position (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart);
+
+    IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_SET, &uNewPos); /* reset seek pointer */
+    LargeInt.QuadPart = -25;
+    uNewPos.u.HighPart = 0xdeadbeef;
+    uNewPos.u.LowPart = 0xdeadbeef;
+    hr = IWICStream_Seek(pSubStream, LargeInt, STREAM_SEEK_END, &uNewPos);
+    ok(hr == WINCODEC_ERR_VALUEOUTOFRANGE,
+       "Seek returned with %#x, expected %#x\n", hr, HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW));
+    ok(uNewPos.u.HighPart == 0xdeadbeef && uNewPos.u.LowPart == 0xdeadbeef, "Seek cursor initialized to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0xdeadbeef, 0xdeadbeef);
+    hr = IWICStream_Seek(pStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+    ok(hr == S_OK, "Seek returned with %#x, expected %#x\n", hr, S_OK);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 0, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, 0); /* remains unchanged */
+    IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_SET, NULL);
+
+
+    /* Read */
+    hr = IWICStream_Read(pSubStream, MemBuf, 12, &uBytesRead);
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+    if(SUCCEEDED(hr)) {
+        ok(uBytesRead == 12, "Read %u bytes, expected %u\n", uBytesRead, 12);
+        ok(memcmp(MemBuf, CmpMem+20, 12) == 0, "Read returned invalid data!\n");
+
+        /* check whether the seek pointer has moved correctly */
+        IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+        ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == uBytesRead, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, uBytesRead);
+    }
+
+    IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_SET, NULL);
+
+    hr = IWICStream_Read(pSubStream, Memory, 10, &uBytesRead);   /* source = dest */
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+    if(SUCCEEDED(hr)) {
+        ok(uBytesRead == 10, "Read %u bytes, expected %u\n", uBytesRead, 10);
+        ok(memcmp(Memory, CmpMem+20, uBytesRead) == 0, "Read returned invalid data!\n");
+    }
+
+    IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_SET, NULL);
+
+    hr = IWICStream_Read(pSubStream, Memory, 30, &uBytesRead);   /* request too many bytes */
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+    if(SUCCEEDED(hr)) {
+        ok(uBytesRead == 20, "Read %u bytes\n", uBytesRead);
+        ok(memcmp(Memory, CmpMem+20, uBytesRead) == 0, "Read returned invalid data!\n");
+    }
+
+    IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_SET, NULL);
+    uBytesRead = 0xdeadbeef;
+    hr = IWICStream_Read(pSubStream, NULL, 1, &uBytesRead);    /* destination buffer = NULL */
+    ok(hr == E_INVALIDARG, "Read returned with %#x, expected %#x\n", hr, E_INVALIDARG);
+    ok(uBytesRead == 0xdeadbeef, "Expected uBytesRead to be unchanged, got %u\n", uBytesRead);
+
+    hr = IWICStream_Read(pSubStream, MemBuf, 0, &uBytesRead);    /* read 0 bytes */
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+
+    uBytesRead = 0xdeadbeef;
+    hr = IWICStream_Read(pSubStream, NULL, 0, &uBytesRead);
+    ok(hr == E_INVALIDARG, "Read returned with %#x, expected %#x\n", hr, E_INVALIDARG);
+    ok(uBytesRead == 0xdeadbeef, "Expected uBytesRead to be unchanged, got %u\n", uBytesRead);
+
+    hr = IWICStream_Read(pSubStream, NULL, 0, NULL);
+    ok(hr == E_INVALIDARG, "Read returned with %#x, expected %#x\n", hr, E_INVALIDARG);
+
+    hr = IWICStream_Read(pSubStream, MemBuf, 1, NULL);
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+
+    IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_SET, NULL);
+    ZeroMemory(MemBuf, sizeof(MemBuf));
+    hr = IWICStream_Read(pSubStream, MemBuf, 30, &uBytesRead);
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+    if(SUCCEEDED(hr)) {
+        ok(uBytesRead == 20, "Read %u bytes\n", uBytesRead);
+        ok(memcmp(Memory, CmpMem+20, 20) == 0, "Read returned invalid data!\n");
+    }
+    IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_SET, NULL);
+
+
+    /* Write */
+    MemBuf[0] = CmpMem[0] + 1;
+    MemBuf[1] = CmpMem[1] + 1;
+    MemBuf[2] = CmpMem[2] + 1;
+    hr = IWICStream_Write(pSubStream, MemBuf, 3, &uBytesWritten);
+    ok(hr == S_OK, "Write returned with %#x, expected %#x\n", hr, S_OK);
+    if(SUCCEEDED(hr)) {
+        ok(uBytesWritten == 3, "Wrote %u bytes, expected %u\n", uBytesWritten, 3);
+        ok(memcmp(MemBuf, Memory+20, 3) == 0, "Wrote returned invalid data!\n"); /* make sure we're writing directly */
+
+        /* check whether the seek pointer has moved correctly */
+        IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+        ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == uBytesWritten, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, uBytesWritten);
+    }
+    IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_SET, NULL);
+
+    hr = IWICStream_Write(pSubStream, MemBuf, 0, &uBytesWritten);
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+
+    uBytesWritten = 0xdeadbeef;
+    hr = IWICStream_Write(pSubStream, NULL, 3, &uBytesWritten);
+    ok(hr == E_INVALIDARG, "Write returned with %#x, expected %#x\n", hr, E_INVALIDARG);
+    ok(uBytesWritten == 0xdeadbeef, "Expected uBytesWritten to be unchanged, got %u\n", uBytesWritten);
+    IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 0, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, 0);
+
+    uBytesWritten = 0xdeadbeef;
+    hr = IWICStream_Write(pSubStream, NULL, 0, &uBytesWritten);
+    ok(hr == E_INVALIDARG, "Write returned with %#x, expected %#x\n", hr, E_INVALIDARG);
+    ok(uBytesWritten == 0xdeadbeef, "Expected uBytesWritten to be unchanged, got %u\n", uBytesWritten);
+    IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 0, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, 0);
+
+    hr = IWICStream_Write(pSubStream, CmpMem, 30, &uBytesWritten);
+    ok(hr == S_OK, "Write returned with %#x, expected %#x\n", hr, STG_E_MEDIUMFULL);
+    ok(uBytesWritten == 20, "Wrote %u bytes, expected %u\n", uBytesWritten, 0);
+    IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == uBytesWritten, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, uBytesWritten);
+    IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_SET, NULL);
+
+
+    /* SetSize */
+    uNewPos.u.HighPart = 0;
+    uNewPos.u.LowPart = sizeof(Memory) + 10;
+    hr = IWICStream_SetSize(pSubStream, uNewPos);
+    ok(hr == E_NOTIMPL, "SetSize returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    uNewPos.u.HighPart = 0;
+    uNewPos.u.LowPart = sizeof(Memory);
+    hr = IWICStream_SetSize(pSubStream, uNewPos);
+    ok(hr == E_NOTIMPL, "SetSize returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    uNewPos.u.HighPart = 0;
+    uNewPos.u.LowPart = sizeof(Memory) - 10;
+    hr = IWICStream_SetSize(pSubStream, uNewPos);
+    ok(hr == E_NOTIMPL, "SetSize returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    uNewPos.u.HighPart = 0;
+    uNewPos.u.LowPart = 0;
+    hr = IWICStream_SetSize(pSubStream, uNewPos);
+    ok(hr == E_NOTIMPL, "SetSize returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    uNewPos.QuadPart = -10;
+    hr = IWICStream_SetSize(pSubStream, uNewPos);
+    ok(hr == E_NOTIMPL, "SetSize returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+
+    /* CopyTo */
+    uNewPos.u.HighPart = 0;
+    uNewPos.u.LowPart = 30;
+    hr = IWICStream_CopyTo(pSubStream, NULL, uNewPos, NULL, NULL);
+    ok(hr == E_NOTIMPL, "CopyTo returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &CopyStream);
+    ok(hr == S_OK, "CreateStream failed with %#x\n", hr);
+
+    hr = IWICStream_CopyTo(pSubStream, CopyStream, uNewPos, NULL, NULL);
+    ok(hr == E_NOTIMPL, "CopyTo returned %#x, expected %#x\n", hr, E_NOTIMPL);
+    IStream_Release(CopyStream);
+
+
+    /* Commit */
+    hr = IWICStream_Commit(pSubStream, STGC_DEFAULT);
+    ok(hr == E_NOTIMPL, "Commit returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    hr = IWICStream_Commit(pSubStream, STGC_OVERWRITE);
+    ok(hr == E_NOTIMPL, "Commit returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    hr = IWICStream_Commit(pSubStream, STGC_ONLYIFCURRENT);
+    ok(hr == E_NOTIMPL, "Commit returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    hr = IWICStream_Commit(pSubStream, STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE);
+    ok(hr == E_NOTIMPL, "Commit returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    hr = IWICStream_Commit(pSubStream, STGC_CONSOLIDATE);
+    ok(hr == E_NOTIMPL, "Commit returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+
+    /* Revert */
+    IWICStream_Write(pSubStream, &MemBuf[5], 6, NULL);
+    hr = IWICStream_Revert(pSubStream);
+    ok(hr == E_NOTIMPL, "Revert returned %#x, expected %#x\n", hr, E_NOTIMPL);
+    memcpy(Memory, CmpMem, sizeof(Memory));
+
+
+    /* LockRegion/UnlockRegion */
+    hr = IWICStream_LockRegion(pSubStream, uLargeNull, uLargeNull, 0);
+    ok(hr == E_NOTIMPL, "LockRegion returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+    hr = IWICStream_UnlockRegion(pSubStream, uLargeNull, uLargeNull, 0);
+    ok(hr == E_NOTIMPL, "UnlockRegion returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+
+    /* Stat */
+    hr = IWICStream_Stat(pSubStream, NULL, 0);
+    ok(hr == E_INVALIDARG, "Stat returned %#x, expected %#x\n", hr, E_INVALIDARG);
+
+    hr = IWICStream_Stat(pSubStream, &Stats, 0);
+    ok(hr == S_OK, "Stat returned %#x, expected %#x\n", hr, S_OK);
+    ok(Stats.pwcsName == NULL, "Stat returned name %p, expected %p\n", Stats.pwcsName, NULL);
+    ok(Stats.type == STGTY_STREAM, "Stat returned type %d, expected %d\n", Stats.type, STGTY_STREAM);
+    ok(Stats.cbSize.u.HighPart == 0 && Stats.cbSize.u.LowPart == 20, "Stat returned size (%u;%u)\n", Stats.cbSize.u.HighPart, Stats.cbSize.u.LowPart);
+    ok(Stats.mtime.dwHighDateTime == 0 && Stats.mtime.dwLowDateTime == 0, "Stat returned mtime (%u;%u), expected (%u;%u)\n", Stats.mtime.dwHighDateTime, Stats.mtime.dwLowDateTime, 0, 0);
+    ok(Stats.ctime.dwHighDateTime == 0 && Stats.ctime.dwLowDateTime == 0, "Stat returned ctime (%u;%u), expected (%u;%u)\n", Stats.ctime.dwHighDateTime, Stats.ctime.dwLowDateTime, 0, 0);
+    ok(Stats.atime.dwHighDateTime == 0 && Stats.atime.dwLowDateTime == 0, "Stat returned atime (%u;%u), expected (%u;%u)\n", Stats.atime.dwHighDateTime, Stats.atime.dwLowDateTime, 0, 0);
+    ok(Stats.grfMode == 0, "Stat returned access mode %d, expected %d\n", Stats.grfMode, 0);
+    ok(Stats.grfLocksSupported == 0, "Stat returned supported locks %#x, expected %#x\n", Stats.grfLocksSupported, 0);
+    ok(Stats.grfStateBits == 0, "Stat returned state bits %#x, expected %#x\n", Stats.grfStateBits, 0);
+
+
+    /* Clone */
+    hr = IWICStream_Clone(pSubStream, &CopyStream);
+    ok(hr == E_NOTIMPL, "Clone returned %#x, expected %#x\n", hr, E_NOTIMPL);
+
+
+    IWICStream_Release(pSubStream);
+
+
+    /* Recreate, this time larger than the original. */
+    hr = IWICImagingFactory_CreateStream(pFactory, &pSubStream);
+    ok(hr == S_OK, "CreateStream returned with %#x, expected %#x\n", hr, S_OK);
+
+    uNewPos.QuadPart = 48;
+    uSize.QuadPart = 32;
+    hr = IWICStream_InitializeFromIStreamRegion(pSubStream, (IStream*)pStream, uNewPos, uSize);
+    ok(hr == S_OK, "InitializeFromMemory returned with %#x, expected %#x\n", hr, S_OK);
+
+    hr = IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_END, &uNewPos);
+    ok(hr == S_OK, "Seek returned with %#x, expected %#x\n", hr, S_OK);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 16, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, 16);
+
+    IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_SET, NULL);
+    hr = IWICStream_Read(pSubStream, Memory, 48, &uBytesRead);
+    ok(hr == S_OK, "Read returned with %#x, expected %#x\n", hr, S_OK);
+    if(SUCCEEDED(hr)) {
+        ok(uBytesRead == 16, "Read %u bytes\n", uBytesRead);
+        ok(memcmp(Memory, CmpMem+48, uBytesRead) == 0, "Read returned invalid data!\n");
+    }
+
+    IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_SET, NULL);
+    uBytesWritten = 0xdeadbeef;
+    hr = IWICStream_Write(pSubStream, CmpMem, 32, &uBytesWritten);
+    ok(hr == STG_E_MEDIUMFULL, "Write returned with %#x, expected %#x\n", hr, STG_E_MEDIUMFULL);
+    ok(uBytesWritten == 0xdeadbeef, "Expected uBytesWritten to be unchanged, got %u\n", uBytesWritten);
+    IWICStream_Seek(pSubStream, LargeNull, STREAM_SEEK_CUR, &uNewPos);
+    ok(uNewPos.u.HighPart == 0 && uNewPos.u.LowPart == 0, "Seek cursor moved to position (%u;%u), expected (%u;%u)\n", uNewPos.u.HighPart, uNewPos.u.LowPart, 0, 0);
+
+
+    IWICStream_Release(pSubStream);
+    IWICStream_Release(pStream);
+    IWICImagingFactory_Release(pFactory);
+    CoUninitialize();
+}
+
+START_TEST(stream)
+{
+    test_StreamOnMemory();
+    test_StreamOnStreamRange();
+}
diff --git a/rostests/winetests/windowscodecs/testlist.c b/rostests/winetests/windowscodecs/testlist.c
new file mode 100644 (file)
index 0000000..cbdd909
--- /dev/null
@@ -0,0 +1,35 @@
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_bitmap(void);
+extern void func_bmpformat(void);
+extern void func_converter(void);
+extern void func_gifformat(void);
+extern void func_icoformat(void);
+extern void func_info(void);
+extern void func_metadata(void);
+extern void func_palette(void);
+extern void func_pngformat(void);
+extern void func_stream(void);
+extern void func_tiffformat(void);
+
+const struct test winetest_testlist[] =
+{
+    { "bitmap", func_bitmap },
+    { "bmpformat", func_bmpformat },
+    { "converter", func_converter },
+    { "gifformat", func_gifformat },
+    { "icoformat", func_icoformat },
+    { "info", func_info },
+    { "metadata", func_metadata },
+    { "palette", func_palette },
+    { "pngformat", func_pngformat },
+    { "stream", func_stream },
+    { "tiffformat", func_tiffformat },
+    { 0, 0 }
+};
diff --git a/rostests/winetests/windowscodecs/tiffformat.c b/rostests/winetests/windowscodecs/tiffformat.c
new file mode 100644 (file)
index 0000000..f32f6a5
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2012 Dmitry Timoshkov
+ *
+ * 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 <stdio.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "wincodec.h"
+#include "wine/test.h"
+
+#define IFD_BYTE 1
+#define IFD_ASCII 2
+#define IFD_SHORT 3
+#define IFD_LONG 4
+#define IFD_RATIONAL 5
+#define IFD_SBYTE 6
+#define IFD_UNDEFINED 7
+#define IFD_SSHORT 8
+#define IFD_SLONG 9
+#define IFD_SRATIONAL 10
+#define IFD_FLOAT 11
+#define IFD_DOUBLE 12
+
+#include "pshpack2.h"
+struct IFD_entry
+{
+    SHORT id;
+    SHORT type;
+    ULONG count;
+    LONG  value;
+};
+
+struct IFD_rational
+{
+    LONG numerator;
+    LONG denominator;
+};
+
+static const struct tiff_1bpp_data
+{
+    USHORT byte_order;
+    USHORT version;
+    ULONG  dir_offset;
+    USHORT number_of_entries;
+    struct IFD_entry entry[13];
+    ULONG next_IFD;
+    struct IFD_rational res;
+    BYTE pixel_data[4];
+} tiff_1bpp_data =
+{
+#ifdef WORDS_BIGENDIAN
+    'M' | 'M' << 8,
+#else
+    'I' | 'I' << 8,
+#endif
+    42,
+    FIELD_OFFSET(struct tiff_1bpp_data, number_of_entries),
+    13,
+    {
+        { 0xff, IFD_SHORT, 1, 0 }, /* SUBFILETYPE */
+        { 0x100, IFD_LONG, 1, 1 }, /* IMAGEWIDTH */
+        { 0x101, IFD_LONG, 1, 1 }, /* IMAGELENGTH */
+        { 0x102, IFD_SHORT, 1, 1 }, /* BITSPERSAMPLE */
+        { 0x103, IFD_SHORT, 1, 1 }, /* COMPRESSION: XP doesn't accept IFD_LONG here */
+        { 0x106, IFD_SHORT, 1, 1 }, /* PHOTOMETRIC */
+        { 0x111, IFD_LONG, 1, FIELD_OFFSET(struct tiff_1bpp_data, pixel_data) }, /* STRIPOFFSETS */
+        { 0x115, IFD_SHORT, 1, 1 }, /* SAMPLESPERPIXEL */
+        { 0x116, IFD_LONG, 1, 1 }, /* ROWSPERSTRIP */
+        { 0x117, IFD_LONG, 1, 1 }, /* STRIPBYTECOUNT */
+        { 0x11a, IFD_RATIONAL, 1, FIELD_OFFSET(struct tiff_1bpp_data, res) },
+        { 0x11b, IFD_RATIONAL, 1, FIELD_OFFSET(struct tiff_1bpp_data, res) },
+        { 0x128, IFD_SHORT, 1, 2 }, /* RESOLUTIONUNIT */
+    },
+    0,
+    { 900, 3 },
+    { 0x11, 0x22, 0x33, 0 }
+};
+#include "poppack.h"
+
+static IWICImagingFactory *factory;
+
+static const char *debugstr_guid(const GUID *guid)
+{
+    static char buf[50];
+
+    if (!guid) return "(null)";
+    sprintf(buf, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
+            guid->Data1, guid->Data2, guid->Data3, guid->Data4[0],
+            guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4],
+            guid->Data4[5], guid->Data4[6], guid->Data4[7]);
+    return buf;
+}
+
+static IStream *create_stream(const void *data, int data_size)
+{
+    HRESULT hr;
+    IStream *stream;
+    HGLOBAL hdata;
+    void *locked_data;
+
+    hdata = GlobalAlloc(GMEM_MOVEABLE, data_size);
+    ok(hdata != 0, "GlobalAlloc failed\n");
+    if (!hdata) return NULL;
+
+    locked_data = GlobalLock(hdata);
+    memcpy(locked_data, data, data_size);
+    GlobalUnlock(hdata);
+
+    hr = CreateStreamOnHGlobal(hdata, TRUE, &stream);
+    ok(hr == S_OK, "CreateStreamOnHGlobal failed, hr=%x\n", hr);
+
+    return stream;
+}
+
+static IWICBitmapDecoder *create_decoder(const void *image_data, UINT image_size)
+{
+    HRESULT hr;
+    IStream *stream;
+    IWICBitmapDecoder *decoder = NULL;
+    GUID guid;
+
+    stream = create_stream(image_data, image_size);
+
+    hr = IWICImagingFactory_CreateDecoderFromStream(factory, stream, NULL, 0, &decoder);
+    ok(hr == S_OK, "CreateDecoderFromStream error %#x\n", hr);
+
+    hr = IWICBitmapDecoder_GetContainerFormat(decoder, &guid);
+    ok(hr == S_OK, "GetContainerFormat error %#x\n", hr);
+    ok(IsEqualGUID(&guid, &GUID_ContainerFormatTiff), "container format is not TIFF\n");
+
+    IStream_Release(stream);
+
+    return decoder;
+}
+
+static void test_tiff_palette(void)
+{
+    HRESULT hr;
+    IWICBitmapDecoder *decoder;
+    IWICBitmapFrameDecode *frame;
+    IWICPalette *palette;
+    GUID format;
+
+    decoder = create_decoder(&tiff_1bpp_data, sizeof(tiff_1bpp_data));
+    ok(decoder != 0, "Failed to load TIFF image data\n");
+
+    hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
+    ok(hr == S_OK, "GetFrame error %#x\n", hr);
+
+    hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &format);
+    ok(hr == S_OK, "GetPixelFormat error %#x\n", hr);
+    ok(IsEqualGUID(&format, &GUID_WICPixelFormatBlackWhite),
+       "got wrong format %s\n", debugstr_guid(&format));
+
+    hr = IWICImagingFactory_CreatePalette(factory, &palette);
+    ok(hr == S_OK, "CreatePalette error %#x\n", hr);
+    hr = IWICBitmapFrameDecode_CopyPalette(frame, palette);
+    ok(hr == WINCODEC_ERR_PALETTEUNAVAILABLE,
+       "expected WINCODEC_ERR_PALETTEUNAVAILABLE, got %#x\n", hr);
+
+    IWICPalette_Release(palette);
+    IWICBitmapFrameDecode_Release(frame);
+    IWICBitmapDecoder_Release(decoder);
+}
+
+static void test_QueryCapability(void)
+{
+    HRESULT hr;
+    IStream *stream;
+    IWICBitmapDecoder *decoder;
+    IWICBitmapFrameDecode *frame;
+    static const DWORD exp_caps = WICBitmapDecoderCapabilityCanDecodeAllImages |
+                                  WICBitmapDecoderCapabilityCanDecodeSomeImages |
+                                  WICBitmapDecoderCapabilityCanEnumerateMetadata;
+    static const DWORD exp_caps_xp = WICBitmapDecoderCapabilityCanDecodeAllImages |
+                                     WICBitmapDecoderCapabilityCanDecodeSomeImages;
+    DWORD capability;
+    LARGE_INTEGER pos;
+    ULARGE_INTEGER cur_pos;
+    UINT frame_count;
+
+    stream = create_stream(&tiff_1bpp_data, sizeof(tiff_1bpp_data));
+    if (!stream) return;
+
+    hr = IWICImagingFactory_CreateDecoder(factory, &GUID_ContainerFormatTiff, NULL, &decoder);
+    ok(hr == S_OK, "CreateDecoder error %#x\n", hr);
+
+    frame_count = 0xdeadbeef;
+    hr = IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
+    ok(hr == S_OK || broken(hr == E_POINTER) /* XP */, "GetFrameCount error %#x\n", hr);
+    ok(frame_count == 0, "expected 0, got %u\n", frame_count);
+
+    hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
+    ok(hr == WINCODEC_ERR_FRAMEMISSING || broken(hr == E_POINTER) /* XP */, "expected WINCODEC_ERR_FRAMEMISSING, got %#x\n", hr);
+
+    pos.QuadPart = 4;
+    hr = IStream_Seek(stream, pos, SEEK_SET, NULL);
+    ok(hr == S_OK, "IStream_Seek error %#x\n", hr);
+
+    capability = 0xdeadbeef;
+    hr = IWICBitmapDecoder_QueryCapability(decoder, stream, &capability);
+    ok(hr == S_OK, "QueryCapability error %#x\n", hr);
+    ok(capability == exp_caps || capability == exp_caps_xp,
+       "expected %#x, got %#x\n", exp_caps, capability);
+
+    frame_count = 0xdeadbeef;
+    hr = IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
+    ok(hr == S_OK, "GetFrameCount error %#x\n", hr);
+    ok(frame_count == 1, "expected 1, got %u\n", frame_count);
+
+    hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
+    ok(hr == S_OK, "GetFrame error %#x\n", hr);
+    IWICBitmapFrameDecode_Release(frame);
+
+    pos.QuadPart = 0;
+    hr = IStream_Seek(stream, pos, SEEK_CUR, &cur_pos);
+    ok(hr == S_OK, "IStream_Seek error %#x\n", hr);
+    ok(cur_pos.QuadPart > 4 && cur_pos.QuadPart < sizeof(tiff_1bpp_data),
+       "current stream pos is at %x/%x\n", cur_pos.u.LowPart, cur_pos.u.HighPart);
+
+    hr = IWICBitmapDecoder_QueryCapability(decoder, stream, &capability);
+    ok(hr == WINCODEC_ERR_WRONGSTATE, "expected WINCODEC_ERR_WRONGSTATE, got %#x\n", hr);
+
+    hr = IWICBitmapDecoder_Initialize(decoder, stream, WICDecodeMetadataCacheOnDemand);
+    ok(hr == WINCODEC_ERR_WRONGSTATE, "expected WINCODEC_ERR_WRONGSTATE, got %#x\n", hr);
+
+    IWICBitmapDecoder_Release(decoder);
+
+    hr = IWICImagingFactory_CreateDecoderFromStream(factory, stream, NULL, 0, &decoder);
+todo_wine
+    ok(hr == WINCODEC_ERR_COMPONENTNOTFOUND, "expected WINCODEC_ERR_COMPONENTNOTFOUND, got %#x\n", hr);
+
+    pos.QuadPart = 0;
+    hr = IStream_Seek(stream, pos, SEEK_SET, NULL);
+    ok(hr == S_OK, "IStream_Seek error %#x\n", hr);
+
+    hr = IWICImagingFactory_CreateDecoderFromStream(factory, stream, NULL, 0, &decoder);
+    ok(hr == S_OK, "CreateDecoderFromStream error %#x\n", hr);
+
+    frame_count = 0xdeadbeef;
+    hr = IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
+    ok(hr == S_OK, "GetFrameCount error %#x\n", hr);
+    ok(frame_count == 1, "expected 1, got %u\n", frame_count);
+
+    hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
+    ok(hr == S_OK, "GetFrame error %#x\n", hr);
+    IWICBitmapFrameDecode_Release(frame);
+
+    hr = IWICBitmapDecoder_Initialize(decoder, stream, WICDecodeMetadataCacheOnDemand);
+    ok(hr == WINCODEC_ERR_WRONGSTATE, "expected WINCODEC_ERR_WRONGSTATE, got %#x\n", hr);
+
+    hr = IWICBitmapDecoder_QueryCapability(decoder, stream, &capability);
+    ok(hr == WINCODEC_ERR_WRONGSTATE, "expected WINCODEC_ERR_WRONGSTATE, got %#x\n", hr);
+
+    IWICBitmapDecoder_Release(decoder);
+    IStream_Release(stream);
+}
+
+START_TEST(tiffformat)
+{
+    HRESULT hr;
+
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+
+    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_IWICImagingFactory, (void **)&factory);
+    ok(hr == S_OK, "CoCreateInstance error %#x\n", hr);
+    if (FAILED(hr)) return;
+
+    test_tiff_palette();
+    test_QueryCapability();
+
+    IWICImagingFactory_Release(factory);
+    CoUninitialize();
+}