windowscodecs: Implement CopyPixels for BMP decoder.
diff --git a/dlls/windowscodecs/bmpdecode.c b/dlls/windowscodecs/bmpdecode.c
index 8391a1b..a6b7577 100644
--- a/dlls/windowscodecs/bmpdecode.c
+++ b/dlls/windowscodecs/bmpdecode.c
@@ -58,7 +58,10 @@
     DWORD bc2AppData;
 } BITMAPCOREHEADER2;
 
-typedef struct {
+struct BmpFrameDecode;
+typedef HRESULT (*ReadDataFunc)(struct BmpFrameDecode* This);
+
+typedef struct BmpFrameDecode {
     const IWICBitmapFrameDecodeVtbl *lpVtbl;
     LONG ref;
     IStream *stream;
@@ -66,6 +69,10 @@
     BITMAPV5HEADER bih;
     const WICPixelFormatGUID *pixelformat;
     int bitsperpixel;
+    ReadDataFunc read_data_func;
+    INT stride;
+    BYTE *imagedata;
+    BYTE *imagedatastart;
 } BmpFrameDecode;
 
 static HRESULT WINAPI BmpFrameDecode_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
@@ -112,6 +119,7 @@
     if (ref == 0)
     {
         IStream_Release(This->stream);
+        HeapFree(GetProcessHeap(), 0, This->imagedata);
         HeapFree(GetProcessHeap(), 0, This);
     }
 
@@ -188,8 +196,23 @@
 static HRESULT WINAPI BmpFrameDecode_CopyPixels(IWICBitmapFrameDecode *iface,
     const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
 {
-    FIXME("(%p,%p,%u,%u,%p): stub\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
-    return E_NOTIMPL;
+    BmpFrameDecode *This = (BmpFrameDecode*)iface;
+    HRESULT hr;
+    UINT width, height;
+    TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
+
+    if (!This->imagedata)
+    {
+        hr = This->read_data_func(This);
+        if (FAILED(hr)) return hr;
+    }
+
+    hr = BmpFrameDecode_GetSize(iface, &width, &height);
+    if (FAILED(hr)) return hr;
+
+    return copy_pixels(This->bitsperpixel, This->imagedatastart,
+        width, height, This->stride,
+        prc, cbStride, cbBufferSize, pbBuffer);
 }
 
 static HRESULT WINAPI BmpFrameDecode_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
@@ -213,6 +236,68 @@
     return E_NOTIMPL;
 }
 
+static HRESULT BmpFrameDecode_ReadUncompressed(BmpFrameDecode* This)
+{
+    UINT bytesperrow;
+    UINT width, height;
+    UINT datasize;
+    int bottomup;
+    HRESULT hr;
+    LARGE_INTEGER offbits;
+    ULONG bytesread;
+
+    if (This->bih.bV5Size == sizeof(BITMAPCOREHEADER))
+    {
+        BITMAPCOREHEADER *bch = (BITMAPCOREHEADER*)&This->bih;
+        width = bch->bcWidth;
+        height = bch->bcHeight;
+        bottomup = 1;
+    }
+    else
+    {
+        width = This->bih.bV5Width;
+        height = abs(This->bih.bV5Height);
+        bottomup = (This->bih.bV5Height > 0);
+    }
+
+    /* row sizes in BMP files must be divisible by 4 bytes */
+    bytesperrow = (((width * This->bitsperpixel)+31)/32)*4;
+    datasize = bytesperrow * height;
+
+    This->imagedata = HeapAlloc(GetProcessHeap(), 0, datasize);
+    if (!This->imagedata) return E_OUTOFMEMORY;
+
+    offbits.QuadPart = This->bfh.bfOffBits;
+    hr = IStream_Seek(This->stream, offbits, STREAM_SEEK_SET, NULL);
+    if (FAILED(hr)) goto fail;
+
+    hr = IStream_Read(This->stream, This->imagedata, datasize, &bytesread);
+    if (FAILED(hr) || bytesread != datasize) goto fail;
+
+    if (bottomup)
+    {
+        This->imagedatastart = This->imagedata + (height-1) * bytesperrow;
+        This->stride = -bytesperrow;
+    }
+    else
+    {
+        This->imagedatastart = This->imagedata;
+        This->stride = bytesperrow;
+    }
+    return S_OK;
+
+fail:
+    HeapFree(GetProcessHeap(), 0, This->imagedata);
+    This->imagedata = NULL;
+    if (SUCCEEDED(hr)) hr = E_FAIL;
+    return hr;
+}
+
+static HRESULT BmpFrameDecode_ReadUnsupported(BmpFrameDecode* This)
+{
+    return E_FAIL;
+}
+
 static const IWICBitmapFrameDecodeVtbl BmpFrameDecode_Vtbl = {
     BmpFrameDecode_QueryInterface,
     BmpFrameDecode_AddRef,
@@ -237,6 +322,7 @@
     BmpFrameDecode *framedecode;
     const WICPixelFormatGUID *pixelformat;
     int bitsperpixel;
+    ReadDataFunc read_data_func;
 } BmpDecoder;
 
 static HRESULT BmpDecoder_ReadHeaders(BmpDecoder* This, IStream *stream)
@@ -271,6 +357,7 @@
         BITMAPCOREHEADER *bch = (BITMAPCOREHEADER*)&This->bih;
         TRACE("BITMAPCOREHEADER with depth=%i\n", bch->bcBitCount);
         This->bitsperpixel = bch->bcBitCount;
+        This->read_data_func = BmpFrameDecode_ReadUncompressed;
         switch(bch->bcBitCount)
         {
         case 1:
@@ -301,6 +388,7 @@
         {
         case BI_RGB:
             This->bitsperpixel = This->bih.bV5BitCount;
+            This->read_data_func = BmpFrameDecode_ReadUncompressed;
             switch(This->bih.bV5BitCount)
             {
             case 1:
@@ -331,6 +419,7 @@
             break;
         default:
             This->bitsperpixel = 0;
+            This->read_data_func = BmpFrameDecode_ReadUnsupported;
             This->pixelformat = &GUID_WICPixelFormatUndefined;
             FIXME("unsupported bitmap type header=%i compression=%i depth=%i\n", This->bih.bV5Size, This->bih.bV5Compression, This->bih.bV5BitCount);
             break;
@@ -493,6 +582,8 @@
         This->framedecode->bih = This->bih;
         This->framedecode->pixelformat = This->pixelformat;
         This->framedecode->bitsperpixel = This->bitsperpixel;
+        This->framedecode->read_data_func = This->read_data_func;
+        This->framedecode->imagedata = NULL;
     }
 
     *ppIBitmapFrame = (IWICBitmapFrameDecode*)This->framedecode;
diff --git a/dlls/windowscodecs/main.c b/dlls/windowscodecs/main.c
index e3912b8..ac63603 100644
--- a/dlls/windowscodecs/main.c
+++ b/dlls/windowscodecs/main.c
@@ -23,6 +23,14 @@
 
 #include "windef.h"
 #include "winbase.h"
+#include "objbase.h"
+#include "wincodec.h"
+
+#include "wincodecs_private.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
 
 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
 {
@@ -40,3 +48,48 @@
 
     return TRUE;
 }
+
+HRESULT copy_pixels(UINT bpp, const BYTE *srcbuffer,
+    UINT srcwidth, UINT srcheight, INT srcstride,
+    const WICRect *rc, UINT dststride, UINT dstbuffersize, BYTE *dstbuffer)
+{
+    UINT bytesperrow;
+    UINT row_offset; /* number of bits into the source rows where the data starts */
+
+    if (rc->X < 0 || rc->Y < 0 || rc->X+rc->Width > srcwidth || rc->Y+rc->Height > srcheight)
+        return E_INVALIDARG;
+
+    bytesperrow = ((bpp * rc->Width)+7)/8;
+
+    if (dststride < bytesperrow)
+        return E_INVALIDARG;
+
+    if ((dststride * rc->Height) > dstbuffersize)
+        return E_INVALIDARG;
+
+    row_offset = rc->X * bpp;
+
+    if (row_offset % 8 == 0)
+    {
+        /* everything lines up on a byte boundary */
+        UINT row;
+        const BYTE *src;
+        BYTE *dst;
+
+        src = srcbuffer + (row_offset / 8);
+        dst = dstbuffer;
+        for (row=0; row < rc->Height; row++)
+        {
+            memcpy(dst, src, bytesperrow);
+            src += srcstride;
+            dst += dststride;
+        }
+        return S_OK;
+    }
+    else
+    {
+        /* we have to do a weird bitwise copy. eww. */
+        FIXME("cannot reliably copy bitmap data if bpp < 8\n");
+        return E_FAIL;
+    }
+}
diff --git a/dlls/windowscodecs/tests/bmpformat.c b/dlls/windowscodecs/tests/bmpformat.c
index 11c5a67..c266dda 100644
--- a/dlls/windowscodecs/tests/bmpformat.c
+++ b/dlls/windowscodecs/tests/bmpformat.c
@@ -56,6 +56,12 @@
     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);
@@ -106,6 +112,42 @@
                 ok(SUCCEEDED(hr), "GetPixelFormat failed, hr=%x\n", hr);
                 ok(IsEqualGUID(&guidresult, &GUID_WICPixelFormat24bppBGR), "unexpected pixel format\n");
 
+                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");
+
                 IWICBitmapFrameDecode_Release(framedecode);
             }
 
diff --git a/dlls/windowscodecs/wincodecs_private.h b/dlls/windowscodecs/wincodecs_private.h
index 0070bfc..313dca5 100644
--- a/dlls/windowscodecs/wincodecs_private.h
+++ b/dlls/windowscodecs/wincodecs_private.h
@@ -22,4 +22,8 @@
 extern HRESULT ImagingFactory_CreateInstance(IUnknown *pUnkOuter, REFIID riid, void** ppv);
 extern HRESULT BmpDecoder_CreateInstance(IUnknown *pUnkOuter, REFIID riid, void** ppv);
 
+extern HRESULT copy_pixels(UINT bpp, const BYTE *srcbuffer,
+    UINT srcwidth, UINT srcheight, INT srcstride,
+    const WICRect *rc, UINT dststride, UINT dstbuffersize, BYTE *dstbuffer);
+
 #endif /* WINCODECS_PRIVATE_H */