[GDIPLUS]
[reactos.git] / reactos / dll / win32 / gdiplus / image.c
index 5552225..d72d4cf 100644 (file)
@@ -31,6 +31,7 @@
 #include "ole2.h"
 
 #include "initguid.h"
+#include "wincodec.h"
 #include "gdiplus.h"
 #include "gdiplus_private.h"
 #include "wine/debug.h"
@@ -70,19 +71,779 @@ static INT ipicture_pixel_width(IPicture *pic)
     return x;
 }
 
+GpStatus WINGDIPAPI GdipBitmapApplyEffect(GpBitmap* bitmap, CGpEffect* effect,
+    RECT* roi, BOOL useAuxData, VOID** auxData, INT* auxDataSize)
+{
+    FIXME("(%p %p %p %d %p %p): stub\n", bitmap, effect, roi, useAuxData, auxData, auxDataSize);
+    /*
+     * Note: According to Jose Roca's GDI+ docs, this function is not
+     * implemented in Windows's GDI+.
+     */
+    return NotImplemented;
+}
+
+GpStatus WINGDIPAPI GdipBitmapCreateApplyEffect(GpBitmap** inputBitmaps,
+    INT numInputs, CGpEffect* effect, RECT* roi, RECT* outputRect,
+    GpBitmap** outputBitmap, BOOL useAuxData, VOID** auxData, INT* auxDataSize)
+{
+    FIXME("(%p %d %p %p %p %p %d %p %p): stub\n", inputBitmaps, numInputs, effect, roi, outputRect, outputBitmap, useAuxData, auxData, auxDataSize);
+    /*
+     * Note: According to Jose Roca's GDI+ docs, this function is not
+     * implemented in Windows's GDI+.
+     */
+    return NotImplemented;
+}
+
+static inline void getpixel_1bppIndexed(BYTE *index, const BYTE *row, UINT x)
+{
+    *index = (row[x/8]>>(7-x%8)) & 1;
+}
+
+static inline void getpixel_4bppIndexed(BYTE *index, const BYTE *row, UINT x)
+{
+    if (x & 1)
+        *index = row[x/2]&0xf;
+    else
+        *index = row[x/2]>>4;
+}
+
+static inline void getpixel_8bppIndexed(BYTE *index, const BYTE *row, UINT x)
+{
+    *index = row[x];
+}
+
+static inline void getpixel_16bppGrayScale(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
+    const BYTE *row, UINT x)
+{
+    *r = *g = *b = row[x*2+1];
+    *a = 255;
+}
+
+static inline void getpixel_16bppRGB555(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
+    const BYTE *row, UINT x)
+{
+    WORD pixel = *((WORD*)(row)+x);
+    *r = (pixel>>7&0xf8)|(pixel>>12&0x7);
+    *g = (pixel>>2&0xf8)|(pixel>>6&0x7);
+    *b = (pixel<<3&0xf8)|(pixel>>2&0x7);
+    *a = 255;
+}
+
+static inline void getpixel_16bppRGB565(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
+    const BYTE *row, UINT x)
+{
+    WORD pixel = *((WORD*)(row)+x);
+    *r = (pixel>>8&0xf8)|(pixel>>13&0x7);
+    *g = (pixel>>3&0xfc)|(pixel>>9&0x3);
+    *b = (pixel<<3&0xf8)|(pixel>>2&0x7);
+    *a = 255;
+}
+
+static inline void getpixel_16bppARGB1555(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
+    const BYTE *row, UINT x)
+{
+    WORD pixel = *((WORD*)(row)+x);
+    *r = (pixel>>7&0xf8)|(pixel>>12&0x7);
+    *g = (pixel>>2&0xf8)|(pixel>>6&0x7);
+    *b = (pixel<<3&0xf8)|(pixel>>2&0x7);
+    if ((pixel&0x8000) == 0x8000)
+        *a = 255;
+    else
+        *a = 0;
+}
+
+static inline void getpixel_24bppRGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
+    const BYTE *row, UINT x)
+{
+    *r = row[x*3+2];
+    *g = row[x*3+1];
+    *b = row[x*3];
+    *a = 255;
+}
+
+static inline void getpixel_32bppRGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
+    const BYTE *row, UINT x)
+{
+    *r = row[x*4+2];
+    *g = row[x*4+1];
+    *b = row[x*4];
+    *a = 255;
+}
+
+static inline void getpixel_32bppARGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
+    const BYTE *row, UINT x)
+{
+    *r = row[x*4+2];
+    *g = row[x*4+1];
+    *b = row[x*4];
+    *a = row[x*4+3];
+}
+
+static inline void getpixel_32bppPARGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
+    const BYTE *row, UINT x)
+{
+    *a = row[x*4+3];
+    if (*a == 0)
+        *r = *g = *b = 0;
+    else
+    {
+        *r = row[x*4+2] * 255 / *a;
+        *g = row[x*4+1] * 255 / *a;
+        *b = row[x*4] * 255 / *a;
+    }
+}
+
+static inline void getpixel_48bppRGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
+    const BYTE *row, UINT x)
+{
+    *r = row[x*6+5];
+    *g = row[x*6+3];
+    *b = row[x*6+1];
+    *a = 255;
+}
+
+static inline void getpixel_64bppARGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
+    const BYTE *row, UINT x)
+{
+    *r = row[x*8+5];
+    *g = row[x*8+3];
+    *b = row[x*8+1];
+    *a = row[x*8+7];
+}
+
+static inline void getpixel_64bppPARGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
+    const BYTE *row, UINT x)
+{
+    *a = row[x*8+7];
+    if (*a == 0)
+        *r = *g = *b = 0;
+    else
+    {
+        *r = row[x*8+5] * 255 / *a;
+        *g = row[x*8+3] * 255 / *a;
+        *b = row[x*8+1] * 255 / *a;
+    }
+}
+
 GpStatus WINGDIPAPI GdipBitmapGetPixel(GpBitmap* bitmap, INT x, INT y,
     ARGB *color)
 {
-    static int calls;
+    BYTE r, g, b, a;
+    BYTE index;
+    BYTE *row;
     TRACE("%p %d %d %p\n", bitmap, x, y, color);
 
-    if(!bitmap || !color)
+    if(!bitmap || !color ||
+       x < 0 || y < 0 || x >= bitmap->width || y >= bitmap->height)
         return InvalidParameter;
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+    row = bitmap->bits+bitmap->stride*y;
+
+    switch (bitmap->format)
+    {
+        case PixelFormat1bppIndexed:
+            getpixel_1bppIndexed(&index,row,x);
+            break;
+        case PixelFormat4bppIndexed:
+            getpixel_4bppIndexed(&index,row,x);
+            break;
+        case PixelFormat8bppIndexed:
+            getpixel_8bppIndexed(&index,row,x);
+            break;
+        case PixelFormat16bppGrayScale:
+            getpixel_16bppGrayScale(&r,&g,&b,&a,row,x);
+            break;
+        case PixelFormat16bppRGB555:
+            getpixel_16bppRGB555(&r,&g,&b,&a,row,x);
+            break;
+        case PixelFormat16bppRGB565:
+            getpixel_16bppRGB565(&r,&g,&b,&a,row,x);
+            break;
+        case PixelFormat16bppARGB1555:
+            getpixel_16bppARGB1555(&r,&g,&b,&a,row,x);
+            break;
+        case PixelFormat24bppRGB:
+            getpixel_24bppRGB(&r,&g,&b,&a,row,x);
+            break;
+        case PixelFormat32bppRGB:
+            getpixel_32bppRGB(&r,&g,&b,&a,row,x);
+            break;
+        case PixelFormat32bppARGB:
+            getpixel_32bppARGB(&r,&g,&b,&a,row,x);
+            break;
+        case PixelFormat32bppPARGB:
+            getpixel_32bppPARGB(&r,&g,&b,&a,row,x);
+            break;
+        case PixelFormat48bppRGB:
+            getpixel_48bppRGB(&r,&g,&b,&a,row,x);
+            break;
+        case PixelFormat64bppARGB:
+            getpixel_64bppARGB(&r,&g,&b,&a,row,x);
+            break;
+        case PixelFormat64bppPARGB:
+            getpixel_64bppPARGB(&r,&g,&b,&a,row,x);
+            break;
+        default:
+            FIXME("not implemented for format 0x%x\n", bitmap->format);
+            return NotImplemented;
+    }
+
+    if (bitmap->format & PixelFormatIndexed)
+        *color = bitmap->image.palette_entries[index];
+    else
+        *color = a<<24|r<<16|g<<8|b;
+
+    return Ok;
+}
+
+static inline void setpixel_16bppGrayScale(BYTE r, BYTE g, BYTE b, BYTE a,
+    BYTE *row, UINT x)
+{
+    *((WORD*)(row)+x) = (r+g+b)*85;
+}
+
+static inline void setpixel_16bppRGB555(BYTE r, BYTE g, BYTE b, BYTE a,
+    BYTE *row, UINT x)
+{
+    *((WORD*)(row)+x) = (r<<7&0x7c00)|
+                        (g<<2&0x03e0)|
+                        (b>>3&0x001f);
+}
+
+static inline void setpixel_16bppRGB565(BYTE r, BYTE g, BYTE b, BYTE a,
+    BYTE *row, UINT x)
+{
+    *((WORD*)(row)+x) = (r<<8&0xf800)|
+                         (g<<3&0x07e0)|
+                         (b>>3&0x001f);
+}
+
+static inline void setpixel_16bppARGB1555(BYTE r, BYTE g, BYTE b, BYTE a,
+    BYTE *row, UINT x)
+{
+    *((WORD*)(row)+x) = (a<<8&0x8000)|
+                        (r<<7&0x7c00)|
+                        (g<<2&0x03e0)|
+                        (b>>3&0x001f);
+}
+
+static inline void setpixel_24bppRGB(BYTE r, BYTE g, BYTE b, BYTE a,
+    BYTE *row, UINT x)
+{
+    row[x*3+2] = r;
+    row[x*3+1] = g;
+    row[x*3] = b;
+}
+
+static inline void setpixel_32bppRGB(BYTE r, BYTE g, BYTE b, BYTE a,
+    BYTE *row, UINT x)
+{
+    *((DWORD*)(row)+x) = (r<<16)|(g<<8)|b;
+}
+
+static inline void setpixel_32bppARGB(BYTE r, BYTE g, BYTE b, BYTE a,
+    BYTE *row, UINT x)
+{
+    *((DWORD*)(row)+x) = (a<<24)|(r<<16)|(g<<8)|b;
+}
+
+static inline void setpixel_32bppPARGB(BYTE r, BYTE g, BYTE b, BYTE a,
+    BYTE *row, UINT x)
+{
+    r = r * a / 255;
+    g = g * a / 255;
+    b = b * a / 255;
+    *((DWORD*)(row)+x) = (a<<24)|(r<<16)|(g<<8)|b;
+}
+
+static inline void setpixel_48bppRGB(BYTE r, BYTE g, BYTE b, BYTE a,
+    BYTE *row, UINT x)
+{
+    row[x*6+5] = row[x*6+4] = r;
+    row[x*6+3] = row[x*6+2] = g;
+    row[x*6+1] = row[x*6] = b;
+}
+
+static inline void setpixel_64bppARGB(BYTE r, BYTE g, BYTE b, BYTE a,
+    BYTE *row, UINT x)
+{
+    UINT64 a64=a, r64=r, g64=g, b64=b;
+    *((UINT64*)(row)+x) = (a64<<56)|(a64<<48)|(r64<<40)|(r64<<32)|(g64<<24)|(g64<<16)|(b64<<8)|b64;
+}
+
+static inline void setpixel_64bppPARGB(BYTE r, BYTE g, BYTE b, BYTE a,
+    BYTE *row, UINT x)
+{
+    UINT64 a64, r64, g64, b64;
+    a64 = a * 257;
+    r64 = r * a / 255;
+    g64 = g * a / 255;
+    b64 = b * a / 255;
+    *((UINT64*)(row)+x) = (a64<<48)|(r64<<32)|(g64<<16)|b64;
+}
+
+GpStatus WINGDIPAPI GdipBitmapSetPixel(GpBitmap* bitmap, INT x, INT y,
+    ARGB color)
+{
+    BYTE a, r, g, b;
+    BYTE *row;
+    TRACE("bitmap:%p, x:%d, y:%d, color:%08x\n", bitmap, x, y, color);
+
+    if(!bitmap || x < 0 || y < 0 || x >= bitmap->width || y >= bitmap->height)
+        return InvalidParameter;
+
+    a = color>>24;
+    r = color>>16;
+    g = color>>8;
+    b = color;
+
+    row = bitmap->bits + bitmap->stride * y;
+
+    switch (bitmap->format)
+    {
+        case PixelFormat16bppGrayScale:
+            setpixel_16bppGrayScale(r,g,b,a,row,x);
+            break;
+        case PixelFormat16bppRGB555:
+            setpixel_16bppRGB555(r,g,b,a,row,x);
+            break;
+        case PixelFormat16bppRGB565:
+            setpixel_16bppRGB565(r,g,b,a,row,x);
+            break;
+        case PixelFormat16bppARGB1555:
+            setpixel_16bppARGB1555(r,g,b,a,row,x);
+            break;
+        case PixelFormat24bppRGB:
+            setpixel_24bppRGB(r,g,b,a,row,x);
+            break;
+        case PixelFormat32bppRGB:
+            setpixel_32bppRGB(r,g,b,a,row,x);
+            break;
+        case PixelFormat32bppARGB:
+            setpixel_32bppARGB(r,g,b,a,row,x);
+            break;
+        case PixelFormat32bppPARGB:
+            setpixel_32bppPARGB(r,g,b,a,row,x);
+            break;
+        case PixelFormat48bppRGB:
+            setpixel_48bppRGB(r,g,b,a,row,x);
+            break;
+        case PixelFormat64bppARGB:
+            setpixel_64bppARGB(r,g,b,a,row,x);
+            break;
+        case PixelFormat64bppPARGB:
+            setpixel_64bppPARGB(r,g,b,a,row,x);
+            break;
+        default:
+            FIXME("not implemented for format 0x%x\n", bitmap->format);
+            return NotImplemented;
+    }
+
+    return Ok;
+}
+
+GpStatus convert_pixels(UINT width, UINT height,
+    INT dst_stride, BYTE *dst_bits, PixelFormat dst_format,
+    INT src_stride, const BYTE *src_bits, PixelFormat src_format, ARGB *src_palette)
+{
+    UINT x, y;
+
+    if (src_format == dst_format ||
+        (dst_format == PixelFormat32bppRGB && PIXELFORMATBPP(src_format) == 32))
+    {
+        UINT widthbytes = PIXELFORMATBPP(src_format) * width / 8;
+        for (y=0; y<height; y++)
+            memcpy(dst_bits+dst_stride*y, src_bits+src_stride*y, widthbytes);
+        return Ok;
+    }
+
+#define convert_indexed_to_rgb(getpixel_function, setpixel_function) do { \
+    for (x=0; x<width; x++) \
+        for (y=0; y<height; y++) { \
+            BYTE index; \
+            BYTE *color; \
+            getpixel_function(&index, src_bits+src_stride*y, x); \
+            color = (BYTE*)(&src_palette[index]); \
+            setpixel_function(color[2], color[1], color[0], color[3], dst_bits+dst_stride*y, x); \
+        } \
+    return Ok; \
+} while (0);
+
+#define convert_rgb_to_rgb(getpixel_function, setpixel_function) do { \
+    for (x=0; x<width; x++) \
+        for (y=0; y<height; y++) { \
+            BYTE r, g, b, a; \
+            getpixel_function(&r, &g, &b, &a, src_bits+src_stride*y, x); \
+            setpixel_function(r, g, b, a, dst_bits+dst_stride*y, x); \
+        } \
+    return Ok; \
+} while (0);
+
+    switch (src_format)
+    {
+    case PixelFormat1bppIndexed:
+        switch (dst_format)
+        {
+        case PixelFormat16bppGrayScale:
+            convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_16bppGrayScale);
+        case PixelFormat16bppRGB555:
+            convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_16bppRGB555);
+        case PixelFormat16bppRGB565:
+            convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_16bppRGB565);
+        case PixelFormat16bppARGB1555:
+            convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_16bppARGB1555);
+        case PixelFormat24bppRGB:
+            convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_24bppRGB);
+        case PixelFormat32bppRGB:
+            convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_32bppRGB);
+        case PixelFormat32bppARGB:
+            convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_32bppARGB);
+        case PixelFormat32bppPARGB:
+            convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_32bppPARGB);
+        case PixelFormat48bppRGB:
+            convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_48bppRGB);
+        case PixelFormat64bppARGB:
+            convert_indexed_to_rgb(getpixel_1bppIndexed, setpixel_64bppARGB);
+        default:
+            break;
+        }
+        break;
+    case PixelFormat4bppIndexed:
+        switch (dst_format)
+        {
+        case PixelFormat16bppGrayScale:
+            convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_16bppGrayScale);
+        case PixelFormat16bppRGB555:
+            convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_16bppRGB555);
+        case PixelFormat16bppRGB565:
+            convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_16bppRGB565);
+        case PixelFormat16bppARGB1555:
+            convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_16bppARGB1555);
+        case PixelFormat24bppRGB:
+            convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_24bppRGB);
+        case PixelFormat32bppRGB:
+            convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_32bppRGB);
+        case PixelFormat32bppARGB:
+            convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_32bppARGB);
+        case PixelFormat32bppPARGB:
+            convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_32bppPARGB);
+        case PixelFormat48bppRGB:
+            convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_48bppRGB);
+        case PixelFormat64bppARGB:
+            convert_indexed_to_rgb(getpixel_4bppIndexed, setpixel_64bppARGB);
+        default:
+            break;
+        }
+        break;
+    case PixelFormat8bppIndexed:
+        switch (dst_format)
+        {
+        case PixelFormat16bppGrayScale:
+            convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_16bppGrayScale);
+        case PixelFormat16bppRGB555:
+            convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_16bppRGB555);
+        case PixelFormat16bppRGB565:
+            convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_16bppRGB565);
+        case PixelFormat16bppARGB1555:
+            convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_16bppARGB1555);
+        case PixelFormat24bppRGB:
+            convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_24bppRGB);
+        case PixelFormat32bppRGB:
+            convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_32bppRGB);
+        case PixelFormat32bppARGB:
+            convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_32bppARGB);
+        case PixelFormat32bppPARGB:
+            convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_32bppPARGB);
+        case PixelFormat48bppRGB:
+            convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_48bppRGB);
+        case PixelFormat64bppARGB:
+            convert_indexed_to_rgb(getpixel_8bppIndexed, setpixel_64bppARGB);
+        default:
+            break;
+        }
+        break;
+    case PixelFormat16bppGrayScale:
+        switch (dst_format)
+        {
+        case PixelFormat16bppRGB555:
+            convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_16bppRGB555);
+        case PixelFormat16bppRGB565:
+            convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_16bppRGB565);
+        case PixelFormat16bppARGB1555:
+            convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_16bppARGB1555);
+        case PixelFormat24bppRGB:
+            convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_24bppRGB);
+        case PixelFormat32bppRGB:
+            convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_32bppRGB);
+        case PixelFormat32bppARGB:
+            convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_32bppARGB);
+        case PixelFormat32bppPARGB:
+            convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_32bppPARGB);
+        case PixelFormat48bppRGB:
+            convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_48bppRGB);
+        case PixelFormat64bppARGB:
+            convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_64bppARGB);
+        default:
+            break;
+        }
+        break;
+    case PixelFormat16bppRGB555:
+        switch (dst_format)
+        {
+        case PixelFormat16bppGrayScale:
+            convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_16bppGrayScale);
+        case PixelFormat16bppRGB565:
+            convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_16bppRGB565);
+        case PixelFormat16bppARGB1555:
+            convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_16bppARGB1555);
+        case PixelFormat24bppRGB:
+            convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_24bppRGB);
+        case PixelFormat32bppRGB:
+            convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_32bppRGB);
+        case PixelFormat32bppARGB:
+            convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_32bppARGB);
+        case PixelFormat32bppPARGB:
+            convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_32bppPARGB);
+        case PixelFormat48bppRGB:
+            convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_48bppRGB);
+        case PixelFormat64bppARGB:
+            convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_64bppARGB);
+        default:
+            break;
+        }
+        break;
+    case PixelFormat16bppRGB565:
+        switch (dst_format)
+        {
+        case PixelFormat16bppGrayScale:
+            convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_16bppGrayScale);
+        case PixelFormat16bppRGB555:
+            convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_16bppRGB555);
+        case PixelFormat16bppARGB1555:
+            convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_16bppARGB1555);
+        case PixelFormat24bppRGB:
+            convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_24bppRGB);
+        case PixelFormat32bppRGB:
+            convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_32bppRGB);
+        case PixelFormat32bppARGB:
+            convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_32bppARGB);
+        case PixelFormat32bppPARGB:
+            convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_32bppPARGB);
+        case PixelFormat48bppRGB:
+            convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_48bppRGB);
+        case PixelFormat64bppARGB:
+            convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_64bppARGB);
+        default:
+            break;
+        }
+        break;
+    case PixelFormat16bppARGB1555:
+        switch (dst_format)
+        {
+        case PixelFormat16bppGrayScale:
+            convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_16bppGrayScale);
+        case PixelFormat16bppRGB555:
+            convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_16bppRGB555);
+        case PixelFormat16bppRGB565:
+            convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_16bppRGB565);
+        case PixelFormat24bppRGB:
+            convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_24bppRGB);
+        case PixelFormat32bppRGB:
+            convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_32bppRGB);
+        case PixelFormat32bppARGB:
+            convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_32bppARGB);
+        case PixelFormat32bppPARGB:
+            convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_32bppPARGB);
+        case PixelFormat48bppRGB:
+            convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_48bppRGB);
+        case PixelFormat64bppARGB:
+            convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_64bppARGB);
+        default:
+            break;
+        }
+        break;
+    case PixelFormat24bppRGB:
+        switch (dst_format)
+        {
+        case PixelFormat16bppGrayScale:
+            convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_16bppGrayScale);
+        case PixelFormat16bppRGB555:
+            convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_16bppRGB555);
+        case PixelFormat16bppRGB565:
+            convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_16bppRGB565);
+        case PixelFormat16bppARGB1555:
+            convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_16bppARGB1555);
+        case PixelFormat32bppRGB:
+            convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_32bppRGB);
+        case PixelFormat32bppARGB:
+            convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_32bppARGB);
+        case PixelFormat32bppPARGB:
+            convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_32bppPARGB);
+        case PixelFormat48bppRGB:
+            convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_48bppRGB);
+        case PixelFormat64bppARGB:
+            convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_64bppARGB);
+        default:
+            break;
+        }
+        break;
+    case PixelFormat32bppRGB:
+        switch (dst_format)
+        {
+        case PixelFormat16bppGrayScale:
+            convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_16bppGrayScale);
+        case PixelFormat16bppRGB555:
+            convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_16bppRGB555);
+        case PixelFormat16bppRGB565:
+            convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_16bppRGB565);
+        case PixelFormat16bppARGB1555:
+            convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_16bppARGB1555);
+        case PixelFormat24bppRGB:
+            convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_24bppRGB);
+        case PixelFormat32bppARGB:
+            convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_32bppARGB);
+        case PixelFormat32bppPARGB:
+            convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_32bppPARGB);
+        case PixelFormat48bppRGB:
+            convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_48bppRGB);
+        case PixelFormat64bppARGB:
+            convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_64bppARGB);
+        default:
+            break;
+        }
+        break;
+    case PixelFormat32bppARGB:
+        switch (dst_format)
+        {
+        case PixelFormat16bppGrayScale:
+            convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_16bppGrayScale);
+        case PixelFormat16bppRGB555:
+            convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_16bppRGB555);
+        case PixelFormat16bppRGB565:
+            convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_16bppRGB565);
+        case PixelFormat16bppARGB1555:
+            convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_16bppARGB1555);
+        case PixelFormat24bppRGB:
+            convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_24bppRGB);
+        case PixelFormat32bppPARGB:
+            convert_32bppARGB_to_32bppPARGB(width, height, dst_bits, dst_stride, src_bits, src_stride);
+            return Ok;
+        case PixelFormat48bppRGB:
+            convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_48bppRGB);
+        case PixelFormat64bppARGB:
+            convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_64bppARGB);
+        default:
+            break;
+        }
+        break;
+    case PixelFormat32bppPARGB:
+        switch (dst_format)
+        {
+        case PixelFormat16bppGrayScale:
+            convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_16bppGrayScale);
+        case PixelFormat16bppRGB555:
+            convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_16bppRGB555);
+        case PixelFormat16bppRGB565:
+            convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_16bppRGB565);
+        case PixelFormat16bppARGB1555:
+            convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_16bppARGB1555);
+        case PixelFormat24bppRGB:
+            convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_24bppRGB);
+        case PixelFormat32bppRGB:
+            convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_32bppRGB);
+        case PixelFormat32bppARGB:
+            convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_32bppARGB);
+        case PixelFormat48bppRGB:
+            convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_48bppRGB);
+        case PixelFormat64bppARGB:
+            convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_64bppARGB);
+        default:
+            break;
+        }
+        break;
+    case PixelFormat48bppRGB:
+        switch (dst_format)
+        {
+        case PixelFormat16bppGrayScale:
+            convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_16bppGrayScale);
+        case PixelFormat16bppRGB555:
+            convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_16bppRGB555);
+        case PixelFormat16bppRGB565:
+            convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_16bppRGB565);
+        case PixelFormat16bppARGB1555:
+            convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_16bppARGB1555);
+        case PixelFormat24bppRGB:
+            convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_24bppRGB);
+        case PixelFormat32bppRGB:
+            convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_32bppRGB);
+        case PixelFormat32bppARGB:
+            convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_32bppARGB);
+        case PixelFormat32bppPARGB:
+            convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_32bppPARGB);
+        case PixelFormat64bppARGB:
+            convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_64bppARGB);
+        default:
+            break;
+        }
+        break;
+    case PixelFormat64bppARGB:
+        switch (dst_format)
+        {
+        case PixelFormat16bppGrayScale:
+            convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_16bppGrayScale);
+        case PixelFormat16bppRGB555:
+            convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_16bppRGB555);
+        case PixelFormat16bppRGB565:
+            convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_16bppRGB565);
+        case PixelFormat16bppARGB1555:
+            convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_16bppARGB1555);
+        case PixelFormat24bppRGB:
+            convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_24bppRGB);
+        case PixelFormat32bppRGB:
+            convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_32bppRGB);
+        case PixelFormat32bppARGB:
+            convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_32bppARGB);
+        case PixelFormat32bppPARGB:
+            convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_32bppPARGB);
+        case PixelFormat48bppRGB:
+            convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_48bppRGB);
+        default:
+            break;
+        }
+        break;
+    case PixelFormat64bppPARGB:
+        switch (dst_format)
+        {
+        case PixelFormat16bppGrayScale:
+            convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_16bppGrayScale);
+        case PixelFormat16bppRGB555:
+            convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_16bppRGB555);
+        case PixelFormat16bppRGB565:
+            convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_16bppRGB565);
+        case PixelFormat16bppARGB1555:
+            convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_16bppARGB1555);
+        case PixelFormat24bppRGB:
+            convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_24bppRGB);
+        case PixelFormat32bppRGB:
+            convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_32bppRGB);
+        case PixelFormat32bppARGB:
+            convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_32bppARGB);
+        case PixelFormat32bppPARGB:
+            convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_32bppPARGB);
+        case PixelFormat48bppRGB:
+            convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_48bppRGB);
+        case PixelFormat64bppARGB:
+            convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_64bppARGB);
+        default:
+            break;
+        }
+        break;
+    default:
+        break;
+    }
 
-    *color = 0xdeadbeef;
+#undef convert_indexed_to_rgb
+#undef convert_rgb_to_rgb
 
     return NotImplemented;
 }
@@ -96,16 +857,13 @@ GpStatus WINGDIPAPI GdipBitmapGetPixel(GpBitmap* bitmap, INT x, INT y,
 GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
     UINT flags, PixelFormat format, BitmapData* lockeddata)
 {
-    BOOL bm_is_selected;
     INT stride, bitspp = PIXELFORMATBPP(format);
-    HDC hdc;
-    HBITMAP hbm, old = NULL;
-    BITMAPINFO *pbmi;
     BYTE *buff = NULL;
     UINT abs_height;
     GpRect act_rect; /* actual rect to be used */
+    GpStatus stat;
 
-    TRACE("%p %p %d %d %p\n", bitmap, rect, flags, format, lockeddata);
+    TRACE("%p %p %d 0x%x %p\n", bitmap, rect, flags, format, lockeddata);
 
     if(!lockeddata || !bitmap)
         return InvalidParameter;
@@ -124,152 +882,224 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
     }
 
     if(flags & ImageLockModeUserInputBuf)
+    {
+        static int fixme=0;
+        if (!fixme++) FIXME("ImageLockModeUserInputBuf not implemented\n");
         return NotImplemented;
+    }
 
     if(bitmap->lockmode)
+    {
+        WARN("bitmap is already locked and cannot be locked again\n");
         return WrongState;
+    }
 
-    IPicture_get_Handle(bitmap->image.picture, (OLE_HANDLE*)&hbm);
-    IPicture_get_CurDC(bitmap->image.picture, &hdc);
-    bm_is_selected = (hdc != 0);
+    if (bitmap->bits && bitmap->format == format)
+    {
+        /* no conversion is necessary; just use the bits directly */
+        lockeddata->Width = act_rect.Width;
+        lockeddata->Height = act_rect.Height;
+        lockeddata->PixelFormat = format;
+        lockeddata->Reserved = flags;
+        lockeddata->Stride = bitmap->stride;
+        lockeddata->Scan0 = bitmap->bits + (bitspp / 8) * act_rect.X +
+                            bitmap->stride * act_rect.Y;
+
+        bitmap->lockmode = flags;
+        bitmap->numlocks++;
 
-    pbmi = GdipAlloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
-    if (!pbmi)
-        return OutOfMemory;
-    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
-    pbmi->bmiHeader.biBitCount = 0;
+        return Ok;
+    }
 
-    if(!bm_is_selected){
-        hdc = CreateCompatibleDC(0);
-        old = SelectObject(hdc, hbm);
+    /* Make sure we can convert to the requested format. */
+    stat = convert_pixels(0, 0, 0, NULL, format, 0, NULL, bitmap->format, NULL);
+    if (stat == NotImplemented)
+    {
+        FIXME("cannot read bitmap from %x to %x\n", bitmap->format, format);
+        return NotImplemented;
     }
 
-    /* fill out bmi */
-    GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);
+    /* If we're opening for writing, make sure we'll be able to write back in
+     * the original format. */
+    if (flags & ImageLockModeWrite)
+    {
+        stat = convert_pixels(0, 0, 0, NULL, bitmap->format, 0, NULL, format, NULL);
+        if (stat == NotImplemented)
+        {
+            FIXME("cannot write bitmap from %x to %x\n", format, bitmap->format);
+            return NotImplemented;
+        }
+    }
 
-    abs_height = abs(pbmi->bmiHeader.biHeight);
-    stride = pbmi->bmiHeader.biWidth * bitspp / 8;
+    abs_height = bitmap->height;
+    stride = (bitmap->width * bitspp + 7) / 8;
     stride = (stride + 3) & ~3;
 
     buff = GdipAlloc(stride * abs_height);
 
-    pbmi->bmiHeader.biBitCount = bitspp;
+    if (!buff) return OutOfMemory;
 
-    if(buff)
-        GetDIBits(hdc, hbm, 0, abs_height, buff, pbmi, DIB_RGB_COLORS);
+    stat = convert_pixels(bitmap->width, bitmap->height,
+        stride, buff, format,
+        bitmap->stride, bitmap->bits, bitmap->format, bitmap->image.palette_entries);
 
-    if(!bm_is_selected){
-        SelectObject(hdc, old);
-        DeleteDC(hdc);
-    }
-
-    if(!buff){
-        GdipFree(pbmi);
-        return OutOfMemory;
+    if (stat != Ok)
+    {
+        GdipFree(buff);
+        return stat;
     }
 
     lockeddata->Width  = act_rect.Width;
     lockeddata->Height = act_rect.Height;
     lockeddata->PixelFormat = format;
     lockeddata->Reserved = flags;
-
-    if(pbmi->bmiHeader.biHeight > 0){
-        lockeddata->Stride = -stride;
-        lockeddata->Scan0  = buff + (bitspp / 8) * act_rect.X +
-                             stride * (abs_height - 1 - act_rect.Y);
-    }
-    else{
-        lockeddata->Stride = stride;
-        lockeddata->Scan0  = buff + (bitspp / 8) * act_rect.X + stride * act_rect.Y;
-    }
+    lockeddata->Stride = stride;
+    lockeddata->Scan0  = buff + (bitspp / 8) * act_rect.X + stride * act_rect.Y;
 
     bitmap->lockmode = flags;
     bitmap->numlocks++;
-
     bitmap->bitmapbits = buff;
 
-    GdipFree(pbmi);
     return Ok;
 }
 
 GpStatus WINGDIPAPI GdipBitmapSetResolution(GpBitmap* bitmap, REAL xdpi, REAL ydpi)
 {
-    FIXME("(%p, %.2f, %.2f)\n", bitmap, xdpi, ydpi);
+    TRACE("(%p, %.2f, %.2f)\n", bitmap, xdpi, ydpi);
 
-    return NotImplemented;
+    if (!bitmap || xdpi == 0.0 || ydpi == 0.0)
+        return InvalidParameter;
+
+    bitmap->image.xres = xdpi;
+    bitmap->image.yres = ydpi;
+
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipBitmapUnlockBits(GpBitmap* bitmap,
     BitmapData* lockeddata)
 {
-    HDC hdc;
-    HBITMAP hbm, old = NULL;
-    BOOL bm_is_selected;
-    BITMAPINFO *pbmi;
+    GpStatus stat;
+
+    TRACE("(%p,%p)\n", bitmap, lockeddata);
+
+    if(!bitmap || !lockeddata)
+        return InvalidParameter;
+
+    if(!bitmap->lockmode)
+        return WrongState;
+
+    if(lockeddata->Reserved & ImageLockModeUserInputBuf)
+        return NotImplemented;
+
+    if(lockeddata->Reserved & ImageLockModeRead){
+        if(!(--bitmap->numlocks))
+            bitmap->lockmode = 0;
+
+        GdipFree(bitmap->bitmapbits);
+        bitmap->bitmapbits = NULL;
+        return Ok;
+    }
+
+    if (!bitmap->bitmapbits)
+    {
+        /* we passed a direct reference; no need to do anything */
+        bitmap->lockmode = 0;
+        bitmap->numlocks = 0;
+        return Ok;
+    }
+
+    stat = convert_pixels(bitmap->width, bitmap->height,
+        bitmap->stride, bitmap->bits, bitmap->format,
+        lockeddata->Stride, bitmap->bitmapbits, lockeddata->PixelFormat, NULL);
+
+    if (stat != Ok)
+    {
+        ERR("failed to convert pixels; this should never happen\n");
+    }
+
+    GdipFree(bitmap->bitmapbits);
+    bitmap->bitmapbits = NULL;
+    bitmap->lockmode = 0;
+    bitmap->numlocks = 0;
+
+    return stat;
+}
+
+GpStatus WINGDIPAPI GdipCloneBitmapArea(REAL x, REAL y, REAL width, REAL height,
+    PixelFormat format, GpBitmap* srcBitmap, GpBitmap** dstBitmap)
+{
+    BitmapData lockeddata_src, lockeddata_dst;
+    int i;
+    UINT row_size;
+    Rect area;
+    GpStatus stat;
 
-    if(!bitmap || !lockeddata)
-        return InvalidParameter;
+    TRACE("(%f,%f,%f,%f,0x%x,%p,%p)\n", x, y, width, height, format, srcBitmap, dstBitmap);
 
-    if(!bitmap->lockmode)
-        return WrongState;
+    if (!srcBitmap || !dstBitmap || srcBitmap->image.type != ImageTypeBitmap ||
+        x < 0 || y < 0 ||
+        x + width > srcBitmap->width || y + height > srcBitmap->height)
+    {
+        TRACE("<-- InvalidParameter\n");
+        return InvalidParameter;
+    }
 
-    if(lockeddata->Reserved & ImageLockModeUserInputBuf)
-        return NotImplemented;
+    if (format == PixelFormatDontCare)
+        format = srcBitmap->format;
 
-    if(lockeddata->Reserved & ImageLockModeRead){
-        if(!(--bitmap->numlocks))
-            bitmap->lockmode = 0;
+    area.X = roundr(x);
+    area.Y = roundr(y);
+    area.Width = roundr(width);
+    area.Height = roundr(height);
 
-        GdipFree(bitmap->bitmapbits);
-        bitmap->bitmapbits = NULL;
-        return Ok;
-    }
+    stat = GdipBitmapLockBits(srcBitmap, &area, ImageLockModeRead, format,
+        &lockeddata_src);
+    if (stat != Ok) return stat;
 
-    IPicture_get_Handle(bitmap->image.picture, (OLE_HANDLE*)&hbm);
-    IPicture_get_CurDC(bitmap->image.picture, &hdc);
-    bm_is_selected = (hdc != 0);
+    stat = GdipCreateBitmapFromScan0(lockeddata_src.Width, lockeddata_src.Height,
+        0, lockeddata_src.PixelFormat, NULL, dstBitmap);
+    if (stat == Ok)
+    {
+        stat = GdipBitmapLockBits(*dstBitmap, NULL, ImageLockModeWrite,
+            lockeddata_src.PixelFormat, &lockeddata_dst);
 
-    pbmi = GdipAlloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
-    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
-    pbmi->bmiHeader.biBitCount = 0;
+        if (stat == Ok)
+        {
+            /* copy the image data */
+            row_size = (lockeddata_src.Width * PIXELFORMATBPP(lockeddata_src.PixelFormat) +7)/8;
+            for (i=0; i<lockeddata_src.Height; i++)
+                memcpy((BYTE*)lockeddata_dst.Scan0+lockeddata_dst.Stride*i,
+                       (BYTE*)lockeddata_src.Scan0+lockeddata_src.Stride*i,
+                       row_size);
+
+            GdipBitmapUnlockBits(*dstBitmap, &lockeddata_dst);
+        }
 
-    if(!bm_is_selected){
-        hdc = CreateCompatibleDC(0);
-        old = SelectObject(hdc, hbm);
+        if (stat != Ok)
+            GdipDisposeImage((GpImage*)*dstBitmap);
     }
 
-    GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);
-    pbmi->bmiHeader.biBitCount = PIXELFORMATBPP(lockeddata->PixelFormat);
-    SetDIBits(hdc, hbm, 0, abs(pbmi->bmiHeader.biHeight),
-              bitmap->bitmapbits, pbmi, DIB_RGB_COLORS);
+    GdipBitmapUnlockBits(srcBitmap, &lockeddata_src);
 
-    if(!bm_is_selected){
-        SelectObject(hdc, old);
-        DeleteDC(hdc);
+    if (stat != Ok)
+    {
+        *dstBitmap = NULL;
     }
 
-    GdipFree(pbmi);
-    GdipFree(bitmap->bitmapbits);
-    bitmap->bitmapbits = NULL;
-    bitmap->lockmode = 0;
-
-    return Ok;
+    return stat;
 }
 
 GpStatus WINGDIPAPI GdipCloneBitmapAreaI(INT x, INT y, INT width, INT height,
     PixelFormat format, GpBitmap* srcBitmap, GpBitmap** dstBitmap)
 {
-    FIXME("(%i,%i,%i,%i,%i,%p,%p)\n", x, y, width, height, format, srcBitmap, dstBitmap);
+    TRACE("(%i,%i,%i,%i,0x%x,%p,%p)\n", x, y, width, height, format, srcBitmap, dstBitmap);
 
-    return NotImplemented;
+    return GdipCloneBitmapArea(x, y, width, height, format, srcBitmap, dstBitmap);
 }
 
 GpStatus WINGDIPAPI GdipCloneImage(GpImage *image, GpImage **cloneImage)
 {
-    IStream* stream;
-    HRESULT hr;
-    INT size;
-    LARGE_INTEGER move;
     GpStatus stat = GenericError;
 
     TRACE("%p, %p\n", image, cloneImage);
@@ -277,29 +1107,86 @@ GpStatus WINGDIPAPI GdipCloneImage(GpImage *image, GpImage **cloneImage)
     if (!image || !cloneImage)
         return InvalidParameter;
 
-    hr = CreateStreamOnHGlobal(0, TRUE, &stream);
-    if (FAILED(hr))
-        return GenericError;
-
-    hr = IPicture_SaveAsFile(image->picture, stream, FALSE, &size);
-    if(FAILED(hr))
+    if (image->picture)
     {
-        WARN("Failed to save image on stream\n");
-        goto out;
+        IStream* stream;
+        HRESULT hr;
+        INT size;
+        LARGE_INTEGER move;
+
+        hr = CreateStreamOnHGlobal(0, TRUE, &stream);
+        if (FAILED(hr))
+            return GenericError;
+
+        hr = IPicture_SaveAsFile(image->picture, stream, FALSE, &size);
+        if(FAILED(hr))
+        {
+            WARN("Failed to save image on stream\n");
+            goto out;
+        }
+
+        /* Set seek pointer back to the beginning of the picture */
+        move.QuadPart = 0;
+        hr = IStream_Seek(stream, move, STREAM_SEEK_SET, NULL);
+        if (FAILED(hr))
+            goto out;
+
+        stat = GdipLoadImageFromStream(stream, cloneImage);
+        if (stat != Ok) WARN("Failed to load image from stream\n");
+
+    out:
+        IStream_Release(stream);
+        return stat;
     }
+    else if (image->type == ImageTypeBitmap)
+    {
+        GpBitmap *bitmap = (GpBitmap*)image;
+        BitmapData lockeddata_src, lockeddata_dst;
+        int i;
+        UINT row_size;
+
+        stat = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, bitmap->format,
+            &lockeddata_src);
+        if (stat != Ok) return stat;
+
+        stat = GdipCreateBitmapFromScan0(lockeddata_src.Width, lockeddata_src.Height,
+            0, lockeddata_src.PixelFormat, NULL, (GpBitmap**)cloneImage);
+        if (stat == Ok)
+        {
+            stat = GdipBitmapLockBits((GpBitmap*)*cloneImage, NULL, ImageLockModeWrite,
+                lockeddata_src.PixelFormat, &lockeddata_dst);
+
+            if (stat == Ok)
+            {
+                /* copy the image data */
+                row_size = (lockeddata_src.Width * PIXELFORMATBPP(lockeddata_src.PixelFormat) +7)/8;
+                for (i=0; i<lockeddata_src.Height; i++)
+                    memcpy((BYTE*)lockeddata_dst.Scan0+lockeddata_dst.Stride*i,
+                           (BYTE*)lockeddata_src.Scan0+lockeddata_src.Stride*i,
+                           row_size);
+
+                GdipBitmapUnlockBits((GpBitmap*)*cloneImage, &lockeddata_dst);
+            }
+
+            if (stat != Ok)
+                GdipDisposeImage(*cloneImage);
+        }
 
-    /* Set seek pointer back to the beginning of the picture */
-    move.QuadPart = 0;
-    hr = IStream_Seek(stream, move, STREAM_SEEK_SET, NULL);
-    if (FAILED(hr))
-        goto out;
+        GdipBitmapUnlockBits(bitmap, &lockeddata_src);
 
-    stat = GdipLoadImageFromStream(stream, cloneImage);
-    if (stat != Ok) WARN("Failed to load image from stream\n");
+        if (stat != Ok)
+        {
+            *cloneImage = NULL;
+        }
+        else memcpy(&(*cloneImage)->format, &image->format, sizeof(GUID));
 
-out:
-    IStream_Release(stream);
-    return stat;
+        return stat;
+    }
+    else
+    {
+        ERR("GpImage with no IPicture or bitmap?!\n");
+        return NotImplemented;
+    }
 }
 
 GpStatus WINGDIPAPI GdipCreateBitmapFromFile(GDIPCONST WCHAR* filename,
@@ -401,11 +1288,69 @@ GpStatus WINGDIPAPI GdipCreateBitmapFromResource(HINSTANCE hInstance,
 GpStatus WINGDIPAPI GdipCreateHBITMAPFromBitmap(GpBitmap* bitmap,
     HBITMAP* hbmReturn, ARGB background)
 {
-    FIXME("stub\n");
+    GpStatus stat;
+    HBITMAP result, oldbitmap;
+    UINT width, height;
+    HDC hdc;
+    GpGraphics *graphics;
+    BITMAPINFOHEADER bih;
+    void *bits;
+    TRACE("(%p,%p,%x)\n", bitmap, hbmReturn, background);
+
+    if (!bitmap || !hbmReturn) return InvalidParameter;
+
+    GdipGetImageWidth((GpImage*)bitmap, &width);
+    GdipGetImageHeight((GpImage*)bitmap, &height);
+
+    bih.biSize = sizeof(bih);
+    bih.biWidth = width;
+    bih.biHeight = height;
+    bih.biPlanes = 1;
+    bih.biBitCount = 32;
+    bih.biCompression = BI_RGB;
+    bih.biSizeImage = 0;
+    bih.biXPelsPerMeter = 0;
+    bih.biYPelsPerMeter = 0;
+    bih.biClrUsed = 0;
+    bih.biClrImportant = 0;
+
+    hdc = CreateCompatibleDC(NULL);
+    if (!hdc) return GenericError;
+
+    result = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS, &bits,
+        NULL, 0);
+
+    if (result)
+    {
+        oldbitmap = SelectObject(hdc, result);
 
-    hbmReturn = NULL;
+        stat = GdipCreateFromHDC(hdc, &graphics);
+        if (stat == Ok)
+        {
+            stat = GdipGraphicsClear(graphics, background);
 
-    return NotImplemented;
+            if (stat == Ok)
+                stat = GdipDrawImage(graphics, (GpImage*)bitmap, 0, 0);
+
+            GdipDeleteGraphics(graphics);
+        }
+
+        SelectObject(hdc, oldbitmap);
+    }
+    else
+        stat = GenericError;
+
+    DeleteDC(hdc);
+
+    if (stat != Ok && result)
+    {
+        DeleteObject(result);
+        result = NULL;
+    }
+
+    *hbmReturn = result;
+
+    return stat;
 }
 
 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
@@ -414,6 +1359,9 @@ GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
 {
     static int calls;
 
+    TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
+        debugstr_w(description), out_metafile);
+
     if(!ref || !metafile || !out_metafile)
         return InvalidParameter;
 
@@ -448,58 +1396,234 @@ GpStatus WINGDIPAPI GdipCreateBitmapFromGraphics(INT width, INT height,
 
 GpStatus WINGDIPAPI GdipCreateBitmapFromHICON(HICON hicon, GpBitmap** bitmap)
 {
-    HICON icon_copy;
+    GpStatus stat;
     ICONINFO iinfo;
-    PICTDESC desc;
+    BITMAP bm;
+    int ret;
+    UINT width, height;
+    GpRect rect;
+    BitmapData lockeddata;
+    HDC screendc;
+    BOOL has_alpha;
+    int x, y;
+    BYTE *bits;
+    BITMAPINFOHEADER bih;
+    DWORD *src;
+    BYTE *dst_row;
+    DWORD *dst;
 
     TRACE("%p, %p\n", hicon, bitmap);
 
     if(!bitmap || !GetIconInfo(hicon, &iinfo))
         return InvalidParameter;
 
-    *bitmap = GdipAlloc(sizeof(GpBitmap));
-    if(!*bitmap)    return OutOfMemory;
+    /* get the size of the icon */
+    ret = GetObjectA(iinfo.hbmColor ? iinfo.hbmColor : iinfo.hbmMask, sizeof(bm), &bm);
+    if (ret == 0) {
+        DeleteObject(iinfo.hbmColor);
+        DeleteObject(iinfo.hbmMask);
+        return GenericError;
+    }
 
-    icon_copy = CreateIconIndirect(&iinfo);
+    width = bm.bmWidth;
 
-    if(!icon_copy){
-        GdipFree(*bitmap);
-        return InvalidParameter;
+    if (iinfo.hbmColor)
+        height = abs(bm.bmHeight);
+    else /* combined bitmap + mask */
+        height = abs(bm.bmHeight) / 2;
+
+    bits = HeapAlloc(GetProcessHeap(), 0, 4*width*height);
+    if (!bits) {
+        DeleteObject(iinfo.hbmColor);
+        DeleteObject(iinfo.hbmMask);
+        return OutOfMemory;
     }
 
-    desc.cbSizeofstruct = sizeof(PICTDESC);
-    desc.picType = PICTYPE_ICON;
-    desc.u.icon.hicon = icon_copy;
+    stat = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppARGB, NULL, bitmap);
+    if (stat != Ok) {
+        DeleteObject(iinfo.hbmColor);
+        DeleteObject(iinfo.hbmMask);
+        HeapFree(GetProcessHeap(), 0, bits);
+        return stat;
+    }
 
-    if(OleCreatePictureIndirect(&desc, &IID_IPicture, TRUE,
-                                (LPVOID*) &((*bitmap)->image.picture)) != S_OK){
-        DestroyIcon(icon_copy);
-        GdipFree(*bitmap);
-        return GenericError;
+    rect.X = 0;
+    rect.Y = 0;
+    rect.Width = width;
+    rect.Height = height;
+
+    stat = GdipBitmapLockBits(*bitmap, &rect, ImageLockModeWrite, PixelFormat32bppARGB, &lockeddata);
+    if (stat != Ok) {
+        DeleteObject(iinfo.hbmColor);
+        DeleteObject(iinfo.hbmMask);
+        HeapFree(GetProcessHeap(), 0, bits);
+        GdipDisposeImage((GpImage*)*bitmap);
+        return stat;
     }
 
-    (*bitmap)->format = PixelFormat32bppARGB;
-    (*bitmap)->image.type  = ImageTypeBitmap;
-    (*bitmap)->image.flags = ImageFlagsNone;
-    (*bitmap)->width  = ipicture_pixel_width((*bitmap)->image.picture);
-    (*bitmap)->height = ipicture_pixel_height((*bitmap)->image.picture);
+    bih.biSize = sizeof(bih);
+    bih.biWidth = width;
+    bih.biHeight = -height;
+    bih.biPlanes = 1;
+    bih.biBitCount = 32;
+    bih.biCompression = BI_RGB;
+    bih.biSizeImage = 0;
+    bih.biXPelsPerMeter = 0;
+    bih.biYPelsPerMeter = 0;
+    bih.biClrUsed = 0;
+    bih.biClrImportant = 0;
+
+    screendc = GetDC(0);
+    if (iinfo.hbmColor)
+    {
+        GetDIBits(screendc, iinfo.hbmColor, 0, height, bits, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
+
+        if (bm.bmBitsPixel == 32)
+        {
+            has_alpha = FALSE;
+
+            /* If any pixel has a non-zero alpha, ignore hbmMask */
+            src = (DWORD*)bits;
+            for (x=0; x<width && !has_alpha; x++)
+                for (y=0; y<height && !has_alpha; y++)
+                    if ((*src++ & 0xff000000) != 0)
+                        has_alpha = TRUE;
+        }
+        else has_alpha = FALSE;
+    }
+    else
+    {
+        GetDIBits(screendc, iinfo.hbmMask, 0, height, bits, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
+        has_alpha = FALSE;
+    }
+
+    /* copy the image data to the Bitmap */
+    src = (DWORD*)bits;
+    dst_row = lockeddata.Scan0;
+    for (y=0; y<height; y++)
+    {
+        memcpy(dst_row, src, width*4);
+        src += width;
+        dst_row += lockeddata.Stride;
+    }
+
+    if (!has_alpha)
+    {
+        if (iinfo.hbmMask)
+        {
+            /* read alpha data from the mask */
+            if (iinfo.hbmColor)
+                GetDIBits(screendc, iinfo.hbmMask, 0, height, bits, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
+            else
+                GetDIBits(screendc, iinfo.hbmMask, height, height, bits, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
+
+            src = (DWORD*)bits;
+            dst_row = lockeddata.Scan0;
+            for (y=0; y<height; y++)
+            {
+                dst = (DWORD*)dst_row;
+                for (x=0; x<height; x++)
+                {
+                    DWORD src_value = *src++;
+                    if (src_value)
+                        *dst++ = 0;
+                    else
+                        *dst++ |= 0xff000000;
+                }
+                dst_row += lockeddata.Stride;
+            }
+        }
+        else
+        {
+            /* set constant alpha of 255 */
+            dst_row = bits;
+            for (y=0; y<height; y++)
+            {
+                dst = (DWORD*)dst_row;
+                for (x=0; x<height; x++)
+                    *dst++ |= 0xff000000;
+                dst_row += lockeddata.Stride;
+            }
+        }
+    }
+
+    ReleaseDC(0, screendc);
 
     DeleteObject(iinfo.hbmColor);
     DeleteObject(iinfo.hbmMask);
 
+    GdipBitmapUnlockBits(*bitmap, &lockeddata);
+
+    HeapFree(GetProcessHeap(), 0, bits);
+
+    return Ok;
+}
+
+static void generate_halftone_palette(ARGB *entries, UINT count)
+{
+    static const BYTE halftone_values[6]={0x00,0x33,0x66,0x99,0xcc,0xff};
+    UINT i;
+
+    for (i=0; i<8 && i<count; i++)
+    {
+        entries[i] = 0xff000000;
+        if (i&1) entries[i] |= 0x800000;
+        if (i&2) entries[i] |= 0x8000;
+        if (i&4) entries[i] |= 0x80;
+    }
+
+    if (8 < count)
+        entries[i] = 0xffc0c0c0;
+
+    for (i=9; i<16 && i<count; i++)
+    {
+        entries[i] = 0xff000000;
+        if (i&1) entries[i] |= 0xff0000;
+        if (i&2) entries[i] |= 0xff00;
+        if (i&4) entries[i] |= 0xff;
+    }
+
+    for (i=16; i<40 && i<count; i++)
+    {
+        entries[i] = 0;
+    }
+
+    for (i=40; i<256 && i<count; i++)
+    {
+        entries[i] = 0xff000000;
+        entries[i] |= halftone_values[(i-40)%6];
+        entries[i] |= halftone_values[((i-40)/6)%6] << 8;
+        entries[i] |= halftone_values[((i-40)/36)%6] << 16;
+    }
+}
+
+static GpStatus get_screen_resolution(REAL *xres, REAL *yres)
+{
+    HDC screendc = GetDC(0);
+
+    if (!screendc) return GenericError;
+
+    *xres = (REAL)GetDeviceCaps(screendc, LOGPIXELSX);
+    *yres = (REAL)GetDeviceCaps(screendc, LOGPIXELSY);
+
+    ReleaseDC(0, screendc);
+
     return Ok;
 }
 
 GpStatus WINGDIPAPI GdipCreateBitmapFromScan0(INT width, INT height, INT stride,
     PixelFormat format, BYTE* scan0, GpBitmap** bitmap)
 {
-    BITMAPFILEHEADER *bmfh;
-    BITMAPINFOHEADER *bmih;
-    BYTE *buff;
-    INT datalen, size;
-    IStream *stream;
+    BITMAPINFO* pbmi;
+    HBITMAP hbitmap;
+    INT row_size, dib_stride;
+    HDC hdc;
+    BYTE *bits;
+    int i;
+    REAL xres, yres;
+    GpStatus stat;
 
-    TRACE("%d %d %d %d %p %p\n", width, height, stride, format, scan0, bitmap);
+    TRACE("%d %d %d 0x%x %p %p\n", width, height, stride, format, scan0, bitmap);
 
     if (!bitmap) return InvalidParameter;
 
@@ -511,78 +1635,107 @@ GpStatus WINGDIPAPI GdipCreateBitmapFromScan0(INT width, INT height, INT stride,
     if(scan0 && !stride)
         return InvalidParameter;
 
-    *bitmap = GdipAlloc(sizeof(GpBitmap));
-    if(!*bitmap)    return OutOfMemory;
+    stat = get_screen_resolution(&xres, &yres);
+    if (stat != Ok) return stat;
 
-    if(stride == 0){
-        stride = width * (PIXELFORMATBPP(format) / 8);
-        stride = (stride + 3) & ~3;
-    }
+    row_size = (width * PIXELFORMATBPP(format)+7) / 8;
+    dib_stride = (row_size + 3) & ~3;
+
+    if(stride == 0)
+        stride = dib_stride;
 
-    datalen = abs(stride * height);
-    size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + datalen;
-    buff = GdipAlloc(size);
-    if(!buff){
-        GdipFree(*bitmap);
+    pbmi = GdipAlloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
+    if (!pbmi)
         return OutOfMemory;
+
+    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+    pbmi->bmiHeader.biWidth = width;
+    pbmi->bmiHeader.biHeight = -height;
+    pbmi->bmiHeader.biPlanes = 1;
+    /* FIXME: use the rest of the data from format */
+    pbmi->bmiHeader.biBitCount = PIXELFORMATBPP(format);
+    pbmi->bmiHeader.biCompression = BI_RGB;
+    pbmi->bmiHeader.biSizeImage = 0;
+    pbmi->bmiHeader.biXPelsPerMeter = 0;
+    pbmi->bmiHeader.biYPelsPerMeter = 0;
+    pbmi->bmiHeader.biClrUsed = 0;
+    pbmi->bmiHeader.biClrImportant = 0;
+
+    hdc = CreateCompatibleDC(NULL);
+    if (!hdc) {
+        GdipFree(pbmi);
+        return GenericError;
     }
 
-    bmfh = (BITMAPFILEHEADER*) buff;
-    bmih = (BITMAPINFOHEADER*) (bmfh + 1);
+    hbitmap = CreateDIBSection(hdc, pbmi, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
 
-    bmfh->bfType    = (((WORD)'M') << 8) + (WORD)'B';
-    bmfh->bfSize    = size;
-    bmfh->bfOffBits = size - datalen;
+    DeleteDC(hdc);
+    GdipFree(pbmi);
 
-    bmih->biSize            = sizeof(BITMAPINFOHEADER);
-    bmih->biWidth           = width;
-    /* FIXME: use the rest of the data from format */
-    bmih->biBitCount        = PIXELFORMATBPP(format);
-    bmih->biCompression     = BI_RGB;
-    bmih->biSizeImage       = datalen;
+    if (!hbitmap) return GenericError;
 
+    /* copy bits to the dib if necessary */
+    /* FIXME: should reference the bits instead of copying them */
     if (scan0)
-    {
-        if (stride > 0)
-        {
-            bmih->biHeight = -height;
-            memcpy(bmih + 1, scan0, datalen);
-        }
-        else
-        {
-            bmih->biHeight = height;
-            memcpy(bmih + 1, scan0 + stride * (height - 1), datalen);
-        }
-    }
-    else
-    {
-        bmih->biHeight = height;
-        memset(bmih + 1, 0, datalen);
-    }
-
-    if(CreateStreamOnHGlobal(buff, TRUE, &stream) != S_OK){
-        ERR("could not make stream\n");
-        GdipFree(*bitmap);
-        GdipFree(buff);
-        *bitmap = NULL;
-        return GenericError;
-    }
+        for (i=0; i<height; i++)
+            memcpy(bits+i*dib_stride, scan0+i*stride, row_size);
 
-    if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
-        (LPVOID*) &((*bitmap)->image.picture)) != S_OK){
-        TRACE("Could not load picture\n");
-        IStream_Release(stream);
-        GdipFree(*bitmap);
-        GdipFree(buff);
-        *bitmap = NULL;
-        return GenericError;
+    *bitmap = GdipAlloc(sizeof(GpBitmap));
+    if(!*bitmap)
+    {
+        DeleteObject(hbitmap);
+        return OutOfMemory;
     }
 
     (*bitmap)->image.type = ImageTypeBitmap;
+    memcpy(&(*bitmap)->image.format, &ImageFormatMemoryBMP, sizeof(GUID));
     (*bitmap)->image.flags = ImageFlagsNone;
+    (*bitmap)->image.palette_flags = 0;
+    (*bitmap)->image.palette_count = 0;
+    (*bitmap)->image.palette_size = 0;
+    (*bitmap)->image.palette_entries = NULL;
+    (*bitmap)->image.xres = xres;
+    (*bitmap)->image.yres = yres;
     (*bitmap)->width = width;
     (*bitmap)->height = height;
     (*bitmap)->format = format;
+    (*bitmap)->image.picture = NULL;
+    (*bitmap)->hbitmap = hbitmap;
+    (*bitmap)->hdc = NULL;
+    (*bitmap)->bits = bits;
+    (*bitmap)->stride = dib_stride;
+
+    if (format == PixelFormat1bppIndexed ||
+        format == PixelFormat4bppIndexed ||
+        format == PixelFormat8bppIndexed)
+    {
+        (*bitmap)->image.palette_size = (*bitmap)->image.palette_count = 1 << PIXELFORMATBPP(format);
+        (*bitmap)->image.palette_entries = GdipAlloc(sizeof(ARGB) * ((*bitmap)->image.palette_size));
+
+        if (!(*bitmap)->image.palette_entries)
+        {
+            GdipDisposeImage(&(*bitmap)->image);
+            *bitmap = NULL;
+            return OutOfMemory;
+        }
+
+        if (format == PixelFormat1bppIndexed)
+        {
+            (*bitmap)->image.palette_flags = PaletteFlagsGrayScale;
+            (*bitmap)->image.palette_entries[0] = 0xff000000;
+            (*bitmap)->image.palette_entries[1] = 0xffffffff;
+        }
+        else
+        {
+            if (format == PixelFormat8bppIndexed)
+                (*bitmap)->image.palette_flags = PaletteFlagsHalftone;
+
+            generate_halftone_palette((*bitmap)->image.palette_entries,
+                (*bitmap)->image.palette_count);
+        }
+    }
+
+    TRACE("<-- %p\n", *bitmap);
 
     return Ok;
 }
@@ -600,8 +1753,8 @@ GpStatus WINGDIPAPI GdipCreateBitmapFromStream(IStream* stream,
         return stat;
 
     if((*bitmap)->image.type != ImageTypeBitmap){
-        IPicture_Release((*bitmap)->image.picture);
-        GdipFree(bitmap);
+        GdipDisposeImage(&(*bitmap)->image);
+        *bitmap = NULL;
         return GenericError; /* FIXME: what error to return? */
     }
 
@@ -640,6 +1793,13 @@ GpStatus WINGDIPAPI GdipCreateCachedBitmap(GpBitmap *bitmap, GpGraphics *graphic
     return Ok;
 }
 
+GpStatus WINGDIPAPI GdipCreateHICONFromBitmap(GpBitmap *bitmap, HICON *hicon)
+{
+    FIXME("(%p, %p)\n", bitmap, hicon);
+
+    return NotImplemented;
+}
+
 GpStatus WINGDIPAPI GdipDeleteCachedBitmap(GpCachedBitmap *cachedbmp)
 {
     TRACE("%p\n", cachedbmp);
@@ -664,20 +1824,29 @@ GpStatus WINGDIPAPI GdipDrawCachedBitmap(GpGraphics *graphics,
     return GdipDrawImage(graphics, cachedbmp->image, (REAL)x, (REAL)y);
 }
 
-GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image)
+GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
+    LPBYTE pData16, INT iMapMode, INT eFlags)
 {
-    HDC hdc;
+    FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
+    return NotImplemented;
+}
 
+GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image)
+{
     TRACE("%p\n", image);
 
     if(!image)
         return InvalidParameter;
 
-    IPicture_get_CurDC(image->picture, &hdc);
-    DeleteDC(hdc);
-    IPicture_Release(image->picture);
+    if (image->picture)
+        IPicture_Release(image->picture);
     if (image->type == ImageTypeBitmap)
+    {
         GdipFree(((GpBitmap*)image)->bitmapbits);
+        DeleteDC(((GpBitmap*)image)->hdc);
+        DeleteObject(((GpBitmap*)image)->hbitmap);
+    }
+    GdipFree(image->palette_entries);
     GdipFree(image);
 
     return Ok;
@@ -685,9 +1854,28 @@ GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image)
 
 GpStatus WINGDIPAPI GdipFindFirstImageItem(GpImage *image, ImageItemData* item)
 {
+    static int calls;
+
+    TRACE("(%p,%p)\n", image, item);
+
     if(!image || !item)
         return InvalidParameter;
 
+    if (!(calls++))
+        FIXME("not implemented\n");
+
+    return NotImplemented;
+}
+
+GpStatus WINGDIPAPI GdipGetImageItemData(GpImage *image, ImageItemData *item)
+{
+    static int calls;
+
+    TRACE("(%p,%p)\n", image, item);
+
+    if (!(calls++))
+        FIXME("not implemented\n");
+
     return NotImplemented;
 }
 
@@ -769,11 +1957,12 @@ GpStatus WINGDIPAPI GdipGetImageGraphicsContext(GpImage *image,
         return NotImplemented;
     }
 
-    IPicture_get_CurDC(image->picture, &hdc);
+    hdc = ((GpBitmap*)image)->hdc;
 
     if(!hdc){
         hdc = CreateCompatibleDC(0);
-        IPicture_SelectPicture(image->picture, hdc, NULL, NULL);
+        SelectObject(hdc, ((GpBitmap*)image)->hbitmap);
+        ((GpBitmap*)image)->hdc = hdc;
     }
 
     return GdipCreateFromHDC(hdc, graphics);
@@ -806,25 +1995,31 @@ GpStatus WINGDIPAPI GdipGetImageHeight(GpImage *image, UINT *height)
 
 GpStatus WINGDIPAPI GdipGetImageHorizontalResolution(GpImage *image, REAL *res)
 {
-    static int calls;
-
     if(!image || !res)
         return InvalidParameter;
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+    *res = image->xres;
 
-    return NotImplemented;
+    TRACE("(%p) <-- %0.2f\n", image, *res);
+
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipGetImagePaletteSize(GpImage *image, INT *size)
 {
-    FIXME("%p %p\n", image, size);
+    TRACE("%p %p\n", image, size);
 
     if(!image || !size)
         return InvalidParameter;
 
-    return NotImplemented;
+    if (image->palette_count == 0)
+        *size = sizeof(ColorPalette);
+    else
+        *size = sizeof(UINT)*2 + sizeof(ARGB)*image->palette_count;
+
+    TRACE("<-- %u\n", *size);
+
+    return Ok;
 }
 
 /* FIXME: test this function for non-bitmap types */
@@ -845,23 +2040,11 @@ GpStatus WINGDIPAPI GdipGetImagePixelFormat(GpImage *image, PixelFormat *format)
 
 GpStatus WINGDIPAPI GdipGetImageRawFormat(GpImage *image, GUID *format)
 {
-    static int calls;
-
     if(!image || !format)
         return InvalidParameter;
 
-    if(!(calls++))
-        FIXME("stub\n");
+    memcpy(format, &image->format, sizeof(GUID));
 
-    /* FIXME: should be detected from embedded picture or stored separately */
-    switch (image->type)
-    {
-    case ImageTypeBitmap:   *format = ImageFormatBMP; break;
-    case ImageTypeMetafile: *format = ImageFormatEMF; break;
-    default:
-        WARN("unknown type %u\n", image->type);
-        *format = ImageFormatUndefined;
-    }
     return Ok;
 }
 
@@ -879,15 +2062,14 @@ GpStatus WINGDIPAPI GdipGetImageType(GpImage *image, ImageType *type)
 
 GpStatus WINGDIPAPI GdipGetImageVerticalResolution(GpImage *image, REAL *res)
 {
-    static int calls;
-
     if(!image || !res)
         return InvalidParameter;
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+    *res = image->yres;
 
-    return NotImplemented;
+    TRACE("(%p) <-- %0.2f\n", image, *res);
+
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipGetImageWidth(GpImage *image, UINT *width)
@@ -991,17 +2173,34 @@ GpStatus WINGDIPAPI GdipGetPropertySize(GpImage *image, UINT* size, UINT* num)
 {
     static int calls;
 
+    TRACE("(%p,%p,%p)\n", image, size, num);
+
     if(!(calls++))
         FIXME("not implemented\n");
 
     return InvalidParameter;
 }
 
+struct image_format_dimension
+{
+    const GUID *format;
+    const GUID *dimension;
+};
+
+struct image_format_dimension image_format_dimensions[] =
+{
+    {&ImageFormatGIF, &FrameDimensionTime},
+    {&ImageFormatIcon, &FrameDimensionResolution},
+    {NULL}
+};
+
 GpStatus WINGDIPAPI GdipImageGetFrameCount(GpImage *image,
     GDIPCONST GUID* dimensionID, UINT* count)
 {
     static int calls;
 
+    TRACE("(%p,%s,%p)\n", image, debugstr_guid(dimensionID), count);
+
     if(!image || !dimensionID || !count)
         return InvalidParameter;
 
@@ -1014,26 +2213,40 @@ GpStatus WINGDIPAPI GdipImageGetFrameCount(GpImage *image,
 GpStatus WINGDIPAPI GdipImageGetFrameDimensionsCount(GpImage *image,
     UINT* count)
 {
+    /* Native gdiplus 1.1 does not yet support multiple frame dimensions. */
+
     if(!image || !count)
         return InvalidParameter;
 
     *count = 1;
 
-    FIXME("stub\n");
-
     return Ok;
 }
 
 GpStatus WINGDIPAPI GdipImageGetFrameDimensionsList(GpImage* image,
     GUID* dimensionIDs, UINT count)
 {
-    static int calls;
+    int i;
+    const GUID *result=NULL;
 
-    if(!image || !dimensionIDs)
+    TRACE("(%p,%p,%u)\n", image, dimensionIDs, count);
+
+    if(!image || !dimensionIDs || count != 1)
         return InvalidParameter;
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+    for (i=0; image_format_dimensions[i].format; i++)
+    {
+        if (IsEqualGUID(&image->format, image_format_dimensions[i].format))
+        {
+            result = image_format_dimensions[i].dimension;
+            break;
+        }
+    }
+
+    if (!result)
+        result = &FrameDimensionPage;
+
+    memcpy(dimensionIDs, result, sizeof(GUID));
 
     return Ok;
 }
@@ -1083,10 +2296,167 @@ GpStatus WINGDIPAPI GdipLoadImageFromFileICM(GDIPCONST WCHAR* filename,GpImage *
     return GdipLoadImageFromFile(filename, image);
 }
 
-GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream* stream, GpImage **image)
+static const WICPixelFormatGUID *wic_pixel_formats[] = {
+    &GUID_WICPixelFormat16bppBGR555,
+    &GUID_WICPixelFormat24bppBGR,
+    &GUID_WICPixelFormat32bppBGR,
+    &GUID_WICPixelFormat32bppBGRA,
+    &GUID_WICPixelFormat32bppPBGRA,
+    NULL
+};
+
+static const PixelFormat wic_gdip_formats[] = {
+    PixelFormat16bppRGB555,
+    PixelFormat24bppRGB,
+    PixelFormat32bppRGB,
+    PixelFormat32bppARGB,
+    PixelFormat32bppPARGB,
+};
+
+static GpStatus decode_image_wic(IStream* stream, REFCLSID clsid, GpImage **image)
+{
+    GpStatus status=Ok;
+    GpBitmap *bitmap;
+    HRESULT hr;
+    IWICBitmapDecoder *decoder;
+    IWICBitmapFrameDecode *frame;
+    IWICBitmapSource *source=NULL;
+    WICPixelFormatGUID wic_format;
+    PixelFormat gdip_format=0;
+    int i;
+    UINT width, height;
+    BitmapData lockeddata;
+    WICRect wrc;
+    HRESULT initresult;
+
+    initresult = CoInitialize(NULL);
+
+    hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICBitmapDecoder, (void**)&decoder);
+    if (FAILED(hr)) goto end;
+
+    hr = IWICBitmapDecoder_Initialize(decoder, (IStream*)stream, WICDecodeMetadataCacheOnLoad);
+    if (SUCCEEDED(hr))
+        hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
+
+    if (SUCCEEDED(hr)) /* got frame */
+    {
+        hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &wic_format);
+
+        if (SUCCEEDED(hr))
+        {
+            for (i=0; wic_pixel_formats[i]; i++)
+            {
+                if (IsEqualGUID(&wic_format, wic_pixel_formats[i]))
+                {
+                    source = (IWICBitmapSource*)frame;
+                    IWICBitmapSource_AddRef(source);
+                    gdip_format = wic_gdip_formats[i];
+                    break;
+                }
+            }
+            if (!source)
+            {
+                /* unknown format; fall back on 32bppARGB */
+                hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, (IWICBitmapSource*)frame, &source);
+                gdip_format = PixelFormat32bppARGB;
+            }
+        }
+
+        if (SUCCEEDED(hr)) /* got source */
+        {
+            hr = IWICBitmapSource_GetSize(source, &width, &height);
+
+            if (SUCCEEDED(hr))
+                status = GdipCreateBitmapFromScan0(width, height, 0, gdip_format,
+                    NULL, &bitmap);
+
+            if (SUCCEEDED(hr) && status == Ok) /* created bitmap */
+            {
+                status = GdipBitmapLockBits(bitmap, NULL, ImageLockModeWrite,
+                    gdip_format, &lockeddata);
+                if (status == Ok) /* locked bitmap */
+                {
+                    wrc.X = 0;
+                    wrc.Width = width;
+                    wrc.Height = 1;
+                    for (i=0; i<height; i++)
+                    {
+                        wrc.Y = i;
+                        hr = IWICBitmapSource_CopyPixels(source, &wrc, abs(lockeddata.Stride),
+                            abs(lockeddata.Stride), (BYTE*)lockeddata.Scan0+lockeddata.Stride*i);
+                        if (FAILED(hr)) break;
+                    }
+
+                    GdipBitmapUnlockBits(bitmap, &lockeddata);
+                }
+
+                if (SUCCEEDED(hr) && status == Ok)
+                    *image = (GpImage*)bitmap;
+                else
+                {
+                    *image = NULL;
+                    GdipDisposeImage((GpImage*)bitmap);
+                }
+            }
+
+            IWICBitmapSource_Release(source);
+        }
+
+        IWICBitmapFrameDecode_Release(frame);
+    }
+
+    IWICBitmapDecoder_Release(decoder);
+
+end:
+    if (SUCCEEDED(initresult)) CoUninitialize();
+
+    if (FAILED(hr) && status == Ok) status = hresult_to_status(hr);
+
+    return status;
+}
+
+static GpStatus decode_image_icon(IStream* stream, REFCLSID clsid, GpImage **image)
+{
+    return decode_image_wic(stream, &CLSID_WICIcoDecoder, image);
+}
+
+static GpStatus decode_image_bmp(IStream* stream, REFCLSID clsid, GpImage **image)
+{
+    GpStatus status;
+    GpBitmap* bitmap;
+
+    status = decode_image_wic(stream, &CLSID_WICBmpDecoder, image);
+
+    bitmap = (GpBitmap*)*image;
+
+    if (status == Ok && bitmap->format == PixelFormat32bppARGB)
+    {
+        /* WIC supports bmp files with alpha, but gdiplus does not */
+        bitmap->format = PixelFormat32bppRGB;
+    }
+
+    return status;
+}
+
+static GpStatus decode_image_jpeg(IStream* stream, REFCLSID clsid, GpImage **image)
+{
+    return decode_image_wic(stream, &CLSID_WICJpegDecoder, image);
+}
+
+static GpStatus decode_image_png(IStream* stream, REFCLSID clsid, GpImage **image)
+{
+    return decode_image_wic(stream, &CLSID_WICPngDecoder, image);
+}
+
+static GpStatus decode_image_gif(IStream* stream, REFCLSID clsid, GpImage **image)
+{
+    return decode_image_wic(stream, &CLSID_WICGifDecoder, image);
+}
+
+static GpStatus decode_image_olepicture_metafile(IStream* stream, REFCLSID clsid, GpImage **image)
 {
     IPicture *pic;
-    short type;
 
     TRACE("%p %p\n", stream, image);
 
@@ -1099,92 +2469,113 @@ GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream* stream, GpImage **image)
         return GenericError;
     }
 
-    IPicture_get_Type(pic, &type);
+    /* FIXME: missing initialization code */
+    *image = GdipAlloc(sizeof(GpMetafile));
+    if(!*image) return OutOfMemory;
+    (*image)->type = ImageTypeMetafile;
+    (*image)->picture = pic;
+    (*image)->flags   = ImageFlagsNone;
+    (*image)->palette_flags = 0;
+    (*image)->palette_count = 0;
+    (*image)->palette_size = 0;
+    (*image)->palette_entries = NULL;
+
+    TRACE("<-- %p\n", *image);
 
-    if(type == PICTYPE_BITMAP){
-        BITMAPINFO *pbmi;
-        BITMAPCOREHEADER* bmch;
-        HBITMAP hbm;
-        HDC hdc;
+    return Ok;
+}
 
-        pbmi = GdipAlloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
-        if (!pbmi)
-            return OutOfMemory;
-        *image = GdipAlloc(sizeof(GpBitmap));
-        if(!*image){
-            GdipFree(pbmi);
-            return OutOfMemory;
-        }
-        (*image)->type = ImageTypeBitmap;
+typedef GpStatus (*encode_image_func)(GpImage *image, IStream* stream,
+    GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params);
 
-        (*((GpBitmap**) image))->width = ipicture_pixel_width(pic);
-        (*((GpBitmap**) image))->height = ipicture_pixel_height(pic);
+typedef GpStatus (*decode_image_func)(IStream *stream, REFCLSID clsid, GpImage** image);
 
-        /* get the pixel format */
-        IPicture_get_Handle(pic, (OLE_HANDLE*)&hbm);
-        IPicture_get_CurDC(pic, &hdc);
+typedef struct image_codec {
+    ImageCodecInfo info;
+    encode_image_func encode_func;
+    decode_image_func decode_func;
+} image_codec;
 
-        bmch = (BITMAPCOREHEADER*) (&pbmi->bmiHeader);
-        bmch->bcSize = sizeof(BITMAPCOREHEADER);
+typedef enum {
+    BMP,
+    JPEG,
+    GIF,
+    EMF,
+    WMF,
+    PNG,
+    ICO,
+    NUM_CODECS
+} ImageFormat;
 
-        if(!hdc){
-            HBITMAP old;
-            hdc = CreateCompatibleDC(0);
-            old = SelectObject(hdc, hbm);
-            GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);
-            SelectObject(hdc, old);
-            DeleteDC(hdc);
-        }
-        else
-            GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);
+static const struct image_codec codecs[NUM_CODECS];
 
-        switch(bmch->bcBitCount)
+static GpStatus get_decoder_info(IStream* stream, const struct image_codec **result)
+{
+    BYTE signature[8];
+    LARGE_INTEGER seek;
+    HRESULT hr;
+    UINT bytesread;
+    int i, j;
+
+    /* seek to the start of the stream */
+    seek.QuadPart = 0;
+    hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
+    if (FAILED(hr)) return hresult_to_status(hr);
+
+    /* read the first 8 bytes */
+    /* FIXME: This assumes all codecs have one signature <= 8 bytes in length */
+    hr = IStream_Read(stream, signature, 8, &bytesread);
+    if (FAILED(hr)) return hresult_to_status(hr);
+    if (hr == S_FALSE || bytesread == 0) return GenericError;
+
+    for (i = 0; i < NUM_CODECS; i++) {
+        if ((codecs[i].info.Flags & ImageCodecFlagsDecoder) &&
+            bytesread >= codecs[i].info.SigSize)
         {
-            case 1:
-                (*((GpBitmap**) image))->format = PixelFormat1bppIndexed;
-                break;
-            case 4:
-                (*((GpBitmap**) image))->format = PixelFormat4bppIndexed;
-                break;
-            case 8:
-                (*((GpBitmap**) image))->format = PixelFormat8bppIndexed;
-                break;
-            case 16:
-                (*((GpBitmap**) image))->format = PixelFormat16bppRGB565;
-                break;
-            case 24:
-                (*((GpBitmap**) image))->format = PixelFormat24bppRGB;
-                break;
-            case 32:
-                (*((GpBitmap**) image))->format = PixelFormat32bppRGB;
-                break;
-            case 48:
-                (*((GpBitmap**) image))->format = PixelFormat48bppRGB;
-                break;
-            default:
-                FIXME("Bit depth %d is not fully supported yet\n", bmch->bcBitCount);
-                (*((GpBitmap**) image))->format = (bmch->bcBitCount << 8) | PixelFormatGDI;
-                break;
+            for (j=0; j<codecs[i].info.SigSize; j++)
+                if ((signature[j] & codecs[i].info.SigMask[j]) != codecs[i].info.SigPattern[j])
+                    break;
+            if (j == codecs[i].info.SigSize)
+            {
+                *result = &codecs[i];
+                return Ok;
+            }
         }
-
-        GdipFree(pbmi);
-    }
-    else if(type == PICTYPE_METAFILE || type == PICTYPE_ENHMETAFILE){
-        /* FIXME: missing initialization code */
-        *image = GdipAlloc(sizeof(GpMetafile));
-        if(!*image) return OutOfMemory;
-        (*image)->type = ImageTypeMetafile;
-    }
-    else{
-        *image = GdipAlloc(sizeof(GpImage));
-        if(!*image) return OutOfMemory;
-        (*image)->type = ImageTypeUnknown;
     }
 
-    (*image)->picture = pic;
-    (*image)->flags   = ImageFlagsNone;
+    TRACE("no match for %i byte signature %x %x %x %x %x %x %x %x\n", bytesread,
+        signature[0],signature[1],signature[2],signature[3],
+        signature[4],signature[5],signature[6],signature[7]);
 
-    return Ok;
+    return GenericError;
+}
+
+GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream* stream, GpImage **image)
+{
+    GpStatus stat;
+    LARGE_INTEGER seek;
+    HRESULT hr;
+    const struct image_codec *codec=NULL;
+
+    /* choose an appropriate image decoder */
+    stat = get_decoder_info(stream, &codec);
+    if (stat != Ok) return stat;
+
+    /* seek to the start of the stream */
+    seek.QuadPart = 0;
+    hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
+    if (FAILED(hr)) return hresult_to_status(hr);
+
+    /* call on the image decoder to do the real work */
+    stat = codec->decode_func(stream, &codec->info.Clsid, image);
+
+    /* take note of the original data format */
+    if (stat == Ok)
+    {
+        memcpy(&(*image)->format, &codec->info.FormatID, sizeof(GUID));
+    }
+
+    return stat;
 }
 
 /* FIXME: no ICM */
@@ -1199,6 +2590,8 @@ GpStatus WINGDIPAPI GdipRemovePropertyItem(GpImage *image, PROPID propId)
 {
     static int calls;
 
+    TRACE("(%p,%u)\n", image, propId);
+
     if(!image)
         return InvalidParameter;
 
@@ -1212,6 +2605,8 @@ GpStatus WINGDIPAPI GdipSetPropertyItem(GpImage *image, GDIPCONST PropertyItem*
 {
     static int calls;
 
+    TRACE("(%p,%p)\n", image, item);
+
     if(!(calls++))
         FIXME("not implemented\n");
 
@@ -1230,9 +2625,6 @@ GpStatus WINGDIPAPI GdipSaveImageToFile(GpImage *image, GDIPCONST WCHAR* filenam
     if (!image || !filename|| !clsidEncoder)
         return InvalidParameter;
 
-    if (!(image->picture))
-        return InvalidParameter;
-
     stat = GdipCreateStreamOnFile(filename, GENERIC_WRITE, &stream);
     if (stat != Ok)
         return GenericError;
@@ -1253,62 +2645,145 @@ GpStatus WINGDIPAPI GdipSaveImageToFile(GpImage *image, GDIPCONST WCHAR* filenam
 #define BITMAP_FORMAT_PNG   0x5089
 #define BITMAP_FORMAT_APM   0xcdd7
 
-static GpStatus encode_image_BMP(LPVOID bitmap_bits, LPBITMAPINFO bitmap_info,
-                                 void **output, unsigned int *output_size)
+static GpStatus encode_image_WIC(GpImage *image, IStream* stream,
+    GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
 {
-    int num_palette_entries;
-    BITMAPFILEHEADER *bmp_file_hdr;
-    BITMAPINFO *bmp_info_hdr;
+    GpStatus stat;
+    GpBitmap *bitmap;
+    IWICBitmapEncoder *encoder;
+    IWICBitmapFrameEncode *frameencode;
+    IPropertyBag2 *encoderoptions;
+    HRESULT hr;
+    UINT width, height;
+    PixelFormat gdipformat=0;
+    WICPixelFormatGUID wicformat;
+    GpRect rc;
+    BitmapData lockeddata;
+    HRESULT initresult;
+    UINT i;
+
+    if (image->type != ImageTypeBitmap)
+        return GenericError;
 
-    if (bitmap_info->bmiHeader.biClrUsed) {
-        num_palette_entries = bitmap_info->bmiHeader.biClrUsed;
-        if (num_palette_entries > 256) num_palette_entries = 256;
-    } else {
-        if (bitmap_info->bmiHeader.biBitCount <= 8)
-            num_palette_entries = 1 << bitmap_info->bmiHeader.biBitCount;
-        else
-            num_palette_entries = 0;
+    bitmap = (GpBitmap*)image;
+
+    GdipGetImageWidth(image, &width);
+    GdipGetImageHeight(image, &height);
+
+    rc.X = 0;
+    rc.Y = 0;
+    rc.Width = width;
+    rc.Height = height;
+
+    initresult = CoInitialize(NULL);
+
+    hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER,
+        &IID_IWICBitmapEncoder, (void**)&encoder);
+    if (FAILED(hr))
+    {
+        if (SUCCEEDED(initresult)) CoUninitialize();
+        return hresult_to_status(hr);
     }
 
-    *output_size =
-        sizeof(BITMAPFILEHEADER) +
-        sizeof(BITMAPINFOHEADER) +
-        num_palette_entries * sizeof(RGBQUAD) +
-        bitmap_info->bmiHeader.biSizeImage;
+    hr = IWICBitmapEncoder_Initialize(encoder, stream, WICBitmapEncoderNoCache);
 
-    *output = GdipAlloc(*output_size);
+    if (SUCCEEDED(hr))
+    {
+        hr = IWICBitmapEncoder_CreateNewFrame(encoder, &frameencode, &encoderoptions);
+    }
 
-    bmp_file_hdr = *output;
-    bmp_file_hdr->bfType = BITMAP_FORMAT_BMP;
-    bmp_file_hdr->bfSize = *output_size;
-    bmp_file_hdr->bfOffBits =
-        sizeof(BITMAPFILEHEADER) +
-        sizeof(BITMAPINFOHEADER) +
-        num_palette_entries * sizeof (RGBQUAD);
+    if (SUCCEEDED(hr)) /* created frame */
+    {
+        hr = IWICBitmapFrameEncode_Initialize(frameencode, encoderoptions);
 
-    bmp_info_hdr = (BITMAPINFO*) ((unsigned char*)(*output) + sizeof(BITMAPFILEHEADER));
-    memcpy(bmp_info_hdr, bitmap_info, sizeof(BITMAPINFOHEADER) + num_palette_entries * sizeof(RGBQUAD));
-    memcpy((unsigned char *)(*output) +
-           sizeof(BITMAPFILEHEADER) +
-           sizeof(BITMAPINFOHEADER) +
-           num_palette_entries * sizeof(RGBQUAD),
-           bitmap_bits, bitmap_info->bmiHeader.biSizeImage);
+        if (SUCCEEDED(hr))
+            hr = IWICBitmapFrameEncode_SetSize(frameencode, width, height);
 
-    return Ok;
-}
+        if (SUCCEEDED(hr))
+            /* FIXME: use the resolution from the image */
+            hr = IWICBitmapFrameEncode_SetResolution(frameencode, 96.0, 96.0);
 
-typedef GpStatus encode_image_func(LPVOID bitmap_bits, LPBITMAPINFO bitmap_info,
-                                   void **output, unsigned int *output_size);
+        if (SUCCEEDED(hr))
+        {
+            for (i=0; wic_pixel_formats[i]; i++)
+            {
+                if (wic_gdip_formats[i] == bitmap->format)
+                    break;
+            }
+            if (wic_pixel_formats[i])
+                memcpy(&wicformat, wic_pixel_formats[i], sizeof(GUID));
+            else
+                memcpy(&wicformat, &GUID_WICPixelFormat32bppBGRA, sizeof(GUID));
+
+            hr = IWICBitmapFrameEncode_SetPixelFormat(frameencode, &wicformat);
+
+            for (i=0; wic_pixel_formats[i]; i++)
+            {
+                if (IsEqualGUID(&wicformat, wic_pixel_formats[i]))
+                    break;
+            }
+            if (wic_pixel_formats[i])
+                gdipformat = wic_gdip_formats[i];
+            else
+            {
+                ERR("cannot provide pixel format %s\n", debugstr_guid(&wicformat));
+                hr = E_FAIL;
+            }
+        }
 
-typedef enum {
-    BMP,
-    NUM_ENCODERS_SUPPORTED
-} ImageFormat;
+        if (SUCCEEDED(hr))
+        {
+            stat = GdipBitmapLockBits(bitmap, &rc, ImageLockModeRead, gdipformat,
+                &lockeddata);
+
+            if (stat == Ok)
+            {
+                UINT row_size = (lockeddata.Width * PIXELFORMATBPP(gdipformat) + 7)/8;
+                BYTE *row;
+
+                /* write one row at a time in case stride is negative */
+                row = lockeddata.Scan0;
+                for (i=0; i<lockeddata.Height; i++)
+                {
+                    hr = IWICBitmapFrameEncode_WritePixels(frameencode, 1, row_size, row_size, row);
+                    if (FAILED(hr)) break;
+                    row += lockeddata.Stride;
+                }
+
+                GdipBitmapUnlockBits(bitmap, &lockeddata);
+            }
+            else
+                hr = E_FAIL;
+        }
 
-static const ImageCodecInfo codecs[NUM_ENCODERS_SUPPORTED];
-static encode_image_func *const encode_image_funcs[NUM_ENCODERS_SUPPORTED] = {
-    encode_image_BMP,
-};
+        if (SUCCEEDED(hr))
+            hr = IWICBitmapFrameEncode_Commit(frameencode);
+
+        IWICBitmapFrameEncode_Release(frameencode);
+        IPropertyBag2_Release(encoderoptions);
+    }
+
+    if (SUCCEEDED(hr))
+        hr = IWICBitmapEncoder_Commit(encoder);
+
+    IWICBitmapEncoder_Release(encoder);
+
+    if (SUCCEEDED(initresult)) CoUninitialize();
+
+    return hresult_to_status(hr);
+}
+
+static GpStatus encode_image_BMP(GpImage *image, IStream* stream,
+    GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
+{
+    return encode_image_WIC(image, stream, &CLSID_WICBmpEncoder, params);
+}
+
+static GpStatus encode_image_png(GpImage *image, IStream* stream,
+    GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
+{
+    return encode_image_WIC(image, stream, &CLSID_WICPngEncoder, params);
+}
 
 /*****************************************************************************
  * GdipSaveImageToStream [GDIPLUS.@]
@@ -1317,84 +2792,50 @@ GpStatus WINGDIPAPI GdipSaveImageToStream(GpImage *image, IStream* stream,
     GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
 {
     GpStatus stat;
-    HRESULT hr;
-    short type;
-    HBITMAP hbmp;
-    HBITMAP old_hbmp;
-    HDC hdc;
-    int bm_is_selected;
-    BITMAPINFO bmp_info;
-    LPVOID bmp_bits;
-    encode_image_func* encode_image;
-    LPVOID output;
-    unsigned int output_size;
-    unsigned int dummy;
+    encode_image_func encode_image;
     int i;
 
-    old_hbmp = 0;
-    output = NULL;
-    output_size = 0;
-
     TRACE("%p %p %p %p\n", image, stream, clsid, params);
 
     if(!image || !stream)
         return InvalidParameter;
 
-    if (!image->picture)
-        return GenericError;
-
-    hr = IPicture_get_Type(image->picture, &type);
-    if (FAILED(hr) || type != PICTYPE_BITMAP)
-        return GenericError;
-
     /* select correct encoder */
     encode_image = NULL;
-    for (i = 0; i < NUM_ENCODERS_SUPPORTED; i++) {
-        if (IsEqualCLSID(clsid, &codecs[i].Clsid))
-            encode_image = encode_image_funcs[i];
+    for (i = 0; i < NUM_CODECS; i++) {
+        if ((codecs[i].info.Flags & ImageCodecFlagsEncoder) &&
+            IsEqualCLSID(clsid, &codecs[i].info.Clsid))
+            encode_image = codecs[i].encode_func;
     }
     if (encode_image == NULL)
         return UnknownImageFormat;
 
-    /* extract underlying hbitmap representation from the IPicture */
-    hr = IPicture_get_Handle(image->picture, (OLE_HANDLE*)&hbmp);
-    if (FAILED(hr) || !hbmp)
-        return GenericError;
-    hr = IPicture_get_CurDC(image->picture, &hdc);
-    if (FAILED(hr))
-        return GenericError;
-    bm_is_selected = (hdc != 0);
-    if (!bm_is_selected) {
-        hdc = CreateCompatibleDC(0);
-        old_hbmp = SelectObject(hdc, hbmp);
-    }
+    stat = encode_image(image, stream, clsid, params);
 
-    /* get bits from HBITMAP */
-    bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);
-    bmp_info.bmiHeader.biBitCount = 0;
-    GetDIBits(hdc, hbmp, 0, 0, NULL, &bmp_info, DIB_RGB_COLORS);
+    return stat;
+}
 
-    bmp_bits = GdipAlloc(bmp_info.bmiHeader.biSizeImage);
+/*****************************************************************************
+ * GdipGetImagePalette [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipGetImagePalette(GpImage *image, ColorPalette *palette, INT size)
+{
+    TRACE("(%p,%p,%i)\n", image, palette, size);
 
-    if (bmp_bits)
-        GetDIBits(hdc, hbmp, 0, abs(bmp_info.bmiHeader.biHeight), bmp_bits, &bmp_info, DIB_RGB_COLORS);
+    if (!image || !palette)
+        return InvalidParameter;
 
-    if (!bm_is_selected) {
-        SelectObject(hdc, old_hbmp);
-        DeleteDC(hdc);
+    if (size < (sizeof(UINT)*2+sizeof(ARGB)*image->palette_count))
+    {
+        TRACE("<-- InsufficientBuffer\n");
+        return InsufficientBuffer;
     }
 
-    if (!bmp_bits)
-        return OutOfMemory;
-
-    stat = encode_image(bmp_bits, &bmp_info, &output, &output_size);
-    if (stat == Ok)
-        IStream_Write(stream, output, output_size, &dummy);
-
-    GdipFree(output);
-    GdipFree(bmp_bits);
+    palette->Flags = image->palette_flags;
+    palette->Count = image->palette_count;
+    memcpy(palette->Entries, image->palette_entries, sizeof(ARGB)*image->palette_count);
 
-    return stat;
+    return Ok;
 }
 
 /*****************************************************************************
@@ -1403,15 +2844,28 @@ GpStatus WINGDIPAPI GdipSaveImageToStream(GpImage *image, IStream* stream,
 GpStatus WINGDIPAPI GdipSetImagePalette(GpImage *image,
     GDIPCONST ColorPalette *palette)
 {
-    static int calls;
+    TRACE("(%p,%p)\n", image, palette);
 
-    if(!image || !palette)
+    if(!image || !palette || palette->Count > 256)
         return InvalidParameter;
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+    if (palette->Count > image->palette_size)
+    {
+        ARGB *new_palette;
 
-    return NotImplemented;
+        new_palette = GdipAlloc(sizeof(ARGB) * palette->Count);
+        if (!new_palette) return OutOfMemory;
+
+        GdipFree(image->palette_entries);
+        image->palette_entries = new_palette;
+        image->palette_size = palette->Count;
+    }
+
+    image->palette_flags = palette->Flags;
+    image->palette_count = palette->Count;
+    memcpy(image->palette_entries, palette->Entries, sizeof(ARGB)*palette->Count);
+
+    return Ok;
 }
 
 /*************************************************************************
@@ -1427,7 +2881,49 @@ static const WCHAR bmp_format[] = {'B', 'M', 'P', 0}; /* BMP */
 static const BYTE bmp_sig_pattern[] = { 0x42, 0x4D };
 static const BYTE bmp_sig_mask[] = { 0xFF, 0xFF };
 
-static const ImageCodecInfo codecs[NUM_ENCODERS_SUPPORTED] =
+static const WCHAR jpeg_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'J','P','E','G', 0};
+static const WCHAR jpeg_extension[] = {'*','.','J','P','G',';', '*','.','J','P','E','G',';', '*','.','J','P','E',';', '*','.','J','F','I','F',0};
+static const WCHAR jpeg_mimetype[] = {'i','m','a','g','e','/','j','p','e','g', 0};
+static const WCHAR jpeg_format[] = {'J','P','E','G',0};
+static const BYTE jpeg_sig_pattern[] = { 0xFF, 0xD8 };
+static const BYTE jpeg_sig_mask[] = { 0xFF, 0xFF };
+
+static const WCHAR gif_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'G','I','F', 0};
+static const WCHAR gif_extension[] = {'*','.','G','I','F',0};
+static const WCHAR gif_mimetype[] = {'i','m','a','g','e','/','g','i','f', 0};
+static const WCHAR gif_format[] = {'G','I','F',0};
+static const BYTE gif_sig_pattern[4] = "GIF8";
+static const BYTE gif_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF };
+
+static const WCHAR emf_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'E','M','F', 0};
+static const WCHAR emf_extension[] = {'*','.','E','M','F',0};
+static const WCHAR emf_mimetype[] = {'i','m','a','g','e','/','x','-','e','m','f', 0};
+static const WCHAR emf_format[] = {'E','M','F',0};
+static const BYTE emf_sig_pattern[] = { 0x01, 0x00, 0x00, 0x00 };
+static const BYTE emf_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF };
+
+static const WCHAR wmf_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'W','M','F', 0};
+static const WCHAR wmf_extension[] = {'*','.','W','M','F',0};
+static const WCHAR wmf_mimetype[] = {'i','m','a','g','e','/','x','-','w','m','f', 0};
+static const WCHAR wmf_format[] = {'W','M','F',0};
+static const BYTE wmf_sig_pattern[] = { 0xd7, 0xcd };
+static const BYTE wmf_sig_mask[] = { 0xFF, 0xFF };
+
+static const WCHAR png_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'P','N','G', 0};
+static const WCHAR png_extension[] = {'*','.','P','N','G',0};
+static const WCHAR png_mimetype[] = {'i','m','a','g','e','/','p','n','g', 0};
+static const WCHAR png_format[] = {'P','N','G',0};
+static const BYTE png_sig_pattern[] = { 137, 80, 78, 71, 13, 10, 26, 10, };
+static const BYTE png_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+static const WCHAR ico_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'I','C','O', 0};
+static const WCHAR ico_extension[] = {'*','.','I','C','O',0};
+static const WCHAR ico_mimetype[] = {'i','m','a','g','e','/','x','-','i','c','o','n', 0};
+static const WCHAR ico_format[] = {'I','C','O',0};
+static const BYTE ico_sig_pattern[] = { 0x00, 0x00, 0x01, 0x00 };
+static const BYTE ico_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF };
+
+static const struct image_codec codecs[NUM_CODECS] = {
     {
         { /* BMP */
             /* Clsid */              { 0x557cf400, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
@@ -1444,20 +2940,145 @@ static const ImageCodecInfo codecs[NUM_ENCODERS_SUPPORTED] =
             /* SigPattern */         bmp_sig_pattern,
             /* SigMask */            bmp_sig_mask,
         },
-    };
+        encode_image_BMP,
+        decode_image_bmp
+    },
+    {
+        { /* JPEG */
+            /* Clsid */              { 0x557cf401, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
+            /* FormatID */           { 0xb96b3caeU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
+            /* CodecName */          jpeg_codecname,
+            /* DllName */            NULL,
+            /* FormatDescription */  jpeg_format,
+            /* FilenameExtension */  jpeg_extension,
+            /* MimeType */           jpeg_mimetype,
+            /* Flags */              ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
+            /* Version */            1,
+            /* SigCount */           1,
+            /* SigSize */            2,
+            /* SigPattern */         jpeg_sig_pattern,
+            /* SigMask */            jpeg_sig_mask,
+        },
+        NULL,
+        decode_image_jpeg
+    },
+    {
+        { /* GIF */
+            /* Clsid */              { 0x557cf402, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
+            /* FormatID */           { 0xb96b3cb0U, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
+            /* CodecName */          gif_codecname,
+            /* DllName */            NULL,
+            /* FormatDescription */  gif_format,
+            /* FilenameExtension */  gif_extension,
+            /* MimeType */           gif_mimetype,
+            /* Flags */              ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
+            /* Version */            1,
+            /* SigCount */           1,
+            /* SigSize */            4,
+            /* SigPattern */         gif_sig_pattern,
+            /* SigMask */            gif_sig_mask,
+        },
+        NULL,
+        decode_image_gif
+    },
+    {
+        { /* EMF */
+            /* Clsid */              { 0x557cf403, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
+            /* FormatID */           { 0xb96b3cacU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
+            /* CodecName */          emf_codecname,
+            /* DllName */            NULL,
+            /* FormatDescription */  emf_format,
+            /* FilenameExtension */  emf_extension,
+            /* MimeType */           emf_mimetype,
+            /* Flags */              ImageCodecFlagsDecoder | ImageCodecFlagsSupportVector | ImageCodecFlagsBuiltin,
+            /* Version */            1,
+            /* SigCount */           1,
+            /* SigSize */            4,
+            /* SigPattern */         emf_sig_pattern,
+            /* SigMask */            emf_sig_mask,
+        },
+        NULL,
+        decode_image_olepicture_metafile
+    },
+    {
+        { /* WMF */
+            /* Clsid */              { 0x557cf404, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
+            /* FormatID */           { 0xb96b3cadU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
+            /* CodecName */          wmf_codecname,
+            /* DllName */            NULL,
+            /* FormatDescription */  wmf_format,
+            /* FilenameExtension */  wmf_extension,
+            /* MimeType */           wmf_mimetype,
+            /* Flags */              ImageCodecFlagsDecoder | ImageCodecFlagsSupportVector | ImageCodecFlagsBuiltin,
+            /* Version */            1,
+            /* SigCount */           1,
+            /* SigSize */            2,
+            /* SigPattern */         wmf_sig_pattern,
+            /* SigMask */            wmf_sig_mask,
+        },
+        NULL,
+        decode_image_olepicture_metafile
+    },
+    {
+        { /* PNG */
+            /* Clsid */              { 0x557cf406, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
+            /* FormatID */           { 0xb96b3cafU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
+            /* CodecName */          png_codecname,
+            /* DllName */            NULL,
+            /* FormatDescription */  png_format,
+            /* FilenameExtension */  png_extension,
+            /* MimeType */           png_mimetype,
+            /* Flags */              ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
+            /* Version */            1,
+            /* SigCount */           1,
+            /* SigSize */            8,
+            /* SigPattern */         png_sig_pattern,
+            /* SigMask */            png_sig_mask,
+        },
+        encode_image_png,
+        decode_image_png
+    },
+    {
+        { /* ICO */
+            /* Clsid */              { 0x557cf407, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
+            /* FormatID */           { 0xb96b3cabU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
+            /* CodecName */          ico_codecname,
+            /* DllName */            NULL,
+            /* FormatDescription */  ico_format,
+            /* FilenameExtension */  ico_extension,
+            /* MimeType */           ico_mimetype,
+            /* Flags */              ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
+            /* Version */            1,
+            /* SigCount */           1,
+            /* SigSize */            4,
+            /* SigPattern */         ico_sig_pattern,
+            /* SigMask */            ico_sig_mask,
+        },
+        NULL,
+        decode_image_icon
+    },
+};
 
 /*****************************************************************************
  * GdipGetImageDecodersSize [GDIPLUS.@]
  */
 GpStatus WINGDIPAPI GdipGetImageDecodersSize(UINT *numDecoders, UINT *size)
 {
-    FIXME("%p %p stub!\n", numDecoders, size);
+    int decoder_count=0;
+    int i;
+    TRACE("%p %p\n", numDecoders, size);
 
     if (!numDecoders || !size)
         return InvalidParameter;
 
-    *numDecoders = 0;
-    *size = 0;
+    for (i=0; i<NUM_CODECS; i++)
+    {
+        if (codecs[i].info.Flags & ImageCodecFlagsDecoder)
+            decoder_count++;
+    }
+
+    *numDecoders = decoder_count;
+    *size = decoder_count * sizeof(ImageCodecInfo);
 
     return Ok;
 }
@@ -1467,12 +3088,26 @@ GpStatus WINGDIPAPI GdipGetImageDecodersSize(UINT *numDecoders, UINT *size)
  */
 GpStatus WINGDIPAPI GdipGetImageDecoders(UINT numDecoders, UINT size, ImageCodecInfo *decoders)
 {
-    FIXME("%u %u %p stub!\n", numDecoders, size, decoders);
+    int i, decoder_count=0;
+    TRACE("%u %u %p\n", numDecoders, size, decoders);
 
-    if (!decoders)
+    if (!decoders ||
+        size != numDecoders * sizeof(ImageCodecInfo))
         return GenericError;
 
-    return NotImplemented;
+    for (i=0; i<NUM_CODECS; i++)
+    {
+        if (codecs[i].info.Flags & ImageCodecFlagsDecoder)
+        {
+            if (decoder_count == numDecoders) return GenericError;
+            memcpy(&decoders[decoder_count], &codecs[i].info, sizeof(ImageCodecInfo));
+            decoder_count++;
+        }
+    }
+
+    if (decoder_count < numDecoders) return GenericError;
+
+    return Ok;
 }
 
 /*****************************************************************************
@@ -1480,13 +3115,21 @@ GpStatus WINGDIPAPI GdipGetImageDecoders(UINT numDecoders, UINT size, ImageCodec
  */
 GpStatus WINGDIPAPI GdipGetImageEncodersSize(UINT *numEncoders, UINT *size)
 {
+    int encoder_count=0;
+    int i;
     TRACE("%p %p\n", numEncoders, size);
 
     if (!numEncoders || !size)
         return InvalidParameter;
 
-    *numEncoders = NUM_ENCODERS_SUPPORTED;
-    *size = sizeof (codecs);
+    for (i=0; i<NUM_CODECS; i++)
+    {
+        if (codecs[i].info.Flags & ImageCodecFlagsEncoder)
+            encoder_count++;
+    }
+
+    *numEncoders = encoder_count;
+    *size = encoder_count * sizeof(ImageCodecInfo);
 
     return Ok;
 }
@@ -1496,14 +3139,24 @@ GpStatus WINGDIPAPI GdipGetImageEncodersSize(UINT *numEncoders, UINT *size)
  */
 GpStatus WINGDIPAPI GdipGetImageEncoders(UINT numEncoders, UINT size, ImageCodecInfo *encoders)
 {
+    int i, encoder_count=0;
     TRACE("%u %u %p\n", numEncoders, size, encoders);
 
     if (!encoders ||
-        (numEncoders != NUM_ENCODERS_SUPPORTED) ||
-        (size != sizeof (codecs)))
+        size != numEncoders * sizeof(ImageCodecInfo))
         return GenericError;
 
-    memcpy(encoders, codecs, sizeof (codecs));
+    for (i=0; i<NUM_CODECS; i++)
+    {
+        if (codecs[i].info.Flags & ImageCodecFlagsEncoder)
+        {
+            if (encoder_count == numEncoders) return GenericError;
+            memcpy(&encoders[encoder_count], &codecs[i].info, sizeof(ImageCodecInfo));
+            encoder_count++;
+        }
+    }
+
+    if (encoder_count < numEncoders) return GenericError;
 
     return Ok;
 }
@@ -1516,7 +3169,8 @@ GpStatus WINGDIPAPI GdipCreateBitmapFromHBITMAP(HBITMAP hbm, HPALETTE hpal, GpBi
     BITMAP bm;
     GpStatus retval;
     PixelFormat format;
-    BYTE* bits;
+    BitmapData lockeddata;
+    INT y;
 
     TRACE("%p %p %p\n", hbm, hpal, bitmap);
 
@@ -1557,20 +3211,87 @@ GpStatus WINGDIPAPI GdipCreateBitmapFromHBITMAP(HBITMAP hbm, HPALETTE hpal, GpBi
             return InvalidParameter;
     }
 
-    if (bm.bmBits)
-        bits = (BYTE*)bm.bmBits + (bm.bmHeight - 1) * bm.bmWidthBytes;
-    else
+    retval = GdipCreateBitmapFromScan0(bm.bmWidth, bm.bmHeight, 0,
+        format, NULL, bitmap);
+
+    if (retval == Ok)
     {
-        FIXME("can only get image data from DIB sections\n");
-        bits = NULL;
+        retval = GdipBitmapLockBits(*bitmap, NULL, ImageLockModeWrite,
+            format, &lockeddata);
+        if (retval == Ok)
+        {
+            if (bm.bmBits)
+            {
+                for (y=0; y<bm.bmHeight; y++)
+                {
+                    memcpy((BYTE*)lockeddata.Scan0+lockeddata.Stride*y,
+                           (BYTE*)bm.bmBits+bm.bmWidthBytes*(bm.bmHeight-1-y),
+                           bm.bmWidthBytes);
+                }
+            }
+            else
+            {
+                HDC hdc;
+                HBITMAP oldhbm;
+                BITMAPINFO *pbmi;
+                INT src_height, dst_stride;
+                BYTE *dst_bits;
+
+                hdc = CreateCompatibleDC(NULL);
+                oldhbm = SelectObject(hdc, hbm);
+
+                pbmi = GdipAlloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
+
+                if (pbmi)
+                {
+                    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+                    pbmi->bmiHeader.biBitCount = 0;
+
+                    GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);
+
+                    src_height = abs(pbmi->bmiHeader.biHeight);
+
+                    if (pbmi->bmiHeader.biHeight > 0)
+                    {
+                        dst_bits = (BYTE*)lockeddata.Scan0+lockeddata.Stride*(src_height-1);
+                        dst_stride = -lockeddata.Stride;
+                    }
+                    else
+                    {
+                        dst_bits = lockeddata.Scan0;
+                        dst_stride = lockeddata.Stride;
+                    }
+
+                    for (y=0; y<src_height; y++)
+                    {
+                        GetDIBits(hdc, hbm, y, 1, dst_bits+dst_stride*y,
+                            pbmi, DIB_RGB_COLORS);
+                    }
+
+                    GdipFree(pbmi);
+                }
+                else
+                    retval = OutOfMemory;
+
+                SelectObject(hdc, oldhbm);
+                DeleteDC(hdc);
+            }
+
+            GdipBitmapUnlockBits(*bitmap, &lockeddata);
+        }
     }
 
-    retval = GdipCreateBitmapFromScan0(bm.bmWidth, bm.bmHeight, -bm.bmWidthBytes,
-        format, bits, bitmap);
-
     return retval;
 }
 
+GpStatus WINGDIPAPI GdipDeleteEffect(CGpEffect *effect)
+{
+    FIXME("(%p): stub\n", effect);
+    /* note: According to Jose Roca's GDI+ Docs, this is not implemented
+     * in Windows's gdiplus */
+    return NotImplemented;
+}
+
 /*****************************************************************************
  * GdipSetEffectParameters [GDIPLUS.@]
  */
@@ -1579,6 +3300,8 @@ GpStatus WINGDIPAPI GdipSetEffectParameters(CGpEffect *effect,
 {
     static int calls;
 
+    TRACE("(%p,%p,%u)\n", effect, params, size);
+
     if(!(calls++))
         FIXME("not implemented\n");
 
@@ -1648,3 +3371,24 @@ GpStatus WINGDIPAPI GdipImageForceValidation(GpImage *image)
 
     return Ok;
 }
+
+/*****************************************************************************
+ * GdipGetImageThumbnail [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipGetImageThumbnail(GpImage *image, UINT width, UINT height,
+                            GpImage **ret_image, GetThumbnailImageAbort cb,
+                            VOID * cb_data)
+{
+    FIXME("(%p %u %u %p %p %p) stub\n",
+        image, width, height, ret_image, cb, cb_data);
+    return NotImplemented;
+}
+
+/*****************************************************************************
+ * GdipImageRotateFlip [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type)
+{
+    FIXME("(%p %u) stub\n", image, type);
+    return NotImplemented;
+}