[GDIPLUS] Sync with Wine Staging 1.9.23. CORE-12409
[reactos.git] / reactos / dll / win32 / gdiplus / graphics.c
index 320664e..56d482d 100644 (file)
@@ -47,9 +47,6 @@ static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text,
                                    GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
                                    INT flags, GDIPCONST GpMatrix *matrix);
 
-static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
-        GpCoordinateSpace src_space, GpMatrix *matrix);
-
 /* Converts from gdiplus path point type to gdi path point type. */
 static BYTE convert_path_point_type(BYTE type)
 {
@@ -1920,9 +1917,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;
@@ -1939,7 +1942,7 @@ typedef struct _GraphicsContainerItem {
 } GraphicsContainerItem;
 
 static GpStatus init_container(GraphicsContainerItem** container,
-        GDIPCONST GpGraphics* graphics){
+        GDIPCONST GpGraphics* graphics, GraphicsContainerType type){
     GpStatus sts;
 
     *container = heap_alloc_zero(sizeof(GraphicsContainerItem));
@@ -1947,6 +1950,7 @@ static GpStatus init_container(GraphicsContainerItem** container,
         return OutOfMemory;
 
     (*container)->contid = graphics->contid + 1;
+    (*container)->type = type;
 
     (*container)->smoothing = graphics->smoothing;
     (*container)->compqual = graphics->compqual;
@@ -3075,6 +3079,8 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
             HDC hdc;
             BOOL temp_hdc = FALSE, temp_bitmap = FALSE;
             HBITMAP hbitmap, old_hbm=NULL;
+            HRGN hrgn;
+            INT save_state;
 
             if (!(bitmap->format == PixelFormat16bppRGB555 ||
                   bitmap->format == PixelFormat24bppRGB ||
@@ -3135,6 +3141,16 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
                 old_hbm = SelectObject(hdc, hbitmap);
             }
 
+            save_state = SaveDC(graphics->hdc);
+
+            stat = get_clip_hrgn(graphics, &hrgn);
+
+            if (stat == Ok && hrgn)
+            {
+                ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
+                DeleteObject(hrgn);
+            }
+
             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,
@@ -3146,6 +3162,8 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
                     hdc, srcx, srcy, srcwidth, srcheight, SRCCOPY);
             }
 
+            RestoreDC(graphics->hdc, save_state);
+
             if (temp_hdc)
             {
                 SelectObject(hdc, old_hbm);
@@ -3284,6 +3302,12 @@ GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
 
     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
 
+    if (!pen)
+        return InvalidParameter;
+
+    if (pen->unit == UnitPixel && pen->width <= 0.0)
+        return Ok;
+
     pt[0].X = x1;
     pt[0].Y = y1;
     pt[1].X = x2;
@@ -3347,26 +3371,12 @@ GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
     return retval;
 }
 
-GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
+GpStatus GDI32_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
 {
     INT save_state;
     GpStatus retval;
     HRGN hrgn=NULL;
 
-    TRACE("(%p, %p, %p)\n", graphics, pen, path);
-
-    if(!pen || !graphics)
-        return InvalidParameter;
-
-    if(graphics->busy)
-        return ObjectBusy;
-
-    if (!graphics->hdc)
-    {
-        FIXME("graphics object has no HDC\n");
-        return Ok;
-    }
-
     save_state = prepare_dc(graphics, pen);
 
     retval = get_clip_hrgn(graphics, &hrgn);
@@ -3387,6 +3397,398 @@ end:
     return retval;
 }
 
+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, CoordinateSpaceDevice,
+                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_bounds(graphics, &gp_bound_rect);
+    }
+
+    if (stat == Ok)
+    {
+        output_area.left = max(output_area.left, floorf(gp_bound_rect.X));
+        output_area.top = max(output_area.top, floorf(gp_bound_rect.Y));
+        output_area.right = min(output_area.right, ceilf(gp_bound_rect.X + gp_bound_rect.Width));
+        output_area.bottom = min(output_area.bottom, ceilf(gp_bound_rect.Y + gp_bound_rect.Height));
+
+        output_width = output_area.right - output_area.left + 1;
+        output_height = output_area.bottom - output_area.top + 1;
+
+        if (output_width <= 0 || output_height <= 0)
+        {
+            GdipDeletePath(flat_path);
+            return Ok;
+        }
+
+        gp_output_area.X = output_area.left;
+        gp_output_area.Y = output_area.top;
+        gp_output_area.Width = output_width;
+        gp_output_area.Height = output_height;
+
+        output_bits = heap_alloc_zero(output_width * output_height * sizeof(DWORD));
+        if (!output_bits)
+            stat = OutOfMemory;
+    }
+
+    if (stat == Ok)
+    {
+        if (pen->brush->bt != BrushTypeSolidColor)
+        {
+            /* allocate and draw brush output */
+            brush_bits = heap_alloc_zero(output_width * output_height * sizeof(DWORD));
+
+            if (brush_bits)
+            {
+                stat = brush_fill_pixels(graphics, pen->brush, brush_bits,
+                    &gp_output_area, output_width);
+            }
+            else
+                stat = OutOfMemory;
+        }
+
+        if (stat == Ok)
+        {
+            /* convert dash pattern to bool array */
+            switch (pen->dash)
+            {
+            case DashStyleCustom:
+            {
+                dash_pattern_size = 0;
+
+                for (i=0; i < pen->numdashes; i++)
+                    dash_pattern_size += gdip_round(pen->dashes[i]);
+
+                if (dash_pattern_size != 0)
+                {
+                    dash_pattern = dyn_dash_pattern = heap_alloc(dash_pattern_size);
+
+                    if (dyn_dash_pattern)
+                    {
+                        int j=0;
+                        for (i=0; i < pen->numdashes; i++)
+                        {
+                            int k;
+                            for (k=0; k < gdip_round(pen->dashes[i]); k++)
+                                dyn_dash_pattern[j++] = (i&1)^1;
+                        }
+                    }
+                    else
+                        stat = OutOfMemory;
+
+                    break;
+                }
+                /* else fall through */
+            }
+            case DashStyleSolid:
+            default:
+                dash_pattern = static_dash_pattern;
+                dash_pattern_size = 1;
+                break;
+            case DashStyleDash:
+                dash_pattern = static_dash_pattern;
+                dash_pattern_size = 4;
+                break;
+            case DashStyleDot:
+                dash_pattern = &static_dash_pattern[4];
+                dash_pattern_size = 2;
+                break;
+            case DashStyleDashDot:
+                dash_pattern = static_dash_pattern;
+                dash_pattern_size = 6;
+                break;
+            case DashStyleDashDotDot:
+                dash_pattern = static_dash_pattern;
+                dash_pattern_size = 8;
+                break;
+            }
+        }
+
+        if (stat == Ok)
+        {
+            /* trace path */
+            GpPointF subpath_start = flat_path->pathdata.Points[0];
+            INT prev_x = INT_MAX, prev_y = INT_MAX;
+            int dash_pos = dash_pattern_size - 1;
+
+            for (i=0; i < flat_path->pathdata.Count; i++)
+            {
+                BYTE type, type2;
+                GpPointF start_point, end_point;
+                GpPoint start_pointi, end_pointi;
+
+                type = flat_path->pathdata.Types[i];
+                if (i+1 < flat_path->pathdata.Count)
+                    type2 = flat_path->pathdata.Types[i+1];
+                else
+                    type2 = PathPointTypeStart;
+
+                start_point = flat_path->pathdata.Points[i];
+
+                if ((type & PathPointTypePathTypeMask) == PathPointTypeStart)
+                    subpath_start = start_point;
+
+                if ((type & PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath)
+                    end_point = subpath_start;
+                else if ((type2 & PathPointTypePathTypeMask) == PathPointTypeStart)
+                    continue;
+                else
+                    end_point = flat_path->pathdata.Points[i+1];
+
+                start_pointi.X = floorf(start_point.X);
+                start_pointi.Y = floorf(start_point.Y);
+                end_pointi.X = floorf(end_point.X);
+                end_pointi.Y = floorf(end_point.Y);
+
+                /* draw line segment */
+                if (abs(start_pointi.Y - end_pointi.Y) > abs(start_pointi.X - end_pointi.X))
+                {
+                    INT x, y, start_y, end_y, step;
+
+                    if (start_pointi.Y < end_pointi.Y)
+                    {
+                        step = 1;
+                        start_y = ceilf(start_point.Y) - output_area.top;
+                        end_y = end_pointi.Y - output_area.top;
+                    }
+                    else
+                    {
+                        step = -1;
+                        start_y = start_point.Y - output_area.top;
+                        end_y = ceilf(end_point.Y) - output_area.top;
+                    }
+
+                    for (y=start_y; y != (end_y+step); y+=step)
+                    {
+                        x = gdip_round( start_point.X +
+                            (end_point.X - start_point.X) * (y + output_area.top - start_point.Y) / (end_point.Y - start_point.Y) )
+                            - output_area.left;
+
+                        if (x == prev_x && y == prev_y)
+                            continue;
+
+                        prev_x = x;
+                        prev_y = y;
+                        dash_pos = (dash_pos + 1 == dash_pattern_size) ? 0 : dash_pos + 1;
+
+                        if (!dash_pattern[dash_pos])
+                            continue;
+
+                        if (x < 0 || x >= output_width || y < 0 || y >= output_height)
+                            continue;
+
+                        if (brush_bits)
+                            output_bits[x + y*output_width] = brush_bits[x + y*output_width];
+                        else
+                            output_bits[x + y*output_width] = ((GpSolidFill*)pen->brush)->color;
+                    }
+                }
+                else
+                {
+                    INT x, y, start_x, end_x, step;
+
+                    if (start_pointi.X < end_pointi.X)
+                    {
+                        step = 1;
+                        start_x = ceilf(start_point.X) - output_area.left;
+                        end_x = end_pointi.X - output_area.left;
+                    }
+                    else
+                    {
+                        step = -1;
+                        start_x = start_point.X - output_area.left;
+                        end_x = ceilf(end_point.X) - output_area.left;
+                    }
+
+                    for (x=start_x; x != (end_x+step); x+=step)
+                    {
+                        y = gdip_round( start_point.Y +
+                            (end_point.Y - start_point.Y) * (x + output_area.left - start_point.X) / (end_point.X - start_point.X) )
+                            - output_area.top;
+
+                        if (x == prev_x && y == prev_y)
+                            continue;
+
+                        prev_x = x;
+                        prev_y = y;
+                        dash_pos = (dash_pos + 1 == dash_pattern_size) ? 0 : dash_pos + 1;
+
+                        if (!dash_pattern[dash_pos])
+                            continue;
+
+                        if (x < 0 || x >= output_width || y < 0 || y >= output_height)
+                            continue;
+
+                        if (brush_bits)
+                            output_bits[x + y*output_width] = brush_bits[x + y*output_width];
+                        else
+                            output_bits[x + y*output_width] = ((GpSolidFill*)pen->brush)->color;
+                    }
+                }
+            }
+        }
+
+        /* draw output image */
+        if (stat == Ok)
+        {
+            stat = alpha_blend_pixels(graphics, output_area.left, output_area.top,
+                (BYTE*)output_bits, output_width, output_height, output_width * 4,
+                PixelFormat32bppARGB);
+        }
+
+        heap_free(brush_bits);
+        heap_free(dyn_dash_pattern);
+        heap_free(output_bits);
+    }
+
+    GdipDeletePath(flat_path);
+
+    return stat;
+}
+
+GpStatus SOFTWARE_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
+{
+    GpStatus stat;
+    GpPath *wide_path;
+    GpMatrix *transform=NULL;
+
+    /* Check if the final pen thickness in pixels is too thin. */
+    if (pen->unit == UnitPixel)
+    {
+        if (pen->width < 1.415)
+            return SOFTWARE_GdipDrawThinPath(graphics, pen, path);
+    }
+    else
+    {
+        GpPointF points[3] = {{0,0}, {1,0}, {0,1}};
+
+        points[1].X = pen->width;
+        points[2].Y = pen->width;
+
+        stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
+            CoordinateSpaceWorld, points, 3);
+
+        if (stat != Ok)
+            return stat;
+
+        if (((points[1].X-points[0].X)*(points[1].X-points[0].X) +
+             (points[1].Y-points[0].Y)*(points[1].Y-points[0].Y) < 2.0001) &&
+            ((points[2].X-points[0].X)*(points[2].X-points[0].X) +
+             (points[2].Y-points[0].Y)*(points[2].Y-points[0].Y) < 2.0001))
+            return SOFTWARE_GdipDrawThinPath(graphics, pen, path);
+    }
+
+    stat = GdipClonePath(path, &wide_path);
+
+    if (stat != Ok)
+        return stat;
+
+    if (pen->unit == UnitPixel)
+    {
+        /* We have to transform this to device coordinates to get the widths right. */
+        stat = GdipCreateMatrix(&transform);
+
+        if (stat == Ok)
+            stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
+                CoordinateSpaceWorld, transform);
+    }
+
+    if (stat == Ok)
+        stat = GdipWidenPath(wide_path, pen, transform, 1.0);
+
+    if (pen->unit == UnitPixel)
+    {
+        /* Transform the path back to world coordinates */
+        if (stat == Ok)
+            stat = GdipInvertMatrix(transform);
+
+        if (stat == Ok)
+            stat = GdipTransformPath(wide_path, transform);
+    }
+
+    /* Actually draw the path */
+    if (stat == Ok)
+        stat = GdipFillPath(graphics, pen->brush, wide_path);
+
+    GdipDeleteMatrix(transform);
+
+    GdipDeletePath(wide_path);
+
+    return stat;
+}
+
+GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
+{
+    GpStatus retval;
+
+    TRACE("(%p, %p, %p)\n", graphics, pen, path);
+
+    if(!pen || !graphics)
+        return InvalidParameter;
+
+    if(graphics->busy)
+        return ObjectBusy;
+
+    if (path->pathdata.Count == 0)
+        return Ok;
+
+    if (!graphics->hdc)
+        retval = SOFTWARE_GdipDrawPath(graphics, pen, path);
+    else
+        retval = GDI32_GdipDrawPath(graphics, pen, path);
+
+    return retval;
+}
+
 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
 {
@@ -3934,6 +4336,23 @@ static GpStatus GDI32_GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
 
     ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
 
+    DeleteObject(hrgn);
+
+    hrgn = NULL;
+    status = get_clip_hrgn(graphics, &hrgn);
+
+    if (status != Ok)
+    {
+        RestoreDC(graphics->hdc, save_state);
+        return status;
+    }
+
+    if (hrgn)
+    {
+        ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
+        DeleteObject(hrgn);
+    }
+
     if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
     {
         BeginPath(graphics->hdc);
@@ -3945,7 +4364,6 @@ static GpStatus GDI32_GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
 
     RestoreDC(graphics->hdc, save_state);
 
-    DeleteObject(hrgn);
 
     return Ok;
 }
@@ -5128,14 +5546,11 @@ GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
     return GdipSetMatrixElements(&graphics->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
 }
 
-GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
-{
-    return GdipEndContainer(graphics, state);
-}
-
 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
     GpMatrixOrder order)
 {
+    GpStatus stat;
+
     TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
 
     if(!graphics)
@@ -5144,45 +5559,119 @@ GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
     if(graphics->busy)
         return ObjectBusy;
 
-    return GdipRotateMatrix(&graphics->worldtrans, angle, order);
-}
+    if (graphics->image && graphics->image->type == ImageTypeMetafile) {
+        stat = METAFILE_RotateWorldTransform((GpMetafile*)graphics->image, angle, order);
 
-GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
-{
-    return GdipBeginContainer2(graphics, state);
+        if (stat != Ok)
+            return stat;
+    }
+
+    return GdipRotateMatrix(&graphics->worldtrans, angle, order);
 }
 
-GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics,
-        GraphicsContainer *state)
+static GpStatus begin_container(GpGraphics *graphics,
+    GraphicsContainerType type, GraphicsContainer *state)
 {
     GraphicsContainerItem *container;
     GpStatus sts;
 
-    TRACE("(%p, %p)\n", graphics, state);
-
     if(!graphics || !state)
         return InvalidParameter;
 
-    sts = init_container(&container, graphics);
+    sts = init_container(&container, graphics, type);
     if(sts != Ok)
         return sts;
 
     list_add_head(&graphics->containers, &container->entry);
     *state = graphics->contid = container->contid;
 
+    if (graphics->image && graphics->image->type == ImageTypeMetafile) {
+        if (type == BEGIN_CONTAINER)
+            METAFILE_BeginContainerNoParams((GpMetafile*)graphics->image, container->contid);
+        else
+            METAFILE_SaveGraphics((GpMetafile*)graphics->image, container->contid);
+    }
+
     return Ok;
 }
 
+GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
+{
+    TRACE("(%p, %p)\n", graphics, state);
+    return begin_container(graphics, SAVE_GRAPHICS, state);
+}
+
+GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics,
+        GraphicsContainer *state)
+{
+    TRACE("(%p, %p)\n", graphics, state);
+    return begin_container(graphics, BEGIN_CONTAINER, state);
+}
+
 GpStatus WINGDIPAPI GdipBeginContainer(GpGraphics *graphics, GDIPCONST GpRectF *dstrect, GDIPCONST GpRectF *srcrect, GpUnit unit, GraphicsContainer *state)
 {
-    FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
-    return NotImplemented;
+    GraphicsContainerItem *container;
+    GpMatrix transform;
+    GpStatus stat;
+    GpRectF scaled_srcrect;
+    REAL scale_x, scale_y;
+
+    TRACE("(%p, %s, %s, %d, %p)\n", graphics, debugstr_rectf(dstrect), debugstr_rectf(srcrect), unit, state);
+
+    if(!graphics || !dstrect || !srcrect || unit < UnitPixel || unit > UnitMillimeter || !state)
+        return InvalidParameter;
+
+    stat = init_container(&container, graphics, BEGIN_CONTAINER);
+    if(stat != Ok)
+        return stat;
+
+    list_add_head(&graphics->containers, &container->entry);
+    *state = graphics->contid = container->contid;
+
+    scale_x = units_to_pixels(1.0, unit, graphics->xres);
+    scale_y = units_to_pixels(1.0, unit, graphics->yres);
+
+    scaled_srcrect.X = scale_x * srcrect->X;
+    scaled_srcrect.Y = scale_y * srcrect->Y;
+    scaled_srcrect.Width = scale_x * srcrect->Width;
+    scaled_srcrect.Height = scale_y * srcrect->Height;
+
+    transform.matrix[0] = dstrect->Width / scaled_srcrect.Width;
+    transform.matrix[1] = 0.0;
+    transform.matrix[2] = 0.0;
+    transform.matrix[3] = dstrect->Height / scaled_srcrect.Height;
+    transform.matrix[4] = dstrect->X - scaled_srcrect.X;
+    transform.matrix[5] = dstrect->Y - scaled_srcrect.Y;
+
+    GdipMultiplyMatrix(&graphics->worldtrans, &transform, MatrixOrderPrepend);
+
+    if (graphics->image && graphics->image->type == ImageTypeMetafile) {
+        METAFILE_BeginContainer((GpMetafile*)graphics->image, dstrect, srcrect, unit, container->contid);
+    }
+
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipBeginContainerI(GpGraphics *graphics, GDIPCONST GpRect *dstrect, GDIPCONST GpRect *srcrect, GpUnit unit, GraphicsContainer *state)
 {
-    FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
-    return NotImplemented;
+    GpRectF dstrectf, srcrectf;
+
+    TRACE("(%p, %p, %p, %d, %p)\n", graphics, dstrect, srcrect, unit, state);
+
+    if (!dstrect || !srcrect)
+        return InvalidParameter;
+
+    dstrectf.X = dstrect->X;
+    dstrectf.Y = dstrect->Y;
+    dstrectf.Width = dstrect->Width;
+    dstrectf.Height = dstrect->Height;
+
+    srcrectf.X = srcrect->X;
+    srcrectf.Y = srcrect->Y;
+    srcrectf.Width = srcrect->Width;
+    srcrectf.Height = srcrect->Height;
+
+    return GdipBeginContainer(graphics, &dstrectf, &srcrectf, unit, state);
 }
 
 GpStatus WINGDIPAPI GdipComment(GpGraphics *graphics, UINT sizeData, GDIPCONST BYTE *data)
@@ -5191,18 +5680,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;
     }
 
@@ -5225,9 +5713,28 @@ 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)
 {
@@ -5459,6 +5966,8 @@ GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
 
 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
 {
+    GpStatus stat;
+
     TRACE("(%p, %p)\n", graphics, matrix);
 
     if(!graphics || !matrix)
@@ -5471,6 +5980,13 @@ GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix
           matrix->matrix[0], matrix->matrix[1], matrix->matrix[2],
           matrix->matrix[3], matrix->matrix[4], matrix->matrix[5]);
 
+    if (graphics->image && graphics->image->type == ImageTypeMetafile) {
+        stat = METAFILE_SetWorldTransform((GpMetafile*)graphics->image, matrix);
+
+        if (stat != Ok)
+            return stat;
+    }
+
     graphics->worldtrans = *matrix;
 
     return Ok;
@@ -5479,6 +5995,8 @@ GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix
 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
     REAL dy, GpMatrixOrder order)
 {
+    GpStatus stat;
+
     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
 
     if(!graphics)
@@ -5487,6 +6005,13 @@ GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
     if(graphics->busy)
         return ObjectBusy;
 
+    if (graphics->image && graphics->image->type == ImageTypeMetafile) {
+        stat = METAFILE_TranslateWorldTransform((GpMetafile*)graphics->image, dx, dy, order);
+
+        if (stat != Ok)
+            return stat;
+    }
+
     return GdipTranslateMatrix(&graphics->worldtrans, dx, dy, order);
 }
 
@@ -5562,6 +6087,13 @@ GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
     if(graphics->busy)
         return ObjectBusy;
 
+    if (graphics->image && graphics->image->type == ImageTypeMetafile)
+    {
+        status = METAFILE_SetClipRect((GpMetafile*)graphics->image, x, y, width, height, mode);
+        if (status != Ok)
+            return status;
+    }
+
     rect.X = x;
     rect.Y = y;
     rect.Width  = width;
@@ -5628,8 +6160,8 @@ GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
     INT count)
 {
-    INT save_state;
-    POINT *pti;
+    GpStatus status;
+    GpPath* path;
 
     TRACE("(%p, %p, %d)\n", graphics, points, count);
 
@@ -5639,24 +6171,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 = heap_alloc_zero(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);
-    heap_free(pti);
+    GdipDeletePath(path);
 
-    return Ok;
+    return status;
 }
 
 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
@@ -5724,6 +6248,13 @@ GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST G
     if(graphics->busy)
         return ObjectBusy;
 
+    if (graphics->image && graphics->image->type == ImageTypeMetafile) {
+        ret = METAFILE_MultiplyWorldTransform((GpMetafile*)graphics->image, matrix, order);
+
+        if (ret != Ok)
+            return ret;
+    }
+
     m = graphics->worldtrans;
 
     ret = GdipMultiplyMatrix(&m, matrix, order);
@@ -5896,7 +6427,7 @@ GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
     return Ok;
 }
 
-static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
+GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
         GpCoordinateSpace src_space, GpMatrix *matrix)
 {
     GpStatus stat = Ok;
@@ -6176,6 +6707,8 @@ static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT1
     GpPointF pt;
     HFONT hfont;
     UINT eto_flags=0;
+    GpStatus status;
+    HRGN hrgn;
 
     if (flags & unsupported_flags)
         FIXME("Ignoring flags %x\n", flags & unsupported_flags);
@@ -6187,6 +6720,14 @@ static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT1
     SetBkMode(graphics->hdc, TRANSPARENT);
     SetTextColor(graphics->hdc, get_gdi_brush_color(brush));
 
+    status = get_clip_hrgn(graphics, &hrgn);
+
+    if (status == Ok && hrgn)
+    {
+        ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
+        DeleteObject(hrgn);
+    }
+
     pt = positions[0];
     GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &pt, 1);