[GDIPLUS]
authorAmine Khaldi <amine.khaldi@reactos.org>
Tue, 11 Dec 2012 21:40:15 +0000 (21:40 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Tue, 11 Dec 2012 21:40:15 +0000 (21:40 +0000)
* Sync with Wine 1.5.19.
[PSDK]
* Update some headers.

svn path=/trunk/; revision=57882

15 files changed:
reactos/dll/win32/gdiplus/brush.c
reactos/dll/win32/gdiplus/font.c
reactos/dll/win32/gdiplus/gdiplus.c
reactos/dll/win32/gdiplus/gdiplus.spec
reactos/dll/win32/gdiplus/gdiplus_private.h
reactos/dll/win32/gdiplus/graphics.c
reactos/dll/win32/gdiplus/graphicspath.c
reactos/dll/win32/gdiplus/image.c
reactos/dll/win32/gdiplus/matrix.c
reactos/dll/win32/gdiplus/metafile.c
reactos/dll/win32/gdiplus/region.c
reactos/dll/win32/gdiplus/stringformat.c
reactos/include/psdk/gdiplusenums.h
reactos/include/psdk/gdiplusimaging.h
reactos/include/psdk/gdipluspixelformats.h

index 7f26a1e..2ada393 100644 (file)
@@ -105,13 +105,7 @@ GpStatus WINGDIPAPI GdipCloneBrush(GpBrush *brush, GpBrush **clone)
                 return stat;
             }
 
-            stat = GdipCloneMatrix(src->transform, &dest->transform);
-
-            if(stat != Ok){
-                GdipDeletePath(dest->path);
-                GdipFree(dest);
-                return stat;
-            }
+            dest->transform = src->transform;
 
             /* blending */
             count = src->blendcount;
@@ -129,7 +123,6 @@ GpStatus WINGDIPAPI GdipCloneBrush(GpBrush *brush, GpBrush **clone)
             if(!dest->blendfac || !dest->blendpos || !dest->surroundcolors ||
                (pcount && (!dest->pblendcolor || !dest->pblendpos))){
                 GdipDeletePath(dest->path);
-                GdipDeleteMatrix(dest->transform);
                 GdipFree(dest->blendfac);
                 GdipFree(dest->blendpos);
                 GdipFree(dest->surroundcolors);
@@ -211,7 +204,7 @@ GpStatus WINGDIPAPI GdipCloneBrush(GpBrush *brush, GpBrush **clone)
 
             if (stat == Ok)
             {
-                memcpy(new_texture->transform, texture->transform, sizeof(GpMatrix));
+                new_texture->transform = texture->transform;
                 *clone = (GpBrush*)new_texture;
             }
             else
@@ -539,7 +532,6 @@ GpStatus WINGDIPAPI GdipCreateLineBrushFromRectWithAngleI(GDIPCONST GpRect* rect
 static GpStatus create_path_gradient(GpPath *path, ARGB centercolor, GpPathGradient **grad)
 {
     GpRectF bounds;
-    GpStatus stat;
 
     if(!path || !grad)
         return InvalidParameter;
@@ -555,18 +547,12 @@ static GpStatus create_path_gradient(GpPath *path, ARGB centercolor, GpPathGradi
         return OutOfMemory;
     }
 
-    stat = GdipCreateMatrix(&(*grad)->transform);
-    if (stat != Ok)
-    {
-        GdipFree(*grad);
-        return stat;
-    }
+    GdipSetMatrixElements(&(*grad)->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
 
     (*grad)->blendfac = GdipAlloc(sizeof(REAL));
     (*grad)->blendpos = GdipAlloc(sizeof(REAL));
     (*grad)->surroundcolors = GdipAlloc(sizeof(ARGB));
     if(!(*grad)->blendfac || !(*grad)->blendpos || !(*grad)->surroundcolors){
-        GdipDeleteMatrix((*grad)->transform);
         GdipFree((*grad)->blendfac);
         GdipFree((*grad)->blendpos);
         GdipFree((*grad)->surroundcolors);
@@ -819,9 +805,7 @@ GpStatus WINGDIPAPI GdipCreateTextureIA(GpImage *image,
         goto exit;
     }
 
-    if((status = GdipCreateMatrix(&(*texture)->transform)) != Ok){
-        goto exit;
-    }
+    GdipSetMatrixElements(&(*texture)->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
 
     if (imageattr)
     {
@@ -848,7 +832,6 @@ exit:
     {
         if (*texture)
         {
-            GdipDeleteMatrix((*texture)->transform);
             GdipDisposeImageAttributes((*texture)->imageattributes);
             GdipFree(*texture);
             *texture = NULL;
@@ -947,7 +930,6 @@ GpStatus WINGDIPAPI GdipDeleteBrush(GpBrush *brush)
     {
         case BrushTypePathGradient:
             GdipDeletePath(((GpPathGradient*) brush)->path);
-            GdipDeleteMatrix(((GpPathGradient*) brush)->transform);
             GdipFree(((GpPathGradient*) brush)->blendfac);
             GdipFree(((GpPathGradient*) brush)->blendpos);
             GdipFree(((GpPathGradient*) brush)->surroundcolors);
@@ -961,7 +943,6 @@ GpStatus WINGDIPAPI GdipDeleteBrush(GpBrush *brush)
             GdipFree(((GpLineGradient*)brush)->pblendpos);
             break;
         case BrushTypeTextureFill:
-            GdipDeleteMatrix(((GpTexture*)brush)->transform);
             GdipDisposeImage(((GpTexture*)brush)->image);
             GdipDisposeImageAttributes(((GpTexture*)brush)->imageattributes);
             GdipFree(((GpTexture*)brush)->bitmap_bits);
@@ -1059,8 +1040,8 @@ GpStatus WINGDIPAPI GdipGetPathGradientCenterPointI(GpPathGradient *grad,
     ret = GdipGetPathGradientCenterPoint(grad,&ptf);
 
     if(ret == Ok){
-        point->X = roundr(ptf.X);
-        point->Y = roundr(ptf.Y);
+        point->X = gdip_round(ptf.X);
+        point->Y = gdip_round(ptf.Y);
     }
 
     return ret;
@@ -1158,10 +1139,10 @@ GpStatus WINGDIPAPI GdipGetPathGradientRectI(GpPathGradient *brush, GpRect *rect
     stat = GdipGetPathGradientRect(brush, &rectf);
     if(stat != Ok)  return stat;
 
-    rect->X = roundr(rectf.X);
-    rect->Y = roundr(rectf.Y);
-    rect->Width  = roundr(rectf.Width);
-    rect->Height = roundr(rectf.Height);
+    rect->X = gdip_round(rectf.X);
+    rect->Y = gdip_round(rectf.Y);
+    rect->Width  = gdip_round(rectf.Width);
+    rect->Height = gdip_round(rectf.Height);
 
     return Ok;
 }
@@ -1253,7 +1234,7 @@ GpStatus WINGDIPAPI GdipGetTextureTransform(GpTexture *brush, GpMatrix *matrix)
     if(!brush || !matrix)
         return InvalidParameter;
 
-    memcpy(matrix, brush->transform, sizeof(GpMatrix));
+    *matrix = brush->transform;
 
     return Ok;
 }
@@ -1284,7 +1265,7 @@ GpStatus WINGDIPAPI GdipMultiplyTextureTransform(GpTexture* brush,
     if(!brush || !matrix)
         return InvalidParameter;
 
-    return GdipMultiplyMatrix(brush->transform, matrix, order);
+    return GdipMultiplyMatrix(&brush->transform, matrix, order);
 }
 
 /******************************************************************************
@@ -1297,7 +1278,7 @@ GpStatus WINGDIPAPI GdipResetTextureTransform(GpTexture* brush)
     if(!brush)
         return InvalidParameter;
 
-    return GdipSetMatrixElements(brush->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
+    return GdipSetMatrixElements(&brush->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
 }
 
 /******************************************************************************
@@ -1311,7 +1292,7 @@ GpStatus WINGDIPAPI GdipScaleTextureTransform(GpTexture* brush,
     if(!brush)
         return InvalidParameter;
 
-    return GdipScaleMatrix(brush->transform, sx, sy, order);
+    return GdipScaleMatrix(&brush->transform, sx, sy, order);
 }
 
 GpStatus WINGDIPAPI GdipSetLineBlend(GpLineGradient *brush,
@@ -1785,7 +1766,7 @@ GpStatus WINGDIPAPI GdipSetPathGradientTransform(GpPathGradient *grad,
     if (!grad || !matrix)
         return InvalidParameter;
 
-    memcpy(grad->transform, matrix, sizeof(GpMatrix));
+    grad->transform = *matrix;
 
     return Ok;
 }
@@ -1798,7 +1779,7 @@ GpStatus WINGDIPAPI GdipGetPathGradientTransform(GpPathGradient *grad,
     if (!grad || !matrix)
         return InvalidParameter;
 
-    memcpy(matrix, grad->transform, sizeof(GpMatrix));
+    *matrix = grad->transform;
 
     return Ok;
 }
@@ -1811,7 +1792,7 @@ GpStatus WINGDIPAPI GdipMultiplyPathGradientTransform(GpPathGradient *grad,
     if (!grad)
         return InvalidParameter;
 
-    return GdipMultiplyMatrix(grad->transform, matrix, order);
+    return GdipMultiplyMatrix(&grad->transform, matrix, order);
 }
 
 GpStatus WINGDIPAPI GdipResetPathGradientTransform(GpPathGradient *grad)
@@ -1821,7 +1802,7 @@ GpStatus WINGDIPAPI GdipResetPathGradientTransform(GpPathGradient *grad)
     if (!grad)
         return InvalidParameter;
 
-    return GdipSetMatrixElements(grad->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
+    return GdipSetMatrixElements(&grad->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
 }
 
 GpStatus WINGDIPAPI GdipRotatePathGradientTransform(GpPathGradient *grad,
@@ -1832,7 +1813,7 @@ GpStatus WINGDIPAPI GdipRotatePathGradientTransform(GpPathGradient *grad,
     if (!grad)
         return InvalidParameter;
 
-    return GdipRotateMatrix(grad->transform, angle, order);
+    return GdipRotateMatrix(&grad->transform, angle, order);
 }
 
 GpStatus WINGDIPAPI GdipScalePathGradientTransform(GpPathGradient *grad,
@@ -1843,7 +1824,7 @@ GpStatus WINGDIPAPI GdipScalePathGradientTransform(GpPathGradient *grad,
     if (!grad)
         return InvalidParameter;
 
-    return GdipScaleMatrix(grad->transform, sx, sy, order);
+    return GdipScaleMatrix(&grad->transform, sx, sy, order);
 }
 
 GpStatus WINGDIPAPI GdipTranslatePathGradientTransform(GpPathGradient *grad,
@@ -1854,7 +1835,7 @@ GpStatus WINGDIPAPI GdipTranslatePathGradientTransform(GpPathGradient *grad,
     if (!grad)
         return InvalidParameter;
 
-    return GdipTranslateMatrix(grad->transform, dx, dy, order);
+    return GdipTranslateMatrix(&grad->transform, dx, dy, order);
 }
 
 GpStatus WINGDIPAPI GdipSetSolidFillColor(GpSolidFill *sf, ARGB argb)
@@ -1879,7 +1860,7 @@ GpStatus WINGDIPAPI GdipSetTextureTransform(GpTexture *texture,
     if(!texture || !matrix)
         return InvalidParameter;
 
-    memcpy(texture->transform, matrix, sizeof(GpMatrix));
+    texture->transform = *matrix;
 
     return Ok;
 }
@@ -1939,7 +1920,7 @@ GpStatus WINGDIPAPI GdipRotateTextureTransform(GpTexture* brush, REAL angle,
     if(!brush)
         return InvalidParameter;
 
-    return GdipRotateMatrix(brush->transform, angle, order);
+    return GdipRotateMatrix(&brush->transform, angle, order);
 }
 
 GpStatus WINGDIPAPI GdipSetLineLinearBlend(GpLineGradient *brush, REAL focus,
@@ -2125,7 +2106,7 @@ GpStatus WINGDIPAPI GdipTranslateTextureTransform(GpTexture* brush, REAL dx, REA
     if(!brush)
         return InvalidParameter;
 
-    return GdipTranslateMatrix(brush->transform, dx, dy, order);
+    return GdipTranslateMatrix(&brush->transform, dx, dy, order);
 }
 
 GpStatus WINGDIPAPI GdipGetLineRect(GpLineGradient *brush, GpRectF *rect)
@@ -2153,10 +2134,10 @@ GpStatus WINGDIPAPI GdipGetLineRectI(GpLineGradient *brush, GpRect *rect)
     ret = GdipGetLineRect(brush, &rectF);
 
     if(ret == Ok){
-        rect->X      = roundr(rectF.X);
-        rect->Y      = roundr(rectF.Y);
-        rect->Width  = roundr(rectF.Width);
-        rect->Height = roundr(rectF.Height);
+        rect->X      = gdip_round(rectF.X);
+        rect->Y      = gdip_round(rectF.Y);
+        rect->Width  = gdip_round(rectF.Width);
+        rect->Height = gdip_round(rectF.Height);
     }
 
     return ret;
index 4b059a0..6e31331 100644 (file)
@@ -116,41 +116,10 @@ typedef struct
 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
 #define MS_HHEA_TAG MS_MAKE_TAG('h','h','e','a')
 
-static const REAL mm_per_inch = 25.4;
-static const REAL inch_per_point = 1.0/72.0;
+static GpStatus clone_font_family(const GpFontFamily *, GpFontFamily **);
 
 static GpFontCollection installedFontCollection = {0};
 
-static LONG em_size_to_pixel(REAL em_size, Unit unit, LONG dpi)
-{
-    switch (unit)
-    {
-    default:
-        FIXME("Unhandled unit type: %d\n", unit);
-        return 0;
-
-    case UnitPixel:
-    case UnitWorld:
-        /* FIXME: Figure out when World != Pixel */
-        return em_size;
-    case UnitDisplay:
-        FIXME("Unknown behavior for UnitDisplay! Please report!\n");
-        /* FIXME: Figure out how this works...
-         * MSDN says that if "DISPLAY" is a monitor, then pixel should be
-         * used. That's not what I got. Tests on Windows revealed no output,
-         * and the tests in tests/font crash windows */
-        return 0;
-    case UnitPoint:
-        return em_size * dpi * inch_per_point;
-    case UnitInch:
-        return em_size * dpi;
-    case UnitDocument:
-        return em_size * dpi / 300.0; /* Per MSDN */
-    case UnitMillimeter:
-        return em_size * dpi / mm_per_inch;
-    }
-}
-
 /*******************************************************************************
  * GdipCreateFont [GDIPLUS.@]
  *
@@ -193,7 +162,7 @@ GpStatus WINGDIPAPI GdipCreateFont(GDIPCONST GpFontFamily *fontFamily,
     stat = GdipGetFamilyName(fontFamily, lfw.lfFaceName, LANG_NEUTRAL);
     if (stat != Ok) return stat;
 
-    lfw.lfHeight = -em_size_to_pixel(emSize, unit, fontFamily->dpi);
+    lfw.lfHeight = -units_to_pixels(emSize, unit, fontFamily->dpi);
     lfw.lfWeight = style & FontStyleBold ? FW_BOLD : FW_REGULAR;
     lfw.lfItalic = style & FontStyleItalic;
     lfw.lfUnderline = style & FontStyleUnderline;
@@ -216,7 +185,7 @@ GpStatus WINGDIPAPI GdipCreateFont(GDIPCONST GpFontFamily *fontFamily,
     (*font)->emSize = emSize;
     (*font)->otm = otm;
 
-    stat = GdipCloneFontFamily((GpFontFamily *)fontFamily, &(*font)->family);
+    stat = clone_font_family(fontFamily, &(*font)->family);
     if (stat != Ok)
     {
         GdipFree(*font);
@@ -236,6 +205,7 @@ GpStatus WINGDIPAPI GdipCreateFontFromLogfontW(HDC hdc,
 {
     HFONT hfont, oldfont;
     OUTLINETEXTMETRICW otm;
+    WCHAR facename[LF_FACESIZE];
     GpStatus stat;
     int ret;
 
@@ -248,6 +218,7 @@ GpStatus WINGDIPAPI GdipCreateFontFromLogfontW(HDC hdc,
     oldfont = SelectObject(hdc, hfont);
     otm.otmSize = sizeof(otm);
     ret = GetOutlineTextMetricsW(hdc, otm.otmSize, &otm);
+    GetTextFaceW(hdc, LF_FACESIZE, facename);
     SelectObject(hdc, oldfont);
     DeleteObject(hfont);
 
@@ -260,7 +231,7 @@ GpStatus WINGDIPAPI GdipCreateFontFromLogfontW(HDC hdc,
     (*font)->emSize = otm.otmTextMetrics.tmAscent;
     (*font)->otm = otm;
 
-    stat = GdipCreateFontFamilyFromName(logfont->lfFaceName, NULL, &(*font)->family);
+    stat = GdipCreateFontFamilyFromName(facename, NULL, &(*font)->family);
     if (stat != Ok)
     {
         GdipFree(*font);
@@ -355,6 +326,11 @@ GpStatus WINGDIPAPI GdipGetFamily(GpFont *font, GpFontFamily **family)
     return GdipCloneFontFamily(font->family, family);
 }
 
+static REAL get_font_size(const GpFont *font)
+{
+    return font->emSize;
+}
+
 /******************************************************************************
  * GdipGetFontSize [GDIPLUS.@]
  *
@@ -377,12 +353,30 @@ GpStatus WINGDIPAPI GdipGetFontSize(GpFont *font, REAL *size)
 
     if (!(font && size)) return InvalidParameter;
 
-    *size = font->emSize;
+    *size = get_font_size(font);
     TRACE("%s,%d => %f\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *size);
 
     return Ok;
 }
 
+static INT get_font_style(const GpFont *font)
+{
+    INT style;
+
+    if (font->otm.otmTextMetrics.tmWeight > FW_REGULAR)
+        style = FontStyleBold;
+    else
+        style = FontStyleRegular;
+    if (font->otm.otmTextMetrics.tmItalic)
+        style |= FontStyleItalic;
+    if (font->otm.otmTextMetrics.tmUnderlined)
+        style |= FontStyleUnderline;
+    if (font->otm.otmTextMetrics.tmStruckOut)
+        style |= FontStyleStrikeout;
+
+    return style;
+}
+
 /*******************************************************************************
  * GdipGetFontStyle [GDIPLUS.@]
  *
@@ -403,16 +397,8 @@ GpStatus WINGDIPAPI GdipGetFontStyle(GpFont *font, INT *style)
     if (!(font && style))
         return InvalidParameter;
 
-    if (font->otm.otmTextMetrics.tmWeight > FW_REGULAR)
-        *style = FontStyleBold;
-    else
-        *style = FontStyleRegular;
-    if (font->otm.otmTextMetrics.tmItalic)
-        *style |= FontStyleItalic;
-    if (font->otm.otmTextMetrics.tmUnderlined)
-        *style |= FontStyleUnderline;
-    if (font->otm.otmTextMetrics.tmStruckOut)
-        *style |= FontStyleStrikeout;
+    *style = get_font_style(font);
+    TRACE("%s,%d => %d\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *style);
 
     return Ok;
 }
@@ -466,31 +452,64 @@ GpStatus WINGDIPAPI GdipGetLogFontA(GpFont *font, GpGraphics *graphics,
 /*******************************************************************************
  * GdipGetLogFontW [GDIPLUS.@]
  */
-GpStatus WINGDIPAPI GdipGetLogFontW(GpFont *font, GpGraphics *graphics,
-    LOGFONTW *lfw)
+GpStatus WINGDIPAPI GdipGetLogFontW(GpFont *font, GpGraphics *graphics, LOGFONTW *lf)
 {
-    TRACE("(%p, %p, %p)\n", font, graphics, lfw);
+    REAL angle, rel_height, height;
+    GpMatrix matrix;
+    GpPointF pt[3];
 
-    /* FIXME: use graphics */
-    if(!font || !graphics || !lfw)
+    TRACE("(%p, %p, %p)\n", font, graphics, lf);
+
+    if (!font || !graphics || !lf)
         return InvalidParameter;
 
-    lfw->lfHeight = -font->otm.otmTextMetrics.tmAscent;
-    lfw->lfWidth = 0;
-    lfw->lfEscapement = 0;
-    lfw->lfOrientation = 0;
-    lfw->lfWeight = font->otm.otmTextMetrics.tmWeight;
-    lfw->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0;
-    lfw->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0;
-    lfw->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0;
-    lfw->lfCharSet = font->otm.otmTextMetrics.tmCharSet;
-    lfw->lfOutPrecision = OUT_DEFAULT_PRECIS;
-    lfw->lfClipPrecision = CLIP_DEFAULT_PRECIS;
-    lfw->lfQuality = DEFAULT_QUALITY;
-    lfw->lfPitchAndFamily = 0;
-    strcpyW(lfw->lfFaceName, font->family->FamilyName);
-
-    TRACE("=> %s,%d\n", debugstr_w(lfw->lfFaceName), lfw->lfHeight);
+    matrix = graphics->worldtrans;
+
+    if (font->unit == UnitPixel)
+    {
+        height = units_to_pixels(font->emSize, graphics->unit, graphics->yres);
+        if (graphics->unit != UnitDisplay)
+            GdipScaleMatrix(&matrix, graphics->scale, graphics->scale, MatrixOrderAppend);
+    }
+    else
+    {
+        if (graphics->unit == UnitDisplay || graphics->unit == UnitPixel)
+            height = units_to_pixels(font->emSize, font->unit, graphics->xres);
+        else
+            height = units_to_pixels(font->emSize, font->unit, graphics->yres);
+    }
+
+    pt[0].X = 0.0;
+    pt[0].Y = 0.0;
+    pt[1].X = 1.0;
+    pt[1].Y = 0.0;
+    pt[2].X = 0.0;
+    pt[2].Y = 1.0;
+    GdipTransformMatrixPoints(&matrix, pt, 3);
+    angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
+    rel_height = sqrt((pt[2].Y - pt[0].Y) * (pt[2].Y - pt[0].Y)+
+                      (pt[2].X - pt[0].X) * (pt[2].X - pt[0].X));
+
+    lf->lfHeight = -gdip_round(height * rel_height);
+    lf->lfWidth = 0;
+    lf->lfEscapement = lf->lfOrientation = gdip_round((angle / M_PI) * 1800.0);
+    if (lf->lfEscapement < 0)
+    {
+        lf->lfEscapement += 3600;
+        lf->lfOrientation += 3600;
+    }
+    lf->lfWeight = font->otm.otmTextMetrics.tmWeight;
+    lf->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0;
+    lf->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0;
+    lf->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0;
+    lf->lfCharSet = font->otm.otmTextMetrics.tmCharSet;
+    lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
+    lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
+    lf->lfQuality = DEFAULT_QUALITY;
+    lf->lfPitchAndFamily = 0;
+    strcpyW(lf->lfFaceName, font->family->FamilyName);
+
+    TRACE("=> %s,%d\n", debugstr_w(lf->lfFaceName), lf->lfHeight);
 
     return Ok;
 }
@@ -535,18 +554,29 @@ GpStatus WINGDIPAPI GdipGetFontHeight(GDIPCONST GpFont *font,
 {
     REAL dpi;
     GpStatus stat;
+    REAL font_height;
 
     TRACE("%p %p %p\n", font, graphics, height);
 
-    if (graphics)
+    stat = GdipGetFontHeightGivenDPI(font, font->family->dpi, &font_height);
+    if (stat != Ok) return stat;
+
+    if (!graphics)
     {
-        stat = GdipGetDpiY((GpGraphics*)graphics, &dpi);
-        if (stat != Ok) return stat;
+        *height = font_height;
+        TRACE("%s,%d => %f\n",
+              debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *height);
+        return Ok;
     }
-    else
-        dpi = font->family->dpi;
 
-    return GdipGetFontHeightGivenDPI(font, dpi, height);
+    stat = GdipGetDpiY((GpGraphics *)graphics, &dpi);
+    if (stat != Ok) return stat;
+
+    *height = pixels_to_units(font_height, graphics->unit, dpi);
+
+    TRACE("%s,%d(unit %d) => %f\n",
+          debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, graphics->unit, *height);
+    return Ok;
 }
 
 /*******************************************************************************
@@ -569,49 +599,24 @@ GpStatus WINGDIPAPI GdipGetFontHeightGivenDPI(GDIPCONST GpFont *font, REAL dpi,
     GpStatus stat;
     INT style;
     UINT16 line_spacing, em_height;
-    REAL font_height, font_size;
+    REAL font_size;
 
     if (!font || !height) return InvalidParameter;
 
     TRACE("%p (%s), %f, %p\n", font,
             debugstr_w(font->family->FamilyName), dpi, height);
 
-    stat = GdipGetFontSize((GpFont *)font, &font_size);
-    if (stat != Ok) return stat;
-    stat = GdipGetFontStyle((GpFont *)font, &style);
-    if (stat != Ok) return stat;
+    font_size = units_to_pixels(get_font_size(font), font->unit, dpi);
+    style = get_font_style(font);
     stat = GdipGetLineSpacing(font->family, style, &line_spacing);
     if (stat != Ok) return stat;
     stat = GdipGetEmHeight(font->family, style, &em_height);
     if (stat != Ok) return stat;
 
-    font_height = (REAL)line_spacing * font_size / (REAL)em_height;
+    *height = (REAL)line_spacing * font_size / (REAL)em_height;
 
-    switch (font->unit)
-    {
-        case UnitPixel:
-        case UnitWorld:
-            *height = font_height;
-            break;
-        case UnitPoint:
-            *height = font_height * dpi * inch_per_point;
-            break;
-        case UnitInch:
-            *height = font_height * dpi;
-            break;
-        case UnitDocument:
-            *height = font_height * (dpi / 300.0);
-            break;
-        case UnitMillimeter:
-            *height = font_height * (dpi / mm_per_inch);
-            break;
-        default:
-            FIXME("Unhandled unit type: %d\n", font->unit);
-            return NotImplemented;
-    }
-
-    TRACE("%s,%d(unit %d) => %f\n",
-          debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, font->unit, *height);
+    TRACE("%s,%d => %f\n",
+          debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *height);
 
     return Ok;
 }
@@ -625,7 +630,7 @@ GpStatus WINGDIPAPI GdipGetFontHeightGivenDPI(GDIPCONST GpFont *font, REAL dpi,
 static INT CALLBACK is_font_installed_proc(const LOGFONTW *elf,
                             const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
 {
-    if (type != TRUETYPE_FONTTYPE)
+    if (type & RASTER_FONTTYPE)
         return 1;
 
     *(LOGFONTW *)lParam = *elf;
@@ -635,6 +640,7 @@ static INT CALLBACK is_font_installed_proc(const LOGFONTW *elf,
 
 struct font_metrics
 {
+    WCHAR facename[LF_FACESIZE];
     UINT16 em_height, ascent, descent, line_spacing; /* in font units */
     int dpi;
 };
@@ -650,6 +656,8 @@ static BOOL get_font_metrics(HDC hdc, struct font_metrics *fm)
     otm.otmSize = sizeof(otm);
     if (!GetOutlineTextMetricsW(hdc, otm.otmSize, &otm)) return FALSE;
 
+    GetTextFaceW(hdc, LF_FACESIZE, fm->facename);
+
     fm->em_height = otm.otmEMSquare;
     fm->dpi = GetDeviceCaps(hdc, LOGPIXELSY);
 
@@ -750,7 +758,7 @@ GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name,
     ffamily = GdipAlloc(sizeof (GpFontFamily));
     if (!ffamily) return OutOfMemory;
 
-    lstrcpynW(ffamily->FamilyName, name, LF_FACESIZE);
+    lstrcpyW(ffamily->FamilyName, fm.facename);
     ffamily->em_height = fm.em_height;
     ffamily->ascent = fm.ascent;
     ffamily->descent = fm.descent;
@@ -764,6 +772,16 @@ GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name,
     return Ok;
 }
 
+static GpStatus clone_font_family(const GpFontFamily *family, GpFontFamily **clone)
+{
+    *clone = GdipAlloc(sizeof(GpFontFamily));
+    if (!*clone) return OutOfMemory;
+
+    **clone = *family;
+
+    return Ok;
+}
+
 /*******************************************************************************
  * GdipCloneFontFamily [GDIPLUS.@]
  *
@@ -778,16 +796,15 @@ GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name,
  */
 GpStatus WINGDIPAPI GdipCloneFontFamily(GpFontFamily* FontFamily, GpFontFamily** clonedFontFamily)
 {
+    GpStatus status;
+
     if (!(FontFamily && clonedFontFamily)) return InvalidParameter;
 
     TRACE("%p (%s), %p\n", FontFamily,
             debugstr_w(FontFamily->FamilyName), clonedFontFamily);
 
-    *clonedFontFamily = GdipAlloc(sizeof(GpFontFamily));
-    if (!*clonedFontFamily) return OutOfMemory;
-
-    **clonedFontFamily = *FontFamily;
-    lstrcpyW((*clonedFontFamily)->FamilyName, FontFamily->FamilyName);
+    status = clone_font_family(FontFamily, clonedFontFamily);
+    if (status != Ok) return status;
 
     TRACE("<-- %p\n", *clonedFontFamily);
 
index 56d8330..1c016be 100644 (file)
@@ -35,6 +35,9 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
 
+static const REAL mm_per_inch = 25.4;
+static const REAL point_per_inch = 72.0;
+
 static Status WINAPI NotificationHook(ULONG_PTR *token)
 {
     TRACE("%p\n", token);
@@ -207,7 +210,7 @@ static void unstretch_angle(REAL * angle, REAL rad_x, REAL rad_y)
         return;
 
     stretched = gdiplus_atan2(sin(*angle) / fabs(rad_y), cos(*angle) / fabs(rad_x));
-    revs_off = roundr(*angle / (2.0 * M_PI)) - roundr(stretched / (2.0 * M_PI));
+    revs_off = gdip_round(*angle / (2.0 * M_PI)) - gdip_round(stretched / (2.0 * M_PI));
     stretched += ((REAL)revs_off) * M_PI * 2.0;
     *angle = stretched;
 }
@@ -318,28 +321,57 @@ GpStatus hresult_to_status(HRESULT res)
 }
 
 /* converts a given unit to its value in pixels */
-REAL convert_unit(REAL logpixels, GpUnit unit)
+REAL units_to_pixels(REAL units, GpUnit unit, REAL dpi)
 {
-    switch(unit)
+    switch (unit)
     {
-        case UnitInch:
-            return logpixels;
-        case UnitPoint:
-            return logpixels / 72.0;
-        case UnitDocument:
-            return logpixels / 300.0;
-        case UnitMillimeter:
-            return logpixels / 25.4;
-        case UnitWorld:
-            ERR("cannot convert UnitWorld\n");
-            return 0.0;
-        case UnitPixel:
-        case UnitDisplay:
-        default:
-            return 1.0;
+    case UnitPixel:
+    case UnitWorld:
+    case UnitDisplay:
+        return units;
+    case UnitPoint:
+        return units * dpi / point_per_inch;
+    case UnitInch:
+        return units * dpi;
+    case UnitDocument:
+        return units * dpi / 300.0; /* Per MSDN */
+    case UnitMillimeter:
+        return units * dpi / mm_per_inch;
+    default:
+        FIXME("Unhandled unit type: %d\n", unit);
+        return 0;
+    }
+}
+
+/* converts value in pixels to a given unit */
+REAL pixels_to_units(REAL pixels, GpUnit unit, REAL dpi)
+{
+    switch (unit)
+    {
+    case UnitPixel:
+    case UnitWorld:
+    case UnitDisplay:
+        return pixels;
+    case UnitPoint:
+        return pixels * point_per_inch / dpi;
+    case UnitInch:
+        return pixels / dpi;
+    case UnitDocument:
+        return pixels * 300.0 / dpi;
+    case UnitMillimeter:
+        return pixels * mm_per_inch / dpi;
+    default:
+        FIXME("Unhandled unit type: %d\n", unit);
+        return 0;
     }
 }
 
+REAL units_scale(GpUnit from, GpUnit to, REAL dpi)
+{
+    REAL pixels = units_to_pixels(1.0, from, dpi);
+    return pixels_to_units(pixels, to, dpi);
+}
+
 /* Calculates Bezier points from cardinal spline points. */
 void calc_curve_bezier(CONST GpPointF *pts, REAL tension, REAL *x1,
     REAL *y1, REAL *x2, REAL *y2)
@@ -362,8 +394,8 @@ void calc_curve_bezier_endp(REAL xend, REAL yend, REAL xadj, REAL yadj,
     REAL tension, REAL *x, REAL *y)
 {
     /* tangent at endpoints is the line from the endpoint to the adjacent point */
-    *x = roundr(tension * (xadj - xend) + xend);
-    *y = roundr(tension * (yadj - yend) + yend);
+    *x = gdip_round(tension * (xadj - xend) + xend);
+    *y = gdip_round(tension * (yadj - yend) + yend);
 }
 
 /* make sure path has enough space for len more points */
index 01e8dd7..ccf47f9 100644 (file)
 471 stdcall GdipRotatePenTransform(ptr float long)
 472 stdcall GdipRotateTextureTransform(ptr float long)
 473 stdcall GdipRotateWorldTransform(ptr float long)
-474 stub GdipSaveAdd
+474 stdcall GdipSaveAdd(ptr ptr)
 475 stub GdipSaveAddImage
 476 stdcall GdipSaveGraphics(ptr ptr)
-477 stdcall GdipSaveImageToFile(ptr ptr ptr ptr)
+477 stdcall GdipSaveImageToFile(ptr wstr ptr ptr)
 478 stdcall GdipSaveImageToStream(ptr ptr ptr ptr)
 479 stdcall GdipScaleLineTransform(ptr float float long)
 480 stdcall GdipScaleMatrix(ptr float float long)
index 1adfe09..cb7330e 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "objbase.h"
 #include "ocidl.h"
+#include "wincodecsdk.h"
 #include "wine/list.h"
 
 #include "gdiplus.h"
@@ -47,7 +48,9 @@ extern INT arc2polybezier(GpPointF * points, REAL x1, REAL y1, REAL x2, REAL y2,
     REAL startAngle, REAL sweepAngle) DECLSPEC_HIDDEN;
 extern REAL gdiplus_atan2(REAL dy, REAL dx) DECLSPEC_HIDDEN;
 extern GpStatus hresult_to_status(HRESULT res) DECLSPEC_HIDDEN;
-extern REAL convert_unit(REAL logpixels, GpUnit unit) DECLSPEC_HIDDEN;
+extern REAL units_to_pixels(REAL units, GpUnit unit, REAL dpi) DECLSPEC_HIDDEN;
+extern REAL pixels_to_units(REAL pixels, GpUnit unit, REAL dpi) DECLSPEC_HIDDEN;
+extern REAL units_scale(GpUnit from, GpUnit to, REAL dpi) DECLSPEC_HIDDEN;
 
 extern GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics) DECLSPEC_HIDDEN;
 
@@ -55,6 +58,7 @@ extern GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **r
 extern GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc) DECLSPEC_HIDDEN;
 extern GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc) DECLSPEC_HIDDEN;
 extern GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile) DECLSPEC_HIDDEN;
+extern MetafileType METAFILE_GetEmfType(HENHMETAFILE hemf) DECLSPEC_HIDDEN;
 
 extern void calc_curve_bezier(CONST GpPointF *pts, REAL tension, REAL *x1,
     REAL *y1, REAL *x2, REAL *y2) DECLSPEC_HIDDEN;
@@ -63,8 +67,6 @@ extern void calc_curve_bezier_endp(REAL xend, REAL yend, REAL xadj, REAL yadj,
 
 extern void free_installed_fonts(void) DECLSPEC_HIDDEN;
 
-extern void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font, HFONT *hfont) DECLSPEC_HIDDEN;
-
 extern BOOL lengthen_path(GpPath *path, INT len) DECLSPEC_HIDDEN;
 
 extern GpStatus trace_path(GpGraphics *graphics, GpPath *path) DECLSPEC_HIDDEN;
@@ -74,7 +76,7 @@ extern void delete_element(region_element *element) DECLSPEC_HIDDEN;
 
 extern GpStatus get_hatch_data(HatchStyle hatchstyle, const char **result) DECLSPEC_HIDDEN;
 
-static inline INT roundr(REAL x)
+static inline INT gdip_round(REAL x)
 {
     return (INT) floorf(x + 0.5);
 }
@@ -121,7 +123,11 @@ extern void convert_32bppARGB_to_32bppPARGB(UINT width, UINT height,
 
 extern GpStatus convert_pixels(INT width, INT height,
     INT dst_stride, BYTE *dst_bits, PixelFormat dst_format,
-    INT src_stride, const BYTE *src_bits, PixelFormat src_format, ARGB *src_palette) DECLSPEC_HIDDEN;
+    INT src_stride, const BYTE *src_bits, PixelFormat src_format, ColorPalette *palette) DECLSPEC_HIDDEN;
+
+struct GpMatrix{
+    REAL matrix[6];
+};
 
 struct GpPen{
     UINT style;
@@ -146,6 +152,7 @@ struct GpGraphics{
     HDC hdc;
     HWND hwnd;
     BOOL owndc;
+    BOOL alpha_hdc;
     GpImage *image;
     SmoothingMode smoothing;
     CompositingQuality compqual;
@@ -155,7 +162,8 @@ struct GpGraphics{
     TextRenderingHint texthint;
     GpUnit unit;    /* page unit */
     REAL scale;     /* page scale */
-    GpMatrix * worldtrans; /* world transform */
+    REAL xres, yres;
+    GpMatrix worldtrans; /* world transform */
     BOOL busy;      /* hdc handle obtained by GdipGetDC */
     GpRegion *clip;
     UINT textcontrast; /* not used yet. get/set only */
@@ -202,7 +210,7 @@ struct GpPathGradient{
     ARGB* pblendcolor; /* preset blend colors */
     REAL* pblendpos; /* preset blend positions */
     INT pblendcount;
-    GpMatrix *transform;
+    GpMatrix transform;
 };
 
 struct GpLineGradient{
@@ -224,7 +232,7 @@ struct GpLineGradient{
 
 struct GpTexture{
     GpBrush brush;
-    GpMatrix *transform;
+    GpMatrix transform;
     GpImage *image;
     GpImageAttributes *imageattributes;
     BYTE *bitmap_bits; /* image bits converted to ARGB and run through imageattributes */
@@ -237,10 +245,6 @@ struct GpPath{
     INT datalen; /* size of the arrays in pathdata */
 };
 
-struct GpMatrix{
-    REAL matrix[6];
-};
-
 struct GpPathIterator{
     GpPathData pathdata;
     INT subpath_pos;    /* for NextSubpath methods */
@@ -262,14 +266,13 @@ struct GpAdustableArrowCap{
 };
 
 struct GpImage{
-    IPicture* picture;
+    IPicture *picture;
+    IStream *stream; /* source stream */
     ImageType type;
     GUID format;
     UINT flags;
-    UINT palette_flags;
-    UINT palette_count;
-    UINT palette_size;
-    ARGB *palette_entries;
+    UINT frame_count, current_frame;
+    ColorPalette *palette;
     REAL xres, yres;
 };
 
@@ -279,6 +282,7 @@ struct GpMetafile{
     GpUnit unit;
     MetafileType metafile_type;
     HENHMETAFILE hemf;
+    int preserve_hemf; /* if true, hemf belongs to the app and should not be deleted */
 
     /* recording */
     HDC record_dc;
@@ -309,6 +313,9 @@ struct GpBitmap{
     INT stride; /* stride of bits if this is a DIB */
     BYTE *own_bits; /* image bits that need to be freed with this object */
     INT lockx, locky; /* X and Y coordinates of the rect when a bitmap is locked for writing. */
+    IWICMetadataReader *metadata_reader; /* NULL if there is no metadata */
+    UINT prop_count;
+    PropertyItem *prop_item; /* cached image properties */
 };
 
 struct GpCachedBitmap{
@@ -366,6 +373,7 @@ struct GpStringFormat{
     REAL *tabs;
     CharacterRange *character_ranges;
     INT range_count;
+    BOOL generic_typographic;
 };
 
 struct GpFontCollection{
@@ -436,4 +444,6 @@ GpStatus gdip_format_string(HDC hdc,
     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
     gdip_format_string_callback callback, void *user_data) DECLSPEC_HIDDEN;
 
+void get_log_fontW(const GpFont *, GpGraphics *, LOGFONTW *) DECLSPEC_HIDDEN;
+
 #endif
index 3881ec8..e9b21eb 100644 (file)
@@ -63,6 +63,11 @@ static volatile LONG g_priv_contid = GDIP_CONTID_STEP;
 #define ANCHOR_WIDTH (2.0)
 #define MAX_ITERS (50)
 
+static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
+                                   GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
+                                   GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
+                                   INT flags, GDIPCONST GpMatrix *matrix);
+
 /* Converts angle (in degrees) to x/y coordinates */
 static void deg2xy(REAL angle, REAL x_0, REAL y_0, REAL *x, REAL *y)
 {
@@ -101,12 +106,6 @@ static BYTE convert_path_point_type(BYTE type)
     return ret;
 }
 
-static REAL graphics_res(GpGraphics *graphics)
-{
-    if (graphics->image) return graphics->image->xres;
-    else return (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
-}
-
 static COLORREF get_gdi_brush_color(const GpBrush *brush)
 {
     ARGB argb;
@@ -273,12 +272,11 @@ static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
         pt[0].Y = 0.0;
         pt[1].X = 1.0;
         pt[1].Y = 1.0;
-        GdipTransformMatrixPoints(graphics->worldtrans, pt, 2);
+        GdipTransformMatrixPoints(&graphics->worldtrans, pt, 2);
         width = sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) +
                      (pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
 
-        width *= pen->width * convert_unit(graphics_res(graphics),
-                              pen->unit == UnitWorld ? graphics->unit : pen->unit);
+        width *= units_to_pixels(pen->width, pen->unit == UnitWorld ? graphics->unit : pen->unit, graphics->xres);
     }
 
     if(pen->dash == DashStyleCustom){
@@ -286,20 +284,20 @@ static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
 
         TRACE("dashes are: ");
         for(i = 0; i < numdashes; i++){
-            dash_array[i] = roundr(width * pen->dashes[i]);
+            dash_array[i] = gdip_round(width * pen->dashes[i]);
             TRACE("%d, ", dash_array[i]);
         }
         TRACE("\n and the pen style is %x\n", pen->style);
 
         create_gdi_logbrush(pen->brush, &lb);
-        gdipen = ExtCreatePen(pen->style, roundr(width), &lb,
+        gdipen = ExtCreatePen(pen->style, gdip_round(width), &lb,
                               numdashes, dash_array);
         free_gdi_logbrush(&lb);
     }
     else
     {
         create_gdi_logbrush(pen->brush, &lb);
-        gdipen = ExtCreatePen(pen->style, roundr(width), &lb, 0, NULL);
+        gdipen = ExtCreatePen(pen->style, gdip_round(width), &lb, 0, NULL);
         free_gdi_logbrush(&lb);
     }
 
@@ -315,7 +313,7 @@ static void restore_dc(GpGraphics *graphics, INT state)
 }
 
 static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
-        GpCoordinateSpace src_space, GpMatrix **matrix);
+        GpCoordinateSpace src_space, GpMatrix *matrix);
 
 /* This helper applies all the changes that the points listed in ptf need in
  * order to be drawn on the device context.  In the end, this should include at
@@ -330,24 +328,27 @@ static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace d
 static void transform_and_round_points(GpGraphics *graphics, POINT *pti,
     GpPointF *ptf, INT count)
 {
-    REAL unitscale;
-    GpMatrix *matrix;
+    REAL scale_x, scale_y;
+    GpMatrix matrix;
     int i;
 
-    unitscale = convert_unit(graphics_res(graphics), graphics->unit);
+    scale_x = units_to_pixels(1.0, graphics->unit, graphics->xres);
+    scale_y = units_to_pixels(1.0, graphics->unit, graphics->yres);
 
     /* apply page scale */
     if(graphics->unit != UnitDisplay)
-        unitscale *= graphics->scale;
+    {
+        scale_x *= graphics->scale;
+        scale_y *= graphics->scale;
+    }
 
-    GdipCloneMatrix(graphics->worldtrans, &matrix);
-    GdipScaleMatrix(matrix, unitscale, unitscale, MatrixOrderAppend);
-    GdipTransformMatrixPoints(matrix, ptf, count);
-    GdipDeleteMatrix(matrix);
+    matrix = graphics->worldtrans;
+    GdipScaleMatrix(&matrix, scale_x, scale_y, MatrixOrderAppend);
+    GdipTransformMatrixPoints(&matrix, ptf, count);
 
     for(i = 0; i < count; i++){
-        pti[i].x = roundr(ptf[i].X);
-        pti[i].y = roundr(ptf[i].Y);
+        pti[i].x = gdip_round(ptf[i].X);
+        pti[i].y = gdip_round(ptf[i].Y);
     }
 }
 
@@ -375,68 +376,67 @@ static void gdi_alpha_blend(GpGraphics *graphics, INT dst_x, INT dst_y, INT dst_
     }
 }
 
+static GpStatus get_clip_hrgn(GpGraphics *graphics, HRGN *hrgn)
+{
+    return GdipGetRegionHRgn(graphics->clip, graphics, hrgn);
+}
+
 /* Draw non-premultiplied ARGB data to the given graphics object */
-static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
+static GpStatus alpha_blend_bmp_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
     const BYTE *src, INT src_width, INT src_height, INT src_stride)
 {
-    if (graphics->image && graphics->image->type == ImageTypeBitmap)
-    {
-        GpBitmap *dst_bitmap = (GpBitmap*)graphics->image;
-        INT x, y;
+    GpBitmap *dst_bitmap = (GpBitmap*)graphics->image;
+    INT x, y;
 
-        for (x=0; x<src_width; x++)
+    for (x=0; x<src_width; x++)
+    {
+        for (y=0; y<src_height; y++)
         {
-            for (y=0; y<src_height; y++)
-            {
-                ARGB dst_color, src_color;
-                GdipBitmapGetPixel(dst_bitmap, x+dst_x, y+dst_y, &dst_color);
-                src_color = ((ARGB*)(src + src_stride * y))[x];
-                GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over(dst_color, src_color));
-            }
+            ARGB dst_color, src_color;
+            GdipBitmapGetPixel(dst_bitmap, x+dst_x, y+dst_y, &dst_color);
+            src_color = ((ARGB*)(src + src_stride * y))[x];
+            GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over(dst_color, src_color));
         }
-
-        return Ok;
-    }
-    else if (graphics->image && graphics->image->type == ImageTypeMetafile)
-    {
-        ERR("This should not be used for metafiles; fix caller\n");
-        return NotImplemented;
     }
-    else
-    {
-        HDC hdc;
-        HBITMAP hbitmap;
-        BITMAPINFOHEADER bih;
-        BYTE *temp_bits;
-
-        hdc = CreateCompatibleDC(0);
-
-        bih.biSize = sizeof(BITMAPINFOHEADER);
-        bih.biWidth = src_width;
-        bih.biHeight = -src_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;
-
-        hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
-            (void**)&temp_bits, NULL, 0);
-
-        convert_32bppARGB_to_32bppPARGB(src_width, src_height, temp_bits,
-            4 * src_width, src, src_stride);
-
-        SelectObject(hdc, hbitmap);
-        gdi_alpha_blend(graphics, dst_x, dst_y, src_width, src_height,
-                        hdc, 0, 0, src_width, src_height);
-        DeleteDC(hdc);
-        DeleteObject(hbitmap);
 
-        return Ok;
-    }
+    return Ok;
+}
+
+static GpStatus alpha_blend_hdc_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
+    const BYTE *src, INT src_width, INT src_height, INT src_stride)
+{
+    HDC hdc;
+    HBITMAP hbitmap;
+    BITMAPINFOHEADER bih;
+    BYTE *temp_bits;
+
+    hdc = CreateCompatibleDC(0);
+
+    bih.biSize = sizeof(BITMAPINFOHEADER);
+    bih.biWidth = src_width;
+    bih.biHeight = -src_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;
+
+    hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
+        (void**)&temp_bits, NULL, 0);
+
+    convert_32bppARGB_to_32bppPARGB(src_width, src_height, temp_bits,
+        4 * src_width, src, src_stride);
+
+    SelectObject(hdc, hbitmap);
+    gdi_alpha_blend(graphics, dst_x, dst_y, src_width, src_height,
+                    hdc, 0, 0, src_width, src_height);
+    DeleteDC(hdc);
+    DeleteObject(hbitmap);
+
+    return Ok;
 }
 
 static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst_y,
@@ -449,20 +449,44 @@ static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst
         int i, size;
         RGNDATA *rgndata;
         RECT *rects;
+        HRGN hrgn, visible_rgn;
+
+        hrgn = CreateRectRgn(dst_x, dst_y, dst_x + src_width, dst_y + src_height);
+        if (!hrgn)
+            return OutOfMemory;
+
+        stat = get_clip_hrgn(graphics, &visible_rgn);
+        if (stat != Ok)
+        {
+            DeleteObject(hrgn);
+            return stat;
+        }
+
+        if (visible_rgn)
+        {
+            CombineRgn(hrgn, hrgn, visible_rgn, RGN_AND);
+            DeleteObject(visible_rgn);
+        }
+
+        if (hregion)
+            CombineRgn(hrgn, hrgn, hregion, RGN_AND);
 
-        size = GetRegionData(hregion, 0, NULL);
+        size = GetRegionData(hrgn, 0, NULL);
 
         rgndata = GdipAlloc(size);
         if (!rgndata)
+        {
+            DeleteObject(hrgn);
             return OutOfMemory;
+        }
 
-        GetRegionData(hregion, size, rgndata);
+        GetRegionData(hrgn, size, rgndata);
 
-        rects = (RECT*)&rgndata->Buffer;
+        rects = (RECT*)rgndata->Buffer;
 
         for (i=0; stat == Ok && i<rgndata->rdh.nCount; i++)
         {
-            stat = alpha_blend_pixels(graphics, rects[i].left, rects[i].top,
+            stat = alpha_blend_bmp_pixels(graphics, rects[i].left, rects[i].top,
                 &src[(rects[i].left - dst_x) * 4 + (rects[i].top - dst_y) * src_stride],
                 rects[i].right - rects[i].left, rects[i].bottom - rects[i].top,
                 src_stride);
@@ -470,6 +494,8 @@ static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst
 
         GdipFree(rgndata);
 
+        DeleteObject(hrgn);
+
         return stat;
     }
     else if (graphics->image && graphics->image->type == ImageTypeMetafile)
@@ -479,21 +505,39 @@ static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst
     }
     else
     {
+        HRGN hrgn;
         int save;
 
+        stat = get_clip_hrgn(graphics, &hrgn);
+
+        if (stat != Ok)
+            return stat;
+
         save = SaveDC(graphics->hdc);
 
-        ExtSelectClipRgn(graphics->hdc, hregion, RGN_AND);
+        if (hrgn)
+            ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
+
+        if (hregion)
+            ExtSelectClipRgn(graphics->hdc, hregion, RGN_AND);
 
-        stat = alpha_blend_pixels(graphics, dst_x, dst_y, src, src_width,
+        stat = alpha_blend_hdc_pixels(graphics, dst_x, dst_y, src, src_width,
             src_height, src_stride);
 
         RestoreDC(graphics->hdc, save);
 
+        DeleteObject(hrgn);
+
         return stat;
     }
 }
 
+static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
+    const BYTE *src, INT src_width, INT src_height, INT src_stride)
+{
+    return alpha_blend_pixels_hrgn(graphics, dst_x, dst_y, src, src_width, src_height, src_stride, NULL);
+}
+
 static ARGB blend_colors(ARGB start, ARGB end, REAL position)
 {
     ARGB result=0;
@@ -759,10 +803,10 @@ static void get_bitmap_sample_size(InterpolationMode interpolation, WrapMode wra
         break;
     case InterpolationModeNearestNeighbor:
     default:
-        left = roundr(srcx);
-        top = roundr(srcy);
-        right = roundr(srcx+srcwidth);
-        bottom = roundr(srcy+srcheight);
+        left = gdip_round(srcx);
+        top = gdip_round(srcy);
+        right = gdip_round(srcx+srcwidth);
+        bottom = gdip_round(srcy+srcheight);
         break;
     }
 
@@ -851,7 +895,7 @@ static ARGB sample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT wi
 
 static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
     UINT height, GpPointF *point, GDIPCONST GpImageAttributes *attributes,
-    InterpolationMode interpolation)
+    InterpolationMode interpolation, PixelOffsetMode offset_mode)
 {
     static int fixme;
 
@@ -896,8 +940,25 @@ static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT
         return blend_colors(top, bottom, point->Y - topyf);
     }
     case InterpolationModeNearestNeighbor:
+    {
+        FLOAT pixel_offset;
+        switch (offset_mode)
+        {
+        default:
+        case PixelOffsetModeNone:
+        case PixelOffsetModeHighSpeed:
+            pixel_offset = 0.5;
+            break;
+
+        case PixelOffsetModeHalf:
+        case PixelOffsetModeHighQuality:
+            pixel_offset = 0.0;
+            break;
+        }
         return sample_bitmap_pixel(src_rect, bits, width, height,
-            roundr(point->X), roundr(point->Y), attributes);
+            floorf(point->X + pixel_offset), floorf(point->Y + pixel_offset), attributes);
+    }
+
     }
 }
 
@@ -1094,7 +1155,6 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
         GpTexture *fill = (GpTexture*)brush;
         GpPointF draw_points[3];
         GpStatus stat;
-        GpMatrix *world_to_texture;
         int x, y;
         GpBitmap *bitmap;
         int src_stride;
@@ -1126,17 +1186,11 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
 
         if (stat == Ok)
         {
-            stat = GdipCloneMatrix(fill->transform, &world_to_texture);
-        }
-
-        if (stat == Ok)
-        {
-            stat = GdipInvertMatrix(world_to_texture);
+            GpMatrix world_to_texture = fill->transform;
 
+            stat = GdipInvertMatrix(&world_to_texture);
             if (stat == Ok)
-                stat = GdipTransformMatrixPoints(world_to_texture, draw_points, 3);
-
-            GdipDeleteMatrix(world_to_texture);
+                stat = GdipTransformMatrixPoints(&world_to_texture, draw_points, 3);
         }
 
         if (stat == Ok && !fill->bitmap_bits)
@@ -1191,7 +1245,8 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
 
                     argb_pixels[x + y*cdwStride] = resample_bitmap_pixel(
                         &src_area, fill->bitmap_bits, bitmap->width, bitmap->height,
-                        &point, fill->imageattributes, graphics->interpolation);
+                        &point, fill->imageattributes, graphics->interpolation,
+                        graphics->pixeloffset);
                 }
             }
         }
@@ -1202,7 +1257,7 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
     {
         GpPathGradient *fill = (GpPathGradient*)brush;
         GpPath *flat_path;
-        GpMatrix *world_to_device;
+        GpMatrix world_to_device;
         GpStatus stat;
         int i, figure_start=0;
         GpPointF start_point, end_point, center_point;
@@ -1244,7 +1299,7 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
         if (!transform_fixme_once)
         {
             BOOL is_identity=TRUE;
-            GdipIsMatrixIdentity(fill->transform, &is_identity);
+            GdipIsMatrixIdentity(&fill->transform, &is_identity);
             if (!is_identity)
             {
                 FIXME("path gradient transform not implemented\n");
@@ -1261,18 +1316,16 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
             CoordinateSpaceWorld, &world_to_device);
         if (stat == Ok)
         {
-            stat = GdipTransformPath(flat_path, world_to_device);
+            stat = GdipTransformPath(flat_path, &world_to_device);
 
             if (stat == Ok)
             {
                 center_point = fill->center;
-                stat = GdipTransformMatrixPoints(world_to_device, &center_point, 1);
+                stat = GdipTransformMatrixPoints(&world_to_device, &center_point, 1);
             }
 
             if (stat == Ok)
                 stat = GdipFlattenPath(flat_path, NULL, 0.5);
-
-            GdipDeleteMatrix(world_to_device);
         }
 
         if (stat != Ok)
@@ -1446,7 +1499,7 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
     const GpCustomLineCap *custom, REAL x1, REAL y1, REAL x2, REAL y2)
 {
     HGDIOBJ oldbrush = NULL, oldpen = NULL;
-    GpMatrix *matrix = NULL;
+    GpMatrix matrix;
     HBRUSH brush = NULL;
     HPEN pen = NULL;
     PointF ptf[4], *custptf = NULL;
@@ -1591,16 +1644,17 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
             custpt = GdipAlloc(count * sizeof(POINT));
             tp = GdipAlloc(count);
 
-            if(!custptf || !custpt || !tp || (GdipCreateMatrix(&matrix) != Ok))
+            if(!custptf || !custpt || !tp)
                 goto custend;
 
             memcpy(custptf, custom->pathdata.Points, count * sizeof(PointF));
 
-            GdipScaleMatrix(matrix, size, size, MatrixOrderAppend);
-            GdipRotateMatrix(matrix, (180.0 / M_PI) * (theta - M_PI_2),
+            GdipSetMatrixElements(&matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
+            GdipScaleMatrix(&matrix, size, size, MatrixOrderAppend);
+            GdipRotateMatrix(&matrix, (180.0 / M_PI) * (theta - M_PI_2),
                              MatrixOrderAppend);
-            GdipTranslateMatrix(matrix, x2, y2, MatrixOrderAppend);
-            GdipTransformMatrixPoints(matrix, custptf, count);
+            GdipTranslateMatrix(&matrix, x2, y2, MatrixOrderAppend);
+            GdipTransformMatrixPoints(&matrix, custptf, count);
 
             transform_and_round_points(graphics, custpt, custptf, count);
 
@@ -1620,7 +1674,6 @@ custend:
             GdipFree(custptf);
             GdipFree(custpt);
             GdipFree(tp);
-            GdipDeleteMatrix(matrix);
             break;
         default:
             break;
@@ -1985,7 +2038,7 @@ typedef struct _GraphicsContainerItem {
     GpUnit unit;
     PixelOffsetMode pixeloffset;
     UINT textcontrast;
-    GpMatrix* worldtrans;
+    GpMatrix worldtrans;
     GpRegion* clip;
     INT origin_x, origin_y;
 } GraphicsContainerItem;
@@ -2011,17 +2064,10 @@ static GpStatus init_container(GraphicsContainerItem** container,
     (*container)->pixeloffset = graphics->pixeloffset;
     (*container)->origin_x = graphics->origin_x;
     (*container)->origin_y = graphics->origin_y;
-
-    sts = GdipCloneMatrix(graphics->worldtrans, &(*container)->worldtrans);
-    if(sts != Ok){
-        GdipFree(*container);
-        *container = NULL;
-        return sts;
-    }
+    (*container)->worldtrans = graphics->worldtrans;
 
     sts = GdipCloneRegion(graphics->clip, &(*container)->clip);
     if(sts != Ok){
-        GdipDeleteMatrix((*container)->worldtrans);
         GdipFree(*container);
         *container = NULL;
         return sts;
@@ -2030,8 +2076,8 @@ static GpStatus init_container(GraphicsContainerItem** container,
     return Ok;
 }
 
-static void delete_container(GraphicsContainerItem* container){
-    GdipDeleteMatrix(container->worldtrans);
+static void delete_container(GraphicsContainerItem* container)
+{
     GdipDeleteRegion(container->clip);
     GdipFree(container);
 }
@@ -2039,21 +2085,12 @@ static void delete_container(GraphicsContainerItem* container){
 static GpStatus restore_container(GpGraphics* graphics,
         GDIPCONST GraphicsContainerItem* container){
     GpStatus sts;
-    GpMatrix *newTrans;
     GpRegion *newClip;
 
-    sts = GdipCloneMatrix(container->worldtrans, &newTrans);
-    if(sts != Ok)
-        return sts;
-
     sts = GdipCloneRegion(container->clip, &newClip);
-    if(sts != Ok){
-        GdipDeleteMatrix(newTrans);
-        return sts;
-    }
+    if(sts != Ok) return sts;
 
-    GdipDeleteMatrix(graphics->worldtrans);
-    graphics->worldtrans = newTrans;
+    graphics->worldtrans = container->worldtrans;
 
     GdipDeleteRegion(graphics->clip);
     graphics->clip = newClip;
@@ -2093,6 +2130,25 @@ static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect)
         stat = GdipGetImageBounds(graphics->image, rect, &unit);
         if (stat == Ok && unit != UnitPixel)
             FIXME("need to convert from unit %i\n", unit);
+    }else if (GetObjectType(graphics->hdc) == OBJ_MEMDC){
+        HBITMAP hbmp;
+        BITMAP bmp;
+
+        rect->X = 0;
+        rect->Y = 0;
+
+        hbmp = GetCurrentObject(graphics->hdc, OBJ_BITMAP);
+        if (hbmp && GetObjectW(hbmp, sizeof(bmp), &bmp))
+        {
+            rect->Width = bmp.bmWidth;
+            rect->Height = bmp.bmHeight;
+        }
+        else
+        {
+            /* FIXME: ??? */
+            rect->Width = 1;
+            rect->Height = 1;
+        }
     }else{
         rect->X = 0;
         rect->Y = 0;
@@ -2130,21 +2186,72 @@ end:
     return stat;
 }
 
-void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font, HFONT *hfont)
+void get_log_fontW(const GpFont *font, GpGraphics *graphics, LOGFONTW *lf)
+{
+    REAL height;
+
+    if (font->unit == UnitPixel)
+    {
+        height = units_to_pixels(font->emSize, graphics->unit, graphics->yres);
+    }
+    else
+    {
+        if (graphics->unit == UnitDisplay || graphics->unit == UnitPixel)
+            height = units_to_pixels(font->emSize, font->unit, graphics->xres);
+        else
+            height = units_to_pixels(font->emSize, font->unit, graphics->yres);
+    }
+
+    lf->lfHeight = -(height + 0.5);
+    lf->lfWidth = 0;
+    lf->lfEscapement = 0;
+    lf->lfOrientation = 0;
+    lf->lfWeight = font->otm.otmTextMetrics.tmWeight;
+    lf->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0;
+    lf->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0;
+    lf->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0;
+    lf->lfCharSet = font->otm.otmTextMetrics.tmCharSet;
+    lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
+    lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
+    lf->lfQuality = DEFAULT_QUALITY;
+    lf->lfPitchAndFamily = 0;
+    strcpyW(lf->lfFaceName, font->family->FamilyName);
+}
+
+static void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font,
+                           GDIPCONST GpStringFormat *format, HFONT *hfont,
+                           GDIPCONST GpMatrix *matrix)
 {
     HDC hdc = CreateCompatibleDC(0);
     GpPointF pt[3];
-    REAL angle, rel_width, rel_height;
+    REAL angle, rel_width, rel_height, font_height;
     LOGFONTW lfw;
     HFONT unscaled_font;
     TEXTMETRICW textmet;
 
+    if (font->unit == UnitPixel)
+        font_height = font->emSize;
+    else
+    {
+        REAL unit_scale, res;
+
+        res = (graphics->unit == UnitDisplay || graphics->unit == UnitPixel) ? graphics->xres : graphics->yres;
+        unit_scale = units_scale(font->unit, graphics->unit, res);
+
+        font_height = font->emSize * unit_scale;
+    }
+
     pt[0].X = 0.0;
     pt[0].Y = 0.0;
     pt[1].X = 1.0;
     pt[1].Y = 0.0;
     pt[2].X = 0.0;
     pt[2].Y = 1.0;
+    if (matrix)
+    {
+        GpMatrix xform = *matrix;
+        GdipTransformMatrixPoints(&xform, pt, 3);
+    }
     if (graphics)
         GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
     angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
@@ -2153,15 +2260,15 @@ void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font, HFONT *hfont)
     rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
                       (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
 
-    GdipGetLogFontW((GpFont *)font, graphics, &lfw);
-    lfw.lfHeight = roundr(lfw.lfHeight * rel_height);
+    get_log_fontW(font, graphics, &lfw);
+    lfw.lfHeight = gdip_round(font_height * rel_height);
     unscaled_font = CreateFontIndirectW(&lfw);
 
     SelectObject(hdc, unscaled_font);
     GetTextMetricsW(hdc, &textmet);
 
-    lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width / rel_height);
-    lfw.lfEscapement = lfw.lfOrientation = roundr((angle / M_PI) * 1800.0);
+    lfw.lfWidth = gdip_round(textmet.tmAveCharWidth * rel_width / rel_height);
+    lfw.lfEscapement = lfw.lfOrientation = gdip_round((angle / M_PI) * 1800.0);
 
     *hfont = CreateFontIndirectW(&lfw);
 
@@ -2179,6 +2286,8 @@ GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
 GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **graphics)
 {
     GpStatus retval;
+    HBITMAP hbitmap;
+    DIBSECTION dib;
 
     TRACE("(%p, %p, %p)\n", hdc, hDevice, graphics);
 
@@ -2196,17 +2305,20 @@ GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **gra
     *graphics = GdipAlloc(sizeof(GpGraphics));
     if(!*graphics)  return OutOfMemory;
 
-    if((retval = GdipCreateMatrix(&(*graphics)->worldtrans)) != Ok){
-        GdipFree(*graphics);
-        return retval;
-    }
+    GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
 
     if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
-        GdipFree((*graphics)->worldtrans);
         GdipFree(*graphics);
         return retval;
     }
 
+    hbitmap = GetCurrentObject(hdc, OBJ_BITMAP);
+    if (hbitmap && GetObjectW(hbitmap, sizeof(dib), &dib) == sizeof(dib) &&
+        dib.dsBmih.biBitCount == 32 && dib.dsBmih.biCompression == BI_RGB)
+    {
+        (*graphics)->alpha_hdc = 1;
+    }
+
     (*graphics)->hdc = hdc;
     (*graphics)->hwnd = WindowFromDC(hdc);
     (*graphics)->owndc = FALSE;
@@ -2217,6 +2329,8 @@ GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **gra
     (*graphics)->compmode = CompositingModeSourceOver;
     (*graphics)->unit = UnitDisplay;
     (*graphics)->scale = 1.0;
+    (*graphics)->xres = GetDeviceCaps(hdc, LOGPIXELSX);
+    (*graphics)->yres = GetDeviceCaps(hdc, LOGPIXELSY);
     (*graphics)->busy = FALSE;
     (*graphics)->textcontrast = 4;
     list_init(&(*graphics)->containers);
@@ -2234,13 +2348,9 @@ GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics)
     *graphics = GdipAlloc(sizeof(GpGraphics));
     if(!*graphics)  return OutOfMemory;
 
-    if((retval = GdipCreateMatrix(&(*graphics)->worldtrans)) != Ok){
-        GdipFree(*graphics);
-        return retval;
-    }
+    GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
 
     if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
-        GdipFree((*graphics)->worldtrans);
         GdipFree(*graphics);
         return retval;
     }
@@ -2256,6 +2366,8 @@ GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics)
     (*graphics)->compmode = CompositingModeSourceOver;
     (*graphics)->unit = UnitDisplay;
     (*graphics)->scale = 1.0;
+    (*graphics)->xres = image->xres;
+    (*graphics)->yres = image->yres;
     (*graphics)->busy = FALSE;
     (*graphics)->textcontrast = 4;
     list_init(&(*graphics)->containers);
@@ -2298,65 +2410,43 @@ GpStatus WINGDIPAPI GdipCreateFromHWNDICM(HWND hwnd, GpGraphics **graphics)
 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
     GpMetafile **metafile)
 {
-    IStream *stream = NULL;
-    UINT read;
-    ENHMETAHEADER *copy;
-    GpStatus retval = Ok;
+    ENHMETAHEADER header;
+    MetafileType metafile_type;
 
     TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
 
     if(!hemf || !metafile)
         return InvalidParameter;
 
-    read = GetEnhMetaFileBits(hemf, 0, NULL);
-    copy = GdipAlloc(read);
-    GetEnhMetaFileBits(hemf, read, (BYTE *)copy);
+    if (GetEnhMetaFileHeader(hemf, sizeof(header), &header) == 0)
+        return GenericError;
 
-    if(CreateStreamOnHGlobal(copy, TRUE, &stream) != S_OK){
-        ERR("could not make stream\n");
-        GdipFree(copy);
-        retval = GenericError;
-        goto err;
-    }
+    metafile_type = METAFILE_GetEmfType(hemf);
 
-    *metafile = GdipAlloc(sizeof(GpMetafile));
-    if(!*metafile){
-        retval = OutOfMemory;
-        goto err;
-    }
-
-    if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
-        (LPVOID*) &((*metafile)->image.picture)) != S_OK)
-    {
-        retval = GenericError;
-        goto err;
-    }
+    if (metafile_type == MetafileTypeInvalid)
+        return GenericError;
 
+    *metafile = GdipAlloc(sizeof(GpMetafile));
+    if (!*metafile)
+        return OutOfMemory;
 
     (*metafile)->image.type = ImageTypeMetafile;
-    memcpy(&(*metafile)->image.format, &ImageFormatWMF, sizeof(GUID));
-    (*metafile)->image.palette_flags = 0;
-    (*metafile)->image.palette_count = 0;
-    (*metafile)->image.palette_size = 0;
-    (*metafile)->image.palette_entries = NULL;
-    (*metafile)->image.xres = (REAL)copy->szlDevice.cx;
-    (*metafile)->image.yres = (REAL)copy->szlDevice.cy;
-    (*metafile)->bounds.X = (REAL)copy->rclBounds.left;
-    (*metafile)->bounds.Y = (REAL)copy->rclBounds.top;
-    (*metafile)->bounds.Width = (REAL)(copy->rclBounds.right - copy->rclBounds.left);
-    (*metafile)->bounds.Height = (REAL)(copy->rclBounds.bottom - copy->rclBounds.top);
+    (*metafile)->image.format = ImageFormatEMF;
+    (*metafile)->image.frame_count = 1;
+    (*metafile)->image.xres = (REAL)header.szlDevice.cx;
+    (*metafile)->image.yres = (REAL)header.szlDevice.cy;
+    (*metafile)->bounds.X = (REAL)header.rclBounds.left;
+    (*metafile)->bounds.Y = (REAL)header.rclBounds.top;
+    (*metafile)->bounds.Width = (REAL)(header.rclBounds.right - header.rclBounds.left);
+    (*metafile)->bounds.Height = (REAL)(header.rclBounds.bottom - header.rclBounds.top);
     (*metafile)->unit = UnitPixel;
-
-    if(delete)
-        DeleteEnhMetaFile(hemf);
+    (*metafile)->metafile_type = metafile_type;
+    (*metafile)->hemf = hemf;
+    (*metafile)->preserve_hemf = !delete;
 
     TRACE("<-- %p\n", *metafile);
 
-err:
-    if (retval != Ok)
-        GdipFree(*metafile);
-    IStream_Release(stream);
-    return retval;
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
@@ -2382,7 +2472,8 @@ GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
     hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
     GdipFree(copy);
 
-    retval = GdipCreateMetafileFromEmf(hemf, FALSE, metafile);
+    /* FIXME: We should store and use hwmf instead of converting to hemf */
+    retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
 
     if (retval == Ok)
     {
@@ -2394,9 +2485,13 @@ GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
                                            placeable->BoundingBox.Left);
         (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
                                             placeable->BoundingBox.Top);
+        (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
+        (*metafile)->image.format = ImageFormatWMF;
 
         if (delete) DeleteMetaFile(hwmf);
     }
+    else
+        DeleteEnhMetaFile(hemf);
     return retval;
 }
 
@@ -2474,7 +2569,6 @@ GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
     }
 
     GdipDeleteRegion(graphics->clip);
-    GdipDeleteMatrix(graphics->worldtrans);
     GdipFree(graphics);
 
     return Ok;
@@ -2946,7 +3040,6 @@ GpStatus WINGDIPAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x,
 GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x, REAL y)
 {
     UINT width, height;
-    GpPointF points[3];
 
     TRACE("(%p, %p, %.2f, %.2f)\n", graphics, image, x, y);
 
@@ -2956,15 +3049,8 @@ GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x,
     GdipGetImageWidth(image, &width);
     GdipGetImageHeight(image, &height);
 
-    /* FIXME: we should use the graphics and image dpi, somehow */
-
-    points[0].X = points[2].X = x;
-    points[0].Y = points[1].Y = y;
-    points[1].X = x + width;
-    points[2].Y = y + height;
-
-    return GdipDrawImagePointsRect(graphics, image, points, 3, 0, 0, width, height,
-        UnitPixel, NULL, NULL, NULL);
+    return GdipDrawImagePointRect(graphics, image, x, y,
+                                  0.0, 0.0, (REAL)width, (REAL)height, UnitPixel);
 }
 
 GpStatus WINGDIPAPI GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x,
@@ -2980,14 +3066,21 @@ GpStatus WINGDIPAPI GdipDrawImagePointRect(GpGraphics *graphics, GpImage *image,
     GpUnit srcUnit)
 {
     GpPointF points[3];
+    REAL scale_x, scale_y, width, height;
+
     TRACE("(%p, %p, %f, %f, %f, %f, %f, %f, %d)\n", graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
 
+    scale_x = units_scale(srcUnit, graphics->unit, graphics->xres);
+    scale_x *= graphics->xres / image->xres;
+    scale_y = units_scale(srcUnit, graphics->unit, graphics->yres);
+    scale_y *= graphics->yres / image->yres;
+    width = srcwidth * scale_x;
+    height = srcheight * scale_y;
+
     points[0].X = points[2].X = x;
     points[0].Y = points[1].Y = y;
-
-    /* FIXME: convert image coordinates to Graphics coordinates? */
-    points[1].X = x + srcwidth;
-    points[2].Y = y + srcheight;
+    points[1].X = x + width;
+    points[2].Y = y + height;
 
     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
         srcwidth, srcheight, srcUnit, NULL, NULL, NULL);
@@ -3037,6 +3130,13 @@ GpStatus WINGDIPAPI GdipDrawImagePointsI(GpGraphics *graphics, GpImage *image,
     return GdipDrawImagePoints(graphics, image, ptf, count);
 }
 
+static BOOL CALLBACK play_metafile_proc(EmfPlusRecordType record_type, unsigned int flags,
+    unsigned int dataSize, const unsigned char *pStr, void *userdata)
+{
+    GdipPlayMetafileRecord(userdata, record_type, flags, dataSize, pStr);
+    return TRUE;
+}
+
 GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image,
      GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
      REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
@@ -3044,7 +3144,6 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
 {
     GpPointF ptf[4];
     POINT pti[4];
-    REAL dx, dy;
     GpStatus stat;
 
     TRACE("(%p, %p, %p, %d, %f, %f, %f, %f, %d, %p, %p, %p)\n", graphics, image, points,
@@ -3067,6 +3166,15 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
         return Ok;
     transform_and_round_points(graphics, pti, ptf, 4);
 
+    TRACE("%s %s %s %s\n", wine_dbgstr_point(&pti[0]), wine_dbgstr_point(&pti[1]),
+        wine_dbgstr_point(&pti[2]), wine_dbgstr_point(&pti[3]));
+
+    srcx = units_to_pixels(srcx, srcUnit, image->xres);
+    srcy = units_to_pixels(srcy, srcUnit, image->yres);
+    srcwidth = units_to_pixels(srcwidth, srcUnit, image->xres);
+    srcheight = units_to_pixels(srcheight, srcUnit, image->yres);
+    TRACE("src pixels: %f,%f %fx%f\n", srcx, srcy, srcwidth, srcheight);
+
     if (image->picture)
     {
         if (!graphics->hdc)
@@ -3074,23 +3182,10 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
             FIXME("graphics object has no HDC\n");
         }
 
-        /* FIXME: partially implemented (only works for rectangular parallelograms) */
-        if(srcUnit == UnitInch)
-            dx = dy = (REAL) INCH_HIMETRIC;
-        else if(srcUnit == UnitPixel){
-            dx = ((REAL) INCH_HIMETRIC) /
-                 ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX));
-            dy = ((REAL) INCH_HIMETRIC) /
-                 ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY));
-        }
-        else
-            return NotImplemented;
-
         if(IPicture_Render(image->picture, graphics->hdc,
             pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
-            srcx * dx, srcy * dy,
-            srcwidth * dx, srcheight * dy,
-            NULL) != S_OK){
+            srcx, srcy, srcwidth, srcheight, NULL) != S_OK)
+        {
             if(callback)
                 callback(callbackData);
             return GenericError;
@@ -3101,21 +3196,14 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
         GpBitmap* bitmap = (GpBitmap*)image;
         int use_software=0;
 
-        if (srcUnit == UnitInch)
-            dx = dy = 96.0; /* FIXME: use the image resolution */
-        else if (srcUnit == UnitPixel)
-            dx = dy = 1.0;
-        else
-            return NotImplemented;
-
-        srcx = srcx * dx;
-        srcy = srcy * dy;
-        srcwidth = srcwidth * dx;
-        srcheight = srcheight * dy;
+        TRACE("graphics: %.2fx%.2f dpi, fmt %#x, scale %f, image: %.2fx%.2f dpi, fmt %#x, color %08x\n",
+            graphics->xres, graphics->yres,
+            graphics->image && graphics->image->type == ImageTypeBitmap ? ((GpBitmap *)graphics->image)->format : 0,
+            graphics->scale, image->xres, image->yres, bitmap->format,
+            imageAttributes ? imageAttributes->outside_color : 0);
 
-        if (imageAttributes ||
+        if (imageAttributes || graphics->alpha_hdc ||
             (graphics->image && graphics->image->type == ImageTypeBitmap) ||
-            !((GpBitmap*)image)->hbitmap ||
             ptf[1].Y != ptf[0].Y || ptf[2].X != ptf[0].X ||
             ptf[1].X - ptf[0].X != srcwidth || ptf[2].Y - ptf[0].Y != srcheight ||
             srcx < 0 || srcy < 0 ||
@@ -3127,11 +3215,12 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
             RECT dst_area;
             GpRect src_area;
             int i, x, y, src_stride, dst_stride;
-            GpMatrix *dst_to_src;
+            GpMatrix dst_to_src;
             REAL m11, m12, m21, m22, mdx, mdy;
             LPBYTE src_data, dst_data;
             BitmapData lockeddata;
             InterpolationMode interpolation = graphics->interpolation;
+            PixelOffsetMode offset_mode = graphics->pixeloffset;
             GpPointF dst_to_src_points[3] = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}};
             REAL x_dx, x_dy, y_dx, y_dy;
             static const GpImageAttributes defaultImageAttributes = {WrapModeClamp, 0, FALSE};
@@ -3149,6 +3238,8 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
                 if (dst_area.bottom < pti[i].y) dst_area.bottom = pti[i].y;
             }
 
+            TRACE("dst_area: %s\n", wine_dbgstr_rect(&dst_area));
+
             m11 = (ptf[1].X - ptf[0].X) / srcwidth;
             m21 = (ptf[2].X - ptf[0].X) / srcheight;
             mdx = ptf[0].X - m11 * srcx - m21 * srcy;
@@ -3156,33 +3247,25 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
             m22 = (ptf[2].Y - ptf[0].Y) / srcheight;
             mdy = ptf[0].Y - m12 * srcx - m22 * srcy;
 
-            stat = GdipCreateMatrix2(m11, m12, m21, m22, mdx, mdy, &dst_to_src);
-            if (stat != Ok) return stat;
+            GdipSetMatrixElements(&dst_to_src, m11, m12, m21, m22, mdx, mdy);
 
-            stat = GdipInvertMatrix(dst_to_src);
-            if (stat != Ok)
-            {
-                GdipDeleteMatrix(dst_to_src);
-                return stat;
-            }
+            stat = GdipInvertMatrix(&dst_to_src);
+            if (stat != Ok) return stat;
 
             dst_data = GdipAlloc(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
-            if (!dst_data)
-            {
-                GdipDeleteMatrix(dst_to_src);
-                return OutOfMemory;
-            }
+            if (!dst_data) return OutOfMemory;
 
             dst_stride = sizeof(ARGB) * (dst_area.right - dst_area.left);
 
             get_bitmap_sample_size(interpolation, imageAttributes->wrap,
                 bitmap, srcx, srcy, srcwidth, srcheight, &src_area);
 
+            TRACE("src_area: %d x %d\n", src_area.Width, src_area.Height);
+
             src_data = GdipAlloc(sizeof(ARGB) * src_area.Width * src_area.Height);
             if (!src_data)
             {
                 GdipFree(dst_data);
-                GdipDeleteMatrix(dst_to_src);
                 return OutOfMemory;
             }
             src_stride = sizeof(ARGB) * src_area.Width;
@@ -3205,8 +3288,7 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
                 if (src_data != dst_data)
                     GdipFree(src_data);
                 GdipFree(dst_data);
-                GdipDeleteMatrix(dst_to_src);
-                return OutOfMemory;
+                return stat;
             }
 
             apply_image_attributes(imageAttributes, src_data,
@@ -3214,7 +3296,7 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
                 src_stride, ColorAdjustTypeBitmap);
 
             /* Transform the bits as needed to the destination. */
-            GdipTransformMatrixPoints(dst_to_src, dst_to_src_points, 3);
+            GdipTransformMatrixPoints(&dst_to_src, dst_to_src_points, 3);
 
             x_dx = dst_to_src_points[1].X - dst_to_src_points[0].X;
             x_dy = dst_to_src_points[1].Y - dst_to_src_points[0].Y;
@@ -3234,14 +3316,13 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
                     dst_color = (ARGB*)(dst_data + dst_stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
 
                     if (src_pointf.X >= srcx && src_pointf.X < srcx + srcwidth && src_pointf.Y >= srcy && src_pointf.Y < srcy+srcheight)
-                        *dst_color = resample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf, imageAttributes, interpolation);
+                        *dst_color = resample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf,
+                                                           imageAttributes, interpolation, offset_mode);
                     else
                         *dst_color = 0;
                 }
             }
 
-            GdipDeleteMatrix(dst_to_src);
-
             GdipFree(src_data);
 
             stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
@@ -3293,11 +3374,19 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
 
                 convert_pixels(bitmap->width, bitmap->height,
                     bitmap->width*4, temp_bits, dst_format,
-                    bitmap->stride, bitmap->bits, bitmap->format, bitmap->image.palette_entries);
+                    bitmap->stride, bitmap->bits, bitmap->format,
+                    bitmap->image.palette);
             }
             else
             {
-                hbitmap = bitmap->hbitmap;
+                if (bitmap->hbitmap)
+                    hbitmap = bitmap->hbitmap;
+                else
+                {
+                    GdipCreateHBITMAPFromBitmap(bitmap, &hbitmap, 0);
+                    temp_bitmap = 1;
+                }
+
                 hdc = bitmap->hdc;
                 temp_hdc = (hdc == 0);
             }
@@ -3329,10 +3418,22 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
                 DeleteObject(hbitmap);
         }
     }
+    else if (image->type == ImageTypeMetafile && ((GpMetafile*)image)->hemf)
+    {
+        GpRectF rc;
+
+        rc.X = srcx;
+        rc.Y = srcy;
+        rc.Width = srcwidth;
+        rc.Height = srcheight;
+
+        return GdipEnumerateMetafileSrcRectDestPoints(graphics, (GpMetafile*)image,
+            points, count, &rc, srcUnit, play_metafile_proc, image, imageAttributes);
+    }
     else
     {
-        ERR("GpImage with no IPicture or HBITMAP?!\n");
-        return NotImplemented;
+        WARN("GpImage with nothing we can draw (metafile in wrong state?)\n");
+        return InvalidParameter;
     }
 
     return Ok;
@@ -3965,7 +4066,7 @@ GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *p
     if(graphics->busy)
         return ObjectBusy;
 
-    if (!graphics->image)
+    if (!graphics->image && !graphics->alpha_hdc)
         stat = GDI32_GdipFillPath(graphics, brush, path);
 
     if (stat == NotImplemented)
@@ -4219,7 +4320,7 @@ static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
 {
     GpStatus stat;
     GpRegion *temp_region;
-    GpMatrix *world_to_device;
+    GpMatrix world_to_device;
     GpRectF graphics_bounds;
     DWORD *pixel_data;
     HRGN hregion;
@@ -4240,11 +4341,7 @@ static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
             CoordinateSpaceWorld, &world_to_device);
 
         if (stat == Ok)
-        {
-            stat = GdipTransformRegion(temp_region, world_to_device);
-
-            GdipDeleteMatrix(world_to_device);
-        }
+            stat = GdipTransformRegion(temp_region, &world_to_device);
 
         if (stat == Ok)
             stat = GdipCombineRegionRect(temp_region, &graphics_bounds, CombineModeIntersect);
@@ -4307,7 +4404,7 @@ GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
     if(graphics->busy)
         return ObjectBusy;
 
-    if (!graphics->image)
+    if (!graphics->image && !graphics->alpha_hdc)
         stat = GDI32_GdipFillRegion(graphics, brush, region);
 
     if (stat == NotImplemented)
@@ -4570,10 +4667,10 @@ GpStatus WINGDIPAPI GdipGetVisibleClipBoundsI(GpGraphics *graphics, GpRect *rect
 
     if((stat = GdipGetVisibleClipBounds(graphics, &rectf)) == Ok)
     {
-        rect->X = roundr(rectf.X);
-        rect->Y = roundr(rectf.Y);
-        rect->Width  = roundr(rectf.Width);
-        rect->Height = roundr(rectf.Height);
+        rect->X = gdip_round(rectf.X);
+        rect->Y = gdip_round(rectf.Y);
+        rect->Width  = gdip_round(rectf.Width);
+        rect->Height = gdip_round(rectf.Height);
     }
 
     return stat;
@@ -4589,7 +4686,7 @@ GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix
     if(graphics->busy)
         return ObjectBusy;
 
-    *matrix = *graphics->worldtrans;
+    *matrix = graphics->worldtrans;
     return Ok;
 }
 
@@ -4738,11 +4835,8 @@ GpStatus gdip_format_string(HDC hdc,
     stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
     if(!stringdup) return OutOfMemory;
 
-    nwidth = roundr(rect->Width);
-    nheight = roundr(rect->Height);
-
-    if (rect->Width >= INT_MAX || rect->Width < 0.5) nwidth = INT_MAX;
-    if (rect->Height >= INT_MAX || rect->Height < 0.5) nheight = INT_MAX;
+    nwidth = rect->Width;
+    nheight = rect->Height;
 
     if (format)
         hkprefix = format->hkprefix;
@@ -4768,6 +4862,10 @@ GpStatus gdip_format_string(HDC hdc,
         if(!isprintW(string[i]) && (string[i] != '\n'))
             continue;
 
+        /* FIXME: tabs should be handled using tabstops from stringformat */
+        if (string[i] == '\t')
+            continue;
+
         if (seen_prefix && hkprefix == HotkeyPrefixShow && string[i] != '&')
             hotkeyprefix_offsets[hotkeyprefix_count++] = j;
         else if (!seen_prefix && hkprefix != HotkeyPrefixNone && string[i] == '&')
@@ -4874,7 +4972,8 @@ GpStatus gdip_format_string(HDC hdc,
             break;
 
         /* Stop if this was a linewrap (but not if it was a linebreak). */
-        if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
+        if ((lret == fitcpy) && format &&
+            (format->attr & (StringFormatFlagsNoWrap | StringFormatFlagsLineLimit)))
             break;
     }
 
@@ -4886,6 +4985,7 @@ GpStatus gdip_format_string(HDC hdc,
 
 struct measure_ranges_args {
     GpRegion **regions;
+    REAL rel_width, rel_height;
 };
 
 static GpStatus measure_ranges_callback(HDC hdc,
@@ -4907,16 +5007,16 @@ static GpStatus measure_ranges_callback(HDC hdc,
             GpRectF range_rect;
             SIZE range_size;
 
-            range_rect.Y = bounds->Y;
-            range_rect.Height = bounds->Height;
+            range_rect.Y = bounds->Y / args->rel_height;
+            range_rect.Height = bounds->Height / args->rel_height;
 
             GetTextExtentExPointW(hdc, string + index, range_start - index,
                                   INT_MAX, NULL, NULL, &range_size);
-            range_rect.X = bounds->X + range_size.cx;
+            range_rect.X = (bounds->X + range_size.cx) / args->rel_width;
 
             GetTextExtentExPointW(hdc, string + index, range_end - index,
                                   INT_MAX, NULL, NULL, &range_size);
-            range_rect.Width = (bounds->X + range_size.cx) - range_rect.X;
+            range_rect.Width = (bounds->X + range_size.cx) / args->rel_width - range_rect.X;
 
             stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion);
             if (stat != Ok)
@@ -4934,10 +5034,12 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
 {
     GpStatus stat;
     int i;
-    LOGFONTW lfw;
-    HFONT oldfont;
+    HFONT gdifont, oldfont;
     struct measure_ranges_args args;
     HDC hdc, temp_hdc=NULL;
+    GpPointF pt[3];
+    RectF scaled_rect;
+    REAL margin_x;
 
     TRACE("(%p %s %d %p %s %p %d %p)\n", graphics, debugstr_w(string),
             length, font, debugstr_rectf(layoutRect), stringFormat, regionCount, regions);
@@ -4948,9 +5050,6 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
     if (regionCount < stringFormat->range_count)
         return InvalidParameter;
 
-    stat = GdipGetLogFontW((GpFont *)font, graphics, &lfw);
-    if (stat != Ok) return stat;
-
     if(!graphics->hdc)
     {
         hdc = temp_hdc = CreateCompatibleDC(0);
@@ -4962,7 +5061,41 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
     if (stringFormat->attr)
         TRACE("may be ignoring some format flags: attr %x\n", stringFormat->attr);
 
-    oldfont = SelectObject(hdc, CreateFontIndirectW(&lfw));
+    pt[0].X = 0.0;
+    pt[0].Y = 0.0;
+    pt[1].X = 1.0;
+    pt[1].Y = 0.0;
+    pt[2].X = 0.0;
+    pt[2].Y = 1.0;
+    GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
+    args.rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
+                     (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
+    args.rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
+                      (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
+
+    margin_x = stringFormat->generic_typographic ? 0.0 : font->emSize / 6.0;
+    margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
+
+    scaled_rect.X = (layoutRect->X + margin_x) * args.rel_width;
+    scaled_rect.Y = layoutRect->Y * args.rel_height;
+    if (stringFormat->attr & StringFormatFlagsNoClip)
+    {
+        scaled_rect.Width = (REAL)(1 << 23);
+        scaled_rect.Height = (REAL)(1 << 23);
+    }
+    else
+    {
+        scaled_rect.Width = layoutRect->Width * args.rel_width;
+        scaled_rect.Height = layoutRect->Height * args.rel_height;
+    }
+    if (scaled_rect.Width >= 0.5)
+    {
+        scaled_rect.Width -= margin_x * 2.0 * args.rel_width;
+        if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */
+    }
+
+    get_font_hfont(graphics, font, stringFormat, &gdifont, NULL);
+    oldfont = SelectObject(hdc, gdifont);
 
     for (i=0; i<stringFormat->range_count; i++)
     {
@@ -4973,10 +5106,11 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
 
     args.regions = regions;
 
-    stat = gdip_format_string(hdc, string, length, font, layoutRect, stringFormat,
+    stat = gdip_format_string(hdc, string, length, font, &scaled_rect, stringFormat,
         measure_ranges_callback, &args);
 
-    DeleteObject(SelectObject(hdc, oldfont));
+    SelectObject(hdc, oldfont);
+    DeleteObject(gdifont);
 
     if (temp_hdc)
         DeleteDC(temp_hdc);
@@ -5001,7 +5135,7 @@ static GpStatus measure_string_callback(HDC hdc,
     REAL new_width, new_height;
 
     new_width = bounds->Width / args->rel_width;
-    new_height = (bounds->Height + bounds->Y - args->bounds->Y) / args->rel_height;
+    new_height = (bounds->Height + bounds->Y) / args->rel_height - args->bounds->Y;
 
     if (new_width > args->bounds->Width)
         args->bounds->Width = new_width;
@@ -5031,6 +5165,9 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
     struct measure_string_args args;
     HDC temp_hdc=NULL, hdc;
     GpPointF pt[3];
+    RectF scaled_rect;
+    REAL margin_x;
+    INT lines, glyphs, format_flags = format ? format->attr : 0;
 
     TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics,
         debugstr_wn(string, length), length, font, debugstr_rectf(rect), format,
@@ -5065,7 +5202,29 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
     args.rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
                       (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
 
-    get_font_hfont(graphics, font, &gdifont);
+    margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0;
+    margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
+
+    scaled_rect.X = (rect->X + margin_x) * args.rel_width;
+    scaled_rect.Y = rect->Y * args.rel_height;
+    scaled_rect.Width = rect->Width * args.rel_width;
+    scaled_rect.Height = rect->Height * args.rel_height;
+
+    if ((format_flags & StringFormatFlagsNoClip) ||
+        scaled_rect.Width >= 1 << 23 || scaled_rect.Width < 0.5) scaled_rect.Width = 1 << 23;
+    if ((format_flags & StringFormatFlagsNoClip) ||
+        scaled_rect.Height >= 1 << 23 || scaled_rect.Height < 0.5) scaled_rect.Height = 1 << 23;
+
+    if (scaled_rect.Width >= 0.5)
+    {
+        scaled_rect.Width -= margin_x * 2.0 * args.rel_width;
+        if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */
+    }
+
+    if (scaled_rect.Width >= 1 << 23 || scaled_rect.Width < 0.5) scaled_rect.Width = 1 << 23;
+    if (scaled_rect.Height >= 1 << 23 || scaled_rect.Height < 0.5) scaled_rect.Height = 1 << 23;
+
+    get_font_hfont(graphics, font, format, &gdifont, NULL);
     oldfont = SelectObject(hdc, gdifont);
 
     bounds->X = rect->X;
@@ -5074,12 +5233,19 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
     bounds->Height = 0.0;
 
     args.bounds = bounds;
-    args.codepointsfitted = codepointsfitted;
-    args.linesfilled = linesfilled;
+    args.codepointsfitted = &glyphs;
+    args.linesfilled = &lines;
+    lines = glyphs = 0;
 
-    gdip_format_string(hdc, string, length, font, rect, format,
+    gdip_format_string(hdc, string, length, font, &scaled_rect, format,
         measure_string_callback, &args);
 
+    if (linesfilled) *linesfilled = lines;
+    if (codepointsfitted) *codepointsfitted = glyphs;
+
+    if (lines)
+        bounds->Width += margin_x * 2.0;
+
     SelectObject(hdc, oldfont);
     DeleteObject(gdifont);
 
@@ -5108,7 +5274,7 @@ static GpStatus draw_string_callback(HDC hdc,
     position.X = args->x + bounds->X / args->rel_width;
     position.Y = args->y + bounds->Y / args->rel_height + args->ascent;
 
-    stat = GdipDrawDriverString(args->graphics, &string[index], length, font,
+    stat = draw_driver_string(args->graphics, &string[index], length, font, format,
         args->brush, &position,
         DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL);
 
@@ -5150,8 +5316,8 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string
     HFONT gdifont;
     GpPointF pt[3], rectcpy[4];
     POINT corners[4];
-    REAL rel_width, rel_height;
-    INT save_state;
+    REAL rel_width, rel_height, margin_x;
+    INT save_state, format_flags = 0;
     REAL offsety = 0.0;
     struct draw_string_args args;
     RectF scaled_rect;
@@ -5176,17 +5342,23 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string
     if(format){
         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
 
+        format_flags = format->attr;
+
         /* Should be no need to explicitly test for StringAlignmentNear as
          * that is default behavior if no alignment is passed. */
         if(format->vertalign != StringAlignmentNear){
-            RectF bounds;
-            GdipMeasureString(graphics, string, length, font, rect, format, &bounds, 0, 0);
+            RectF bounds, in_rect = *rect;
+            in_rect.Height = 0.0; /* avoid height clipping */
+            GdipMeasureString(graphics, string, length, font, &in_rect, format, &bounds, 0, 0);
+
+            TRACE("bounds %s\n", debugstr_rectf(&bounds));
 
             if(format->vertalign == StringAlignmentCenter)
                 offsety = (rect->Height - bounds.Height) / 2;
             else if(format->vertalign == StringAlignmentFar)
                 offsety = (rect->Height - bounds.Height);
         }
+        TRACE("vertical align %d, offsety %f\n", format->vertalign, offsety);
     }
 
     save_state = SaveDC(hdc);
@@ -5204,24 +5376,42 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string
                       (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
 
     rectcpy[3].X = rectcpy[0].X = rect->X;
-    rectcpy[1].Y = rectcpy[0].Y = rect->Y + offsety;
+    rectcpy[1].Y = rectcpy[0].Y = rect->Y;
     rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
-    rectcpy[3].Y = rectcpy[2].Y = rect->Y + offsety + rect->Height;
+    rectcpy[3].Y = rectcpy[2].Y = rect->Y + rect->Height;
     transform_and_round_points(graphics, corners, rectcpy, 4);
 
-    scaled_rect.X = 0.0;
+    margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0;
+    margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
+
+    scaled_rect.X = margin_x * rel_width;
     scaled_rect.Y = 0.0;
     scaled_rect.Width = rel_width * rect->Width;
     scaled_rect.Height = rel_height * rect->Height;
 
-    if (roundr(scaled_rect.Width) != 0 && roundr(scaled_rect.Height) != 0)
+    if ((format_flags & StringFormatFlagsNoClip) ||
+        scaled_rect.Width >= 1 << 23 || scaled_rect.Width < 0.5) scaled_rect.Width = 1 << 23;
+    if ((format_flags & StringFormatFlagsNoClip) ||
+        scaled_rect.Height >= 1 << 23 || scaled_rect.Height < 0.5) scaled_rect.Height = 1 << 23;
+
+    if (scaled_rect.Width >= 0.5)
+    {
+        scaled_rect.Width -= margin_x * 2.0 * rel_width;
+        if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */
+    }
+
+    if (scaled_rect.Width >= 1 << 23 || scaled_rect.Width < 0.5) scaled_rect.Width = 1 << 23;
+    if (scaled_rect.Height >= 1 << 23 || scaled_rect.Height < 0.5) scaled_rect.Height = 1 << 23;
+
+    if (!(format_flags & StringFormatFlagsNoClip) &&
+        scaled_rect.Width != 1 << 23 && scaled_rect.Height != 1 << 23)
     {
         /* FIXME: If only the width or only the height is 0, we should probably still clip */
         rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
         SelectClipRgn(hdc, rgn);
     }
 
-    get_font_hfont(graphics, font, &gdifont);
+    get_font_hfont(graphics, font, format, &gdifont, NULL);
     SelectObject(hdc, gdifont);
 
     args.graphics = graphics;
@@ -5272,14 +5462,7 @@ GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
     if(graphics->busy)
         return ObjectBusy;
 
-    graphics->worldtrans->matrix[0] = 1.0;
-    graphics->worldtrans->matrix[1] = 0.0;
-    graphics->worldtrans->matrix[2] = 0.0;
-    graphics->worldtrans->matrix[3] = 1.0;
-    graphics->worldtrans->matrix[4] = 0.0;
-    graphics->worldtrans->matrix[5] = 0.0;
-
-    return Ok;
+    return GdipSetMatrixElements(&graphics->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
 }
 
 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
@@ -5298,7 +5481,7 @@ GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
     if(graphics->busy)
         return ObjectBusy;
 
-    return GdipRotateMatrix(graphics->worldtrans, angle, order);
+    return GdipRotateMatrix(&graphics->worldtrans, angle, order);
 }
 
 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
@@ -5393,7 +5576,7 @@ GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
     if(graphics->busy)
         return ObjectBusy;
 
-    return GdipScaleMatrix(graphics->worldtrans, sx, sy, order);
+    return GdipScaleMatrix(&graphics->worldtrans, sx, sy, order);
 }
 
 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
@@ -5594,8 +5777,13 @@ GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix
     if(graphics->busy)
         return ObjectBusy;
 
-    GdipDeleteMatrix(graphics->worldtrans);
-    return GdipCloneMatrix(matrix, &graphics->worldtrans);
+    TRACE("%f,%f,%f,%f,%f,%f\n",
+          matrix->matrix[0], matrix->matrix[1], matrix->matrix[2],
+          matrix->matrix[3], matrix->matrix[4], matrix->matrix[5]);
+
+    graphics->worldtrans = *matrix;
+
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
@@ -5609,7 +5797,7 @@ GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
     if(graphics->busy)
         return ObjectBusy;
 
-    return GdipTranslateMatrix(graphics->worldtrans, dx, dy, order);
+    return GdipTranslateMatrix(&graphics->worldtrans, dx, dy, order);
 }
 
 /*****************************************************************************
@@ -5779,11 +5967,7 @@ GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
     if(graphics->busy)
         return ObjectBusy;
 
-    if (graphics->image)
-        *dpi = graphics->image->xres;
-    else
-        *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
-
+    *dpi = graphics->xres;
     return Ok;
 }
 
@@ -5797,11 +5981,7 @@ GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
     if(graphics->busy)
         return ObjectBusy;
 
-    if (graphics->image)
-        *dpi = graphics->image->yres;
-    else
-        *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSY);
-
+    *dpi = graphics->yres;
     return Ok;
 }
 
@@ -5819,11 +5999,11 @@ GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST G
     if(graphics->busy)
         return ObjectBusy;
 
-    m = *(graphics->worldtrans);
+    m = graphics->worldtrans;
 
     ret = GdipMultiplyMatrix(&m, matrix, order);
     if(ret == Ok)
-        *(graphics->worldtrans) = m;
+        graphics->worldtrans = m;
 
     return ret;
 }
@@ -5847,7 +6027,7 @@ GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
     {
         stat = METAFILE_GetDC((GpMetafile*)graphics->image, hdc);
     }
-    else if (!graphics->hdc ||
+    else if (!graphics->hdc || graphics->alpha_hdc ||
         (graphics->image && graphics->image->type == ImageTypeBitmap && ((GpBitmap*)graphics->image)->format & PixelFormatAlpha))
     {
         /* Create a fake HDC and fill it with a constant color. */
@@ -5983,28 +6163,34 @@ GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
 }
 
 static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
-        GpCoordinateSpace src_space, GpMatrix **matrix)
+        GpCoordinateSpace src_space, GpMatrix *matrix)
 {
-    GpStatus stat = GdipCreateMatrix(matrix);
-    REAL unitscale;
+    GpStatus stat = Ok;
+    REAL scale_x, scale_y;
 
-    if (dst_space != src_space && stat == Ok)
+    GdipSetMatrixElements(matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
+
+    if (dst_space != src_space)
     {
-        unitscale = convert_unit(graphics_res(graphics), graphics->unit);
+        scale_x = units_to_pixels(1.0, graphics->unit, graphics->xres);
+        scale_y = units_to_pixels(1.0, graphics->unit, graphics->yres);
 
         if(graphics->unit != UnitDisplay)
-            unitscale *= graphics->scale;
+        {
+            scale_x *= graphics->scale;
+            scale_y *= graphics->scale;
+        }
 
         /* transform from src_space to CoordinateSpacePage */
         switch (src_space)
         {
         case CoordinateSpaceWorld:
-            GdipMultiplyMatrix(*matrix, graphics->worldtrans, MatrixOrderAppend);
+            GdipMultiplyMatrix(matrix, &graphics->worldtrans, MatrixOrderAppend);
             break;
         case CoordinateSpacePage:
             break;
         case CoordinateSpaceDevice:
-            GdipScaleMatrix(*matrix, 1.0/unitscale, 1.0/unitscale, MatrixOrderAppend);
+            GdipScaleMatrix(matrix, 1.0/scale_x, 1.0/scale_y, MatrixOrderAppend);
             break;
         }
 
@@ -6013,21 +6199,16 @@ static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace d
         {
         case CoordinateSpaceWorld:
             {
-                GpMatrix *inverted_transform;
-                stat = GdipCloneMatrix(graphics->worldtrans, &inverted_transform);
+                GpMatrix inverted_transform = graphics->worldtrans;
+                stat = GdipInvertMatrix(&inverted_transform);
                 if (stat == Ok)
-                {
-                    stat = GdipInvertMatrix(inverted_transform);
-                    if (stat == Ok)
-                        GdipMultiplyMatrix(*matrix, inverted_transform, MatrixOrderAppend);
-                    GdipDeleteMatrix(inverted_transform);
-                }
+                    GdipMultiplyMatrix(matrix, &inverted_transform, MatrixOrderAppend);
                 break;
             }
         case CoordinateSpacePage:
             break;
         case CoordinateSpaceDevice:
-            GdipScaleMatrix(*matrix, unitscale, unitscale, MatrixOrderAppend);
+            GdipScaleMatrix(matrix, scale_x, scale_y, MatrixOrderAppend);
             break;
         }
     }
@@ -6037,7 +6218,7 @@ static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace d
 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
                                         GpCoordinateSpace src_space, GpPointF *points, INT count)
 {
-    GpMatrix *matrix;
+    GpMatrix matrix;
     GpStatus stat;
 
     if(!graphics || !points || count <= 0)
@@ -6051,15 +6232,9 @@ GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace
     if (src_space == dst_space) return Ok;
 
     stat = get_graphics_transform(graphics, dst_space, src_space, &matrix);
+    if (stat != Ok) return stat;
 
-    if (stat == Ok)
-    {
-        stat = GdipTransformMatrixPoints(matrix, points, count);
-
-        GdipDeleteMatrix(matrix);
-    }
-
-    return stat;
+    return GdipTransformMatrixPoints(&matrix, points, count);
 }
 
 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
@@ -6087,8 +6262,8 @@ GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace
 
     if(ret == Ok)
         for(i = 0; i < count; i++){
-            points[i].X = roundr(pointsF[i].X);
-            points[i].Y = roundr(pointsF[i].Y);
+            points[i].X = gdip_round(pointsF[i].X);
+            points[i].Y = gdip_round(pointsF[i].Y);
         }
     GdipFree(pointsF);
 
@@ -6177,10 +6352,7 @@ GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT
     if (flags & unsupported_flags)
         FIXME("Ignoring flags %x\n", flags & unsupported_flags);
 
-    if (matrix)
-        FIXME("Ignoring matrix\n");
-
-    get_font_hfont(graphics, font, &hfont);
+    get_font_hfont(graphics, font, NULL, &hfont, matrix);
 
     hdc = CreateCompatibleDC(0);
     SelectObject(hdc, hfont);
@@ -6193,6 +6365,11 @@ GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT
     pt[1].Y = 0.0;
     pt[2].X = 0.0;
     pt[2].Y = 1.0;
+    if (matrix)
+    {
+        GpMatrix xform = *matrix;
+        GdipTransformMatrixPoints(&xform, pt, 3);
+    }
     GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
     rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
@@ -6232,7 +6409,7 @@ GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT
         }
 
         GetCharABCWidthsW(hdc, glyph_indices[i], glyph_indices[i], &abc);
-        char_width = abc.abcA + abc.abcB + abc.abcB;
+        char_width = abc.abcA + abc.abcB + abc.abcC;
 
         if (min_y > y - ascent) min_y = y - ascent;
         if (max_y < y + descent) max_y = y + descent;
@@ -6256,9 +6433,9 @@ GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT
 }
 
 static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
-                                     GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
-                                     GDIPCONST PointF *positions, INT flags,
-                                     GDIPCONST GpMatrix *matrix )
+                                           GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
+                                           GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
+                                           INT flags, GDIPCONST GpMatrix *matrix)
 {
     static const INT unsupported_flags = ~(DriverStringOptionsRealizedAdvance|DriverStringOptionsCmapLookup);
     INT save_state;
@@ -6269,9 +6446,6 @@ static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT1
     if (flags & unsupported_flags)
         FIXME("Ignoring flags %x\n", flags & unsupported_flags);
 
-    if (matrix)
-        FIXME("Ignoring matrix\n");
-
     if (!(flags & DriverStringOptionsCmapLookup))
         eto_flags |= ETO_GLYPH_INDEX;
 
@@ -6282,12 +6456,12 @@ static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT1
     pt = positions[0];
     GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &pt, 1);
 
-    get_font_hfont(graphics, font, &hfont);
+    get_font_hfont(graphics, font, format, &hfont, matrix);
     SelectObject(graphics->hdc, hfont);
 
     SetTextAlign(graphics->hdc, TA_BASELINE|TA_LEFT);
 
-    ExtTextOutW(graphics->hdc, roundr(pt.X), roundr(pt.Y), eto_flags, NULL, text, length, NULL);
+    ExtTextOutW(graphics->hdc, gdip_round(pt.X), gdip_round(pt.Y), eto_flags, NULL, text, length, NULL);
 
     RestoreDC(graphics->hdc, save_state);
 
@@ -6297,9 +6471,9 @@ static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT1
 }
 
 static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
-                                         GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
-                                         GDIPCONST PointF *positions, INT flags,
-                                         GDIPCONST GpMatrix *matrix )
+                                        GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
+                                        GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
+                                        INT flags, GDIPCONST GpMatrix *matrix)
 {
     static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance);
     GpStatus stat;
@@ -6328,9 +6502,6 @@ static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UI
     if (flags & unsupported_flags)
         FIXME("Ignoring flags %x\n", flags & unsupported_flags);
 
-    if (matrix)
-        FIXME("Ignoring matrix\n");
-
     pti = GdipAlloc(sizeof(POINT) * length);
     if (!pti)
         return OutOfMemory;
@@ -6357,7 +6528,7 @@ static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UI
         GdipFree(real_positions);
     }
 
-    get_font_hfont(graphics, font, &hfont);
+    get_font_hfont(graphics, font, format, &hfont, matrix);
 
     hdc = CreateCompatibleDC(0);
     SelectObject(hdc, hfont);
@@ -6490,6 +6661,28 @@ static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UI
     return stat;
 }
 
+static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
+                                   GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
+                                   GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
+                                   INT flags, GDIPCONST GpMatrix *matrix)
+{
+    GpStatus stat = NotImplemented;
+
+    if (length == -1)
+        length = strlenW(text);
+
+    if (graphics->hdc && !graphics->alpha_hdc &&
+        ((flags & DriverStringOptionsRealizedAdvance) || length <= 1) &&
+        brush->bt == BrushTypeSolidColor &&
+        (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000)
+        stat = GDI32_GdipDrawDriverString(graphics, text, length, font, format,
+                                          brush, positions, flags, matrix);
+    if (stat == NotImplemented)
+        stat = SOFTWARE_GdipDrawDriverString(graphics, text, length, font, format,
+                                             brush, positions, flags, matrix);
+    return stat;
+}
+
 /*****************************************************************************
  * GdipDrawDriverString [GDIPLUS.@]
  */
@@ -6498,28 +6691,13 @@ GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16
                                          GDIPCONST PointF *positions, INT flags,
                                          GDIPCONST GpMatrix *matrix )
 {
-    GpStatus stat=NotImplemented;
-
     TRACE("(%p %s %p %p %p %d %p)\n", graphics, debugstr_wn(text, length), font, brush, positions, flags, matrix);
 
     if (!graphics || !text || !font || !brush || !positions)
         return InvalidParameter;
 
-    if (length == -1)
-        length = strlenW(text);
-
-    if (graphics->hdc &&
-        ((flags & DriverStringOptionsRealizedAdvance) || length <= 1) &&
-        brush->bt == BrushTypeSolidColor &&
-        (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000)
-        stat = GDI32_GdipDrawDriverString(graphics, text, length, font, brush,
-            positions, flags, matrix);
-
-    if (stat == NotImplemented)
-        stat = SOFTWARE_GdipDrawDriverString(graphics, text, length, font, brush,
-            positions, flags, matrix);
-
-    return stat;
+    return draw_driver_string(graphics, text, length, font, NULL,
+                              brush, positions, flags, matrix);
 }
 
 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
index 5da53b2..1fbebba 100644 (file)
@@ -155,7 +155,7 @@ static BOOL flatten_bezier(path_list_node_t *start, REAL x2, REAL y2, REAL x3, R
         if(!(node = add_path_list_node(start, mp[2].X, mp[2].Y, PathPointTypeLine)))
             return FALSE;
 
-    /* do the same with halfs */
+    /* do the same with halves */
     flatten_bezier(start, mp[0].X, mp[0].Y, mp[1].X, mp[1].Y, node, flatness);
     flatten_bezier(node,  mp[3].X, mp[3].Y, mp[4].X, mp[4].Y, end,  flatness);
 
@@ -840,7 +840,9 @@ static float fromfixedpoint(const FIXED v)
 struct format_string_args
 {
     GpPath *path;
-    UINT maxY;
+    float maxY;
+    float scale;
+    float ascent;
 };
 
 static GpStatus format_string_callback(HDC dc,
@@ -853,20 +855,22 @@ static GpStatus format_string_callback(HDC dc,
     struct format_string_args *args = priv;
     GpPath *path = args->path;
     GpStatus status = Ok;
-    float x = bounds->X;
-    float y = bounds->Y;
+    float x = rect->X + (bounds->X - rect->X) * args->scale;
+    float y = rect->Y + (bounds->Y - rect->Y) * args->scale;
     int i;
 
     if (underlined_index_count)
         FIXME("hotkey underlines not drawn yet\n");
 
+    if (y + bounds->Height * args->scale > args->maxY)
+        args->maxY = y + bounds->Height * args->scale;
+
     for (i = index; i < length; ++i)
     {
         GLYPHMETRICS gm;
         TTPOLYGONHEADER *ph = NULL;
         char *start;
         DWORD len, ofs = 0;
-        UINT bb_end;
         len = GetGlyphOutlineW(dc, string[i], GGO_BEZIER, &gm, 0, NULL, &identity);
         if (len == GDI_ERROR)
         {
@@ -881,9 +885,6 @@ static GpStatus format_string_callback(HDC dc,
             break;
         }
         GetGlyphOutlineW(dc, string[i], GGO_BEZIER, &gm, len, start, &identity);
-        bb_end = gm.gmBlackBoxY + gm.gmptGlyphOrigin.y;
-        if (bb_end + y > args->maxY)
-            args->maxY = bb_end + y;
 
         ofs = 0;
         while (ofs < len)
@@ -891,8 +892,8 @@ static GpStatus format_string_callback(HDC dc,
             DWORD ofs_start = ofs;
             ph = (TTPOLYGONHEADER*)&start[ofs];
             path->pathdata.Types[path->pathdata.Count] = PathPointTypeStart;
-            path->pathdata.Points[path->pathdata.Count].X = x + fromfixedpoint(ph->pfxStart.x);
-            path->pathdata.Points[path->pathdata.Count++].Y = y + bb_end - fromfixedpoint(ph->pfxStart.y);
+            path->pathdata.Points[path->pathdata.Count].X = x + fromfixedpoint(ph->pfxStart.x) * args->scale;
+            path->pathdata.Points[path->pathdata.Count++].Y = y + args->ascent - fromfixedpoint(ph->pfxStart.y) * args->scale;
             TRACE("Starting at count %i with pos %f, %f)\n", path->pathdata.Count, x, y);
             ofs += sizeof(*ph);
             while (ofs - ofs_start < ph->cb)
@@ -907,16 +908,16 @@ static GpStatus format_string_callback(HDC dc,
                     for (j = 0; j < curve->cpfx; ++j)
                     {
                         path->pathdata.Types[path->pathdata.Count] = PathPointTypeLine;
-                        path->pathdata.Points[path->pathdata.Count].X = x + fromfixedpoint(curve->apfx[j].x);
-                        path->pathdata.Points[path->pathdata.Count++].Y = y + bb_end - fromfixedpoint(curve->apfx[j].y);
+                        path->pathdata.Points[path->pathdata.Count].X = x + fromfixedpoint(curve->apfx[j].x) * args->scale;
+                        path->pathdata.Points[path->pathdata.Count++].Y = y + args->ascent - fromfixedpoint(curve->apfx[j].y) * args->scale;
                     }
                     break;
                 case TT_PRIM_CSPLINE:
                     for (j = 0; j < curve->cpfx; ++j)
                     {
                         path->pathdata.Types[path->pathdata.Count] = PathPointTypeBezier;
-                        path->pathdata.Points[path->pathdata.Count].X = x + fromfixedpoint(curve->apfx[j].x);
-                        path->pathdata.Points[path->pathdata.Count++].Y = y + bb_end - fromfixedpoint(curve->apfx[j].y);
+                        path->pathdata.Points[path->pathdata.Count].X = x + fromfixedpoint(curve->apfx[j].x) * args->scale;
+                        path->pathdata.Points[path->pathdata.Count++].Y = y + args->ascent - fromfixedpoint(curve->apfx[j].y) * args->scale;
                     }
                     break;
                 default:
@@ -927,8 +928,8 @@ static GpStatus format_string_callback(HDC dc,
             path->pathdata.Types[path->pathdata.Count - 1] |= PathPointTypeCloseSubpath;
         }
         path->newfigure = TRUE;
-        x += gm.gmCellIncX;
-        y += gm.gmCellIncY;
+        x += gm.gmCellIncX * args->scale;
+        y += gm.gmCellIncY * args->scale;
 
         GdipFree(ph);
         if (status != Ok)
@@ -945,39 +946,70 @@ GpStatus WINGDIPAPI GdipAddPathString(GpPath* path, GDIPCONST WCHAR* string, INT
     LOGFONTW lfw;
     HANDLE hfont;
     HDC dc;
+    GpGraphics *graphics;
     GpPath *backup;
     struct format_string_args args;
     int i;
+    UINT16 native_height;
+    RectF scaled_layout_rect;
+    TEXTMETRICW textmetric;
 
-    FIXME("(%p, %s, %d, %p, %d, %f, %p, %p): stub\n", path, debugstr_w(string), length, family, style, emSize, layoutRect, format);
+    TRACE("(%p, %s, %d, %p, %d, %f, %p, %p)\n", path, debugstr_w(string), length, family, style, emSize, layoutRect, format);
     if (!path || !string || !family || !emSize || !layoutRect || !format)
         return InvalidParameter;
 
-    status = GdipCreateFont(family, emSize, style, UnitPixel, &font);
+    status = GdipGetEmHeight(family, style, &native_height);
     if (status != Ok)
         return status;
 
-    status = GdipGetLogFontW((GpFont *)font, NULL, &lfw);
-    if (status != Ok) return status;
-    hfont = CreateFontIndirectW(&lfw);
-    if (!hfont)
+    scaled_layout_rect.X = layoutRect->X;
+    scaled_layout_rect.Y = layoutRect->Y;
+    scaled_layout_rect.Width = layoutRect->Width * native_height / emSize;
+    scaled_layout_rect.Height = layoutRect->Height * native_height / emSize;
+
+    if ((status = GdipClonePath(path, &backup)) != Ok)
+        return status;
+
+    dc = CreateCompatibleDC(0);
+    status = GdipCreateFromHDC(dc, &graphics);
+    if (status != Ok)
     {
-        WARN("Failed to create font\n");
-        return GenericError;
+        DeleteDC(dc);
+        GdipDeletePath(backup);
+        return status;
     }
 
-    if ((status = GdipClonePath(path, &backup)) != Ok)
+    status = GdipCreateFont(family, native_height, style, UnitPixel, &font);
+    if (status != Ok)
     {
-        DeleteObject(hfont);
+        GdipDeleteGraphics(graphics);
+        DeleteDC(dc);
+        GdipDeletePath(backup);
         return status;
     }
 
-    dc = CreateCompatibleDC(0);
+    get_log_fontW(font, graphics, &lfw);
+    GdipDeleteFont(font);
+    GdipDeleteGraphics(graphics);
+
+    hfont = CreateFontIndirectW(&lfw);
+    if (!hfont)
+    {
+        WARN("Failed to create font\n");
+        DeleteDC(dc);
+        GdipDeletePath(backup);
+        return GenericError;
+    }
+
     SelectObject(dc, hfont);
 
+    GetTextMetricsW(dc, &textmetric);
+
     args.path = path;
     args.maxY = 0;
-    status = gdip_format_string(dc, string, length, NULL, layoutRect, format, format_string_callback, &args);
+    args.scale = emSize / native_height;
+    args.ascent = textmetric.tmAscent * args.scale;
+    status = gdip_format_string(dc, string, length, NULL, &scaled_layout_rect, format, format_string_callback, &args);
 
     DeleteDC(dc);
     DeleteObject(hfont);
@@ -992,12 +1024,12 @@ GpStatus WINGDIPAPI GdipAddPathString(GpPath* path, GDIPCONST WCHAR* string, INT
     }
     if (format && format->vertalign == StringAlignmentCenter && layoutRect->Y + args.maxY < layoutRect->Height)
     {
-        float inc = layoutRect->Height - args.maxY - layoutRect->Y;
+        float inc = layoutRect->Height + layoutRect->Y - args.maxY;
         inc /= 2;
         for (i = backup->pathdata.Count; i < path->pathdata.Count; ++i)
             path->pathdata.Points[i].Y += inc;
     } else if (format && format->vertalign == StringAlignmentFar) {
-        float inc = layoutRect->Height - args.maxY - layoutRect->Y;
+        float inc = layoutRect->Height + layoutRect->Y - args.maxY;
         for (i = backup->pathdata.Count; i < path->pathdata.Count; ++i)
             path->pathdata.Points[i].Y += inc;
     }
@@ -1035,9 +1067,9 @@ GpStatus WINGDIPAPI GdipClonePath(GpPath* path, GpPath **clone)
     (*clone)->pathdata.Points = GdipAlloc(path->datalen * sizeof(PointF));
     (*clone)->pathdata.Types = GdipAlloc(path->datalen);
     if(!(*clone)->pathdata.Points || !(*clone)->pathdata.Types){
-        GdipFree(*clone);
         GdipFree((*clone)->pathdata.Points);
         GdipFree((*clone)->pathdata.Types);
+        GdipFree(*clone);
         return OutOfMemory;
     }
 
@@ -1173,20 +1205,22 @@ GpStatus WINGDIPAPI GdipFlattenPath(GpPath *path, GpMatrix* matrix, REAL flatnes
     GpPointF pt;
     INT i = 1;
     INT startidx = 0;
+    GpStatus stat;
 
     TRACE("(%p, %p, %.2f)\n", path, matrix, flatness);
 
     if(!path)
         return InvalidParameter;
 
-    if(matrix){
-        WARN("transformation not supported yet!\n");
-        return NotImplemented;
-    }
-
     if(path->pathdata.Count == 0)
         return Ok;
 
+    if(matrix){
+        stat = GdipTransformPath(path, matrix);
+        if (stat != Ok)
+            return stat;
+    }
+
     pt = path->pathdata.Points[0];
     if(!init_path_list(&list, pt.X, pt.Y))
         return OutOfMemory;
@@ -1339,8 +1373,8 @@ GpStatus WINGDIPAPI GdipGetPathPointsI(GpPath *path, GpPoint* points, INT count)
     ret = GdipGetPathPoints(path,ptf,count);
     if(ret == Ok)
         for(i = 0;i < count;i++){
-            points[i].X = roundr(ptf[i].X);
-            points[i].Y = roundr(ptf[i].Y);
+            points[i].X = gdip_round(ptf[i].X);
+            points[i].Y = gdip_round(ptf[i].Y);
         };
     GdipFree(ptf);
 
@@ -1460,10 +1494,10 @@ GpStatus WINGDIPAPI GdipGetPathWorldBoundsI(GpPath* path, GpRect* bounds,
     ret = GdipGetPathWorldBounds(path,&boundsF,matrix,pen);
 
     if(ret == Ok){
-        bounds->X      = roundr(boundsF.X);
-        bounds->Y      = roundr(boundsF.Y);
-        bounds->Width  = roundr(boundsF.Width);
-        bounds->Height = roundr(boundsF.Height);
+        bounds->X      = gdip_round(boundsF.X);
+        bounds->Y      = gdip_round(boundsF.Y);
+        bounds->Width  = gdip_round(boundsF.Width);
+        bounds->Height = gdip_round(boundsF.Height);
     }
 
     return ret;
@@ -1589,7 +1623,7 @@ GpStatus WINGDIPAPI GdipIsVisiblePathPoint(GpPath* path, REAL x, REAL y, GpGraph
         return status;
     }
 
-    *result = PtInRegion(hrgn, roundr(x), roundr(y));
+    *result = PtInRegion(hrgn, gdip_round(x), gdip_round(y));
 
     DeleteObject(hrgn);
     GdipDeleteRegion(region);
@@ -1688,6 +1722,35 @@ static void widen_joint(const GpPointF *p1, const GpPointF *p2, const GpPointF *
 {
     switch (pen->join)
     {
+    case LineJoinMiter:
+    case LineJoinMiterClipped:
+        if ((p2->X - p1->X) * (p3->Y - p1->Y) > (p2->Y - p1->Y) * (p3->X - p1->X))
+        {
+            float distance = pen->width/2.0;
+            float length_0 = sqrtf((p2->X-p1->X)*(p2->X-p1->X)+(p2->Y-p1->Y)*(p2->Y-p1->Y));
+            float length_1 = sqrtf((p3->X-p2->X)*(p3->X-p2->X)+(p3->Y-p2->Y)*(p3->Y-p2->Y));
+            float dx0 = distance * (p2->X - p1->X) / length_0;
+            float dy0 = distance * (p2->Y - p1->Y) / length_0;
+            float dx1 = distance * (p3->X - p2->X) / length_1;
+            float dy1 = distance * (p3->Y - p2->Y) / length_1;
+            float det = (dy0*dx1 - dx0*dy1);
+            float dx = (dx0*dx1*(dx0-dx1) + dy0*dy0*dx1 - dy1*dy1*dx0)/det;
+            float dy = (dy0*dy1*(dy0-dy1) + dx0*dx0*dy1 - dx1*dx1*dy0)/det;
+            if (dx*dx + dy*dy < pen->miterlimit*pen->miterlimit * distance*distance)
+            {
+                *last_point = add_path_list_node(*last_point, p2->X + dx,
+                    p2->Y + dy, PathPointTypeLine);
+                break;
+            }
+            else if (pen->join == LineJoinMiter)
+            {
+                static int once;
+                if (!once++)
+                    FIXME("should add a clipped corner\n");
+            }
+            /* else fall-through */
+        }
+        /* else fall-through */
     default:
     case LineJoinBevel:
         add_bevel_point(p2, p1, pen, 1, last_point);
@@ -1709,6 +1772,74 @@ static void widen_cap(const GpPointF *endpoint, const GpPointF *nextpoint,
         if (add_last_point)
             add_bevel_point(endpoint, nextpoint, pen, 0, last_point);
         break;
+    case LineCapSquare:
+    {
+        REAL segment_dy = nextpoint->Y-endpoint->Y;
+        REAL segment_dx = nextpoint->X-endpoint->X;
+        REAL segment_length = sqrtf(segment_dy*segment_dy + segment_dx*segment_dx);
+        REAL distance = pen->width/2.0;
+        REAL bevel_dx, bevel_dy;
+        REAL extend_dx, extend_dy;
+
+        extend_dx = -distance * segment_dx / segment_length;
+        extend_dy = -distance * segment_dy / segment_length;
+
+        bevel_dx = -distance * segment_dy / segment_length;
+        bevel_dy = distance * segment_dx / segment_length;
+
+        if (add_first_points)
+            *last_point = add_path_list_node(*last_point, endpoint->X + extend_dx + bevel_dx,
+                endpoint->Y + extend_dy + bevel_dy, PathPointTypeLine);
+
+        if (add_last_point)
+            *last_point = add_path_list_node(*last_point, endpoint->X + extend_dx - bevel_dx,
+                endpoint->Y + extend_dy - bevel_dy, PathPointTypeLine);
+
+        break;
+    }
+    case LineCapRound:
+    {
+        REAL segment_dy = nextpoint->Y-endpoint->Y;
+        REAL segment_dx = nextpoint->X-endpoint->X;
+        REAL segment_length = sqrtf(segment_dy*segment_dy + segment_dx*segment_dx);
+        REAL distance = pen->width/2.0;
+        REAL dx, dy, dx2, dy2;
+        const REAL control_point_distance = 0.5522847498307935; /* 4/3 * (sqrt(2) - 1) */
+
+        if (add_first_points)
+        {
+            dx = -distance * segment_dx / segment_length;
+            dy = -distance * segment_dy / segment_length;
+
+            dx2 = dx * control_point_distance;
+            dy2 = dy * control_point_distance;
+
+            /* first 90-degree arc */
+            *last_point = add_path_list_node(*last_point, endpoint->X + dy,
+                endpoint->Y - dx, PathPointTypeLine);
+
+            *last_point = add_path_list_node(*last_point, endpoint->X + dy + dx2,
+                endpoint->Y - dx + dy2, PathPointTypeBezier);
+
+            *last_point = add_path_list_node(*last_point, endpoint->X + dx + dy2,
+                endpoint->Y + dy - dx2, PathPointTypeBezier);
+
+            /* midpoint */
+            *last_point = add_path_list_node(*last_point, endpoint->X + dx,
+                endpoint->Y + dy, PathPointTypeBezier);
+
+            /* second 90-degree arc */
+            *last_point = add_path_list_node(*last_point, endpoint->X + dx - dy2,
+                endpoint->Y + dy + dx2, PathPointTypeBezier);
+
+            *last_point = add_path_list_node(*last_point, endpoint->X - dy + dx2,
+                endpoint->Y + dx + dy2, PathPointTypeBezier);
+
+            *last_point = add_path_list_node(*last_point, endpoint->X - dy,
+                endpoint->Y + dx, PathPointTypeBezier);
+        }
+        break;
+    }
     }
 }
 
@@ -1716,15 +1847,16 @@ static void widen_open_figure(GpPath *path, GpPen *pen, int start, int end,
     path_list_node_t **last_point)
 {
     int i;
+    path_list_node_t *prev_point;
 
     if (end <= start)
         return;
 
+    prev_point = *last_point;
+
     widen_cap(&path->pathdata.Points[start], &path->pathdata.Points[start+1],
         pen, pen->startcap, pen->customstart, FALSE, TRUE, last_point);
 
-    (*last_point)->type = PathPointTypeStart;
-
     for (i=start+1; i<end; i++)
         widen_joint(&path->pathdata.Points[i-1], &path->pathdata.Points[i],
             &path->pathdata.Points[i+1], pen, last_point);
@@ -1739,6 +1871,7 @@ static void widen_open_figure(GpPath *path, GpPen *pen, int start, int end,
     widen_cap(&path->pathdata.Points[start], &path->pathdata.Points[start+1],
         pen, pen->startcap, pen->customstart, TRUE, FALSE, last_point);
 
+    prev_point->next->type = PathPointTypeStart;
     (*last_point)->type |= PathPointTypeCloseSubpath;
 }
 
@@ -1813,16 +1946,16 @@ GpStatus WINGDIPAPI GdipWidenPath(GpPath *path, GpPen *pen, GpMatrix *matrix,
     {
         last_point = points;
 
-        if (pen->endcap != LineCapFlat)
+        if (pen->endcap > LineCapRound)
             FIXME("unimplemented end cap %x\n", pen->endcap);
 
-        if (pen->startcap != LineCapFlat)
+        if (pen->startcap > LineCapRound)
             FIXME("unimplemented start cap %x\n", pen->startcap);
 
         if (pen->dashcap != DashCapFlat)
             FIXME("unimplemented dash cap %d\n", pen->dashcap);
 
-        if (pen->join != LineJoinBevel)
+        if (pen->join == LineJoinRound)
             FIXME("unimplemented line join %d\n", pen->join);
 
         if (pen->dash != DashStyleSolid)
index ba8b979..9d88629 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2007 Google (Evan Stade)
+ * Copyright (C) 2012 Dmitry Timoshkov
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -17,6 +18,7 @@
  */
 
 #include <stdarg.h>
+#include <assert.h>
 
 #define NONAMELESSUNION
 
@@ -40,6 +42,73 @@ WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
 
 #define PIXELFORMATBPP(x) ((x) ? ((x) >> 8) & 255 : 24)
 
+static const struct
+{
+    const WICPixelFormatGUID *wic_format;
+    PixelFormat gdip_format;
+    /* predefined palette type to use for pixel format conversions */
+    WICBitmapPaletteType palette_type;
+} pixel_formats[] =
+{
+    { &GUID_WICPixelFormatBlackWhite, PixelFormat1bppIndexed, WICBitmapPaletteTypeFixedBW },
+    { &GUID_WICPixelFormat1bppIndexed, PixelFormat1bppIndexed, WICBitmapPaletteTypeFixedBW },
+    { &GUID_WICPixelFormat8bppGray, PixelFormat8bppIndexed, WICBitmapPaletteTypeFixedGray256 },
+    { &GUID_WICPixelFormat8bppIndexed, PixelFormat8bppIndexed, WICBitmapPaletteTypeFixedHalftone256 },
+    { &GUID_WICPixelFormat16bppBGR555, PixelFormat16bppRGB555, WICBitmapPaletteTypeFixedHalftone256 },
+    { &GUID_WICPixelFormat24bppBGR, PixelFormat24bppRGB, WICBitmapPaletteTypeFixedHalftone256 },
+    { &GUID_WICPixelFormat32bppBGR, PixelFormat32bppRGB, WICBitmapPaletteTypeFixedHalftone256 },
+    { &GUID_WICPixelFormat32bppBGRA, PixelFormat32bppARGB, WICBitmapPaletteTypeFixedHalftone256 },
+    { &GUID_WICPixelFormat32bppPBGRA, PixelFormat32bppPARGB, WICBitmapPaletteTypeFixedHalftone256 },
+    { NULL }
+};
+
+static ColorPalette *get_palette(IWICBitmapFrameDecode *frame, WICBitmapPaletteType palette_type)
+{
+    HRESULT hr;
+    IWICImagingFactory *factory;
+    IWICPalette *wic_palette;
+    ColorPalette *palette = NULL;
+
+    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_IWICImagingFactory, (void **)&factory);
+    if (hr != S_OK) return NULL;
+
+    hr = IWICImagingFactory_CreatePalette(factory, &wic_palette);
+    if (hr == S_OK)
+    {
+        hr = WINCODEC_ERR_PALETTEUNAVAILABLE;
+        if (frame)
+            hr = IWICBitmapFrameDecode_CopyPalette(frame, wic_palette);
+        if (hr != S_OK)
+        {
+            TRACE("using predefined palette %#x\n", palette_type);
+            hr = IWICPalette_InitializePredefined(wic_palette, palette_type, FALSE);
+        }
+        if (hr == S_OK)
+        {
+            UINT count;
+            BOOL mono, gray;
+
+            IWICPalette_IsBlackWhite(wic_palette, &mono);
+            IWICPalette_IsGrayscale(wic_palette, &gray);
+
+            IWICPalette_GetColorCount(wic_palette, &count);
+            palette = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(UINT) + count * sizeof(ARGB));
+            IWICPalette_GetColors(wic_palette, count, palette->Entries, &palette->Count);
+
+            if (mono)
+                palette->Flags = 0;
+            else if (gray)
+                palette->Flags = PaletteFlagsGrayScale;
+            else
+                palette->Flags = PaletteFlagsHalftone;
+        }
+        IWICPalette_Release(wic_palette);
+    }
+    IWICImagingFactory_Release(factory);
+    return palette;
+}
+
 static INT ipicture_pixel_height(IPicture *pic)
 {
     HDC hdcref;
@@ -289,18 +358,21 @@ GpStatus WINGDIPAPI GdipBitmapGetPixel(GpBitmap* bitmap, INT x, INT y,
     }
 
     if (bitmap->format & PixelFormatIndexed)
-        *color = bitmap->image.palette_entries[index];
+        *color = bitmap->image.palette->Entries[index];
     else
         *color = a<<24|r<<16|g<<8|b;
 
     return Ok;
 }
 
-static inline UINT get_palette_index(BYTE r, BYTE g, BYTE b, BYTE a, GpBitmap* bitmap) {
+static inline UINT get_palette_index(BYTE r, BYTE g, BYTE b, BYTE a, ColorPalette *palette)
+{
     BYTE index = 0;
     int best_distance = 0x7fff;
     int distance;
     int i;
+
+    if (!palette) return 0;
     /* This algorithm scans entire palette,
        computes difference from desired color (all color components have equal weight)
        and returns the index of color with least difference.
@@ -310,8 +382,8 @@ static inline UINT get_palette_index(BYTE r, BYTE g, BYTE b, BYTE a, GpBitmap* b
        tables and thus may actually be slower if this method is called only few times per
        every image.
     */
-    for(i=0;i<bitmap->image.palette_size;i++) {
-        ARGB color=bitmap->image.palette_entries[i];
+    for(i=0;i<palette->Count;i++) {
+        ARGB color=palette->Entries[i];
         distance=abs(b-(color & 0xff)) + abs(g-(color>>8 & 0xff)) + abs(r-(color>>16 & 0xff)) + abs(a-(color>>24 & 0xff));
         if (distance<best_distance) {
             best_distance=distance;
@@ -322,25 +394,25 @@ static inline UINT get_palette_index(BYTE r, BYTE g, BYTE b, BYTE a, GpBitmap* b
 }
 
 static inline void setpixel_8bppIndexed(BYTE r, BYTE g, BYTE b, BYTE a,
-    BYTE *row, UINT x, GpBitmap* bitmap)
+    BYTE *row, UINT x, ColorPalette *palette)
 {
-     BYTE index = get_palette_index(r,g,b,a,bitmap);
+     BYTE index = get_palette_index(r,g,b,a,palette);
      row[x]=index;
 }
 
 static inline void setpixel_1bppIndexed(BYTE r, BYTE g, BYTE b, BYTE a,
-    BYTE *row, UINT x, GpBitmap* bitmap)
+    BYTE *row, UINT x, ColorPalette *palette)
 {
-    row[x/8]  = (row[x/8] & ~(1<<(7-x%8))) | (get_palette_index(r,g,b,a,bitmap)<<(7-x%8));
+    row[x/8]  = (row[x/8] & ~(1<<(7-x%8))) | (get_palette_index(r,g,b,a,palette)<<(7-x%8));
 }
 
 static inline void setpixel_4bppIndexed(BYTE r, BYTE g, BYTE b, BYTE a,
-    BYTE *row, UINT x, GpBitmap* bitmap)
+    BYTE *row, UINT x, ColorPalette *palette)
 {
     if (x & 1)
-        row[x/2] = (row[x/2] & 0xf0) | get_palette_index(r,g,b,a,bitmap);
+        row[x/2] = (row[x/2] & 0xf0) | get_palette_index(r,g,b,a,palette);
     else
-        row[x/2] = (row[x/2] & 0x0f) | get_palette_index(r,g,b,a,bitmap)<<4;
+        row[x/2] = (row[x/2] & 0x0f) | get_palette_index(r,g,b,a,palette)<<4;
 }
 
 static inline void setpixel_16bppGrayScale(BYTE r, BYTE g, BYTE b, BYTE a,
@@ -482,13 +554,13 @@ GpStatus WINGDIPAPI GdipBitmapSetPixel(GpBitmap* bitmap, INT x, INT y,
             setpixel_64bppPARGB(r,g,b,a,row,x);
             break;
         case PixelFormat8bppIndexed:
-            setpixel_8bppIndexed(r,g,b,a,row,x,bitmap);
+            setpixel_8bppIndexed(r,g,b,a,row,x,bitmap->image.palette);
             break;
         case PixelFormat4bppIndexed:
-            setpixel_4bppIndexed(r,g,b,a,row,x,bitmap);
+            setpixel_4bppIndexed(r,g,b,a,row,x,bitmap->image.palette);
             break;
         case PixelFormat1bppIndexed:
-            setpixel_1bppIndexed(r,g,b,a,row,x,bitmap);
+            setpixel_1bppIndexed(r,g,b,a,row,x,bitmap->image.palette);
             break;
         default:
             FIXME("not implemented for format 0x%x\n", bitmap->format);
@@ -500,7 +572,8 @@ GpStatus WINGDIPAPI GdipBitmapSetPixel(GpBitmap* bitmap, INT x, INT y,
 
 GpStatus convert_pixels(INT width, INT height,
     INT dst_stride, BYTE *dst_bits, PixelFormat dst_format,
-    INT src_stride, const BYTE *src_bits, PixelFormat src_format, ARGB *src_palette)
+    INT src_stride, const BYTE *src_bits, PixelFormat src_format,
+    ColorPalette *palette)
 {
     INT x, y;
 
@@ -517,9 +590,10 @@ GpStatus convert_pixels(INT width, INT height,
     for (x=0; x<width; x++) \
         for (y=0; y<height; y++) { \
             BYTE index; \
-            BYTE *color; \
+            ARGB argb; \
+            BYTE *color = (BYTE *)&argb; \
             getpixel_function(&index, src_bits+src_stride*y, x); \
-            color = (BYTE*)(&src_palette[index]); \
+            argb = (palette && index < palette->Count) ? palette->Entries[index] : 0; \
             setpixel_function(color[2], color[1], color[0], color[3], dst_bits+dst_stride*y, x); \
         } \
     return Ok; \
@@ -535,6 +609,16 @@ GpStatus convert_pixels(INT width, INT height,
     return Ok; \
 } while (0);
 
+#define convert_rgb_to_indexed(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, palette); \
+        } \
+    return Ok; \
+} while (0);
+
     switch (src_format)
     {
     case PixelFormat1bppIndexed:
@@ -621,6 +705,10 @@ GpStatus convert_pixels(INT width, INT height,
     case PixelFormat16bppGrayScale:
         switch (dst_format)
         {
+        case PixelFormat1bppIndexed:
+            convert_rgb_to_indexed(getpixel_16bppGrayScale, setpixel_1bppIndexed);
+        case PixelFormat8bppIndexed:
+            convert_rgb_to_indexed(getpixel_16bppGrayScale, setpixel_8bppIndexed);
         case PixelFormat16bppRGB555:
             convert_rgb_to_rgb(getpixel_16bppGrayScale, setpixel_16bppRGB555);
         case PixelFormat16bppRGB565:
@@ -646,6 +734,10 @@ GpStatus convert_pixels(INT width, INT height,
     case PixelFormat16bppRGB555:
         switch (dst_format)
         {
+        case PixelFormat1bppIndexed:
+            convert_rgb_to_indexed(getpixel_16bppRGB555, setpixel_1bppIndexed);
+        case PixelFormat8bppIndexed:
+            convert_rgb_to_indexed(getpixel_16bppRGB555, setpixel_8bppIndexed);
         case PixelFormat16bppGrayScale:
             convert_rgb_to_rgb(getpixel_16bppRGB555, setpixel_16bppGrayScale);
         case PixelFormat16bppRGB565:
@@ -671,6 +763,10 @@ GpStatus convert_pixels(INT width, INT height,
     case PixelFormat16bppRGB565:
         switch (dst_format)
         {
+        case PixelFormat1bppIndexed:
+            convert_rgb_to_indexed(getpixel_16bppRGB565, setpixel_1bppIndexed);
+        case PixelFormat8bppIndexed:
+            convert_rgb_to_indexed(getpixel_16bppRGB565, setpixel_8bppIndexed);
         case PixelFormat16bppGrayScale:
             convert_rgb_to_rgb(getpixel_16bppRGB565, setpixel_16bppGrayScale);
         case PixelFormat16bppRGB555:
@@ -696,6 +792,10 @@ GpStatus convert_pixels(INT width, INT height,
     case PixelFormat16bppARGB1555:
         switch (dst_format)
         {
+        case PixelFormat1bppIndexed:
+            convert_rgb_to_indexed(getpixel_16bppARGB1555, setpixel_1bppIndexed);
+        case PixelFormat8bppIndexed:
+            convert_rgb_to_indexed(getpixel_16bppARGB1555, setpixel_8bppIndexed);
         case PixelFormat16bppGrayScale:
             convert_rgb_to_rgb(getpixel_16bppARGB1555, setpixel_16bppGrayScale);
         case PixelFormat16bppRGB555:
@@ -721,6 +821,10 @@ GpStatus convert_pixels(INT width, INT height,
     case PixelFormat24bppRGB:
         switch (dst_format)
         {
+        case PixelFormat1bppIndexed:
+            convert_rgb_to_indexed(getpixel_24bppRGB, setpixel_1bppIndexed);
+        case PixelFormat8bppIndexed:
+            convert_rgb_to_indexed(getpixel_24bppRGB, setpixel_8bppIndexed);
         case PixelFormat16bppGrayScale:
             convert_rgb_to_rgb(getpixel_24bppRGB, setpixel_16bppGrayScale);
         case PixelFormat16bppRGB555:
@@ -746,6 +850,10 @@ GpStatus convert_pixels(INT width, INT height,
     case PixelFormat32bppRGB:
         switch (dst_format)
         {
+        case PixelFormat1bppIndexed:
+            convert_rgb_to_indexed(getpixel_32bppRGB, setpixel_1bppIndexed);
+        case PixelFormat8bppIndexed:
+            convert_rgb_to_indexed(getpixel_32bppRGB, setpixel_8bppIndexed);
         case PixelFormat16bppGrayScale:
             convert_rgb_to_rgb(getpixel_32bppRGB, setpixel_16bppGrayScale);
         case PixelFormat16bppRGB555:
@@ -771,6 +879,10 @@ GpStatus convert_pixels(INT width, INT height,
     case PixelFormat32bppARGB:
         switch (dst_format)
         {
+        case PixelFormat1bppIndexed:
+            convert_rgb_to_indexed(getpixel_32bppARGB, setpixel_1bppIndexed);
+        case PixelFormat8bppIndexed:
+            convert_rgb_to_indexed(getpixel_32bppARGB, setpixel_8bppIndexed);
         case PixelFormat16bppGrayScale:
             convert_rgb_to_rgb(getpixel_32bppARGB, setpixel_16bppGrayScale);
         case PixelFormat16bppRGB555:
@@ -795,6 +907,10 @@ GpStatus convert_pixels(INT width, INT height,
     case PixelFormat32bppPARGB:
         switch (dst_format)
         {
+        case PixelFormat1bppIndexed:
+            convert_rgb_to_indexed(getpixel_32bppPARGB, setpixel_1bppIndexed);
+        case PixelFormat8bppIndexed:
+            convert_rgb_to_indexed(getpixel_32bppPARGB, setpixel_8bppIndexed);
         case PixelFormat16bppGrayScale:
             convert_rgb_to_rgb(getpixel_32bppPARGB, setpixel_16bppGrayScale);
         case PixelFormat16bppRGB555:
@@ -820,6 +936,10 @@ GpStatus convert_pixels(INT width, INT height,
     case PixelFormat48bppRGB:
         switch (dst_format)
         {
+        case PixelFormat1bppIndexed:
+            convert_rgb_to_indexed(getpixel_48bppRGB, setpixel_1bppIndexed);
+        case PixelFormat8bppIndexed:
+            convert_rgb_to_indexed(getpixel_48bppRGB, setpixel_8bppIndexed);
         case PixelFormat16bppGrayScale:
             convert_rgb_to_rgb(getpixel_48bppRGB, setpixel_16bppGrayScale);
         case PixelFormat16bppRGB555:
@@ -845,6 +965,10 @@ GpStatus convert_pixels(INT width, INT height,
     case PixelFormat64bppARGB:
         switch (dst_format)
         {
+        case PixelFormat1bppIndexed:
+            convert_rgb_to_indexed(getpixel_64bppARGB, setpixel_1bppIndexed);
+        case PixelFormat8bppIndexed:
+            convert_rgb_to_indexed(getpixel_64bppARGB, setpixel_8bppIndexed);
         case PixelFormat16bppGrayScale:
             convert_rgb_to_rgb(getpixel_64bppARGB, setpixel_16bppGrayScale);
         case PixelFormat16bppRGB555:
@@ -870,6 +994,10 @@ GpStatus convert_pixels(INT width, INT height,
     case PixelFormat64bppPARGB:
         switch (dst_format)
         {
+        case PixelFormat1bppIndexed:
+            convert_rgb_to_indexed(getpixel_64bppPARGB, setpixel_1bppIndexed);
+        case PixelFormat8bppIndexed:
+            convert_rgb_to_indexed(getpixel_64bppPARGB, setpixel_8bppIndexed);
         case PixelFormat16bppGrayScale:
             convert_rgb_to_rgb(getpixel_64bppPARGB, setpixel_16bppGrayScale);
         case PixelFormat16bppRGB555:
@@ -952,7 +1080,7 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
         lockeddata->Scan0 = bitmap->bits + (bitspp / 8) * act_rect.X +
                             bitmap->stride * act_rect.Y;
 
-        bitmap->lockmode = flags;
+        bitmap->lockmode = flags | ImageLockModeRead;
         bitmap->numlocks++;
 
         return Ok;
@@ -1011,7 +1139,7 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
             lockeddata->Stride, lockeddata->Scan0, format,
             bitmap->stride,
             bitmap->bits + bitmap->stride * act_rect.Y + PIXELFORMATBPP(bitmap->format) * act_rect.X / 8,
-            bitmap->format, bitmap->image.palette_entries);
+            bitmap->format, bitmap->image.palette);
 
         if (stat != Ok)
         {
@@ -1021,7 +1149,7 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
         }
     }
 
-    bitmap->lockmode = flags;
+    bitmap->lockmode = flags | ImageLockModeRead;
     bitmap->numlocks++;
     bitmap->lockx = act_rect.X;
     bitmap->locky = act_rect.Y;
@@ -1120,10 +1248,10 @@ GpStatus WINGDIPAPI GdipCloneBitmapArea(REAL x, REAL y, REAL width, REAL height,
     if (format == PixelFormatDontCare)
         format = srcBitmap->format;
 
-    area.X = roundr(x);
-    area.Y = roundr(y);
-    area.Width = roundr(width);
-    area.Height = roundr(height);
+    area.X = gdip_round(x);
+    area.Y = gdip_round(y);
+    area.Width = gdip_round(width);
+    area.Height = gdip_round(height);
 
     stat = GdipBitmapLockBits(srcBitmap, &area, ImageLockModeRead, format,
         &lockeddata_src);
@@ -1254,10 +1382,40 @@ GpStatus WINGDIPAPI GdipCloneImage(GpImage *image, GpImage **cloneImage)
 
         return stat;
     }
+    else if (image->type == ImageTypeMetafile && ((GpMetafile*)image)->hemf)
+    {
+        GpMetafile *result, *metafile;
+
+        metafile = (GpMetafile*)image;
+
+        result = GdipAlloc(sizeof(*result));
+        if (!result)
+            return OutOfMemory;
+
+        result->image.type = ImageTypeMetafile;
+        result->image.format = image->format;
+        result->image.flags = image->flags;
+        result->image.frame_count = 1;
+        result->image.xres = image->xres;
+        result->image.yres = image->yres;
+        result->bounds = metafile->bounds;
+        result->unit = metafile->unit;
+        result->metafile_type = metafile->metafile_type;
+        result->hemf = CopyEnhMetaFileW(metafile->hemf, NULL);
+
+        if (!result->hemf)
+        {
+            GdipFree(result);
+            return OutOfMemory;
+        }
+
+        *cloneImage = &result->image;
+        return Ok;
+    }
     else
     {
-        ERR("GpImage with no IPicture or bitmap?!\n");
-        return NotImplemented;
+        WARN("GpImage with no image data (metafile in wrong state?)\n");
+        return InvalidParameter;
     }
 }
 
@@ -1767,21 +1925,24 @@ GpStatus WINGDIPAPI GdipCreateBitmapFromScan0(INT width, INT height, INT stride,
     (*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.frame_count = 1;
+    (*bitmap)->image.current_frame = 0;
+    (*bitmap)->image.palette = NULL;
     (*bitmap)->image.xres = xres;
     (*bitmap)->image.yres = yres;
     (*bitmap)->width = width;
     (*bitmap)->height = height;
     (*bitmap)->format = format;
     (*bitmap)->image.picture = NULL;
+    (*bitmap)->image.stream = NULL;
     (*bitmap)->hbitmap = hbitmap;
     (*bitmap)->hdc = NULL;
     (*bitmap)->bits = bits;
     (*bitmap)->stride = stride;
     (*bitmap)->own_bits = own_bits;
+    (*bitmap)->metadata_reader = NULL;
+    (*bitmap)->prop_count = 0;
+    (*bitmap)->prop_item = NULL;
 
     /* set format-related flags */
     if (format & (PixelFormatAlpha|PixelFormatPAlpha|PixelFormatIndexed))
@@ -1791,29 +1952,30 @@ GpStatus WINGDIPAPI GdipCreateBitmapFromScan0(INT width, INT height, INT stride,
         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));
+        (*bitmap)->image.palette = GdipAlloc(sizeof(UINT) * 2 + sizeof(ARGB) * (1 << PIXELFORMATBPP(format)));
 
-        if (!(*bitmap)->image.palette_entries)
+        if (!(*bitmap)->image.palette)
         {
             GdipDisposeImage(&(*bitmap)->image);
             *bitmap = NULL;
             return OutOfMemory;
         }
 
+        (*bitmap)->image.palette->Count = 1 << PIXELFORMATBPP(format);
+
         if (format == PixelFormat1bppIndexed)
         {
-            (*bitmap)->image.palette_flags = PaletteFlagsGrayScale;
-            (*bitmap)->image.palette_entries[0] = 0xff000000;
-            (*bitmap)->image.palette_entries[1] = 0xffffffff;
+            (*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;
+                (*bitmap)->image.palette->Flags = PaletteFlagsHalftone;
 
-            generate_halftone_palette((*bitmap)->image.palette_entries,
-                (*bitmap)->image.palette_count);
+            generate_halftone_palette((*bitmap)->image.palette->Entries,
+                (*bitmap)->image.palette->Count);
         }
     }
 
@@ -1964,19 +2126,21 @@ GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
  * and free src. */
 static void move_bitmap(GpBitmap *dst, GpBitmap *src, BOOL clobber_palette)
 {
+    assert(src->image.type == ImageTypeBitmap);
+    assert(dst->image.type == ImageTypeBitmap);
+
     GdipFree(dst->bitmapbits);
+    GdipFree(dst->own_bits);
     DeleteDC(dst->hdc);
     DeleteObject(dst->hbitmap);
 
     if (clobber_palette)
     {
-        GdipFree(dst->image.palette_entries);
-        dst->image.palette_flags = src->image.palette_flags;
-        dst->image.palette_count = src->image.palette_count;
-        dst->image.palette_entries = src->image.palette_entries;
+        GdipFree(dst->image.palette);
+        dst->image.palette = src->image.palette;
     }
     else
-        GdipFree(src->image.palette_entries);
+        GdipFree(src->image.palette);
 
     dst->image.xres = src->image.xres;
     dst->image.yres = src->image.yres;
@@ -1988,14 +2152,25 @@ static void move_bitmap(GpBitmap *dst, GpBitmap *src, BOOL clobber_palette)
     dst->bits = src->bits;
     dst->stride = src->stride;
     dst->own_bits = src->own_bits;
-
+    if (dst->metadata_reader)
+        IWICMetadataReader_Release(dst->metadata_reader);
+    dst->metadata_reader = src->metadata_reader;
+    GdipFree(dst->prop_item);
+    dst->prop_item = src->prop_item;
+    dst->prop_count = src->prop_count;
+    if (dst->image.stream)
+        IStream_Release(dst->image.stream);
+    dst->image.stream = src->image.stream;
+    dst->image.frame_count = src->image.frame_count;
+    dst->image.current_frame = src->image.current_frame;
+    dst->image.format = src->image.format;
+
+    src->image.type = ~0;
     GdipFree(src);
 }
 
-GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image)
+static GpStatus free_image_data(GpImage *image)
 {
-    TRACE("%p\n", image);
-
     if(!image)
         return InvalidParameter;
 
@@ -2005,13 +2180,17 @@ GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image)
         GdipFree(((GpBitmap*)image)->own_bits);
         DeleteDC(((GpBitmap*)image)->hdc);
         DeleteObject(((GpBitmap*)image)->hbitmap);
+        if (((GpBitmap*)image)->metadata_reader)
+            IWICMetadataReader_Release(((GpBitmap*)image)->metadata_reader);
+        GdipFree(((GpBitmap*)image)->prop_item);
     }
     else if (image->type == ImageTypeMetafile)
     {
         GpMetafile *metafile = (GpMetafile*)image;
         GdipFree(metafile->comment_data);
         DeleteEnhMetaFile(CloseEnhMetaFile(metafile->record_dc));
-        DeleteEnhMetaFile(metafile->hemf);
+        if (!metafile->preserve_hemf)
+            DeleteEnhMetaFile(metafile->hemf);
         if (metafile->record_graphics)
         {
             WARN("metafile closed while recording\n");
@@ -2027,7 +2206,21 @@ GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image)
     }
     if (image->picture)
         IPicture_Release(image->picture);
-    GdipFree(image->palette_entries);
+    if (image->stream)
+        IStream_Release(image->stream);
+    GdipFree(image->palette);
+
+    return Ok;
+}
+
+GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image)
+{
+    GpStatus status;
+
+    TRACE("%p\n", image);
+
+    status = free_image_data(image);
+    if (status != Ok) return status;
     image->type = ~0;
     GdipFree(image);
 
@@ -2100,18 +2293,9 @@ GpStatus WINGDIPAPI GdipGetImageDimension(GpImage *image, REAL *width,
         return InvalidParameter;
 
     if(image->type == ImageTypeMetafile){
-        HDC hdc = GetDC(0);
-        REAL res = (REAL)GetDeviceCaps(hdc, LOGPIXELSX);
-
-        ReleaseDC(0, hdc);
-
-        *height = convert_unit(res, ((GpMetafile*)image)->unit) *
-                        ((GpMetafile*)image)->bounds.Height;
-
-        *width = convert_unit(res, ((GpMetafile*)image)->unit) *
-                        ((GpMetafile*)image)->bounds.Width;
+        *height = units_to_pixels(((GpMetafile*)image)->bounds.Height, ((GpMetafile*)image)->unit, image->yres);
+        *width = units_to_pixels(((GpMetafile*)image)->bounds.Width, ((GpMetafile*)image)->unit, image->xres);
     }
-
     else if(image->type == ImageTypeBitmap){
         *height = ((GpBitmap*)image)->height;
         *width = ((GpBitmap*)image)->width;
@@ -2149,7 +2333,11 @@ GpStatus WINGDIPAPI GdipGetImageGraphicsContext(GpImage *image,
         stat = GdipCreateFromHDC(hdc, graphics);
 
         if (stat == Ok)
+        {
             (*graphics)->image = image;
+            (*graphics)->xres = image->xres;
+            (*graphics)->yres = image->yres;
+        }
     }
     else if (image->type == ImageTypeMetafile)
         stat = METAFILE_GetGraphicsContext((GpMetafile*)image, graphics);
@@ -2166,15 +2354,8 @@ GpStatus WINGDIPAPI GdipGetImageHeight(GpImage *image, UINT *height)
     if(!image || !height)
         return InvalidParameter;
 
-    if(image->type == ImageTypeMetafile){
-        HDC hdc = GetDC(0);
-        REAL res = (REAL)GetDeviceCaps(hdc, LOGPIXELSX);
-
-        ReleaseDC(0, hdc);
-
-        *height = roundr(convert_unit(res, ((GpMetafile*)image)->unit) *
-                        ((GpMetafile*)image)->bounds.Height);
-    }
+    if(image->type == ImageTypeMetafile)
+        *height = units_to_pixels(((GpMetafile*)image)->bounds.Height, ((GpMetafile*)image)->unit, image->yres);
     else if(image->type == ImageTypeBitmap)
         *height = ((GpBitmap*)image)->height;
     else
@@ -2204,10 +2385,10 @@ GpStatus WINGDIPAPI GdipGetImagePaletteSize(GpImage *image, INT *size)
     if(!image || !size)
         return InvalidParameter;
 
-    if (image->palette_count == 0)
+    if (!image->palette || image->palette->Count == 0)
         *size = sizeof(ColorPalette);
     else
-        *size = sizeof(UINT)*2 + sizeof(ARGB)*image->palette_count;
+        *size = sizeof(UINT)*2 + sizeof(ARGB)*image->palette->Count;
 
     TRACE("<-- %u\n", *size);
 
@@ -2273,15 +2454,8 @@ GpStatus WINGDIPAPI GdipGetImageWidth(GpImage *image, UINT *width)
     if(!image || !width)
         return InvalidParameter;
 
-    if(image->type == ImageTypeMetafile){
-        HDC hdc = GetDC(0);
-        REAL res = (REAL)GetDeviceCaps(hdc, LOGPIXELSX);
-
-        ReleaseDC(0, hdc);
-
-        *width = roundr(convert_unit(res, ((GpMetafile*)image)->unit) *
-                        ((GpMetafile*)image)->bounds.Width);
-    }
+    if(image->type == ImageTypeMetafile)
+        *width = units_to_pixels(((GpMetafile*)image)->bounds.Width, ((GpMetafile*)image)->unit, image->xres);
     else if(image->type == ImageTypeBitmap)
         *width = ((GpBitmap*)image)->width;
     else
@@ -2362,172 +2536,566 @@ GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
     return Ok;
 }
 
-GpStatus WINGDIPAPI GdipGetAllPropertyItems(GpImage *image, UINT size,
-    UINT num, PropertyItem* items)
+GpStatus WINGDIPAPI GdipGetPropertyCount(GpImage *image, UINT *num)
 {
-    static int calls;
-
-    TRACE("(%p, %u, %u, %p)\n", image, size, num, items);
-
-    if(!(calls++))
-        FIXME("not implemented\n");
+    TRACE("(%p, %p)\n", image, num);
 
-    return InvalidParameter;
-}
+    if (!image || !num) return InvalidParameter;
 
-GpStatus WINGDIPAPI GdipGetPropertyCount(GpImage *image, UINT* num)
-{
-    static int calls;
+    *num = 0;
 
-    TRACE("(%p, %p)\n", image, num);
+    if (image->type == ImageTypeBitmap)
+    {
+        if (((GpBitmap *)image)->prop_item)
+        {
+            *num = ((GpBitmap *)image)->prop_count;
+            return Ok;
+        }
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+        if (((GpBitmap *)image)->metadata_reader)
+            IWICMetadataReader_GetCount(((GpBitmap *)image)->metadata_reader, num);
+    }
 
-    *num = 0;
     return Ok;
 }
 
-GpStatus WINGDIPAPI GdipGetPropertyIdList(GpImage *image, UINT num, PROPIDlist)
+GpStatus WINGDIPAPI GdipGetPropertyIdList(GpImage *image, UINT num, PROPID *list)
 {
-    static int calls;
+    HRESULT hr;
+    IWICMetadataReader *reader;
+    IWICEnumMetadataItem *enumerator;
+    UINT prop_count, i, items_returned;
 
     TRACE("(%p, %u, %p)\n", image, num, list);
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+    if (!image || !list) return InvalidParameter;
 
-    return InvalidParameter;
-}
+    if (image->type != ImageTypeBitmap)
+    {
+        FIXME("Not implemented for type %d\n", image->type);
+        return NotImplemented;
+    }
 
-GpStatus WINGDIPAPI GdipGetPropertyItem(GpImage *image, PROPID id, UINT size,
-    PropertyItem* buffer)
-{
-    static int calls;
+    if (((GpBitmap *)image)->prop_item)
+    {
+        if (num != ((GpBitmap *)image)->prop_count) return InvalidParameter;
 
-    TRACE("(%p, %u, %u, %p)\n", image, id, size, buffer);
+        for (i = 0; i < num; i++)
+        {
+            list[i] = ((GpBitmap *)image)->prop_item[i].id;
+        }
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+        return Ok;
+    }
 
-    return InvalidParameter;
-}
+    reader = ((GpBitmap *)image)->metadata_reader;
+    if (!reader)
+    {
+        if (num != 0) return InvalidParameter;
+        return Ok;
+    }
 
-GpStatus WINGDIPAPI GdipGetPropertyItemSize(GpImage *image, PROPID pid,
-    UINT* size)
-{
-    static int calls;
+    hr = IWICMetadataReader_GetCount(reader, &prop_count);
+    if (FAILED(hr)) return hresult_to_status(hr);
 
-    TRACE("%p %x %p\n", image, pid, size);
+    if (num != prop_count) return InvalidParameter;
 
-    if(!size || !image)
-        return InvalidParameter;
+    hr = IWICMetadataReader_GetEnumerator(reader, &enumerator);
+    if (FAILED(hr)) return hresult_to_status(hr);
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+    IWICEnumMetadataItem_Reset(enumerator);
 
-    return NotImplemented;
-}
+    for (i = 0; i < num; i++)
+    {
+        PROPVARIANT id;
 
-GpStatus WINGDIPAPI GdipGetPropertySize(GpImage *image, UINT* size, UINT* num)
-{
-    static int calls;
+        hr = IWICEnumMetadataItem_Next(enumerator, 1, NULL, &id, NULL, &items_returned);
+        if (hr != S_OK) break;
 
-    TRACE("(%p,%p,%p)\n", image, size, num);
+        if (id.vt != VT_UI2)
+        {
+            FIXME("not supported propvariant type for id: %u\n", id.vt);
+            list[i] = 0;
+            continue;
+        }
+        list[i] = id.u.uiVal;
+    }
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+    IWICEnumMetadataItem_Release(enumerator);
 
-    return InvalidParameter;
+    return hr == S_OK ? Ok : hresult_to_status(hr);
 }
 
-struct image_format_dimension
+static UINT propvariant_size(PROPVARIANT *value)
 {
-    const GUID *format;
-    const GUID *dimension;
-};
+    switch (value->vt & ~VT_VECTOR)
+    {
+    case VT_EMPTY:
+        return 0;
+    case VT_I1:
+    case VT_UI1:
+        if (!(value->vt & VT_VECTOR)) return 1;
+        return value->u.caub.cElems;
+    case VT_I2:
+    case VT_UI2:
+        if (!(value->vt & VT_VECTOR)) return 2;
+        return value->u.caui.cElems * 2;
+    case VT_I4:
+    case VT_UI4:
+    case VT_R4:
+        if (!(value->vt & VT_VECTOR)) return 4;
+        return value->u.caul.cElems * 4;
+    case VT_I8:
+    case VT_UI8:
+    case VT_R8:
+        if (!(value->vt & VT_VECTOR)) return 8;
+        return value->u.cauh.cElems * 8;
+    case VT_LPSTR:
+        return value->u.pszVal ? strlen(value->u.pszVal) + 1 : 0;
+    case VT_BLOB:
+        return value->u.blob.cbSize;
+    default:
+        FIXME("not supported variant type %d\n", value->vt);
+        return 0;
+    }
+}
 
-static struct image_format_dimension image_format_dimensions[] =
+GpStatus WINGDIPAPI GdipGetPropertyItemSize(GpImage *image, PROPID propid, UINT *size)
 {
-    {&ImageFormatGIF, &FrameDimensionTime},
-    {&ImageFormatIcon, &FrameDimensionResolution},
-    {NULL}
-};
+    HRESULT hr;
+    IWICMetadataReader *reader;
+    PROPVARIANT id, value;
 
-/* FIXME: Need to handle multi-framed images */
-GpStatus WINGDIPAPI GdipImageGetFrameCount(GpImage *image,
-    GDIPCONST GUID* dimensionID, UINT* count)
-{
-    static int calls;
+    TRACE("(%p,%#x,%p)\n", image, propid, size);
 
-    TRACE("(%p,%s,%p)\n", image, debugstr_guid(dimensionID), count);
+    if (!size || !image) return InvalidParameter;
 
-    if(!image || !count)
-        return InvalidParameter;
+    if (image->type != ImageTypeBitmap)
+    {
+        FIXME("Not implemented for type %d\n", image->type);
+        return NotImplemented;
+    }
 
-    if(!(calls++))
-        FIXME("returning frame count of 1\n");
+    if (((GpBitmap *)image)->prop_item)
+    {
+        UINT i;
 
-    *count = 1;
+        for (i = 0; i < ((GpBitmap *)image)->prop_count; i++)
+        {
+            if (propid == ((GpBitmap *)image)->prop_item[i].id)
+            {
+                *size = sizeof(PropertyItem) + ((GpBitmap *)image)->prop_item[i].length;
+                return Ok;
+            }
+        }
+
+        return PropertyNotFound;
+    }
+
+    reader = ((GpBitmap *)image)->metadata_reader;
+    if (!reader) return PropertyNotFound;
+
+    id.vt = VT_UI2;
+    id.u.uiVal = propid;
+    hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
+    if (FAILED(hr)) return PropertyNotFound;
+
+    *size = propvariant_size(&value);
+    if (*size) *size += sizeof(PropertyItem);
+    PropVariantClear(&value);
 
     return Ok;
 }
 
-GpStatus WINGDIPAPI GdipImageGetFrameDimensionsCount(GpImage *image,
-    UINT* count)
+#ifndef PropertyTagTypeSByte
+#define PropertyTagTypeSByte  6
+#define PropertyTagTypeSShort 8
+#define PropertyTagTypeFloat  11
+#define PropertyTagTypeDouble 12
+#endif
+
+static UINT vt_to_itemtype(UINT vt)
 {
-    TRACE("(%p, %p)\n", image, count);
+    static const struct
+    {
+        UINT vt, type;
+    } vt2type[] =
+    {
+        { VT_I1, PropertyTagTypeSByte },
+        { VT_UI1, PropertyTagTypeByte },
+        { VT_I2, PropertyTagTypeSShort },
+        { VT_UI2, PropertyTagTypeShort },
+        { VT_I4, PropertyTagTypeSLONG },
+        { VT_UI4, PropertyTagTypeLong },
+        { VT_I8, PropertyTagTypeSRational },
+        { VT_UI8, PropertyTagTypeRational },
+        { VT_R4, PropertyTagTypeFloat },
+        { VT_R8, PropertyTagTypeDouble },
+        { VT_LPSTR, PropertyTagTypeASCII },
+        { VT_BLOB, PropertyTagTypeUndefined }
+    };
+    UINT i;
+    for (i = 0; i < sizeof(vt2type)/sizeof(vt2type[0]); i++)
+    {
+        if (vt2type[i].vt == vt) return vt2type[i].type;
+    }
+    FIXME("not supported variant type %u\n", vt);
+    return 0;
+}
 
-    /* Native gdiplus 1.1 does not yet support multiple frame dimensions. */
+static GpStatus propvariant_to_item(PROPVARIANT *value, PropertyItem *item,
+                                    UINT size, PROPID id)
+{
+    UINT item_size, item_type;
 
-    if(!image || !count)
+    item_size = propvariant_size(value);
+    if (size != item_size + sizeof(PropertyItem)) return InvalidParameter;
+
+    item_type = vt_to_itemtype(value->vt & ~VT_VECTOR);
+    if (!item_type) return InvalidParameter;
+
+    item->value = item + 1;
+
+    switch (value->vt & ~VT_VECTOR)
+    {
+    case VT_I1:
+    case VT_UI1:
+        if (!(value->vt & VT_VECTOR))
+            *(BYTE *)item->value = value->u.bVal;
+        else
+            memcpy(item->value, value->u.caub.pElems, item_size);
+        break;
+    case VT_I2:
+    case VT_UI2:
+        if (!(value->vt & VT_VECTOR))
+            *(USHORT *)item->value = value->u.uiVal;
+        else
+            memcpy(item->value, value->u.caui.pElems, item_size);
+        break;
+    case VT_I4:
+    case VT_UI4:
+    case VT_R4:
+        if (!(value->vt & VT_VECTOR))
+            *(ULONG *)item->value = value->u.ulVal;
+        else
+            memcpy(item->value, value->u.caul.pElems, item_size);
+        break;
+    case VT_I8:
+    case VT_UI8:
+    case VT_R8:
+        if (!(value->vt & VT_VECTOR))
+            *(ULONGLONG *)item->value = value->u.uhVal.QuadPart;
+        else
+            memcpy(item->value, value->u.cauh.pElems, item_size);
+        break;
+    case VT_LPSTR:
+        memcpy(item->value, value->u.pszVal, item_size);
+        break;
+    case VT_BLOB:
+        memcpy(item->value, value->u.blob.pBlobData, item_size);
+        break;
+    default:
+        FIXME("not supported variant type %d\n", value->vt);
         return InvalidParameter;
+    }
 
-    *count = 1;
+    item->length = item_size;
+    item->type = item_type;
+    item->id = id;
 
     return Ok;
 }
 
-GpStatus WINGDIPAPI GdipImageGetFrameDimensionsList(GpImage* image,
-    GUID* dimensionIDs, UINT count)
+GpStatus WINGDIPAPI GdipGetPropertyItem(GpImage *image, PROPID propid, UINT size,
+                                        PropertyItem *buffer)
 {
-    int i;
-    const GUID *result=NULL;
+    GpStatus stat;
+    HRESULT hr;
+    IWICMetadataReader *reader;
+    PROPVARIANT id, value;
 
-    TRACE("(%p,%p,%u)\n", image, dimensionIDs, count);
+    TRACE("(%p,%#x,%u,%p)\n", image, propid, size, buffer);
 
-    if(!image || !dimensionIDs || count != 1)
-        return InvalidParameter;
+    if (!image || !buffer) return InvalidParameter;
 
-    for (i=0; image_format_dimensions[i].format; i++)
+    if (image->type != ImageTypeBitmap)
     {
-        if (IsEqualGUID(&image->format, image_format_dimensions[i].format))
+        FIXME("Not implemented for type %d\n", image->type);
+        return NotImplemented;
+    }
+
+    if (((GpBitmap *)image)->prop_item)
+    {
+        UINT i;
+
+        for (i = 0; i < ((GpBitmap *)image)->prop_count; i++)
         {
-            result = image_format_dimensions[i].dimension;
-            break;
+            if (propid == ((GpBitmap *)image)->prop_item[i].id)
+            {
+                if (size != sizeof(PropertyItem) + ((GpBitmap *)image)->prop_item[i].length)
+                    return InvalidParameter;
+
+                *buffer = ((GpBitmap *)image)->prop_item[i];
+                buffer->value = buffer + 1;
+                memcpy(buffer->value, ((GpBitmap *)image)->prop_item[i].value, buffer->length);
+                return Ok;
+            }
         }
+
+        return PropertyNotFound;
     }
 
-    if (!result)
-        result = &FrameDimensionPage;
+    reader = ((GpBitmap *)image)->metadata_reader;
+    if (!reader) return PropertyNotFound;
 
-    memcpy(dimensionIDs, result, sizeof(GUID));
+    id.vt = VT_UI2;
+    id.u.uiVal = propid;
+    hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
+    if (FAILED(hr)) return PropertyNotFound;
 
-    return Ok;
+    stat = propvariant_to_item(&value, buffer, size, propid);
+    PropVariantClear(&value);
+
+    return stat;
 }
 
-GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image,
-    GDIPCONST GUID* dimensionID, UINT frameidx)
+GpStatus WINGDIPAPI GdipGetPropertySize(GpImage *image, UINT *size, UINT *count)
 {
-    static int calls;
+    HRESULT hr;
+    IWICMetadataReader *reader;
+    IWICEnumMetadataItem *enumerator;
+    UINT prop_count, prop_size, i;
+    PROPVARIANT id, value;
 
-    TRACE("(%p, %s, %u)\n", image, debugstr_guid(dimensionID), frameidx);
+    TRACE("(%p,%p,%p)\n", image, size, count);
 
-    if(!image || !dimensionID)
-        return InvalidParameter;
+    if (!image || !size || !count) return InvalidParameter;
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+    if (image->type != ImageTypeBitmap)
+    {
+        FIXME("Not implemented for type %d\n", image->type);
+        return NotImplemented;
+    }
+
+    if (((GpBitmap *)image)->prop_item)
+    {
+        *count = ((GpBitmap *)image)->prop_count;
+        *size = 0;
+
+        for (i = 0; i < ((GpBitmap *)image)->prop_count; i++)
+        {
+            *size += sizeof(PropertyItem) + ((GpBitmap *)image)->prop_item[i].length;
+        }
+
+        return Ok;
+    }
+
+    reader = ((GpBitmap *)image)->metadata_reader;
+    if (!reader) return PropertyNotFound;
+
+    hr = IWICMetadataReader_GetCount(reader, &prop_count);
+    if (FAILED(hr)) return hresult_to_status(hr);
+
+    hr = IWICMetadataReader_GetEnumerator(reader, &enumerator);
+    if (FAILED(hr)) return hresult_to_status(hr);
+
+    IWICEnumMetadataItem_Reset(enumerator);
+
+    prop_size = 0;
+
+    PropVariantInit(&id);
+    PropVariantInit(&value);
+
+    for (i = 0; i < prop_count; i++)
+    {
+        UINT items_returned, item_size;
+
+        hr = IWICEnumMetadataItem_Next(enumerator, 1, NULL, &id, &value, &items_returned);
+        if (hr != S_OK) break;
+
+        item_size = propvariant_size(&value);
+        if (item_size) prop_size += sizeof(PropertyItem) + item_size;
+
+        PropVariantClear(&id);
+        PropVariantClear(&value);
+    }
+
+    IWICEnumMetadataItem_Release(enumerator);
+
+    if (hr != S_OK) return PropertyNotFound;
+
+    *count = prop_count;
+    *size = prop_size;
+    return Ok;
+}
+
+GpStatus WINGDIPAPI GdipGetAllPropertyItems(GpImage *image, UINT size,
+                                            UINT count, PropertyItem *buf)
+{
+    GpStatus status;
+    HRESULT hr;
+    IWICMetadataReader *reader;
+    IWICEnumMetadataItem *enumerator;
+    UINT prop_count, prop_size, i;
+    PROPVARIANT id, value;
+    char *item_value;
+
+    TRACE("(%p,%u,%u,%p)\n", image, size, count, buf);
+
+    if (!image || !buf) return InvalidParameter;
+
+    if (image->type != ImageTypeBitmap)
+    {
+        FIXME("Not implemented for type %d\n", image->type);
+        return NotImplemented;
+    }
+
+    status = GdipGetPropertySize(image, &prop_size, &prop_count);
+    if (status != Ok) return status;
+
+    if (prop_count != count || prop_size != size) return InvalidParameter;
+
+    if (((GpBitmap *)image)->prop_item)
+    {
+        memcpy(buf, ((GpBitmap *)image)->prop_item, prop_size);
+
+        item_value = (char *)(buf + prop_count);
+
+        for (i = 0; i < prop_count; i++)
+        {
+            buf[i].value = item_value;
+            item_value += buf[i].length;
+        }
+
+        return Ok;
+    }
+
+    reader = ((GpBitmap *)image)->metadata_reader;
+    if (!reader) return PropertyNotFound;
+
+    hr = IWICMetadataReader_GetEnumerator(reader, &enumerator);
+    if (FAILED(hr)) return hresult_to_status(hr);
+
+    IWICEnumMetadataItem_Reset(enumerator);
+
+    item_value = (char *)(buf + prop_count);
+
+    PropVariantInit(&id);
+    PropVariantInit(&value);
+
+    for (i = 0; i < prop_count; i++)
+    {
+        PropertyItem *item;
+        UINT items_returned, item_size;
+
+        hr = IWICEnumMetadataItem_Next(enumerator, 1, NULL, &id, &value, &items_returned);
+        if (hr != S_OK) break;
+
+        if (id.vt != VT_UI2)
+        {
+            FIXME("not supported propvariant type for id: %u\n", id.vt);
+            continue;
+        }
+
+        item_size = propvariant_size(&value);
+        if (item_size)
+        {
+            item = HeapAlloc(GetProcessHeap(), 0, item_size + sizeof(*item));
+
+            propvariant_to_item(&value, item, item_size + sizeof(*item), id.u.uiVal);
+            buf[i].id = item->id;
+            buf[i].type = item->type;
+            buf[i].length = item_size;
+            buf[i].value = item_value;
+            memcpy(item_value, item->value, item_size);
+            item_value += item_size;
+
+            HeapFree(GetProcessHeap(), 0, item);
+        }
+
+        PropVariantClear(&id);
+        PropVariantClear(&value);
+    }
+
+    IWICEnumMetadataItem_Release(enumerator);
+
+    if (hr != S_OK) return PropertyNotFound;
+
+    return Ok;
+}
+
+struct image_format_dimension
+{
+    const GUID *format;
+    const GUID *dimension;
+};
+
+static const struct image_format_dimension image_format_dimensions[] =
+{
+    {&ImageFormatGIF, &FrameDimensionTime},
+    {&ImageFormatIcon, &FrameDimensionResolution},
+    {NULL}
+};
+
+GpStatus WINGDIPAPI GdipImageGetFrameCount(GpImage *image,
+    GDIPCONST GUID* dimensionID, UINT* count)
+{
+    TRACE("(%p,%s,%p)\n", image, debugstr_guid(dimensionID), count);
+
+    if(!image || !count)
+        return InvalidParameter;
+
+    if (!dimensionID ||
+        IsEqualGUID(dimensionID, &image->format) ||
+        IsEqualGUID(dimensionID, &FrameDimensionPage) ||
+        IsEqualGUID(dimensionID, &FrameDimensionTime))
+    {
+        *count = image->frame_count;
+        return Ok;
+    }
+
+    return InvalidParameter;
+}
+
+GpStatus WINGDIPAPI GdipImageGetFrameDimensionsCount(GpImage *image,
+    UINT* count)
+{
+    TRACE("(%p, %p)\n", image, count);
+
+    /* Native gdiplus 1.1 does not yet support multiple frame dimensions. */
+
+    if(!image || !count)
+        return InvalidParameter;
+
+    *count = 1;
+
+    return Ok;
+}
+
+GpStatus WINGDIPAPI GdipImageGetFrameDimensionsList(GpImage* image,
+    GUID* dimensionIDs, UINT count)
+{
+    int i;
+    const GUID *result=NULL;
+
+    TRACE("(%p,%p,%u)\n", image, dimensionIDs, count);
+
+    if(!image || !dimensionIDs || count != 1)
+        return InvalidParameter;
+
+    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;
 }
@@ -2563,24 +3131,419 @@ GpStatus WINGDIPAPI GdipLoadImageFromFileICM(GDIPCONST WCHAR* filename,GpImage *
     return GdipLoadImageFromFile(filename, image);
 }
 
-static const WICPixelFormatGUID *wic_pixel_formats[] = {
-    &GUID_WICPixelFormat16bppBGR555,
-    &GUID_WICPixelFormat24bppBGR,
-    &GUID_WICPixelFormat32bppBGR,
-    &GUID_WICPixelFormat32bppBGRA,
-    &GUID_WICPixelFormat32bppPBGRA,
-    NULL
-};
+static void add_property(GpBitmap *bitmap, PropertyItem *item)
+{
+    UINT prop_size, prop_count;
+    PropertyItem *prop_item;
 
-static const PixelFormat wic_gdip_formats[] = {
-    PixelFormat16bppRGB555,
-    PixelFormat24bppRGB,
-    PixelFormat32bppRGB,
-    PixelFormat32bppARGB,
-    PixelFormat32bppPARGB,
-};
+    if (bitmap->prop_item == NULL)
+    {
+        prop_size = prop_count = 0;
+        prop_item = GdipAlloc(item->length + sizeof(PropertyItem));
+        if (!prop_item) return;
+    }
+    else
+    {
+        UINT i;
+        char *item_value;
+
+        GdipGetPropertySize((GpImage *)bitmap, &prop_size, &prop_count);
+
+        prop_item = GdipAlloc(prop_size + item->length + sizeof(PropertyItem));
+        if (!prop_item) return;
+        memcpy(prop_item, bitmap->prop_item, sizeof(PropertyItem) * bitmap->prop_count);
+        prop_size -= sizeof(PropertyItem) * bitmap->prop_count;
+        memcpy(prop_item + prop_count + 1, bitmap->prop_item + prop_count, prop_size);
+
+        item_value = (char *)(prop_item + prop_count + 1);
+
+        for (i = 0; i < prop_count; i++)
+        {
+            prop_item[i].value = item_value;
+            item_value += prop_item[i].length;
+        }
+    }
+
+    prop_item[prop_count].id = item->id;
+    prop_item[prop_count].type = item->type;
+    prop_item[prop_count].length = item->length;
+    prop_item[prop_count].value = (char *)(prop_item + prop_count + 1) + prop_size;
+    memcpy(prop_item[prop_count].value, item->value, item->length);
 
-static GpStatus decode_image_wic(IStream* stream, REFCLSID clsid, GpImage **image)
+    GdipFree(bitmap->prop_item);
+    bitmap->prop_item = prop_item;
+    bitmap->prop_count++;
+}
+
+static BOOL get_bool_property(IWICMetadataReader *reader, const GUID *guid, const WCHAR *prop_name)
+{
+    HRESULT hr;
+    GUID format;
+    PROPVARIANT id, value;
+    BOOL ret = FALSE;
+
+    IWICMetadataReader_GetMetadataFormat(reader, &format);
+    if (!IsEqualGUID(&format, guid)) return FALSE;
+
+    PropVariantInit(&id);
+    PropVariantInit(&value);
+
+    id.vt = VT_LPWSTR;
+    id.u.pwszVal = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(prop_name) + 1) * sizeof(WCHAR));
+    if (!id.u.pwszVal) return FALSE;
+    lstrcpyW(id.u.pwszVal, prop_name);
+    hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
+    if (hr == S_OK && value.vt == VT_BOOL)
+        ret = value.u.boolVal;
+
+    PropVariantClear(&id);
+    PropVariantClear(&value);
+
+    return ret;
+}
+
+static PropertyItem *get_property(IWICMetadataReader *reader, const GUID *guid, const WCHAR *prop_name)
+{
+    HRESULT hr;
+    GUID format;
+    PROPVARIANT id, value;
+    PropertyItem *item = NULL;
+
+    IWICMetadataReader_GetMetadataFormat(reader, &format);
+    if (!IsEqualGUID(&format, guid)) return NULL;
+
+    PropVariantInit(&id);
+    PropVariantInit(&value);
+
+    id.vt = VT_LPWSTR;
+    id.u.pwszVal = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(prop_name) + 1) * sizeof(WCHAR));
+    if (!id.u.pwszVal) return NULL;
+    lstrcpyW(id.u.pwszVal, prop_name);
+    hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value);
+    if (hr == S_OK)
+    {
+        UINT item_size = propvariant_size(&value);
+        if (item_size)
+        {
+            item_size += sizeof(*item);
+            item = GdipAlloc(item_size);
+            if (propvariant_to_item(&value, item, item_size, 0) != Ok)
+            {
+                GdipFree(item);
+                item = NULL;
+            }
+        }
+    }
+
+    PropVariantClear(&id);
+    PropVariantClear(&value);
+
+    return item;
+}
+
+static PropertyItem *get_gif_comment(IWICMetadataReader *reader)
+{
+    static const WCHAR textentryW[] = { 'T','e','x','t','E','n','t','r','y',0 };
+    PropertyItem *comment;
+
+    comment = get_property(reader, &GUID_MetadataFormatGifComment, textentryW);
+    if (comment)
+        comment->id = PropertyTagExifUserComment;
+
+    return comment;
+}
+
+static PropertyItem *get_gif_loopcount(IWICMetadataReader *reader)
+{
+    static const WCHAR applicationW[] = { 'A','p','p','l','i','c','a','t','i','o','n',0 };
+    static const WCHAR dataW[] = { 'D','a','t','a',0 };
+    PropertyItem *appext = NULL, *appdata = NULL, *loop = NULL;
+
+    appext = get_property(reader, &GUID_MetadataFormatAPE, applicationW);
+    if (appext)
+    {
+        if (appext->type == PropertyTagTypeByte && appext->length == 11 &&
+            (!memcmp(appext->value, "NETSCAPE2.0", 11) || !memcmp(appext->value, "ANIMEXTS1.0", 11)))
+        {
+            appdata = get_property(reader, &GUID_MetadataFormatAPE, dataW);
+            if (appdata)
+            {
+                if (appdata->type == PropertyTagTypeByte && appdata->length == 4)
+                {
+                    BYTE *data = appdata->value;
+                    if (data[0] == 3 && data[1] == 1)
+                    {
+                        loop = GdipAlloc(sizeof(*loop) + sizeof(SHORT));
+                        if (loop)
+                        {
+                            loop->type = PropertyTagTypeShort;
+                            loop->id = PropertyTagLoopCount;
+                            loop->length = sizeof(SHORT);
+                            loop->value = loop + 1;
+                            *(SHORT *)loop->value = data[2] | (data[3] << 8);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    GdipFree(appext);
+    GdipFree(appdata);
+
+    return loop;
+}
+
+static PropertyItem *get_gif_background(IWICMetadataReader *reader)
+{
+    static const WCHAR backgroundW[] = { 'B','a','c','k','g','r','o','u','n','d','C','o','l','o','r','I','n','d','e','x',0 };
+    PropertyItem *background;
+
+    background = get_property(reader, &GUID_MetadataFormatLSD, backgroundW);
+    if (background)
+        background->id = PropertyTagIndexBackground;
+
+    return background;
+}
+
+static PropertyItem *get_gif_palette(IWICBitmapDecoder *decoder, IWICMetadataReader *reader)
+{
+    static const WCHAR global_flagW[] = { 'G','l','o','b','a','l','C','o','l','o','r','T','a','b','l','e','F','l','a','g',0 };
+    HRESULT hr;
+    IWICImagingFactory *factory;
+    IWICPalette *palette;
+    UINT count = 0;
+    WICColor colors[256];
+
+    if (!get_bool_property(reader, &GUID_MetadataFormatLSD, global_flagW))
+        return NULL;
+
+    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_IWICImagingFactory, (void **)&factory);
+    if (hr != S_OK) return NULL;
+
+    hr = IWICImagingFactory_CreatePalette(factory, &palette);
+    if (hr == S_OK)
+    {
+        hr = IWICBitmapDecoder_CopyPalette(decoder, palette);
+        if (hr == S_OK)
+            IWICPalette_GetColors(palette, 256, colors, &count);
+
+        IWICPalette_Release(palette);
+    }
+
+    IWICImagingFactory_Release(factory);
+
+    if (count)
+    {
+        PropertyItem *pal;
+        UINT i;
+        BYTE *rgb;
+
+        pal = GdipAlloc(sizeof(*pal) + count * 3);
+        if (!pal) return NULL;
+        pal->type = PropertyTagTypeByte;
+        pal->id = PropertyTagGlobalPalette;
+        pal->value = pal + 1;
+        pal->length = count * 3;
+
+        rgb = pal->value;
+
+        for (i = 0; i < count; i++)
+        {
+            rgb[i*3] = (colors[i] >> 16) & 0xff;
+            rgb[i*3 + 1] = (colors[i] >> 8) & 0xff;
+            rgb[i*3 + 2] = colors[i] & 0xff;
+        }
+
+        return pal;
+    }
+
+    return NULL;
+}
+
+static PropertyItem *get_gif_transparent_idx(IWICMetadataReader *reader)
+{
+    static const WCHAR transparency_flagW[] = { 'T','r','a','n','s','p','a','r','e','n','c','y','F','l','a','g',0 };
+    static const WCHAR colorW[] = { 'T','r','a','n','s','p','a','r','e','n','t','C','o','l','o','r','I','n','d','e','x',0 };
+    PropertyItem *index = NULL;
+
+    if (get_bool_property(reader, &GUID_MetadataFormatGCE, transparency_flagW))
+    {
+        index = get_property(reader, &GUID_MetadataFormatGCE, colorW);
+        if (index)
+            index->id = PropertyTagIndexTransparent;
+    }
+    return index;
+}
+
+static LONG get_gif_frame_delay(IWICBitmapFrameDecode *frame)
+{
+    static const WCHAR delayW[] = { 'D','e','l','a','y',0 };
+    HRESULT hr;
+    IWICMetadataBlockReader *block_reader;
+    IWICMetadataReader *reader;
+    UINT block_count, i;
+    PropertyItem *delay;
+    LONG value = 0;
+
+    hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader);
+    if (hr == S_OK)
+    {
+        hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count);
+        if (hr == S_OK)
+        {
+            for (i = 0; i < block_count; i++)
+            {
+                hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
+                if (hr == S_OK)
+                {
+                    delay = get_property(reader, &GUID_MetadataFormatGCE, delayW);
+                    if (delay)
+                    {
+                        if (delay->type == PropertyTagTypeShort && delay->length == 2)
+                            value = *(SHORT *)delay->value;
+
+                        GdipFree(delay);
+                    }
+                    IWICMetadataReader_Release(reader);
+                }
+            }
+        }
+        IWICMetadataBlockReader_Release(block_reader);
+    }
+
+    return value;
+}
+
+static void gif_metadata_reader(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT active_frame)
+{
+    HRESULT hr;
+    IWICBitmapFrameDecode *frame;
+    IWICMetadataBlockReader *block_reader;
+    IWICMetadataReader *reader;
+    UINT frame_count, block_count, i;
+    PropertyItem *delay = NULL, *comment = NULL, *background = NULL;
+    PropertyItem *transparent_idx = NULL, *loop = NULL, *palette = NULL;
+
+    IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
+    if (frame_count > 1)
+    {
+        delay = GdipAlloc(sizeof(*delay) + frame_count * sizeof(LONG));
+        if (delay)
+        {
+            LONG *value;
+
+            delay->type = PropertyTagTypeLong;
+            delay->id = PropertyTagFrameDelay;
+            delay->length = frame_count * sizeof(LONG);
+            delay->value = delay + 1;
+
+            value = delay->value;
+
+            for (i = 0; i < frame_count; i++)
+            {
+                hr = IWICBitmapDecoder_GetFrame(decoder, i, &frame);
+                if (hr == S_OK)
+                {
+                    value[i] = get_gif_frame_delay(frame);
+                    IWICBitmapFrameDecode_Release(frame);
+                }
+                else value[i] = 0;
+            }
+        }
+    }
+
+    hr = IWICBitmapDecoder_QueryInterface(decoder, &IID_IWICMetadataBlockReader, (void **)&block_reader);
+    if (hr == S_OK)
+    {
+        hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count);
+        if (hr == S_OK)
+        {
+            for (i = 0; i < block_count; i++)
+            {
+                hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
+                if (hr == S_OK)
+                {
+                    if (!comment)
+                        comment = get_gif_comment(reader);
+
+                    if (frame_count > 1 && !loop)
+                        loop = get_gif_loopcount(reader);
+
+                    if (!background)
+                        background = get_gif_background(reader);
+
+                    if (!palette)
+                        palette = get_gif_palette(decoder, reader);
+
+                    IWICMetadataReader_Release(reader);
+                }
+            }
+        }
+        IWICMetadataBlockReader_Release(block_reader);
+    }
+
+    if (frame_count > 1 && !loop)
+    {
+        loop = GdipAlloc(sizeof(*loop) + sizeof(SHORT));
+        if (loop)
+        {
+            loop->type = PropertyTagTypeShort;
+            loop->id = PropertyTagLoopCount;
+            loop->length = sizeof(SHORT);
+            loop->value = loop + 1;
+            *(SHORT *)loop->value = 1;
+        }
+    }
+
+    if (delay) add_property(bitmap, delay);
+    if (comment) add_property(bitmap, comment);
+    if (loop) add_property(bitmap, loop);
+    if (palette) add_property(bitmap, palette);
+    if (background) add_property(bitmap, background);
+
+    GdipFree(delay);
+    GdipFree(comment);
+    GdipFree(loop);
+    GdipFree(palette);
+    GdipFree(background);
+
+    /* Win7 gdiplus always returns transparent color index from frame 0 */
+    hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
+    if (hr != S_OK) return;
+
+    hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader);
+    if (hr == S_OK)
+    {
+        hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count);
+        if (hr == S_OK)
+        {
+            for (i = 0; i < block_count; i++)
+            {
+                hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
+                if (hr == S_OK)
+                {
+                    if (!transparent_idx)
+                        transparent_idx = get_gif_transparent_idx(reader);
+
+                    IWICMetadataReader_Release(reader);
+                }
+            }
+        }
+        IWICMetadataBlockReader_Release(block_reader);
+    }
+
+    if (transparent_idx) add_property(bitmap, transparent_idx);
+    GdipFree(transparent_idx);
+
+    IWICBitmapFrameDecode_Release(frame);
+}
+
+typedef void (*metadata_reader_func)(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT frame);
+
+static GpStatus decode_image_wic(IStream *stream, GDIPCONST CLSID *clsid,
+    UINT active_frame, metadata_reader_func metadata_reader, GpImage **image)
 {
     GpStatus status=Ok;
     GpBitmap *bitmap;
@@ -2588,23 +3551,31 @@ static GpStatus decode_image_wic(IStream* stream, REFCLSID clsid, GpImage **imag
     IWICBitmapDecoder *decoder;
     IWICBitmapFrameDecode *frame;
     IWICBitmapSource *source=NULL;
+    IWICMetadataBlockReader *block_reader;
     WICPixelFormatGUID wic_format;
     PixelFormat gdip_format=0;
+    ColorPalette *palette = NULL;
+    WICBitmapPaletteType palette_type = WICBitmapPaletteTypeFixedHalftone256;
     int i;
-    UINT width, height;
+    UINT width, height, frame_count;
     BitmapData lockeddata;
     WICRect wrc;
     HRESULT initresult;
 
+    TRACE("%p,%s,%u,%p\n", stream, wine_dbgstr_guid(clsid), active_frame, image);
+
     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);
+    hr = IWICBitmapDecoder_Initialize(decoder, stream, WICDecodeMetadataCacheOnLoad);
     if (SUCCEEDED(hr))
-        hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
+    {
+        IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
+        hr = IWICBitmapDecoder_GetFrame(decoder, active_frame, &frame);
+    }
 
     if (SUCCEEDED(hr)) /* got frame */
     {
@@ -2612,22 +3583,27 @@ static GpStatus decode_image_wic(IStream* stream, REFCLSID clsid, GpImage **imag
 
         if (SUCCEEDED(hr))
         {
-            for (i=0; wic_pixel_formats[i]; i++)
+            IWICBitmapSource *bmp_source;
+            IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICBitmapSource, (void **)&bmp_source);
+
+            for (i=0; pixel_formats[i].wic_format; i++)
             {
-                if (IsEqualGUID(&wic_format, wic_pixel_formats[i]))
+                if (IsEqualGUID(&wic_format, pixel_formats[i].wic_format))
                 {
-                    source = (IWICBitmapSource*)frame;
-                    IWICBitmapSource_AddRef(source);
-                    gdip_format = wic_gdip_formats[i];
+                    source = bmp_source;
+                    gdip_format = pixel_formats[i].gdip_format;
+                    palette_type = pixel_formats[i].palette_type;
                     break;
                 }
             }
             if (!source)
             {
                 /* unknown format; fall back on 32bppARGB */
-                hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, (IWICBitmapSource*)frame, &source);
+                hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, bmp_source, &source);
                 gdip_format = PixelFormat32bppARGB;
+                IWICBitmapSource_Release(bmp_source);
             }
+            TRACE("%s => %#x\n", wine_dbgstr_guid(&wic_format), gdip_format);
         }
 
         if (SUCCEEDED(hr)) /* got source */
@@ -2682,7 +3658,22 @@ static GpStatus decode_image_wic(IStream* stream, REFCLSID clsid, GpImage **imag
             IWICBitmapSource_Release(source);
         }
 
-        IWICBitmapFrameDecode_Release(frame);
+        if (SUCCEEDED(hr)) {
+            bitmap->metadata_reader = NULL;
+
+            if (metadata_reader)
+                metadata_reader(bitmap, decoder, active_frame);
+            else if (IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader) == S_OK)
+            {
+                UINT block_count = 0;
+                if (IWICMetadataBlockReader_GetCount(block_reader, &block_count) == S_OK && block_count)
+                    IWICMetadataBlockReader_GetReaderByIndex(block_reader, 0, &bitmap->metadata_reader);
+                IWICMetadataBlockReader_Release(block_reader);
+            }
+
+            palette = get_palette(frame, palette_type);
+            IWICBitmapFrameDecode_Release(frame);
+        }
     }
 
     IWICBitmapDecoder_Release(decoder);
@@ -2696,22 +3687,38 @@ end:
     {
         /* Native GDI+ used to be smarter, but since Win7 it just sets these flags. */
         bitmap->image.flags |= ImageFlagsReadOnly|ImageFlagsHasRealPixelSize|ImageFlagsHasRealDPI|ImageFlagsColorSpaceRGB;
+        bitmap->image.frame_count = frame_count;
+        bitmap->image.current_frame = active_frame;
+        bitmap->image.stream = stream;
+        if (palette)
+        {
+            GdipFree(bitmap->image.palette);
+            bitmap->image.palette = palette;
+        }
+        else
+        {
+            if (IsEqualGUID(&wic_format, &GUID_WICPixelFormatBlackWhite))
+                bitmap->image.palette->Flags = 0;
+        }
+        /* Pin the source stream */
+        IStream_AddRef(stream);
+        TRACE("=> %p\n", *image);
     }
 
     return status;
 }
 
-static GpStatus decode_image_icon(IStream* stream, REFCLSID clsid, GpImage **image)
+static GpStatus decode_image_icon(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image)
 {
-    return decode_image_wic(stream, &CLSID_WICIcoDecoder, image);
+    return decode_image_wic(stream, &CLSID_WICIcoDecoder, active_frame, NULL, image);
 }
 
-static GpStatus decode_image_bmp(IStream* stream, REFCLSID clsid, GpImage **image)
+static GpStatus decode_image_bmp(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image)
 {
     GpStatus status;
     GpBitmap* bitmap;
 
-    status = decode_image_wic(stream, &CLSID_WICBmpDecoder, image);
+    status = decode_image_wic(stream, &CLSID_WICBmpDecoder, active_frame, NULL, image);
 
     bitmap = (GpBitmap*)*image;
 
@@ -2724,27 +3731,27 @@ static GpStatus decode_image_bmp(IStream* stream, REFCLSID clsid, GpImage **imag
     return status;
 }
 
-static GpStatus decode_image_jpeg(IStream* stream, REFCLSID clsid, GpImage **image)
+static GpStatus decode_image_jpeg(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image)
 {
-    return decode_image_wic(stream, &CLSID_WICJpegDecoder, image);
+    return decode_image_wic(stream, &CLSID_WICJpegDecoder, active_frame, NULL, image);
 }
 
-static GpStatus decode_image_png(IStream* stream, REFCLSID clsid, GpImage **image)
+static GpStatus decode_image_png(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image)
 {
-    return decode_image_wic(stream, &CLSID_WICPngDecoder, image);
+    return decode_image_wic(stream, &CLSID_WICPngDecoder, active_frame, NULL, image);
 }
 
-static GpStatus decode_image_gif(IStream* stream, REFCLSID clsid, GpImage **image)
+static GpStatus decode_image_gif(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image)
 {
-    return decode_image_wic(stream, &CLSID_WICGifDecoder, image);
+    return decode_image_wic(stream, &CLSID_WICGifDecoder, active_frame, gif_metadata_reader, image);
 }
 
-static GpStatus decode_image_tiff(IStream* stream, REFCLSID clsid, GpImage **image)
+static GpStatus decode_image_tiff(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image)
 {
-    return decode_image_wic(stream, &CLSID_WICTiffDecoder, image);
+    return decode_image_wic(stream, &CLSID_WICTiffDecoder, active_frame, NULL, image);
 }
 
-static GpStatus decode_image_olepicture_metafile(IStream* stream, REFCLSID clsid, GpImage **image)
+static GpStatus decode_image_olepicture_metafile(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image)
 {
     IPicture *pic;
 
@@ -2763,12 +3770,12 @@ static GpStatus decode_image_olepicture_metafile(IStream* stream, REFCLSID clsid
     *image = GdipAlloc(sizeof(GpMetafile));
     if(!*image) return OutOfMemory;
     (*image)->type = ImageTypeMetafile;
+    (*image)->stream = NULL;
     (*image)->picture = pic;
     (*image)->flags   = ImageFlagsNone;
-    (*image)->palette_flags = 0;
-    (*image)->palette_count = 0;
-    (*image)->palette_size = 0;
-    (*image)->palette_entries = NULL;
+    (*image)->frame_count = 1;
+    (*image)->current_frame = 0;
+    (*image)->palette = NULL;
 
     TRACE("<-- %p\n", *image);
 
@@ -2778,7 +3785,7 @@ static GpStatus decode_image_olepicture_metafile(IStream* stream, REFCLSID clsid
 typedef GpStatus (*encode_image_func)(GpImage *image, IStream* stream,
     GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params);
 
-typedef GpStatus (*decode_image_func)(IStream *stream, REFCLSID clsid, GpImage** image);
+typedef GpStatus (*decode_image_func)(IStream *stream, REFCLSID clsid, UINT active_frame, GpImage **image);
 
 typedef struct image_codec {
     ImageCodecInfo info;
@@ -2847,7 +3854,75 @@ static GpStatus get_decoder_info(IStream* stream, const struct image_codec **res
     return GenericError;
 }
 
-GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream* stream, GpImage **image)
+GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image, GDIPCONST GUID *dimensionID,
+                                               UINT frame)
+{
+    GpStatus stat;
+    LARGE_INTEGER seek;
+    HRESULT hr;
+    const struct image_codec *codec = NULL;
+    GpImage *new_image;
+
+    TRACE("(%p,%s,%u)\n", image, debugstr_guid(dimensionID), frame);
+
+    if (!image || !dimensionID)
+        return InvalidParameter;
+
+    if (frame >= image->frame_count)
+    {
+        WARN("requested frame %u, but image has only %u\n", frame, image->frame_count);
+        return InvalidParameter;
+    }
+
+    if (image->type != ImageTypeBitmap && image->type != ImageTypeMetafile)
+    {
+        WARN("invalid image type %d\n", image->type);
+        return InvalidParameter;
+    }
+
+    if (image->current_frame == frame)
+        return Ok;
+
+    if (!image->stream)
+    {
+        TRACE("image doesn't have an associated stream\n");
+        return Ok;
+    }
+
+    /* choose an appropriate image decoder */
+    stat = get_decoder_info(image->stream, &codec);
+    if (stat != Ok)
+    {
+        WARN("can't find decoder info\n");
+        return stat;
+    }
+
+    /* seek to the start of the stream */
+    seek.QuadPart = 0;
+    hr = IStream_Seek(image->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(image->stream, &codec->info.Clsid, frame, &new_image);
+
+    if (stat == Ok)
+    {
+        memcpy(&new_image->format, &codec->info.FormatID, sizeof(GUID));
+        free_image_data(image);
+        if (image->type == ImageTypeBitmap)
+            *(GpBitmap *)image = *(GpBitmap *)new_image;
+        else if (image->type == ImageTypeMetafile)
+            *(GpMetafile *)image = *(GpMetafile *)new_image;
+        new_image->type = ~0;
+        GdipFree(new_image);
+        return Ok;
+    }
+
+    return stat;
+}
+
+GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream *stream, GpImage **image)
 {
     GpStatus stat;
     LARGE_INTEGER seek;
@@ -2864,12 +3939,13 @@ GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream* stream, GpImage **image)
     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);
+    stat = codec->decode_func(stream, &codec->info.Clsid, 0, image);
 
     /* take note of the original data format */
     if (stat == Ok)
     {
         memcpy(&(*image)->format, &codec->info.FormatID, sizeof(GUID));
+        return Ok;
     }
 
     return stat;
@@ -2902,12 +3978,14 @@ GpStatus WINGDIPAPI GdipSetPropertyItem(GpImage *image, GDIPCONST PropertyItem*
 {
     static int calls;
 
-    TRACE("(%p,%p)\n", image, item);
+    if (!image || !item) return InvalidParameter;
+
+    TRACE("(%p,%p:%#x,%u,%u,%p)\n", image, item, item->id, item->type, item->length, item->value);
 
     if(!(calls++))
         FIXME("not implemented\n");
 
-    return NotImplemented;
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipSaveImageToFile(GpImage *image, GDIPCONST WCHAR* filename,
@@ -2997,35 +4075,26 @@ static GpStatus encode_image_WIC(GpImage *image, IStream* stream,
             hr = IWICBitmapFrameEncode_SetSize(frameencode, width, height);
 
         if (SUCCEEDED(hr))
-            /* FIXME: use the resolution from the image */
-            hr = IWICBitmapFrameEncode_SetResolution(frameencode, 96.0, 96.0);
+            hr = IWICBitmapFrameEncode_SetResolution(frameencode, image->xres, image->yres);
 
         if (SUCCEEDED(hr))
         {
-            for (i=0; wic_pixel_formats[i]; i++)
+            for (i=0; pixel_formats[i].wic_format; i++)
             {
-                if (wic_gdip_formats[i] == bitmap->format)
+                if (pixel_formats[i].gdip_format == bitmap->format)
+                {
+                    memcpy(&wicformat, pixel_formats[i].wic_format, sizeof(GUID));
+                    gdipformat = bitmap->format;
                     break;
+                }
             }
-            if (wic_pixel_formats[i])
-                memcpy(&wicformat, wic_pixel_formats[i], sizeof(GUID));
-            else
+            if (!gdipformat)
+            {
                 memcpy(&wicformat, &GUID_WICPixelFormat32bppBGRA, sizeof(GUID));
+                gdipformat = PixelFormat32bppARGB;
+            }
 
             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;
-            }
         }
 
         if (SUCCEEDED(hr))
@@ -3124,26 +4193,46 @@ GpStatus WINGDIPAPI GdipSaveImageToStream(GpImage *image, IStream* stream,
     return stat;
 }
 
+/*****************************************************************************
+ * GdipSaveAdd [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipSaveAdd(GpImage *image, GDIPCONST EncoderParameters *params)
+{
+    FIXME("(%p,%p): stub\n", image, params);
+    return Ok;
+}
+
 /*****************************************************************************
  * GdipGetImagePalette [GDIPLUS.@]
  */
 GpStatus WINGDIPAPI GdipGetImagePalette(GpImage *image, ColorPalette *palette, INT size)
 {
+    INT count;
+
     TRACE("(%p,%p,%i)\n", image, palette, size);
 
     if (!image || !palette)
         return InvalidParameter;
 
-    if (size < (sizeof(UINT)*2+sizeof(ARGB)*image->palette_count))
+    count = image->palette ? image->palette->Count : 0;
+
+    if (size < (sizeof(UINT)*2+sizeof(ARGB)*count))
     {
         TRACE("<-- InsufficientBuffer\n");
         return InsufficientBuffer;
     }
 
-    palette->Flags = image->palette_flags;
-    palette->Count = image->palette_count;
-    memcpy(palette->Entries, image->palette_entries, sizeof(ARGB)*image->palette_count);
-
+    if (image->palette)
+    {
+        palette->Flags = image->palette->Flags;
+        palette->Count = image->palette->Count;
+        memcpy(palette->Entries, image->palette->Entries, sizeof(ARGB)*image->palette->Count);
+    }
+    else
+    {
+        palette->Flags = 0;
+        palette->Count = 0;
+    }
     return Ok;
 }
 
@@ -3153,26 +4242,21 @@ GpStatus WINGDIPAPI GdipGetImagePalette(GpImage *image, ColorPalette *palette, I
 GpStatus WINGDIPAPI GdipSetImagePalette(GpImage *image,
     GDIPCONST ColorPalette *palette)
 {
+    ColorPalette *new_palette;
+
     TRACE("(%p,%p)\n", image, palette);
 
     if(!image || !palette || palette->Count > 256)
         return InvalidParameter;
 
-    if (palette->Count > image->palette_size)
-    {
-        ARGB *new_palette;
-
-        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;
-    }
+    new_palette = GdipAlloc(2 * sizeof(UINT) + palette->Count * sizeof(ARGB));
+    if (!new_palette) return OutOfMemory;
 
-    image->palette_flags = palette->Flags;
-    image->palette_count = palette->Count;
-    memcpy(image->palette_entries, palette->Entries, sizeof(ARGB)*palette->Count);
+    GdipFree(image->palette);
+    image->palette = new_palette;
+    image->palette->Flags = palette->Flags;
+    image->palette->Count = palette->Count;
+    memcpy(image->palette->Entries, palette->Entries, sizeof(ARGB)*palette->Count);
 
     return Ok;
 }
@@ -3201,8 +4285,8 @@ static const WCHAR gif_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ',
 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 BYTE gif_sig_pattern[12] = "GIF87aGIF89a";
+static const BYTE gif_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
 
 static const WCHAR tiff_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'T','I','F','F', 0};
 static const WCHAR tiff_extension[] = {'*','.','T','I','F','F',';','*','.','T','I','F',0};
@@ -3289,8 +4373,8 @@ static const struct image_codec codecs[NUM_CODECS] = {
             /* MimeType */           gif_mimetype,
             /* Flags */              ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
             /* Version */            1,
-            /* SigCount */           1,
-            /* SigSize */            4,
+            /* SigCount */           2,
+            /* SigSize */            6,
             /* SigPattern */         gif_sig_pattern,
             /* SigMask */            gif_sig_mask,
         },
@@ -3561,7 +4645,6 @@ GpStatus WINGDIPAPI GdipCreateBitmapFromHBITMAP(HBITMAP hbm, HPALETTE hpal, GpBi
     GpStatus retval;
     PixelFormat format;
     BitmapData lockeddata;
-    INT y;
 
     TRACE("%p %p %p\n", hbm, hpal, bitmap);
 
@@ -3610,62 +4693,31 @@ GpStatus WINGDIPAPI GdipCreateBitmapFromHBITMAP(HBITMAP hbm, HPALETTE hpal, GpBi
             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 hdc;
+            BITMAPINFO *pbmi;
+            INT src_height;
 
-                hdc = CreateCompatibleDC(NULL);
-                oldhbm = SelectObject(hdc, hbm);
+            hdc = CreateCompatibleDC(NULL);
+            pbmi = GdipAlloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
 
-                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)
+            {
+                pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+                pbmi->bmiHeader.biBitCount = 0;
 
-                    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;
-                    }
+                GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);
 
-                    for (y=0; y<src_height; y++)
-                    {
-                        GetDIBits(hdc, hbm, y, 1, dst_bits+dst_stride*y,
-                            pbmi, DIB_RGB_COLORS);
-                    }
+                src_height = abs(pbmi->bmiHeader.biHeight);
+                pbmi->bmiHeader.biHeight = -src_height;
 
-                    GdipFree(pbmi);
-                }
-                else
-                    retval = OutOfMemory;
+                GetDIBits(hdc, hbm, 0, src_height, lockeddata.Scan0, pbmi, DIB_RGB_COLORS);
 
-                SelectObject(hdc, oldhbm);
-                DeleteDC(hdc);
+                GdipFree(pbmi);
             }
+            else
+                retval = OutOfMemory;
+
+            DeleteDC(hdc);
 
             GdipBitmapUnlockBits(*bitmap, &lockeddata);
         }
@@ -3835,7 +4887,7 @@ GpStatus WINGDIPAPI GdipGetImageThumbnail(GpImage *image, UINT width, UINT heigh
     GdipGetImageWidth(image, &srcwidth);
     GdipGetImageHeight(image, &srcheight);
 
-    stat = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppARGB,
+    stat = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppPARGB,
         NULL, (GpBitmap**)ret_image);
 
     if (stat == Ok)
@@ -3878,6 +4930,9 @@ GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type)
 
     TRACE("(%p, %u)\n", image, type);
 
+    if (!image)
+        return InvalidParameter;
+
     rotate_90 = type&1;
     flip_x = (type&6) == 2 || (type&6) == 4;
     flip_y = (type&3) == 1 || (type&3) == 2;
index acfe989..fe03e65 100644 (file)
@@ -393,8 +393,8 @@ GpStatus WINGDIPAPI GdipTransformMatrixPointsI(GpMatrix *matrix, GpPoint *pts, I
 
     if(ret == Ok)
         for(i = 0; i < count; i++){
-            pts[i].X = roundr(ptsF[i].X);
-            pts[i].Y = roundr(ptsF[i].Y);
+            pts[i].X = gdip_round(ptsF[i].X);
+            pts[i].Y = gdip_round(ptsF[i].Y);
         }
     GdipFree(ptsF);
 
@@ -474,8 +474,8 @@ GpStatus WINGDIPAPI GdipVectorTransformMatrixPointsI(GpMatrix *matrix, GpPoint *
     /* store back */
     if(ret == Ok)
         for(i = 0; i < count; i++){
-            pts[i].X = roundr(ptsF[i].X);
-            pts[i].Y = roundr(ptsF[i].Y);
+            pts[i].X = gdip_round(ptsF[i].X);
+            pts[i].Y = gdip_round(ptsF[i].Y);
         }
     GdipFree(ptsF);
 
index 38923a0..4345ece 100644 (file)
@@ -232,10 +232,7 @@ GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF
     (*metafile)->image.type = ImageTypeMetafile;
     (*metafile)->image.picture = NULL;
     (*metafile)->image.flags   = ImageFlagsNone;
-    (*metafile)->image.palette_flags = 0;
-    (*metafile)->image.palette_count = 0;
-    (*metafile)->image.palette_size = 0;
-    (*metafile)->image.palette_entries = NULL;
+    (*metafile)->image.palette = NULL;
     (*metafile)->bounds = *frameRect;
     (*metafile)->unit = frameUnit;
     (*metafile)->metafile_type = type;
@@ -551,3 +548,41 @@ GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
 
     return stat;
 }
+
+static int CALLBACK get_metafile_type_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
+    int nObj, LPARAM lpData)
+{
+    MetafileType *result = (MetafileType*)lpData;
+
+    if (lpEMFR->iType == EMR_GDICOMMENT)
+    {
+        const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
+
+        if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
+        {
+            const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
+
+            if (4 + sizeof(EmfPlusRecordHeader) <= comment->cbData &&
+                header->Type == EmfPlusRecordTypeHeader)
+            {
+                if ((header->Flags & 1) == 1)
+                    *result = MetafileTypeEmfPlusDual;
+                else
+                    *result = MetafileTypeEmfPlusOnly;
+            }
+        }
+        else
+            *result = MetafileTypeEmf;
+    }
+    else
+        *result = MetafileTypeEmf;
+
+    return FALSE;
+}
+
+MetafileType METAFILE_GetEmfType(HENHMETAFILE hemf)
+{
+    MetafileType result = MetafileTypeInvalid;
+    EnumEnhMetaFile(NULL, hemf, get_metafile_type_proc, &result, NULL);
+    return result;
+}
index 485c3a3..f62b599 100644 (file)
@@ -151,15 +151,15 @@ static inline GpStatus clone_element(const region_element* element,
     {
         case RegionDataRect:
             (*element2)->elementdata.rect = element->elementdata.rect;
-            break;
+            return Ok;
         case RegionDataEmptyRect:
         case RegionDataInfiniteRect:
-            break;
+            return Ok;
         case RegionDataPath:
             (*element2)->elementdata.pathdata.pathheader = element->elementdata.pathdata.pathheader;
             stat = GdipClonePath(element->elementdata.pathdata.path,
                     &(*element2)->elementdata.pathdata.path);
-            if (stat != Ok) goto clone_out;
+            if (stat == Ok) return Ok;
             break;
         default:
             (*element2)->elementdata.combine.left  = NULL;
@@ -167,16 +167,15 @@ static inline GpStatus clone_element(const region_element* element,
 
             stat = clone_element(element->elementdata.combine.left,
                     &(*element2)->elementdata.combine.left);
-            if (stat != Ok) goto clone_out;
-            stat = clone_element(element->elementdata.combine.right,
-                    &(*element2)->elementdata.combine.right);
-            if (stat != Ok) goto clone_out;
+            if (stat == Ok)
+            {
+                stat = clone_element(element->elementdata.combine.right,
+                        &(*element2)->elementdata.combine.right);
+                if (stat == Ok) return Ok;
+            }
             break;
     }
 
-    return Ok;
-
-clone_out:
     delete_element(*element2);
     *element2 = NULL;
     return stat;
@@ -254,20 +253,20 @@ GpStatus WINGDIPAPI GdipCombineRegionPath(GpRegion *region, GpPath *path, Combin
     }
 
     left = GdipAlloc(sizeof(region_element));
-    if (!left)
-        goto out;
-    *left = region->node;
-
-    stat = clone_element(&path_region->node, &right);
-    if (stat != Ok)
-        goto out;
-
-    fuse_region(region, left, right, mode);
-
-    GdipDeleteRegion(path_region);
-    return Ok;
+    if (left)
+    {
+        *left = region->node;
+        stat = clone_element(&path_region->node, &right);
+        if (stat == Ok)
+        {
+            fuse_region(region, left, right, mode);
+            GdipDeleteRegion(path_region);
+            return Ok;
+        }
+    }
+    else
+        stat = OutOfMemory;
 
-out:
     GdipFree(left);
     GdipDeleteRegion(path_region);
     return stat;
@@ -301,20 +300,20 @@ GpStatus WINGDIPAPI GdipCombineRegionRect(GpRegion *region,
     }
 
     left = GdipAlloc(sizeof(region_element));
-    if (!left)
-        goto out;
-    memcpy(left, &region->node, sizeof(region_element));
-
-    stat = clone_element(&rect_region->node, &right);
-    if (stat != Ok)
-        goto out;
-
-    fuse_region(region, left, right, mode);
-
-    GdipDeleteRegion(rect_region);
-    return Ok;
+    if (left)
+    {
+        memcpy(left, &region->node, sizeof(region_element));
+        stat = clone_element(&rect_region->node, &right);
+        if (stat == Ok)
+        {
+            fuse_region(region, left, right, mode);
+            GdipDeleteRegion(rect_region);
+            return Ok;
+        }
+    }
+    else
+        stat = OutOfMemory;
 
-out:
     GdipFree(left);
     GdipDeleteRegion(rect_region);
     return stat;
@@ -399,6 +398,8 @@ GpStatus WINGDIPAPI GdipCreateRegion(GpRegion **region)
     if(!*region)
         return OutOfMemory;
 
+    TRACE("=> %p\n", *region);
+
     return init_region(*region, RegionDataInfiniteRect);
 }
 
@@ -680,6 +681,7 @@ GpStatus WINGDIPAPI GdipGetRegionBounds(GpRegion *region, GpGraphics *graphics,
     if(!hrgn){
         rect->X = rect->Y = -(REAL)(1 << 22);
         rect->Width = rect->Height = (REAL)(1 << 23);
+        TRACE("%p => infinite\n", region);
         return Ok;
     }
 
@@ -688,6 +690,7 @@ GpStatus WINGDIPAPI GdipGetRegionBounds(GpRegion *region, GpGraphics *graphics,
         rect->Y = r.top;
         rect->Width  = r.right  - r.left;
         rect->Height = r.bottom - r.top;
+        TRACE("%p => %s\n", region, debugstr_rectf(rect));
     }
     else
         status = GenericError;
@@ -712,10 +715,10 @@ GpStatus WINGDIPAPI GdipGetRegionBoundsI(GpRegion *region, GpGraphics *graphics,
 
     status = GdipGetRegionBounds(region, graphics, &rectf);
     if(status == Ok){
-        rect->X = roundr(rectf.X);
-        rect->Y = roundr(rectf.X);
-        rect->Width  = roundr(rectf.Width);
-        rect->Height = roundr(rectf.Height);
+        rect->X = gdip_round(rectf.X);
+        rect->Y = gdip_round(rectf.X);
+        rect->Width  = gdip_round(rectf.Width);
+        rect->Height = gdip_round(rectf.Height);
     }
 
     return status;
@@ -1080,12 +1083,19 @@ GpStatus WINGDIPAPI GdipGetRegionHRgn(GpRegion *region, GpGraphics *graphics, HR
 
 GpStatus WINGDIPAPI GdipIsEmptyRegion(GpRegion *region, GpGraphics *graphics, BOOL *res)
 {
+    GpStatus status;
+    GpRectF rect;
+
     TRACE("(%p, %p, %p)\n", region, graphics, res);
 
     if(!region || !graphics || !res)
         return InvalidParameter;
 
-    *res = (region->node.type == RegionDataEmptyRect);
+    status = GdipGetRegionBounds(region, graphics, &rect);
+    if (status != Ok) return status;
+
+    *res = rect.Width == 0.0 && rect.Height == 0.0;
+    TRACE("=> %d\n", *res);
 
     return Ok;
 }
@@ -1210,7 +1220,7 @@ GpStatus WINGDIPAPI GdipIsVisibleRegionPoint(GpRegion* region, REAL x, REAL y, G
         return Ok;
     }
 
-    *res = PtInRegion(hrgn, roundr(x), roundr(y));
+    *res = PtInRegion(hrgn, gdip_round(x), gdip_round(y));
 
     DeleteObject(hrgn);
 
index cf6ea1f..7d6bcb0 100644 (file)
@@ -50,6 +50,7 @@ GpStatus WINGDIPAPI GdipCreateStringFormat(INT attr, LANGID lang,
     (*format)->digitsub = StringDigitSubstituteUser;
     (*format)->character_ranges = NULL;
     (*format)->range_count = 0;
+    (*format)->generic_typographic = FALSE;
     /* tabstops */
     (*format)->tabcount = 0;
     (*format)->firsttab = 0.0;
@@ -386,6 +387,9 @@ GpStatus WINGDIPAPI GdipStringFormatGetGenericTypographic(GpStringFormat **forma
     (*format)->hkprefix  = HotkeyPrefixNone;
     (*format)->align     = StringAlignmentNear;
     (*format)->vertalign = StringAlignmentNear;
+    (*format)->generic_typographic = TRUE;
+
+    TRACE("%p => %p\n", format, *format);
 
     return Ok;
 }
index 69c8154..d0fc11b 100644 (file)
@@ -299,13 +299,6 @@ enum HotkeyPrefix
     HotkeyPrefixHide   = 2
 };
 
-enum PaletteFlags
-{
-    PaletteFlagsHasAlpha        = 1,
-    PaletteFlagsGrayScale       = 2,
-    PaletteFlagsHalftone        = 4
-};
-
 enum ImageCodecFlags
 {
     ImageCodecFlagsEncoder          = 1,
index 7c199c3..93114d9 100644 (file)
@@ -364,6 +364,9 @@ typedef struct PropertyItem
 
 #define PropertyTagFrameDelay        0x5100
 #define PropertyTagLoopCount         0x5101
+#define PropertyTagGlobalPalette     0x5102
+#define PropertyTagIndexBackground   0x5103
+#define PropertyTagIndexTransparent  0x5104
 
 #define PropertyTagPixelUnit         0x5110
 #define PropertyTagPixelPerUnitX     0x5111
index d7382f1..9922b24 100644 (file)
@@ -46,7 +46,40 @@ typedef INT PixelFormat;
 #define    PixelFormat48bppRGB          (12 | (48 << 8) | PixelFormatExtended)
 #define    PixelFormat64bppARGB         (13 | (64 << 8) | PixelFormatAlpha  | PixelFormatCanonical | PixelFormatExtended)
 #define    PixelFormat64bppPARGB        (14 | (64 << 8) | PixelFormatAlpha  | PixelFormatPAlpha | PixelFormatExtended)
-#define    PixelFormatMax               15
+#define    PixelFormat32bppCMYK         (15 | (32 << 8))
+#define    PixelFormatMax               16
+
+static inline BOOL IsIndexedPixelFormat(PixelFormat format)
+{
+    return (format & PixelFormatIndexed) != 0;
+}
+
+static inline BOOL IsAlphaPixelFormat(PixelFormat format)
+{
+    return (format & PixelFormatAlpha) != 0;
+}
+
+static inline BOOL IsCanonicalPixelFormat(PixelFormat format)
+{
+    return (format & PixelFormatCanonical) != 0;
+}
+
+static inline BOOL IsExtendedPixelFormat(PixelFormat format)
+{
+    return (format & PixelFormatExtended) != 0;
+}
+
+static inline UINT GetPixelFormatSize(PixelFormat format)
+{
+    return (format >> 8) & 0xff;
+}
+
+enum PaletteFlags
+{
+    PaletteFlagsHasAlpha        = 1,
+    PaletteFlagsGrayScale       = 2,
+    PaletteFlagsHalftone        = 4
+};
 
 #ifdef __cplusplus