[SHELL32] CDrivesFolder: Implement the eject and disconnect menu items. CORE-13841
[reactos.git] / dll / win32 / windowscodecs / icoformat.c
index 8012b4e..8662956 100644 (file)
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-#include "config.h"
-
-#include <stdarg.h>
-
-#define COBJMACROS
-
-#include "windef.h"
-#include "winbase.h"
-#include "wingdi.h"
-#include "objbase.h"
-#include "wincodec.h"
-
 #include "wincodecs_private.h"
 
-#include "wine/debug.h"
-
-WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
-
-#include "pshpack1.h"
+#include <pshpack1.h>
 
 typedef struct {
     BYTE bWidth;
@@ -54,10 +38,10 @@ typedef struct
     WORD idCount;
 } ICONHEADER;
 
-#include "poppack.h"
+#include <poppack.h>
 
 typedef struct {
-    const IWICBitmapDecoderVtbl *lpVtbl;
+    IWICBitmapDecoder IWICBitmapDecoder_iface;
     LONG ref;
     BOOL initialized;
     IStream *stream;
@@ -66,17 +50,27 @@ typedef struct {
 } IcoDecoder;
 
 typedef struct {
-    const IWICBitmapFrameDecodeVtbl *lpVtbl;
+    IWICBitmapFrameDecode IWICBitmapFrameDecode_iface;
     LONG ref;
-    ICONDIRENTRY entry;
-    IcoDecoder *parent;
+    UINT width, height;
+    double dpiX, dpiY;
     BYTE *bits;
 } IcoFrameDecode;
 
+static inline IcoDecoder *impl_from_IWICBitmapDecoder(IWICBitmapDecoder *iface)
+{
+    return CONTAINING_RECORD(iface, IcoDecoder, IWICBitmapDecoder_iface);
+}
+
+static inline IcoFrameDecode *impl_from_IWICBitmapFrameDecode(IWICBitmapFrameDecode *iface)
+{
+    return CONTAINING_RECORD(iface, IcoFrameDecode, IWICBitmapFrameDecode_iface);
+}
+
 static HRESULT WINAPI IcoFrameDecode_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
     void **ppv)
 {
-    IcoFrameDecode *This = (IcoFrameDecode*)iface;
+    IcoFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
 
     if (!ppv) return E_INVALIDARG;
@@ -85,7 +79,7 @@ static HRESULT WINAPI IcoFrameDecode_QueryInterface(IWICBitmapFrameDecode *iface
         IsEqualIID(&IID_IWICBitmapSource, iid) ||
         IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
     {
-        *ppv = This;
+        *ppv = &This->IWICBitmapFrameDecode_iface;
     }
     else
     {
@@ -99,7 +93,7 @@ static HRESULT WINAPI IcoFrameDecode_QueryInterface(IWICBitmapFrameDecode *iface
 
 static ULONG WINAPI IcoFrameDecode_AddRef(IWICBitmapFrameDecode *iface)
 {
-    IcoFrameDecode *This = (IcoFrameDecode*)iface;
+    IcoFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
     ULONG ref = InterlockedIncrement(&This->ref);
 
     TRACE("(%p) refcount=%u\n", iface, ref);
@@ -109,14 +103,13 @@ static ULONG WINAPI IcoFrameDecode_AddRef(IWICBitmapFrameDecode *iface)
 
 static ULONG WINAPI IcoFrameDecode_Release(IWICBitmapFrameDecode *iface)
 {
-    IcoFrameDecode *This = (IcoFrameDecode*)iface;
+    IcoFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
     ULONG ref = InterlockedDecrement(&This->ref);
 
     TRACE("(%p) refcount=%u\n", iface, ref);
 
     if (ref == 0)
     {
-        IUnknown_Release((IUnknown*)This->parent);
         HeapFree(GetProcessHeap(), 0, This->bits);
         HeapFree(GetProcessHeap(), 0, This);
     }
@@ -127,10 +120,10 @@ static ULONG WINAPI IcoFrameDecode_Release(IWICBitmapFrameDecode *iface)
 static HRESULT WINAPI IcoFrameDecode_GetSize(IWICBitmapFrameDecode *iface,
     UINT *puiWidth, UINT *puiHeight)
 {
-    IcoFrameDecode *This = (IcoFrameDecode*)iface;
+    IcoFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
 
-    *puiWidth = This->entry.bWidth ? This->entry.bWidth : 256;
-    *puiHeight = This->entry.bHeight ? This->entry.bHeight : 256;
+    *puiWidth = This->width;
+    *puiHeight = This->height;
 
     TRACE("(%p) -> (%i,%i)\n", iface, *puiWidth, *puiHeight);
 
@@ -147,8 +140,14 @@ static HRESULT WINAPI IcoFrameDecode_GetPixelFormat(IWICBitmapFrameDecode *iface
 static HRESULT WINAPI IcoFrameDecode_GetResolution(IWICBitmapFrameDecode *iface,
     double *pDpiX, double *pDpiY)
 {
-    FIXME("(%p,%p,%p): stub\n", iface, pDpiX, pDpiY);
-    return E_NOTIMPL;
+    IcoFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
+
+    *pDpiX = This->dpiX;
+    *pDpiY = This->dpiY;
+
+    TRACE("(%p) -> (%f,%f)\n", iface, *pDpiX, *pDpiY);
+
+    return S_OK;
 }
 
 static HRESULT WINAPI IcoFrameDecode_CopyPalette(IWICBitmapFrameDecode *iface,
@@ -158,427 +157,283 @@ static HRESULT WINAPI IcoFrameDecode_CopyPalette(IWICBitmapFrameDecode *iface,
     return WINCODEC_ERR_PALETTEUNAVAILABLE;
 }
 
-static inline void pixel_set_trans(DWORD* pixel, BOOL transparent)
+static HRESULT WINAPI IcoFrameDecode_CopyPixels(IWICBitmapFrameDecode *iface,
+    const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
 {
-    if (transparent) *pixel = 0;
-    else *pixel |= 0xff000000;
+    IcoFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface);
+    TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
+
+    return copy_pixels(32, This->bits, This->width, This->height, This->width * 4,
+        prc, cbStride, cbBufferSize, pbBuffer);
 }
 
-static HRESULT IcoFrameDecode_ReadPixels(IcoFrameDecode *This)
+static HRESULT WINAPI IcoFrameDecode_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
+    IWICMetadataQueryReader **ppIMetadataQueryReader)
 {
-    BITMAPINFOHEADER bih;
-    DWORD colors[256];
-    UINT colorcount=0;
-    LARGE_INTEGER seek;
-    ULONG bytesread;
-    HRESULT hr;
-    BYTE *tempdata = NULL;
-    BYTE *bits = NULL;
-    UINT bitsStride;
-    UINT bitsSize;
-    UINT width, height;
-
-    width = This->entry.bWidth ? This->entry.bWidth : 256;
-    height = This->entry.bHeight ? This->entry.bHeight : 256;
-
-    /* read the BITMAPINFOHEADER */
-    seek.QuadPart = This->entry.dwDIBOffset;
-    hr = IStream_Seek(This->parent->stream, seek, STREAM_SEEK_SET, NULL);
-    if (FAILED(hr)) goto fail;
+    TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
+    return WINCODEC_ERR_UNSUPPORTEDOPERATION;
+}
 
-    hr = IStream_Read(This->parent->stream, &bih, sizeof(BITMAPINFOHEADER), &bytesread);
-    if (FAILED(hr) || bytesread != sizeof(BITMAPINFOHEADER)) goto fail;
+static HRESULT WINAPI IcoFrameDecode_GetColorContexts(IWICBitmapFrameDecode *iface,
+    UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
+{
+    TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
+    return WINCODEC_ERR_UNSUPPORTEDOPERATION;
+}
 
-    if (bih.biBitCount <= 8)
-    {
-        /* read the palette */
-        colorcount = bih.biClrUsed ? bih.biClrUsed : 1 << bih.biBitCount;
+static HRESULT WINAPI IcoFrameDecode_GetThumbnail(IWICBitmapFrameDecode *iface,
+    IWICBitmapSource **ppIThumbnail)
+{
+    TRACE("(%p,%p)\n", iface, ppIThumbnail);
+    return IWICBitmapFrameDecode_QueryInterface(iface, &IID_IWICBitmapSource, (void **)ppIThumbnail);
+}
 
-        hr = IStream_Read(This->parent->stream, colors, sizeof(RGBQUAD)*colorcount, &bytesread);
-        if (FAILED(hr) || bytesread != sizeof(RGBQUAD)*colorcount) goto fail;
-    }
+static const IWICBitmapFrameDecodeVtbl IcoFrameDecode_Vtbl = {
+    IcoFrameDecode_QueryInterface,
+    IcoFrameDecode_AddRef,
+    IcoFrameDecode_Release,
+    IcoFrameDecode_GetSize,
+    IcoFrameDecode_GetPixelFormat,
+    IcoFrameDecode_GetResolution,
+    IcoFrameDecode_CopyPalette,
+    IcoFrameDecode_CopyPixels,
+    IcoFrameDecode_GetMetadataQueryReader,
+    IcoFrameDecode_GetColorContexts,
+    IcoFrameDecode_GetThumbnail
+};
 
-    bitsStride = width * 4;
-    bitsSize = bitsStride * height;
+static inline void pixel_set_trans(DWORD* pixel, BOOL transparent)
+{
+    if (transparent) *pixel = 0;
+    else *pixel |= 0xff000000;
+}
 
-    /* read the XOR data */
-    switch (bih.biBitCount)
-    {
-    case 1:
+static HRESULT ReadIcoDib(IStream *stream, IcoFrameDecode *result)
+{
+    HRESULT hr;
+    BmpDecoder *bmp_decoder;
+    IWICBitmapDecoder *decoder;
+    IWICBitmapFrameDecode *framedecode;
+    WICPixelFormatGUID pixelformat;
+    IWICBitmapSource *source;
+    BOOL has_alpha=FALSE; /* if TRUE, alpha data might be in the image data */
+    WICRect rc;
+
+    hr = IcoDibDecoder_CreateInstance(&bmp_decoder);
+    if (SUCCEEDED(hr))
     {
-        UINT xorBytesPerRow = (width+31)/32*4;
-        UINT xorBytes = xorBytesPerRow * height;
-        INT xorStride;
-        BYTE *xorRow;
-        BYTE *bitsRow;
-        UINT x, y;
-
-        tempdata = HeapAlloc(GetProcessHeap(), 0, xorBytes);
-        if (!tempdata)
-        {
-            hr = E_OUTOFMEMORY;
-            goto fail;
-        }
+        BmpDecoder_GetWICDecoder(bmp_decoder, &decoder);
+        hr = IWICBitmapDecoder_Initialize(decoder, stream, WICDecodeMetadataCacheOnLoad);
 
-        hr = IStream_Read(This->parent->stream, tempdata, xorBytes, &bytesread);
-        if (FAILED(hr) || bytesread != xorBytes) goto fail;
+        if (SUCCEEDED(hr))
+            hr = IWICBitmapDecoder_GetFrame(decoder, 0, &framedecode);
 
-        if (bih.biHeight > 0) /* bottom-up DIB */
-        {
-            xorStride = -xorBytesPerRow;
-            xorRow = tempdata + (height-1)*xorBytesPerRow;
-        }
-        else /* top-down DIB */
+        if (SUCCEEDED(hr))
         {
-            xorStride = xorBytesPerRow;
-            xorRow = tempdata;
-        }
+            hr = IWICBitmapFrameDecode_GetSize(framedecode, &result->width, &result->height);
 
-        bits = HeapAlloc(GetProcessHeap(), 0, bitsSize);
-
-        /* palette-map the 1-bit data */
-        bitsRow = bits;
-        for (y=0; y<height; y++) {
-            BYTE *xorByte=xorRow;
-            DWORD *bitsPixel=(DWORD*)bitsRow;
-            for (x=0; x<width; x+=8) {
-                BYTE xorVal;
-                xorVal=*xorByte++;
-                *bitsPixel++ = colors[xorVal>>7];
-                if (x+1 < width) *bitsPixel++ = colors[xorVal>>6&1];
-                if (x+2 < width) *bitsPixel++ = colors[xorVal>>5&1];
-                if (x+3 < width) *bitsPixel++ = colors[xorVal>>4&1];
-                if (x+4 < width) *bitsPixel++ = colors[xorVal>>3&1];
-                if (x+5 < width) *bitsPixel++ = colors[xorVal>>2&1];
-                if (x+6 < width) *bitsPixel++ = colors[xorVal>>1&1];
-                if (x+7 < width) *bitsPixel++ = colors[xorVal&1];
+            if (SUCCEEDED(hr))
+            {
+                result->bits = HeapAlloc(GetProcessHeap(), 0, result->width * result->height * 4);
+                if (!result->bits) hr = E_OUTOFMEMORY;
             }
-            xorRow += xorStride;
-            bitsRow += bitsStride;
-        }
 
-        HeapFree(GetProcessHeap(), 0, tempdata);
-        break;
-    }
-    case 4:
-    {
-        UINT xorBytesPerRow = (width+7)/8*4;
-        UINT xorBytes = xorBytesPerRow * height;
-        INT xorStride;
-        BYTE *xorRow;
-        BYTE *bitsRow;
-        UINT x, y;
-
-        tempdata = HeapAlloc(GetProcessHeap(), 0, xorBytes);
-        if (!tempdata)
-        {
-            hr = E_OUTOFMEMORY;
-            goto fail;
-        }
-
-        hr = IStream_Read(This->parent->stream, tempdata, xorBytes, &bytesread);
-        if (FAILED(hr) || bytesread != xorBytes) goto fail;
-
-        if (bih.biHeight > 0) /* bottom-up DIB */
-        {
-            xorStride = -xorBytesPerRow;
-            xorRow = tempdata + (height-1)*xorBytesPerRow;
-        }
-        else /* top-down DIB */
-        {
-            xorStride = xorBytesPerRow;
-            xorRow = tempdata;
-        }
+            if (SUCCEEDED(hr))
+                hr = IWICBitmapFrameDecode_GetPixelFormat(framedecode, &pixelformat);
 
-        bits = HeapAlloc(GetProcessHeap(), 0, bitsSize);
-
-        /* palette-map the 4-bit data */
-        bitsRow = bits;
-        for (y=0; y<height; y++) {
-            BYTE *xorByte=xorRow;
-            DWORD *bitsPixel=(DWORD*)bitsRow;
-            for (x=0; x<width; x+=2) {
-                BYTE xorVal;
-                xorVal=*xorByte++;
-                *bitsPixel++ = colors[xorVal>>4];
-                if (x+1 < width) *bitsPixel++ = colors[xorVal&0xf];
+            if (IsEqualGUID(&pixelformat, &GUID_WICPixelFormat32bppBGR) ||
+                IsEqualGUID(&pixelformat, &GUID_WICPixelFormat32bppBGRA))
+            {
+                source = (IWICBitmapSource*)framedecode;
+                IWICBitmapSource_AddRef(source);
+                has_alpha = TRUE;
+            }
+            else
+            {
+                hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA,
+                    (IWICBitmapSource*)framedecode, &source);
+                has_alpha = FALSE;
             }
-            xorRow += xorStride;
-            bitsRow += bitsStride;
-        }
-
-        HeapFree(GetProcessHeap(), 0, tempdata);
-        break;
-    }
-    case 8:
-    {
-        UINT xorBytesPerRow = (width+3)/4*4;
-        UINT xorBytes = xorBytesPerRow * height;
-        INT xorStride;
-        BYTE *xorRow;
-        BYTE *bitsRow;
-        UINT x, y;
-
-        tempdata = HeapAlloc(GetProcessHeap(), 0, xorBytes);
-        if (!tempdata)
-        {
-            hr = E_OUTOFMEMORY;
-            goto fail;
-        }
-
-        hr = IStream_Read(This->parent->stream, tempdata, xorBytes, &bytesread);
-        if (FAILED(hr) || bytesread != xorBytes) goto fail;
-
-        if (bih.biHeight > 0) /* bottom-up DIB */
-        {
-            xorStride = -xorBytesPerRow;
-            xorRow = tempdata + (height-1)*xorBytesPerRow;
-        }
-        else /* top-down DIB */
-        {
-            xorStride = xorBytesPerRow;
-            xorRow = tempdata;
-        }
-
-        bits = HeapAlloc(GetProcessHeap(), 0, bitsSize);
-
-        /* palette-map the 8-bit data */
-        bitsRow = bits;
-        for (y=0; y<height; y++) {
-            BYTE *xorByte=xorRow;
-            DWORD *bitsPixel=(DWORD*)bitsRow;
-            for (x=0; x<width; x++)
-                *bitsPixel++ = colors[*xorByte++];
-            xorRow += xorStride;
-            bitsRow += bitsStride;
-        }
-
-        HeapFree(GetProcessHeap(), 0, tempdata);
-        break;
-    }
-    case 24:
-    {
-        UINT xorBytesPerRow = (width*3+3)/4*4;
-        UINT xorBytes = xorBytesPerRow * height;
-        INT xorStride;
-        BYTE *xorRow;
-        BYTE *bitsRow;
-        UINT x, y;
-
-        tempdata = HeapAlloc(GetProcessHeap(), 0, xorBytes);
-        if (!tempdata)
-        {
-            hr = E_OUTOFMEMORY;
-            goto fail;
-        }
-
-        hr = IStream_Read(This->parent->stream, tempdata, xorBytes, &bytesread);
-        if (FAILED(hr) || bytesread != xorBytes) goto fail;
-
-        if (bih.biHeight > 0) /* bottom-up DIB */
-        {
-            xorStride = -xorBytesPerRow;
-            xorRow = tempdata + (height-1)*xorBytesPerRow;
-        }
-        else /* top-down DIB */
-        {
-            xorStride = xorBytesPerRow;
-            xorRow = tempdata;
-        }
-
-        bits = HeapAlloc(GetProcessHeap(), 0, bitsSize);
 
-        /* copy BGR->BGRA */
-        bitsRow = bits;
-        for (y=0; y<height; y++) {
-            BYTE *xorByte=xorRow;
-            BYTE *bitsByte=bitsRow;
-            for (x=0; x<width; x++)
+            if (SUCCEEDED(hr))
             {
-                *bitsByte++ = *xorByte++; /* blue */
-                *bitsByte++ = *xorByte++; /* green */
-                *bitsByte++ = *xorByte++; /* red */
-                bitsByte++; /* alpha */
+                rc.X = 0;
+                rc.Y = 0;
+                rc.Width = result->width;
+                rc.Height = result->height;
+                hr = IWICBitmapSource_CopyPixels(source, &rc, result->width * 4,
+                    result->width * result->height * 4, result->bits);
+
+                IWICBitmapSource_Release(source);
             }
-            xorRow += xorStride;
-            bitsRow += bitsStride;
-        }
 
-        HeapFree(GetProcessHeap(), 0, tempdata);
-        break;
-    }
-    case 32:
-    {
-        UINT xorBytesPerRow = width*4;
-        UINT xorBytes = xorBytesPerRow * height;
+            if (SUCCEEDED(hr))
+                hr = IWICBitmapFrameDecode_GetResolution(framedecode, &result->dpiX, &result->dpiY);
 
-        bits = HeapAlloc(GetProcessHeap(), 0, xorBytes);
-        if (!bits)
-        {
-            hr = E_OUTOFMEMORY;
-            goto fail;
+            IWICBitmapFrameDecode_Release(framedecode);
         }
 
-        if (bih.biHeight > 0) /* bottom-up DIB */
+        if (SUCCEEDED(hr) && has_alpha)
         {
-            /* read the rows backwards so we get a top-down DIB */
+            /* If the alpha channel is fully transparent, we should ignore it. */
+            int nonzero_alpha = 0;
             UINT i;
-            BYTE *xorRow = bits + xorBytesPerRow * (height-1);
 
-            for (i=0; i<height; i++)
+            for (i=0; i<(result->height*result->width); i++)
             {
-                hr = IStream_Read(This->parent->stream, xorRow, xorBytesPerRow, &bytesread);
-                if (FAILED(hr) || bytesread != xorBytesPerRow) goto fail;
-                xorRow -= xorBytesPerRow;
+                if (result->bits[i*4+3] != 0)
+                {
+                    nonzero_alpha = 1;
+                    break;
+                }
             }
-        }
-        else /* top-down DIB */
-        {
-            hr = IStream_Read(This->parent->stream, bits, xorBytes, &bytesread);
-            if (FAILED(hr) || bytesread != xorBytes) goto fail;
-        }
-        break;
-    }
-    default:
-        FIXME("unsupported bitcount: %u\n", bih.biBitCount);
-        goto fail;
-    }
 
-    if (bih.biBitCount < 32)
-    {
-        /* set alpha data based on the AND mask */
-        UINT andBytesPerRow = (width+31)/32*4;
-        UINT andBytes = andBytesPerRow * height;
-        INT andStride;
-        BYTE *andRow;
-        BYTE *bitsRow;
-        UINT x, y;
-
-        tempdata = HeapAlloc(GetProcessHeap(), 0, andBytes);
-        if (!tempdata)
-        {
-            hr = E_OUTOFMEMORY;
-            goto fail;
-        }
-
-        hr = IStream_Read(This->parent->stream, tempdata, andBytes, &bytesread);
-        if (FAILED(hr) || bytesread != andBytes) goto fail;
+            if (!nonzero_alpha)
+            {
+                for (i=0; i<(result->height*result->width); i++)
+                    result->bits[i*4+3] = 0xff;
 
-        if (bih.biHeight > 0) /* bottom-up DIB */
-        {
-            andStride = -andBytesPerRow;
-            andRow = tempdata + (height-1)*andBytesPerRow;
-        }
-        else /* top-down DIB */
-        {
-            andStride = andBytesPerRow;
-            andRow = tempdata;
+                has_alpha = FALSE;
+            }
         }
 
-        bitsRow = bits;
-        for (y=0; y<height; y++) {
-            BYTE *andByte=andRow;
-            DWORD *bitsPixel=(DWORD*)bitsRow;
-            for (x=0; x<width; x+=8) {
-                BYTE andVal=*andByte++;
-                pixel_set_trans(bitsPixel++, andVal>>7&1);
-                if (x+1 < width) pixel_set_trans(bitsPixel++, andVal>>6&1);
-                if (x+2 < width) pixel_set_trans(bitsPixel++, andVal>>5&1);
-                if (x+3 < width) pixel_set_trans(bitsPixel++, andVal>>4&1);
-                if (x+4 < width) pixel_set_trans(bitsPixel++, andVal>>3&1);
-                if (x+5 < width) pixel_set_trans(bitsPixel++, andVal>>2&1);
-                if (x+6 < width) pixel_set_trans(bitsPixel++, andVal>>1&1);
-                if (x+7 < width) pixel_set_trans(bitsPixel++, andVal&1);
+        if (SUCCEEDED(hr) && !has_alpha)
+        {
+            /* set alpha data based on the AND mask */
+            UINT andBytesPerRow = (result->width+31)/32*4;
+            UINT andBytes = andBytesPerRow * result->height;
+            INT andStride;
+            BYTE *tempdata=NULL;
+            BYTE *andRow;
+            BYTE *bitsRow;
+            UINT bitsStride = result->width * 4;
+            UINT x, y;
+            ULONG offset;
+            ULONG bytesread;
+            LARGE_INTEGER seek;
+            int topdown;
+
+            BmpDecoder_FindIconMask(bmp_decoder, &offset, &topdown);
+
+            if (offset)
+            {
+                seek.QuadPart = offset;
+
+                hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, 0);
+
+                if (SUCCEEDED(hr))
+                {
+                    tempdata = HeapAlloc(GetProcessHeap(), 0, andBytes);
+                    if (!tempdata) hr = E_OUTOFMEMORY;
+                }
+
+                if (SUCCEEDED(hr))
+                    hr = IStream_Read(stream, tempdata, andBytes, &bytesread);
+
+                if (SUCCEEDED(hr) && bytesread == andBytes)
+                {
+                    if (topdown)
+                    {
+                        andStride = andBytesPerRow;
+                        andRow = tempdata;
+                    }
+                    else
+                    {
+                        andStride = -andBytesPerRow;
+                        andRow = tempdata + (result->height-1)*andBytesPerRow;
+                    }
+
+                    bitsRow = result->bits;
+                    for (y=0; y<result->height; y++) {
+                        BYTE *andByte=andRow;
+                        DWORD *bitsPixel=(DWORD*)bitsRow;
+                        for (x=0; x<result->width; x+=8) {
+                            BYTE andVal=*andByte++;
+                            pixel_set_trans(bitsPixel++, andVal>>7&1);
+                            if (x+1 < result->width) pixel_set_trans(bitsPixel++, andVal>>6&1);
+                            if (x+2 < result->width) pixel_set_trans(bitsPixel++, andVal>>5&1);
+                            if (x+3 < result->width) pixel_set_trans(bitsPixel++, andVal>>4&1);
+                            if (x+4 < result->width) pixel_set_trans(bitsPixel++, andVal>>3&1);
+                            if (x+5 < result->width) pixel_set_trans(bitsPixel++, andVal>>2&1);
+                            if (x+6 < result->width) pixel_set_trans(bitsPixel++, andVal>>1&1);
+                            if (x+7 < result->width) pixel_set_trans(bitsPixel++, andVal&1);
+                        }
+                        andRow += andStride;
+                        bitsRow += bitsStride;
+                    }
+                }
+
+                HeapFree(GetProcessHeap(), 0, tempdata);
             }
-            andRow += andStride;
-            bitsRow += bitsStride;
         }
 
-        HeapFree(GetProcessHeap(), 0, tempdata);
+        IWICBitmapDecoder_Release(decoder);
     }
 
-    This->bits = bits;
-
-    return S_OK;
-
-fail:
-    HeapFree(GetProcessHeap(), 0, tempdata);
-    HeapFree(GetProcessHeap(), 0, bits);
-    if (SUCCEEDED(hr)) hr = E_FAIL;
-    TRACE("<-- %x\n", hr);
     return hr;
 }
 
-static HRESULT WINAPI IcoFrameDecode_CopyPixels(IWICBitmapFrameDecode *iface,
-    const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
+static HRESULT ReadIcoPng(IStream *stream, IcoFrameDecode *result)
 {
-    IcoFrameDecode *This = (IcoFrameDecode*)iface;
-    HRESULT hr=S_OK;
-    UINT width, height, stride;
-    TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
+    IWICBitmapDecoder *decoder = NULL;
+    IWICBitmapFrameDecode *sourceFrame = NULL;
+    IWICBitmapSource *sourceBitmap = NULL;
+    WICRect rect;
+    HRESULT hr;
 
-    EnterCriticalSection(&This->parent->lock);
-    if (!This->bits)
+    hr = PngDecoder_CreateInstance(&IID_IWICBitmapDecoder, (void**)&decoder);
+    if (FAILED(hr))
+        goto end;
+    hr = IWICBitmapDecoder_Initialize(decoder, stream, WICDecodeMetadataCacheOnLoad);
+    if (FAILED(hr))
+        goto end;
+    hr = IWICBitmapDecoder_GetFrame(decoder, 0, &sourceFrame);
+    if (FAILED(hr))
+        goto end;
+    hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, (IWICBitmapSource*)sourceFrame, &sourceBitmap);
+    if (FAILED(hr))
+        goto end;
+    hr = IWICBitmapFrameDecode_GetSize(sourceFrame, &result->width, &result->height);
+    if (FAILED(hr))
+        goto end;
+    hr = IWICBitmapFrameDecode_GetResolution(sourceFrame, &result->dpiX, &result->dpiY);
+    if (FAILED(hr))
+        goto end;
+    result->bits = HeapAlloc(GetProcessHeap(), 0, 4 * result->width * result->height);
+    if (result->bits == NULL)
     {
-        hr = IcoFrameDecode_ReadPixels(This);
+        hr = E_OUTOFMEMORY;
+        goto end;
     }
-    LeaveCriticalSection(&This->parent->lock);
-    if (FAILED(hr)) return hr;
+    rect.X = 0;
+    rect.Y = 0;
+    rect.Width = result->width;
+    rect.Height = result->height;
+    hr = IWICBitmapSource_CopyPixels(sourceBitmap, &rect, 4*result->width,
+                                     4*result->width*result->height, result->bits);
 
-    width = This->entry.bWidth ? This->entry.bWidth : 256;
-    height = This->entry.bHeight ? This->entry.bHeight : 256;
-    stride = width * 4;
-
-    return copy_pixels(32, This->bits, width, height, stride,
-        prc, cbStride, cbBufferSize, pbBuffer);
-}
-
-static HRESULT WINAPI IcoFrameDecode_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
-    IWICMetadataQueryReader **ppIMetadataQueryReader)
-{
-    TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
-    return WINCODEC_ERR_UNSUPPORTEDOPERATION;
-}
-
-static HRESULT WINAPI IcoFrameDecode_GetColorContexts(IWICBitmapFrameDecode *iface,
-    UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
-{
-    TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
-    return WINCODEC_ERR_UNSUPPORTEDOPERATION;
-}
-
-static HRESULT WINAPI IcoFrameDecode_GetThumbnail(IWICBitmapFrameDecode *iface,
-    IWICBitmapSource **ppIThumbnail)
-{
-    TRACE("(%p,%p)\n", iface, ppIThumbnail);
-    return WINCODEC_ERR_CODECNOTHUMBNAIL;
+end:
+    if (decoder != NULL)
+        IWICBitmapDecoder_Release(decoder);
+    if (sourceFrame != NULL)
+        IWICBitmapFrameDecode_Release(sourceFrame);
+    if (sourceBitmap != NULL)
+        IWICBitmapSource_Release(sourceBitmap);
+    return hr;
 }
 
-static const IWICBitmapFrameDecodeVtbl IcoFrameDecode_Vtbl = {
-    IcoFrameDecode_QueryInterface,
-    IcoFrameDecode_AddRef,
-    IcoFrameDecode_Release,
-    IcoFrameDecode_GetSize,
-    IcoFrameDecode_GetPixelFormat,
-    IcoFrameDecode_GetResolution,
-    IcoFrameDecode_CopyPalette,
-    IcoFrameDecode_CopyPixels,
-    IcoFrameDecode_GetMetadataQueryReader,
-    IcoFrameDecode_GetColorContexts,
-    IcoFrameDecode_GetThumbnail
-};
-
 static HRESULT WINAPI IcoDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid,
     void **ppv)
 {
-    IcoDecoder *This = (IcoDecoder*)iface;
+    IcoDecoder *This = impl_from_IWICBitmapDecoder(iface);
     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
 
     if (!ppv) return E_INVALIDARG;
 
-    if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IWICBitmapDecoder, iid))
+    if (IsEqualIID(&IID_IUnknown, iid) ||
+        IsEqualIID(&IID_IWICBitmapDecoder, iid))
     {
-        *ppv = This;
+        *ppv = &This->IWICBitmapDecoder_iface;
     }
     else
     {
@@ -592,7 +447,7 @@ static HRESULT WINAPI IcoDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID
 
 static ULONG WINAPI IcoDecoder_AddRef(IWICBitmapDecoder *iface)
 {
-    IcoDecoder *This = (IcoDecoder*)iface;
+    IcoDecoder *This = impl_from_IWICBitmapDecoder(iface);
     ULONG ref = InterlockedIncrement(&This->ref);
 
     TRACE("(%p) refcount=%u\n", iface, ref);
@@ -602,7 +457,7 @@ static ULONG WINAPI IcoDecoder_AddRef(IWICBitmapDecoder *iface)
 
 static ULONG WINAPI IcoDecoder_Release(IWICBitmapDecoder *iface)
 {
-    IcoDecoder *This = (IcoDecoder*)iface;
+    IcoDecoder *This = impl_from_IWICBitmapDecoder(iface);
     ULONG ref = InterlockedDecrement(&This->ref);
 
     TRACE("(%p) refcount=%u\n", iface, ref);
@@ -618,17 +473,26 @@ static ULONG WINAPI IcoDecoder_Release(IWICBitmapDecoder *iface)
     return ref;
 }
 
-static HRESULT WINAPI IcoDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *pIStream,
-    DWORD *pdwCapability)
+static HRESULT WINAPI IcoDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *stream,
+    DWORD *capability)
 {
-    FIXME("(%p,%p,%p): stub\n", iface, pIStream, pdwCapability);
-    return E_NOTIMPL;
+    HRESULT hr;
+
+    TRACE("(%p,%p,%p)\n", iface, stream, capability);
+
+    if (!stream || !capability) return E_INVALIDARG;
+
+    hr = IWICBitmapDecoder_Initialize(iface, stream, WICDecodeMetadataCacheOnDemand);
+    if (hr != S_OK) return hr;
+
+    *capability = WICBitmapDecoderCapabilityCanDecodeAllImages;
+    return S_OK;
 }
 
 static HRESULT WINAPI IcoDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream,
     WICDecodeOptions cacheOptions)
 {
-    IcoDecoder *This = (IcoDecoder*)iface;
+    IcoDecoder *This = impl_from_IWICBitmapDecoder(iface);
     LARGE_INTEGER seek;
     HRESULT hr;
     ULONG bytesread;
@@ -670,15 +534,27 @@ end:
 static HRESULT WINAPI IcoDecoder_GetContainerFormat(IWICBitmapDecoder *iface,
     GUID *pguidContainerFormat)
 {
-    FIXME("(%p,%p): stub\n", iface, pguidContainerFormat);
-    return E_NOTIMPL;
+    memcpy(pguidContainerFormat, &GUID_ContainerFormatIco, sizeof(GUID));
+    return S_OK;
 }
 
 static HRESULT WINAPI IcoDecoder_GetDecoderInfo(IWICBitmapDecoder *iface,
     IWICBitmapDecoderInfo **ppIDecoderInfo)
 {
-    FIXME("(%p,%p): stub\n", iface, ppIDecoderInfo);
-    return E_NOTIMPL;
+    HRESULT hr;
+    IWICComponentInfo *compinfo;
+
+    TRACE("(%p,%p)\n", iface, ppIDecoderInfo);
+
+    hr = CreateComponentInfo(&CLSID_WICIcoDecoder, &compinfo);
+    if (FAILED(hr)) return hr;
+
+    hr = IWICComponentInfo_QueryInterface(compinfo, &IID_IWICBitmapDecoderInfo,
+        (void**)ppIDecoderInfo);
+
+    IWICComponentInfo_Release(compinfo);
+
+    return hr;
 }
 
 static HRESULT WINAPI IcoDecoder_CopyPalette(IWICBitmapDecoder *iface,
@@ -719,13 +595,15 @@ static HRESULT WINAPI IcoDecoder_GetThumbnail(IWICBitmapDecoder *iface,
 static HRESULT WINAPI IcoDecoder_GetFrameCount(IWICBitmapDecoder *iface,
     UINT *pCount)
 {
-    IcoDecoder *This = (IcoDecoder*)iface;
-    TRACE("(%p,%p)\n", iface, pCount);
+    IcoDecoder *This = impl_from_IWICBitmapDecoder(iface);
 
-    if (!This->initialized) return WINCODEC_ERR_NOTINITIALIZED;
+    if (!pCount) return E_INVALIDARG;
 
-    *pCount = This->header.idCount;
-    TRACE("<-- %u\n", *pCount);
+    EnterCriticalSection(&This->lock);
+    *pCount = This->initialized ? This->header.idCount : 0;
+    LeaveCriticalSection(&This->lock);
+
+    TRACE("(%p) <-- %d\n", iface, *pCount);
 
     return S_OK;
 }
@@ -733,18 +611,22 @@ static HRESULT WINAPI IcoDecoder_GetFrameCount(IWICBitmapDecoder *iface,
 static HRESULT WINAPI IcoDecoder_GetFrame(IWICBitmapDecoder *iface,
     UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
 {
-    IcoDecoder *This = (IcoDecoder*)iface;
+    IcoDecoder *This = impl_from_IWICBitmapDecoder(iface);
     IcoFrameDecode *result=NULL;
     LARGE_INTEGER seek;
+    ULARGE_INTEGER offset, length;
     HRESULT hr;
     ULONG bytesread;
+    ICONDIRENTRY entry;
+    IWICStream *substream=NULL;
+    DWORD magic;
     TRACE("(%p,%u,%p)\n", iface, index, ppIBitmapFrame);
 
     EnterCriticalSection(&This->lock);
 
     if (!This->initialized)
     {
-        hr = WINCODEC_ERR_NOTINITIALIZED;
+        hr = WINCODEC_ERR_FRAMEMISSING;
         goto fail;
     }
 
@@ -761,9 +643,8 @@ static HRESULT WINAPI IcoDecoder_GetFrame(IWICBitmapDecoder *iface,
         goto fail;
     }
 
-    result->lpVtbl = &IcoFrameDecode_Vtbl;
+    result->IWICBitmapFrameDecode_iface.lpVtbl = &IcoFrameDecode_Vtbl;
     result->ref = 1;
-    result->parent = This;
     result->bits = NULL;
 
     /* read the icon entry */
@@ -771,20 +652,54 @@ static HRESULT WINAPI IcoDecoder_GetFrame(IWICBitmapDecoder *iface,
     hr = IStream_Seek(This->stream, seek, STREAM_SEEK_SET, 0);
     if (FAILED(hr)) goto fail;
 
-    hr = IStream_Read(This->stream, &result->entry, sizeof(ICONDIRENTRY), &bytesread);
+    hr = IStream_Read(This->stream, &entry, sizeof(ICONDIRENTRY), &bytesread);
     if (FAILED(hr) || bytesread != sizeof(ICONDIRENTRY)) goto fail;
 
-    IWICBitmapDecoder_AddRef(iface);
+    /* create a stream object for this icon */
+    hr = StreamImpl_Create(&substream);
+    if (FAILED(hr)) goto fail;
 
-    *ppIBitmapFrame = (IWICBitmapFrameDecode*)result;
+    offset.QuadPart = entry.dwDIBOffset;
+    length.QuadPart = entry.dwDIBSize;
+    hr = IWICStream_InitializeFromIStreamRegion(substream, This->stream, offset, length);
+    if (FAILED(hr)) goto fail;
+
+    /* read the bitmapinfo size or magic number */
+    hr = IWICStream_Read(substream, &magic, sizeof(magic), &bytesread);
+    if (FAILED(hr) || bytesread != sizeof(magic)) goto fail;
+
+    /* forward to the appropriate decoding function based on the magic number */
+    switch (magic)
+    {
+    case sizeof(BITMAPCOREHEADER):
+    case 64: /* sizeof(BITMAPCOREHEADER2) */
+    case sizeof(BITMAPINFOHEADER):
+    case sizeof(BITMAPV4HEADER):
+    case sizeof(BITMAPV5HEADER):
+        hr = ReadIcoDib((IStream*)substream, result);
+        break;
+    case 0x474e5089:
+        hr = ReadIcoPng((IStream*)substream, result);
+        break;
+    default:
+        FIXME("Unrecognized ICO frame magic: %x\n", magic);
+        hr = E_FAIL;
+        break;
+    }
+    if (FAILED(hr)) goto fail;
+
+    *ppIBitmapFrame = &result->IWICBitmapFrameDecode_iface;
 
     LeaveCriticalSection(&This->lock);
 
+    IWICStream_Release(substream);
+
     return S_OK;
 
 fail:
     LeaveCriticalSection(&This->lock);
     HeapFree(GetProcessHeap(), 0, result);
+    if (substream) IWICStream_Release(substream);
     if (SUCCEEDED(hr)) hr = E_FAIL;
     TRACE("<-- %x\n", hr);
     return hr;
@@ -807,29 +722,27 @@ static const IWICBitmapDecoderVtbl IcoDecoder_Vtbl = {
     IcoDecoder_GetFrame
 };
 
-HRESULT IcoDecoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
+HRESULT IcoDecoder_CreateInstance(REFIID iid, void** ppv)
 {
     IcoDecoder *This;
     HRESULT ret;
 
-    TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
+    TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
 
     *ppv = NULL;
 
-    if (pUnkOuter) return CLASS_E_NOAGGREGATION;
-
     This = HeapAlloc(GetProcessHeap(), 0, sizeof(IcoDecoder));
     if (!This) return E_OUTOFMEMORY;
 
-    This->lpVtbl = &IcoDecoder_Vtbl;
+    This->IWICBitmapDecoder_iface.lpVtbl = &IcoDecoder_Vtbl;
     This->ref = 1;
     This->stream = NULL;
     This->initialized = FALSE;
     InitializeCriticalSection(&This->lock);
     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": IcoDecoder.lock");
 
-    ret = IUnknown_QueryInterface((IUnknown*)This, iid, ppv);
-    IUnknown_Release((IUnknown*)This);
+    ret = IWICBitmapDecoder_QueryInterface(&This->IWICBitmapDecoder_iface, iid, ppv);
+    IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
 
     return ret;
 }