[GDIPLUS] Sync with Wine Staging 4.0. CORE-15682
[reactos.git] / dll / win32 / gdiplus / graphics.c
index 3270c89..199b204 100644 (file)
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include <stdarg.h>
+#include <math.h>
+#include <limits.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "wingdi.h"
+#include "wine/unicode.h"
+
+#define COBJMACROS
+#include "objbase.h"
+#include "ocidl.h"
+#include "olectl.h"
+#include "ole2.h"
+
+#include "winreg.h"
+#include "shlwapi.h"
+
+#include "gdiplus.h"
 #include "gdiplus_private.h"
+#include "wine/debug.h"
+#include "wine/list.h"
 
-#include <winreg.h>
-#include <shlwapi.h>
+WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
 
 /* Mike "tamlin" Nordell 2012-09-14 for ReactOS:
  * NOTE: Wine uses per-GpGraphics id's ('contid' starting from zero in
@@ -38,6 +59,10 @@ static volatile LONG g_priv_contid = GDIP_CONTID_STEP;
 #define GDIP_GET_NEW_CONTID_FOR(pGpGraphics) \
    (UINT)(InterlockedExchangeAdd(&g_priv_contid,GDIP_CONTID_STEP))
 
+
+/* ReactOS FIXME: Inspect */
+#define fmax max
+
 /* looks-right constants */
 #define ANCHOR_WIDTH (2.0)
 #define MAX_ITERS (50)
@@ -47,9 +72,6 @@ static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text,
                                    GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
                                    INT flags, GDIPCONST GpMatrix *matrix);
 
-static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
-        GpCoordinateSpace src_space, GpMatrix *matrix);
-
 /* Converts from gdiplus path point type to gdi path point type. */
 static BYTE convert_path_point_type(BYTE type)
 {
@@ -247,6 +269,15 @@ static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
                      (pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
 
         width *= units_to_pixels(pen->width, pen->unit == UnitWorld ? graphics->unit : pen->unit, graphics->xres);
+        width *= graphics->scale;
+
+        pt[0].X = 0.0;
+        pt[0].Y = 0.0;
+        pt[1].X = 1.0;
+        pt[1].Y = 1.0;
+        gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceDevice, 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);
     }
 
     if(pen->dash == DashStyleCustom){
@@ -282,37 +313,10 @@ static void restore_dc(GpGraphics *graphics, INT state)
     RestoreDC(graphics->hdc, state);
 }
 
-/* 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
- * least:
- *  -scaling by page unit
- *  -applying world transformation
- *  -converting from float to int
- * Native gdiplus uses gdi32 to do all this (via SetMapMode, SetViewportExtEx,
- * SetWindowExtEx, SetWorldTransform, etc.) but we cannot because we are using
- * gdi to draw, and these functions would irreparably mess with line widths.
- */
-static void transform_and_round_points(GpGraphics *graphics, POINT *pti,
-    GpPointF *ptf, INT count)
+static void round_points(POINT *pti, GpPointF *ptf, INT count)
 {
-    REAL scale_x, scale_y;
-    GpMatrix matrix;
     int i;
 
-    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)
-    {
-        scale_x *= graphics->scale;
-        scale_y *= graphics->scale;
-    }
-
-    matrix = graphics->worldtrans;
-    GdipScaleMatrix(&matrix, scale_x, scale_y, MatrixOrderAppend);
-    GdipTransformMatrixPoints(&matrix, ptf, count);
-
     for(i = 0; i < count; i++){
         pti[i].x = gdip_round(ptf[i].X);
         pti[i].y = gdip_round(ptf[i].Y);
@@ -322,7 +326,8 @@ static void transform_and_round_points(GpGraphics *graphics, POINT *pti,
 static void gdi_alpha_blend(GpGraphics *graphics, INT dst_x, INT dst_y, INT dst_width, INT dst_height,
                             HDC hdc, INT src_x, INT src_y, INT src_width, INT src_height)
 {
-    if (GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE)
+    if (GetDeviceCaps(graphics->hdc, TECHNOLOGY) == DT_RASPRINTER &&
+        GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE)
     {
         TRACE("alpha blending not supported by device, fallback to StretchBlt\n");
 
@@ -345,25 +350,66 @@ static void gdi_alpha_blend(GpGraphics *graphics, INT dst_x, INT dst_y, INT dst_
 
 static GpStatus get_clip_hrgn(GpGraphics *graphics, HRGN *hrgn)
 {
-    /* clipping region is in device coords */
-    return GdipGetRegionHRgn(graphics->clip, NULL, hrgn);
+    GpRegion *rgn;
+    GpMatrix transform;
+    GpStatus stat;
+    BOOL identity;
+
+    stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceDevice, &transform);
+
+    if (stat == Ok)
+        stat = GdipIsMatrixIdentity(&transform, &identity);
+
+    if (stat == Ok)
+        stat = GdipCloneRegion(graphics->clip, &rgn);
+
+    if (stat == Ok)
+    {
+        if (!identity)
+            stat = GdipTransformRegion(rgn, &transform);
+
+        if (stat == Ok)
+            stat = GdipGetRegionHRgn(rgn, NULL, hrgn);
+
+        GdipDeleteRegion(rgn);
+    }
+
+    if (stat == Ok && graphics->gdi_clip)
+    {
+        if (*hrgn)
+            CombineRgn(*hrgn, *hrgn, graphics->gdi_clip, RGN_AND);
+        else
+        {
+            *hrgn = CreateRectRgn(0,0,0,0);
+            CombineRgn(*hrgn, graphics->gdi_clip, graphics->gdi_clip, RGN_COPY);
+        }
+    }
+
+    return stat;
 }
 
-/* Draw non-premultiplied ARGB data to the given graphics object */
+/* Draw ARGB data to the given graphics object */
 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)
+    const BYTE *src, INT src_width, INT src_height, INT src_stride, const PixelFormat fmt)
 {
     GpBitmap *dst_bitmap = (GpBitmap*)graphics->image;
     INT x, y;
 
-    for (x=0; x<src_width; x++)
+    for (y=0; y<src_height; y++)
     {
-        for (y=0; y<src_height; y++)
+        for (x=0; x<src_width; x++)
         {
             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));
+
+            if (!(src_color & 0xff000000))
+                continue;
+
+            GdipBitmapGetPixel(dst_bitmap, x+dst_x, y+dst_y, &dst_color);
+            if (fmt & PixelFormatPAlpha)
+                GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over_fgpremult(dst_color, src_color));
+            else
+                GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over(dst_color, src_color));
         }
     }
 
@@ -371,7 +417,7 @@ static GpStatus alpha_blend_bmp_pixels(GpGraphics *graphics, INT dst_x, INT dst_
 }
 
 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)
+    const BYTE *src, INT src_width, INT src_height, INT src_stride, PixelFormat fmt)
 {
     HDC hdc;
     HBITMAP hbitmap;
@@ -395,7 +441,9 @@ static GpStatus alpha_blend_hdc_pixels(GpGraphics *graphics, INT dst_x, INT dst_
     hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
         (void**)&temp_bits, NULL, 0);
 
-    if (GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE)
+    if ((GetDeviceCaps(graphics->hdc, TECHNOLOGY) == DT_RASPRINTER &&
+         GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE) ||
+            fmt & PixelFormatPAlpha)
         memcpy(temp_bits, src, src_width * src_height * 4);
     else
         convert_32bppARGB_to_32bppPARGB(src_width, src_height, temp_bits,
@@ -411,7 +459,7 @@ static GpStatus alpha_blend_hdc_pixels(GpGraphics *graphics, INT dst_x, INT dst_
 }
 
 static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst_y,
-    const BYTE *src, INT src_width, INT src_height, INT src_stride, HRGN hregion)
+    const BYTE *src, INT src_width, INT src_height, INT src_stride, HRGN hregion, PixelFormat fmt)
 {
     GpStatus stat=Ok;
 
@@ -445,7 +493,7 @@ static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst
 
         size = GetRegionData(hrgn, 0, NULL);
 
-        rgndata = GdipAlloc(size);
+        rgndata = heap_alloc_zero(size);
         if (!rgndata)
         {
             DeleteObject(hrgn);
@@ -461,10 +509,10 @@ static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst
             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);
+                src_stride, fmt);
         }
 
-        GdipFree(rgndata);
+        heap_free(rgndata);
 
         DeleteObject(hrgn);
 
@@ -487,14 +535,13 @@ static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst
 
         save = SaveDC(graphics->hdc);
 
-        if (hrgn)
-            ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
+        ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
 
         if (hregion)
             ExtSelectClipRgn(graphics->hdc, hregion, RGN_AND);
 
         stat = alpha_blend_hdc_pixels(graphics, dst_x, dst_y, src, src_width,
-            src_height, src_stride);
+            src_height, src_stride, fmt);
 
         RestoreDC(graphics->hdc, save);
 
@@ -505,27 +552,40 @@ static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst
 }
 
 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)
+    const BYTE *src, INT src_width, INT src_height, INT src_stride, PixelFormat fmt)
+{
+    return alpha_blend_pixels_hrgn(graphics, dst_x, dst_y, src, src_width, src_height, src_stride, NULL, fmt);
+}
+
+/* NOTE: start and end pixels must be in pre-multiplied ARGB format */
+static inline ARGB blend_colors_premult(ARGB start, ARGB end, REAL position)
 {
-    return alpha_blend_pixels_hrgn(graphics, dst_x, dst_y, src, src_width, src_height, src_stride, NULL);
+    UINT pos = position * 255.0f + 0.5f;
+    return
+        (((((start >> 24)       ) << 8) + (((end >> 24)       ) - ((start >> 24)       )) * pos) >> 8) << 24 |
+        (((((start >> 16) & 0xff) << 8) + (((end >> 16) & 0xff) - ((start >> 16) & 0xff)) * pos) >> 8) << 16 |
+        (((((start >>  8) & 0xff) << 8) + (((end >>  8) & 0xff) - ((start >>  8) & 0xff)) * pos) >> 8) <<  8 |
+        (((((start      ) & 0xff) << 8) + (((end      ) & 0xff) - ((start      ) & 0xff)) * pos) >> 8);
 }
 
 static ARGB blend_colors(ARGB start, ARGB end, REAL position)
 {
-    ARGB result=0;
-    ARGB i;
-    INT a1, a2, a3;
+    INT start_a, end_a, final_a;
+    INT pos;
 
-    a1 = (start >> 24) & 0xff;
-    a2 = (end >> 24) & 0xff;
+    pos = (INT)(position * 255.0f + 0.5f);
 
-    a3 = (int)(a1*(1.0f - position)+a2*(position));
+    start_a = ((start >> 24) & 0xff) * (pos ^ 0xff);
+    end_a = ((end >> 24) & 0xff) * pos;
 
-    result |= a3 << 24;
+    final_a = start_a + end_a;
 
-    for (i=0xff; i<=0xff0000; i = i << 8)
-        result |= (int)((start&i)*(1.0f - position)+(end&i)*(position))&i;
-    return result;
+    if (final_a < 0xff) return 0;
+
+    return (final_a / 0xff) << 24 |
+        ((((start >> 16) & 0xff) * start_a + (((end >> 16) & 0xff) * end_a)) / final_a) << 16 |
+        ((((start >> 8) & 0xff) * start_a + (((end >> 8) & 0xff) * end_a)) / final_a) << 8 |
+        (((start & 0xff) * start_a + ((end & 0xff) * end_a)) / final_a);
 }
 
 static ARGB blend_line_gradient(GpLineGradient* brush, REAL position)
@@ -533,6 +593,7 @@ static ARGB blend_line_gradient(GpLineGradient* brush, REAL position)
     REAL blendfac;
 
     /* clamp to between 0.0 and 1.0, using the wrap mode */
+    position = (position - brush->rect.X) / brush->rect.Width;
     if (brush->wrap == WrapModeTile)
     {
         position = fmodf(position, 1.0f);
@@ -645,12 +706,18 @@ static BOOL color_is_gray(ARGB color)
     return (r == g) && (g == b);
 }
 
-static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE data,
-    UINT width, UINT height, INT stride, ColorAdjustType type)
+/* returns preferred pixel format for the applied attributes */
+PixelFormat apply_image_attributes(const GpImageAttributes *attributes, LPBYTE data,
+    UINT width, UINT height, INT stride, ColorAdjustType type, PixelFormat fmt)
 {
     UINT x, y;
     INT i;
 
+    if ((attributes->noop[type] == IMAGEATTR_NOOP_UNDEFINED &&
+            attributes->noop[ColorAdjustTypeDefault] == IMAGEATTR_NOOP_SET) ||
+            (attributes->noop[type] == IMAGEATTR_NOOP_SET))
+        return fmt;
+
     if (attributes->colorkeys[type].enabled ||
         attributes->colorkeys[ColorAdjustTypeDefault].enabled)
     {
@@ -658,6 +725,9 @@ static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE d
         BYTE min_blue, min_green, min_red;
         BYTE max_blue, max_green, max_red;
 
+        if (!data || fmt != PixelFormat32bppARGB)
+            return PixelFormat32bppARGB;
+
         if (attributes->colorkeys[type].enabled)
             key = &attributes->colorkeys[type];
         else
@@ -691,6 +761,9 @@ static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE d
     {
         const struct color_remap_table *table;
 
+        if (!data || fmt != PixelFormat32bppARGB)
+            return PixelFormat32bppARGB;
+
         if (attributes->colorremaptables[type].enabled)
             table = &attributes->colorremaptables[type];
         else
@@ -720,6 +793,9 @@ static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE d
         int gray_matrix[5][5];
         BOOL identity;
 
+        if (!data || fmt != PixelFormat32bppARGB)
+            return PixelFormat32bppARGB;
+
         if (attributes->colormatrices[type].enabled)
             colormatrices = &attributes->colormatrices[type];
         else
@@ -758,6 +834,9 @@ static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE d
     {
         REAL gamma;
 
+        if (!data || fmt != PixelFormat32bppARGB)
+            return PixelFormat32bppARGB;
+
         if (attributes->gamma_enabled[type])
             gamma = attributes->gamma[type];
         else
@@ -782,6 +861,8 @@ static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE d
                 *src_color = (*src_color & 0xff000000) | (red << 16) | (green << 8) | blue;
             }
     }
+
+    return fmt;
 }
 
 /* Given a bitmap and its source rectangle, find the smallest rectangle in the
@@ -823,6 +904,10 @@ static void get_bitmap_sample_size(InterpolationMode interpolation, WrapMode wra
             right = bitmap->width-1;
         if (bottom >= bitmap->height)
             bottom = bitmap->height-1;
+        if (bottom < top || right < left)
+            /* entirely outside image, just sample a pixel so we don't have to
+             * special-case this later */
+            left = top = right = bottom = 0;
     }
     else
     {
@@ -864,9 +949,8 @@ static ARGB sample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT wi
         if (y < 0)
             y = height*2 + y % (height * 2);
 
-        if ((attributes->wrap & 1) == 1)
+        if (attributes->wrap & WrapModeTileFlipX)
         {
-            /* Flip X */
             if ((x / width) % 2 == 0)
                 x = x % width;
             else
@@ -875,9 +959,8 @@ static ARGB sample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT wi
         else
             x = x % width;
 
-        if ((attributes->wrap & 2) == 2)
+        if (attributes->wrap & WrapModeTileFlipY)
         {
-            /* Flip Y */
             if ((y / height) % 2 == 0)
                 y = y % height;
             else
@@ -896,6 +979,11 @@ static ARGB sample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT wi
     return ((DWORD*)(bits))[(x - src_rect->X) + (y - src_rect->Y) * src_rect->Width];
 }
 
+static inline int positive_ceilf(float f)
+{
+    return f - (int)f > 0.0f ? f + 1.0f : f;
+}
+
 static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
     UINT height, GpPointF *point, GDIPCONST GpImageAttributes *attributes,
     InterpolationMode interpolation, PixelOffsetMode offset_mode)
@@ -916,12 +1004,12 @@ static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT
         ARGB top, bottom;
         float x_offset;
 
-        leftxf = floorf(point->X);
-        leftx = (INT)leftxf;
-        rightx = (INT)ceilf(point->X);
-        topyf = floorf(point->Y);
-        topy = (INT)topyf;
-        bottomy = (INT)ceilf(point->Y);
+        leftx = (INT)point->X;
+        leftxf = (REAL)leftx;
+        rightx = positive_ceilf(point->X);
+        topy = (INT)point->Y;
+        topyf = (REAL)topy;
+        bottomy = positive_ceilf(point->Y);
 
         if (leftx == rightx && topy == bottomy)
             return sample_bitmap_pixel(src_rect, bits, width, height,
@@ -965,17 +1053,95 @@ static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT
     }
 }
 
+static ARGB resample_bitmap_pixel_premult(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
+    UINT height, GpPointF *point, GDIPCONST GpImageAttributes *attributes,
+    InterpolationMode interpolation, PixelOffsetMode offset_mode)
+{
+    static int fixme;
+
+    switch (interpolation)
+    {
+    default:
+        if (!fixme++)
+            FIXME("Unimplemented interpolation %i\n", interpolation);
+        /* fall-through */
+    case InterpolationModeBilinear:
+    {
+        REAL leftxf, topyf;
+        INT leftx, rightx, topy, bottomy;
+        ARGB topleft, topright, bottomleft, bottomright;
+        ARGB top, bottom;
+        float x_offset;
+
+        leftx = (INT)point->X;
+        leftxf = (REAL)leftx;
+        rightx = positive_ceilf(point->X);
+        topy = (INT)point->Y;
+        topyf = (REAL)topy;
+        bottomy = positive_ceilf(point->Y);
+
+        if (leftx == rightx && topy == bottomy)
+            return sample_bitmap_pixel(src_rect, bits, width, height,
+                leftx, topy, attributes);
+
+        topleft = sample_bitmap_pixel(src_rect, bits, width, height,
+            leftx, topy, attributes);
+        topright = sample_bitmap_pixel(src_rect, bits, width, height,
+            rightx, topy, attributes);
+        bottomleft = sample_bitmap_pixel(src_rect, bits, width, height,
+            leftx, bottomy, attributes);
+        bottomright = sample_bitmap_pixel(src_rect, bits, width, height,
+            rightx, bottomy, attributes);
+
+        x_offset = point->X - leftxf;
+        top = blend_colors_premult(topleft, topright, x_offset);
+        bottom = blend_colors_premult(bottomleft, bottomright, x_offset);
+
+        return blend_colors_premult(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,
+            floorf(point->X + pixel_offset), point->Y + pixel_offset, attributes);
+    }
+
+    }
+}
+
 static REAL intersect_line_scanline(const GpPointF *p1, const GpPointF *p2, REAL y)
 {
     return (p1->X - p2->X) * (p2->Y - y) / (p2->Y - p1->Y) + p2->X;
 }
 
-static BOOL brush_can_fill_path(GpBrush *brush)
+/* is_fill is TRUE if filling regions, FALSE for drawing primitives */
+static BOOL brush_can_fill_path(GpBrush *brush, BOOL is_fill)
 {
     switch (brush->bt)
     {
     case BrushTypeSolidColor:
-        return TRUE;
+    {
+        if (is_fill)
+            return TRUE;
+        else
+        {
+            /* cannot draw semi-transparent colors */
+            return (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000;
+        }
+    }
     case BrushTypeHatchFill:
     {
         GpHatch *hatch = (GpHatch*)brush;
@@ -990,8 +1156,9 @@ static BOOL brush_can_fill_path(GpBrush *brush)
     }
 }
 
-static void brush_fill_path(GpGraphics *graphics, GpBrush* brush)
+static GpStatus brush_fill_path(GpGraphics *graphics, GpBrush *brush)
 {
+    GpStatus status = Ok;
     switch (brush->bt)
     {
     case BrushTypeSolidColor:
@@ -1004,12 +1171,22 @@ static void brush_fill_path(GpGraphics *graphics, GpBrush* brush)
             RECT rc;
             /* partially transparent fill */
 
-            SelectClipPath(graphics->hdc, RGN_AND);
+            if (!SelectClipPath(graphics->hdc, RGN_AND))
+            {
+                status = GenericError;
+                DeleteObject(bmp);
+                break;
+            }
             if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
             {
                 HDC hdc = CreateCompatibleDC(NULL);
 
-                if (!hdc) break;
+                if (!hdc)
+                {
+                    status = OutOfMemory;
+                    DeleteObject(bmp);
+                    break;
+                }
 
                 SelectObject(hdc, bmp);
                 gdi_alpha_blend(graphics, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
@@ -1027,7 +1204,11 @@ static void brush_fill_path(GpGraphics *graphics, GpBrush* brush)
         HBRUSH gdibrush, old_brush;
 
         gdibrush = create_gdi_brush(brush);
-        if (!gdibrush) return;
+        if (!gdibrush)
+        {
+            status = OutOfMemory;
+            break;
+        }
 
         old_brush = SelectObject(graphics->hdc, gdibrush);
         FillPath(graphics->hdc);
@@ -1036,6 +1217,8 @@ static void brush_fill_path(GpGraphics *graphics, GpBrush* brush)
         break;
     }
     }
+
+    return status;
 }
 
 static BOOL brush_can_fill_pixels(GpBrush *brush)
@@ -1096,10 +1279,8 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
     case BrushTypeLinearGradient:
     {
         GpLineGradient *fill = (GpLineGradient*)brush;
-        GpPointF draw_points[3], line_points[3];
+        GpPointF draw_points[3];
         GpStatus stat;
-        static const GpRectF box_1 = { 0.0, 0.0, 1.0, 1.0 };
-        GpMatrix *world_to_gradient; /* FIXME: Store this in the brush? */
         int x, y;
 
         draw_points[0].X = fill_area->X;
@@ -1112,27 +1293,16 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
         /* Transform the points to a co-ordinate space where X is the point's
          * position in the gradient, 0.0 being the start point and 1.0 the
          * end point. */
-        stat = GdipTransformPoints(graphics, CoordinateSpaceWorld,
-            CoordinateSpaceDevice, draw_points, 3);
-
-        if (stat == Ok)
-        {
-            line_points[0] = fill->startpoint;
-            line_points[1] = fill->endpoint;
-            line_points[2].X = fill->startpoint.X + (fill->startpoint.Y - fill->endpoint.Y);
-            line_points[2].Y = fill->startpoint.Y + (fill->endpoint.X - fill->startpoint.X);
-
-            stat = GdipCreateMatrix3(&box_1, line_points, &world_to_gradient);
-        }
+        stat = gdip_transform_points(graphics, CoordinateSpaceWorld,
+            WineCoordinateSpaceGdiDevice, draw_points, 3);
 
         if (stat == Ok)
         {
-            stat = GdipInvertMatrix(world_to_gradient);
+            GpMatrix world_to_gradient = fill->transform;
 
+            stat = GdipInvertMatrix(&world_to_gradient);
             if (stat == Ok)
-                stat = GdipTransformMatrixPoints(world_to_gradient, draw_points, 3);
-
-            GdipDeleteMatrix(world_to_gradient);
+                stat = GdipTransformMatrixPoints(&world_to_gradient, draw_points, 3);
         }
 
         if (stat == Ok)
@@ -1184,8 +1354,8 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
         draw_points[2].Y = fill_area->Y+1;
 
         /* Transform the points to the co-ordinate space of the bitmap. */
-        stat = GdipTransformPoints(graphics, CoordinateSpaceWorld,
-            CoordinateSpaceDevice, draw_points, 3);
+        stat = gdip_transform_points(graphics, CoordinateSpaceWorld,
+            WineCoordinateSpaceGdiDevice, draw_points, 3);
 
         if (stat == Ok)
         {
@@ -1200,7 +1370,7 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
         {
             BitmapData lockeddata;
 
-            fill->bitmap_bits = GdipAlloc(sizeof(ARGB) * bitmap->width * bitmap->height);
+            fill->bitmap_bits = heap_alloc_zero(sizeof(ARGB) * bitmap->width * bitmap->height);
             if (!fill->bitmap_bits)
                 stat = OutOfMemory;
 
@@ -1222,11 +1392,11 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
             if (stat == Ok)
                 apply_image_attributes(fill->imageattributes, fill->bitmap_bits,
                     bitmap->width, bitmap->height,
-                    src_stride, ColorAdjustTypeBitmap);
+                    src_stride, ColorAdjustTypeBitmap, lockeddata.PixelFormat);
 
             if (stat != Ok)
             {
-                GdipFree(fill->bitmap_bits);
+                heap_free(fill->bitmap_bits);
                 fill->bitmap_bits = NULL;
             }
         }
@@ -1315,7 +1485,7 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
         if (stat != Ok)
             return stat;
 
-        stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
+        stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
             CoordinateSpaceWorld, &world_to_device);
         if (stat == Ok)
         {
@@ -1451,8 +1621,17 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
                         REAL blend_amount, pdy, pdx;
                         pdy = yf - center_point.Y;
                         pdx = xf - center_point.X;
-                        blend_amount = ( (center_point.Y - start_point.Y) * pdx + (start_point.X - center_point.X) * pdy ) / ( dy * pdx - dx * pdy );
-                        outer_color = blend_colors(start_color, end_color, blend_amount);
+
+                        if (fabs(pdx) <= 0.001 && fabs(pdy) <= 0.001)
+                        {
+                            /* Too close to center point, don't try to calculate outer color */
+                            outer_color = start_color;
+                        }
+                        else
+                        {
+                            blend_amount = ( (center_point.Y - start_point.Y) * pdx + (start_point.X - center_point.X) * pdy ) / ( dy * pdx - dx * pdy );
+                            outer_color = blend_colors(start_color, end_color, blend_amount);
+                        }
                     }
 
                     distance = (end_point.Y - start_point.Y) * (start_point.X - xf) +
@@ -1538,7 +1717,10 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
             ptf[3].X = x2 - dbig;
             ptf[2].X = x2 + dsmall;
 
-            transform_and_round_points(graphics, pt, ptf, 4);
+            gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 4);
+
+            round_points(pt, ptf, 4);
+
             Polygon(graphics->hdc, pt, 4);
 
             break;
@@ -1560,7 +1742,10 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
             ptf[2].X = x2;
             ptf[2].Y = y2;
 
-            transform_and_round_points(graphics, pt, ptf, 3);
+            gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 3);
+
+            round_points(pt, ptf, 3);
+
             Polygon(graphics->hdc, pt, 3);
 
             break;
@@ -1572,7 +1757,10 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
             ptf[1].X = x2 + dx;
             ptf[1].Y = y2 + dy;
 
-            transform_and_round_points(graphics, pt, ptf, 2);
+            gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 2);
+
+            round_points(pt, ptf, 2);
+
             Ellipse(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
 
             break;
@@ -1592,7 +1780,10 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
             ptf[2].X = x2 + dx;
             ptf[2].Y = y2 + dy;
 
-            transform_and_round_points(graphics, pt, ptf, 3);
+            gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 3);
+
+            round_points(pt, ptf, 3);
+
             Polygon(graphics->hdc, pt, 3);
 
             break;
@@ -1612,7 +1803,10 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
             ptf[3].X = x2 + dx;
             ptf[3].Y = y2 + dy;
 
-            transform_and_round_points(graphics, pt, ptf, 4);
+            gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 4);
+
+            round_points(pt, ptf, 4);
+
             Pie(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y, pt[2].x,
                 pt[2].y, pt[3].x, pt[3].y);
 
@@ -1621,10 +1815,17 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
             if(!custom)
                 break;
 
+            if (custom->type == CustomLineCapTypeAdjustableArrow)
+            {
+                GpAdjustableArrowCap *arrow = (GpAdjustableArrowCap *)custom;
+                if (arrow->cap.fill && arrow->height <= 0.0)
+                    break;
+            }
+
             count = custom->pathdata.Count;
-            custptf = GdipAlloc(count * sizeof(PointF));
-            custpt = GdipAlloc(count * sizeof(POINT));
-            tp = GdipAlloc(count);
+            custptf = heap_alloc_zero(count * sizeof(PointF));
+            custpt = heap_alloc_zero(count * sizeof(POINT));
+            tp = heap_alloc_zero(count);
 
             if(!custptf || !custpt || !tp)
                 goto custend;
@@ -1638,7 +1839,9 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
             GdipTranslateMatrix(&matrix, x2, y2, MatrixOrderAppend);
             GdipTransformMatrixPoints(&matrix, custptf, count);
 
-            transform_and_round_points(graphics, custpt, custptf, count);
+            gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, custptf, count);
+
+            round_points(custpt, custptf, count);
 
             for(i = 0; i < count; i++)
                 tp[i] = convert_path_point_type(custom->pathdata.Types[i]);
@@ -1653,9 +1856,9 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
                 PolyDraw(graphics->hdc, custpt, tp, count);
 
 custend:
-            GdipFree(custptf);
-            GdipFree(custpt);
-            GdipFree(tp);
+            heap_free(custptf);
+            heap_free(custpt);
+            heap_free(tp);
             break;
         default:
             break;
@@ -1757,9 +1960,9 @@ static void shorten_bezier_amt(GpPointF * pt, REAL amt, BOOL rev)
 static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF * pt,
     GDIPCONST BYTE * types, INT count, BOOL caps)
 {
-    POINT *pti = GdipAlloc(count * sizeof(POINT));
-    BYTE *tp = GdipAlloc(count);
-    GpPointF *ptcopy = GdipAlloc(count * sizeof(GpPointF));
+    POINT *pti = heap_alloc_zero(count * sizeof(POINT));
+    BYTE *tp = heap_alloc_zero(count);
+    GpPointF *ptcopy = heap_alloc_zero(count * sizeof(GpPointF));
     INT i, j;
     GpStatus status = GenericError;
 
@@ -1775,7 +1978,7 @@ static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF *
     for(i = 1; i < count; i++){
         if((types[i] & PathPointTypePathTypeMask) == PathPointTypeBezier){
             if((i + 2 >= count) || !(types[i + 1] & PathPointTypeBezier)
-                || !(types[i + 1] & PathPointTypeBezier)){
+                || !(types[i + 2] & PathPointTypeBezier)){
                 ERR("Bad bezier points\n");
                 goto end;
             }
@@ -1861,7 +2064,9 @@ static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF *
         }
     }
 
-    transform_and_round_points(graphics, pti, ptcopy, count);
+    gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptcopy, count);
+
+    round_points(pti, ptcopy, count);
 
     for(i = 0; i < count; i++){
         tp[i] = convert_path_point_type(types[i]);
@@ -1872,9 +2077,9 @@ static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF *
     status = Ok;
 
 end:
-    GdipFree(pti);
-    GdipFree(ptcopy);
-    GdipFree(tp);
+    heap_free(pti);
+    heap_free(ptcopy);
+    heap_free(tp);
 
     return status;
 }
@@ -1890,9 +2095,15 @@ GpStatus trace_path(GpGraphics *graphics, GpPath *path)
     return result;
 }
 
+typedef enum GraphicsContainerType {
+    BEGIN_CONTAINER,
+    SAVE_GRAPHICS
+} GraphicsContainerType;
+
 typedef struct _GraphicsContainerItem {
     struct list entry;
     GraphicsContainer contid;
+    GraphicsContainerType type;
 
     SmoothingMode smoothing;
     CompositingQuality compqual;
@@ -1909,14 +2120,15 @@ typedef struct _GraphicsContainerItem {
 } GraphicsContainerItem;
 
 static GpStatus init_container(GraphicsContainerItem** container,
-        GDIPCONST GpGraphics* graphics){
+        GDIPCONST GpGraphics* graphics, GraphicsContainerType type){
     GpStatus sts;
 
-    *container = GdipAlloc(sizeof(GraphicsContainerItem));
+    *container = heap_alloc_zero(sizeof(GraphicsContainerItem));
     if(!(*container))
         return OutOfMemory;
 
     (*container)->contid = graphics->contid + 1;
+    (*container)->type = type;
 
     (*container)->smoothing = graphics->smoothing;
     (*container)->compqual = graphics->compqual;
@@ -1933,7 +2145,7 @@ static GpStatus init_container(GraphicsContainerItem** container,
 
     sts = GdipCloneRegion(graphics->clip, &(*container)->clip);
     if(sts != Ok){
-        GdipFree(*container);
+        heap_free(*container);
         *container = NULL;
         return sts;
     }
@@ -1944,7 +2156,7 @@ static GpStatus init_container(GraphicsContainerItem** container,
 static void delete_container(GraphicsContainerItem* container)
 {
     GdipDeleteRegion(container->clip);
-    GdipFree(container);
+    heap_free(container);
 }
 
 static GpStatus restore_container(GpGraphics* graphics,
@@ -1977,7 +2189,7 @@ static GpStatus restore_container(GpGraphics* graphics,
     return Ok;
 }
 
-static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect)
+static GpStatus get_graphics_device_bounds(GpGraphics* graphics, GpRectF* rect)
 {
     RECT wnd_rect;
     GpStatus stat=Ok;
@@ -2021,22 +2233,39 @@ static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect)
         rect->Height = GetDeviceCaps(graphics->hdc, VERTRES);
     }
 
-    if (graphics->hdc &&
-        (GetMapMode(graphics->hdc) != MM_TEXT || GetGraphicsMode(graphics->hdc) != GM_COMPATIBLE))
+    return stat;
+}
+
+static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect)
+{
+    GpStatus stat = get_graphics_device_bounds(graphics, rect);
+
+    if (stat == Ok && graphics->hdc)
     {
-        POINT points[2];
+        GpPointF points[4], min_point, max_point;
+        int i;
+
+        points[0].X = points[2].X = rect->X;
+        points[0].Y = points[1].Y = rect->Y;
+        points[1].X = points[3].X = rect->X + rect->Width;
+        points[2].Y = points[3].Y = rect->Y + rect->Height;
 
-        points[0].x = rect->X;
-        points[0].y = rect->Y;
-        points[1].x = rect->X + rect->Width;
-        points[1].y = rect->Y + rect->Height;
+        gdip_transform_points(graphics, CoordinateSpaceDevice, WineCoordinateSpaceGdiDevice, points, 4);
 
-        DPtoLP(graphics->hdc, points, sizeof(points)/sizeof(points[0]));
+        min_point = max_point = points[0];
 
-        rect->X = min(points[0].x, points[1].x);
-        rect->Y = min(points[0].y, points[1].y);
-        rect->Width = abs(points[1].x - points[0].x);
-        rect->Height = abs(points[1].y - points[0].y);
+        for (i=1; i<4; i++)
+        {
+            if (points[i].X < min_point.X) min_point.X = points[i].X;
+            if (points[i].Y < min_point.Y) min_point.Y = points[i].Y;
+            if (points[i].X > max_point.X) max_point.X = points[i].X;
+            if (points[i].Y > max_point.Y) max_point.Y = points[i].Y;
+        }
+
+        rect->X = min_point.X;
+        rect->Y = min_point.Y;
+        rect->Width = max_point.X - min_point.X;
+        rect->Height = max_point.Y - min_point.Y;
     }
 
     return stat;
@@ -2050,6 +2279,10 @@ static GpStatus get_visible_clip_region(GpGraphics *graphics, GpRegion *rgn)
     GpRectF rectf;
     GpRegion* tmp;
 
+    /* Ignore graphics image bounds for metafiles */
+    if (graphics->image && graphics->image_type == ImageTypeMetafile)
+        return GdipCombineRegionRegion(rgn, graphics->clip, CombineModeReplace);
+
     if((stat = get_graphics_bounds(graphics, &rectf)) != Ok)
         return stat;
 
@@ -2112,7 +2345,7 @@ static void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font,
     HFONT unscaled_font;
     TEXTMETRICW textmet;
 
-    if (font->unit == UnitPixel)
+    if (font->unit == UnitPixel || font->unit == UnitWorld)
         font_height = font->emSize;
     else
     {
@@ -2135,8 +2368,8 @@ static void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font,
         GpMatrix xform = *matrix;
         GdipTransformMatrixPoints(&xform, pt, 3);
     }
-    if (graphics)
-        GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
+
+    gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
     angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
     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));
@@ -2166,6 +2399,20 @@ GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
     return GdipCreateFromHDC2(hdc, NULL, graphics);
 }
 
+static void get_gdi_transform(GpGraphics *graphics, GpMatrix *matrix)
+{
+    XFORM xform;
+
+    if (graphics->hdc == NULL)
+    {
+        GdipSetMatrixElements(matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
+        return;
+    }
+
+    GetTransform(graphics->hdc, 0x204, &xform);
+    GdipSetMatrixElements(matrix, xform.eM11, xform.eM12, xform.eM21, xform.eM22, xform.eDx, xform.eDy);
+}
+
 GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **graphics)
 {
     GpStatus retval;
@@ -2183,13 +2430,13 @@ GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **gra
     if(graphics == NULL)
         return InvalidParameter;
 
-    *graphics = GdipAlloc(sizeof(GpGraphics));
+    *graphics = heap_alloc_zero(sizeof(GpGraphics));
     if(!*graphics)  return OutOfMemory;
 
     GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
 
     if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
-        GdipFree(*graphics);
+        heap_free(*graphics);
         return retval;
     }
 
@@ -2215,7 +2462,19 @@ GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **gra
     (*graphics)->busy = FALSE;
     (*graphics)->textcontrast = 4;
     list_init(&(*graphics)->containers);
+#ifdef __REACTOS__
     (*graphics)->contid = GDIP_GET_NEW_CONTID_FOR(*graphics);
+#else
+    (*graphics)->contid = 0;
+#endif
+    get_gdi_transform(*graphics, &(*graphics)->gdi_transform);
+
+    (*graphics)->gdi_clip = CreateRectRgn(0,0,0,0);
+    if (!GetClipRgn(hdc, (*graphics)->gdi_clip))
+    {
+        DeleteObject((*graphics)->gdi_clip);
+        (*graphics)->gdi_clip = NULL;
+    }
 
     TRACE("<-- %p\n", *graphics);
 
@@ -2226,13 +2485,14 @@ GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics)
 {
     GpStatus retval;
 
-    *graphics = GdipAlloc(sizeof(GpGraphics));
+    *graphics = heap_alloc_zero(sizeof(GpGraphics));
     if(!*graphics)  return OutOfMemory;
 
     GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
+    GdipSetMatrixElements(&(*graphics)->gdi_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
 
     if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
-        GdipFree(*graphics);
+        heap_free(*graphics);
         return retval;
     }
 
@@ -2255,7 +2515,11 @@ GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics)
     (*graphics)->busy = FALSE;
     (*graphics)->textcontrast = 4;
     list_init(&(*graphics)->containers);
+#ifdef __REACTOS__
     (*graphics)->contid = GDIP_GET_NEW_CONTID_FOR(*graphics);
+#else
+    (*graphics)->contid = 0;
+#endif
 
     TRACE("<-- %p\n", *graphics);
 
@@ -2340,12 +2604,14 @@ GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
 
     GdipDeleteRegion(graphics->clip);
 
+    DeleteObject(graphics->gdi_clip);
+
     /* Native returns ObjectBusy on the second free, instead of crashing as we'd
      * do otherwise, but we can't have that in the test suite because it means
      * accessing freed memory. */
     graphics->busy = TRUE;
 
-    GdipFree(graphics);
+    heap_free(graphics);
 
     return Ok;
 }
@@ -2459,7 +2725,7 @@ GpStatus WINGDIPAPI GdipDrawBeziersI(GpGraphics *graphics, GpPen *pen,
     if(graphics->busy)
         return ObjectBusy;
 
-    pts = GdipAlloc(sizeof(GpPointF) * count);
+    pts = heap_alloc_zero(sizeof(GpPointF) * count);
     if(!pts)
         return OutOfMemory;
 
@@ -2470,7 +2736,7 @@ GpStatus WINGDIPAPI GdipDrawBeziersI(GpGraphics *graphics, GpPen *pen,
 
     ret = GdipDrawBeziers(graphics,pen,pts,count);
 
-    GdipFree(pts);
+    heap_free(pts);
 
     return ret;
 }
@@ -2529,7 +2795,7 @@ GpStatus WINGDIPAPI GdipDrawClosedCurve2I(GpGraphics *graphics, GpPen *pen,
     if(!points || count <= 0)
         return InvalidParameter;
 
-    ptf = GdipAlloc(sizeof(GpPointF)*count);
+    ptf = heap_alloc_zero(sizeof(GpPointF)*count);
     if(!ptf)
         return OutOfMemory;
 
@@ -2540,7 +2806,7 @@ GpStatus WINGDIPAPI GdipDrawClosedCurve2I(GpGraphics *graphics, GpPen *pen,
 
     stat = GdipDrawClosedCurve2(graphics, pen, ptf, count, tension);
 
-    GdipFree(ptf);
+    heap_free(ptf);
 
     return stat;
 }
@@ -2565,7 +2831,7 @@ GpStatus WINGDIPAPI GdipDrawCurveI(GpGraphics *graphics, GpPen *pen,
     if(!points)
         return InvalidParameter;
 
-    pointsF = GdipAlloc(sizeof(GpPointF)*count);
+    pointsF = heap_alloc_zero(sizeof(GpPointF)*count);
     if(!pointsF)
         return OutOfMemory;
 
@@ -2575,7 +2841,7 @@ GpStatus WINGDIPAPI GdipDrawCurveI(GpGraphics *graphics, GpPen *pen,
     }
 
     ret = GdipDrawCurve(graphics,pen,pointsF,count);
-    GdipFree(pointsF);
+    heap_free(pointsF);
 
     return ret;
 }
@@ -2621,7 +2887,7 @@ GpStatus WINGDIPAPI GdipDrawCurve2I(GpGraphics *graphics, GpPen *pen,
     if(!points)
         return InvalidParameter;
 
-    pointsF = GdipAlloc(sizeof(GpPointF)*count);
+    pointsF = heap_alloc_zero(sizeof(GpPointF)*count);
     if(!pointsF)
         return OutOfMemory;
 
@@ -2631,7 +2897,7 @@ GpStatus WINGDIPAPI GdipDrawCurve2I(GpGraphics *graphics, GpPen *pen,
     }
 
     ret = GdipDrawCurve2(graphics,pen,pointsF,count,tension);
-    GdipFree(pointsF);
+    heap_free(pointsF);
 
     return ret;
 }
@@ -2824,6 +3090,13 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
     TRACE("%s %s %s\n", debugstr_pointf(&points[0]), debugstr_pointf(&points[1]),
         debugstr_pointf(&points[2]));
 
+    if (graphics->image && graphics->image->type == ImageTypeMetafile)
+    {
+        return METAFILE_DrawImagePointsRect((GpMetafile*)graphics->image,
+                image, points, count, srcx, srcy, srcwidth, srcheight,
+                srcUnit, imageAttributes, callback, callbackData);
+    }
+
     memcpy(ptf, points, 3 * sizeof(GpPointF));
 
     /* Ensure source width/height is positive */
@@ -2853,7 +3126,8 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
     ptf[3].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
     if (!srcwidth || !srcheight || (ptf[3].X == ptf[0].X && ptf[3].Y == ptf[0].Y))
         return Ok;
-    transform_and_round_points(graphics, pti, ptf, 4);
+    gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 4);
+    round_points(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]));
@@ -2864,23 +3138,7 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
     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)
-        {
-            FIXME("graphics object has no HDC\n");
-        }
-
-        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, srcy, srcwidth, srcheight, NULL) != S_OK)
-        {
-            if(callback)
-                callback(callbackData);
-            return GenericError;
-        }
-    }
-    else if (image->type == ImageTypeBitmap)
+    if (image->type == ImageTypeBitmap)
     {
         GpBitmap* bitmap = (GpBitmap*)image;
         BOOL do_resampling = FALSE;
@@ -2931,7 +3189,7 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
                 if (dst_area.bottom < pti[i].y) dst_area.bottom = pti[i].y;
             }
 
-            stat = get_graphics_bounds(graphics, &graphics_bounds);
+            stat = get_graphics_device_bounds(graphics, &graphics_bounds);
             if (stat != Ok) return stat;
 
             if (graphics_bounds.X > dst_area.left) dst_area.left = floorf(graphics_bounds.X);
@@ -2971,41 +3229,48 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
 
             TRACE("src_area: %d x %d\n", src_area.Width, src_area.Height);
 
-            src_data = GdipAlloc(sizeof(ARGB) * src_area.Width * src_area.Height);
+            src_data = heap_alloc_zero(sizeof(ARGB) * src_area.Width * src_area.Height);
             if (!src_data)
                 return OutOfMemory;
             src_stride = sizeof(ARGB) * src_area.Width;
 
-            /* Read the bits we need from the source bitmap into an ARGB buffer. */
+            /* Read the bits we need from the source bitmap into a compatible buffer. */
             lockeddata.Width = src_area.Width;
             lockeddata.Height = src_area.Height;
             lockeddata.Stride = src_stride;
-            lockeddata.PixelFormat = PixelFormat32bppARGB;
             lockeddata.Scan0 = src_data;
+            if (!do_resampling && bitmap->format == PixelFormat32bppPARGB)
+                lockeddata.PixelFormat = apply_image_attributes(imageAttributes, NULL, 0, 0, 0, ColorAdjustTypeBitmap, bitmap->format);
+            else if (imageAttributes != &defaultImageAttributes)
+                lockeddata.PixelFormat = PixelFormat32bppARGB;
+            else
+                lockeddata.PixelFormat = PixelFormat32bppPARGB;
 
             stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
-                PixelFormat32bppARGB, &lockeddata);
+                lockeddata.PixelFormat, &lockeddata);
 
             if (stat == Ok)
                 stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
 
             if (stat != Ok)
             {
-                GdipFree(src_data);
+                heap_free(src_data);
                 return stat;
             }
 
             apply_image_attributes(imageAttributes, src_data,
                 src_area.Width, src_area.Height,
-                src_stride, ColorAdjustTypeBitmap);
+                src_stride, ColorAdjustTypeBitmap, lockeddata.PixelFormat);
 
             if (do_resampling)
             {
+                REAL delta_xx, delta_xy, delta_yx, delta_yy;
+
                 /* Transform the bits as needed to the destination. */
-                dst_data = dst_dyn_data = GdipAlloc(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
+                dst_data = dst_dyn_data = heap_alloc_zero(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
                 if (!dst_data)
                 {
-                    GdipFree(src_data);
+                    heap_free(src_data);
                     return OutOfMemory;
                 }
 
@@ -3018,24 +3283,42 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
                 y_dx = dst_to_src_points[2].X - dst_to_src_points[0].X;
                 y_dy = dst_to_src_points[2].Y - dst_to_src_points[0].Y;
 
-                for (x=dst_area.left; x<dst_area.right; x++)
+                delta_yy = dst_area.top * y_dy;
+                delta_yx = dst_area.top * y_dx;
+
+                for (y=dst_area.top; y<dst_area.bottom; y++)
                 {
-                    for (y=dst_area.top; y<dst_area.bottom; y++)
+                    delta_xx = dst_area.left * x_dx;
+                    delta_xy = dst_area.left * x_dy;
+
+                    for (x=dst_area.left; x<dst_area.right; x++)
                     {
                         GpPointF src_pointf;
                         ARGB *dst_color;
 
-                        src_pointf.X = dst_to_src_points[0].X + x * x_dx + y * y_dx;
-                        src_pointf.Y = dst_to_src_points[0].Y + x * x_dy + y * y_dy;
+                        src_pointf.X = dst_to_src_points[0].X + delta_xx + delta_yx;
+                        src_pointf.Y = dst_to_src_points[0].Y + delta_xy + delta_yy;
 
                         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, offset_mode);
+                        {
+                            if (lockeddata.PixelFormat != PixelFormat32bppPARGB)
+                                *dst_color = resample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf,
+                                                                   imageAttributes, interpolation, offset_mode);
+                            else
+                                *dst_color = resample_bitmap_pixel_premult(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf,
+                                                                           imageAttributes, interpolation, offset_mode);
+                        }
                         else
                             *dst_color = 0;
+
+                        delta_xx += x_dx;
+                        delta_yx += y_dx;
                     }
+
+                    delta_xy += x_dy;
+                    delta_yy += y_dy;
                 }
             }
             else
@@ -3044,12 +3327,17 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
                 dst_stride = src_stride;
             }
 
+            gdi_transform_acquire(graphics);
+
             stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
-                dst_data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, dst_stride);
+                dst_data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, dst_stride,
+                lockeddata.PixelFormat);
+
+            gdi_transform_release(graphics);
 
-            GdipFree(src_data);
+            heap_free(src_data);
 
-            GdipFree(dst_dyn_data);
+            heap_free(dst_dyn_data);
 
             return stat;
         }
@@ -3058,6 +3346,8 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
             HDC hdc;
             BOOL temp_hdc = FALSE, temp_bitmap = FALSE;
             HBITMAP hbitmap, old_hbm=NULL;
+            HRGN hrgn;
+            INT save_state;
 
             if (!(bitmap->format == PixelFormat16bppRGB555 ||
                   bitmap->format == PixelFormat24bppRGB ||
@@ -3118,6 +3408,18 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
                 old_hbm = SelectObject(hdc, hbitmap);
             }
 
+            save_state = SaveDC(graphics->hdc);
+
+            stat = get_clip_hrgn(graphics, &hrgn);
+
+            if (stat == Ok)
+            {
+                ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
+                DeleteObject(hrgn);
+            }
+
+            gdi_transform_acquire(graphics);
+
             if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
             {
                 gdi_alpha_blend(graphics, pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
@@ -3129,6 +3431,10 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
                     hdc, srcx, srcy, srcwidth, srcheight, SRCCOPY);
             }
 
+            gdi_transform_release(graphics);
+
+            RestoreDC(graphics->hdc, save_state);
+
             if (temp_hdc)
             {
                 SelectObject(hdc, old_hbm);
@@ -3267,6 +3573,12 @@ GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
 
     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
 
+    if (!pen)
+        return InvalidParameter;
+
+    if (pen->unit == UnitPixel && pen->width <= 0.0)
+        return Ok;
+
     pt[0].X = x1;
     pt[0].Y = y1;
     pt[1].X = x2;
@@ -3316,7 +3628,7 @@ GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
 
     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
 
-    ptf = GdipAlloc(count * sizeof(GpPointF));
+    ptf = heap_alloc_zero(count * sizeof(GpPointF));
     if(!ptf) return OutOfMemory;
 
     for(i = 0; i < count; i ++){
@@ -3326,30 +3638,16 @@ GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
 
     retval = GdipDrawLines(graphics, pen, ptf, count);
 
-    GdipFree(ptf);
+    heap_free(ptf);
     return retval;
 }
 
-GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
+static GpStatus GDI32_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
 {
     INT save_state;
     GpStatus retval;
     HRGN hrgn=NULL;
 
-    TRACE("(%p, %p, %p)\n", graphics, pen, path);
-
-    if(!pen || !graphics)
-        return InvalidParameter;
-
-    if(graphics->busy)
-        return ObjectBusy;
-
-    if (!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
-
     save_state = prepare_dc(graphics, pen);
 
     retval = get_clip_hrgn(graphics, &hrgn);
@@ -3357,12 +3655,15 @@ GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
     if (retval != Ok)
         goto end;
 
-    if (hrgn)
-        ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
+    ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
+
+    gdi_transform_acquire(graphics);
 
     retval = draw_poly(graphics, pen, path->pathdata.Points,
                        path->pathdata.Types, path->pathdata.Count, TRUE);
 
+    gdi_transform_release(graphics);
+
 end:
     restore_dc(graphics, save_state);
     DeleteObject(hrgn);
@@ -3370,26 +3671,443 @@ end:
     return retval;
 }
 
-GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
-    REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
+static GpStatus SOFTWARE_GdipDrawThinPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
 {
-    GpStatus status;
-    GpPath *path;
+    GpStatus stat;
+    GpPath* flat_path;
+    GpMatrix* transform;
+    GpRectF gp_bound_rect;
+    GpRect gp_output_area;
+    RECT output_area;
+    INT output_height, output_width;
+    DWORD *output_bits, *brush_bits=NULL;
+    int i;
+    static const BYTE static_dash_pattern[] = {1,1,1,0,1,0,1,0};
+    const BYTE *dash_pattern;
+    INT dash_pattern_size;
+    BYTE *dyn_dash_pattern = NULL;
 
-    TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
-            width, height, startAngle, sweepAngle);
+    stat = GdipClonePath(path, &flat_path);
 
-    if(!graphics || !pen)
-        return InvalidParameter;
+    if (stat != Ok)
+        return stat;
 
-    if(graphics->busy)
-        return ObjectBusy;
+    stat = GdipCreateMatrix(&transform);
 
-    status = GdipCreatePath(FillModeAlternate, &path);
-    if (status != Ok) return status;
+    if (stat == Ok)
+    {
+        stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
+                CoordinateSpaceWorld, transform);
 
-    status = GdipAddPathPie(path, x, y, width, height, startAngle, sweepAngle);
-    if (status == Ok)
+        if (stat == Ok)
+            stat = GdipFlattenPath(flat_path, transform, 1.0);
+
+        GdipDeleteMatrix(transform);
+    }
+
+    /* estimate the output size in pixels, can be larger than necessary */
+    if (stat == Ok)
+    {
+        output_area.left = floorf(flat_path->pathdata.Points[0].X);
+        output_area.right = ceilf(flat_path->pathdata.Points[0].X);
+        output_area.top = floorf(flat_path->pathdata.Points[0].Y);
+        output_area.bottom = ceilf(flat_path->pathdata.Points[0].Y);
+
+        for (i=1; i<flat_path->pathdata.Count; i++)
+        {
+            REAL x, y;
+            x = flat_path->pathdata.Points[i].X;
+            y = flat_path->pathdata.Points[i].Y;
+
+            if (floorf(x) < output_area.left) output_area.left = floorf(x);
+            if (floorf(y) < output_area.top) output_area.top = floorf(y);
+            if (ceilf(x) > output_area.right) output_area.right = ceilf(x);
+            if (ceilf(y) > output_area.bottom) output_area.bottom = ceilf(y);
+        }
+
+        stat = get_graphics_device_bounds(graphics, &gp_bound_rect);
+    }
+
+    if (stat == Ok)
+    {
+        output_area.left = max(output_area.left, floorf(gp_bound_rect.X));
+        output_area.top = max(output_area.top, floorf(gp_bound_rect.Y));
+        output_area.right = min(output_area.right, ceilf(gp_bound_rect.X + gp_bound_rect.Width));
+        output_area.bottom = min(output_area.bottom, ceilf(gp_bound_rect.Y + gp_bound_rect.Height));
+
+        output_width = output_area.right - output_area.left + 1;
+        output_height = output_area.bottom - output_area.top + 1;
+
+        if (output_width <= 0 || output_height <= 0)
+        {
+            GdipDeletePath(flat_path);
+            return Ok;
+        }
+
+        gp_output_area.X = output_area.left;
+        gp_output_area.Y = output_area.top;
+        gp_output_area.Width = output_width;
+        gp_output_area.Height = output_height;
+
+        output_bits = heap_alloc_zero(output_width * output_height * sizeof(DWORD));
+        if (!output_bits)
+            stat = OutOfMemory;
+    }
+
+    if (stat == Ok)
+    {
+        if (pen->brush->bt != BrushTypeSolidColor)
+        {
+            /* allocate and draw brush output */
+            brush_bits = heap_alloc_zero(output_width * output_height * sizeof(DWORD));
+
+            if (brush_bits)
+            {
+                stat = brush_fill_pixels(graphics, pen->brush, brush_bits,
+                    &gp_output_area, output_width);
+            }
+            else
+                stat = OutOfMemory;
+        }
+
+        if (stat == Ok)
+        {
+            /* convert dash pattern to bool array */
+            switch (pen->dash)
+            {
+            case DashStyleCustom:
+            {
+                dash_pattern_size = 0;
+
+                for (i=0; i < pen->numdashes; i++)
+                    dash_pattern_size += gdip_round(pen->dashes[i]);
+
+                if (dash_pattern_size != 0)
+                {
+                    dash_pattern = dyn_dash_pattern = heap_alloc(dash_pattern_size);
+
+                    if (dyn_dash_pattern)
+                    {
+                        int j=0;
+                        for (i=0; i < pen->numdashes; i++)
+                        {
+                            int k;
+                            for (k=0; k < gdip_round(pen->dashes[i]); k++)
+                                dyn_dash_pattern[j++] = (i&1)^1;
+                        }
+                    }
+                    else
+                        stat = OutOfMemory;
+
+                    break;
+                }
+                /* else fall through */
+            }
+            case DashStyleSolid:
+            default:
+                dash_pattern = static_dash_pattern;
+                dash_pattern_size = 1;
+                break;
+            case DashStyleDash:
+                dash_pattern = static_dash_pattern;
+                dash_pattern_size = 4;
+                break;
+            case DashStyleDot:
+                dash_pattern = &static_dash_pattern[4];
+                dash_pattern_size = 2;
+                break;
+            case DashStyleDashDot:
+                dash_pattern = static_dash_pattern;
+                dash_pattern_size = 6;
+                break;
+            case DashStyleDashDotDot:
+                dash_pattern = static_dash_pattern;
+                dash_pattern_size = 8;
+                break;
+            }
+        }
+
+        if (stat == Ok)
+        {
+            /* trace path */
+            GpPointF subpath_start = flat_path->pathdata.Points[0];
+            INT prev_x = INT_MAX, prev_y = INT_MAX;
+            int dash_pos = dash_pattern_size - 1;
+
+            for (i=0; i < flat_path->pathdata.Count; i++)
+            {
+                BYTE type, type2;
+                GpPointF start_point, end_point;
+                GpPoint start_pointi, end_pointi;
+
+                type = flat_path->pathdata.Types[i];
+                if (i+1 < flat_path->pathdata.Count)
+                    type2 = flat_path->pathdata.Types[i+1];
+                else
+                    type2 = PathPointTypeStart;
+
+                start_point = flat_path->pathdata.Points[i];
+
+                if ((type & PathPointTypePathTypeMask) == PathPointTypeStart)
+                    subpath_start = start_point;
+
+                if ((type & PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath)
+                    end_point = subpath_start;
+                else if ((type2 & PathPointTypePathTypeMask) == PathPointTypeStart)
+                    continue;
+                else
+                    end_point = flat_path->pathdata.Points[i+1];
+
+                start_pointi.X = floorf(start_point.X);
+                start_pointi.Y = floorf(start_point.Y);
+                end_pointi.X = floorf(end_point.X);
+                end_pointi.Y = floorf(end_point.Y);
+
+                if(start_pointi.X == end_pointi.X && start_pointi.Y == end_pointi.Y)
+                    continue;
+
+                /* draw line segment */
+                if (abs(start_pointi.Y - end_pointi.Y) > abs(start_pointi.X - end_pointi.X))
+                {
+                    INT x, y, start_y, end_y, step;
+
+                    if (start_pointi.Y < end_pointi.Y)
+                    {
+                        step = 1;
+                        start_y = ceilf(start_point.Y) - output_area.top;
+                        end_y = end_pointi.Y - output_area.top;
+                    }
+                    else
+                    {
+                        step = -1;
+                        start_y = start_point.Y - output_area.top;
+                        end_y = ceilf(end_point.Y) - output_area.top;
+                    }
+
+                    for (y=start_y; y != (end_y+step); y+=step)
+                    {
+                        x = gdip_round( start_point.X +
+                            (end_point.X - start_point.X) * (y + output_area.top - start_point.Y) / (end_point.Y - start_point.Y) )
+                            - output_area.left;
+
+                        if (x == prev_x && y == prev_y)
+                            continue;
+
+                        prev_x = x;
+                        prev_y = y;
+                        dash_pos = (dash_pos + 1 == dash_pattern_size) ? 0 : dash_pos + 1;
+
+                        if (!dash_pattern[dash_pos])
+                            continue;
+
+                        if (x < 0 || x >= output_width || y < 0 || y >= output_height)
+                            continue;
+
+                        if (brush_bits)
+                            output_bits[x + y*output_width] = brush_bits[x + y*output_width];
+                        else
+                            output_bits[x + y*output_width] = ((GpSolidFill*)pen->brush)->color;
+                    }
+                }
+                else
+                {
+                    INT x, y, start_x, end_x, step;
+
+                    if (start_pointi.X < end_pointi.X)
+                    {
+                        step = 1;
+                        start_x = ceilf(start_point.X) - output_area.left;
+                        end_x = end_pointi.X - output_area.left;
+                    }
+                    else
+                    {
+                        step = -1;
+                        start_x = start_point.X - output_area.left;
+                        end_x = ceilf(end_point.X) - output_area.left;
+                    }
+
+                    for (x=start_x; x != (end_x+step); x+=step)
+                    {
+                        y = gdip_round( start_point.Y +
+                            (end_point.Y - start_point.Y) * (x + output_area.left - start_point.X) / (end_point.X - start_point.X) )
+                            - output_area.top;
+
+                        if (x == prev_x && y == prev_y)
+                            continue;
+
+                        prev_x = x;
+                        prev_y = y;
+                        dash_pos = (dash_pos + 1 == dash_pattern_size) ? 0 : dash_pos + 1;
+
+                        if (!dash_pattern[dash_pos])
+                            continue;
+
+                        if (x < 0 || x >= output_width || y < 0 || y >= output_height)
+                            continue;
+
+                        if (brush_bits)
+                            output_bits[x + y*output_width] = brush_bits[x + y*output_width];
+                        else
+                            output_bits[x + y*output_width] = ((GpSolidFill*)pen->brush)->color;
+                    }
+                }
+            }
+        }
+
+        /* draw output image */
+        if (stat == Ok)
+        {
+            gdi_transform_acquire(graphics);
+
+            stat = alpha_blend_pixels(graphics, output_area.left, output_area.top,
+                (BYTE*)output_bits, output_width, output_height, output_width * 4,
+                PixelFormat32bppARGB);
+
+            gdi_transform_release(graphics);
+        }
+
+        heap_free(brush_bits);
+        heap_free(dyn_dash_pattern);
+        heap_free(output_bits);
+    }
+
+    GdipDeletePath(flat_path);
+
+    return stat;
+}
+
+static GpStatus SOFTWARE_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
+{
+    GpStatus stat;
+    GpPath *wide_path;
+    GpMatrix *transform=NULL;
+    REAL flatness=1.0;
+
+    /* Check if the final pen thickness in pixels is too thin. */
+    if (pen->unit == UnitPixel)
+    {
+        if (pen->width < 1.415)
+            return SOFTWARE_GdipDrawThinPath(graphics, pen, path);
+    }
+    else
+    {
+        GpPointF points[3] = {{0,0}, {1,0}, {0,1}};
+
+        points[1].X = pen->width;
+        points[2].Y = pen->width;
+
+        stat = gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice,
+            CoordinateSpaceWorld, points, 3);
+
+        if (stat != Ok)
+            return stat;
+
+        if (((points[1].X-points[0].X)*(points[1].X-points[0].X) +
+             (points[1].Y-points[0].Y)*(points[1].Y-points[0].Y) < 2.0001) &&
+            ((points[2].X-points[0].X)*(points[2].X-points[0].X) +
+             (points[2].Y-points[0].Y)*(points[2].Y-points[0].Y) < 2.0001))
+            return SOFTWARE_GdipDrawThinPath(graphics, pen, path);
+    }
+
+    stat = GdipClonePath(path, &wide_path);
+
+    if (stat != Ok)
+        return stat;
+
+    if (pen->unit == UnitPixel)
+    {
+        /* We have to transform this to device coordinates to get the widths right. */
+        stat = GdipCreateMatrix(&transform);
+
+        if (stat == Ok)
+            stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
+                CoordinateSpaceWorld, transform);
+    }
+    else
+    {
+        /* Set flatness based on the final coordinate space */
+        GpMatrix t;
+
+        stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
+            CoordinateSpaceWorld, &t);
+
+        if (stat != Ok)
+            return stat;
+
+        flatness = 1.0/sqrt(fmax(
+            t.matrix[0] * t.matrix[0] + t.matrix[1] * t.matrix[1],
+            t.matrix[2] * t.matrix[2] + t.matrix[3] * t.matrix[3]));
+    }
+
+    if (stat == Ok)
+        stat = GdipWidenPath(wide_path, pen, transform, flatness);
+
+    if (pen->unit == UnitPixel)
+    {
+        /* Transform the path back to world coordinates */
+        if (stat == Ok)
+            stat = GdipInvertMatrix(transform);
+
+        if (stat == Ok)
+            stat = GdipTransformPath(wide_path, transform);
+    }
+
+    /* Actually draw the path */
+    if (stat == Ok)
+        stat = GdipFillPath(graphics, pen->brush, wide_path);
+
+    GdipDeleteMatrix(transform);
+
+    GdipDeletePath(wide_path);
+
+    return stat;
+}
+
+GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
+{
+    GpStatus retval;
+
+    TRACE("(%p, %p, %p)\n", graphics, pen, path);
+
+    if(!pen || !graphics)
+        return InvalidParameter;
+
+    if(graphics->busy)
+        return ObjectBusy;
+
+    if (path->pathdata.Count == 0)
+        return Ok;
+
+    if (graphics->image && graphics->image->type == ImageTypeMetafile)
+        retval = METAFILE_DrawPath((GpMetafile*)graphics->image, pen, path);
+    else if (!graphics->hdc || graphics->alpha_hdc || !brush_can_fill_path(pen->brush, FALSE))
+        retval = SOFTWARE_GdipDrawPath(graphics, pen, path);
+    else
+        retval = GDI32_GdipDrawPath(graphics, pen, path);
+
+    return retval;
+}
+
+GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
+    REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
+{
+    GpStatus status;
+    GpPath *path;
+
+    TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
+            width, height, startAngle, sweepAngle);
+
+    if(!graphics || !pen)
+        return InvalidParameter;
+
+    if(graphics->busy)
+        return ObjectBusy;
+
+    status = GdipCreatePath(FillModeAlternate, &path);
+    if (status != Ok) return status;
+
+    status = GdipAddPathPie(path, x, y, width, height, startAngle, sweepAngle);
+    if (status == Ok)
         status = GdipDrawPath(graphics, pen, path);
 
     GdipDeletePath(path);
@@ -3475,7 +4193,7 @@ GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
     if(!rects || count<=0)
         return InvalidParameter;
 
-    rectsF = GdipAlloc(sizeof(GpRectF) * count);
+    rectsF = heap_alloc_zero(sizeof(GpRectF) * count);
     if(!rectsF)
         return OutOfMemory;
 
@@ -3487,7 +4205,7 @@ GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
     }
 
     ret = GdipDrawRectangles(graphics, pen, rectsF, count);
-    GdipFree(rectsF);
+    heap_free(rectsF);
 
     return ret;
 }
@@ -3537,7 +4255,7 @@ GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
     if(count == 1)    /* Do nothing */
         return Ok;
 
-    ptf = GdipAlloc(sizeof(GpPointF)*count);
+    ptf = heap_alloc_zero(sizeof(GpPointF)*count);
     if(!ptf)
         return OutOfMemory;
 
@@ -3548,7 +4266,7 @@ GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
 
     stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
 
-    GdipFree(ptf);
+    heap_free(ptf);
 
     return stat;
 }
@@ -3612,7 +4330,7 @@ static GpStatus GDI32_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath
     GpStatus retval;
     HRGN hrgn=NULL;
 
-    if(!graphics->hdc || !brush_can_fill_path(brush))
+    if(!graphics->hdc || !brush_can_fill_path(brush, TRUE))
         return NotImplemented;
 
     save_state = SaveDC(graphics->hdc);
@@ -3625,20 +4343,21 @@ static GpStatus GDI32_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath
     if (retval != Ok)
         goto end;
 
-    if (hrgn)
-        ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
+    ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
+
+    gdi_transform_acquire(graphics);
 
     BeginPath(graphics->hdc);
     retval = draw_poly(graphics, NULL, path->pathdata.Points,
                        path->pathdata.Types, path->pathdata.Count, FALSE);
 
-    if(retval != Ok)
-        goto end;
-
-    EndPath(graphics->hdc);
-    brush_fill_path(graphics, brush);
+    if(retval == Ok)
+    {
+        EndPath(graphics->hdc);
+        retval = brush_fill_path(graphics, brush);
+    }
 
-    retval = Ok;
+    gdi_transform_release(graphics);
 
 end:
     RestoreDC(graphics->hdc, save_state);
@@ -3681,6 +4400,12 @@ GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *p
     if(graphics->busy)
         return ObjectBusy;
 
+    if (!path->pathdata.Count)
+        return Ok;
+
+    if (graphics->image && graphics->image->type == ImageTypeMetafile)
+        return METAFILE_FillPath((GpMetafile*)graphics->image, brush, path);
+
     if (!graphics->image && !graphics->alpha_hdc)
         stat = GDI32_GdipFillPath(graphics, brush, path);
 
@@ -3880,19 +4605,19 @@ GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GD
     if(!rects || count <= 0)
         return InvalidParameter;
 
-    rectsF = GdipAlloc(sizeof(GpRectF)*count);
+    rectsF = heap_alloc_zero(sizeof(GpRectF)*count);
     if(!rectsF)
         return OutOfMemory;
 
     for(i = 0; i < count; i++){
         rectsF[i].X      = (REAL)rects[i].X;
         rectsF[i].Y      = (REAL)rects[i].Y;
-        rectsF[i].X      = (REAL)rects[i].Width;
+        rectsF[i].Width  = (REAL)rects[i].Width;
         rectsF[i].Height = (REAL)rects[i].Height;
     }
 
     ret = GdipFillRectangles(graphics,brush,rectsF,count);
-    GdipFree(rectsF);
+    heap_free(rectsF);
 
     return ret;
 }
@@ -3905,17 +4630,32 @@ static GpStatus GDI32_GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
     HRGN hrgn;
     RECT rc;
 
-    if(!graphics->hdc || !brush_can_fill_path(brush))
+    if(!graphics->hdc || !brush_can_fill_path(brush, TRUE))
         return NotImplemented;
 
-    status = GdipGetRegionHRgn(region, graphics, &hrgn);
-    if(status != Ok)
-        return status;
-
     save_state = SaveDC(graphics->hdc);
     EndPath(graphics->hdc);
 
+    hrgn = NULL;
+    status = get_clip_hrgn(graphics, &hrgn);
+    if (status != Ok)
+    {
+        RestoreDC(graphics->hdc, save_state);
+        return status;
+    }
+
+    ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
+    DeleteObject(hrgn);
+
+    status = GdipGetRegionHRgn(region, graphics, &hrgn);
+    if (status != Ok)
+    {
+        RestoreDC(graphics->hdc, save_state);
+        return status;
+    }
+
     ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
+    DeleteObject(hrgn);
 
     if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
     {
@@ -3923,14 +4663,13 @@ static GpStatus GDI32_GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
         Rectangle(graphics->hdc, rc.left, rc.top, rc.right, rc.bottom);
         EndPath(graphics->hdc);
 
-        brush_fill_path(graphics, brush);
+        status = brush_fill_path(graphics, brush);
     }
 
     RestoreDC(graphics->hdc, save_state);
 
-    DeleteObject(hrgn);
 
-    return Ok;
+    return status;
 }
 
 static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
@@ -3948,14 +4687,17 @@ static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
     if (!brush_can_fill_pixels(brush))
         return NotImplemented;
 
-    stat = get_graphics_bounds(graphics, &graphics_bounds);
+    stat = gdi_transform_acquire(graphics);
+
+    if (stat == Ok)
+        stat = get_graphics_device_bounds(graphics, &graphics_bounds);
 
     if (stat == Ok)
         stat = GdipCloneRegion(region, &temp_region);
 
     if (stat == Ok)
     {
-        stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
+        stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
             CoordinateSpaceWorld, &world_to_device);
 
         if (stat == Ok)
@@ -3973,6 +4715,7 @@ static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
     if (stat == Ok && GetRgnBox(hregion, &bound_rect) == NULLREGION)
     {
         DeleteObject(hregion);
+        gdi_transform_release(graphics);
         return Ok;
     }
 
@@ -3983,7 +4726,7 @@ static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
         gp_bound_rect.Width = bound_rect.right - bound_rect.left;
         gp_bound_rect.Height = bound_rect.bottom - bound_rect.top;
 
-        pixel_data = GdipAlloc(sizeof(*pixel_data) * gp_bound_rect.Width * gp_bound_rect.Height);
+        pixel_data = heap_alloc_zero(sizeof(*pixel_data) * gp_bound_rect.Width * gp_bound_rect.Height);
         if (!pixel_data)
             stat = OutOfMemory;
 
@@ -3995,14 +4738,17 @@ static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
             if (stat == Ok)
                 stat = alpha_blend_pixels_hrgn(graphics, gp_bound_rect.X,
                     gp_bound_rect.Y, (BYTE*)pixel_data, gp_bound_rect.Width,
-                    gp_bound_rect.Height, gp_bound_rect.Width * 4, hregion);
+                    gp_bound_rect.Height, gp_bound_rect.Width * 4, hregion,
+                    PixelFormat32bppARGB);
 
-            GdipFree(pixel_data);
+            heap_free(pixel_data);
         }
 
         DeleteObject(hregion);
     }
 
+    gdi_transform_release(graphics);
+
     return stat;
 }
 
@@ -4259,6 +5005,7 @@ GpStatus WINGDIPAPI GdipGetVisibleClipBounds(GpGraphics *graphics, GpRectF *rect
 {
     GpRegion *clip_rgn;
     GpStatus stat;
+    GpMatrix device_to_world;
 
     TRACE("(%p, %p)\n", graphics, rect);
 
@@ -4275,6 +5022,13 @@ GpStatus WINGDIPAPI GdipGetVisibleClipBounds(GpGraphics *graphics, GpRectF *rect
     if((stat = get_visible_clip_region(graphics, clip_rgn)) != Ok)
         goto cleanup;
 
+    /* transform to world coordinates */
+    if((stat = get_graphics_transform(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, &device_to_world)) != Ok)
+        goto cleanup;
+
+    if((stat = GdipTransformRegion(clip_rgn, &device_to_world)) != Ok)
+        goto cleanup;
+
     /* get bounds of the region */
     stat = GdipGetRegionBounds(clip_rgn, graphics, rect);
 
@@ -4333,10 +5087,13 @@ GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
     if(graphics->busy)
         return ObjectBusy;
 
+    if (graphics->image && graphics->image->type == ImageTypeMetafile)
+        return METAFILE_GraphicsClear((GpMetafile*)graphics->image, color);
+
     if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
         return stat;
 
-    if((stat = get_graphics_bounds(graphics, &wnd_rect)) != Ok){
+    if((stat = GdipGetVisibleClipBounds(graphics, &wnd_rect)) != Ok){
         GdipDeleteBrush((GpBrush*)brush);
         return stat;
     }
@@ -4458,23 +5215,14 @@ GpStatus gdip_format_string(HDC hdc,
     INT hotkeyprefix_count=0;
     INT hotkeyprefix_pos=0, hotkeyprefix_end_pos=0;
     BOOL seen_prefix = FALSE;
-    GpStringFormat *dyn_format=NULL;
 
     if(length == -1) length = lstrlenW(string);
 
-    stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
+    stringdup = heap_alloc_zero((length + 1) * sizeof(WCHAR));
     if(!stringdup) return OutOfMemory;
 
     if (!format)
-    {
-        stat = GdipStringFormatGetGenericDefault(&dyn_format);
-        if (stat != Ok)
-        {
-            GdipFree(stringdup);
-            return stat;
-        }
-        format = dyn_format;
-    }
+        format = &default_drawstring_format;
 
     nwidth = rect->Width;
     nheight = rect->Height;
@@ -4496,7 +5244,7 @@ GpStatus gdip_format_string(HDC hdc,
     }
 
     if (hotkeyprefix_count)
-        hotkeyprefix_offsets = GdipAlloc(sizeof(INT) * hotkeyprefix_count);
+        hotkeyprefix_offsets = heap_alloc_zero(sizeof(INT) * hotkeyprefix_count);
 
     hotkeyprefix_count = 0;
 
@@ -4622,9 +5370,8 @@ GpStatus gdip_format_string(HDC hdc,
             break;
     }
 
-    GdipFree(stringdup);
-    GdipFree(hotkeyprefix_offsets);
-    GdipDeleteStringFormat(dyn_format);
+    heap_free(stringdup);
+    heap_free(hotkeyprefix_offsets);
 
     return stat;
 }
@@ -4687,7 +5434,7 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
     RectF scaled_rect;
     REAL margin_x;
 
-    TRACE("(%p %s %d %p %s %p %d %p)\n", graphics, debugstr_w(string),
+    TRACE("(%p %s %d %p %s %p %d %p)\n", graphics, debugstr_wn(string, length),
             length, font, debugstr_rectf(layoutRect), stringFormat, regionCount, regions);
 
     if (!(graphics && string && font && layoutRect && stringFormat && regions))
@@ -4713,7 +5460,7 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
     pt[1].Y = 0.0;
     pt[2].X = 0.0;
     pt[2].Y = 1.0;
-    GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
+    gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, 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)+
@@ -4727,6 +5474,9 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
     scaled_rect.Width = layoutRect->Width * args.rel_width;
     scaled_rect.Height = layoutRect->Height * args.rel_height;
 
+    if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23;
+    if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23;
+
     get_font_hfont(graphics, font, stringFormat, &gdifont, NULL);
     oldfont = SelectObject(hdc, gdifont);
 
@@ -4739,9 +5489,13 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
 
     args.regions = regions;
 
+    gdi_transform_acquire(graphics);
+
     stat = gdip_format_string(hdc, string, length, font, &scaled_rect, stringFormat,
         (stringFormat->attr & StringFormatFlagsNoClip) != 0, measure_ranges_callback, &args);
 
+    gdi_transform_release(graphics);
+
     SelectObject(hdc, oldfont);
     DeleteObject(gdifont);
 
@@ -4829,7 +5583,7 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
     pt[1].Y = 0.0;
     pt[2].X = 0.0;
     pt[2].Y = 1.0;
-    GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
+    gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, 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)+
@@ -4864,9 +5618,13 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
     args.linesfilled = &lines;
     lines = glyphs = 0;
 
+    gdi_transform_acquire(graphics);
+
     gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE,
         measure_string_callback, &args);
 
+    gdi_transform_release(graphics);
+
     if (linesfilled) *linesfilled = lines;
     if (codepointsfitted) *codepointsfitted = glyphs;
 
@@ -4973,19 +5731,19 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string
 
         /* Should be no need to explicitly test for StringAlignmentNear as
          * that is default behavior if no alignment is passed. */
-        if(format->vertalign != StringAlignmentNear){
+        if(format->line_align != StringAlignmentNear){
             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)
+            if(format->line_align == StringAlignmentCenter)
                 offsety = (rect->Height - bounds.Height) / 2;
-            else if(format->vertalign == StringAlignmentFar)
+            else if(format->line_align == StringAlignmentFar)
                 offsety = (rect->Height - bounds.Height);
         }
-        TRACE("vertical align %d, offsety %f\n", format->vertalign, offsety);
+        TRACE("line align %d, offsety %f\n", format->line_align, offsety);
     }
 
     save_state = SaveDC(hdc);
@@ -4996,7 +5754,7 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string
     pt[1].Y = 0.0;
     pt[2].X = 0.0;
     pt[2].Y = 1.0;
-    GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
+    gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, 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));
     rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
@@ -5006,7 +5764,8 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string
     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 + rect->Height;
-    transform_and_round_points(graphics, corners, rectcpy, 4);
+    gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, rectcpy, 4);
+    round_points(corners, rectcpy, 4);
 
     margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0;
     margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
@@ -5045,12 +5804,16 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string
     args.rel_width = rel_width;
     args.rel_height = rel_height;
 
+    gdi_transform_acquire(graphics);
+
     GetTextMetricsW(hdc, &textmetric);
     args.ascent = textmetric.tmAscent / rel_height;
 
     gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE,
         draw_string_callback, &args);
 
+    gdi_transform_release(graphics);
+
     DeleteObject(rgn);
     DeleteObject(gdifont);
 
@@ -5076,6 +5839,8 @@ GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
 
 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
 {
+    GpStatus stat;
+
     TRACE("(%p)\n", graphics);
 
     if(!graphics)
@@ -5084,17 +5849,21 @@ GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
     if(graphics->busy)
         return ObjectBusy;
 
-    return GdipSetMatrixElements(&graphics->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
-}
+    if (graphics->image && graphics->image->type == ImageTypeMetafile) {
+        stat = METAFILE_ResetWorldTransform((GpMetafile*)graphics->image);
 
-GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
-{
-    return GdipEndContainer(graphics, state);
+        if (stat != Ok)
+            return stat;
+    }
+
+    return GdipSetMatrixElements(&graphics->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
 }
 
 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
     GpMatrixOrder order)
 {
+    GpStatus stat;
+
     TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
 
     if(!graphics)
@@ -5103,45 +5872,119 @@ GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
     if(graphics->busy)
         return ObjectBusy;
 
-    return GdipRotateMatrix(&graphics->worldtrans, angle, order);
-}
+    if (graphics->image && graphics->image->type == ImageTypeMetafile) {
+        stat = METAFILE_RotateWorldTransform((GpMetafile*)graphics->image, angle, order);
 
-GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
-{
-    return GdipBeginContainer2(graphics, state);
+        if (stat != Ok)
+            return stat;
+    }
+
+    return GdipRotateMatrix(&graphics->worldtrans, angle, order);
 }
 
-GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics,
-        GraphicsContainer *state)
+static GpStatus begin_container(GpGraphics *graphics,
+    GraphicsContainerType type, GraphicsContainer *state)
 {
     GraphicsContainerItem *container;
     GpStatus sts;
 
-    TRACE("(%p, %p)\n", graphics, state);
-
     if(!graphics || !state)
         return InvalidParameter;
 
-    sts = init_container(&container, graphics);
+    sts = init_container(&container, graphics, type);
     if(sts != Ok)
         return sts;
 
     list_add_head(&graphics->containers, &container->entry);
     *state = graphics->contid = container->contid;
 
+    if (graphics->image && graphics->image->type == ImageTypeMetafile) {
+        if (type == BEGIN_CONTAINER)
+            METAFILE_BeginContainerNoParams((GpMetafile*)graphics->image, container->contid);
+        else
+            METAFILE_SaveGraphics((GpMetafile*)graphics->image, container->contid);
+    }
+
     return Ok;
 }
 
+GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
+{
+    TRACE("(%p, %p)\n", graphics, state);
+    return begin_container(graphics, SAVE_GRAPHICS, state);
+}
+
+GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics,
+        GraphicsContainer *state)
+{
+    TRACE("(%p, %p)\n", graphics, state);
+    return begin_container(graphics, BEGIN_CONTAINER, state);
+}
+
 GpStatus WINGDIPAPI GdipBeginContainer(GpGraphics *graphics, GDIPCONST GpRectF *dstrect, GDIPCONST GpRectF *srcrect, GpUnit unit, GraphicsContainer *state)
 {
-    FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
-    return NotImplemented;
+    GraphicsContainerItem *container;
+    GpMatrix transform;
+    GpStatus stat;
+    GpRectF scaled_srcrect;
+    REAL scale_x, scale_y;
+
+    TRACE("(%p, %s, %s, %d, %p)\n", graphics, debugstr_rectf(dstrect), debugstr_rectf(srcrect), unit, state);
+
+    if(!graphics || !dstrect || !srcrect || unit < UnitPixel || unit > UnitMillimeter || !state)
+        return InvalidParameter;
+
+    stat = init_container(&container, graphics, BEGIN_CONTAINER);
+    if(stat != Ok)
+        return stat;
+
+    list_add_head(&graphics->containers, &container->entry);
+    *state = graphics->contid = container->contid;
+
+    scale_x = units_to_pixels(1.0, unit, graphics->xres);
+    scale_y = units_to_pixels(1.0, unit, graphics->yres);
+
+    scaled_srcrect.X = scale_x * srcrect->X;
+    scaled_srcrect.Y = scale_y * srcrect->Y;
+    scaled_srcrect.Width = scale_x * srcrect->Width;
+    scaled_srcrect.Height = scale_y * srcrect->Height;
+
+    transform.matrix[0] = dstrect->Width / scaled_srcrect.Width;
+    transform.matrix[1] = 0.0;
+    transform.matrix[2] = 0.0;
+    transform.matrix[3] = dstrect->Height / scaled_srcrect.Height;
+    transform.matrix[4] = dstrect->X - scaled_srcrect.X;
+    transform.matrix[5] = dstrect->Y - scaled_srcrect.Y;
+
+    GdipMultiplyMatrix(&graphics->worldtrans, &transform, MatrixOrderPrepend);
+
+    if (graphics->image && graphics->image->type == ImageTypeMetafile) {
+        METAFILE_BeginContainer((GpMetafile*)graphics->image, dstrect, srcrect, unit, container->contid);
+    }
+
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipBeginContainerI(GpGraphics *graphics, GDIPCONST GpRect *dstrect, GDIPCONST GpRect *srcrect, GpUnit unit, GraphicsContainer *state)
 {
-    FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
-    return NotImplemented;
+    GpRectF dstrectf, srcrectf;
+
+    TRACE("(%p, %p, %p, %d, %p)\n", graphics, dstrect, srcrect, unit, state);
+
+    if (!dstrect || !srcrect)
+        return InvalidParameter;
+
+    dstrectf.X = dstrect->X;
+    dstrectf.Y = dstrect->Y;
+    dstrectf.Width = dstrect->Width;
+    dstrectf.Height = dstrect->Height;
+
+    srcrectf.X = srcrect->X;
+    srcrectf.Y = srcrect->Y;
+    srcrectf.Width = srcrect->Width;
+    srcrectf.Height = srcrect->Height;
+
+    return GdipBeginContainer(graphics, &dstrectf, &srcrectf, unit, state);
 }
 
 GpStatus WINGDIPAPI GdipComment(GpGraphics *graphics, UINT sizeData, GDIPCONST BYTE *data)
@@ -5150,18 +5993,17 @@ GpStatus WINGDIPAPI GdipComment(GpGraphics *graphics, UINT sizeData, GDIPCONST B
     return NotImplemented;
 }
 
-GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsContainer state)
+static GpStatus end_container(GpGraphics *graphics, GraphicsContainerType type,
+    GraphicsContainer state)
 {
     GpStatus sts;
     GraphicsContainerItem *container, *container2;
 
-    TRACE("(%p, %x)\n", graphics, state);
-
     if(!graphics)
         return InvalidParameter;
 
     LIST_FOR_EACH_ENTRY(container, &graphics->containers, GraphicsContainerItem, entry){
-        if(container->contid == state)
+        if(container->contid == state && container->type == type)
             break;
     }
 
@@ -5184,12 +6026,33 @@ GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsContainer sta
     list_remove(&container->entry);
     delete_container(container);
 
+    if (graphics->image && graphics->image->type == ImageTypeMetafile) {
+        if (type == BEGIN_CONTAINER)
+            METAFILE_EndContainer((GpMetafile*)graphics->image, state);
+        else
+            METAFILE_RestoreGraphics((GpMetafile*)graphics->image, state);
+    }
+
     return Ok;
 }
 
+GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsContainer state)
+{
+    TRACE("(%p, %x)\n", graphics, state);
+    return end_container(graphics, BEGIN_CONTAINER, state);
+}
+
+GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
+{
+    TRACE("(%p, %x)\n", graphics, state);
+    return end_container(graphics, SAVE_GRAPHICS, state);
+}
+
 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
     REAL sy, GpMatrixOrder order)
 {
+    GpStatus stat;
+
     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
 
     if(!graphics)
@@ -5198,6 +6061,13 @@ GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
     if(graphics->busy)
         return ObjectBusy;
 
+    if (graphics->image && graphics->image->type == ImageTypeMetafile) {
+        stat = METAFILE_ScaleWorldTransform((GpMetafile*)graphics->image, sx, sy, order);
+
+        if (stat != Ok)
+            return stat;
+    }
+
     return GdipScaleMatrix(&graphics->worldtrans, sx, sy, order);
 }
 
@@ -5223,6 +6093,19 @@ GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
     if(graphics->busy)
         return ObjectBusy;
 
+    if(graphics->compmode == mode)
+        return Ok;
+
+    if(graphics->image && graphics->image->type == ImageTypeMetafile)
+    {
+        GpStatus stat;
+
+        stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
+                EmfPlusRecordTypeSetCompositingMode, mode);
+        if(stat != Ok)
+            return stat;
+    }
+
     graphics->compmode = mode;
 
     return Ok;
@@ -5239,6 +6122,19 @@ GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
     if(graphics->busy)
         return ObjectBusy;
 
+    if(graphics->compqual == quality)
+        return Ok;
+
+    if(graphics->image && graphics->image->type == ImageTypeMetafile)
+    {
+        GpStatus stat;
+
+        stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
+                EmfPlusRecordTypeSetCompositingQuality, quality);
+        if(stat != Ok)
+            return stat;
+    }
+
     graphics->compqual = quality;
 
     return Ok;
@@ -5261,6 +6157,19 @@ GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
     if (mode == InterpolationModeHighQuality)
         mode = InterpolationModeHighQualityBicubic;
 
+    if (mode == graphics->interpolation)
+        return Ok;
+
+    if (graphics->image && graphics->image->type == ImageTypeMetafile)
+    {
+        GpStatus stat;
+
+        stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
+                EmfPlusRecordTypeSetInterpolationMode, mode);
+        if (stat != Ok)
+            return stat;
+    }
+
     graphics->interpolation = mode;
 
     return Ok;
@@ -5328,6 +6237,19 @@ GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
     if(graphics->busy)
         return ObjectBusy;
 
+    if(graphics->pixeloffset == mode)
+        return Ok;
+
+    if(graphics->image && graphics->image->type == ImageTypeMetafile)
+    {
+        GpStatus stat;
+
+        stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
+                EmfPlusRecordTypeSetPixelOffsetMode, mode);
+        if(stat != Ok)
+            return stat;
+    }
+
     graphics->pixeloffset = mode;
 
     return Ok;
@@ -5374,6 +6296,20 @@ GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mod
     if(graphics->busy)
         return ObjectBusy;
 
+    if(graphics->smoothing == mode)
+        return Ok;
+
+    if(graphics->image && graphics->image->type == ImageTypeMetafile) {
+         GpStatus stat;
+         BOOL antialias = (mode != SmoothingModeDefault &&
+                 mode != SmoothingModeNone && mode != SmoothingModeHighSpeed);
+
+         stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
+                 EmfPlusRecordTypeSetAntiAliasMode, (mode << 1) + antialias);
+         if(stat != Ok)
+             return stat;
+     }
+
     graphics->smoothing = mode;
 
     return Ok;
@@ -5402,6 +6338,18 @@ GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
     if(graphics->busy)
         return ObjectBusy;
 
+    if(graphics->texthint == hint)
+        return Ok;
+
+    if(graphics->image && graphics->image->type == ImageTypeMetafile) {
+        GpStatus stat;
+
+        stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
+                EmfPlusRecordTypeSetTextRenderingHint, hint);
+        if(stat != Ok)
+            return stat;
+    }
+
     graphics->texthint = hint;
 
     return Ok;
@@ -5409,6 +6357,8 @@ GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
 
 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
 {
+    GpStatus stat;
+
     TRACE("(%p, %p)\n", graphics, matrix);
 
     if(!graphics || !matrix)
@@ -5421,6 +6371,13 @@ GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix
           matrix->matrix[0], matrix->matrix[1], matrix->matrix[2],
           matrix->matrix[3], matrix->matrix[4], matrix->matrix[5]);
 
+    if (graphics->image && graphics->image->type == ImageTypeMetafile) {
+        stat = METAFILE_SetWorldTransform((GpMetafile*)graphics->image, matrix);
+
+        if (stat != Ok)
+            return stat;
+    }
+
     graphics->worldtrans = *matrix;
 
     return Ok;
@@ -5429,6 +6386,8 @@ GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix
 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
     REAL dy, GpMatrixOrder order)
 {
+    GpStatus stat;
+
     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
 
     if(!graphics)
@@ -5437,6 +6396,13 @@ GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
     if(graphics->busy)
         return ObjectBusy;
 
+    if (graphics->image && graphics->image->type == ImageTypeMetafile) {
+        stat = METAFILE_TranslateWorldTransform((GpMetafile*)graphics->image, dx, dy, order);
+
+        if (stat != Ok)
+            return stat;
+    }
+
     return GdipTranslateMatrix(&graphics->worldtrans, dx, dy, order);
 }
 
@@ -5447,6 +6413,7 @@ GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode
 {
     GpRegion *region;
     GpStatus status;
+    GpMatrix transform;
 
     TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
 
@@ -5456,14 +6423,21 @@ GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode
     if(graphics->busy)
         return ObjectBusy;
 
-    /* hrgn is already in device units */
+    /* hrgn is in gdi32 device units */
     status = GdipCreateRegionHrgn(hrgn, &region);
-    if(status != Ok)
-        return status;
 
-    status = GdipCombineRegionRegion(graphics->clip, region, mode);
+    if (status == Ok)
+    {
+        status = get_graphics_transform(graphics, CoordinateSpaceDevice, WineCoordinateSpaceGdiDevice, &transform);
 
-    GdipDeleteRegion(region);
+        if (status == Ok)
+            status = GdipTransformRegion(region, &transform);
+
+        if (status == Ok)
+            status = GdipCombineRegionRegion(graphics->clip, region, mode);
+
+        GdipDeleteRegion(region);
+    }
     return status;
 }
 
@@ -5512,6 +6486,13 @@ GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
     if(graphics->busy)
         return ObjectBusy;
 
+    if (graphics->image && graphics->image->type == ImageTypeMetafile)
+    {
+        status = METAFILE_SetClipRect((GpMetafile*)graphics->image, x, y, width, height, mode);
+        if (status != Ok)
+            return status;
+    }
+
     rect.X = x;
     rect.Y = y;
     rect.Width  = width;
@@ -5520,9 +6501,12 @@ GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
     if (status == Ok)
     {
         GpMatrix world_to_device;
+        BOOL identity;
 
         get_graphics_transform(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
-        status = GdipTransformRegion(region, &world_to_device);
+        status = GdipIsMatrixIdentity(&world_to_device, &identity);
+        if (status == Ok && !identity)
+            status = GdipTransformRegion(region, &world_to_device);
         if (status == Ok)
             status = GdipCombineRegionRegion(graphics->clip, region, mode);
 
@@ -5560,13 +6544,23 @@ GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
     if(graphics->busy)
         return ObjectBusy;
 
+    if (graphics->image && graphics->image->type == ImageTypeMetafile)
+    {
+        status = METAFILE_SetClipRegion((GpMetafile*)graphics->image, region, mode);
+        if (status != Ok)
+            return status;
+    }
+
     status = GdipCloneRegion(region, &clip);
     if (status == Ok)
     {
         GpMatrix world_to_device;
+        BOOL identity;
 
         get_graphics_transform(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
-        status = GdipTransformRegion(clip, &world_to_device);
+        status = GdipIsMatrixIdentity(&world_to_device, &identity);
+        if (status == Ok && !identity)
+            status = GdipTransformRegion(clip, &world_to_device);
         if (status == Ok)
             status = GdipCombineRegionRegion(graphics->clip, clip, mode);
 
@@ -5578,8 +6572,8 @@ GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
     INT count)
 {
-    INT save_state;
-    POINT *pti;
+    GpStatus status;
+    GpPath* path;
 
     TRACE("(%p, %p, %d)\n", graphics, points, count);
 
@@ -5589,24 +6583,16 @@ GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST Gp
     if(graphics->busy)
         return ObjectBusy;
 
-    if (!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
-
-    pti = GdipAlloc(sizeof(POINT) * count);
-
-    save_state = prepare_dc(graphics, pen);
-    SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
+    status = GdipCreatePath(FillModeAlternate, &path);
+    if (status != Ok) return status;
 
-    transform_and_round_points(graphics, pti, (GpPointF*)points, count);
-    Polygon(graphics->hdc, pti, count);
+    status = GdipAddPathPolygon(path, points, count);
+    if (status == Ok)
+        status = GdipDrawPath(graphics, pen, path);
 
-    restore_dc(graphics, save_state);
-    GdipFree(pti);
+    GdipDeletePath(path);
 
-    return Ok;
+    return status;
 }
 
 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
@@ -5619,7 +6605,7 @@ GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST G
     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
 
     if(count<=0)    return InvalidParameter;
-    ptf = GdipAlloc(sizeof(GpPointF) * count);
+    ptf = heap_alloc_zero(sizeof(GpPointF) * count);
 
     for(i = 0;i < count; i++){
         ptf[i].X = (REAL)points[i].X;
@@ -5627,7 +6613,7 @@ GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST G
     }
 
     ret = GdipDrawPolygon(graphics,pen,ptf,count);
-    GdipFree(ptf);
+    heap_free(ptf);
 
     return ret;
 }
@@ -5674,6 +6660,13 @@ GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST G
     if(graphics->busy)
         return ObjectBusy;
 
+    if (graphics->image && graphics->image->type == ImageTypeMetafile) {
+        ret = METAFILE_MultiplyWorldTransform((GpMetafile*)graphics->image, matrix, order);
+
+        if (ret != Ok)
+            return ret;
+    }
+
     m = graphics->worldtrans;
 
     ret = GdipMultiplyMatrix(&m, matrix, order);
@@ -5702,7 +6695,7 @@ GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
     {
         stat = METAFILE_GetDC((GpMetafile*)graphics->image, hdc);
     }
-    else if (!graphics->hdc || graphics->alpha_hdc ||
+    else if (!graphics->hdc ||
         (graphics->image && graphics->image->type == ImageTypeBitmap && ((GpBitmap*)graphics->image)->format & PixelFormatAlpha))
     {
         /* Create a fake HDC and fill it with a constant color. */
@@ -5794,7 +6787,7 @@ GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
         /* Write the changed pixels to the real target. */
         alpha_blend_pixels(graphics, 0, 0, graphics->temp_bits,
             graphics->temp_hbitmap_width, graphics->temp_hbitmap_height,
-            graphics->temp_hbitmap_width * 4);
+            graphics->temp_hbitmap_width * 4, PixelFormat32bppARGB);
 
         /* Clean up. */
         DeleteDC(graphics->temp_hdc);
@@ -5841,12 +6834,41 @@ GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
     /* free everything except root node and header */
     delete_element(&region->node);
     memcpy(region, clip, sizeof(GpRegion));
-    GdipFree(clip);
+    heap_free(clip);
+
+    return Ok;
+}
+
+GpStatus gdi_transform_acquire(GpGraphics *graphics)
+{
+    if (graphics->gdi_transform_acquire_count == 0 && graphics->hdc)
+    {
+        graphics->gdi_transform_save = SaveDC(graphics->hdc);
+        SetGraphicsMode(graphics->hdc, GM_COMPATIBLE);
+        SetMapMode(graphics->hdc, MM_TEXT);
+        SetWindowOrgEx(graphics->hdc, 0, 0, NULL);
+        SetViewportOrgEx(graphics->hdc, 0, 0, NULL);
+    }
+    graphics->gdi_transform_acquire_count++;
+    return Ok;
+}
 
+GpStatus gdi_transform_release(GpGraphics *graphics)
+{
+    if (graphics->gdi_transform_acquire_count <= 0)
+    {
+        ERR("called without matching gdi_transform_acquire\n");
+        return GenericError;
+    }
+    if (graphics->gdi_transform_acquire_count == 1 && graphics->hdc)
+    {
+        RestoreDC(graphics->hdc, graphics->gdi_transform_save);
+    }
+    graphics->gdi_transform_acquire_count--;
     return Ok;
 }
 
-static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
+GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
         GpCoordinateSpace src_space, GpMatrix *matrix)
 {
     GpStatus stat = Ok;
@@ -5865,23 +6887,29 @@ static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace d
             scale_y *= graphics->scale;
         }
 
-        /* transform from src_space to CoordinateSpacePage */
-        switch (src_space)
+        if (dst_space < src_space)
         {
-        case CoordinateSpaceWorld:
-            GdipMultiplyMatrix(matrix, &graphics->worldtrans, MatrixOrderAppend);
-            break;
-        case CoordinateSpacePage:
-            break;
-        case CoordinateSpaceDevice:
-            GdipScaleMatrix(matrix, 1.0/scale_x, 1.0/scale_y, MatrixOrderAppend);
-            break;
-        }
-
-        /* transform from CoordinateSpacePage to dst_space */
-        switch (dst_space)
-        {
-        case CoordinateSpaceWorld:
+            /* transform towards world space */
+            switch ((int)src_space)
+            {
+            case WineCoordinateSpaceGdiDevice:
+            {
+                GpMatrix gdixform;
+                gdixform = graphics->gdi_transform;
+                stat = GdipInvertMatrix(&gdixform);
+                if (stat != Ok)
+                    break;
+                GdipMultiplyMatrix(matrix, &gdixform, MatrixOrderAppend);
+                if (dst_space == CoordinateSpaceDevice)
+                    break;
+                /* else fall-through */
+            }
+            case CoordinateSpaceDevice:
+                GdipScaleMatrix(matrix, 1.0/scale_x, 1.0/scale_y, MatrixOrderAppend);
+                if (dst_space == CoordinateSpacePage)
+                    break;
+                /* else fall-through */
+            case CoordinateSpacePage:
             {
                 GpMatrix inverted_transform = graphics->worldtrans;
                 stat = GdipInvertMatrix(&inverted_transform);
@@ -5889,23 +6917,52 @@ static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace d
                     GdipMultiplyMatrix(matrix, &inverted_transform, MatrixOrderAppend);
                 break;
             }
-        case CoordinateSpacePage:
-            break;
-        case CoordinateSpaceDevice:
-            GdipScaleMatrix(matrix, scale_x, scale_y, MatrixOrderAppend);
-            break;
+            }
+        }
+        else
+        {
+            /* transform towards device space */
+            switch ((int)src_space)
+            {
+            case CoordinateSpaceWorld:
+                GdipMultiplyMatrix(matrix, &graphics->worldtrans, MatrixOrderAppend);
+                if (dst_space == CoordinateSpacePage)
+                    break;
+                /* else fall-through */
+            case CoordinateSpacePage:
+                GdipScaleMatrix(matrix, scale_x, scale_y, MatrixOrderAppend);
+                if (dst_space == CoordinateSpaceDevice)
+                    break;
+                /* else fall-through */
+            case CoordinateSpaceDevice:
+            {
+                GdipMultiplyMatrix(matrix, &graphics->gdi_transform, MatrixOrderAppend);
+                break;
+            }
+            }
         }
     }
     return stat;
 }
 
-GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
-                                        GpCoordinateSpace src_space, GpPointF *points, INT count)
+GpStatus gdip_transform_points(GpGraphics *graphics, GpCoordinateSpace dst_space,
+                               GpCoordinateSpace src_space, GpPointF *points, INT count)
 {
     GpMatrix matrix;
     GpStatus stat;
 
-    if(!graphics || !points || count <= 0)
+    stat = get_graphics_transform(graphics, dst_space, src_space, &matrix);
+    if (stat != Ok) return stat;
+
+    return GdipTransformMatrixPoints(&matrix, points, count);
+}
+
+GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
+                                        GpCoordinateSpace src_space, GpPointF *points, INT count)
+{
+    if(!graphics || !points || count <= 0 ||
+       dst_space < 0 || dst_space > CoordinateSpaceDevice ||
+       src_space < 0 || src_space > CoordinateSpaceDevice)
         return InvalidParameter;
 
     if(graphics->busy)
@@ -5915,10 +6972,7 @@ 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;
-
-    return GdipTransformMatrixPoints(&matrix, points, count);
+    return gdip_transform_points(graphics, dst_space, src_space, points, count);
 }
 
 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
@@ -5933,7 +6987,7 @@ GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace
     if(count <= 0)
         return InvalidParameter;
 
-    pointsF = GdipAlloc(sizeof(GpPointF) * count);
+    pointsF = heap_alloc_zero(sizeof(GpPointF) * count);
     if(!pointsF)
         return OutOfMemory;
 
@@ -5949,7 +7003,7 @@ GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace
             points[i].X = gdip_round(pointsF[i].X);
             points[i].Y = gdip_round(pointsF[i].Y);
         }
-    GdipFree(pointsF);
+    heap_free(pointsF);
 
     return ret;
 }
@@ -6054,7 +7108,7 @@ GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT
         GpMatrix xform = *matrix;
         GdipTransformMatrixPoints(&xform, pt, 3);
     }
-    GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
+    gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, 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));
     rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
@@ -6062,7 +7116,7 @@ GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT
 
     if (flags & DriverStringOptionsCmapLookup)
     {
-        glyph_indices = dynamic_glyph_indices = GdipAlloc(sizeof(WORD) * length);
+        glyph_indices = dynamic_glyph_indices = heap_alloc_zero(sizeof(WORD) * length);
         if (!glyph_indices)
         {
             DeleteDC(hdc);
@@ -6104,7 +7158,7 @@ GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT
         if (max_x < x) max_x = x;
     }
 
-    GdipFree(dynamic_glyph_indices);
+    heap_free(dynamic_glyph_indices);
     DeleteDC(hdc);
     DeleteObject(hfont);
 
@@ -6126,6 +7180,8 @@ static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT1
     GpPointF pt;
     HFONT hfont;
     UINT eto_flags=0;
+    GpStatus status;
+    HRGN hrgn;
 
     if (flags & unsupported_flags)
         FIXME("Ignoring flags %x\n", flags & unsupported_flags);
@@ -6137,16 +7193,28 @@ static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT1
     SetBkMode(graphics->hdc, TRANSPARENT);
     SetTextColor(graphics->hdc, get_gdi_brush_color(brush));
 
+    status = get_clip_hrgn(graphics, &hrgn);
+
+    if (status == Ok)
+    {
+        ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
+        DeleteObject(hrgn);
+    }
+
     pt = positions[0];
-    GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &pt, 1);
+    gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, &pt, 1);
 
     get_font_hfont(graphics, font, format, &hfont, matrix);
     SelectObject(graphics->hdc, hfont);
 
     SetTextAlign(graphics->hdc, TA_BASELINE|TA_LEFT);
 
+    gdi_transform_acquire(graphics);
+
     ExtTextOutW(graphics->hdc, gdip_round(pt.X), gdip_round(pt.Y), eto_flags, NULL, text, length, NULL);
 
+    gdi_transform_release(graphics);
+
     RestoreDC(graphics->hdc, save_state);
 
     DeleteObject(hfont);
@@ -6186,7 +7254,7 @@ static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UI
     if (flags & unsupported_flags)
         FIXME("Ignoring flags %x\n", flags & unsupported_flags);
 
-    pti = GdipAlloc(sizeof(POINT) * length);
+    pti = heap_alloc_zero(sizeof(POINT) * length);
     if (!pti)
         return OutOfMemory;
 
@@ -6194,22 +7262,24 @@ static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UI
     {
         real_position = positions[0];
 
-        transform_and_round_points(graphics, pti, &real_position, 1);
+        gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, &real_position, 1);
+        round_points(pti, &real_position, 1);
     }
     else
     {
-        real_positions = GdipAlloc(sizeof(PointF) * length);
+        real_positions = heap_alloc_zero(sizeof(PointF) * length);
         if (!real_positions)
         {
-            GdipFree(pti);
+            heap_free(pti);
             return OutOfMemory;
         }
 
         memcpy(real_positions, positions, sizeof(PointF) * length);
 
-        transform_and_round_points(graphics, pti, real_positions, length);
+        gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, real_positions, length);
+        round_points(pti, real_positions, length);
 
-        GdipFree(real_positions);
+        heap_free(real_positions);
     }
 
     get_font_hfont(graphics, font, format, &hfont, matrix);
@@ -6229,7 +7299,7 @@ static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UI
         if (glyphsize == GDI_ERROR)
         {
             ERR("GetGlyphOutlineW failed\n");
-            GdipFree(pti);
+            heap_free(pti);
             DeleteDC(hdc);
             DeleteObject(hfont);
             return GenericError;
@@ -6262,15 +7332,15 @@ static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UI
         /* Nothing to draw. */
         return Ok;
 
-    glyph_mask = GdipAlloc(max_glyphsize);
-    text_mask = GdipAlloc((max_x - min_x) * (max_y - min_y));
+    glyph_mask = heap_alloc_zero(max_glyphsize);
+    text_mask = heap_alloc_zero((max_x - min_x) * (max_y - min_y));
     text_mask_stride = max_x - min_x;
 
     if (!(glyph_mask && text_mask))
     {
-        GdipFree(glyph_mask);
-        GdipFree(text_mask);
-        GdipFree(pti);
+        heap_free(glyph_mask);
+        heap_free(text_mask);
+        heap_free(pti);
         DeleteDC(hdc);
         DeleteObject(hfont);
         return OutOfMemory;
@@ -6305,16 +7375,16 @@ static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UI
         }
     }
 
-    GdipFree(pti);
+    heap_free(pti);
     DeleteDC(hdc);
     DeleteObject(hfont);
-    GdipFree(glyph_mask);
+    heap_free(glyph_mask);
 
     /* get the brush data */
-    pixel_data = GdipAlloc(4 * (max_x - min_x) * (max_y - min_y));
+    pixel_data = heap_alloc_zero(4 * (max_x - min_x) * (max_y - min_y));
     if (!pixel_data)
     {
-        GdipFree(text_mask);
+        heap_free(text_mask);
         return OutOfMemory;
     }
 
@@ -6327,8 +7397,8 @@ static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UI
     stat = brush_fill_pixels(graphics, (GpBrush*)brush, (DWORD*)pixel_data, &pixel_area, pixel_area.Width);
     if (stat != Ok)
     {
-        GdipFree(text_mask);
-        GdipFree(pixel_data);
+        heap_free(text_mask);
+        heap_free(pixel_data);
         return stat;
     }
 
@@ -6345,13 +7415,17 @@ static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UI
         }
     }
 
-    GdipFree(text_mask);
+    heap_free(text_mask);
+
+    gdi_transform_acquire(graphics);
 
     /* draw the result */
     stat = alpha_blend_pixels(graphics, min_x, min_y, pixel_data, pixel_area.Width,
-        pixel_area.Height, pixel_data_stride);
+        pixel_area.Height, pixel_data_stride, PixelFormat32bppARGB);
 
-    GdipFree(pixel_data);
+    gdi_transform_release(graphics);
+
+    heap_free(pixel_data);
 
     return stat;
 }
@@ -6395,13 +7469,6 @@ GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16
                               brush, positions, flags, matrix);
 }
 
-GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
-                                        MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
-{
-    FIXME("(%p %p %d %p %d %p %p): stub\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
-    return NotImplemented;
-}
-
 /*****************************************************************************
  * GdipIsVisibleClipEmpty [GDIPLUS.@]
  */
@@ -6436,3 +7503,16 @@ GpStatus WINGDIPAPI GdipResetPageTransform(GpGraphics *graphics)
 
     return NotImplemented;
 }
+
+GpStatus WINGDIPAPI GdipGraphicsSetAbort(GpGraphics *graphics, GdiplusAbort *pabort)
+{
+    TRACE("(%p, %p)\n", graphics, pabort);
+
+    if (!graphics)
+        return InvalidParameter;
+
+    if (pabort)
+        FIXME("Abort callback is not supported.\n");
+
+    return Ok;
+}