[GDIPLUS] Sync with Wine Staging 4.0. CORE-15682
[reactos.git] / dll / win32 / gdiplus / graphics.c
index e85a8ba..199b204 100644 (file)
 
 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
+ * every GpGraphics). Windows seems to use process-global id's, or at
+ * least more unique id's.
+ * This have the following implications. It:
+ * 1. fails the current gdiplus test case.
+ * 2. is not what Windows does.
+ * 
+ * We therefore "obfuscate" the 'contid' a little to more match Windows'
+ * behaviour. The observable behviour should still remain the same,
+ * except for handing out more "unique" id's.
+ */
+#define GDIP_CONTID_STEP 64
+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)
 
-/* Converts angle (in degrees) to x/y coordinates */
-static void deg2xy(REAL angle, REAL x_0, REAL y_0, REAL *x, REAL *y)
-{
-    REAL radAngle, hypotenuse;
-
-    radAngle = deg2rad(angle);
-    hypotenuse = 50.0; /* arbitrary */
-
-    *x = x_0 + cos(radAngle) * hypotenuse;
-    *y = y_0 + sin(radAngle) * hypotenuse;
-}
+static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
+                                   GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
+                                   GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
+                                   INT flags, GDIPCONST GpMatrix *matrix);
 
 /* Converts from gdiplus path point type to gdi path point type. */
 static BYTE convert_path_point_type(BYTE type)
@@ -84,14 +98,152 @@ static BYTE convert_path_point_type(BYTE type)
     return ret;
 }
 
-static REAL graphics_res(GpGraphics *graphics)
+static COLORREF get_gdi_brush_color(const GpBrush *brush)
+{
+    ARGB argb;
+
+    switch (brush->bt)
+    {
+        case BrushTypeSolidColor:
+        {
+            const GpSolidFill *sf = (const GpSolidFill *)brush;
+            argb = sf->color;
+            break;
+        }
+        case BrushTypeHatchFill:
+        {
+            const GpHatch *hatch = (const GpHatch *)brush;
+            argb = hatch->forecol;
+            break;
+        }
+        case BrushTypeLinearGradient:
+        {
+            const GpLineGradient *line = (const GpLineGradient *)brush;
+            argb = line->startcolor;
+            break;
+        }
+        case BrushTypePathGradient:
+        {
+            const GpPathGradient *grad = (const GpPathGradient *)brush;
+            argb = grad->centercolor;
+            break;
+        }
+        default:
+            FIXME("unhandled brush type %d\n", brush->bt);
+            argb = 0;
+            break;
+    }
+    return ARGB2COLORREF(argb);
+}
+
+static HBITMAP create_hatch_bitmap(const GpHatch *hatch)
+{
+    HBITMAP hbmp;
+    BITMAPINFOHEADER bmih;
+    DWORD *bits;
+    int x, y;
+
+    bmih.biSize = sizeof(bmih);
+    bmih.biWidth = 8;
+    bmih.biHeight = 8;
+    bmih.biPlanes = 1;
+    bmih.biBitCount = 32;
+    bmih.biCompression = BI_RGB;
+    bmih.biSizeImage = 0;
+
+    hbmp = CreateDIBSection(0, (BITMAPINFO *)&bmih, DIB_RGB_COLORS, (void **)&bits, NULL, 0);
+    if (hbmp)
+    {
+        const char *hatch_data;
+
+        if (get_hatch_data(hatch->hatchstyle, &hatch_data) == Ok)
+        {
+            for (y = 0; y < 8; y++)
+            {
+                for (x = 0; x < 8; x++)
+                {
+                    if (hatch_data[y] & (0x80 >> x))
+                        bits[y * 8 + x] = hatch->forecol;
+                    else
+                        bits[y * 8 + x] = hatch->backcol;
+                }
+            }
+        }
+        else
+        {
+            FIXME("Unimplemented hatch style %d\n", hatch->hatchstyle);
+
+            for (y = 0; y < 64; y++)
+                bits[y] = hatch->forecol;
+        }
+    }
+
+    return hbmp;
+}
+
+static GpStatus create_gdi_logbrush(const GpBrush *brush, LOGBRUSH *lb)
+{
+    switch (brush->bt)
+    {
+        case BrushTypeSolidColor:
+        {
+            const GpSolidFill *sf = (const GpSolidFill *)brush;
+            lb->lbStyle = BS_SOLID;
+            lb->lbColor = ARGB2COLORREF(sf->color);
+            lb->lbHatch = 0;
+            return Ok;
+        }
+
+        case BrushTypeHatchFill:
+        {
+            const GpHatch *hatch = (const GpHatch *)brush;
+            HBITMAP hbmp;
+
+            hbmp = create_hatch_bitmap(hatch);
+            if (!hbmp) return OutOfMemory;
+
+            lb->lbStyle = BS_PATTERN;
+            lb->lbColor = 0;
+            lb->lbHatch = (ULONG_PTR)hbmp;
+            return Ok;
+        }
+
+        default:
+            FIXME("unhandled brush type %d\n", brush->bt);
+            lb->lbStyle = BS_SOLID;
+            lb->lbColor = get_gdi_brush_color(brush);
+            lb->lbHatch = 0;
+            return Ok;
+    }
+}
+
+static GpStatus free_gdi_logbrush(LOGBRUSH *lb)
 {
-    if (graphics->image) return graphics->image->xres;
-    else return (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
+    switch (lb->lbStyle)
+    {
+        case BS_PATTERN:
+            DeleteObject((HGDIOBJ)(ULONG_PTR)lb->lbHatch);
+            break;
+    }
+    return Ok;
+}
+
+static HBRUSH create_gdi_brush(const GpBrush *brush)
+{
+    LOGBRUSH lb;
+    HBRUSH gdibrush;
+
+    if (create_gdi_logbrush(brush, &lb) != Ok) return 0;
+
+    gdibrush = CreateBrushIndirect(&lb);
+    free_gdi_logbrush(&lb);
+
+    return gdibrush;
 }
 
 static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
 {
+    LOGBRUSH lb;
     HPEN gdipen;
     REAL width;
     INT save_state, i, numdashes;
@@ -112,12 +264,20 @@ static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
         pt[0].Y = 0.0;
         pt[1].X = 1.0;
         pt[1].Y = 1.0;
-        GdipTransformMatrixPoints(graphics->worldtrans, pt, 2);
+        GdipTransformMatrixPoints(&graphics->worldtrans, pt, 2);
         width = sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) +
                      (pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
 
-        width *= pen->width * convert_unit(graphics_res(graphics),
-                              pen->unit == UnitWorld ? graphics->unit : pen->unit);
+        width *= units_to_pixels(pen->width, pen->unit == UnitWorld ? graphics->unit : pen->unit, graphics->xres);
+        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){
@@ -125,16 +285,22 @@ static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
 
         TRACE("dashes are: ");
         for(i = 0; i < numdashes; i++){
-            dash_array[i] = roundr(width * pen->dashes[i]);
+            dash_array[i] = gdip_round(width * pen->dashes[i]);
             TRACE("%d, ", dash_array[i]);
         }
         TRACE("\n and the pen style is %x\n", pen->style);
 
-        gdipen = ExtCreatePen(pen->style, roundr(width), &pen->brush->lb,
+        create_gdi_logbrush(pen->brush, &lb);
+        gdipen = ExtCreatePen(pen->style, gdip_round(width), &lb,
                               numdashes, dash_array);
+        free_gdi_logbrush(&lb);
     }
     else
-        gdipen = ExtCreatePen(pen->style, roundr(width), &pen->brush->lb, 0, NULL);
+    {
+        create_gdi_logbrush(pen->brush, &lb);
+        gdipen = ExtCreatePen(pen->style, gdip_round(width), &lb, 0, NULL);
+        free_gdi_logbrush(&lb);
+    }
 
     SelectObject(graphics->hdc, gdipen);
 
@@ -147,115 +313,279 @@ 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 unitscale;
-    GpMatrix *matrix;
     int i;
 
-    unitscale = convert_unit(graphics_res(graphics), graphics->unit);
+    for(i = 0; i < count; i++){
+        pti[i].x = gdip_round(ptf[i].X);
+        pti[i].y = gdip_round(ptf[i].Y);
+    }
+}
 
-    /* apply page scale */
-    if(graphics->unit != UnitDisplay)
-        unitscale *= graphics->scale;
+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, TECHNOLOGY) == DT_RASPRINTER &&
+        GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE)
+    {
+        TRACE("alpha blending not supported by device, fallback to StretchBlt\n");
 
-    GdipCloneMatrix(graphics->worldtrans, &matrix);
-    GdipScaleMatrix(matrix, unitscale, unitscale, MatrixOrderAppend);
-    GdipTransformMatrixPoints(matrix, ptf, count);
-    GdipDeleteMatrix(matrix);
+        StretchBlt(graphics->hdc, dst_x, dst_y, dst_width, dst_height,
+                   hdc, src_x, src_y, src_width, src_height, SRCCOPY);
+    }
+    else
+    {
+        BLENDFUNCTION bf;
 
-    for(i = 0; i < count; i++){
-        pti[i].x = roundr(ptf[i].X);
-        pti[i].y = roundr(ptf[i].Y);
+        bf.BlendOp = AC_SRC_OVER;
+        bf.BlendFlags = 0;
+        bf.SourceConstantAlpha = 255;
+        bf.AlphaFormat = AC_SRC_ALPHA;
+
+        GdiAlphaBlend(graphics->hdc, dst_x, dst_y, dst_width, dst_height,
+                      hdc, src_x, src_y, src_width, src_height, bf);
     }
 }
 
-/* Draw non-premultiplied ARGB data to the given graphics object */
-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)
+static GpStatus get_clip_hrgn(GpGraphics *graphics, HRGN *hrgn)
 {
-    if (graphics->image && graphics->image->type == ImageTypeBitmap)
+    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)
     {
-        GpBitmap *dst_bitmap = (GpBitmap*)graphics->image;
-        INT x, y;
+        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 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 PixelFormat fmt)
+{
+    GpBitmap *dst_bitmap = (GpBitmap*)graphics->image;
+    INT x, y;
+
+    for (y=0; y<src_height; y++)
+    {
         for (x=0; x<src_width; x++)
         {
-            for (y=0; y<src_height; y++)
-            {
-                ARGB dst_color, src_color;
-                GdipBitmapGetPixel(dst_bitmap, x+dst_x, y+dst_y, &dst_color);
-                src_color = ((ARGB*)(src + src_stride * y))[x];
+            ARGB dst_color, src_color;
+            src_color = ((ARGB*)(src + src_stride * y))[x];
+
+            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));
-            }
         }
+    }
 
-        return Ok;
+    return Ok;
+}
+
+static GpStatus alpha_blend_hdc_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
+    const BYTE *src, INT src_width, INT src_height, INT src_stride, PixelFormat fmt)
+{
+    HDC hdc;
+    HBITMAP hbitmap;
+    BITMAPINFOHEADER bih;
+    BYTE *temp_bits;
+
+    hdc = CreateCompatibleDC(0);
+
+    bih.biSize = sizeof(BITMAPINFOHEADER);
+    bih.biWidth = src_width;
+    bih.biHeight = -src_height;
+    bih.biPlanes = 1;
+    bih.biBitCount = 32;
+    bih.biCompression = BI_RGB;
+    bih.biSizeImage = 0;
+    bih.biXPelsPerMeter = 0;
+    bih.biYPelsPerMeter = 0;
+    bih.biClrUsed = 0;
+    bih.biClrImportant = 0;
+
+    hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
+        (void**)&temp_bits, NULL, 0);
+
+    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,
+                                        4 * src_width, src, src_stride);
+
+    SelectObject(hdc, hbitmap);
+    gdi_alpha_blend(graphics, dst_x, dst_y, src_width, src_height,
+                    hdc, 0, 0, src_width, src_height);
+    DeleteDC(hdc);
+    DeleteObject(hbitmap);
+
+    return Ok;
+}
+
+static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst_y,
+    const BYTE *src, INT src_width, INT src_height, INT src_stride, HRGN hregion, PixelFormat fmt)
+{
+    GpStatus stat=Ok;
+
+    if (graphics->image && graphics->image->type == ImageTypeBitmap)
+    {
+        DWORD i;
+        int size;
+        RGNDATA *rgndata;
+        RECT *rects;
+        HRGN hrgn, visible_rgn;
+
+        hrgn = CreateRectRgn(dst_x, dst_y, dst_x + src_width, dst_y + src_height);
+        if (!hrgn)
+            return OutOfMemory;
+
+        stat = get_clip_hrgn(graphics, &visible_rgn);
+        if (stat != Ok)
+        {
+            DeleteObject(hrgn);
+            return stat;
+        }
+
+        if (visible_rgn)
+        {
+            CombineRgn(hrgn, hrgn, visible_rgn, RGN_AND);
+            DeleteObject(visible_rgn);
+        }
+
+        if (hregion)
+            CombineRgn(hrgn, hrgn, hregion, RGN_AND);
+
+        size = GetRegionData(hrgn, 0, NULL);
+
+        rgndata = heap_alloc_zero(size);
+        if (!rgndata)
+        {
+            DeleteObject(hrgn);
+            return OutOfMemory;
+        }
+
+        GetRegionData(hrgn, size, rgndata);
+
+        rects = (RECT*)rgndata->Buffer;
+
+        for (i=0; stat == Ok && i<rgndata->rdh.nCount; i++)
+        {
+            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, fmt);
+        }
+
+        heap_free(rgndata);
+
+        DeleteObject(hrgn);
+
+        return stat;
+    }
+    else if (graphics->image && graphics->image->type == ImageTypeMetafile)
+    {
+        ERR("This should not be used for metafiles; fix caller\n");
+        return NotImplemented;
     }
     else
     {
-        HDC hdc;
-        HBITMAP hbitmap, old_hbm=NULL;
-        BITMAPINFOHEADER bih;
-        BYTE *temp_bits;
-        BLENDFUNCTION bf;
+        HRGN hrgn;
+        int save;
 
-        hdc = CreateCompatibleDC(0);
+        stat = get_clip_hrgn(graphics, &hrgn);
 
-        bih.biSize = sizeof(BITMAPINFOHEADER);
-        bih.biWidth = src_width;
-        bih.biHeight = -src_height;
-        bih.biPlanes = 1;
-        bih.biBitCount = 32;
-        bih.biCompression = BI_RGB;
-        bih.biSizeImage = 0;
-        bih.biXPelsPerMeter = 0;
-        bih.biYPelsPerMeter = 0;
-        bih.biClrUsed = 0;
-        bih.biClrImportant = 0;
+        if (stat != Ok)
+            return stat;
 
-        hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
-            (void**)&temp_bits, NULL, 0);
+        save = SaveDC(graphics->hdc);
 
-        convert_32bppARGB_to_32bppPARGB(src_width, src_height, temp_bits,
-            4 * src_width, src, src_stride);
+        ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
 
-        old_hbm = SelectObject(hdc, hbitmap);
+        if (hregion)
+            ExtSelectClipRgn(graphics->hdc, hregion, RGN_AND);
 
-        bf.BlendOp = AC_SRC_OVER;
-        bf.BlendFlags = 0;
-        bf.SourceConstantAlpha = 255;
-        bf.AlphaFormat = AC_SRC_ALPHA;
+        stat = alpha_blend_hdc_pixels(graphics, dst_x, dst_y, src, src_width,
+            src_height, src_stride, fmt);
 
-        GdiAlphaBlend(graphics->hdc, dst_x, dst_y, src_width, src_height,
-            hdc, 0, 0, src_width, src_height, bf);
+        RestoreDC(graphics->hdc, save);
 
-        SelectObject(hdc, old_hbm);
-        DeleteDC(hdc);
-        DeleteObject(hbitmap);
+        DeleteObject(hrgn);
 
-        return Ok;
+        return stat;
     }
 }
 
+static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
+    const BYTE *src, INT src_width, INT src_height, INT src_stride, 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)
+{
+    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;
-    for (i=0xff; i<=0xff0000; i = i << 8)
-        result |= (int)((start&i)*(1.0f - position)+(end&i)*(position))&i;
-    return result;
+    INT start_a, end_a, final_a;
+    INT pos;
+
+    pos = (INT)(position * 255.0f + 0.5f);
+
+    start_a = ((start >> 24) & 0xff) * (pos ^ 0xff);
+    end_a = ((end >> 24) & 0xff) * pos;
+
+    final_a = start_a + end_a;
+
+    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)
@@ -263,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);
@@ -319,190 +650,1007 @@ static ARGB blend_line_gradient(GpLineGradient* brush, REAL position)
     }
 }
 
-static void brush_fill_path(GpGraphics *graphics, GpBrush* brush)
+static BOOL round_color_matrix(const ColorMatrix *matrix, int values[5][5])
 {
-    switch (brush->bt)
-    {
-    case BrushTypeLinearGradient:
-    {
-        GpLineGradient *line = (GpLineGradient*)brush;
-        RECT rc;
+    /* Convert floating point color matrix to int[5][5], return TRUE if it's an identity */
+    BOOL identity = TRUE;
+    int i, j;
 
-        SelectClipPath(graphics->hdc, RGN_AND);
-        if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
+    for (i=0; i<4; i++)
+        for (j=0; j<5; j++)
         {
-            GpPointF endpointsf[2];
-            POINT endpointsi[2];
-            POINT poly[4];
-
-            SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
-
-            endpointsf[0] = line->startpoint;
-            endpointsf[1] = line->endpoint;
-            transform_and_round_points(graphics, endpointsi, endpointsf, 2);
-
-            if (abs(endpointsi[0].x-endpointsi[1].x) > abs(endpointsi[0].y-endpointsi[1].y))
-            {
-                /* vertical-ish gradient */
-                int startx, endx; /* x co-ordinates of endpoints shifted to intersect the top of the visible rectangle */
-                int startbottomx; /* x co-ordinate of start point shifted to intersect the bottom of the visible rectangle */
-                int width;
-                COLORREF col;
-                HBRUSH hbrush, hprevbrush;
-                int leftx, rightx; /* x co-ordinates where the leftmost and rightmost gradient lines hit the top of the visible rectangle */
-                int x;
-                int tilt; /* horizontal distance covered by a gradient line */
-
-                startx = roundr((rc.top - endpointsf[0].Y) * (endpointsf[1].Y - endpointsf[0].Y) / (endpointsf[0].X - endpointsf[1].X) + endpointsf[0].X);
-                endx = roundr((rc.top - endpointsf[1].Y) * (endpointsf[1].Y - endpointsf[0].Y) / (endpointsf[0].X - endpointsf[1].X) + endpointsf[1].X);
-                width = endx - startx;
-                startbottomx = roundr((rc.bottom - endpointsf[0].Y) * (endpointsf[1].Y - endpointsf[0].Y) / (endpointsf[0].X - endpointsf[1].X) + endpointsf[0].X);
-                tilt = startx - startbottomx;
-
-                if (startx >= startbottomx)
-                {
-                    leftx = rc.left;
-                    rightx = rc.right + tilt;
-                }
-                else
-                {
-                    leftx = rc.left + tilt;
-                    rightx = rc.right;
-                }
+            if (matrix->m[j][i] != (i == j ? 1.0 : 0.0))
+                identity = FALSE;
+            values[j][i] = gdip_round(matrix->m[j][i] * 256.0);
+        }
 
-                poly[0].y = rc.bottom;
-                poly[1].y = rc.top;
-                poly[2].y = rc.top;
-                poly[3].y = rc.bottom;
+    return identity;
+}
 
-                for (x=leftx; x<=rightx; x++)
-                {
-                    ARGB argb = blend_line_gradient(line, (x-startx)/(REAL)width);
-                    col = ARGB2COLORREF(argb);
-                    hbrush = CreateSolidBrush(col);
-                    hprevbrush = SelectObject(graphics->hdc, hbrush);
-                    poly[0].x = x - tilt - 1;
-                    poly[1].x = x - 1;
-                    poly[2].x = x;
-                    poly[3].x = x - tilt;
-                    Polygon(graphics->hdc, poly, 4);
-                    SelectObject(graphics->hdc, hprevbrush);
-                    DeleteObject(hbrush);
-                }
-            }
-            else if (endpointsi[0].y != endpointsi[1].y)
-            {
-                /* horizontal-ish gradient */
-                int starty, endy; /* y co-ordinates of endpoints shifted to intersect the left of the visible rectangle */
-                int startrighty; /* y co-ordinate of start point shifted to intersect the right of the visible rectangle */
-                int height;
-                COLORREF col;
-                HBRUSH hbrush, hprevbrush;
-                int topy, bottomy; /* y co-ordinates where the topmost and bottommost gradient lines hit the left of the visible rectangle */
-                int y;
-                int tilt; /* vertical distance covered by a gradient line */
-
-                starty = roundr((rc.left - endpointsf[0].X) * (endpointsf[0].X - endpointsf[1].X) / (endpointsf[1].Y - endpointsf[0].Y) + endpointsf[0].Y);
-                endy = roundr((rc.left - endpointsf[1].X) * (endpointsf[0].X - endpointsf[1].X) / (endpointsf[1].Y - endpointsf[0].Y) + endpointsf[1].Y);
-                height = endy - starty;
-                startrighty = roundr((rc.right - endpointsf[0].X) * (endpointsf[0].X - endpointsf[1].X) / (endpointsf[1].Y - endpointsf[0].Y) + endpointsf[0].Y);
-                tilt = starty - startrighty;
-
-                if (starty >= startrighty)
-                {
-                    topy = rc.top;
-                    bottomy = rc.bottom + tilt;
-                }
-                else
-                {
-                    topy = rc.top + tilt;
-                    bottomy = rc.bottom;
-                }
+static ARGB transform_color(ARGB color, int matrix[5][5])
+{
+    int val[5], res[4];
+    int i, j;
+    unsigned char a, r, g, b;
 
-                poly[0].x = rc.right;
-                poly[1].x = rc.left;
-                poly[2].x = rc.left;
-                poly[3].x = rc.right;
+    val[0] = ((color >> 16) & 0xff); /* red */
+    val[1] = ((color >> 8) & 0xff); /* green */
+    val[2] = (color & 0xff); /* blue */
+    val[3] = ((color >> 24) & 0xff); /* alpha */
+    val[4] = 255; /* translation */
 
-                for (y=topy; y<=bottomy; y++)
-                {
-                    ARGB argb = blend_line_gradient(line, (y-starty)/(REAL)height);
-                    col = ARGB2COLORREF(argb);
-                    hbrush = CreateSolidBrush(col);
-                    hprevbrush = SelectObject(graphics->hdc, hbrush);
-                    poly[0].y = y - tilt - 1;
-                    poly[1].y = y - 1;
-                    poly[2].y = y;
-                    poly[3].y = y - tilt;
-                    Polygon(graphics->hdc, poly, 4);
-                    SelectObject(graphics->hdc, hprevbrush);
-                    DeleteObject(hbrush);
-                }
-            }
-            /* else startpoint == endpoint */
-        }
-        break;
-    }
-    case BrushTypeSolidColor:
+    for (i=0; i<4; i++)
     {
-        GpSolidFill *fill = (GpSolidFill*)brush;
-        if (fill->bmp)
-        {
-            RECT rc;
-            /* partially transparent fill */
-
-            SelectClipPath(graphics->hdc, RGN_AND);
-            if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
-            {
-                HDC hdc = CreateCompatibleDC(NULL);
-                HBITMAP oldbmp;
-                BLENDFUNCTION bf;
+        res[i] = 0;
 
-                if (!hdc) break;
+        for (j=0; j<5; j++)
+            res[i] += matrix[j][i] * val[j];
+    }
 
-                oldbmp = SelectObject(hdc, fill->bmp);
+    a = min(max(res[3] / 256, 0), 255);
+    r = min(max(res[0] / 256, 0), 255);
+    g = min(max(res[1] / 256, 0), 255);
+    b = min(max(res[2] / 256, 0), 255);
 
-                bf.BlendOp = AC_SRC_OVER;
-                bf.BlendFlags = 0;
-                bf.SourceConstantAlpha = 255;
-                bf.AlphaFormat = AC_SRC_ALPHA;
+    return (a << 24) | (r << 16) | (g << 8) | b;
+}
 
-                GdiAlphaBlend(graphics->hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, hdc, 0, 0, 1, 1, bf);
+static BOOL color_is_gray(ARGB color)
+{
+    unsigned char r, g, b;
 
-                SelectObject(hdc, oldbmp);
-                DeleteDC(hdc);
-            }
+    r = (color >> 16) & 0xff;
+    g = (color >> 8) & 0xff;
+    b = color & 0xff;
 
-            break;
-        }
-        /* else fall through */
-    }
-    default:
-        SelectObject(graphics->hdc, brush->gdibrush);
-        FillPath(graphics->hdc);
-        break;
-    }
+    return (r == g) && (g == b);
 }
 
-/* GdipDrawPie/GdipFillPie helper function */
-static void draw_pie(GpGraphics *graphics, REAL x, REAL y, REAL width,
-    REAL height, REAL startAngle, REAL sweepAngle)
+/* 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)
 {
-    GpPointF ptf[4];
-    POINT pti[4];
+    UINT x, y;
+    INT i;
 
-    ptf[0].X = x;
-    ptf[0].Y = y;
-    ptf[1].X = x + width;
-    ptf[1].Y = y + height;
+    if ((attributes->noop[type] == IMAGEATTR_NOOP_UNDEFINED &&
+            attributes->noop[ColorAdjustTypeDefault] == IMAGEATTR_NOOP_SET) ||
+            (attributes->noop[type] == IMAGEATTR_NOOP_SET))
+        return fmt;
 
-    deg2xy(startAngle+sweepAngle, x + width / 2.0, y + width / 2.0, &ptf[2].X, &ptf[2].Y);
-    deg2xy(startAngle, x + width / 2.0, y + width / 2.0, &ptf[3].X, &ptf[3].Y);
+    if (attributes->colorkeys[type].enabled ||
+        attributes->colorkeys[ColorAdjustTypeDefault].enabled)
+    {
+        const struct color_key *key;
+        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
+            key = &attributes->colorkeys[ColorAdjustTypeDefault];
+
+        min_blue = key->low&0xff;
+        min_green = (key->low>>8)&0xff;
+        min_red = (key->low>>16)&0xff;
+
+        max_blue = key->high&0xff;
+        max_green = (key->high>>8)&0xff;
+        max_red = (key->high>>16)&0xff;
+
+        for (x=0; x<width; x++)
+            for (y=0; y<height; y++)
+            {
+                ARGB *src_color;
+                BYTE blue, green, red;
+                src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
+                blue = *src_color&0xff;
+                green = (*src_color>>8)&0xff;
+                red = (*src_color>>16)&0xff;
+                if (blue >= min_blue && green >= min_green && red >= min_red &&
+                    blue <= max_blue && green <= max_green && red <= max_red)
+                    *src_color = 0x00000000;
+            }
+    }
+
+    if (attributes->colorremaptables[type].enabled ||
+        attributes->colorremaptables[ColorAdjustTypeDefault].enabled)
+    {
+        const struct color_remap_table *table;
+
+        if (!data || fmt != PixelFormat32bppARGB)
+            return PixelFormat32bppARGB;
+
+        if (attributes->colorremaptables[type].enabled)
+            table = &attributes->colorremaptables[type];
+        else
+            table = &attributes->colorremaptables[ColorAdjustTypeDefault];
+
+        for (x=0; x<width; x++)
+            for (y=0; y<height; y++)
+            {
+                ARGB *src_color;
+                src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
+                for (i=0; i<table->mapsize; i++)
+                {
+                    if (*src_color == table->colormap[i].oldColor.Argb)
+                    {
+                        *src_color = table->colormap[i].newColor.Argb;
+                        break;
+                    }
+                }
+            }
+    }
+
+    if (attributes->colormatrices[type].enabled ||
+        attributes->colormatrices[ColorAdjustTypeDefault].enabled)
+    {
+        const struct color_matrix *colormatrices;
+        int color_matrix[5][5];
+        int gray_matrix[5][5];
+        BOOL identity;
+
+        if (!data || fmt != PixelFormat32bppARGB)
+            return PixelFormat32bppARGB;
+
+        if (attributes->colormatrices[type].enabled)
+            colormatrices = &attributes->colormatrices[type];
+        else
+            colormatrices = &attributes->colormatrices[ColorAdjustTypeDefault];
+
+        identity = round_color_matrix(&colormatrices->colormatrix, color_matrix);
+
+        if (colormatrices->flags == ColorMatrixFlagsAltGray)
+            identity = (round_color_matrix(&colormatrices->graymatrix, gray_matrix) && identity);
+
+        if (!identity)
+        {
+            for (x=0; x<width; x++)
+            {
+                for (y=0; y<height; y++)
+                {
+                    ARGB *src_color;
+                    src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
+
+                    if (colormatrices->flags == ColorMatrixFlagsDefault ||
+                        !color_is_gray(*src_color))
+                    {
+                        *src_color = transform_color(*src_color, color_matrix);
+                    }
+                    else if (colormatrices->flags == ColorMatrixFlagsAltGray)
+                    {
+                        *src_color = transform_color(*src_color, gray_matrix);
+                    }
+                }
+            }
+        }
+    }
+
+    if (attributes->gamma_enabled[type] ||
+        attributes->gamma_enabled[ColorAdjustTypeDefault])
+    {
+        REAL gamma;
+
+        if (!data || fmt != PixelFormat32bppARGB)
+            return PixelFormat32bppARGB;
+
+        if (attributes->gamma_enabled[type])
+            gamma = attributes->gamma[type];
+        else
+            gamma = attributes->gamma[ColorAdjustTypeDefault];
+
+        for (x=0; x<width; x++)
+            for (y=0; y<height; y++)
+            {
+                ARGB *src_color;
+                BYTE blue, green, red;
+                src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
+
+                blue = *src_color&0xff;
+                green = (*src_color>>8)&0xff;
+                red = (*src_color>>16)&0xff;
+
+                /* FIXME: We should probably use a table for this. */
+                blue = floorf(powf(blue / 255.0, gamma) * 255.0);
+                green = floorf(powf(green / 255.0, gamma) * 255.0);
+                red = floorf(powf(red / 255.0, gamma) * 255.0);
+
+                *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
+ * bitmap that contains all the pixels we may need to draw it. */
+static void get_bitmap_sample_size(InterpolationMode interpolation, WrapMode wrap,
+    GpBitmap* bitmap, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight,
+    GpRect *rect)
+{
+    INT left, top, right, bottom;
+
+    switch (interpolation)
+    {
+    case InterpolationModeHighQualityBilinear:
+    case InterpolationModeHighQualityBicubic:
+    /* FIXME: Include a greater range for the prefilter? */
+    case InterpolationModeBicubic:
+    case InterpolationModeBilinear:
+        left = (INT)(floorf(srcx));
+        top = (INT)(floorf(srcy));
+        right = (INT)(ceilf(srcx+srcwidth));
+        bottom = (INT)(ceilf(srcy+srcheight));
+        break;
+    case InterpolationModeNearestNeighbor:
+    default:
+        left = gdip_round(srcx);
+        top = gdip_round(srcy);
+        right = gdip_round(srcx+srcwidth);
+        bottom = gdip_round(srcy+srcheight);
+        break;
+    }
+
+    if (wrap == WrapModeClamp)
+    {
+        if (left < 0)
+            left = 0;
+        if (top < 0)
+            top = 0;
+        if (right >= bitmap->width)
+            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
+    {
+        /* In some cases we can make the rectangle smaller here, but the logic
+         * is hard to get right, and tiling suggests we're likely to use the
+         * entire source image. */
+        if (left < 0 || right >= bitmap->width)
+        {
+            left = 0;
+            right = bitmap->width-1;
+        }
+
+        if (top < 0 || bottom >= bitmap->height)
+        {
+            top = 0;
+            bottom = bitmap->height-1;
+        }
+    }
+
+    rect->X = left;
+    rect->Y = top;
+    rect->Width = right - left + 1;
+    rect->Height = bottom - top + 1;
+}
+
+static ARGB sample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
+    UINT height, INT x, INT y, GDIPCONST GpImageAttributes *attributes)
+{
+    if (attributes->wrap == WrapModeClamp)
+    {
+        if (x < 0 || y < 0 || x >= width || y >= height)
+            return attributes->outside_color;
+    }
+    else
+    {
+        /* Tiling. Make sure co-ordinates are positive as it simplifies the math. */
+        if (x < 0)
+            x = width*2 + x % (width * 2);
+        if (y < 0)
+            y = height*2 + y % (height * 2);
+
+        if (attributes->wrap & WrapModeTileFlipX)
+        {
+            if ((x / width) % 2 == 0)
+                x = x % width;
+            else
+                x = width - 1 - x % width;
+        }
+        else
+            x = x % width;
+
+        if (attributes->wrap & WrapModeTileFlipY)
+        {
+            if ((y / height) % 2 == 0)
+                y = y % height;
+            else
+                y = height - 1 - y % height;
+        }
+        else
+            y = y % height;
+    }
+
+    if (x < src_rect->X || y < src_rect->Y || x >= src_rect->X + src_rect->Width || y >= src_rect->Y + src_rect->Height)
+    {
+        ERR("out of range pixel requested\n");
+        return 0xffcd0084;
+    }
+
+    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)
+{
+    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(topleft, topright, x_offset);
+        bottom = blend_colors(bottomleft, bottomright, x_offset);
+
+        return blend_colors(top, bottom, point->Y - topyf);
+    }
+    case InterpolationModeNearestNeighbor:
+    {
+        FLOAT pixel_offset;
+        switch (offset_mode)
+        {
+        default:
+        case PixelOffsetModeNone:
+        case PixelOffsetModeHighSpeed:
+            pixel_offset = 0.5;
+            break;
+
+        case PixelOffsetModeHalf:
+        case PixelOffsetModeHighQuality:
+            pixel_offset = 0.0;
+            break;
+        }
+        return sample_bitmap_pixel(src_rect, bits, width, height,
+            floorf(point->X + pixel_offset), floorf(point->Y + pixel_offset), attributes);
+    }
+
+    }
+}
+
+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;
+}
+
+/* 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:
+    {
+        if (is_fill)
+            return TRUE;
+        else
+        {
+            /* cannot draw semi-transparent colors */
+            return (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000;
+        }
+    }
+    case BrushTypeHatchFill:
+    {
+        GpHatch *hatch = (GpHatch*)brush;
+        return ((hatch->forecol & 0xff000000) == 0xff000000) &&
+               ((hatch->backcol & 0xff000000) == 0xff000000);
+    }
+    case BrushTypeLinearGradient:
+    case BrushTypeTextureFill:
+    /* Gdi32 isn't much help with these, so we should use brush_fill_pixels instead. */
+    default:
+        return FALSE;
+    }
+}
+
+static GpStatus brush_fill_path(GpGraphics *graphics, GpBrush *brush)
+{
+    GpStatus status = Ok;
+    switch (brush->bt)
+    {
+    case BrushTypeSolidColor:
+    {
+        GpSolidFill *fill = (GpSolidFill*)brush;
+        HBITMAP bmp = ARGB2BMP(fill->color);
+
+        if (bmp)
+        {
+            RECT rc;
+            /* partially transparent fill */
+
+            if (!SelectClipPath(graphics->hdc, RGN_AND))
+            {
+                status = GenericError;
+                DeleteObject(bmp);
+                break;
+            }
+            if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
+            {
+                HDC hdc = CreateCompatibleDC(NULL);
+
+                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,
+                                hdc, 0, 0, 1, 1);
+                DeleteDC(hdc);
+            }
+
+            DeleteObject(bmp);
+            break;
+        }
+        /* else fall through */
+    }
+    default:
+    {
+        HBRUSH gdibrush, old_brush;
+
+        gdibrush = create_gdi_brush(brush);
+        if (!gdibrush)
+        {
+            status = OutOfMemory;
+            break;
+        }
+
+        old_brush = SelectObject(graphics->hdc, gdibrush);
+        FillPath(graphics->hdc);
+        SelectObject(graphics->hdc, old_brush);
+        DeleteObject(gdibrush);
+        break;
+    }
+    }
+
+    return status;
+}
+
+static BOOL brush_can_fill_pixels(GpBrush *brush)
+{
+    switch (brush->bt)
+    {
+    case BrushTypeSolidColor:
+    case BrushTypeHatchFill:
+    case BrushTypeLinearGradient:
+    case BrushTypeTextureFill:
+    case BrushTypePathGradient:
+        return TRUE;
+    default:
+        return FALSE;
+    }
+}
+
+static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
+    DWORD *argb_pixels, GpRect *fill_area, UINT cdwStride)
+{
+    switch (brush->bt)
+    {
+    case BrushTypeSolidColor:
+    {
+        int x, y;
+        GpSolidFill *fill = (GpSolidFill*)brush;
+        for (x=0; x<fill_area->Width; x++)
+            for (y=0; y<fill_area->Height; y++)
+                argb_pixels[x + y*cdwStride] = fill->color;
+        return Ok;
+    }
+    case BrushTypeHatchFill:
+    {
+        int x, y;
+        GpHatch *fill = (GpHatch*)brush;
+        const char *hatch_data;
+
+        if (get_hatch_data(fill->hatchstyle, &hatch_data) != Ok)
+            return NotImplemented;
+
+        for (x=0; x<fill_area->Width; x++)
+            for (y=0; y<fill_area->Height; y++)
+            {
+                int hx, hy;
+
+                /* FIXME: Account for the rendering origin */
+                hx = (x + fill_area->X) % 8;
+                hy = (y + fill_area->Y) % 8;
+
+                if ((hatch_data[7-hy] & (0x80 >> hx)) != 0)
+                    argb_pixels[x + y*cdwStride] = fill->forecol;
+                else
+                    argb_pixels[x + y*cdwStride] = fill->backcol;
+            }
+
+        return Ok;
+    }
+    case BrushTypeLinearGradient:
+    {
+        GpLineGradient *fill = (GpLineGradient*)brush;
+        GpPointF draw_points[3];
+        GpStatus stat;
+        int x, y;
+
+        draw_points[0].X = fill_area->X;
+        draw_points[0].Y = fill_area->Y;
+        draw_points[1].X = fill_area->X+1;
+        draw_points[1].Y = fill_area->Y;
+        draw_points[2].X = fill_area->X;
+        draw_points[2].Y = fill_area->Y+1;
+
+        /* 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 = gdip_transform_points(graphics, CoordinateSpaceWorld,
+            WineCoordinateSpaceGdiDevice, draw_points, 3);
+
+        if (stat == Ok)
+        {
+            GpMatrix world_to_gradient = fill->transform;
+
+            stat = GdipInvertMatrix(&world_to_gradient);
+            if (stat == Ok)
+                stat = GdipTransformMatrixPoints(&world_to_gradient, draw_points, 3);
+        }
+
+        if (stat == Ok)
+        {
+            REAL x_delta = draw_points[1].X - draw_points[0].X;
+            REAL y_delta = draw_points[2].X - draw_points[0].X;
+
+            for (y=0; y<fill_area->Height; y++)
+            {
+                for (x=0; x<fill_area->Width; x++)
+                {
+                    REAL pos = draw_points[0].X + x * x_delta + y * y_delta;
+
+                    argb_pixels[x + y*cdwStride] = blend_line_gradient(fill, pos);
+                }
+            }
+        }
+
+        return stat;
+    }
+    case BrushTypeTextureFill:
+    {
+        GpTexture *fill = (GpTexture*)brush;
+        GpPointF draw_points[3];
+        GpStatus stat;
+        int x, y;
+        GpBitmap *bitmap;
+        int src_stride;
+        GpRect src_area;
+
+        if (fill->image->type != ImageTypeBitmap)
+        {
+            FIXME("metafile texture brushes not implemented\n");
+            return NotImplemented;
+        }
+
+        bitmap = (GpBitmap*)fill->image;
+        src_stride = sizeof(ARGB) * bitmap->width;
+
+        src_area.X = src_area.Y = 0;
+        src_area.Width = bitmap->width;
+        src_area.Height = bitmap->height;
+
+        draw_points[0].X = fill_area->X;
+        draw_points[0].Y = fill_area->Y;
+        draw_points[1].X = fill_area->X+1;
+        draw_points[1].Y = fill_area->Y;
+        draw_points[2].X = fill_area->X;
+        draw_points[2].Y = fill_area->Y+1;
+
+        /* Transform the points to the co-ordinate space of the bitmap. */
+        stat = gdip_transform_points(graphics, CoordinateSpaceWorld,
+            WineCoordinateSpaceGdiDevice, draw_points, 3);
+
+        if (stat == Ok)
+        {
+            GpMatrix world_to_texture = fill->transform;
+
+            stat = GdipInvertMatrix(&world_to_texture);
+            if (stat == Ok)
+                stat = GdipTransformMatrixPoints(&world_to_texture, draw_points, 3);
+        }
+
+        if (stat == Ok && !fill->bitmap_bits)
+        {
+            BitmapData lockeddata;
+
+            fill->bitmap_bits = heap_alloc_zero(sizeof(ARGB) * bitmap->width * bitmap->height);
+            if (!fill->bitmap_bits)
+                stat = OutOfMemory;
+
+            if (stat == Ok)
+            {
+                lockeddata.Width = bitmap->width;
+                lockeddata.Height = bitmap->height;
+                lockeddata.Stride = src_stride;
+                lockeddata.PixelFormat = PixelFormat32bppARGB;
+                lockeddata.Scan0 = fill->bitmap_bits;
+
+                stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
+                    PixelFormat32bppARGB, &lockeddata);
+            }
+
+            if (stat == Ok)
+                stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
+
+            if (stat == Ok)
+                apply_image_attributes(fill->imageattributes, fill->bitmap_bits,
+                    bitmap->width, bitmap->height,
+                    src_stride, ColorAdjustTypeBitmap, lockeddata.PixelFormat);
+
+            if (stat != Ok)
+            {
+                heap_free(fill->bitmap_bits);
+                fill->bitmap_bits = NULL;
+            }
+        }
+
+        if (stat == Ok)
+        {
+            REAL x_dx = draw_points[1].X - draw_points[0].X;
+            REAL x_dy = draw_points[1].Y - draw_points[0].Y;
+            REAL y_dx = draw_points[2].X - draw_points[0].X;
+            REAL y_dy = draw_points[2].Y - draw_points[0].Y;
+
+            for (y=0; y<fill_area->Height; y++)
+            {
+                for (x=0; x<fill_area->Width; x++)
+                {
+                    GpPointF point;
+                    point.X = draw_points[0].X + x * x_dx + y * y_dx;
+                    point.Y = draw_points[0].Y + y * x_dy + y * y_dy;
+
+                    argb_pixels[x + y*cdwStride] = resample_bitmap_pixel(
+                        &src_area, fill->bitmap_bits, bitmap->width, bitmap->height,
+                        &point, fill->imageattributes, graphics->interpolation,
+                        graphics->pixeloffset);
+                }
+            }
+        }
+
+        return stat;
+    }
+    case BrushTypePathGradient:
+    {
+        GpPathGradient *fill = (GpPathGradient*)brush;
+        GpPath *flat_path;
+        GpMatrix world_to_device;
+        GpStatus stat;
+        int i, figure_start=0;
+        GpPointF start_point, end_point, center_point;
+        BYTE type;
+        REAL min_yf, max_yf, line1_xf, line2_xf;
+        INT min_y, max_y, min_x, max_x;
+        INT x, y;
+        ARGB outer_color;
+        static BOOL transform_fixme_once;
+
+        if (fill->focus.X != 0.0 || fill->focus.Y != 0.0)
+        {
+            static int once;
+            if (!once++)
+                FIXME("path gradient focus not implemented\n");
+        }
+
+        if (fill->gamma)
+        {
+            static int once;
+            if (!once++)
+                FIXME("path gradient gamma correction not implemented\n");
+        }
+
+        if (fill->blendcount)
+        {
+            static int once;
+            if (!once++)
+                FIXME("path gradient blend not implemented\n");
+        }
+
+        if (fill->pblendcount)
+        {
+            static int once;
+            if (!once++)
+                FIXME("path gradient preset blend not implemented\n");
+        }
+
+        if (!transform_fixme_once)
+        {
+            BOOL is_identity=TRUE;
+            GdipIsMatrixIdentity(&fill->transform, &is_identity);
+            if (!is_identity)
+            {
+                FIXME("path gradient transform not implemented\n");
+                transform_fixme_once = TRUE;
+            }
+        }
+
+        stat = GdipClonePath(fill->path, &flat_path);
+
+        if (stat != Ok)
+            return stat;
+
+        stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
+            CoordinateSpaceWorld, &world_to_device);
+        if (stat == Ok)
+        {
+            stat = GdipTransformPath(flat_path, &world_to_device);
+
+            if (stat == Ok)
+            {
+                center_point = fill->center;
+                stat = GdipTransformMatrixPoints(&world_to_device, &center_point, 1);
+            }
+
+            if (stat == Ok)
+                stat = GdipFlattenPath(flat_path, NULL, 0.5);
+        }
+
+        if (stat != Ok)
+        {
+            GdipDeletePath(flat_path);
+            return stat;
+        }
+
+        for (i=0; i<flat_path->pathdata.Count; i++)
+        {
+            int start_center_line=0, end_center_line=0;
+            BOOL seen_start = FALSE, seen_end = FALSE, seen_center = FALSE;
+            REAL center_distance;
+            ARGB start_color, end_color;
+            REAL dy, dx;
+
+            type = flat_path->pathdata.Types[i];
+
+            if ((type&PathPointTypePathTypeMask) == PathPointTypeStart)
+                figure_start = i;
+
+            start_point = flat_path->pathdata.Points[i];
+
+            start_color = fill->surroundcolors[min(i, fill->surroundcolorcount-1)];
+
+            if ((type&PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath || i+1 >= flat_path->pathdata.Count)
+            {
+                end_point = flat_path->pathdata.Points[figure_start];
+                end_color = fill->surroundcolors[min(figure_start, fill->surroundcolorcount-1)];
+            }
+            else if ((flat_path->pathdata.Types[i+1] & PathPointTypePathTypeMask) == PathPointTypeLine)
+            {
+                end_point = flat_path->pathdata.Points[i+1];
+                end_color = fill->surroundcolors[min(i+1, fill->surroundcolorcount-1)];
+            }
+            else
+                continue;
+
+            outer_color = start_color;
+
+            min_yf = center_point.Y;
+            if (min_yf > start_point.Y) min_yf = start_point.Y;
+            if (min_yf > end_point.Y) min_yf = end_point.Y;
+
+            if (min_yf < fill_area->Y)
+                min_y = fill_area->Y;
+            else
+                min_y = (INT)ceil(min_yf);
+
+            max_yf = center_point.Y;
+            if (max_yf < start_point.Y) max_yf = start_point.Y;
+            if (max_yf < end_point.Y) max_yf = end_point.Y;
+
+            if (max_yf > fill_area->Y + fill_area->Height)
+                max_y = fill_area->Y + fill_area->Height;
+            else
+                max_y = (INT)ceil(max_yf);
+
+            dy = end_point.Y - start_point.Y;
+            dx = end_point.X - start_point.X;
+
+            /* This is proportional to the distance from start-end line to center point. */
+            center_distance = dy * (start_point.X - center_point.X) +
+                dx * (center_point.Y - start_point.Y);
+
+            for (y=min_y; y<max_y; y++)
+            {
+                REAL yf = (REAL)y;
+
+                if (!seen_start && yf >= start_point.Y)
+                {
+                    seen_start = TRUE;
+                    start_center_line ^= 1;
+                }
+                if (!seen_end && yf >= end_point.Y)
+                {
+                    seen_end = TRUE;
+                    end_center_line ^= 1;
+                }
+                if (!seen_center && yf >= center_point.Y)
+                {
+                    seen_center = TRUE;
+                    start_center_line ^= 1;
+                    end_center_line ^= 1;
+                }
+
+                if (start_center_line)
+                    line1_xf = intersect_line_scanline(&start_point, &center_point, yf);
+                else
+                    line1_xf = intersect_line_scanline(&start_point, &end_point, yf);
+
+                if (end_center_line)
+                    line2_xf = intersect_line_scanline(&end_point, &center_point, yf);
+                else
+                    line2_xf = intersect_line_scanline(&start_point, &end_point, yf);
+
+                if (line1_xf < line2_xf)
+                {
+                    min_x = (INT)ceil(line1_xf);
+                    max_x = (INT)ceil(line2_xf);
+                }
+                else
+                {
+                    min_x = (INT)ceil(line2_xf);
+                    max_x = (INT)ceil(line1_xf);
+                }
+
+                if (min_x < fill_area->X)
+                    min_x = fill_area->X;
+                if (max_x > fill_area->X + fill_area->Width)
+                    max_x = fill_area->X + fill_area->Width;
+
+                for (x=min_x; x<max_x; x++)
+                {
+                    REAL xf = (REAL)x;
+                    REAL distance;
+
+                    if (start_color != end_color)
+                    {
+                        REAL blend_amount, pdy, pdx;
+                        pdy = yf - center_point.Y;
+                        pdx = xf - center_point.X;
+
+                        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);
+                        }
+                    }
 
-    transform_and_round_points(graphics, pti, ptf, 4);
+                    distance = (end_point.Y - start_point.Y) * (start_point.X - xf) +
+                        (end_point.X - start_point.X) * (yf - start_point.Y);
+
+                    distance = distance / center_distance;
+
+                    argb_pixels[(x-fill_area->X) + (y-fill_area->Y)*cdwStride] =
+                        blend_colors(outer_color, fill->centercolor, distance);
+                }
+            }
+        }
 
-    Pie(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y, pti[2].x,
-        pti[2].y, pti[3].x, pti[3].y);
+        GdipDeletePath(flat_path);
+        return stat;
+    }
+    default:
+        return NotImplemented;
+    }
 }
 
 /* Draws the linecap the specified color and size on the hdc.  The linecap is in
@@ -512,7 +1660,7 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
     const GpCustomLineCap *custom, REAL x1, REAL y1, REAL x2, REAL y2)
 {
     HGDIOBJ oldbrush = NULL, oldpen = NULL;
-    GpMatrix *matrix = NULL;
+    GpMatrix matrix;
     HBRUSH brush = NULL;
     HPEN pen = NULL;
     PointF ptf[4], *custptf = NULL;
@@ -569,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;
@@ -591,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;
@@ -603,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;
@@ -623,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;
@@ -643,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);
 
@@ -652,23 +1815,33 @@ 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 || (GdipCreateMatrix(&matrix) != Ok))
+            if(!custptf || !custpt || !tp)
                 goto custend;
 
             memcpy(custptf, custom->pathdata.Points, count * sizeof(PointF));
 
-            GdipScaleMatrix(matrix, size, size, MatrixOrderAppend);
-            GdipRotateMatrix(matrix, (180.0 / M_PI) * (theta - M_PI_2),
+            GdipSetMatrixElements(&matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
+            GdipScaleMatrix(&matrix, size, size, MatrixOrderAppend);
+            GdipRotateMatrix(&matrix, (180.0 / M_PI) * (theta - M_PI_2),
                              MatrixOrderAppend);
-            GdipTranslateMatrix(matrix, x2, y2, MatrixOrderAppend);
-            GdipTransformMatrixPoints(matrix, custptf, count);
+            GdipTranslateMatrix(&matrix, x2, y2, MatrixOrderAppend);
+            GdipTransformMatrixPoints(&matrix, custptf, count);
+
+            gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, custptf, count);
 
-            transform_and_round_points(graphics, custpt, custptf, count);
+            round_points(custpt, custptf, count);
 
             for(i = 0; i < count; i++)
                 tp[i] = convert_path_point_type(custom->pathdata.Types[i]);
@@ -683,10 +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);
-            GdipDeleteMatrix(matrix);
+            heap_free(custptf);
+            heap_free(custpt);
+            heap_free(tp);
             break;
         default:
             break;
@@ -741,63 +1913,6 @@ static void shorten_line_amt(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL amt)
     shorten_line_percent(x1, y1, x2, y2, percent);
 }
 
-/* Draws lines between the given points, and if caps is true then draws an endcap
- * at the end of the last line. */
-static GpStatus draw_polyline(GpGraphics *graphics, GpPen *pen,
-    GDIPCONST GpPointF * pt, INT count, BOOL caps)
-{
-    POINT *pti = NULL;
-    GpPointF *ptcopy = NULL;
-    GpStatus status = GenericError;
-
-    if(!count)
-        return Ok;
-
-    pti = GdipAlloc(count * sizeof(POINT));
-    ptcopy = GdipAlloc(count * sizeof(GpPointF));
-
-    if(!pti || !ptcopy){
-        status = OutOfMemory;
-        goto end;
-    }
-
-    memcpy(ptcopy, pt, count * sizeof(GpPointF));
-
-    if(caps){
-        if(pen->endcap == LineCapArrowAnchor)
-            shorten_line_amt(ptcopy[count-2].X, ptcopy[count-2].Y,
-                             &ptcopy[count-1].X, &ptcopy[count-1].Y, pen->width);
-        else if((pen->endcap == LineCapCustom) && pen->customend)
-            shorten_line_amt(ptcopy[count-2].X, ptcopy[count-2].Y,
-                             &ptcopy[count-1].X, &ptcopy[count-1].Y,
-                             pen->customend->inset * pen->width);
-
-        if(pen->startcap == LineCapArrowAnchor)
-            shorten_line_amt(ptcopy[1].X, ptcopy[1].Y,
-                             &ptcopy[0].X, &ptcopy[0].Y, pen->width);
-        else if((pen->startcap == LineCapCustom) && pen->customstart)
-            shorten_line_amt(ptcopy[1].X, ptcopy[1].Y,
-                             &ptcopy[0].X, &ptcopy[0].Y,
-                             pen->customstart->inset * pen->width);
-
-        draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
-                 pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X, pt[count - 1].Y);
-        draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
-                         pt[1].X, pt[1].Y, pt[0].X, pt[0].Y);
-    }
-
-    transform_and_round_points(graphics, pti, ptcopy, count);
-
-    if(Polyline(graphics->hdc, pti, count))
-        status = Ok;
-
-end:
-    GdipFree(pti);
-    GdipFree(ptcopy);
-
-    return status;
-}
-
 /* Conducts a linear search to find the bezier points that will back off
  * the endpoint of the curve by a distance of amt. Linear search works
  * better than binary in this case because there are multiple solutions,
@@ -841,73 +1956,13 @@ static void shorten_bezier_amt(GpPointF * pt, REAL amt, BOOL rev)
     }
 }
 
-/* Draws bezier curves between given points, and if caps is true then draws an
- * endcap at the end of the last line. */
-static GpStatus draw_polybezier(GpGraphics *graphics, GpPen *pen,
-    GDIPCONST GpPointF * pt, INT count, BOOL caps)
-{
-    POINT *pti;
-    GpPointF *ptcopy;
-    GpStatus status = GenericError;
-
-    if(!count)
-        return Ok;
-
-    pti = GdipAlloc(count * sizeof(POINT));
-    ptcopy = GdipAlloc(count * sizeof(GpPointF));
-
-    if(!pti || !ptcopy){
-        status = OutOfMemory;
-        goto end;
-    }
-
-    memcpy(ptcopy, pt, count * sizeof(GpPointF));
-
-    if(caps){
-        if(pen->endcap == LineCapArrowAnchor)
-            shorten_bezier_amt(&ptcopy[count-4], pen->width, FALSE);
-        else if((pen->endcap == LineCapCustom) && pen->customend)
-            shorten_bezier_amt(&ptcopy[count-4], pen->width * pen->customend->inset,
-                               FALSE);
-
-        if(pen->startcap == LineCapArrowAnchor)
-            shorten_bezier_amt(ptcopy, pen->width, TRUE);
-        else if((pen->startcap == LineCapCustom) && pen->customstart)
-            shorten_bezier_amt(ptcopy, pen->width * pen->customstart->inset, TRUE);
-
-        /* the direction of the line cap is parallel to the direction at the
-         * end of the bezier (which, if it has been shortened, is not the same
-         * as the direction from pt[count-2] to pt[count-1]) */
-        draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
-            pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
-            pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
-            pt[count - 1].X, pt[count - 1].Y);
-
-        draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
-            pt[0].X - (ptcopy[0].X - ptcopy[1].X),
-            pt[0].Y - (ptcopy[0].Y - ptcopy[1].Y), pt[0].X, pt[0].Y);
-    }
-
-    transform_and_round_points(graphics, pti, ptcopy, count);
-
-    PolyBezier(graphics->hdc, pti, count);
-
-    status = Ok;
-
-end:
-    GdipFree(pti);
-    GdipFree(ptcopy);
-
-    return status;
-}
-
 /* Draws a combination of bezier curves and lines between points. */
 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;
 
@@ -923,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;
             }
@@ -944,7 +1999,7 @@ static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF *
                     shorten_bezier_amt(&ptcopy[count - 4],
                                        pen->width * pen->customend->inset, FALSE);
 
-                draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
+                draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->endcap, pen->width, pen->customend,
                     pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
                     pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
                     pt[count - 1].X, pt[count - 1].Y);
@@ -960,7 +2015,7 @@ static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF *
                                      &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
                                      pen->customend->inset * pen->width);
 
-                draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
+                draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->endcap, pen->width, pen->customend,
                          pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X,
                          pt[count - 1].Y);
 
@@ -982,7 +2037,7 @@ static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF *
                     shorten_bezier_amt(&ptcopy[j - 1],
                                        pen->width * pen->customstart->inset, TRUE);
 
-                draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
+                draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->startcap, pen->width, pen->customstart,
                     pt[j - 1].X - (ptcopy[j - 1].X - ptcopy[j].X),
                     pt[j - 1].Y - (ptcopy[j - 1].Y - ptcopy[j].Y),
                     pt[j - 1].X, pt[j - 1].Y);
@@ -998,7 +2053,7 @@ static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF *
                                      &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
                                      pen->customstart->inset * pen->width);
 
-                draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
+                draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->startcap, pen->width, pen->customstart,
                          pt[j].X, pt[j].Y, pt[j - 1].X,
                          pt[j - 1].Y);
 
@@ -1009,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]);
@@ -1020,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;
 }
@@ -1038,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;
@@ -1051,19 +2114,21 @@ typedef struct _GraphicsContainerItem {
     GpUnit unit;
     PixelOffsetMode pixeloffset;
     UINT textcontrast;
-    GpMatrix* worldtrans;
+    GpMatrix worldtrans;
     GpRegion* clip;
+    INT origin_x, origin_y;
 } 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;
@@ -1074,18 +2139,13 @@ static GpStatus init_container(GraphicsContainerItem** container,
     (*container)->unit = graphics->unit;
     (*container)->textcontrast = graphics->textcontrast;
     (*container)->pixeloffset = graphics->pixeloffset;
-
-    sts = GdipCloneMatrix(graphics->worldtrans, &(*container)->worldtrans);
-    if(sts != Ok){
-        GdipFree(*container);
-        *container = NULL;
-        return sts;
-    }
+    (*container)->origin_x = graphics->origin_x;
+    (*container)->origin_y = graphics->origin_y;
+    (*container)->worldtrans = graphics->worldtrans;
 
     sts = GdipCloneRegion(graphics->clip, &(*container)->clip);
     if(sts != Ok){
-        GdipDeleteMatrix((*container)->worldtrans);
-        GdipFree(*container);
+        heap_free(*container);
         *container = NULL;
         return sts;
     }
@@ -1093,30 +2153,21 @@ static GpStatus init_container(GraphicsContainerItem** container,
     return Ok;
 }
 
-static void delete_container(GraphicsContainerItem* container){
-    GdipDeleteMatrix(container->worldtrans);
+static void delete_container(GraphicsContainerItem* container)
+{
     GdipDeleteRegion(container->clip);
-    GdipFree(container);
+    heap_free(container);
 }
 
 static GpStatus restore_container(GpGraphics* graphics,
         GDIPCONST GraphicsContainerItem* container){
     GpStatus sts;
-    GpMatrix *newTrans;
     GpRegion *newClip;
 
-    sts = GdipCloneMatrix(container->worldtrans, &newTrans);
-    if(sts != Ok)
-        return sts;
-
     sts = GdipCloneRegion(container->clip, &newClip);
-    if(sts != Ok){
-        GdipDeleteMatrix(newTrans);
-        return sts;
-    }
+    if(sts != Ok) return sts;
 
-    GdipDeleteMatrix(graphics->worldtrans);
-    graphics->worldtrans = newTrans;
+    graphics->worldtrans = container->worldtrans;
 
     GdipDeleteRegion(graphics->clip);
     graphics->clip = newClip;
@@ -1132,11 +2183,13 @@ static GpStatus restore_container(GpGraphics* graphics,
     graphics->unit = container->unit;
     graphics->textcontrast = container->textcontrast;
     graphics->pixeloffset = container->pixeloffset;
+    graphics->origin_x = container->origin_x;
+    graphics->origin_y = container->origin_y;
 
     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;
@@ -1154,6 +2207,25 @@ static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect)
         stat = GdipGetImageBounds(graphics->image, rect, &unit);
         if (stat == Ok && unit != UnitPixel)
             FIXME("need to convert from unit %i\n", unit);
+    }else if (GetObjectType(graphics->hdc) == OBJ_MEMDC){
+        HBITMAP hbmp;
+        BITMAP bmp;
+
+        rect->X = 0;
+        rect->Y = 0;
+
+        hbmp = GetCurrentObject(graphics->hdc, OBJ_BITMAP);
+        if (hbmp && GetObjectW(hbmp, sizeof(bmp), &bmp))
+        {
+            rect->Width = bmp.bmWidth;
+            rect->Height = bmp.bmHeight;
+        }
+        else
+        {
+            /* FIXME: ??? */
+            rect->Width = 1;
+            rect->Height = 1;
+        }
     }else{
         rect->X = 0;
         rect->Y = 0;
@@ -1164,6 +2236,41 @@ static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect)
     return stat;
 }
 
+static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect)
+{
+    GpStatus stat = get_graphics_device_bounds(graphics, rect);
+
+    if (stat == Ok && graphics->hdc)
+    {
+        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;
+
+        gdip_transform_points(graphics, CoordinateSpaceDevice, WineCoordinateSpaceGdiDevice, points, 4);
+
+        min_point = max_point = points[0];
+
+        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;
+}
+
 /* on success, rgn will contain the region of the graphics object which
  * is visible after clipping has been applied */
 static GpStatus get_visible_clip_region(GpGraphics *graphics, GpRegion *rgn)
@@ -1172,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;
 
@@ -1191,6 +2302,96 @@ end:
     return stat;
 }
 
+void get_log_fontW(const GpFont *font, GpGraphics *graphics, LOGFONTW *lf)
+{
+    REAL height;
+
+    if (font->unit == UnitPixel)
+    {
+        height = units_to_pixels(font->emSize, graphics->unit, graphics->yres);
+    }
+    else
+    {
+        if (graphics->unit == UnitDisplay || graphics->unit == UnitPixel)
+            height = units_to_pixels(font->emSize, font->unit, graphics->xres);
+        else
+            height = units_to_pixels(font->emSize, font->unit, graphics->yres);
+    }
+
+    lf->lfHeight = -(height + 0.5);
+    lf->lfWidth = 0;
+    lf->lfEscapement = 0;
+    lf->lfOrientation = 0;
+    lf->lfWeight = font->otm.otmTextMetrics.tmWeight;
+    lf->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0;
+    lf->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0;
+    lf->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0;
+    lf->lfCharSet = font->otm.otmTextMetrics.tmCharSet;
+    lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
+    lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
+    lf->lfQuality = DEFAULT_QUALITY;
+    lf->lfPitchAndFamily = 0;
+    strcpyW(lf->lfFaceName, font->family->FamilyName);
+}
+
+static void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font,
+                           GDIPCONST GpStringFormat *format, HFONT *hfont,
+                           GDIPCONST GpMatrix *matrix)
+{
+    HDC hdc = CreateCompatibleDC(0);
+    GpPointF pt[3];
+    REAL angle, rel_width, rel_height, font_height;
+    LOGFONTW lfw;
+    HFONT unscaled_font;
+    TEXTMETRICW textmet;
+
+    if (font->unit == UnitPixel || font->unit == UnitWorld)
+        font_height = font->emSize;
+    else
+    {
+        REAL unit_scale, res;
+
+        res = (graphics->unit == UnitDisplay || graphics->unit == UnitPixel) ? graphics->xres : graphics->yres;
+        unit_scale = units_scale(font->unit, graphics->unit, res);
+
+        font_height = font->emSize * unit_scale;
+    }
+
+    pt[0].X = 0.0;
+    pt[0].Y = 0.0;
+    pt[1].X = 1.0;
+    pt[1].Y = 0.0;
+    pt[2].X = 0.0;
+    pt[2].Y = 1.0;
+    if (matrix)
+    {
+        GpMatrix xform = *matrix;
+        GdipTransformMatrixPoints(&xform, pt, 3);
+    }
+
+    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));
+    rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
+                      (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
+
+    get_log_fontW(font, graphics, &lfw);
+    lfw.lfHeight = -gdip_round(font_height * rel_height);
+    unscaled_font = CreateFontIndirectW(&lfw);
+
+    SelectObject(hdc, unscaled_font);
+    GetTextMetricsW(hdc, &textmet);
+
+    lfw.lfWidth = gdip_round(textmet.tmAveCharWidth * rel_width / rel_height);
+    lfw.lfEscapement = lfw.lfOrientation = gdip_round((angle / M_PI) * 1800.0);
+
+    *hfont = CreateFontIndirectW(&lfw);
+
+    DeleteDC(hdc);
+    DeleteObject(unscaled_font);
+}
+
 GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
 {
     TRACE("(%p, %p)\n", hdc, graphics);
@@ -1198,16 +2399,30 @@ 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;
+    HBITMAP hbitmap;
+    DIBSECTION dib;
 
     TRACE("(%p, %p, %p)\n", hdc, hDevice, graphics);
 
-    if(hDevice != NULL) {
+    if(hDevice != NULL)
         FIXME("Don't know how to handle parameter hDevice\n");
-        return NotImplemented;
-    }
 
     if(hdc == NULL)
         return OutOfMemory;
@@ -1215,18 +2430,21 @@ 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;
 
-    if((retval = GdipCreateMatrix(&(*graphics)->worldtrans)) != Ok){
-        GdipFree(*graphics);
+    GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
+
+    if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
+        heap_free(*graphics);
         return retval;
     }
 
-    if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
-        GdipFree((*graphics)->worldtrans);
-        GdipFree(*graphics);
-        return retval;
+    hbitmap = GetCurrentObject(hdc, OBJ_BITMAP);
+    if (hbitmap && GetObjectW(hbitmap, sizeof(dib), &dib) == sizeof(dib) &&
+        dib.dsBmih.biBitCount == 32 && dib.dsBmih.biCompression == BI_RGB)
+    {
+        (*graphics)->alpha_hdc = 1;
     }
 
     (*graphics)->hdc = hdc;
@@ -1234,15 +2452,29 @@ GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **gra
     (*graphics)->owndc = FALSE;
     (*graphics)->smoothing = SmoothingModeDefault;
     (*graphics)->compqual = CompositingQualityDefault;
-    (*graphics)->interpolation = InterpolationModeDefault;
+    (*graphics)->interpolation = InterpolationModeBilinear;
     (*graphics)->pixeloffset = PixelOffsetModeDefault;
     (*graphics)->compmode = CompositingModeSourceOver;
     (*graphics)->unit = UnitDisplay;
     (*graphics)->scale = 1.0;
+    (*graphics)->xres = GetDeviceCaps(hdc, LOGPIXELSX);
+    (*graphics)->yres = GetDeviceCaps(hdc, LOGPIXELSY);
     (*graphics)->busy = FALSE;
     (*graphics)->textcontrast = 4;
     list_init(&(*graphics)->containers);
+#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);
 
@@ -1253,17 +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;
 
-    if((retval = GdipCreateMatrix(&(*graphics)->worldtrans)) != Ok){
-        GdipFree(*graphics);
-        return retval;
-    }
+    GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
+    GdipSetMatrixElements(&(*graphics)->gdi_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
 
     if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
-        GdipFree((*graphics)->worldtrans);
-        GdipFree(*graphics);
+        heap_free(*graphics);
         return retval;
     }
 
@@ -1271,17 +2500,26 @@ GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics)
     (*graphics)->hwnd = NULL;
     (*graphics)->owndc = FALSE;
     (*graphics)->image = image;
+    /* We have to store the image type here because the image may be freed
+     * before GdipDeleteGraphics is called, and metafiles need special treatment. */
+    (*graphics)->image_type = image->type;
     (*graphics)->smoothing = SmoothingModeDefault;
     (*graphics)->compqual = CompositingQualityDefault;
-    (*graphics)->interpolation = InterpolationModeDefault;
+    (*graphics)->interpolation = InterpolationModeBilinear;
     (*graphics)->pixeloffset = PixelOffsetModeDefault;
     (*graphics)->compmode = CompositingModeSourceOver;
     (*graphics)->unit = UnitDisplay;
     (*graphics)->scale = 1.0;
+    (*graphics)->xres = image->xres;
+    (*graphics)->yres = image->yres;
     (*graphics)->busy = FALSE;
     (*graphics)->textcontrast = 4;
     list_init(&(*graphics)->containers);
+#ifdef __REACTOS__
+    (*graphics)->contid = GDIP_GET_NEW_CONTID_FOR(*graphics);
+#else
     (*graphics)->contid = 0;
+#endif
 
     TRACE("<-- %p\n", *graphics);
 
@@ -1317,126 +2555,6 @@ GpStatus WINGDIPAPI GdipCreateFromHWNDICM(HWND hwnd, GpGraphics **graphics)
     return GdipCreateFromHWND(hwnd, graphics);
 }
 
-GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
-    GpMetafile **metafile)
-{
-    static int calls;
-
-    TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
-
-    if(!hemf || !metafile)
-        return InvalidParameter;
-
-    if(!(calls++))
-        FIXME("not implemented\n");
-
-    return NotImplemented;
-}
-
-GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
-    GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
-{
-    IStream *stream = NULL;
-    UINT read;
-    BYTE* copy;
-    HENHMETAFILE hemf;
-    GpStatus retval = Ok;
-
-    TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
-
-    if(!hwmf || !metafile || !placeable)
-        return InvalidParameter;
-
-    *metafile = NULL;
-    read = GetMetaFileBitsEx(hwmf, 0, NULL);
-    if(!read)
-        return GenericError;
-    copy = GdipAlloc(read);
-    GetMetaFileBitsEx(hwmf, read, copy);
-
-    hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
-    GdipFree(copy);
-
-    read = GetEnhMetaFileBits(hemf, 0, NULL);
-    copy = GdipAlloc(read);
-    GetEnhMetaFileBits(hemf, read, copy);
-    DeleteEnhMetaFile(hemf);
-
-    if(CreateStreamOnHGlobal(copy, TRUE, &stream) != S_OK){
-        ERR("could not make stream\n");
-        GdipFree(copy);
-        retval = GenericError;
-        goto err;
-    }
-
-    *metafile = GdipAlloc(sizeof(GpMetafile));
-    if(!*metafile){
-        retval = OutOfMemory;
-        goto err;
-    }
-
-    if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
-        (LPVOID*) &((*metafile)->image.picture)) != S_OK)
-    {
-        retval = GenericError;
-        goto err;
-    }
-
-
-    (*metafile)->image.type = ImageTypeMetafile;
-    memcpy(&(*metafile)->image.format, &ImageFormatWMF, sizeof(GUID));
-    (*metafile)->image.palette_flags = 0;
-    (*metafile)->image.palette_count = 0;
-    (*metafile)->image.palette_size = 0;
-    (*metafile)->image.palette_entries = NULL;
-    (*metafile)->image.xres = (REAL)placeable->Inch;
-    (*metafile)->image.yres = (REAL)placeable->Inch;
-    (*metafile)->bounds.X = ((REAL) placeable->BoundingBox.Left) / ((REAL) placeable->Inch);
-    (*metafile)->bounds.Y = ((REAL) placeable->BoundingBox.Top) / ((REAL) placeable->Inch);
-    (*metafile)->bounds.Width = ((REAL) (placeable->BoundingBox.Right
-                    - placeable->BoundingBox.Left));
-    (*metafile)->bounds.Height = ((REAL) (placeable->BoundingBox.Bottom
-                   - placeable->BoundingBox.Top));
-    (*metafile)->unit = UnitPixel;
-
-    if(delete)
-        DeleteMetaFile(hwmf);
-
-    TRACE("<-- %p\n", *metafile);
-
-err:
-    if (retval != Ok)
-        GdipFree(*metafile);
-    IStream_Release(stream);
-    return retval;
-}
-
-GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
-    GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
-{
-    HMETAFILE hmf = GetMetaFileW(file);
-
-    TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
-
-    if(!hmf) return InvalidParameter;
-
-    return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
-}
-
-GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
-    GpMetafile **metafile)
-{
-    FIXME("(%p, %p): stub\n", file, metafile);
-    return NotImplemented;
-}
-
-GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
-    GpMetafile **metafile)
-{
-    FIXME("(%p, %p): stub\n", stream, metafile);
-    return NotImplemented;
-}
-
 GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename,
     UINT access, IStream **stream)
 {
@@ -1463,11 +2581,19 @@ GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename,
 GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
 {
     GraphicsContainerItem *cont, *next;
+    GpStatus stat;
     TRACE("(%p)\n", graphics);
 
     if(!graphics) return InvalidParameter;
     if(graphics->busy) return ObjectBusy;
 
+    if (graphics->image && graphics->image_type == ImageTypeMetafile)
+    {
+        stat = METAFILE_GraphicsDeleted((GpMetafile*)graphics->image);
+        if (stat != Ok)
+            return stat;
+    }
+
     if(graphics->owndc)
         ReleaseDC(graphics->hwnd, graphics->hdc);
 
@@ -1477,8 +2603,15 @@ GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
     }
 
     GdipDeleteRegion(graphics->clip);
-    GdipDeleteMatrix(graphics->worldtrans);
-    GdipFree(graphics);
+
+    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;
+
+    heap_free(graphics);
 
     return Ok;
 }
@@ -1486,9 +2619,8 @@ GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
 GpStatus WINGDIPAPI GdipDrawArc(GpGraphics *graphics, GpPen *pen, REAL x,
     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
 {
-    INT save_state, num_pts;
-    GpPointF points[MAX_ARC_PTS];
-    GpStatus retval;
+    GpStatus status;
+    GpPath *path;
 
     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
           width, height, startAngle, sweepAngle);
@@ -1499,21 +2631,15 @@ GpStatus WINGDIPAPI GdipDrawArc(GpGraphics *graphics, GpPen *pen, REAL x,
     if(graphics->busy)
         return ObjectBusy;
 
-    if (!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
-
-    num_pts = arc2polybezier(points, x, y, width, height, startAngle, sweepAngle);
+    status = GdipCreatePath(FillModeAlternate, &path);
+    if (status != Ok) return status;
 
-    save_state = prepare_dc(graphics, pen);
-
-    retval = draw_polybezier(graphics, pen, points, num_pts, TRUE);
-
-    restore_dc(graphics, save_state);
+    status = GdipAddPathArc(path, x, y, width, height, startAngle, sweepAngle);
+    if (status == Ok)
+        status = GdipDrawPath(graphics, pen, path);
 
-    return retval;
+    GdipDeletePath(path);
+    return status;
 }
 
 GpStatus WINGDIPAPI GdipDrawArcI(GpGraphics *graphics, GpPen *pen, INT x,
@@ -1528,9 +2654,7 @@ GpStatus WINGDIPAPI GdipDrawArcI(GpGraphics *graphics, GpPen *pen, INT x,
 GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics *graphics, GpPen *pen, REAL x1,
     REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
 {
-    INT save_state;
     GpPointF pt[4];
-    GpStatus retval;
 
     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1,
           x2, y2, x3, y3, x4, y4);
@@ -1541,12 +2665,6 @@ GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics *graphics, GpPen *pen, REAL x1,
     if(graphics->busy)
         return ObjectBusy;
 
-    if (!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
-
     pt[0].X = x1;
     pt[0].Y = y1;
     pt[1].X = x2;
@@ -1555,61 +2673,23 @@ GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics *graphics, GpPen *pen, REAL x1,
     pt[2].Y = y3;
     pt[3].X = x4;
     pt[3].Y = y4;
-
-    save_state = prepare_dc(graphics, pen);
-
-    retval = draw_polybezier(graphics, pen, pt, 4, TRUE);
-
-    restore_dc(graphics, save_state);
-
-    return retval;
+    return GdipDrawBeziers(graphics, pen, pt, 4);
 }
 
 GpStatus WINGDIPAPI GdipDrawBezierI(GpGraphics *graphics, GpPen *pen, INT x1,
     INT y1, INT x2, INT y2, INT x3, INT y3, INT x4, INT y4)
 {
-    INT save_state;
-    GpPointF pt[4];
-    GpStatus retval;
-
     TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d)\n", graphics, pen, x1, y1,
           x2, y2, x3, y3, x4, y4);
 
-    if(!graphics || !pen)
-        return InvalidParameter;
-
-    if(graphics->busy)
-        return ObjectBusy;
-
-    if (!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
-
-    pt[0].X = x1;
-    pt[0].Y = y1;
-    pt[1].X = x2;
-    pt[1].Y = y2;
-    pt[2].X = x3;
-    pt[2].Y = y3;
-    pt[3].X = x4;
-    pt[3].Y = y4;
-
-    save_state = prepare_dc(graphics, pen);
-
-    retval = draw_polybezier(graphics, pen, pt, 4, TRUE);
-
-    restore_dc(graphics, save_state);
-
-    return retval;
+    return GdipDrawBezier(graphics, pen, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2, (REAL)x3, (REAL)y3, (REAL)x4, (REAL)y4);
 }
 
 GpStatus WINGDIPAPI GdipDrawBeziers(GpGraphics *graphics, GpPen *pen,
     GDIPCONST GpPointF *points, INT count)
 {
-    INT i;
-    GpStatus ret;
+    GpStatus status;
+    GpPath *path;
 
     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
 
@@ -1619,17 +2699,15 @@ GpStatus WINGDIPAPI GdipDrawBeziers(GpGraphics *graphics, GpPen *pen,
     if(graphics->busy)
         return ObjectBusy;
 
-    for(i = 0; i < floor(count / 4); i++){
-        ret = GdipDrawBezier(graphics, pen,
-                             points[4*i].X, points[4*i].Y,
-                             points[4*i + 1].X, points[4*i + 1].Y,
-                             points[4*i + 2].X, points[4*i + 2].Y,
-                             points[4*i + 3].X, points[4*i + 3].Y);
-        if(ret != Ok)
-            return ret;
-    }
+    status = GdipCreatePath(FillModeAlternate, &path);
+    if (status != Ok) return status;
 
-    return Ok;
+    status = GdipAddPathBeziers(path, points, count);
+    if (status == Ok)
+        status = GdipDrawPath(graphics, pen, path);
+
+    GdipDeletePath(path);
+    return status;
 }
 
 GpStatus WINGDIPAPI GdipDrawBeziersI(GpGraphics *graphics, GpPen *pen,
@@ -1647,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;
 
@@ -1658,7 +2736,7 @@ GpStatus WINGDIPAPI GdipDrawBeziersI(GpGraphics *graphics, GpPen *pen,
 
     ret = GdipDrawBeziers(graphics,pen,pts,count);
 
-    GdipFree(pts);
+    heap_free(pts);
 
     return ret;
 }
@@ -1683,7 +2761,7 @@ GpStatus WINGDIPAPI GdipDrawClosedCurve2(GpGraphics *graphics, GpPen *pen,
     GDIPCONST GpPointF *points, INT count, REAL tension)
 {
     GpPath *path;
-    GpStatus stat;
+    GpStatus status;
 
     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
 
@@ -1693,20 +2771,16 @@ GpStatus WINGDIPAPI GdipDrawClosedCurve2(GpGraphics *graphics, GpPen *pen,
     if(graphics->busy)
         return ObjectBusy;
 
-    if((stat = GdipCreatePath(FillModeAlternate, &path)) != Ok)
-        return stat;
-
-    stat = GdipAddPathClosedCurve2(path, points, count, tension);
-    if(stat != Ok){
-        GdipDeletePath(path);
-        return stat;
-    }
+    status = GdipCreatePath(FillModeAlternate, &path);
+    if (status != Ok) return status;
 
-    stat = GdipDrawPath(graphics, pen, path);
+    status = GdipAddPathClosedCurve2(path, points, count, tension);
+    if (status == Ok)
+        status = GdipDrawPath(graphics, pen, path);
 
     GdipDeletePath(path);
 
-    return stat;
+    return status;
 }
 
 GpStatus WINGDIPAPI GdipDrawClosedCurve2I(GpGraphics *graphics, GpPen *pen,
@@ -1721,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;
 
@@ -1732,7 +2806,7 @@ GpStatus WINGDIPAPI GdipDrawClosedCurve2I(GpGraphics *graphics, GpPen *pen,
 
     stat = GdipDrawClosedCurve2(graphics, pen, ptf, count, tension);
 
-    GdipFree(ptf);
+    heap_free(ptf);
 
     return stat;
 }
@@ -1757,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;
 
@@ -1767,7 +2841,7 @@ GpStatus WINGDIPAPI GdipDrawCurveI(GpGraphics *graphics, GpPen *pen,
     }
 
     ret = GdipDrawCurve(graphics,pen,pointsF,count);
-    GdipFree(pointsF);
+    heap_free(pointsF);
 
     return ret;
 }
@@ -1776,11 +2850,8 @@ GpStatus WINGDIPAPI GdipDrawCurveI(GpGraphics *graphics, GpPen *pen,
 GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics *graphics, GpPen *pen,
     GDIPCONST GpPointF *points, INT count, REAL tension)
 {
-    /* PolyBezier expects count*3-2 points. */
-    INT i, len_pt = count*3-2, save_state;
-    GpPointF *pt;
-    REAL x1, x2, y1, y2;
-    GpStatus retval;
+    GpPath *path;
+    GpStatus status;
 
     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
 
@@ -1793,53 +2864,15 @@ GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics *graphics, GpPen *pen,
     if(count < 2)
         return InvalidParameter;
 
-    if (!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
-
-    pt = GdipAlloc(len_pt * sizeof(GpPointF));
-    if(!pt)
-        return OutOfMemory;
-
-    tension = tension * TENSION_CONST;
-
-    calc_curve_bezier_endp(points[0].X, points[0].Y, points[1].X, points[1].Y,
-        tension, &x1, &y1);
-
-    pt[0].X = points[0].X;
-    pt[0].Y = points[0].Y;
-    pt[1].X = x1;
-    pt[1].Y = y1;
-
-    for(i = 0; i < count-2; i++){
-        calc_curve_bezier(&(points[i]), tension, &x1, &y1, &x2, &y2);
-
-        pt[3*i+2].X = x1;
-        pt[3*i+2].Y = y1;
-        pt[3*i+3].X = points[i+1].X;
-        pt[3*i+3].Y = points[i+1].Y;
-        pt[3*i+4].X = x2;
-        pt[3*i+4].Y = y2;
-    }
-
-    calc_curve_bezier_endp(points[count-1].X, points[count-1].Y,
-        points[count-2].X, points[count-2].Y, tension, &x1, &y1);
-
-    pt[len_pt-2].X = x1;
-    pt[len_pt-2].Y = y1;
-    pt[len_pt-1].X = points[count-1].X;
-    pt[len_pt-1].Y = points[count-1].Y;
+    status = GdipCreatePath(FillModeAlternate, &path);
+    if (status != Ok) return status;
 
-    save_state = prepare_dc(graphics, pen);
-
-    retval = draw_polybezier(graphics, pen, pt, len_pt, TRUE);
-
-    GdipFree(pt);
-    restore_dc(graphics, save_state);
+    status = GdipAddPathCurve2(path, points, count, tension);
+    if (status == Ok)
+        status = GdipDrawPath(graphics, pen, path);
 
-    return retval;
+    GdipDeletePath(path);
+    return status;
 }
 
 GpStatus WINGDIPAPI GdipDrawCurve2I(GpGraphics *graphics, GpPen *pen,
@@ -1854,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;
 
@@ -1864,7 +2897,7 @@ GpStatus WINGDIPAPI GdipDrawCurve2I(GpGraphics *graphics, GpPen *pen,
     }
 
     ret = GdipDrawCurve2(graphics,pen,pointsF,count,tension);
-    GdipFree(pointsF);
+    heap_free(pointsF);
 
     return ret;
 }
@@ -1902,9 +2935,8 @@ GpStatus WINGDIPAPI GdipDrawCurve3I(GpGraphics *graphics, GpPen *pen,
 GpStatus WINGDIPAPI GdipDrawEllipse(GpGraphics *graphics, GpPen *pen, REAL x,
     REAL y, REAL width, REAL height)
 {
-    INT save_state;
-    GpPointF ptf[2];
-    POINT pti[2];
+    GpPath *path;
+    GpStatus status;
 
     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
 
@@ -1914,27 +2946,15 @@ GpStatus WINGDIPAPI GdipDrawEllipse(GpGraphics *graphics, GpPen *pen, REAL x,
     if(graphics->busy)
         return ObjectBusy;
 
-    if (!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
-
-    ptf[0].X = x;
-    ptf[0].Y = y;
-    ptf[1].X = x + width;
-    ptf[1].Y = y + height;
-
-    save_state = prepare_dc(graphics, pen);
-    SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
-
-    transform_and_round_points(graphics, pti, ptf, 2);
-
-    Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
+    status = GdipCreatePath(FillModeAlternate, &path);
+    if (status != Ok) return status;
 
-    restore_dc(graphics, save_state);
+    status = GdipAddPathEllipse(path, x, y, width, height);
+    if (status == Ok)
+        status = GdipDrawPath(graphics, pen, path);
 
-    return Ok;
+    GdipDeletePath(path);
+    return status;
 }
 
 GpStatus WINGDIPAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x,
@@ -1949,7 +2969,6 @@ GpStatus WINGDIPAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x,
 GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x, REAL y)
 {
     UINT width, height;
-    GpPointF points[3];
 
     TRACE("(%p, %p, %.2f, %.2f)\n", graphics, image, x, y);
 
@@ -1959,15 +2978,8 @@ GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x,
     GdipGetImageWidth(image, &width);
     GdipGetImageHeight(image, &height);
 
-    /* FIXME: we should use the graphics and image dpi, somehow */
-
-    points[0].X = points[2].X = x;
-    points[0].Y = points[1].Y = y;
-    points[1].X = x + width;
-    points[2].Y = y + height;
-
-    return GdipDrawImagePointsRect(graphics, image, points, 3, 0, 0, width, height,
-        UnitPixel, NULL, NULL, NULL);
+    return GdipDrawImagePointRect(graphics, image, x, y,
+                                  0.0, 0.0, (REAL)width, (REAL)height, UnitPixel);
 }
 
 GpStatus WINGDIPAPI GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x,
@@ -1983,14 +2995,23 @@ GpStatus WINGDIPAPI GdipDrawImagePointRect(GpGraphics *graphics, GpImage *image,
     GpUnit srcUnit)
 {
     GpPointF points[3];
+    REAL scale_x, scale_y, width, height;
+
     TRACE("(%p, %p, %f, %f, %f, %f, %f, %f, %d)\n", graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
 
+    if (!graphics || !image) return InvalidParameter;
+
+    scale_x = units_scale(srcUnit, graphics->unit, graphics->xres);
+    scale_x *= graphics->xres / image->xres;
+    scale_y = units_scale(srcUnit, graphics->unit, graphics->yres);
+    scale_y *= graphics->yres / image->yres;
+    width = srcwidth * scale_x;
+    height = srcheight * scale_y;
+
     points[0].X = points[2].X = x;
     points[0].Y = points[1].Y = y;
-
-    /* FIXME: convert image coordinates to Graphics coordinates? */
-    points[1].X = x + srcwidth;
-    points[2].Y = y + srcheight;
+    points[1].X = x + width;
+    points[2].Y = y + height;
 
     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
         srcwidth, srcheight, srcUnit, NULL, NULL, NULL);
@@ -2006,15 +3027,45 @@ GpStatus WINGDIPAPI GdipDrawImagePointRectI(GpGraphics *graphics, GpImage *image
 GpStatus WINGDIPAPI GdipDrawImagePoints(GpGraphics *graphics, GpImage *image,
     GDIPCONST GpPointF *dstpoints, INT count)
 {
-    FIXME("(%p, %p, %p, %d): stub\n", graphics, image, dstpoints, count);
-    return NotImplemented;
+    UINT width, height;
+
+    TRACE("(%p, %p, %p, %d)\n", graphics, image, dstpoints, count);
+
+    if(!image)
+        return InvalidParameter;
+
+    GdipGetImageWidth(image, &width);
+    GdipGetImageHeight(image, &height);
+
+    return GdipDrawImagePointsRect(graphics, image, dstpoints, count, 0, 0,
+        width, height, UnitPixel, NULL, NULL, NULL);
 }
 
 GpStatus WINGDIPAPI GdipDrawImagePointsI(GpGraphics *graphics, GpImage *image,
     GDIPCONST GpPoint *dstpoints, INT count)
 {
-    FIXME("(%p, %p, %p, %d): stub\n", graphics, image, dstpoints, count);
-    return NotImplemented;
+    GpPointF ptf[3];
+
+    TRACE("(%p, %p, %p, %d)\n", graphics, image, dstpoints, count);
+
+    if (count != 3 || !dstpoints)
+        return InvalidParameter;
+
+    ptf[0].X = (REAL)dstpoints[0].X;
+    ptf[0].Y = (REAL)dstpoints[0].Y;
+    ptf[1].X = (REAL)dstpoints[1].X;
+    ptf[1].Y = (REAL)dstpoints[1].Y;
+    ptf[2].X = (REAL)dstpoints[2].X;
+    ptf[2].Y = (REAL)dstpoints[2].Y;
+
+    return GdipDrawImagePoints(graphics, image, ptf, count);
+}
+
+static BOOL CALLBACK play_metafile_proc(EmfPlusRecordType record_type, unsigned int flags,
+    unsigned int dataSize, const unsigned char *pStr, void *userdata)
+{
+    GdipPlayMetafileRecord(userdata, record_type, flags, dataSize, pStr);
+    return TRUE;
 }
 
 GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image,
@@ -2024,83 +3075,109 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
 {
     GpPointF ptf[4];
     POINT pti[4];
-    REAL dx, dy;
     GpStatus stat;
 
     TRACE("(%p, %p, %p, %d, %f, %f, %f, %f, %d, %p, %p, %p)\n", graphics, image, points,
           count, srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
           callbackData);
 
+    if (count > 3)
+        return NotImplemented;
+
     if(!graphics || !image || !points || count != 3)
          return InvalidParameter;
 
     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 */
+    if (srcwidth < 0)
+    {
+        GpPointF tmp = ptf[1];
+        srcx = srcx + srcwidth;
+        srcwidth = -srcwidth;
+        ptf[2].X = ptf[2].X + ptf[1].X - ptf[0].X;
+        ptf[2].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
+        ptf[1] = ptf[0];
+        ptf[0] = tmp;
+    }
+
+    if (srcheight < 0)
+    {
+        GpPointF tmp = ptf[2];
+        srcy = srcy + srcheight;
+        srcheight = -srcheight;
+        ptf[1].X = ptf[1].X + ptf[2].X - ptf[0].X;
+        ptf[1].Y = ptf[1].Y + ptf[2].Y - ptf[0].Y;
+        ptf[2] = ptf[0];
+        ptf[0] = tmp;
+    }
+
     ptf[3].X = ptf[2].X + ptf[1].X - ptf[0].X;
     ptf[3].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
-    transform_and_round_points(graphics, pti, ptf, 4);
+    if (!srcwidth || !srcheight || (ptf[3].X == ptf[0].X && ptf[3].Y == ptf[0].Y))
+        return Ok;
+    gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 4);
+    round_points(pti, ptf, 4);
 
-    if (image->picture)
-    {
-        if (!graphics->hdc)
-        {
-            FIXME("graphics object has no HDC\n");
-        }
+    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]));
 
-        /* FIXME: partially implemented (only works for rectangular parallelograms) */
-        if(srcUnit == UnitInch)
-            dx = dy = (REAL) INCH_HIMETRIC;
-        else if(srcUnit == UnitPixel){
-            dx = ((REAL) INCH_HIMETRIC) /
-                 ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX));
-            dy = ((REAL) INCH_HIMETRIC) /
-                 ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY));
-        }
-        else
-            return NotImplemented;
+    srcx = units_to_pixels(srcx, srcUnit, image->xres);
+    srcy = units_to_pixels(srcy, srcUnit, image->yres);
+    srcwidth = units_to_pixels(srcwidth, srcUnit, image->xres);
+    srcheight = units_to_pixels(srcheight, srcUnit, image->yres);
+    TRACE("src pixels: %f,%f %fx%f\n", srcx, srcy, srcwidth, srcheight);
 
-        if(IPicture_Render(image->picture, graphics->hdc,
-            pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
-            srcx * dx, srcy * dy,
-            srcwidth * dx, srcheight * dy,
-            NULL) != S_OK){
-            if(callback)
-                callback(callbackData);
-            return GenericError;
-        }
-    }
-    else if (image->type == ImageTypeBitmap)
+    if (image->type == ImageTypeBitmap)
     {
         GpBitmap* bitmap = (GpBitmap*)image;
-        int use_software=0;
+        BOOL do_resampling = FALSE;
+        BOOL use_software = FALSE;
 
-        if (srcUnit == UnitInch)
-            dx = dy = 96.0; /* FIXME: use the image resolution */
-        else if (srcUnit == UnitPixel)
-            dx = dy = 1.0;
-        else
-            return NotImplemented;
+        TRACE("graphics: %.2fx%.2f dpi, fmt %#x, scale %f, image: %.2fx%.2f dpi, fmt %#x, color %08x\n",
+            graphics->xres, graphics->yres,
+            graphics->image && graphics->image->type == ImageTypeBitmap ? ((GpBitmap *)graphics->image)->format : 0,
+            graphics->scale, image->xres, image->yres, bitmap->format,
+            imageAttributes ? imageAttributes->outside_color : 0);
+
+        if (ptf[1].Y != ptf[0].Y || ptf[2].X != ptf[0].X ||
+            ptf[1].X - ptf[0].X != srcwidth || ptf[2].Y - ptf[0].Y != srcheight ||
+            srcx < 0 || srcy < 0 ||
+            srcx + srcwidth > bitmap->width || srcy + srcheight > bitmap->height)
+            do_resampling = TRUE;
 
-        if (imageAttributes ||
-            (graphics->image && graphics->image->type == ImageTypeBitmap) ||
-            !((GpBitmap*)image)->hbitmap ||
-            ptf[1].Y != ptf[0].Y || ptf[2].X != ptf[0].X)
-            use_software = 1;
+        if (imageAttributes || graphics->alpha_hdc || do_resampling ||
+            (graphics->image && graphics->image->type == ImageTypeBitmap))
+            use_software = TRUE;
 
         if (use_software)
         {
-            RECT src_area, dst_area;
-            int i, x, y, stride;
-            GpMatrix *dst_to_src;
+            RECT dst_area;
+            GpRectF graphics_bounds;
+            GpRect src_area;
+            int i, x, y, src_stride, dst_stride;
+            GpMatrix dst_to_src;
             REAL m11, m12, m21, m22, mdx, mdy;
-            LPBYTE data;
+            LPBYTE src_data, dst_data, dst_dyn_data=NULL;
+            BitmapData lockeddata;
+            InterpolationMode interpolation = graphics->interpolation;
+            PixelOffsetMode offset_mode = graphics->pixeloffset;
+            GpPointF dst_to_src_points[3] = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}};
+            REAL x_dx, x_dy, y_dx, y_dy;
+            static const GpImageAttributes defaultImageAttributes = {WrapModeClamp, 0, FALSE};
 
-            src_area.left = srcx*dx;
-            src_area.top = srcy*dy;
-            src_area.right = (srcx+srcwidth)*dx;
-            src_area.bottom = (srcy+srcheight)*dy;
+            if (!imageAttributes)
+                imageAttributes = &defaultImageAttributes;
 
             dst_area.left = dst_area.right = pti[0].x;
             dst_area.top = dst_area.bottom = pti[0].y;
@@ -2112,6 +3189,18 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
                 if (dst_area.bottom < pti[i].y) dst_area.bottom = pti[i].y;
             }
 
+            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);
+            if (graphics_bounds.Y > dst_area.top) dst_area.top = floorf(graphics_bounds.Y);
+            if (graphics_bounds.X + graphics_bounds.Width < dst_area.right) dst_area.right = ceilf(graphics_bounds.X + graphics_bounds.Width);
+            if (graphics_bounds.Y + graphics_bounds.Height < dst_area.bottom) dst_area.bottom = ceilf(graphics_bounds.Y + graphics_bounds.Height);
+
+            TRACE("dst_area: %s\n", wine_dbgstr_rect(&dst_area));
+
+            if (IsRectEmpty(&dst_area)) return Ok;
+
             m11 = (ptf[1].X - ptf[0].X) / srcwidth;
             m21 = (ptf[2].X - ptf[0].X) / srcheight;
             mdx = ptf[0].X - m11 * srcx - m21 * srcy;
@@ -2119,155 +3208,146 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
             m22 = (ptf[2].Y - ptf[0].Y) / srcheight;
             mdy = ptf[0].Y - m12 * srcx - m22 * srcy;
 
-            stat = GdipCreateMatrix2(m11, m12, m21, m22, mdx, mdy, &dst_to_src);
-            if (stat != Ok) return stat;
-
-            stat = GdipInvertMatrix(dst_to_src);
-            if (stat != Ok)
-            {
-                GdipDeleteMatrix(dst_to_src);
-                return stat;
-            }
-
-            data = GdipAlloc(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
-            if (!data)
-            {
-                GdipDeleteMatrix(dst_to_src);
-                return OutOfMemory;
-            }
+            GdipSetMatrixElements(&dst_to_src, m11, m12, m21, m22, mdx, mdy);
 
-            stride = sizeof(ARGB) * (dst_area.right - dst_area.left);
+            stat = GdipInvertMatrix(&dst_to_src);
+            if (stat != Ok) return stat;
 
-            if (imageAttributes &&
-                (imageAttributes->wrap != WrapModeClamp ||
-                 imageAttributes->outside_color != 0x00000000 ||
-                 imageAttributes->clamp))
+            if (do_resampling)
             {
-                static int fixme;
-                if (!fixme++)
-                    FIXME("Image wrap mode not implemented\n");
+                get_bitmap_sample_size(interpolation, imageAttributes->wrap,
+                    bitmap, srcx, srcy, srcwidth, srcheight, &src_area);
             }
-
-            for (x=dst_area.left; x<dst_area.right; x++)
+            else
             {
-                for (y=dst_area.top; y<dst_area.bottom; y++)
-                {
-                    GpPointF src_pointf;
-                    int src_x, src_y;
-                    ARGB *src_color;
+                /* Make sure src_area is equal in size to dst_area. */
+                src_area.X = srcx + dst_area.left - pti[0].x;
+                src_area.Y = srcy + dst_area.top - pti[0].y;
+                src_area.Width = dst_area.right - dst_area.left;
+                src_area.Height = dst_area.bottom - dst_area.top;
+            }
 
-                    src_pointf.X = x;
-                    src_pointf.Y = y;
+            TRACE("src_area: %d x %d\n", src_area.Width, src_area.Height);
 
-                    GdipTransformMatrixPoints(dst_to_src, &src_pointf, 1);
+            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 a compatible buffer. */
+            lockeddata.Width = src_area.Width;
+            lockeddata.Height = src_area.Height;
+            lockeddata.Stride = src_stride;
+            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;
 
-                    src_x = roundr(src_pointf.X);
-                    src_y = roundr(src_pointf.Y);
+            stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
+                lockeddata.PixelFormat, &lockeddata);
 
-                    src_color = (ARGB*)(data + stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
+            if (stat == Ok)
+                stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
 
-                    if (src_x < src_area.left || src_x >= src_area.right ||
-                        src_y < src_area.top || src_y >= src_area.bottom)
-                        *src_color = 0;
-                    else
-                        GdipBitmapGetPixel(bitmap, src_x, src_y, src_color);
-                }
+            if (stat != Ok)
+            {
+                heap_free(src_data);
+                return stat;
             }
 
-            GdipDeleteMatrix(dst_to_src);
+            apply_image_attributes(imageAttributes, src_data,
+                src_area.Width, src_area.Height,
+                src_stride, ColorAdjustTypeBitmap, lockeddata.PixelFormat);
 
-            if (imageAttributes)
+            if (do_resampling)
             {
-                if (imageAttributes->colorkeys[ColorAdjustTypeBitmap].enabled ||
-                    imageAttributes->colorkeys[ColorAdjustTypeDefault].enabled)
+                REAL delta_xx, delta_xy, delta_yx, delta_yy;
+
+                /* Transform the bits as needed to the destination. */
+                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)
                 {
-                    const struct color_key *key;
-                    BYTE min_blue, min_green, min_red;
-                    BYTE max_blue, max_green, max_red;
+                    heap_free(src_data);
+                    return OutOfMemory;
+                }
 
-                    if (imageAttributes->colorkeys[ColorAdjustTypeBitmap].enabled)
-                        key = &imageAttributes->colorkeys[ColorAdjustTypeBitmap];
-                    else
-                        key = &imageAttributes->colorkeys[ColorAdjustTypeDefault];
+                dst_stride = sizeof(ARGB) * (dst_area.right - dst_area.left);
 
-                    min_blue = key->low&0xff;
-                    min_green = (key->low>>8)&0xff;
-                    min_red = (key->low>>16)&0xff;
+                GdipTransformMatrixPoints(&dst_to_src, dst_to_src_points, 3);
 
-                    max_blue = key->high&0xff;
-                    max_green = (key->high>>8)&0xff;
-                    max_red = (key->high>>16)&0xff;
+                x_dx = dst_to_src_points[1].X - dst_to_src_points[0].X;
+                x_dy = dst_to_src_points[1].Y - dst_to_src_points[0].Y;
+                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++)
-                        for (y=dst_area.top; y<dst_area.bottom; y++)
-                        {
-                            ARGB *src_color;
-                            BYTE blue, green, red;
-                            src_color = (ARGB*)(data + stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
-                            blue = *src_color&0xff;
-                            green = (*src_color>>8)&0xff;
-                            red = (*src_color>>16)&0xff;
-                            if (blue >= min_blue && green >= min_green && red >= min_red &&
-                                blue <= max_blue && green <= max_green && red <= max_red)
-                                *src_color = 0x00000000;
-                        }
-                }
+                delta_yy = dst_area.top * y_dy;
+                delta_yx = dst_area.top * y_dx;
 
-                if (imageAttributes->colorremaptables[ColorAdjustTypeBitmap].enabled ||
-                    imageAttributes->colorremaptables[ColorAdjustTypeDefault].enabled)
+                for (y=dst_area.top; y<dst_area.bottom; y++)
                 {
-                    const struct color_remap_table *table;
-
-                    if (imageAttributes->colorremaptables[ColorAdjustTypeBitmap].enabled)
-                        table = &imageAttributes->colorremaptables[ColorAdjustTypeBitmap];
-                    else
-                        table = &imageAttributes->colorremaptables[ColorAdjustTypeDefault];
+                    delta_xx = dst_area.left * x_dx;
+                    delta_xy = dst_area.left * x_dy;
 
                     for (x=dst_area.left; x<dst_area.right; x++)
-                        for (y=dst_area.top; y<dst_area.bottom; y++)
+                    {
+                        GpPointF src_pointf;
+                        ARGB *dst_color;
+
+                        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)
                         {
-                            ARGB *src_color;
-                            src_color = (ARGB*)(data + stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
-                            for (i=0; i<table->mapsize; i++)
-                            {
-                                if (*src_color == table->colormap[i].oldColor.Argb)
-                                {
-                                    *src_color = table->colormap[i].newColor.Argb;
-                                    break;
-                                }
-                            }
+                            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;
 
-                if (imageAttributes->colormatrices[ColorAdjustTypeBitmap].enabled ||
-                    imageAttributes->colormatrices[ColorAdjustTypeDefault].enabled)
-                {
-                    static int fixme;
-                    if (!fixme++)
-                        FIXME("Color transforms not implemented\n");
-                }
+                        delta_xx += x_dx;
+                        delta_yx += y_dx;
+                    }
 
-                if (imageAttributes->gamma_enabled[ColorAdjustTypeBitmap] ||
-                    imageAttributes->gamma_enabled[ColorAdjustTypeDefault])
-                {
-                    static int fixme;
-                    if (!fixme++)
-                        FIXME("Gamma adjustment not implemented\n");
+                    delta_xy += x_dy;
+                    delta_yy += y_dy;
                 }
             }
+            else
+            {
+                dst_data = src_data;
+                dst_stride = src_stride;
+            }
+
+            gdi_transform_acquire(graphics);
 
             stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
-                data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, stride);
+                dst_data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, dst_stride,
+                lockeddata.PixelFormat);
+
+            gdi_transform_release(graphics);
 
-            GdipFree(data);
+            heap_free(src_data);
+
+            heap_free(dst_dyn_data);
 
             return stat;
         }
         else
         {
             HDC hdc;
-            int temp_hdc=0, temp_bitmap=0;
+            BOOL temp_hdc = FALSE, temp_bitmap = FALSE;
             HBITMAP hbitmap, old_hbm=NULL;
+            HRGN hrgn;
+            INT save_state;
 
             if (!(bitmap->format == PixelFormat16bppRGB555 ||
                   bitmap->format == PixelFormat24bppRGB ||
@@ -2280,8 +3360,8 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
 
                 /* we can't draw a bitmap of this format directly */
                 hdc = CreateCompatibleDC(0);
-                temp_hdc = 1;
-                temp_bitmap = 1;
+                temp_hdc = TRUE;
+                temp_bitmap = TRUE;
 
                 bih.biSize = sizeof(BITMAPINFOHEADER);
                 bih.biWidth = bitmap->width;
@@ -2305,11 +3385,19 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
 
                 convert_pixels(bitmap->width, bitmap->height,
                     bitmap->width*4, temp_bits, dst_format,
-                    bitmap->stride, bitmap->bits, bitmap->format, bitmap->image.palette_entries);
+                    bitmap->stride, bitmap->bits, bitmap->format,
+                    bitmap->image.palette);
             }
             else
             {
-                hbitmap = bitmap->hbitmap;
+                if (bitmap->hbitmap)
+                    hbitmap = bitmap->hbitmap;
+                else
+                {
+                    GdipCreateHBITMAPFromBitmap(bitmap, &hbitmap, 0);
+                    temp_bitmap = TRUE;
+                }
+
                 hdc = bitmap->hdc;
                 temp_hdc = (hdc == 0);
             }
@@ -2320,24 +3408,33 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
                 old_hbm = SelectObject(hdc, hbitmap);
             }
 
-            if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
+            save_state = SaveDC(graphics->hdc);
+
+            stat = get_clip_hrgn(graphics, &hrgn);
+
+            if (stat == Ok)
             {
-                BLENDFUNCTION bf;
+                ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
+                DeleteObject(hrgn);
+            }
 
-                bf.BlendOp = AC_SRC_OVER;
-                bf.BlendFlags = 0;
-                bf.SourceConstantAlpha = 255;
-                bf.AlphaFormat = AC_SRC_ALPHA;
+            gdi_transform_acquire(graphics);
 
-                GdiAlphaBlend(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
-                    hdc, srcx*dx, srcy*dy, srcwidth*dx, srcheight*dy, bf);
+            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,
+                                hdc, srcx, srcy, srcwidth, srcheight);
             }
             else
             {
                 StretchBlt(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
-                    hdc, srcx*dx, srcy*dy, srcwidth*dx, srcheight*dy, SRCCOPY);
+                    hdc, srcx, srcy, srcwidth, srcheight, SRCCOPY);
             }
 
+            gdi_transform_release(graphics);
+
+            RestoreDC(graphics->hdc, save_state);
+
             if (temp_hdc)
             {
                 SelectObject(hdc, old_hbm);
@@ -2348,10 +3445,22 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
                 DeleteObject(hbitmap);
         }
     }
+    else if (image->type == ImageTypeMetafile && ((GpMetafile*)image)->hemf)
+    {
+        GpRectF rc;
+
+        rc.X = srcx;
+        rc.Y = srcy;
+        rc.Width = srcwidth;
+        rc.Height = srcheight;
+
+        return GdipEnumerateMetafileSrcRectDestPoints(graphics, (GpMetafile*)image,
+            points, count, &rc, srcUnit, play_metafile_proc, image, imageAttributes);
+    }
     else
     {
-        ERR("GpImage with no IPicture or HBITMAP?!\n");
-        return NotImplemented;
+        WARN("GpImage with nothing we can draw (metafile in wrong state?)\n");
+        return InvalidParameter;
     }
 
     return Ok;
@@ -2369,236 +3478,593 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRectI(GpGraphics *graphics, GpImage *imag
           srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
           callbackData);
 
-    if(!points || count!=3)
-        return InvalidParameter;
+    if(!points || count!=3)
+        return InvalidParameter;
+
+    for(i = 0; i < count; i++){
+        pointsF[i].X = (REAL)points[i].X;
+        pointsF[i].Y = (REAL)points[i].Y;
+    }
+
+    return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
+                                   (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
+                                   callback, callbackData);
+}
+
+GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image,
+    REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
+    REAL srcwidth, REAL srcheight, GpUnit srcUnit,
+    GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback,
+    VOID * callbackData)
+{
+    GpPointF points[3];
+
+    TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n",
+          graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
+          srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
+
+    points[0].X = dstx;
+    points[0].Y = dsty;
+    points[1].X = dstx + dstwidth;
+    points[1].Y = dsty;
+    points[2].X = dstx;
+    points[2].Y = dsty + dstheight;
+
+    return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
+               srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
+}
+
+GpStatus WINGDIPAPI GdipDrawImageRectRectI(GpGraphics *graphics, GpImage *image,
+       INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy,
+       INT srcwidth, INT srcheight, GpUnit srcUnit,
+       GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback,
+       VOID * callbackData)
+{
+    GpPointF points[3];
+
+    TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n",
+          graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
+          srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
+
+    points[0].X = dstx;
+    points[0].Y = dsty;
+    points[1].X = dstx + dstwidth;
+    points[1].Y = dsty;
+    points[2].X = dstx;
+    points[2].Y = dsty + dstheight;
+
+    return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
+               srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
+}
+
+GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image,
+    REAL x, REAL y, REAL width, REAL height)
+{
+    RectF bounds;
+    GpUnit unit;
+    GpStatus ret;
+
+    TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
+
+    if(!graphics || !image)
+        return InvalidParameter;
+
+    ret = GdipGetImageBounds(image, &bounds, &unit);
+    if(ret != Ok)
+        return ret;
+
+    return GdipDrawImageRectRect(graphics, image, x, y, width, height,
+                                 bounds.X, bounds.Y, bounds.Width, bounds.Height,
+                                 unit, NULL, NULL, NULL);
+}
+
+GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image,
+    INT x, INT y, INT width, INT height)
+{
+    TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
+
+    return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
+}
+
+GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
+    REAL y1, REAL x2, REAL y2)
+{
+    GpPointF pt[2];
+
+    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;
+    pt[1].Y = y2;
+    return GdipDrawLines(graphics, pen, pt, 2);
+}
+
+GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
+    INT y1, INT x2, INT y2)
+{
+    TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
+
+    return GdipDrawLine(graphics, pen, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2);
+}
+
+GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
+    GpPointF *points, INT count)
+{
+    GpStatus status;
+    GpPath *path;
+
+    TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
+
+    if(!pen || !graphics || (count < 2))
+        return InvalidParameter;
+
+    if(graphics->busy)
+        return ObjectBusy;
+
+    status = GdipCreatePath(FillModeAlternate, &path);
+    if (status != Ok) return status;
+
+    status = GdipAddPathLine2(path, points, count);
+    if (status == Ok)
+        status = GdipDrawPath(graphics, pen, path);
+
+    GdipDeletePath(path);
+    return status;
+}
+
+GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
+    GpPoint *points, INT count)
+{
+    GpStatus retval;
+    GpPointF *ptf;
+    int i;
+
+    TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
+
+    ptf = heap_alloc_zero(count * sizeof(GpPointF));
+    if(!ptf) return OutOfMemory;
+
+    for(i = 0; i < count; i ++){
+        ptf[i].X = (REAL) points[i].X;
+        ptf[i].Y = (REAL) points[i].Y;
+    }
+
+    retval = GdipDrawLines(graphics, pen, ptf, count);
+
+    heap_free(ptf);
+    return retval;
+}
+
+static GpStatus GDI32_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
+{
+    INT save_state;
+    GpStatus retval;
+    HRGN hrgn=NULL;
+
+    save_state = prepare_dc(graphics, pen);
+
+    retval = get_clip_hrgn(graphics, &hrgn);
+
+    if (retval != Ok)
+        goto end;
+
+    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);
+
+    return retval;
+}
+
+static GpStatus SOFTWARE_GdipDrawThinPath(GpGraphics *graphics, GpPen *pen, 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;
+
+    stat = GdipClonePath(path, &flat_path);
+
+    if (stat != Ok)
+        return stat;
+
+    stat = GdipCreateMatrix(&transform);
+
+    if (stat == Ok)
+    {
+        stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
+                CoordinateSpaceWorld, transform);
+
+        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;
 
-    for(i = 0; i < count; i++){
-        pointsF[i].X = (REAL)points[i].X;
-        pointsF[i].Y = (REAL)points[i].Y;
+        output_bits = heap_alloc_zero(output_width * output_height * sizeof(DWORD));
+        if (!output_bits)
+            stat = OutOfMemory;
     }
 
-    return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
-                                   (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
-                                   callback, callbackData);
-}
+    if (stat == Ok)
+    {
+        if (pen->brush->bt != BrushTypeSolidColor)
+        {
+            /* allocate and draw brush output */
+            brush_bits = heap_alloc_zero(output_width * output_height * sizeof(DWORD));
 
-GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image,
-    REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
-    REAL srcwidth, REAL srcheight, GpUnit srcUnit,
-    GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback,
-    VOID * callbackData)
-{
-    GpPointF points[3];
+            if (brush_bits)
+            {
+                stat = brush_fill_pixels(graphics, pen->brush, brush_bits,
+                    &gp_output_area, output_width);
+            }
+            else
+                stat = OutOfMemory;
+        }
 
-    TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n",
-          graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
-          srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
+        if (stat == Ok)
+        {
+            /* convert dash pattern to bool array */
+            switch (pen->dash)
+            {
+            case DashStyleCustom:
+            {
+                dash_pattern_size = 0;
 
-    points[0].X = dstx;
-    points[0].Y = dsty;
-    points[1].X = dstx + dstwidth;
-    points[1].Y = dsty;
-    points[2].X = dstx;
-    points[2].Y = dsty + dstheight;
+                for (i=0; i < pen->numdashes; i++)
+                    dash_pattern_size += gdip_round(pen->dashes[i]);
 
-    return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
-               srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
-}
+                if (dash_pattern_size != 0)
+                {
+                    dash_pattern = dyn_dash_pattern = heap_alloc(dash_pattern_size);
 
-GpStatus WINGDIPAPI GdipDrawImageRectRectI(GpGraphics *graphics, GpImage *image,
-       INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy,
-       INT srcwidth, INT srcheight, GpUnit srcUnit,
-       GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback,
-       VOID * callbackData)
-{
-    GpPointF points[3];
+                    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;
 
-    TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n",
-          graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
-          srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
+                    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;
+            }
+        }
 
-    points[0].X = dstx;
-    points[0].Y = dsty;
-    points[1].X = dstx + dstwidth;
-    points[1].Y = dsty;
-    points[2].X = dstx;
-    points[2].Y = dsty + dstheight;
+        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;
 
-    return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
-               srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
-}
+            for (i=0; i < flat_path->pathdata.Count; i++)
+            {
+                BYTE type, type2;
+                GpPointF start_point, end_point;
+                GpPoint start_pointi, end_pointi;
 
-GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image,
-    REAL x, REAL y, REAL width, REAL height)
-{
-    RectF bounds;
-    GpUnit unit;
-    GpStatus ret;
+                type = flat_path->pathdata.Types[i];
+                if (i+1 < flat_path->pathdata.Count)
+                    type2 = flat_path->pathdata.Types[i+1];
+                else
+                    type2 = PathPointTypeStart;
 
-    TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
+                start_point = flat_path->pathdata.Points[i];
 
-    if(!graphics || !image)
-        return InvalidParameter;
+                if ((type & PathPointTypePathTypeMask) == PathPointTypeStart)
+                    subpath_start = start_point;
 
-    ret = GdipGetImageBounds(image, &bounds, &unit);
-    if(ret != Ok)
-        return ret;
+                if ((type & PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath)
+                    end_point = subpath_start;
+                else if ((type2 & PathPointTypePathTypeMask) == PathPointTypeStart)
+                    continue;
+                else
+                    end_point = flat_path->pathdata.Points[i+1];
 
-    return GdipDrawImageRectRect(graphics, image, x, y, width, height,
-                                 bounds.X, bounds.Y, bounds.Width, bounds.Height,
-                                 unit, NULL, NULL, NULL);
-}
+                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);
 
-GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image,
-    INT x, INT y, INT width, INT height)
-{
-    TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
+                if(start_pointi.X == end_pointi.X && start_pointi.Y == end_pointi.Y)
+                    continue;
 
-    return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
-}
+                /* 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;
 
-GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
-    REAL y1, REAL x2, REAL y2)
-{
-    INT save_state;
-    GpPointF pt[2];
-    GpStatus retval;
+                    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;
+                    }
 
-    TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
+                    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(!pen || !graphics)
-        return InvalidParameter;
+                        if (x == prev_x && y == prev_y)
+                            continue;
 
-    if(graphics->busy)
-        return ObjectBusy;
+                        prev_x = x;
+                        prev_y = y;
+                        dash_pos = (dash_pos + 1 == dash_pattern_size) ? 0 : dash_pos + 1;
 
-    if (!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
+                        if (!dash_pattern[dash_pos])
+                            continue;
 
-    pt[0].X = x1;
-    pt[0].Y = y1;
-    pt[1].X = x2;
-    pt[1].Y = y2;
+                        if (x < 0 || x >= output_width || y < 0 || y >= output_height)
+                            continue;
 
-    save_state = prepare_dc(graphics, pen);
+                        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;
 
-    retval = draw_polyline(graphics, pen, pt, 2, TRUE);
+                    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;
+                    }
 
-    restore_dc(graphics, save_state);
+                    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;
 
-    return retval;
-}
+                        if (x == prev_x && y == prev_y)
+                            continue;
 
-GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
-    INT y1, INT x2, INT y2)
-{
-    INT save_state;
-    GpPointF pt[2];
-    GpStatus retval;
+                        prev_x = x;
+                        prev_y = y;
+                        dash_pos = (dash_pos + 1 == dash_pattern_size) ? 0 : dash_pos + 1;
 
-    TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
+                        if (!dash_pattern[dash_pos])
+                            continue;
 
-    if(!pen || !graphics)
-        return InvalidParameter;
+                        if (x < 0 || x >= output_width || y < 0 || y >= output_height)
+                            continue;
 
-    if(graphics->busy)
-        return ObjectBusy;
+                        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;
+                    }
+                }
+            }
+        }
 
-    if (!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
+        /* draw output image */
+        if (stat == Ok)
+        {
+            gdi_transform_acquire(graphics);
 
-    pt[0].X = (REAL)x1;
-    pt[0].Y = (REAL)y1;
-    pt[1].X = (REAL)x2;
-    pt[1].Y = (REAL)y2;
+            stat = alpha_blend_pixels(graphics, output_area.left, output_area.top,
+                (BYTE*)output_bits, output_width, output_height, output_width * 4,
+                PixelFormat32bppARGB);
 
-    save_state = prepare_dc(graphics, pen);
+            gdi_transform_release(graphics);
+        }
 
-    retval = draw_polyline(graphics, pen, pt, 2, TRUE);
+        heap_free(brush_bits);
+        heap_free(dyn_dash_pattern);
+        heap_free(output_bits);
+    }
 
-    restore_dc(graphics, save_state);
+    GdipDeletePath(flat_path);
 
-    return retval;
+    return stat;
 }
 
-GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
-    GpPointF *points, INT count)
+static GpStatus SOFTWARE_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
 {
-    INT save_state;
-    GpStatus retval;
+    GpStatus stat;
+    GpPath *wide_path;
+    GpMatrix *transform=NULL;
+    REAL flatness=1.0;
 
-    TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
+    /* 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}};
 
-    if(!pen || !graphics || (count < 2))
-        return InvalidParameter;
+        points[1].X = pen->width;
+        points[2].Y = pen->width;
 
-    if(graphics->busy)
-        return ObjectBusy;
+        stat = gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice,
+            CoordinateSpaceWorld, points, 3);
 
-    if (!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
+        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);
     }
 
-    save_state = prepare_dc(graphics, pen);
+    stat = GdipClonePath(path, &wide_path);
 
-    retval = draw_polyline(graphics, pen, points, count, TRUE);
+    if (stat != Ok)
+        return stat;
 
-    restore_dc(graphics, save_state);
+    if (pen->unit == UnitPixel)
+    {
+        /* We have to transform this to device coordinates to get the widths right. */
+        stat = GdipCreateMatrix(&transform);
 
-    return retval;
-}
+        if (stat == Ok)
+            stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
+                CoordinateSpaceWorld, transform);
+    }
+    else
+    {
+        /* Set flatness based on the final coordinate space */
+        GpMatrix t;
 
-GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
-    GpPoint *points, INT count)
-{
-    INT save_state;
-    GpStatus retval;
-    GpPointF *ptf = NULL;
-    int i;
+        stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
+            CoordinateSpaceWorld, &t);
 
-    TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
+        if (stat != Ok)
+            return stat;
 
-    if(!pen || !graphics || (count < 2))
-        return InvalidParameter;
+        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(graphics->busy)
-        return ObjectBusy;
+    if (stat == Ok)
+        stat = GdipWidenPath(wide_path, pen, transform, flatness);
 
-    if (!graphics->hdc)
+    if (pen->unit == UnitPixel)
     {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
-
-    ptf = GdipAlloc(count * sizeof(GpPointF));
-    if(!ptf) return OutOfMemory;
+        /* Transform the path back to world coordinates */
+        if (stat == Ok)
+            stat = GdipInvertMatrix(transform);
 
-    for(i = 0; i < count; i ++){
-        ptf[i].X = (REAL) points[i].X;
-        ptf[i].Y = (REAL) points[i].Y;
+        if (stat == Ok)
+            stat = GdipTransformPath(wide_path, transform);
     }
 
-    save_state = prepare_dc(graphics, pen);
+    /* Actually draw the path */
+    if (stat == Ok)
+        stat = GdipFillPath(graphics, pen->brush, wide_path);
 
-    retval = draw_polyline(graphics, pen, ptf, count, TRUE);
+    GdipDeleteMatrix(transform);
 
-    restore_dc(graphics, save_state);
+    GdipDeletePath(wide_path);
 
-    GdipFree(ptf);
-    return retval;
+    return stat;
 }
 
 GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
 {
-    INT save_state;
     GpStatus retval;
 
     TRACE("(%p, %p, %p)\n", graphics, pen, path);
@@ -2609,18 +4075,15 @@ GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
     if(graphics->busy)
         return ObjectBusy;
 
-    if (!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
+    if (path->pathdata.Count == 0)
         return Ok;
-    }
-
-    save_state = prepare_dc(graphics, pen);
 
-    retval = draw_poly(graphics, pen, path->pathdata.Points,
-                       path->pathdata.Types, path->pathdata.Count, TRUE);
-
-    restore_dc(graphics, save_state);
+    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;
 }
@@ -2628,7 +4091,8 @@ GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
 {
-    INT save_state;
+    GpStatus status;
+    GpPath *path;
 
     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
             width, height, startAngle, sweepAngle);
@@ -2639,20 +4103,15 @@ GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
     if(graphics->busy)
         return ObjectBusy;
 
-    if (!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
-
-    save_state = prepare_dc(graphics, pen);
-    SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
-
-    draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
+    status = GdipCreatePath(FillModeAlternate, &path);
+    if (status != Ok) return status;
 
-    restore_dc(graphics, save_state);
+    status = GdipAddPathPie(path, x, y, width, height, startAngle, sweepAngle);
+    if (status == Ok)
+        status = GdipDrawPath(graphics, pen, path);
 
-    return Ok;
+    GdipDeletePath(path);
+    return status;
 }
 
 GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x,
@@ -2667,42 +4126,26 @@ GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x,
 GpStatus WINGDIPAPI GdipDrawRectangle(GpGraphics *graphics, GpPen *pen, REAL x,
     REAL y, REAL width, REAL height)
 {
-    INT save_state;
-    GpPointF ptf[4];
-    POINT pti[4];
+    GpStatus status;
+    GpPath *path;
 
     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
 
     if(!pen || !graphics)
-        return InvalidParameter;
-
-    if(graphics->busy)
-        return ObjectBusy;
-
-    if (!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
-
-    ptf[0].X = x;
-    ptf[0].Y = y;
-    ptf[1].X = x + width;
-    ptf[1].Y = y;
-    ptf[2].X = x + width;
-    ptf[2].Y = y + height;
-    ptf[3].X = x;
-    ptf[3].Y = y + height;
+        return InvalidParameter;
 
-    save_state = prepare_dc(graphics, pen);
-    SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
+    if(graphics->busy)
+        return ObjectBusy;
 
-    transform_and_round_points(graphics, pti, ptf, 4);
-    Polygon(graphics->hdc, pti, 4);
+    status = GdipCreatePath(FillModeAlternate, &path);
+    if (status != Ok) return status;
 
-    restore_dc(graphics, save_state);
+    status = GdipAddPathRectangle(path, x, y, width, height);
+    if (status == Ok)
+        status = GdipDrawPath(graphics, pen, path);
 
-    return Ok;
+    GdipDeletePath(path);
+    return status;
 }
 
 GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
@@ -2716,9 +4159,8 @@ GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
 GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen,
     GDIPCONST GpRectF* rects, INT count)
 {
-    GpPointF *ptf;
-    POINT *pti;
-    INT save_state, i;
+    GpStatus status;
+    GpPath *path;
 
     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
 
@@ -2728,42 +4170,15 @@ GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen,
     if(graphics->busy)
         return ObjectBusy;
 
-    if (!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
-
-    ptf = GdipAlloc(4 * count * sizeof(GpPointF));
-    pti = GdipAlloc(4 * count * sizeof(POINT));
+    status = GdipCreatePath(FillModeAlternate, &path);
+    if (status != Ok) return status;
 
-    if(!ptf || !pti){
-        GdipFree(ptf);
-        GdipFree(pti);
-        return OutOfMemory;
-    }
-
-    for(i = 0; i < count; i++){
-        ptf[4 * i + 3].X = ptf[4 * i].X = rects[i].X;
-        ptf[4 * i + 1].Y = ptf[4 * i].Y = rects[i].Y;
-        ptf[4 * i + 2].X = ptf[4 * i + 1].X = rects[i].X + rects[i].Width;
-        ptf[4 * i + 3].Y = ptf[4 * i + 2].Y = rects[i].Y + rects[i].Height;
-    }
-
-    save_state = prepare_dc(graphics, pen);
-    SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
-
-    transform_and_round_points(graphics, pti, ptf, 4 * count);
+    status = GdipAddPathRectangles(path, rects, count);
+    if (status == Ok)
+        status = GdipDrawPath(graphics, pen, path);
 
-    for(i = 0; i < count; i++)
-        Polygon(graphics->hdc, &pti[4 * i], 4);
-
-    restore_dc(graphics, save_state);
-
-    GdipFree(ptf);
-    GdipFree(pti);
-
-    return Ok;
+    GdipDeletePath(path);
+    return status;
 }
 
 GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
@@ -2778,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;
 
@@ -2790,7 +4205,7 @@ GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
     }
 
     ret = GdipDrawRectangles(graphics, pen, rectsF, count);
-    GdipFree(rectsF);
+    heap_free(rectsF);
 
     return ret;
 }
@@ -2799,7 +4214,7 @@ GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
     GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
 {
     GpPath *path;
-    GpStatus stat;
+    GpStatus status;
 
     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
             count, tension, fill);
@@ -2813,25 +4228,15 @@ GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
     if(count == 1)    /* Do nothing */
         return Ok;
 
-    stat = GdipCreatePath(fill, &path);
-    if(stat != Ok)
-        return stat;
-
-    stat = GdipAddPathClosedCurve2(path, points, count, tension);
-    if(stat != Ok){
-        GdipDeletePath(path);
-        return stat;
-    }
+    status = GdipCreatePath(fill, &path);
+    if (status != Ok) return status;
 
-    stat = GdipFillPath(graphics, brush, path);
-    if(stat != Ok){
-        GdipDeletePath(path);
-        return stat;
-    }
+    status = GdipAddPathClosedCurve2(path, points, count, tension);
+    if (status == Ok)
+        status = GdipFillPath(graphics, brush, path);
 
     GdipDeletePath(path);
-
-    return Ok;
+    return status;
 }
 
 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
@@ -2850,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;
 
@@ -2861,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;
 }
@@ -2885,9 +4290,8 @@ GpStatus WINGDIPAPI GdipFillClosedCurveI(GpGraphics *graphics, GpBrush *brush,
 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
     REAL y, REAL width, REAL height)
 {
-    INT save_state;
-    GpPointF ptf[2];
-    POINT pti[2];
+    GpStatus stat;
+    GpPath *path;
 
     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
 
@@ -2897,45 +4301,96 @@ GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x
     if(graphics->busy)
         return ObjectBusy;
 
-    if(!graphics->hdc)
+    stat = GdipCreatePath(FillModeAlternate, &path);
+
+    if (stat == Ok)
     {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
+        stat = GdipAddPathEllipse(path, x, y, width, height);
+
+        if (stat == Ok)
+            stat = GdipFillPath(graphics, brush, path);
+
+        GdipDeletePath(path);
     }
 
-    ptf[0].X = x;
-    ptf[0].Y = y;
-    ptf[1].X = x + width;
-    ptf[1].Y = y + height;
+    return stat;
+}
+
+GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
+    INT y, INT width, INT height)
+{
+    TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
+
+    return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
+}
+
+static GpStatus GDI32_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
+{
+    INT save_state;
+    GpStatus retval;
+    HRGN hrgn=NULL;
+
+    if(!graphics->hdc || !brush_can_fill_path(brush, TRUE))
+        return NotImplemented;
 
     save_state = SaveDC(graphics->hdc);
     EndPath(graphics->hdc);
+    SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
+                                                                    : WINDING));
+
+    retval = get_clip_hrgn(graphics, &hrgn);
+
+    if (retval != Ok)
+        goto end;
 
-    transform_and_round_points(graphics, pti, ptf, 2);
+    ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
+
+    gdi_transform_acquire(graphics);
 
     BeginPath(graphics->hdc);
-    Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
-    EndPath(graphics->hdc);
+    retval = draw_poly(graphics, NULL, path->pathdata.Points,
+                       path->pathdata.Types, path->pathdata.Count, FALSE);
+
+    if(retval == Ok)
+    {
+        EndPath(graphics->hdc);
+        retval = brush_fill_path(graphics, brush);
+    }
 
-    brush_fill_path(graphics, brush);
+    gdi_transform_release(graphics);
 
+end:
     RestoreDC(graphics->hdc, save_state);
+    DeleteObject(hrgn);
 
-    return Ok;
+    return retval;
 }
 
-GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
-    INT y, INT width, INT height)
+static GpStatus SOFTWARE_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
 {
-    TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
+    GpStatus stat;
+    GpRegion *rgn;
 
-    return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
+    if (!brush_can_fill_pixels(brush))
+        return NotImplemented;
+
+    /* FIXME: This could probably be done more efficiently without regions. */
+
+    stat = GdipCreateRegionPath(path, &rgn);
+
+    if (stat == Ok)
+    {
+        stat = GdipFillRegion(graphics, brush, rgn);
+
+        GdipDeleteRegion(rgn);
+    }
+
+    return stat;
 }
 
 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
 {
-    INT save_state;
-    GpStatus retval;
+    GpStatus stat = NotImplemented;
 
     TRACE("(%p, %p, %p)\n", graphics, brush, path);
 
@@ -2945,39 +4400,32 @@ GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *p
     if(graphics->busy)
         return ObjectBusy;
 
-    if(!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
+    if (!path->pathdata.Count)
         return Ok;
-    }
-
-    save_state = SaveDC(graphics->hdc);
-    EndPath(graphics->hdc);
-    SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
-                                                                    : WINDING));
-
-    BeginPath(graphics->hdc);
-    retval = draw_poly(graphics, NULL, path->pathdata.Points,
-                       path->pathdata.Types, path->pathdata.Count, FALSE);
 
-    if(retval != Ok)
-        goto end;
+    if (graphics->image && graphics->image->type == ImageTypeMetafile)
+        return METAFILE_FillPath((GpMetafile*)graphics->image, brush, path);
 
-    EndPath(graphics->hdc);
-    brush_fill_path(graphics, brush);
+    if (!graphics->image && !graphics->alpha_hdc)
+        stat = GDI32_GdipFillPath(graphics, brush, path);
 
-    retval = Ok;
+    if (stat == NotImplemented)
+        stat = SOFTWARE_GdipFillPath(graphics, brush, path);
 
-end:
-    RestoreDC(graphics->hdc, save_state);
+    if (stat == NotImplemented)
+    {
+        FIXME("Not implemented for brushtype %i\n", brush->bt);
+        stat = Ok;
+    }
 
-    return retval;
+    return stat;
 }
 
 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
 {
-    INT save_state;
+    GpStatus stat;
+    GpPath *path;
 
     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
             graphics, brush, x, y, width, height, startAngle, sweepAngle);
@@ -2988,24 +4436,19 @@ GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
     if(graphics->busy)
         return ObjectBusy;
 
-    if(!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
-
-    save_state = SaveDC(graphics->hdc);
-    EndPath(graphics->hdc);
+    stat = GdipCreatePath(FillModeAlternate, &path);
 
-    BeginPath(graphics->hdc);
-    draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
-    EndPath(graphics->hdc);
+    if (stat == Ok)
+    {
+        stat = GdipAddPathPie(path, x, y, width, height, startAngle, sweepAngle);
 
-    brush_fill_path(graphics, brush);
+        if (stat == Ok)
+            stat = GdipFillPath(graphics, brush, path);
 
-    RestoreDC(graphics->hdc, save_state);
+        GdipDeletePath(path);
+    }
 
-    return Ok;
+    return stat;
 }
 
 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
@@ -3020,10 +4463,8 @@ GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
     GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
 {
-    INT save_state;
-    GpPointF *ptf = NULL;
-    POINT *pti = NULL;
-    GpStatus retval = Ok;
+    GpStatus stat;
+    GpPath *path;
 
     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
 
@@ -3033,50 +4474,26 @@ GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
     if(graphics->busy)
         return ObjectBusy;
 
-    if(!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
-
-    ptf = GdipAlloc(count * sizeof(GpPointF));
-    pti = GdipAlloc(count * sizeof(POINT));
-    if(!ptf || !pti){
-        retval = OutOfMemory;
-        goto end;
-    }
-
-    memcpy(ptf, points, count * sizeof(GpPointF));
-
-    save_state = SaveDC(graphics->hdc);
-    EndPath(graphics->hdc);
-    SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
-                                                                  : WINDING));
-
-    transform_and_round_points(graphics, pti, ptf, count);
-
-    BeginPath(graphics->hdc);
-    Polygon(graphics->hdc, pti, count);
-    EndPath(graphics->hdc);
+    stat = GdipCreatePath(fillMode, &path);
 
-    brush_fill_path(graphics, brush);
+    if (stat == Ok)
+    {
+        stat = GdipAddPathPolygon(path, points, count);
 
-    RestoreDC(graphics->hdc, save_state);
+        if (stat == Ok)
+            stat = GdipFillPath(graphics, brush, path);
 
-end:
-    GdipFree(ptf);
-    GdipFree(pti);
+        GdipDeletePath(path);
+    }
 
-    return retval;
+    return stat;
 }
 
 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
     GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
 {
-    INT save_state, i;
-    GpPointF *ptf = NULL;
-    POINT *pti = NULL;
-    GpStatus retval = Ok;
+    GpStatus stat;
+    GpPath *path;
 
     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
 
@@ -3086,44 +4503,19 @@ GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
     if(graphics->busy)
         return ObjectBusy;
 
-    if(!graphics->hdc)
+    stat = GdipCreatePath(fillMode, &path);
+
+    if (stat == Ok)
     {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
+        stat = GdipAddPathPolygonI(path, points, count);
 
-    ptf = GdipAlloc(count * sizeof(GpPointF));
-    pti = GdipAlloc(count * sizeof(POINT));
-    if(!ptf || !pti){
-        retval = OutOfMemory;
-        goto end;
-    }
+        if (stat == Ok)
+            stat = GdipFillPath(graphics, brush, path);
 
-    for(i = 0; i < count; i ++){
-        ptf[i].X = (REAL) points[i].X;
-        ptf[i].Y = (REAL) points[i].Y;
+        GdipDeletePath(path);
     }
 
-    save_state = SaveDC(graphics->hdc);
-    EndPath(graphics->hdc);
-    SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
-                                                                  : WINDING));
-
-    transform_and_round_points(graphics, pti, ptf, count);
-
-    BeginPath(graphics->hdc);
-    Polygon(graphics->hdc, pti, count);
-    EndPath(graphics->hdc);
-
-    brush_fill_path(graphics, brush);
-
-    RestoreDC(graphics->hdc, save_state);
-
-end:
-    GdipFree(ptf);
-    GdipFree(pti);
-
-    return retval;
+    return stat;
 }
 
 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
@@ -3145,112 +4537,60 @@ GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
     REAL x, REAL y, REAL width, REAL height)
 {
-    INT save_state;
-    GpPointF ptf[4];
-    POINT pti[4];
+    GpRectF rect;
 
     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
 
-    if(!graphics || !brush)
-        return InvalidParameter;
-
-    if(graphics->busy)
-        return ObjectBusy;
-
-    if(!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
-
-    ptf[0].X = x;
-    ptf[0].Y = y;
-    ptf[1].X = x + width;
-    ptf[1].Y = y;
-    ptf[2].X = x + width;
-    ptf[2].Y = y + height;
-    ptf[3].X = x;
-    ptf[3].Y = y + height;
-
-    save_state = SaveDC(graphics->hdc);
-    EndPath(graphics->hdc);
-
-    transform_and_round_points(graphics, pti, ptf, 4);
-
-    BeginPath(graphics->hdc);
-    Polygon(graphics->hdc, pti, 4);
-    EndPath(graphics->hdc);
-
-    brush_fill_path(graphics, brush);
-
-    RestoreDC(graphics->hdc, save_state);
+    rect.X = x;
+    rect.Y = y;
+    rect.Width = width;
+    rect.Height = height;
 
-    return Ok;
+    return GdipFillRectangles(graphics, brush, &rect, 1);
 }
 
 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
     INT x, INT y, INT width, INT height)
 {
-    INT save_state;
-    GpPointF ptf[4];
-    POINT pti[4];
+    GpRectF rect;
 
     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
 
-    if(!graphics || !brush)
-        return InvalidParameter;
-
-    if(graphics->busy)
-        return ObjectBusy;
-
-    if(!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
-
-    ptf[0].X = x;
-    ptf[0].Y = y;
-    ptf[1].X = x + width;
-    ptf[1].Y = y;
-    ptf[2].X = x + width;
-    ptf[2].Y = y + height;
-    ptf[3].X = x;
-    ptf[3].Y = y + height;
-
-    save_state = SaveDC(graphics->hdc);
-    EndPath(graphics->hdc);
-
-    transform_and_round_points(graphics, pti, ptf, 4);
-
-    BeginPath(graphics->hdc);
-    Polygon(graphics->hdc, pti, 4);
-    EndPath(graphics->hdc);
-
-    brush_fill_path(graphics, brush);
-
-    RestoreDC(graphics->hdc, save_state);
+    rect.X = (REAL)x;
+    rect.Y = (REAL)y;
+    rect.Width = (REAL)width;
+    rect.Height = (REAL)height;
 
-    return Ok;
+    return GdipFillRectangles(graphics, brush, &rect, 1);
 }
 
 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
     INT count)
 {
-    GpStatus ret;
-    INT i;
+    GpStatus status;
+    GpPath *path;
 
     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
 
-    if(!rects)
+    if(!graphics || !brush || !rects || count <= 0)
         return InvalidParameter;
 
-    for(i = 0; i < count; i++){
-        ret = GdipFillRectangle(graphics, brush, rects[i].X, rects[i].Y, rects[i].Width, rects[i].Height);
-        if(ret != Ok)   return ret;
+    if (graphics->image && graphics->image->type == ImageTypeMetafile)
+    {
+        status = METAFILE_FillRectangles((GpMetafile*)graphics->image, brush, rects, count);
+        /* FIXME: Add gdi32 drawing. */
+        return status;
     }
 
-    return Ok;
+    status = GdipCreatePath(FillModeAlternate, &path);
+    if (status != Ok) return status;
+
+    status = GdipAddPathRectangles(path, rects, count);
+    if (status == Ok)
+        status = GdipFillPath(graphics, brush, path);
+
+    GdipDeletePath(path);
+    return status;
 }
 
 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
@@ -3265,56 +4605,57 @@ 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;
 }
 
-/*****************************************************************************
- * GdipFillRegion [GDIPLUS.@]
- */
-GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
-        GpRegion* region)
+static GpStatus GDI32_GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
+    GpRegion* region)
 {
     INT save_state;
     GpStatus status;
     HRGN hrgn;
     RECT rc;
 
-    TRACE("(%p, %p, %p)\n", graphics, brush, region);
-
-    if (!(graphics && brush && region))
-        return InvalidParameter;
+    if(!graphics->hdc || !brush_can_fill_path(brush, TRUE))
+        return NotImplemented;
 
-    if(graphics->busy)
-        return ObjectBusy;
+    save_state = SaveDC(graphics->hdc);
+    EndPath(graphics->hdc);
 
-    if(!graphics->hdc)
+    hrgn = NULL;
+    status = get_clip_hrgn(graphics, &hrgn);
+    if (status != Ok)
     {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
+        RestoreDC(graphics->hdc, save_state);
+        return status;
     }
 
+    ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
+    DeleteObject(hrgn);
+
     status = GdipGetRegionHRgn(region, graphics, &hrgn);
-    if(status != Ok)
+    if (status != Ok)
+    {
+        RestoreDC(graphics->hdc, save_state);
         return status;
-
-    save_state = SaveDC(graphics->hdc);
-    EndPath(graphics->hdc);
+    }
 
     ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
+    DeleteObject(hrgn);
 
     if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
     {
@@ -3322,14 +4663,124 @@ GpStatus WINGDIPAPI 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,
+    GpRegion* region)
+{
+    GpStatus stat;
+    GpRegion *temp_region;
+    GpMatrix world_to_device;
+    GpRectF graphics_bounds;
+    DWORD *pixel_data;
+    HRGN hregion;
+    RECT bound_rect;
+    GpRect gp_bound_rect;
+
+    if (!brush_can_fill_pixels(brush))
+        return NotImplemented;
+
+    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, WineCoordinateSpaceGdiDevice,
+            CoordinateSpaceWorld, &world_to_device);
+
+        if (stat == Ok)
+            stat = GdipTransformRegion(temp_region, &world_to_device);
+
+        if (stat == Ok)
+            stat = GdipCombineRegionRect(temp_region, &graphics_bounds, CombineModeIntersect);
+
+        if (stat == Ok)
+            stat = GdipGetRegionHRgn(temp_region, NULL, &hregion);
+
+        GdipDeleteRegion(temp_region);
+    }
+
+    if (stat == Ok && GetRgnBox(hregion, &bound_rect) == NULLREGION)
+    {
+        DeleteObject(hregion);
+        gdi_transform_release(graphics);
+        return Ok;
+    }
+
+    if (stat == Ok)
+    {
+        gp_bound_rect.X = bound_rect.left;
+        gp_bound_rect.Y = bound_rect.top;
+        gp_bound_rect.Width = bound_rect.right - bound_rect.left;
+        gp_bound_rect.Height = bound_rect.bottom - bound_rect.top;
+
+        pixel_data = heap_alloc_zero(sizeof(*pixel_data) * gp_bound_rect.Width * gp_bound_rect.Height);
+        if (!pixel_data)
+            stat = OutOfMemory;
+
+        if (stat == Ok)
+        {
+            stat = brush_fill_pixels(graphics, brush, pixel_data,
+                &gp_bound_rect, gp_bound_rect.Width);
+
+            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,
+                    PixelFormat32bppARGB);
+
+            heap_free(pixel_data);
+        }
+
+        DeleteObject(hregion);
+    }
+
+    gdi_transform_release(graphics);
+
+    return stat;
+}
+
+/*****************************************************************************
+ * GdipFillRegion [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
+        GpRegion* region)
+{
+    GpStatus stat = NotImplemented;
+
+    TRACE("(%p, %p, %p)\n", graphics, brush, region);
+
+    if (!(graphics && brush && region))
+        return InvalidParameter;
+
+    if(graphics->busy)
+        return ObjectBusy;
+
+    if (!graphics->image && !graphics->alpha_hdc)
+        stat = GDI32_GdipFillRegion(graphics, brush, region);
+
+    if (stat == NotImplemented)
+        stat = SOFTWARE_GdipFillRegion(graphics, brush, region);
+
+    if (stat == NotImplemented)
+    {
+        FIXME("not implemented for brushtype %i\n", brush->bt);
+        stat = Ok;
+    }
+
+    return stat;
 }
 
 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
@@ -3355,6 +4806,9 @@ GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
  */
 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
 {
+    GpStatus status;
+    GpRegion *clip;
+
     TRACE("(%p, %p)\n", graphics, rect);
 
     if(!graphics)
@@ -3363,7 +4817,15 @@ GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
     if(graphics->busy)
         return ObjectBusy;
 
-    return GdipGetRegionBounds(graphics->clip, graphics, rect);
+    status = GdipCreateRegion(&clip);
+    if (status != Ok) return status;
+
+    status = GdipGetClip(graphics, clip);
+    if (status == Ok)
+        status = GdipGetRegionBounds(clip, graphics, rect);
+
+    GdipDeleteRegion(clip);
+    return status;
 }
 
 /*****************************************************************************
@@ -3543,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);
 
@@ -3559,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);
 
@@ -3580,10 +5050,10 @@ GpStatus WINGDIPAPI GdipGetVisibleClipBoundsI(GpGraphics *graphics, GpRect *rect
 
     if((stat = GdipGetVisibleClipBounds(graphics, &rectf)) == Ok)
     {
-        rect->X = roundr(rectf.X);
-        rect->Y = roundr(rectf.Y);
-        rect->Width  = roundr(rectf.Width);
-        rect->Height = roundr(rectf.Height);
+        rect->X = gdip_round(rectf.X);
+        rect->Y = gdip_round(rectf.Y);
+        rect->Width  = gdip_round(rectf.Width);
+        rect->Height = gdip_round(rectf.Height);
     }
 
     return stat;
@@ -3599,7 +5069,7 @@ GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix
     if(graphics->busy)
         return ObjectBusy;
 
-    *matrix = *graphics->worldtrans;
+    *matrix = graphics->worldtrans;
     return Ok;
 }
 
@@ -3617,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;
     }
@@ -3725,14 +5198,9 @@ GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT w
     return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result);
 }
 
-typedef GpStatus (*gdip_format_string_callback)(GpGraphics *graphics,
-    GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
-    GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
-    INT lineno, const RectF *bounds, void *user_data);
-
-static GpStatus gdip_format_string(GpGraphics *graphics,
+GpStatus gdip_format_string(HDC hdc,
     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
-    GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
+    GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip,
     gdip_format_string_callback callback, void *user_data)
 {
     WCHAR* stringdup;
@@ -3742,34 +5210,73 @@ static GpStatus gdip_format_string(GpGraphics *graphics,
     StringAlignment halign;
     GpStatus stat = Ok;
     SIZE size;
+    HotkeyPrefix hkprefix;
+    INT *hotkeyprefix_offsets=NULL;
+    INT hotkeyprefix_count=0;
+    INT hotkeyprefix_pos=0, hotkeyprefix_end_pos=0;
+    BOOL seen_prefix = FALSE;
 
     if(length == -1) length = lstrlenW(string);
 
-    stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
+    stringdup = heap_alloc_zero((length + 1) * sizeof(WCHAR));
     if(!stringdup) return OutOfMemory;
 
-    nwidth = roundr(rect->Width);
-    nheight = roundr(rect->Height);
+    if (!format)
+        format = &default_drawstring_format;
+
+    nwidth = rect->Width;
+    nheight = rect->Height;
+    if (ignore_empty_clip)
+    {
+        if (!nwidth) nwidth = INT_MAX;
+        if (!nheight) nheight = INT_MAX;
+    }
+
+    hkprefix = format->hkprefix;
+
+    if (hkprefix == HotkeyPrefixShow)
+    {
+        for (i=0; i<length; i++)
+        {
+            if (string[i] == '&')
+                hotkeyprefix_count++;
+        }
+    }
+
+    if (hotkeyprefix_count)
+        hotkeyprefix_offsets = heap_alloc_zero(sizeof(INT) * hotkeyprefix_count);
 
-    if (rect->Width >= INT_MAX || rect->Width < 0.5) nwidth = INT_MAX;
-    if (rect->Height >= INT_MAX || rect->Width < 0.5) nheight = INT_MAX;
+    hotkeyprefix_count = 0;
 
     for(i = 0, j = 0; i < length; i++){
         /* FIXME: This makes the indexes passed to callback inaccurate. */
         if(!isprintW(string[i]) && (string[i] != '\n'))
             continue;
 
+        /* FIXME: tabs should be handled using tabstops from stringformat */
+        if (string[i] == '\t')
+            continue;
+
+        if (seen_prefix && hkprefix == HotkeyPrefixShow && string[i] != '&')
+            hotkeyprefix_offsets[hotkeyprefix_count++] = j;
+        else if (!seen_prefix && hkprefix != HotkeyPrefixNone && string[i] == '&')
+        {
+            seen_prefix = TRUE;
+            continue;
+        }
+
+        seen_prefix = FALSE;
+
         stringdup[j] = string[i];
         j++;
     }
 
     length = j;
 
-    if (format) halign = format->align;
-    else halign = StringAlignmentNear;
+    halign = format->align;
 
     while(sum < length){
-        GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
+        GetTextExtentExPointW(hdc, stringdup + sum, length - sum,
                               nwidth, &fit, NULL, &size);
         fitcpy = fit;
 
@@ -3807,13 +5314,17 @@ static GpStatus gdip_format_string(GpGraphics *graphics,
         else
             lineend = fit;
 
-        GetTextExtentExPointW(graphics->hdc, stringdup + sum, lineend,
+        GetTextExtentExPointW(hdc, stringdup + sum, lineend,
                               nwidth, &j, NULL, &size);
 
         bounds.Width = size.cx;
 
         if(height + size.cy > nheight)
+        {
+            if (format->attr & StringFormatFlagsLineLimit)
+                break;
             bounds.Height = nheight - (height + size.cy);
+        }
         else
             bounds.Height = size.cy;
 
@@ -3833,8 +5344,14 @@ static GpStatus gdip_format_string(GpGraphics *graphics,
             break;
         }
 
-        stat = callback(graphics, stringdup, sum, lineend,
-            font, rect, format, lineno, &bounds, user_data);
+        for (hotkeyprefix_end_pos=hotkeyprefix_pos; hotkeyprefix_end_pos<hotkeyprefix_count; hotkeyprefix_end_pos++)
+            if (hotkeyprefix_offsets[hotkeyprefix_end_pos] >= sum + lineend)
+                break;
+
+        stat = callback(hdc, stringdup, sum, lineend,
+            font, rect, format, lineno, &bounds,
+            &hotkeyprefix_offsets[hotkeyprefix_pos],
+            hotkeyprefix_end_pos-hotkeyprefix_pos, user_data);
 
         if (stat != Ok)
             break;
@@ -3843,27 +5360,32 @@ static GpStatus gdip_format_string(GpGraphics *graphics,
         height += size.cy;
         lineno++;
 
+        hotkeyprefix_pos = hotkeyprefix_end_pos;
+
         if(height > nheight)
             break;
 
         /* Stop if this was a linewrap (but not if it was a linebreak). */
-        if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
+        if ((lret == fitcpy) && (format->attr & StringFormatFlagsNoWrap))
             break;
     }
 
-    GdipFree(stringdup);
+    heap_free(stringdup);
+    heap_free(hotkeyprefix_offsets);
 
     return stat;
 }
 
 struct measure_ranges_args {
     GpRegion **regions;
+    REAL rel_width, rel_height;
 };
 
-static GpStatus measure_ranges_callback(GpGraphics *graphics,
+static GpStatus measure_ranges_callback(HDC hdc,
     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
-    INT lineno, const RectF *bounds, void *user_data)
+    INT lineno, const RectF *bounds, INT *underlined_indexes,
+    INT underlined_index_count, void *user_data)
 {
     int i;
     GpStatus stat = Ok;
@@ -3878,16 +5400,16 @@ static GpStatus measure_ranges_callback(GpGraphics *graphics,
             GpRectF range_rect;
             SIZE range_size;
 
-            range_rect.Y = bounds->Y;
-            range_rect.Height = bounds->Height;
+            range_rect.Y = bounds->Y / args->rel_height;
+            range_rect.Height = bounds->Height / args->rel_height;
 
-            GetTextExtentExPointW(graphics->hdc, string + index, range_start - index,
+            GetTextExtentExPointW(hdc, string + index, range_start - index,
                                   INT_MAX, NULL, NULL, &range_size);
-            range_rect.X = bounds->X + range_size.cx;
+            range_rect.X = (bounds->X + range_size.cx) / args->rel_width;
 
-            GetTextExtentExPointW(graphics->hdc, string + index, range_end - index,
+            GetTextExtentExPointW(hdc, string + index, range_end - index,
                                   INT_MAX, NULL, NULL, &range_size);
-            range_rect.Width = (bounds->X + range_size.cx) - range_rect.X;
+            range_rect.Width = (bounds->X + range_size.cx) / args->rel_width - range_rect.X;
 
             stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion);
             if (stat != Ok)
@@ -3905,11 +5427,14 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
 {
     GpStatus stat;
     int i;
-    HFONT oldfont;
+    HFONT gdifont, oldfont;
     struct measure_ranges_args args;
-    HDC temp_hdc=NULL;
+    HDC hdc, temp_hdc=NULL;
+    GpPointF pt[3];
+    RectF scaled_rect;
+    REAL margin_x;
 
-    TRACE("(%p %s %d %p %s %p %d %p)\n", graphics, debugstr_w(string),
+    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))
@@ -3920,14 +5445,40 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
 
     if(!graphics->hdc)
     {
-        temp_hdc = graphics->hdc = CreateCompatibleDC(0);
+        hdc = temp_hdc = CreateCompatibleDC(0);
         if (!temp_hdc) return OutOfMemory;
     }
+    else
+        hdc = graphics->hdc;
 
     if (stringFormat->attr)
         TRACE("may be ignoring some format flags: attr %x\n", stringFormat->attr);
 
-    oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
+    pt[0].X = 0.0;
+    pt[0].Y = 0.0;
+    pt[1].X = 1.0;
+    pt[1].Y = 0.0;
+    pt[2].X = 0.0;
+    pt[2].Y = 1.0;
+    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)+
+                      (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
+
+    margin_x = stringFormat->generic_typographic ? 0.0 : font->emSize / 6.0;
+    margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
+
+    scaled_rect.X = (layoutRect->X + margin_x) * args.rel_width;
+    scaled_rect.Y = layoutRect->Y * args.rel_height;
+    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);
 
     for (i=0; i<stringFormat->range_count; i++)
     {
@@ -3938,16 +5489,18 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
 
     args.regions = regions;
 
-    stat = gdip_format_string(graphics, string, length, font, layoutRect, stringFormat,
-        measure_ranges_callback, &args);
+    gdi_transform_acquire(graphics);
+
+    stat = gdip_format_string(hdc, string, length, font, &scaled_rect, stringFormat,
+        (stringFormat->attr & StringFormatFlagsNoClip) != 0, measure_ranges_callback, &args);
 
-    DeleteObject(SelectObject(graphics->hdc, oldfont));
+    gdi_transform_release(graphics);
+
+    SelectObject(hdc, oldfont);
+    DeleteObject(gdifont);
 
     if (temp_hdc)
-    {
-        graphics->hdc = NULL;
         DeleteDC(temp_hdc);
-    }
 
     return stat;
 }
@@ -3956,20 +5509,26 @@ struct measure_string_args {
     RectF *bounds;
     INT *codepointsfitted;
     INT *linesfilled;
+    REAL rel_width, rel_height;
 };
 
-static GpStatus measure_string_callback(GpGraphics *graphics,
+static GpStatus measure_string_callback(HDC hdc,
     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
-    INT lineno, const RectF *bounds, void *user_data)
+    INT lineno, const RectF *bounds, INT *underlined_indexes,
+    INT underlined_index_count, void *user_data)
 {
     struct measure_string_args *args = user_data;
+    REAL new_width, new_height;
 
-    if (bounds->Width > args->bounds->Width)
-        args->bounds->Width = bounds->Width;
+    new_width = bounds->Width / args->rel_width;
+    new_height = (bounds->Height + bounds->Y) / args->rel_height - args->bounds->Y;
 
-    if (bounds->Height + bounds->Y > args->bounds->Height + args->bounds->Y)
-        args->bounds->Height = bounds->Height + bounds->Y - args->bounds->Y;
+    if (new_width > args->bounds->Width)
+        args->bounds->Width = new_width;
+
+    if (new_height > args->bounds->Height)
+        args->bounds->Height = new_height;
 
     if (args->codepointsfitted)
         *args->codepointsfitted = index + length;
@@ -3989,9 +5548,13 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
     INT *codepointsfitted, INT *linesfilled)
 {
-    HFONT oldfont;
+    HFONT oldfont, gdifont;
     struct measure_string_args args;
-    HDC temp_hdc=NULL;
+    HDC temp_hdc=NULL, hdc;
+    GpPointF pt[3];
+    RectF scaled_rect;
+    REAL margin_x;
+    INT lines, glyphs;
 
     TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics,
         debugstr_wn(string, length), length, font, debugstr_rectf(rect), format,
@@ -4002,9 +5565,11 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
 
     if(!graphics->hdc)
     {
-        temp_hdc = graphics->hdc = CreateCompatibleDC(0);
+        hdc = temp_hdc = CreateCompatibleDC(0);
         if (!temp_hdc) return OutOfMemory;
     }
+    else
+        hdc = graphics->hdc;
 
     if(linesfilled) *linesfilled = 0;
     if(codepointsfitted) *codepointsfitted = 0;
@@ -4012,7 +5577,36 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
     if(format)
         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
 
-    oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
+    pt[0].X = 0.0;
+    pt[0].Y = 0.0;
+    pt[1].X = 1.0;
+    pt[1].Y = 0.0;
+    pt[2].X = 0.0;
+    pt[2].Y = 1.0;
+    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)+
+                      (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
+
+    margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0;
+    margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
+
+    scaled_rect.X = (rect->X + margin_x) * args.rel_width;
+    scaled_rect.Y = rect->Y * args.rel_height;
+    scaled_rect.Width = rect->Width * args.rel_width;
+    scaled_rect.Height = rect->Height * args.rel_height;
+    if (scaled_rect.Width >= 0.5)
+    {
+        scaled_rect.Width -= margin_x * 2.0 * args.rel_width;
+        if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */
+    }
+
+    if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23;
+    if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23;
+
+    get_font_hfont(graphics, font, format, &gdifont, NULL);
+    oldfont = SelectObject(hdc, gdifont);
 
     bounds->X = rect->X;
     bounds->Y = rect->Y;
@@ -4020,43 +5614,83 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
     bounds->Height = 0.0;
 
     args.bounds = bounds;
-    args.codepointsfitted = codepointsfitted;
-    args.linesfilled = linesfilled;
+    args.codepointsfitted = &glyphs;
+    args.linesfilled = &lines;
+    lines = glyphs = 0;
+
+    gdi_transform_acquire(graphics);
 
-    gdip_format_string(graphics, string, length, font, rect, format,
+    gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE,
         measure_string_callback, &args);
 
-    DeleteObject(SelectObject(graphics->hdc, oldfont));
+    gdi_transform_release(graphics);
+
+    if (linesfilled) *linesfilled = lines;
+    if (codepointsfitted) *codepointsfitted = glyphs;
+
+    if (lines)
+        bounds->Width += margin_x * 2.0;
+
+    SelectObject(hdc, oldfont);
+    DeleteObject(gdifont);
 
     if (temp_hdc)
-    {
-        graphics->hdc = NULL;
         DeleteDC(temp_hdc);
-    }
 
     return Ok;
 }
 
 struct draw_string_args {
-    POINT drawbase;
-    UINT drawflags;
-    REAL ang_cos, ang_sin;
+    GpGraphics *graphics;
+    GDIPCONST GpBrush *brush;
+    REAL x, y, rel_width, rel_height, ascent;
 };
 
-static GpStatus draw_string_callback(GpGraphics *graphics,
+static GpStatus draw_string_callback(HDC hdc,
     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
-    INT lineno, const RectF *bounds, void *user_data)
+    INT lineno, const RectF *bounds, INT *underlined_indexes,
+    INT underlined_index_count, void *user_data)
 {
     struct draw_string_args *args = user_data;
-    RECT drawcoord;
+    PointF position;
+    GpStatus stat;
 
-    drawcoord.left = drawcoord.right = args->drawbase.x + roundr(args->ang_sin * bounds->Y);
-    drawcoord.top = drawcoord.bottom = args->drawbase.y + roundr(args->ang_cos * bounds->Y);
+    position.X = args->x + bounds->X / args->rel_width;
+    position.Y = args->y + bounds->Y / args->rel_height + args->ascent;
 
-    DrawTextW(graphics->hdc, string + index, length, &drawcoord, args->drawflags);
+    stat = draw_driver_string(args->graphics, &string[index], length, font, format,
+        args->brush, &position,
+        DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL);
 
-    return Ok;
+    if (stat == Ok && underlined_index_count)
+    {
+        OUTLINETEXTMETRICW otm;
+        REAL underline_y, underline_height;
+        int i;
+
+        GetOutlineTextMetricsW(hdc, sizeof(otm), &otm);
+
+        underline_height = otm.otmsUnderscoreSize / args->rel_height;
+        underline_y = position.Y - otm.otmsUnderscorePosition / args->rel_height - underline_height / 2;
+
+        for (i=0; i<underlined_index_count; i++)
+        {
+            REAL start_x, end_x;
+            SIZE text_size;
+            INT ofs = underlined_indexes[i] - index;
+
+            GetTextExtentExPointW(hdc, string + index, ofs, INT_MAX, NULL, NULL, &text_size);
+            start_x = text_size.cx / args->rel_width;
+
+            GetTextExtentExPointW(hdc, string + index, ofs+1, INT_MAX, NULL, NULL, &text_size);
+            end_x = text_size.cx / args->rel_width;
+
+            GdipFillRectangle(args->graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height);
+        }
+    }
+
+    return stat;
 }
 
 GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
@@ -4065,14 +5699,15 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string
 {
     HRGN rgn = NULL;
     HFONT gdifont;
-    LOGFONTW lfw;
-    TEXTMETRICW textmet;
     GpPointF pt[3], rectcpy[4];
     POINT corners[4];
-    REAL angle, rel_width, rel_height;
-    INT offsety = 0, save_state;
+    REAL rel_width, rel_height, margin_x;
+    INT save_state, format_flags = 0;
+    REAL offsety = 0.0;
     struct draw_string_args args;
     RectF scaled_rect;
+    HDC hdc, temp_hdc=NULL;
+    TEXTMETRICW textmetric;
 
     TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length),
         length, font, debugstr_rectf(rect), format, brush);
@@ -4080,36 +5715,38 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string
     if(!graphics || !string || !font || !brush || !rect)
         return InvalidParameter;
 
-    if((brush->bt != BrushTypeSolidColor)){
-        FIXME("not implemented for given parameters\n");
-        return NotImplemented;
+    if(graphics->hdc)
+    {
+        hdc = graphics->hdc;
     }
-
-    if(!graphics->hdc)
+    else
     {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
+        hdc = temp_hdc = CreateCompatibleDC(0);
     }
 
     if(format){
         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
 
+        format_flags = format->attr;
+
         /* Should be no need to explicitly test for StringAlignmentNear as
          * that is default behavior if no alignment is passed. */
-        if(format->vertalign != StringAlignmentNear){
-            RectF bounds;
-            GdipMeasureString(graphics, string, length, font, rect, format, &bounds, 0, 0);
+        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);
 
-            if(format->vertalign == StringAlignmentCenter)
+            TRACE("bounds %s\n", debugstr_rectf(&bounds));
+
+            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("line align %d, offsety %f\n", format->line_align, offsety);
     }
 
-    save_state = SaveDC(graphics->hdc);
-    SetBkMode(graphics->hdc, TRANSPARENT);
-    SetTextColor(graphics->hdc, brush->lb.lbColor);
+    save_state = SaveDC(hdc);
 
     pt[0].X = 0.0;
     pt[0].Y = 0.0;
@@ -4117,73 +5754,72 @@ 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);
-    angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
-    args.ang_cos = cos(angle);
-    args.ang_sin = sin(angle);
+    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)+
                       (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
 
     rectcpy[3].X = rectcpy[0].X = rect->X;
-    rectcpy[1].Y = rectcpy[0].Y = rect->Y + offsety;
+    rectcpy[1].Y = rectcpy[0].Y = rect->Y;
     rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
-    rectcpy[3].Y = rectcpy[2].Y = rect->Y + offsety + rect->Height;
-    transform_and_round_points(graphics, corners, rectcpy, 4);
+    rectcpy[3].Y = rectcpy[2].Y = rect->Y + rect->Height;
+    gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, rectcpy, 4);
+    round_points(corners, rectcpy, 4);
 
-    scaled_rect.X = 0.0;
+    margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0;
+    margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
+
+    scaled_rect.X = margin_x * rel_width;
     scaled_rect.Y = 0.0;
     scaled_rect.Width = rel_width * rect->Width;
     scaled_rect.Height = rel_height * rect->Height;
+    if (scaled_rect.Width >= 0.5)
+    {
+        scaled_rect.Width -= margin_x * 2.0 * rel_width;
+        if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */
+    }
+
+    if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23;
+    if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23;
 
-    if (roundr(scaled_rect.Width) != 0 && roundr(scaled_rect.Height) != 0)
+    if (!(format_flags & StringFormatFlagsNoClip) &&
+        scaled_rect.Width != 1 << 23 && scaled_rect.Height != 1 << 23 &&
+        rect->Width > 0.0 && rect->Height > 0.0)
     {
         /* FIXME: If only the width or only the height is 0, we should probably still clip */
         rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
-        SelectClipRgn(graphics->hdc, rgn);
+        SelectClipRgn(hdc, rgn);
     }
 
-    /* Use gdi to find the font, then perform transformations on it (height,
-     * width, angle). */
-    SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
-    GetTextMetricsW(graphics->hdc, &textmet);
-    lfw = font->lfw;
+    get_font_hfont(graphics, font, format, &gdifont, NULL);
+    SelectObject(hdc, gdifont);
 
-    lfw.lfHeight = roundr(((REAL)lfw.lfHeight) * rel_height);
-    lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width);
+    args.graphics = graphics;
+    args.brush = brush;
 
-    lfw.lfEscapement = lfw.lfOrientation = roundr((angle / M_PI) * 1800.0);
+    args.x = rect->X;
+    args.y = rect->Y + offsety;
 
-    gdifont = CreateFontIndirectW(&lfw);
-    DeleteObject(SelectObject(graphics->hdc, CreateFontIndirectW(&lfw)));
+    args.rel_width = rel_width;
+    args.rel_height = rel_height;
 
-    if (!format || format->align == StringAlignmentNear)
-    {
-        args.drawbase.x = corners[0].x;
-        args.drawbase.y = corners[0].y;
-        args.drawflags = DT_NOCLIP | DT_EXPANDTABS;
-    }
-    else if (format->align == StringAlignmentCenter)
-    {
-        args.drawbase.x = (corners[0].x + corners[1].x)/2;
-        args.drawbase.y = (corners[0].y + corners[1].y)/2;
-        args.drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_CENTER;
-    }
-    else /* (format->align == StringAlignmentFar) */
-    {
-        args.drawbase.x = corners[1].x;
-        args.drawbase.y = corners[1].y;
-        args.drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_RIGHT;
-    }
+    gdi_transform_acquire(graphics);
 
-    gdip_format_string(graphics, string, length, font, &scaled_rect, format,
+    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);
 
-    RestoreDC(graphics->hdc, save_state);
+    RestoreDC(hdc, save_state);
+
+    DeleteDC(temp_hdc);
 
     return Ok;
 }
@@ -4203,6 +5839,8 @@ GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
 
 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
 {
+    GpStatus stat;
+
     TRACE("(%p)\n", graphics);
 
     if(!graphics)
@@ -4211,71 +5849,142 @@ GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
     if(graphics->busy)
         return ObjectBusy;
 
-    graphics->worldtrans->matrix[0] = 1.0;
-    graphics->worldtrans->matrix[1] = 0.0;
-    graphics->worldtrans->matrix[2] = 0.0;
-    graphics->worldtrans->matrix[3] = 1.0;
-    graphics->worldtrans->matrix[4] = 0.0;
-    graphics->worldtrans->matrix[5] = 0.0;
+    if (graphics->image && graphics->image->type == ImageTypeMetafile) {
+        stat = METAFILE_ResetWorldTransform((GpMetafile*)graphics->image);
 
-    return Ok;
-}
+        if (stat != Ok)
+            return stat;
+    }
 
-GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
-{
-    return GdipEndContainer(graphics, state);
+    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)
+    if(!graphics)
+        return InvalidParameter;
+
+    if(graphics->busy)
+        return ObjectBusy;
+
+    if (graphics->image && graphics->image->type == ImageTypeMetafile) {
+        stat = METAFILE_RotateWorldTransform((GpMetafile*)graphics->image, angle, order);
+
+        if (stat != Ok)
+            return stat;
+    }
+
+    return GdipRotateMatrix(&graphics->worldtrans, angle, order);
+}
+
+static GpStatus begin_container(GpGraphics *graphics,
+    GraphicsContainerType type, GraphicsContainer *state)
+{
+    GraphicsContainerItem *container;
+    GpStatus sts;
+
+    if(!graphics || !state)
         return InvalidParameter;
 
-    if(graphics->busy)
-        return ObjectBusy;
+    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 GdipRotateMatrix(graphics->worldtrans, angle, order);
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
 {
-    return GdipBeginContainer2(graphics, 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)
 {
     GraphicsContainerItem *container;
-    GpStatus sts;
+    GpMatrix transform;
+    GpStatus stat;
+    GpRectF scaled_srcrect;
+    REAL scale_x, scale_y;
 
-    TRACE("(%p, %p)\n", graphics, state);
+    TRACE("(%p, %s, %s, %d, %p)\n", graphics, debugstr_rectf(dstrect), debugstr_rectf(srcrect), unit, state);
 
-    if(!graphics || !state)
+    if(!graphics || !dstrect || !srcrect || unit < UnitPixel || unit > UnitMillimeter || !state)
         return InvalidParameter;
 
-    sts = init_container(&container, graphics);
-    if(sts != Ok)
-        return sts;
+    stat = init_container(&container, graphics, BEGIN_CONTAINER);
+    if(stat != Ok)
+        return stat;
 
     list_add_head(&graphics->containers, &container->entry);
     *state = graphics->contid = container->contid;
 
-    return Ok;
-}
+    scale_x = units_to_pixels(1.0, unit, graphics->xres);
+    scale_y = units_to_pixels(1.0, unit, graphics->yres);
 
-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;
+    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)
@@ -4284,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;
     }
 
@@ -4318,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)
@@ -4332,7 +6061,14 @@ GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
     if(graphics->busy)
         return ObjectBusy;
 
-    return GdipScaleMatrix(graphics->worldtrans, sx, sy, order);
+    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);
 }
 
 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
@@ -4357,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;
@@ -4373,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;
@@ -4383,12 +6145,31 @@ GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
 {
     TRACE("(%p, %d)\n", graphics, mode);
 
-    if(!graphics)
+    if(!graphics || mode == InterpolationModeInvalid || mode > InterpolationModeHighQualityBicubic)
         return InvalidParameter;
 
     if(graphics->busy)
         return ObjectBusy;
 
+    if (mode == InterpolationModeDefault || mode == InterpolationModeLowQuality)
+        mode = InterpolationModeBilinear;
+
+    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;
@@ -4396,6 +6177,8 @@ GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
 
 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
 {
+    GpStatus stat;
+
     TRACE("(%p, %.2f)\n", graphics, scale);
 
     if(!graphics || (scale <= 0.0))
@@ -4404,6 +6187,13 @@ GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
     if(graphics->busy)
         return ObjectBusy;
 
+    if (graphics->image && graphics->image->type == ImageTypeMetafile)
+    {
+        stat = METAFILE_SetPageTransform((GpMetafile*)graphics->image, graphics->unit, scale);
+        if (stat != Ok)
+            return stat;
+    }
+
     graphics->scale = scale;
 
     return Ok;
@@ -4411,6 +6201,8 @@ GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
 
 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
 {
+    GpStatus stat;
+
     TRACE("(%p, %d)\n", graphics, unit);
 
     if(!graphics)
@@ -4422,6 +6214,13 @@ GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
     if(unit == UnitWorld)
         return InvalidParameter;
 
+    if (graphics->image && graphics->image->type == ImageTypeMetafile)
+    {
+        stat = METAFILE_SetPageTransform((GpMetafile*)graphics->image, unit, graphics->scale);
+        if (stat != Ok)
+            return stat;
+    }
+
     graphics->unit = unit;
 
     return Ok;
@@ -4438,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;
@@ -4450,23 +6262,28 @@ GpStatus WINGDIPAPI GdipSetRenderingOrigin(GpGraphics *graphics, INT x, INT y)
     TRACE("(%p,%i,%i)\n", graphics, x, y);
 
     if (!(calls++))
-        FIXME("not implemented\n");
+        FIXME("value is unused in rendering\n");
 
-    return NotImplemented;
+    if (!graphics)
+        return InvalidParameter;
+
+    graphics->origin_x = x;
+    graphics->origin_y = y;
+
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipGetRenderingOrigin(GpGraphics *graphics, INT *x, INT *y)
 {
-    static int calls;
-
     TRACE("(%p,%p,%p)\n", graphics, x, y);
 
-    if (!(calls++))
-        FIXME("not implemented\n");
+    if (!graphics || !x || !y)
+        return InvalidParameter;
 
-    *x = *y = 0;
+    *x = graphics->origin_x;
+    *y = graphics->origin_y;
 
-    return NotImplemented;
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
@@ -4479,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;
@@ -4501,12 +6332,24 @@ GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
 {
     TRACE("(%p, %d)\n", graphics, hint);
 
-    if(!graphics)
+    if(!graphics || hint > TextRenderingHintClearTypeGridFit)
         return InvalidParameter;
 
     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;
@@ -4514,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)
@@ -4522,13 +6367,27 @@ GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix
     if(graphics->busy)
         return ObjectBusy;
 
-    GdipDeleteMatrix(graphics->worldtrans);
-    return GdipCloneMatrix(matrix, &graphics->worldtrans);
+    TRACE("%f,%f,%f,%f,%f,%f\n",
+          matrix->matrix[0], matrix->matrix[1], matrix->matrix[2],
+          matrix->matrix[3], matrix->matrix[4], matrix->matrix[5]);
+
+    if (graphics->image && graphics->image->type == ImageTypeMetafile) {
+        stat = METAFILE_SetWorldTransform((GpMetafile*)graphics->image, matrix);
+
+        if (stat != Ok)
+            return stat;
+    }
+
+    graphics->worldtrans = *matrix;
+
+    return Ok;
 }
 
 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)
@@ -4537,7 +6396,14 @@ GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
     if(graphics->busy)
         return ObjectBusy;
 
-    return GdipTranslateMatrix(graphics->worldtrans, dx, dy, order);
+    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);
 }
 
 /*****************************************************************************
@@ -4547,24 +6413,39 @@ GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode
 {
     GpRegion *region;
     GpStatus status;
+    GpMatrix transform;
 
     TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
 
     if(!graphics)
         return InvalidParameter;
 
+    if(graphics->busy)
+        return ObjectBusy;
+
+    /* hrgn is in gdi32 device units */
     status = GdipCreateRegionHrgn(hrgn, &region);
-    if(status != Ok)
-        return status;
 
-    status = GdipSetClipRegion(graphics, region, mode);
+    if (status == Ok)
+    {
+        status = get_graphics_transform(graphics, CoordinateSpaceDevice, WineCoordinateSpaceGdiDevice, &transform);
+
+        if (status == Ok)
+            status = GdipTransformRegion(region, &transform);
+
+        if (status == Ok)
+            status = GdipCombineRegionRegion(graphics->clip, region, mode);
 
-    GdipDeleteRegion(region);
+        GdipDeleteRegion(region);
+    }
     return status;
 }
 
 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
 {
+    GpStatus status;
+    GpPath *clip_path;
+
     TRACE("(%p, %p, %d)\n", graphics, path, mode);
 
     if(!graphics)
@@ -4573,14 +6454,29 @@ GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineM
     if(graphics->busy)
         return ObjectBusy;
 
-    return GdipCombineRegionPath(graphics->clip, path, mode);
+    status = GdipClonePath(path, &clip_path);
+    if (status == Ok)
+    {
+        GpMatrix world_to_device;
+
+        get_graphics_transform(graphics, CoordinateSpaceDevice,
+                               CoordinateSpaceWorld, &world_to_device);
+        status = GdipTransformPath(clip_path, &world_to_device);
+        if (status == Ok)
+            GdipCombineRegionPath(graphics->clip, clip_path, mode);
+
+        GdipDeletePath(clip_path);
+    }
+    return status;
 }
 
 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
                                     REAL width, REAL height,
                                     CombineMode mode)
 {
+    GpStatus status;
     GpRectF rect;
+    GpRegion *region;
 
     TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
 
@@ -4590,12 +6486,33 @@ 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;
     rect.Height = height;
+    status = GdipCreateRegionRect(&rect, &region);
+    if (status == Ok)
+    {
+        GpMatrix world_to_device;
+        BOOL identity;
 
-    return GdipCombineRegionRect(graphics->clip, &rect, mode);
+        get_graphics_transform(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &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);
+
+        GdipDeleteRegion(region);
+    }
+    return status;
 }
 
 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
@@ -4616,6 +6533,9 @@ GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
                                       CombineMode mode)
 {
+    GpStatus status;
+    GpRegion *clip;
+
     TRACE("(%p, %p, %d)\n", graphics, region, mode);
 
     if(!graphics || !region)
@@ -4624,27 +6544,36 @@ GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
     if(graphics->busy)
         return ObjectBusy;
 
-    return GdipCombineRegionRegion(graphics->clip, region, mode);
-}
-
-GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
-    UINT limitDpi)
-{
-    static int calls;
+    if (graphics->image && graphics->image->type == ImageTypeMetafile)
+    {
+        status = METAFILE_SetClipRegion((GpMetafile*)graphics->image, region, mode);
+        if (status != Ok)
+            return status;
+    }
 
-    TRACE("(%p,%u)\n", metafile, limitDpi);
+    status = GdipCloneRegion(region, &clip);
+    if (status == Ok)
+    {
+        GpMatrix world_to_device;
+        BOOL identity;
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+        get_graphics_transform(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &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);
 
-    return NotImplemented;
+        GdipDeleteRegion(clip);
+    }
+    return status;
 }
 
 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);
 
@@ -4654,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,
@@ -4684,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;
@@ -4692,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;
 }
@@ -4707,11 +6628,7 @@ GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
     if(graphics->busy)
         return ObjectBusy;
 
-    if (graphics->image)
-        *dpi = graphics->image->xres;
-    else
-        *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
-
+    *dpi = graphics->xres;
     return Ok;
 }
 
@@ -4725,11 +6642,7 @@ GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
     if(graphics->busy)
         return ObjectBusy;
 
-    if (graphics->image)
-        *dpi = graphics->image->yres;
-    else
-        *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSY);
-
+    *dpi = graphics->yres;
     return Ok;
 }
 
@@ -4747,11 +6660,18 @@ GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST G
     if(graphics->busy)
         return ObjectBusy;
 
-    m = *(graphics->worldtrans);
+    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);
     if(ret == Ok)
-        *(graphics->worldtrans) = m;
+        graphics->worldtrans = m;
 
     return ret;
 }
@@ -4761,6 +6681,8 @@ static const COLORREF DC_BACKGROUND_KEY = 0x0c0b0d;
 
 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
 {
+    GpStatus stat=Ok;
+
     TRACE("(%p, %p)\n", graphics, hdc);
 
     if(!graphics || !hdc)
@@ -4769,13 +6691,16 @@ GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
     if(graphics->busy)
         return ObjectBusy;
 
-    if (!graphics->hdc ||
+    if (graphics->image && graphics->image->type == ImageTypeMetafile)
+    {
+        stat = METAFILE_GetDC((GpMetafile*)graphics->image, 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. */
         HDC temp_hdc;
         HBITMAP hbitmap;
-        GpStatus stat;
         GpRectF bounds;
         BITMAPINFOHEADER bmih;
         int i;
@@ -4824,22 +6749,26 @@ GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
         *hdc = graphics->hdc;
     }
 
-    graphics->busy = TRUE;
+    if (stat == Ok)
+        graphics->busy = TRUE;
 
-    return Ok;
+    return stat;
 }
 
 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
 {
-    TRACE("(%p, %p)\n", graphics, hdc);
+    GpStatus stat=Ok;
 
-    if(!graphics || !hdc)
-        return InvalidParameter;
+    TRACE("(%p, %p)\n", graphics, hdc);
 
-    if((graphics->hdc != hdc && graphics->temp_hdc != hdc) || !(graphics->busy))
+    if(!graphics || !hdc || !graphics->busy)
         return InvalidParameter;
 
-    if (graphics->temp_hdc == hdc)
+    if (graphics->image && graphics->image->type == ImageTypeMetafile)
+    {
+        stat = METAFILE_ReleaseDC((GpMetafile*)graphics->image, hdc);
+    }
+    else if (graphics->temp_hdc == hdc)
     {
         DWORD* pos;
         int i;
@@ -4858,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);
@@ -4866,16 +6795,22 @@ GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
         graphics->temp_hdc = NULL;
         graphics->temp_hbitmap = NULL;
     }
+    else if (hdc != graphics->hdc)
+    {
+        stat = InvalidParameter;
+    }
 
-    graphics->busy = FALSE;
+    if (stat == Ok)
+        graphics->busy = FALSE;
 
-    return Ok;
+    return stat;
 }
 
 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
 {
     GpRegion *clip;
     GpStatus status;
+    GpMatrix device_to_world;
 
     TRACE("(%p, %p)\n", graphics, region);
 
@@ -4888,82 +6823,156 @@ GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
     if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
         return status;
 
+    get_graphics_transform(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, &device_to_world);
+    status = GdipTransformRegion(clip, &device_to_world);
+    if (status != Ok)
+    {
+        GdipDeleteRegion(clip);
+        return status;
+    }
+
     /* 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;
 }
 
-GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
-                                        GpCoordinateSpace src_space, GpPointF *points, INT count)
+GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
+        GpCoordinateSpace src_space, GpMatrix *matrix)
 {
-    GpMatrix *matrix;
-    GpStatus stat;
-    REAL unitscale;
-
-    if(!graphics || !points || count <= 0)
-        return InvalidParameter;
-
-    if(graphics->busy)
-        return ObjectBusy;
-
-    TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
+    GpStatus stat = Ok;
+    REAL scale_x, scale_y;
 
-    if (src_space == dst_space) return Ok;
+    GdipSetMatrixElements(matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
 
-    stat = GdipCreateMatrix(&matrix);
-    if (stat == Ok)
+    if (dst_space != src_space)
     {
-        unitscale = convert_unit(graphics_res(graphics), graphics->unit);
+        scale_x = units_to_pixels(1.0, graphics->unit, graphics->xres);
+        scale_y = units_to_pixels(1.0, graphics->unit, graphics->yres);
 
         if(graphics->unit != UnitDisplay)
-            unitscale *= graphics->scale;
-
-        /* transform from src_space to CoordinateSpacePage */
-        switch (src_space)
         {
-        case CoordinateSpaceWorld:
-            GdipMultiplyMatrix(matrix, graphics->worldtrans, MatrixOrderAppend);
-            break;
-        case CoordinateSpacePage:
-            break;
-        case CoordinateSpaceDevice:
-            GdipScaleMatrix(matrix, 1.0/unitscale, 1.0/unitscale, MatrixOrderAppend);
-            break;
+            scale_x *= graphics->scale;
+            scale_y *= graphics->scale;
         }
 
-        /* transform from CoordinateSpacePage to dst_space */
-        switch (dst_space)
+        if (dst_space < src_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;
-                stat = GdipCloneMatrix(graphics->worldtrans, &inverted_transform);
+                GpMatrix inverted_transform = graphics->worldtrans;
+                stat = GdipInvertMatrix(&inverted_transform);
                 if (stat == Ok)
-                {
-                    stat = GdipInvertMatrix(inverted_transform);
-                    if (stat == Ok)
-                        GdipMultiplyMatrix(matrix, inverted_transform, MatrixOrderAppend);
-                    GdipDeleteMatrix(inverted_transform);
-                }
+                    GdipMultiplyMatrix(matrix, &inverted_transform, MatrixOrderAppend);
                 break;
             }
-        case CoordinateSpacePage:
-            break;
-        case CoordinateSpaceDevice:
-            GdipScaleMatrix(matrix, unitscale, unitscale, MatrixOrderAppend);
-            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;
+}
 
-        if (stat == Ok)
-            stat = GdipTransformMatrixPoints(matrix, points, count);
+GpStatus gdip_transform_points(GpGraphics *graphics, GpCoordinateSpace dst_space,
+                               GpCoordinateSpace src_space, GpPointF *points, INT count)
+{
+    GpMatrix matrix;
+    GpStatus stat;
 
-        GdipDeleteMatrix(matrix);
-    }
+    stat = get_graphics_transform(graphics, dst_space, src_space, &matrix);
+    if (stat != Ok) return stat;
 
-    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)
+        return ObjectBusy;
+
+    TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
+
+    if (src_space == dst_space) return Ok;
+
+    return gdip_transform_points(graphics, dst_space, src_space, points, count);
 }
 
 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
@@ -4978,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;
 
@@ -4991,10 +7000,10 @@ GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace
 
     if(ret == Ok)
         for(i = 0; i < count; i++){
-            points[i].X = roundr(pointsF[i].X);
-            points[i].Y = roundr(pointsF[i].Y);
+            points[i].X = gdip_round(pointsF[i].X);
+            points[i].Y = gdip_round(pointsF[i].Y);
         }
-    GdipFree(pointsF);
+    heap_free(pointsF);
 
     return ret;
 }
@@ -5051,44 +7060,413 @@ GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT
                                             GDIPCONST GpFont *font, GDIPCONST PointF *positions,
                                             INT flags, GDIPCONST GpMatrix *matrix, RectF *boundingBox)
 {
-    FIXME("(%p %p %d %p %p %d %p %p): stub\n", graphics, text, length, font, positions, flags, matrix, boundingBox);
-    return NotImplemented;
+    static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance);
+    HFONT hfont;
+    HDC hdc;
+    REAL min_x, min_y, max_x, max_y, x, y;
+    int i;
+    TEXTMETRICW textmetric;
+    const WORD *glyph_indices;
+    WORD *dynamic_glyph_indices=NULL;
+    REAL rel_width, rel_height, ascent, descent;
+    GpPointF pt[3];
+
+    TRACE("(%p %p %d %p %p %d %p %p)\n", graphics, text, length, font, positions, flags, matrix, boundingBox);
+
+    if (!graphics || !text || !font || !positions || !boundingBox)
+        return InvalidParameter;
+
+    if (length == -1)
+        length = strlenW(text);
+
+    if (length == 0)
+    {
+        boundingBox->X = 0.0;
+        boundingBox->Y = 0.0;
+        boundingBox->Width = 0.0;
+        boundingBox->Height = 0.0;
+    }
+
+    if (flags & unsupported_flags)
+        FIXME("Ignoring flags %x\n", flags & unsupported_flags);
+
+    get_font_hfont(graphics, font, NULL, &hfont, matrix);
+
+    hdc = CreateCompatibleDC(0);
+    SelectObject(hdc, hfont);
+
+    GetTextMetricsW(hdc, &textmetric);
+
+    pt[0].X = 0.0;
+    pt[0].Y = 0.0;
+    pt[1].X = 1.0;
+    pt[1].Y = 0.0;
+    pt[2].X = 0.0;
+    pt[2].Y = 1.0;
+    if (matrix)
+    {
+        GpMatrix xform = *matrix;
+        GdipTransformMatrixPoints(&xform, pt, 3);
+    }
+    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)+
+                      (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
+
+    if (flags & DriverStringOptionsCmapLookup)
+    {
+        glyph_indices = dynamic_glyph_indices = heap_alloc_zero(sizeof(WORD) * length);
+        if (!glyph_indices)
+        {
+            DeleteDC(hdc);
+            DeleteObject(hfont);
+            return OutOfMemory;
+        }
+
+        GetGlyphIndicesW(hdc, text, length, dynamic_glyph_indices, 0);
+    }
+    else
+        glyph_indices = text;
+
+    min_x = max_x = x = positions[0].X;
+    min_y = max_y = y = positions[0].Y;
+
+    ascent = textmetric.tmAscent / rel_height;
+    descent = textmetric.tmDescent / rel_height;
+
+    for (i=0; i<length; i++)
+    {
+        int char_width;
+        ABC abc;
+
+        if (!(flags & DriverStringOptionsRealizedAdvance))
+        {
+            x = positions[i].X;
+            y = positions[i].Y;
+        }
+
+        GetCharABCWidthsW(hdc, glyph_indices[i], glyph_indices[i], &abc);
+        char_width = abc.abcA + abc.abcB + abc.abcC;
+
+        if (min_y > y - ascent) min_y = y - ascent;
+        if (max_y < y + descent) max_y = y + descent;
+        if (min_x > x) min_x = x;
+
+        x += char_width / rel_width;
+
+        if (max_x < x) max_x = x;
+    }
+
+    heap_free(dynamic_glyph_indices);
+    DeleteDC(hdc);
+    DeleteObject(hfont);
+
+    boundingBox->X = min_x;
+    boundingBox->Y = min_y;
+    boundingBox->Width = max_x - min_x;
+    boundingBox->Height = max_y - min_y;
+
+    return Ok;
 }
 
-/*****************************************************************************
- * GdipDrawDriverString [GDIPLUS.@]
- */
-GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
-                                         GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
-                                         GDIPCONST PointF *positions, INT flags,
-                                         GDIPCONST GpMatrix *matrix )
+static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
+                                           GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
+                                           GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
+                                           INT flags, GDIPCONST GpMatrix *matrix)
 {
-    FIXME("(%p %p %d %p %p %p %d %p): stub\n", graphics, text, length, font, brush, positions, flags, matrix);
-    return NotImplemented;
+    static const INT unsupported_flags = ~(DriverStringOptionsRealizedAdvance|DriverStringOptionsCmapLookup);
+    INT save_state;
+    GpPointF pt;
+    HFONT hfont;
+    UINT eto_flags=0;
+    GpStatus status;
+    HRGN hrgn;
+
+    if (flags & unsupported_flags)
+        FIXME("Ignoring flags %x\n", flags & unsupported_flags);
+
+    if (!(flags & DriverStringOptionsCmapLookup))
+        eto_flags |= ETO_GLYPH_INDEX;
+
+    save_state = SaveDC(graphics->hdc);
+    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];
+    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);
+
+    return Ok;
 }
 
-GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
-                                       MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
+static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
+                                        GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
+                                        GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
+                                        INT flags, GDIPCONST GpMatrix *matrix)
 {
-    FIXME("(%p %d %p %d %p %p): stub\n", hdc, type, frameRect, frameUnit, desc, metafile);
-    return NotImplemented;
+    static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance);
+    GpStatus stat;
+    PointF *real_positions, real_position;
+    POINT *pti;
+    HFONT hfont;
+    HDC hdc;
+    int min_x=INT_MAX, min_y=INT_MAX, max_x=INT_MIN, max_y=INT_MIN, i, x, y;
+    DWORD max_glyphsize=0;
+    GLYPHMETRICS glyphmetrics;
+    static const MAT2 identity = {{0,1}, {0,0}, {0,0}, {0,1}};
+    BYTE *glyph_mask;
+    BYTE *text_mask;
+    int text_mask_stride;
+    BYTE *pixel_data;
+    int pixel_data_stride;
+    GpRect pixel_area;
+    UINT ggo_flags = GGO_GRAY8_BITMAP;
+
+    if (length <= 0)
+        return Ok;
+
+    if (!(flags & DriverStringOptionsCmapLookup))
+        ggo_flags |= GGO_GLYPH_INDEX;
+
+    if (flags & unsupported_flags)
+        FIXME("Ignoring flags %x\n", flags & unsupported_flags);
+
+    pti = heap_alloc_zero(sizeof(POINT) * length);
+    if (!pti)
+        return OutOfMemory;
+
+    if (flags & DriverStringOptionsRealizedAdvance)
+    {
+        real_position = positions[0];
+
+        gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, &real_position, 1);
+        round_points(pti, &real_position, 1);
+    }
+    else
+    {
+        real_positions = heap_alloc_zero(sizeof(PointF) * length);
+        if (!real_positions)
+        {
+            heap_free(pti);
+            return OutOfMemory;
+        }
+
+        memcpy(real_positions, positions, sizeof(PointF) * length);
+
+        gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, real_positions, length);
+        round_points(pti, real_positions, length);
+
+        heap_free(real_positions);
+    }
+
+    get_font_hfont(graphics, font, format, &hfont, matrix);
+
+    hdc = CreateCompatibleDC(0);
+    SelectObject(hdc, hfont);
+
+    /* Get the boundaries of the text to be drawn */
+    for (i=0; i<length; i++)
+    {
+        DWORD glyphsize;
+        int left, top, right, bottom;
+
+        glyphsize = GetGlyphOutlineW(hdc, text[i], ggo_flags,
+            &glyphmetrics, 0, NULL, &identity);
+
+        if (glyphsize == GDI_ERROR)
+        {
+            ERR("GetGlyphOutlineW failed\n");
+            heap_free(pti);
+            DeleteDC(hdc);
+            DeleteObject(hfont);
+            return GenericError;
+        }
+
+        if (glyphsize > max_glyphsize)
+            max_glyphsize = glyphsize;
+
+        if (glyphsize != 0)
+        {
+            left = pti[i].x + glyphmetrics.gmptGlyphOrigin.x;
+            top = pti[i].y - glyphmetrics.gmptGlyphOrigin.y;
+            right = pti[i].x + glyphmetrics.gmptGlyphOrigin.x + glyphmetrics.gmBlackBoxX;
+            bottom = pti[i].y - glyphmetrics.gmptGlyphOrigin.y + glyphmetrics.gmBlackBoxY;
+
+            if (left < min_x) min_x = left;
+            if (top < min_y) min_y = top;
+            if (right > max_x) max_x = right;
+            if (bottom > max_y) max_y = bottom;
+        }
+
+        if (i+1 < length && (flags & DriverStringOptionsRealizedAdvance) == DriverStringOptionsRealizedAdvance)
+        {
+            pti[i+1].x = pti[i].x + glyphmetrics.gmCellIncX;
+            pti[i+1].y = pti[i].y + glyphmetrics.gmCellIncY;
+        }
+    }
+
+    if (max_glyphsize == 0)
+        /* Nothing to draw. */
+        return Ok;
+
+    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))
+    {
+        heap_free(glyph_mask);
+        heap_free(text_mask);
+        heap_free(pti);
+        DeleteDC(hdc);
+        DeleteObject(hfont);
+        return OutOfMemory;
+    }
+
+    /* Generate a mask for the text */
+    for (i=0; i<length; i++)
+    {
+        DWORD ret;
+        int left, top, stride;
+
+        ret = GetGlyphOutlineW(hdc, text[i], ggo_flags,
+            &glyphmetrics, max_glyphsize, glyph_mask, &identity);
+
+        if (ret == GDI_ERROR || ret == 0)
+            continue; /* empty glyph */
+
+        left = pti[i].x + glyphmetrics.gmptGlyphOrigin.x;
+        top = pti[i].y - glyphmetrics.gmptGlyphOrigin.y;
+        stride = (glyphmetrics.gmBlackBoxX + 3) & (~3);
+
+        for (y=0; y<glyphmetrics.gmBlackBoxY; y++)
+        {
+            BYTE *glyph_val = glyph_mask + y * stride;
+            BYTE *text_val = text_mask + (left - min_x) + (top - min_y + y) * text_mask_stride;
+            for (x=0; x<glyphmetrics.gmBlackBoxX; x++)
+            {
+                *text_val = min(64, *text_val + *glyph_val);
+                glyph_val++;
+                text_val++;
+            }
+        }
+    }
+
+    heap_free(pti);
+    DeleteDC(hdc);
+    DeleteObject(hfont);
+    heap_free(glyph_mask);
+
+    /* get the brush data */
+    pixel_data = heap_alloc_zero(4 * (max_x - min_x) * (max_y - min_y));
+    if (!pixel_data)
+    {
+        heap_free(text_mask);
+        return OutOfMemory;
+    }
+
+    pixel_area.X = min_x;
+    pixel_area.Y = min_y;
+    pixel_area.Width = max_x - min_x;
+    pixel_area.Height = max_y - min_y;
+    pixel_data_stride = pixel_area.Width * 4;
+
+    stat = brush_fill_pixels(graphics, (GpBrush*)brush, (DWORD*)pixel_data, &pixel_area, pixel_area.Width);
+    if (stat != Ok)
+    {
+        heap_free(text_mask);
+        heap_free(pixel_data);
+        return stat;
+    }
+
+    /* multiply the brush data by the mask */
+    for (y=0; y<pixel_area.Height; y++)
+    {
+        BYTE *text_val = text_mask + text_mask_stride * y;
+        BYTE *pixel_val = pixel_data + pixel_data_stride * y + 3;
+        for (x=0; x<pixel_area.Width; x++)
+        {
+            *pixel_val = (*pixel_val) * (*text_val) / 64;
+            text_val++;
+            pixel_val+=4;
+        }
+    }
+
+    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, PixelFormat32bppARGB);
+
+    gdi_transform_release(graphics);
+
+    heap_free(pixel_data);
+
+    return stat;
 }
 
-/*****************************************************************************
- * GdipRecordMetafileI [GDIPLUS.@]
- */
-GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
-                                        MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
+static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
+                                   GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
+                                   GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
+                                   INT flags, GDIPCONST GpMatrix *matrix)
 {
-    FIXME("(%p %d %p %d %p %p): stub\n", hdc, type, frameRect, frameUnit, desc, metafile);
-    return NotImplemented;
+    GpStatus stat = NotImplemented;
+
+    if (length == -1)
+        length = strlenW(text);
+
+    if (graphics->hdc && !graphics->alpha_hdc &&
+        ((flags & DriverStringOptionsRealizedAdvance) || length <= 1) &&
+        brush->bt == BrushTypeSolidColor &&
+        (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000)
+        stat = GDI32_GdipDrawDriverString(graphics, text, length, font, format,
+                                          brush, positions, flags, matrix);
+    if (stat == NotImplemented)
+        stat = SOFTWARE_GdipDrawDriverString(graphics, text, length, font, format,
+                                             brush, positions, flags, matrix);
+    return stat;
 }
 
-GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
-                                        MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
+/*****************************************************************************
+ * GdipDrawDriverString [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
+                                         GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
+                                         GDIPCONST PointF *positions, INT flags,
+                                         GDIPCONST GpMatrix *matrix )
 {
-    FIXME("(%p %p %d %p %d %p %p): stub\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
-    return NotImplemented;
+    TRACE("(%p %s %p %p %p %d %p)\n", graphics, debugstr_wn(text, length), font, brush, positions, flags, matrix);
+
+    if (!graphics || !text || !font || !brush || !positions)
+        return InvalidParameter;
+
+    return draw_driver_string(graphics, text, length, font, NULL,
+                              brush, positions, flags, matrix);
 }
 
 /*****************************************************************************
@@ -5113,3 +7491,28 @@ cleanup:
     GdipDeleteRegion(rgn);
     return stat;
 }
+
+GpStatus WINGDIPAPI GdipResetPageTransform(GpGraphics *graphics)
+{
+    static int calls;
+
+    TRACE("(%p) stub\n", graphics);
+
+    if(!(calls++))
+        FIXME("not implemented\n");
+
+    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;
+}