[GDIPLUS_WINETEST] Sync with Wine Staging 1.9.23. CORE-12409
authorAmine Khaldi <amine.khaldi@reactos.org>
Thu, 17 Nov 2016 22:37:09 +0000 (22:37 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Thu, 17 Nov 2016 22:37:09 +0000 (22:37 +0000)
svn path=/trunk/; revision=73271

rostests/winetests/gdiplus/graphics.c
rostests/winetests/gdiplus/graphicspath.c
rostests/winetests/gdiplus/image.c
rostests/winetests/gdiplus/matrix.c
rostests/winetests/gdiplus/metafile.c

index 7d308d7..1ba522e 100644 (file)
@@ -332,6 +332,64 @@ static void test_save_restore(void)
 
     log_state(state_a, &state_log);
 
 
     log_state(state_a, &state_log);
 
+    /* A state created by SaveGraphics cannot be restored with EndContainer. */
+    GdipCreateFromHDC(hdc, &graphics1);
+    GdipSetInterpolationMode(graphics1, InterpolationModeBilinear);
+    stat = GdipSaveGraphics(graphics1, &state_a);
+    expect(Ok, stat);
+    GdipSetInterpolationMode(graphics1, InterpolationModeBicubic);
+    stat = GdipEndContainer(graphics1, state_a);
+    expect(Ok, stat);
+    GdipGetInterpolationMode(graphics1, &mode);
+    expect(InterpolationModeBicubic, mode);
+    stat = GdipRestoreGraphics(graphics1, state_a);
+    expect(Ok, stat);
+    GdipGetInterpolationMode(graphics1, &mode);
+    expect(InterpolationModeBilinear, mode);
+    GdipDeleteGraphics(graphics1);
+
+    log_state(state_a, &state_log);
+
+    /* A state created by BeginContainer cannot be restored with RestoreGraphics. */
+    GdipCreateFromHDC(hdc, &graphics1);
+    GdipSetInterpolationMode(graphics1, InterpolationModeBilinear);
+    stat = GdipBeginContainer2(graphics1, &state_a);
+    expect(Ok, stat);
+    GdipSetInterpolationMode(graphics1, InterpolationModeBicubic);
+    stat = GdipRestoreGraphics(graphics1, state_a);
+    expect(Ok, stat);
+    GdipGetInterpolationMode(graphics1, &mode);
+    expect(InterpolationModeBicubic, mode);
+    stat = GdipEndContainer(graphics1, state_a);
+    expect(Ok, stat);
+    GdipGetInterpolationMode(graphics1, &mode);
+    expect(InterpolationModeBilinear, mode);
+    GdipDeleteGraphics(graphics1);
+
+    log_state(state_a, &state_log);
+
+    /* BeginContainer and SaveGraphics use the same stack. */
+    GdipCreateFromHDC(hdc, &graphics1);
+    GdipSetInterpolationMode(graphics1, InterpolationModeBilinear);
+    stat = GdipBeginContainer2(graphics1, &state_a);
+    expect(Ok, stat);
+    GdipSetInterpolationMode(graphics1, InterpolationModeBicubic);
+    stat = GdipSaveGraphics(graphics1, &state_b);
+    expect(Ok, stat);
+    GdipSetInterpolationMode(graphics1, InterpolationModeNearestNeighbor);
+    stat = GdipEndContainer(graphics1, state_a);
+    expect(Ok, stat);
+    GdipGetInterpolationMode(graphics1, &mode);
+    expect(InterpolationModeBilinear, mode);
+    stat = GdipRestoreGraphics(graphics1, state_b);
+    expect(Ok, stat);
+    GdipGetInterpolationMode(graphics1, &mode);
+    expect(InterpolationModeBilinear, mode);
+    GdipDeleteGraphics(graphics1);
+
+    log_state(state_a, &state_log);
+    log_state(state_b, &state_log);
+
     /* The same state value should never be returned twice. */
     todo_wine
         check_no_duplicates(state_log);
     /* The same state value should never be returned twice. */
     todo_wine
         check_no_duplicates(state_log);
@@ -3847,15 +3905,18 @@ static void test_pen_thickness(void)
         REAL res_x, res_y, scale;
         GpUnit pen_unit, page_unit;
         REAL pen_width;
         REAL res_x, res_y, scale;
         GpUnit pen_unit, page_unit;
         REAL pen_width;
-        INT cx, cy;
+        INT cx, cy, path_cx, path_cy;
     } td[] =
     {
     } td[] =
     {
-        { 10.0, 10.0, 1.0, UnitPixel, UnitPixel, 1.0, 1, 1 },
-        { 10.0, 10.0, 3.0, UnitPixel, UnitPixel, 2.0, 2, 2 },
-        { 10.0, 10.0, 30.0, UnitPixel, UnitInch, 1.0, 1, 1 },
-        { 10.0, 10.0, 1.0, UnitWorld, UnitPixel, 1.0, 1, 1 },
-        { 10.0, 10.0, 3.0, UnitWorld, UnitPixel, 2.0, 6, 6 },
-        { 10.0, 10.0, 2.0, UnitWorld, UnitInch, 1.0, 20, 20 },
+        { 10.0, 10.0, 1.0, UnitPixel, UnitPixel, 1.0, 1, 1, 1, 1 },
+        { 10.0, 10.0, 1.0, UnitPixel, UnitPixel, 0.0, 0, 0, 1, 1 },
+        { 10.0, 10.0, 1.0, UnitPixel, UnitPixel, 0.1, 1, 1, 1, 1 },
+        { 10.0, 10.0, 3.0, UnitPixel, UnitPixel, 2.0, 2, 2, 2, 2 },
+        { 10.0, 10.0, 30.0, UnitPixel, UnitInch, 1.0, 1, 1, 1, 1 },
+        { 10.0, 10.0, 1.0, UnitWorld, UnitPixel, 1.0, 1, 1, 1, 1 },
+        { 10.0, 10.0, 1.0, UnitWorld, UnitPixel, 0.0, 1, 1, 1, 1 },
+        { 10.0, 10.0, 3.0, UnitWorld, UnitPixel, 2.0, 6, 6, 6, 6 },
+        { 10.0, 10.0, 2.0, UnitWorld, UnitInch, 1.0, 20, 20, 20, 20 },
     };
     GpStatus status;
     int i, j;
     };
     GpStatus status;
     int i, j;
@@ -3867,6 +3928,7 @@ static void test_pen_thickness(void)
     } u;
     GpPen *pen;
     GpPointF corner;
     } u;
     GpPen *pen;
     GpPointF corner;
+    GpPath *path;
     BitmapData bd;
     INT min, max, size;
 
     BitmapData bd;
     INT min, max, size;
 
@@ -3956,6 +4018,82 @@ static void test_pen_thickness(void)
         status = GdipBitmapUnlockBits(u.bitmap, &bd);
         expect(Ok, status);
 
         status = GdipBitmapUnlockBits(u.bitmap, &bd);
         expect(Ok, status);
 
+        status = GdipGraphicsClear(graphics, 0xff000000);
+        expect(Ok, status);
+
+        status = GdipCreatePath(FillModeAlternate, &path);
+        expect(Ok, status);
+
+        status = GdipAddPathLine(path, corner.X/2, 0, corner.X/2, corner.Y);
+        expect(Ok, status);
+
+        status = GdipClosePathFigure(path);
+        expect(Ok, status);
+
+        status = GdipAddPathLine(path, 0, corner.Y/2, corner.X, corner.Y/2);
+        expect(Ok, status);
+
+        status = GdipDrawPath(graphics, pen, path);
+        expect(Ok, status);
+
+        GdipDeletePath(path);
+
+        status = GdipBitmapLockBits(u.bitmap, NULL, ImageLockModeRead, PixelFormat24bppRGB, &bd);
+        expect(Ok, status);
+
+        min = -1;
+        max = -2;
+
+        for (j=0; j<100; j++)
+        {
+            if (((BYTE*)bd.Scan0)[j*3] == 0xff)
+            {
+                min = j;
+                break;
+            }
+        }
+
+        for (j=99; j>=0; j--)
+        {
+            if (((BYTE*)bd.Scan0)[j*3] == 0xff)
+            {
+                max = j;
+                break;
+            }
+        }
+
+        size = max-min+1;
+
+        ok(size == td[i].path_cx, "%u: expected %d, got %d\n", i, td[i].path_cx, size);
+
+        min = -1;
+        max = -2;
+
+        for (j=0; j<100; j++)
+        {
+            if (((BYTE*)bd.Scan0)[bd.Stride*j] == 0xff)
+            {
+                min = j;
+                break;
+            }
+        }
+
+        for (j=99; j>=0; j--)
+        {
+            if (((BYTE*)bd.Scan0)[bd.Stride*j] == 0xff)
+            {
+                max = j;
+                break;
+            }
+        }
+
+        size = max-min+1;
+
+        ok(size == td[i].path_cy, "%u: expected %d, got %d\n", i, td[i].path_cy, size);
+
+        status = GdipBitmapUnlockBits(u.bitmap, &bd);
+        expect(Ok, status);
+
         GdipDeletePen(pen);
         GdipDeleteGraphics(graphics);
         GdipDisposeImage(u.image);
         GdipDeletePen(pen);
         GdipDeleteGraphics(graphics);
         GdipDisposeImage(u.image);
@@ -5845,6 +5983,178 @@ static void test_GdipGetVisibleClipBounds_memoryDC(void)
     ReleaseDC(hwnd, dc);
 }
 
     ReleaseDC(hwnd, dc);
 }
 
+static void test_container_rects(void)
+{
+    GpStatus status;
+    GpGraphics *graphics;
+    HDC hdc = GetDC( hwnd );
+    GpRectF dstrect, srcrect;
+    GraphicsContainer state;
+    static const GpPointF test_points[3] = {{0.0,0.0}, {1.0,0.0}, {0.0,1.0}};
+    GpPointF points[3];
+    REAL dpix, dpiy;
+
+    status = GdipCreateFromHDC(hdc, &graphics);
+    expect(Ok, status);
+
+    dstrect.X = 0.0;
+    dstrect.Y = 0.0;
+    dstrect.Width = 1.0;
+    dstrect.Height = 1.0;
+    srcrect = dstrect;
+
+    status = GdipGetDpiX(graphics, &dpix);
+    expect(Ok, status);
+
+    status = GdipGetDpiY(graphics, &dpiy);
+    expect(Ok, status);
+
+    status = GdipBeginContainer(graphics, &dstrect, &srcrect, UnitWorld, &state);
+    expect(InvalidParameter, status);
+
+    status = GdipBeginContainer(graphics, &dstrect, &srcrect, UnitDisplay, &state);
+    expect(InvalidParameter, status);
+
+    status = GdipBeginContainer(graphics, &dstrect, &srcrect, UnitMillimeter+1, &state);
+    expect(InvalidParameter, status);
+
+    status = GdipBeginContainer(NULL, &dstrect, &srcrect, UnitPixel, &state);
+    expect(InvalidParameter, status);
+
+    status = GdipBeginContainer(graphics, NULL, &srcrect, UnitPixel, &state);
+    expect(InvalidParameter, status);
+
+    status = GdipBeginContainer(graphics, &dstrect, NULL, UnitPixel, &state);
+    expect(InvalidParameter, status);
+
+    status = GdipBeginContainer(graphics, &dstrect, &srcrect, -1, &state);
+    expect(InvalidParameter, status);
+
+    status = GdipBeginContainer(graphics, &dstrect, &srcrect, UnitPixel, NULL);
+    expect(InvalidParameter, status);
+
+    status = GdipBeginContainer(graphics, &dstrect, &srcrect, UnitPixel, &state);
+    expect(Ok, status);
+
+    memcpy(points, test_points, sizeof(points));
+    status = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, points, 3);
+    expect(Ok, status);
+    expectf(0.0, points[0].X);
+    expectf(0.0, points[0].Y);
+    expectf(1.0, points[1].X);
+    expectf(0.0, points[1].Y);
+    expectf(0.0, points[2].X);
+    expectf(1.0, points[2].Y);
+
+    status = GdipEndContainer(graphics, state);
+    expect(Ok, status);
+
+    status = GdipBeginContainer(graphics, &dstrect, &srcrect, UnitInch, &state);
+    expect(Ok, status);
+
+    memcpy(points, test_points, sizeof(points));
+    status = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, points, 3);
+    expect(Ok, status);
+    expectf(0.0, points[0].X);
+    expectf(0.0, points[0].Y);
+    expectf(1.0/dpix, points[1].X);
+    expectf(0.0, points[1].Y);
+    expectf(0.0, points[2].X);
+    expectf(1.0/dpiy, points[2].Y);
+
+    status = GdipEndContainer(graphics, state);
+    expect(Ok, status);
+
+    status = GdipScaleWorldTransform(graphics, 2.0, 2.0, MatrixOrderPrepend);
+    expect(Ok, status);
+
+    dstrect.X = 1.0;
+    dstrect.Height = 3.0;
+    status = GdipBeginContainer(graphics, &dstrect, &srcrect, UnitPixel, &state);
+    expect(Ok, status);
+
+    memcpy(points, test_points, sizeof(points));
+    status = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, points, 3);
+    expect(Ok, status);
+    expectf(2.0, points[0].X);
+    expectf(0.0, points[0].Y);
+    expectf(4.0, points[1].X);
+    expectf(0.0, points[1].Y);
+    expectf(2.0, points[2].X);
+    expectf(6.0, points[2].Y);
+
+    status = GdipEndContainer(graphics, state);
+    expect(Ok, status);
+
+    memcpy(points, test_points, sizeof(points));
+    status = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, points, 3);
+    expect(Ok, status);
+    expectf(0.0, points[0].X);
+    expectf(0.0, points[0].Y);
+    expectf(2.0, points[1].X);
+    expectf(0.0, points[1].Y);
+    expectf(0.0, points[2].X);
+    expectf(2.0, points[2].Y);
+
+    status = GdipResetWorldTransform(graphics);
+    expect(Ok, status);
+
+    status = GdipBeginContainer(graphics, &dstrect, &srcrect, UnitInch, &state);
+    expect(Ok, status);
+
+    memcpy(points, test_points, sizeof(points));
+    status = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, points, 3);
+    expect(Ok, status);
+    expectf(1.0, points[0].X);
+    expectf(0.0, points[0].Y);
+    expectf((dpix+1.0)/dpix, points[1].X);
+    expectf(0.0, points[1].Y);
+    expectf(1.0, points[2].X);
+    expectf(3.0/dpiy, points[2].Y);
+
+    status = GdipEndContainer(graphics, state);
+    expect(Ok, status);
+
+    status = GdipSetPageUnit(graphics, UnitInch);
+    expect(Ok, status);
+
+    status = GdipBeginContainer(graphics, &dstrect, &srcrect, UnitPixel, &state);
+    expect(Ok, status);
+
+    memcpy(points, test_points, sizeof(points));
+    status = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, points, 3);
+    expect(Ok, status);
+    expectf(dpix, points[0].X);
+    expectf(0.0, points[0].Y);
+    expectf(dpix*2, points[1].X);
+    expectf(0.0, points[1].Y);
+    expectf(dpix, points[2].X);
+    expectf(dpiy*3, points[2].Y);
+
+    status = GdipEndContainer(graphics, state);
+    expect(Ok, status);
+
+    status = GdipBeginContainer(graphics, &dstrect, &srcrect, UnitInch, &state);
+    expect(Ok, status);
+
+    memcpy(points, test_points, sizeof(points));
+    status = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, points, 3);
+    expect(Ok, status);
+    expectf(dpix, points[0].X);
+    expectf(0.0, points[0].Y);
+    expectf(dpix+1.0, points[1].X);
+    expectf(0.0, points[1].Y);
+    expectf(dpix, points[2].X);
+    expectf(3.0, points[2].Y);
+
+    status = GdipEndContainer(graphics, state);
+    expect(Ok, status);
+
+    GdipDeleteGraphics(graphics);
+
+    ReleaseDC(hwnd, hdc);
+}
+
 START_TEST(graphics)
 {
     struct GdiplusStartupInput gdiplusStartupInput;
 START_TEST(graphics)
 {
     struct GdiplusStartupInput gdiplusStartupInput;
@@ -5918,6 +6228,7 @@ START_TEST(graphics)
     test_bitmapfromgraphics();
     test_GdipFillRectangles();
     test_GdipGetVisibleClipBounds_memoryDC();
     test_bitmapfromgraphics();
     test_GdipFillRectangles();
     test_GdipGetVisibleClipBounds_memoryDC();
+    test_container_rects();
 
     GdiplusShutdown(gdiplusToken);
     DestroyWindow( hwnd );
 
     GdiplusShutdown(gdiplusToken);
     DestroyWindow( hwnd );
index c28c627..25aea41 100644 (file)
@@ -1077,6 +1077,7 @@ static void test_widen(void)
     GpPath *path;
     GpPen *pen;
     GpMatrix *m;
     GpPath *path;
     GpPen *pen;
     GpMatrix *m;
+    INT count=-1;
 
     status = GdipCreatePath(FillModeAlternate, &path);
     expect(Ok, status);
 
     status = GdipCreatePath(FillModeAlternate, &path);
     expect(Ok, status);
@@ -1201,6 +1202,23 @@ static void test_widen(void)
     expect(Ok, status);
     ok_path(path, widenline_path, sizeof(widenline_path)/sizeof(path_test_t), FALSE);
 
     expect(Ok, status);
     ok_path(path, widenline_path, sizeof(widenline_path)/sizeof(path_test_t), FALSE);
 
+    /* pen width = 0 pixels - native fails to widen but can draw with this pen */
+    GdipDeletePen(pen);
+    status = GdipCreatePen1(0xffffffff, 0.0, UnitPixel, &pen);
+    expect(Ok, status);
+
+    status = GdipResetPath(path);
+    expect(Ok, status);
+    status = GdipAddPathLine(path, 5.0, 10.0, 50.0, 10.0);
+    expect(Ok, status);
+
+    status = GdipWidenPath(path, pen, m, 1.0);
+    expect(Ok, status);
+
+    status = GdipGetPointCount(path, &count);
+    expect(Ok, status);
+    todo_wine expect(0, count);
+
     GdipDeleteMatrix(m);
     GdipDeletePen(pen);
     GdipDeletePath(path);
     GdipDeleteMatrix(m);
     GdipDeletePen(pen);
     GdipDeletePath(path);
index d0ebea8..60b8111 100644 (file)
@@ -50,6 +50,12 @@ DEFINE_GUID(ColorBalanceEffectGuid, 0x537e597d, 0x251e, 0x48da, 0x96, 0x64, 0x29
 DEFINE_GUID(RedEyeCorrectionEffectGuid, 0x74d29d05, 0x69a4, 0x4266, 0x95, 0x49, 0x3c, 0xc5, 0x28, 0x36, 0xb6, 0x32);
 DEFINE_GUID(ColorCurveEffectGuid, 0xdd6a0022, 0x58e4, 0x4a67, 0x9d, 0x9b, 0xd4, 0x8e, 0xb8, 0x81, 0xa5, 0x3d);
 
 DEFINE_GUID(RedEyeCorrectionEffectGuid, 0x74d29d05, 0x69a4, 0x4266, 0x95, 0x49, 0x3c, 0xc5, 0x28, 0x36, 0xb6, 0x32);
 DEFINE_GUID(ColorCurveEffectGuid, 0xdd6a0022, 0x58e4, 0x4a67, 0x9d, 0x9b, 0xd4, 0x8e, 0xb8, 0x81, 0xa5, 0x3d);
 
+static GpStatus (WINAPI *pGdipBitmapGetHistogramSize)(HistogramFormat,UINT*);
+static GpStatus (WINAPI *pGdipBitmapGetHistogram)(GpBitmap*,HistogramFormat,UINT,UINT*,UINT*,UINT*,UINT*);
+static GpStatus (WINAPI *pGdipImageSetAbort)(GpImage*,GdiplusAbort*);
+
+static GpStatus (WINGDIPAPI *pGdipInitializePalette)(ColorPalette*,PaletteType,INT,BOOL,GpBitmap*);
+
 #define expect(expected, got) ok((got) == (expected), "Expected %d, got %d\n", (UINT)(expected), (UINT)(got))
 #define expectf(expected, got) ok(fabs((expected) - (got)) < 0.0001, "Expected %f, got %f\n", (expected), (got))
 
 #define expect(expected, got) ok((got) == (expected), "Expected %d, got %d\n", (UINT)(expected), (UINT)(got))
 #define expectf(expected, got) ok(fabs((expected) - (got)) < 0.0001, "Expected %f, got %f\n", (expected), (got))
 
@@ -4657,14 +4663,13 @@ static void test_supported_encoders(void)
     {
         LPCWSTR mime;
         const GUID *format;
     {
         LPCWSTR mime;
         const GUID *format;
-        BOOL todo;
     } td[] =
     {
     } td[] =
     {
-        { bmp_mimetype, &ImageFormatBMP, FALSE },
-        { jpeg_mimetype, &ImageFormatJPEG, FALSE },
-        { gif_mimetype, &ImageFormatGIF, TRUE },
-        { tiff_mimetype, &ImageFormatTIFF, FALSE },
-        { png_mimetype, &ImageFormatPNG, FALSE }
+        { bmp_mimetype, &ImageFormatBMP },
+        { jpeg_mimetype, &ImageFormatJPEG },
+        { gif_mimetype, &ImageFormatGIF },
+        { tiff_mimetype, &ImageFormatTIFF },
+        { png_mimetype, &ImageFormatPNG }
     };
     GUID format, clsid;
     BOOL ret;
     };
     GUID format, clsid;
     BOOL ret;
@@ -4690,8 +4695,7 @@ static void test_supported_encoders(void)
         ok(hr == S_OK, "CreateStreamOnHGlobal error %#x\n", hr);
 
         status = GdipSaveImageToStream((GpImage *)bm, stream, &clsid, NULL);
         ok(hr == S_OK, "CreateStreamOnHGlobal error %#x\n", hr);
 
         status = GdipSaveImageToStream((GpImage *)bm, stream, &clsid, NULL);
-        todo_wine_if (td[i].todo)
-            ok(status == Ok, "GdipSaveImageToStream error %d\n", status);
+        ok(status == Ok, "GdipSaveImageToStream error %d\n", status);
 
         IStream_Release(stream);
     }
 
         IStream_Release(stream);
     }
@@ -4805,6 +4809,158 @@ static void test_getadjustedpalette(void)
     GdipDisposeImageAttributes(imageattributes);
 }
 
     GdipDisposeImageAttributes(imageattributes);
 }
 
+static void test_histogram(void)
+{
+    UINT ch0[256], ch1[256], ch2[256], ch3[256];
+    HistogramFormat test_formats[] =
+    {
+        HistogramFormatARGB,
+        HistogramFormatPARGB,
+        HistogramFormatRGB,
+        HistogramFormatGray,
+        HistogramFormatB,
+        HistogramFormatG,
+        HistogramFormatR,
+        HistogramFormatA,
+    };
+    const UINT WIDTH = 8, HEIGHT = 16;
+    UINT num, i, x;
+    GpStatus stat;
+    GpBitmap *bm;
+
+    if (!pGdipBitmapGetHistogramSize)
+    {
+        win_skip("GdipBitmapGetHistogramSize is not supported\n");
+        return;
+    }
+
+    stat = pGdipBitmapGetHistogramSize(HistogramFormatARGB, NULL);
+    expect(InvalidParameter, stat);
+
+    stat = pGdipBitmapGetHistogramSize(0xff, NULL);
+    expect(InvalidParameter, stat);
+
+    num = 123;
+    stat = pGdipBitmapGetHistogramSize(10, &num);
+    expect(Ok, stat);
+    expect(256, num);
+
+    for (i = 0; i < sizeof(test_formats)/sizeof(test_formats[0]); i++)
+    {
+        num = 0;
+        stat = pGdipBitmapGetHistogramSize(test_formats[i], &num);
+        expect(Ok, stat);
+        expect(256, num);
+    }
+
+    bm = NULL;
+    stat = GdipCreateBitmapFromScan0(WIDTH, HEIGHT, 0, PixelFormat24bppRGB, NULL, &bm);
+    expect(Ok, stat);
+
+    /* Three solid rgb rows, next three rows are rgb shades. */
+    for (x = 0; x < WIDTH; x++)
+    {
+        GdipBitmapSetPixel(bm, x, 0, 0xffff0000);
+        GdipBitmapSetPixel(bm, x, 1, 0xff00ff00);
+        GdipBitmapSetPixel(bm, x, 2, 0xff0000ff);
+
+        GdipBitmapSetPixel(bm, x, 3, 0xff010000);
+        GdipBitmapSetPixel(bm, x, 4, 0xff003f00);
+        GdipBitmapSetPixel(bm, x, 5, 0xff000020);
+    }
+
+    stat = pGdipBitmapGetHistogram(NULL, HistogramFormatRGB, 256, ch0, ch1, ch2, ch3);
+    expect(InvalidParameter, stat);
+
+    stat = pGdipBitmapGetHistogram(bm, 123, 256, ch0, ch1, ch2, ch3);
+    expect(InvalidParameter, stat);
+
+    stat = pGdipBitmapGetHistogram(bm, 123, 256, ch0, ch1, ch2, NULL);
+    expect(InvalidParameter, stat);
+
+    stat = pGdipBitmapGetHistogram(bm, 123, 256, ch0, ch1, NULL, NULL);
+    expect(InvalidParameter, stat);
+
+    stat = pGdipBitmapGetHistogram(bm, 123, 256, ch0, NULL, NULL, NULL);
+    expect(InvalidParameter, stat);
+
+    /* Requested format matches bitmap format */
+    stat = pGdipBitmapGetHistogram(bm, HistogramFormatRGB, 256, ch0, ch1, ch2, ch3);
+    expect(InvalidParameter, stat);
+
+    stat = pGdipBitmapGetHistogram(bm, HistogramFormatRGB, 100, ch0, ch1, ch2, NULL);
+    expect(InvalidParameter, stat);
+
+    stat = pGdipBitmapGetHistogram(bm, HistogramFormatRGB, 257, ch0, ch1, ch2, NULL);
+    expect(InvalidParameter, stat);
+
+    /* Channel 3 is not used, must be NULL */
+    stat = pGdipBitmapGetHistogram(bm, HistogramFormatRGB, 256, ch0, ch1, ch2, NULL);
+    expect(Ok, stat);
+
+    ok(ch0[0xff] == WIDTH, "Got red (0xff) %u\n", ch0[0xff]);
+    ok(ch1[0xff] == WIDTH, "Got green (0xff) %u\n", ch1[0xff]);
+    ok(ch2[0xff] == WIDTH, "Got blue (0xff) %u\n", ch1[0xff]);
+    ok(ch0[0x01] == WIDTH, "Got red (0x01) %u\n", ch0[0x01]);
+    ok(ch1[0x3f] == WIDTH, "Got green (0x3f) %u\n", ch1[0x3f]);
+    ok(ch2[0x20] == WIDTH, "Got blue (0x20) %u\n", ch1[0x20]);
+
+    /* ARGB histogram from RGB data. */
+    stat = pGdipBitmapGetHistogram(bm, HistogramFormatARGB, 256, ch0, ch1, ch2, NULL);
+    expect(InvalidParameter, stat);
+
+    stat = pGdipBitmapGetHistogram(bm, HistogramFormatARGB, 256, ch0, ch1, ch2, ch3);
+    expect(Ok, stat);
+
+    ok(ch1[0xff] == WIDTH, "Got red (0xff) %u\n", ch1[0xff]);
+    ok(ch2[0xff] == WIDTH, "Got green (0xff) %u\n", ch2[0xff]);
+    ok(ch3[0xff] == WIDTH, "Got blue (0xff) %u\n", ch3[0xff]);
+    ok(ch1[0x01] == WIDTH, "Got red (0x01) %u\n", ch1[0x01]);
+    ok(ch2[0x3f] == WIDTH, "Got green (0x3f) %u\n", ch2[0x3f]);
+    ok(ch3[0x20] == WIDTH, "Got blue (0x20) %u\n", ch3[0x20]);
+
+    ok(ch0[0xff] == WIDTH * HEIGHT, "Got alpha (0xff) %u\n", ch0[0xff]);
+
+    /* Request grayscale histogram from RGB bitmap. */
+    stat = pGdipBitmapGetHistogram(bm, HistogramFormatGray, 256, ch0, ch1, ch2, ch3);
+    expect(InvalidParameter, stat);
+
+    stat = pGdipBitmapGetHistogram(bm, HistogramFormatGray, 256, ch0, ch1, ch2, NULL);
+    expect(InvalidParameter, stat);
+
+    stat = pGdipBitmapGetHistogram(bm, HistogramFormatGray, 256, ch0, ch1, NULL, NULL);
+    expect(InvalidParameter, stat);
+
+    stat = pGdipBitmapGetHistogram(bm, HistogramFormatGray, 256, ch0, NULL, NULL, NULL);
+    expect(Ok, stat);
+
+    GdipDisposeImage((GpImage*)bm);
+}
+
+static void test_imageabort(void)
+{
+    GpStatus stat;
+    GpBitmap *bm;
+
+    if (!pGdipImageSetAbort)
+    {
+        win_skip("GdipImageSetAbort() is not supported.\n");
+        return;
+    }
+
+    bm = NULL;
+    stat = GdipCreateBitmapFromScan0(8, 8, 0, PixelFormat24bppRGB, NULL, &bm);
+    expect(Ok, stat);
+
+    stat = pGdipImageSetAbort(NULL, NULL);
+    expect(InvalidParameter, stat);
+
+    stat = pGdipImageSetAbort((GpImage*)bm, NULL);
+    expect(Ok, stat);
+
+    GdipDisposeImage((GpImage*)bm);
+}
+
 /* RGB 24 bpp 1x1 pixel PNG image */
 static const char png_1x1_data[] = {
   0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a,
 /* RGB 24 bpp 1x1 pixel PNG image */
 static const char png_1x1_data[] = {
   0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a,
@@ -4869,8 +5025,152 @@ static void test_png_color_formats(void)
     }
 }
 
     }
 }
 
+static BYTE *init_bitmap(UINT *width, UINT *height, UINT *stride)
+{
+    BYTE *src;
+    UINT i, j, scale;
+
+    *width = 256;
+    *height = 256;
+    *stride = (*width * 3 + 3) & ~3;
+    trace("width %d, height %d, stride %d\n", *width, *height, *stride);
+
+    src = HeapAlloc(GetProcessHeap(), 0, *stride * *height);
+
+    scale = 256 / *width;
+    if (!scale) scale = 1;
+
+    for (i = 0; i < *height; i++)
+    {
+        for (j = 0; j < *width; j++)
+        {
+            src[i * *stride + j*3 + 0] = scale * i;
+            src[i * *stride + j*3 + 1] = scale * (255 - (i+j)/2);
+            src[i * *stride + j*3 + 2] = scale * j;
+        }
+    }
+
+    return src;
+}
+
+static void test_GdipInitializePalette(void)
+{
+    GpStatus status;
+    BYTE *data;
+    GpBitmap *bitmap;
+    ColorPalette *palette;
+    UINT width, height, stride;
+
+    pGdipInitializePalette = (void *)GetProcAddress(GetModuleHandleA("gdiplus.dll"), "GdipInitializePalette");
+    if (!pGdipInitializePalette)
+    {
+        win_skip("GdipInitializePalette is not supported on this platform\n");
+        return;
+    }
+
+    data = init_bitmap(&width, &height, &stride);
+
+    status = GdipCreateBitmapFromScan0(width, height, stride, PixelFormat24bppRGB, data, &bitmap);
+    expect(Ok, status);
+
+    palette = GdipAlloc(sizeof(*palette) + sizeof(ARGB) * 255);
+
+    palette->Flags = 0;
+    palette->Count = 15;
+    status = pGdipInitializePalette(palette, PaletteTypeOptimal, 16, FALSE, bitmap);
+    expect(GenericError, status);
+
+    palette->Flags = 0;
+    palette->Count = 256;
+    status = pGdipInitializePalette(palette, PaletteTypeOptimal, 16, FALSE, NULL);
+    expect(InvalidParameter, status);
+
+    memset(palette->Entries, 0x11, sizeof(ARGB) * 256);
+    palette->Flags = 0;
+    palette->Count = 256;
+    status = pGdipInitializePalette(palette, PaletteTypeCustom, 16, FALSE, NULL);
+    expect(Ok, status);
+    expect(0, palette->Flags);
+    expect(256, palette->Count);
+    expect(0x11111111, palette->Entries[0]);
+    expect(0x11111111, palette->Entries[128]);
+    expect(0x11111111, palette->Entries[255]);
+
+    memset(palette->Entries, 0x11, sizeof(ARGB) * 256);
+    palette->Flags = 0;
+    palette->Count = 256;
+    status = pGdipInitializePalette(palette, PaletteTypeFixedBW, 0, FALSE, bitmap);
+    expect(Ok, status);
+todo_wine
+    expect(0x200, palette->Flags);
+    expect(2, palette->Count);
+    expect(0xff000000, palette->Entries[0]);
+    expect(0xffffffff, palette->Entries[1]);
+
+    memset(palette->Entries, 0x11, sizeof(ARGB) * 256);
+    palette->Flags = 0;
+    palette->Count = 256;
+    status = pGdipInitializePalette(palette, PaletteTypeFixedHalftone8, 1, FALSE, NULL);
+    expect(Ok, status);
+todo_wine
+    expect(0x300, palette->Flags);
+    expect(16, palette->Count);
+    expect(0xff000000, palette->Entries[0]);
+    expect(0xffc0c0c0, palette->Entries[8]);
+    expect(0xff008080, palette->Entries[15]);
+
+    memset(palette->Entries, 0x11, sizeof(ARGB) * 256);
+    palette->Flags = 0;
+    palette->Count = 256;
+    status = pGdipInitializePalette(palette, PaletteTypeFixedHalftone8, 1, FALSE, bitmap);
+    expect(Ok, status);
+todo_wine
+    expect(0x300, palette->Flags);
+    expect(16, palette->Count);
+    expect(0xff000000, palette->Entries[0]);
+    expect(0xffc0c0c0, palette->Entries[8]);
+    expect(0xff008080, palette->Entries[15]);
+
+    memset(palette->Entries, 0x11, sizeof(ARGB) * 256);
+    palette->Flags = 0;
+    palette->Count = 256;
+    status = pGdipInitializePalette(palette, PaletteTypeFixedHalftone252, 1, FALSE, bitmap);
+    expect(Ok, status);
+todo_wine
+    expect(0x800, palette->Flags);
+    expect(252, palette->Count);
+    expect(0xff000000, palette->Entries[0]);
+    expect(0xff990066, palette->Entries[128]);
+    expect(0xffffffff, palette->Entries[251]);
+
+    palette->Flags = 0;
+    palette->Count = 256;
+    status = pGdipInitializePalette(palette, PaletteTypeOptimal, 1, FALSE, bitmap);
+    expect(InvalidParameter, status);
+
+    palette->Flags = 0;
+    palette->Count = 256;
+    status = pGdipInitializePalette(palette, PaletteTypeOptimal, 2, FALSE, bitmap);
+    expect(Ok, status);
+    expect(0, palette->Flags);
+    expect(2, palette->Count);
+
+    palette->Flags = 0;
+    palette->Count = 256;
+    status = pGdipInitializePalette(palette, PaletteTypeOptimal, 16, FALSE, bitmap);
+    expect(Ok, status);
+    expect(0, palette->Flags);
+    expect(16, palette->Count);
+
+    /* passing invalid enumeration palette type crashes under most Windows versions */
+
+    GdipFree(palette);
+    GdipDisposeImage((GpImage *)bitmap);
+}
+
 START_TEST(image)
 {
 START_TEST(image)
 {
+    HMODULE mod = GetModuleHandleA("gdiplus.dll");
     struct GdiplusStartupInput gdiplusStartupInput;
     ULONG_PTR gdiplusToken;
 
     struct GdiplusStartupInput gdiplusStartupInput;
     ULONG_PTR gdiplusToken;
 
@@ -4881,6 +5181,11 @@ START_TEST(image)
 
     GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
 
 
     GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
 
+    pGdipBitmapGetHistogramSize = (void*)GetProcAddress(mod, "GdipBitmapGetHistogramSize");
+    pGdipBitmapGetHistogram = (void*)GetProcAddress(mod, "GdipBitmapGetHistogram");
+    pGdipImageSetAbort = (void*)GetProcAddress(mod, "GdipImageSetAbort");
+
+    test_GdipInitializePalette();
     test_png_color_formats();
     test_supported_encoders();
     test_CloneBitmapArea();
     test_png_color_formats();
     test_supported_encoders();
     test_CloneBitmapArea();
@@ -4928,6 +5233,8 @@ START_TEST(image)
     test_dispose();
     test_createeffect();
     test_getadjustedpalette();
     test_dispose();
     test_createeffect();
     test_getadjustedpalette();
+    test_histogram();
+    test_imageabort();
 
     GdiplusShutdown(gdiplusToken);
 }
 
     GdiplusShutdown(gdiplusToken);
 }
index db7551b..be82f18 100644 (file)
@@ -309,7 +309,42 @@ static void test_constructor3(void)
     expectf(0.0, values[4]);
     expectf(0.0, values[5]);
 
     expectf(0.0, values[4]);
     expectf(0.0, values[5]);
 
-    GdipDeleteMatrix(matrix);}
+    GdipDeleteMatrix(matrix);
+}
+
+static void test_isidentity(void)
+{
+    GpMatrix *matrix;
+    GpStatus stat;
+    BOOL result;
+
+    stat = GdipIsMatrixIdentity(NULL, NULL);
+    expect(InvalidParameter, stat);
+
+    stat = GdipIsMatrixIdentity(NULL, &result);
+    expect(InvalidParameter, stat);
+
+    stat = GdipCreateMatrix2(1.0, 0.0, 0.0, 1.0, 0.0, 0.0, &matrix);
+    expect(Ok, stat);
+
+    stat = GdipIsMatrixIdentity(matrix, NULL);
+    expect(InvalidParameter, stat);
+
+    result = FALSE;
+    stat = GdipIsMatrixIdentity(matrix, &result);
+    expect(Ok, stat);
+    ok(!!result, "got %d\n", result);
+
+    stat = GdipSetMatrixElements(matrix, 1.0, 0.0, 0.0, 1.0, 0.1, 0.0);
+    expect(Ok, stat);
+
+    result = TRUE;
+    stat = GdipIsMatrixIdentity(matrix, &result);
+    expect(Ok, stat);
+    ok(!result, "got %d\n", result);
+
+    GdipDeleteMatrix(matrix);
+}
 
 START_TEST(matrix)
 {
 
 START_TEST(matrix)
 {
@@ -329,6 +364,7 @@ START_TEST(matrix)
     test_invert();
     test_shear();
     test_constructor3();
     test_invert();
     test_shear();
     test_constructor3();
+    test_isidentity();
 
     GdiplusShutdown(gdiplusToken);
 }
 
     GdiplusShutdown(gdiplusToken);
 }
index c268d9e..b5351ef 100644 (file)
 #define expectf(expected, got) expectf_((expected), (got), 0.001)
 
 static BOOL save_metafiles;
 #define expectf(expected, got) expectf_((expected), (got), 0.001)
 
 static BOOL save_metafiles;
+static BOOL load_metafiles;
 
 typedef struct emfplus_record
 {
     BOOL  todo;
     ULONG record_type;
     BOOL  playback_todo;
 
 typedef struct emfplus_record
 {
     BOOL  todo;
     ULONG record_type;
     BOOL  playback_todo;
+    void (*playback_fn)(GpMetafile* metafile, EmfPlusRecordType record_type,
+        unsigned int flags, unsigned int dataSize, const unsigned char *pStr);
 } emfplus_record;
 
 typedef struct emfplus_check_state
 } emfplus_record;
 
 typedef struct emfplus_check_state
@@ -209,18 +212,29 @@ static BOOL CALLBACK play_metafile_proc(EmfPlusRecordType record_type, unsigned
     emfplus_check_state *state = (emfplus_check_state*)userdata;
     GpStatus stat;
 
     emfplus_check_state *state = (emfplus_check_state*)userdata;
     GpStatus stat;
 
-    stat = GdipPlayMetafileRecord(state->metafile, record_type, flags, dataSize, pStr);
-
     if (state->expected[state->count].record_type)
     {
     if (state->expected[state->count].record_type)
     {
-        todo_wine_if (state->expected[state->count].playback_todo)
-            ok(stat == Ok, "%s.%i: GdipPlayMetafileRecord failed with stat %i\n", state->desc, state->count, stat);
+        BOOL match = (state->expected[state->count].record_type == record_type);
+
+        if (match && state->expected[state->count].playback_fn)
+            state->expected[state->count].playback_fn(state->metafile, record_type, flags, dataSize, pStr);
+        else
+        {
+            stat = GdipPlayMetafileRecord(state->metafile, record_type, flags, dataSize, pStr);
+            todo_wine_if (state->expected[state->count].playback_todo)
+                ok(stat == Ok, "%s.%i: GdipPlayMetafileRecord failed with stat %i\n", state->desc, state->count, stat);
+        }
+
+        todo_wine_if (state->expected[state->count].todo)
+            ok(state->expected[state->count].record_type == record_type,
+                "%s.%i: expected record type 0x%x, got 0x%x\n", state->desc, state->count,
+                state->expected[state->count].record_type, record_type);
         state->count++;
     }
     else
     {
         todo_wine_if (state->expected[state->count].playback_todo)
         state->count++;
     }
     else
     {
         todo_wine_if (state->expected[state->count].playback_todo)
-            ok(0, "%s: too many records\n", state->desc);
+            ok(0, "%s: unexpected record 0x%x\n", state->desc, record_type);
 
         return FALSE;
     }
 
         return FALSE;
     }
@@ -244,15 +258,17 @@ static void play_metafile(GpMetafile *metafile, GpGraphics *graphics, const emfp
     expect(Ok, stat);
 }
 
     expect(Ok, stat);
 }
 
-static void save_metafile(GpMetafile *metafile, const char *filename)
+/* When 'save' or 'load' is specified on the command line, save or
+ * load the specified filename. */
+static void sync_metafile(GpMetafile **metafile, const char *filename)
 {
 {
+    GpStatus stat;
     if (save_metafiles)
     {
         GpMetafile *clone;
         HENHMETAFILE hemf;
     if (save_metafiles)
     {
         GpMetafile *clone;
         HENHMETAFILE hemf;
-        GpStatus stat;
 
 
-        stat = GdipCloneImage((GpImage*)metafile, (GpImage**)&clone);
+        stat = GdipCloneImage((GpImage*)*metafile, (GpImage**)&clone);
         expect(Ok, stat);
 
         stat = GdipGetHemfFromMetafile(clone, &hemf);
         expect(Ok, stat);
 
         stat = GdipGetHemfFromMetafile(clone, &hemf);
@@ -265,6 +281,20 @@ static void save_metafile(GpMetafile *metafile, const char *filename)
         stat = GdipDisposeImage((GpImage*)clone);
         expect(Ok, stat);
     }
         stat = GdipDisposeImage((GpImage*)clone);
         expect(Ok, stat);
     }
+    else if (load_metafiles)
+    {
+        HENHMETAFILE hemf;
+
+        stat = GdipDisposeImage((GpImage*)*metafile);
+        expect(Ok, stat);
+        *metafile = NULL;
+
+        hemf = GetEnhMetaFileA(filename);
+        ok(hemf != NULL, "%s could not be opened\n", filename);
+
+        stat = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
+        expect(Ok, stat);
+    }
 }
 
 static const emfplus_record empty_records[] = {
 }
 
 static const emfplus_record empty_records[] = {
@@ -332,7 +362,7 @@ static void test_empty(void)
 
     check_metafile(metafile, empty_records, "empty metafile", dst_points, &frame, UnitPixel);
 
 
     check_metafile(metafile, empty_records, "empty metafile", dst_points, &frame, UnitPixel);
 
-    save_metafile(metafile, "empty.emf");
+    sync_metafile(&metafile, "empty.emf");
 
     stat = GdipGetImageBounds((GpImage*)metafile, &bounds, &unit);
     expect(Ok, stat);
 
     stat = GdipGetImageBounds((GpImage*)metafile, &bounds, &unit);
     expect(Ok, stat);
@@ -348,6 +378,34 @@ static void test_empty(void)
     stat = GdipGetImageVerticalResolution((GpImage*)metafile, &yres);
     expect(Ok, stat);
 
     stat = GdipGetImageVerticalResolution((GpImage*)metafile, &yres);
     expect(Ok, stat);
 
+    memset(&header, 0xaa, sizeof(header));
+    stat = GdipGetMetafileHeaderFromMetafile(metafile, &header);
+    expect(Ok, stat);
+    expect(MetafileTypeEmfPlusOnly, header.Type);
+    expect(U(header).EmfHeader.nBytes, header.Size);
+    ok(header.Version == 0xdbc01001 || header.Version == 0xdbc01002, "Unexpected version %x\n", header.Version);
+    expect(1, header.EmfPlusFlags); /* reference device was display, not printer */
+    expectf(xres, header.DpiX);
+    expectf(xres, U(header).EmfHeader.szlDevice.cx / (REAL)U(header).EmfHeader.szlMillimeters.cx * 25.4);
+    expectf(yres, header.DpiY);
+    expectf(yres, U(header).EmfHeader.szlDevice.cy / (REAL)U(header).EmfHeader.szlMillimeters.cy * 25.4);
+    expect(0, header.X);
+    expect(0, header.Y);
+    expect(100, header.Width);
+    expect(100, header.Height);
+    expect(28, header.EmfPlusHeaderSize);
+    expect(96, header.LogicalDpiX);
+    expect(96, header.LogicalDpiX);
+    expect(EMR_HEADER, U(header).EmfHeader.iType);
+    expect(0, U(header).EmfHeader.rclBounds.left);
+    expect(0, U(header).EmfHeader.rclBounds.top);
+    expect(-1, U(header).EmfHeader.rclBounds.right);
+    expect(-1, U(header).EmfHeader.rclBounds.bottom);
+    expect(0, U(header).EmfHeader.rclFrame.left);
+    expect(0, U(header).EmfHeader.rclFrame.top);
+    expectf_(100.0, U(header).EmfHeader.rclFrame.right * xres / 2540.0, 2.0);
+    expectf_(100.0, U(header).EmfHeader.rclFrame.bottom * yres / 2540.0, 2.0);
+
     stat = GdipGetHemfFromMetafile(metafile, &hemf);
     expect(Ok, stat);
 
     stat = GdipGetHemfFromMetafile(metafile, &hemf);
     expect(Ok, stat);
 
@@ -406,6 +464,34 @@ static void test_empty(void)
     expect(Ok, stat);
     expectf(header.DpiY, yres);
 
     expect(Ok, stat);
     expectf(header.DpiY, yres);
 
+    memset(&header, 0xaa, sizeof(header));
+    stat = GdipGetMetafileHeaderFromMetafile(metafile, &header);
+    expect(Ok, stat);
+    expect(MetafileTypeEmfPlusOnly, header.Type);
+    expect(U(header).EmfHeader.nBytes, header.Size);
+    ok(header.Version == 0xdbc01001 || header.Version == 0xdbc01002, "Unexpected version %x\n", header.Version);
+    expect(1, header.EmfPlusFlags); /* reference device was display, not printer */
+    expectf(xres, header.DpiX);
+    expectf(xres, U(header).EmfHeader.szlDevice.cx / (REAL)U(header).EmfHeader.szlMillimeters.cx * 25.4);
+    expectf(yres, header.DpiY);
+    expectf(yres, U(header).EmfHeader.szlDevice.cy / (REAL)U(header).EmfHeader.szlMillimeters.cy * 25.4);
+    expect(0, header.X);
+    expect(0, header.Y);
+    expect(100, header.Width);
+    expect(100, header.Height);
+    expect(28, header.EmfPlusHeaderSize);
+    expect(96, header.LogicalDpiX);
+    expect(96, header.LogicalDpiX);
+    expect(EMR_HEADER, U(header).EmfHeader.iType);
+    expect(0, U(header).EmfHeader.rclBounds.left);
+    expect(0, U(header).EmfHeader.rclBounds.top);
+    expect(-1, U(header).EmfHeader.rclBounds.right);
+    expect(-1, U(header).EmfHeader.rclBounds.bottom);
+    expect(0, U(header).EmfHeader.rclFrame.left);
+    expect(0, U(header).EmfHeader.rclFrame.top);
+    expectf_(100.0, U(header).EmfHeader.rclFrame.right * xres / 2540.0, 2.0);
+    expectf_(100.0, U(header).EmfHeader.rclFrame.bottom * yres / 2540.0, 2.0);
+
     stat = GdipDisposeImage((GpImage*)metafile);
     expect(Ok, stat);
 }
     stat = GdipDisposeImage((GpImage*)metafile);
     expect(Ok, stat);
 }
@@ -484,7 +570,7 @@ static void test_getdc(void)
 
     check_metafile(metafile, getdc_records, "getdc metafile", dst_points, &frame, UnitPixel);
 
 
     check_metafile(metafile, getdc_records, "getdc metafile", dst_points, &frame, UnitPixel);
 
-    save_metafile(metafile, "getdc.emf");
+    sync_metafile(&metafile, "getdc.emf");
 
     stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap);
     expect(Ok, stat);
 
     stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap);
     expect(Ok, stat);
@@ -591,6 +677,13 @@ static void test_emfonly(void)
     stat = GdipGetHemfFromMetafile(metafile, &hemf);
     expect(InvalidParameter, stat);
 
     stat = GdipGetHemfFromMetafile(metafile, &hemf);
     expect(InvalidParameter, stat);
 
+    memset(&header, 0xaa, sizeof(header));
+    stat = GdipGetMetafileHeaderFromMetafile(metafile, &header);
+    expect(Ok, stat);
+    expect(MetafileTypeEmf, header.Type);
+    ok(header.Version == 0xdbc01001 || header.Version == 0xdbc01002, "Unexpected version %x\n", header.Version);
+    /* The rest is zeroed or seemingly random/uninitialized garbage. */
+
     stat = GdipGetImageGraphicsContext((GpImage*)metafile, &graphics);
     expect(Ok, stat);
 
     stat = GdipGetImageGraphicsContext((GpImage*)metafile, &graphics);
     expect(Ok, stat);
 
@@ -622,7 +715,7 @@ static void test_emfonly(void)
 
     check_metafile(metafile, emfonly_records, "emfonly metafile", dst_points, &frame, UnitPixel);
 
 
     check_metafile(metafile, emfonly_records, "emfonly metafile", dst_points, &frame, UnitPixel);
 
-    save_metafile(metafile, "emfonly.emf");
+    sync_metafile(&metafile, "emfonly.emf");
 
     stat = GdipGetImageBounds((GpImage*)metafile, &bounds, &unit);
     expect(Ok, stat);
 
     stat = GdipGetImageBounds((GpImage*)metafile, &bounds, &unit);
     expect(Ok, stat);
@@ -638,6 +731,35 @@ static void test_emfonly(void)
     stat = GdipGetImageVerticalResolution((GpImage*)metafile, &yres);
     expect(Ok, stat);
 
     stat = GdipGetImageVerticalResolution((GpImage*)metafile, &yres);
     expect(Ok, stat);
 
+    memset(&header, 0xaa, sizeof(header));
+    stat = GdipGetMetafileHeaderFromMetafile(metafile, &header);
+    expect(Ok, stat);
+    expect(MetafileTypeEmf, header.Type);
+    expect(U(header).EmfHeader.nBytes, header.Size);
+    /* For some reason a recoreded EMF Metafile has an EMF+ version. */
+    todo_wine ok(header.Version == 0xdbc01001 || header.Version == 0xdbc01002, "Unexpected version %x\n", header.Version);
+    expect(0, header.EmfPlusFlags);
+    expectf(xres, header.DpiX);
+    expectf(xres, U(header).EmfHeader.szlDevice.cx / (REAL)U(header).EmfHeader.szlMillimeters.cx * 25.4);
+    expectf(yres, header.DpiY);
+    expectf(yres, U(header).EmfHeader.szlDevice.cy / (REAL)U(header).EmfHeader.szlMillimeters.cy * 25.4);
+    expect(0, header.X);
+    expect(0, header.Y);
+    expect(100, header.Width);
+    expect(100, header.Height);
+    expect(0, header.EmfPlusHeaderSize);
+    expect(0, header.LogicalDpiX);
+    expect(0, header.LogicalDpiX);
+    expect(EMR_HEADER, U(header).EmfHeader.iType);
+    expect(25, U(header).EmfHeader.rclBounds.left);
+    expect(25, U(header).EmfHeader.rclBounds.top);
+    expect(74, U(header).EmfHeader.rclBounds.right);
+    expect(74, U(header).EmfHeader.rclBounds.bottom);
+    expect(0, U(header).EmfHeader.rclFrame.left);
+    expect(0, U(header).EmfHeader.rclFrame.top);
+    expectf_(100.0, U(header).EmfHeader.rclFrame.right * xres / 2540.0, 2.0);
+    expectf_(100.0, U(header).EmfHeader.rclFrame.bottom * yres / 2540.0, 2.0);
+
     stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap);
     expect(Ok, stat);
 
     stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap);
     expect(Ok, stat);
 
@@ -753,6 +875,34 @@ static void test_emfonly(void)
     expect(Ok, stat);
     expectf(header.DpiY, yres);
 
     expect(Ok, stat);
     expectf(header.DpiY, yres);
 
+    memset(&header, 0xaa, sizeof(header));
+    stat = GdipGetMetafileHeaderFromMetafile(metafile, &header);
+    expect(Ok, stat);
+    expect(MetafileTypeEmf, header.Type);
+    expect(U(header).EmfHeader.nBytes, header.Size);
+    expect(0x10000, header.Version);
+    expect(0, header.EmfPlusFlags);
+    expectf(xres, header.DpiX);
+    expectf(xres, U(header).EmfHeader.szlDevice.cx / (REAL)U(header).EmfHeader.szlMillimeters.cx * 25.4);
+    expectf(yres, header.DpiY);
+    expectf(yres, U(header).EmfHeader.szlDevice.cy / (REAL)U(header).EmfHeader.szlMillimeters.cy * 25.4);
+    expect(0, header.X);
+    expect(0, header.Y);
+    expect(100, header.Width);
+    expect(100, header.Height);
+    expect(0, header.EmfPlusHeaderSize);
+    expect(0, header.LogicalDpiX);
+    expect(0, header.LogicalDpiX);
+    expect(EMR_HEADER, U(header).EmfHeader.iType);
+    expect(25, U(header).EmfHeader.rclBounds.left);
+    expect(25, U(header).EmfHeader.rclBounds.top);
+    expect(74, U(header).EmfHeader.rclBounds.right);
+    expect(74, U(header).EmfHeader.rclBounds.bottom);
+    expect(0, U(header).EmfHeader.rclFrame.left);
+    expect(0, U(header).EmfHeader.rclFrame.top);
+    expectf_(100.0, U(header).EmfHeader.rclFrame.right * xres / 2540.0, 2.0);
+    expectf_(100.0, U(header).EmfHeader.rclFrame.bottom * yres / 2540.0, 2.0);
+
     stat = GdipDisposeImage((GpImage*)metafile);
     expect(Ok, stat);
 }
     stat = GdipDisposeImage((GpImage*)metafile);
     expect(Ok, stat);
 }
@@ -811,7 +961,7 @@ static void test_fillrect(void)
 
     check_metafile(metafile, fillrect_records, "fillrect metafile", dst_points, &frame, UnitPixel);
 
 
     check_metafile(metafile, fillrect_records, "fillrect metafile", dst_points, &frame, UnitPixel);
 
-    save_metafile(metafile, "fillrect.emf");
+    sync_metafile(&metafile, "fillrect.emf");
 
     stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap);
     expect(Ok, stat);
 
     stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap);
     expect(Ok, stat);
@@ -915,7 +1065,7 @@ static void test_clear(void)
     stat = GdipDeleteGraphics(graphics);
     expect(Ok, stat);
 
     stat = GdipDeleteGraphics(graphics);
     expect(Ok, stat);
 
-    save_metafile(metafile, "clear.emf");
+    sync_metafile(&metafile, "clear.emf");
 
     stat = GdipCreateBitmapFromScan0(30, 30, 0, PixelFormat32bppRGB, NULL, &bitmap);
     expect(Ok, stat);
 
     stat = GdipCreateBitmapFromScan0(30, 30, 0, PixelFormat32bppRGB, NULL, &bitmap);
     expect(Ok, stat);
@@ -1228,7 +1378,7 @@ static void test_pagetransform(void)
 
     check_metafile(metafile, pagetransform_records, "pagetransform metafile", dst_points, &frame, UnitPixel);
 
 
     check_metafile(metafile, pagetransform_records, "pagetransform metafile", dst_points, &frame, UnitPixel);
 
-    save_metafile(metafile, "pagetransform.emf");
+    sync_metafile(&metafile, "pagetransform.emf");
 
     stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap);
     expect(Ok, stat);
 
     stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap);
     expect(Ok, stat);
@@ -1280,6 +1430,14 @@ static const emfplus_record worldtransform_records[] = {
     {0, EmfPlusRecordTypeFillRects},
     {0, EmfPlusRecordTypeResetWorldTransform},
     {0, EmfPlusRecordTypeFillRects},
     {0, EmfPlusRecordTypeFillRects},
     {0, EmfPlusRecordTypeResetWorldTransform},
     {0, EmfPlusRecordTypeFillRects},
+    {0, EmfPlusRecordTypeMultiplyWorldTransform},
+    {0, EmfPlusRecordTypeFillRects},
+    {0, EmfPlusRecordTypeRotateWorldTransform},
+    {0, EmfPlusRecordTypeFillRects},
+    {0, EmfPlusRecordTypeSetWorldTransform},
+    {0, EmfPlusRecordTypeFillRects},
+    {0, EmfPlusRecordTypeTranslateWorldTransform},
+    {0, EmfPlusRecordTypeFillRects},
     {0, EmfPlusRecordTypeEndOfFile},
     {0, EMR_EOF},
     {0}
     {0, EmfPlusRecordTypeEndOfFile},
     {0, EMR_EOF},
     {0}
@@ -1379,6 +1537,112 @@ static void test_worldtransform(void)
     stat = GdipDeleteBrush(brush);
     expect(Ok, stat);
 
     stat = GdipDeleteBrush(brush);
     expect(Ok, stat);
 
+    /* multiply transform */
+    stat = GdipSetMatrixElements(transform, 2.0, 0.0, 0.0, 1.0, 0.0, 0.0);
+    expect(Ok, stat);
+
+    stat = GdipMultiplyWorldTransform(graphics, transform, MatrixOrderPrepend);
+    expect(Ok, stat);
+
+    stat = GdipGetWorldTransform(graphics, transform);
+    expect(Ok, stat);
+
+    stat = GdipGetMatrixElements(transform, elements);
+    expect(Ok, stat);
+    expectf(2.0, elements[0]);
+    expectf(0.0, elements[1]);
+    expectf(0.0, elements[2]);
+    expectf(1.0, elements[3]);
+    expectf(0.0, elements[4]);
+    expectf(0.0, elements[5]);
+
+    stat = GdipCreateSolidFill((ARGB)0xffff0000, (GpSolidFill**)&brush);
+    expect(Ok, stat);
+
+    stat = GdipFillRectangle(graphics, brush, 1.0, 1.0, 0.5, 1.0);
+    expect(Ok, stat);
+
+    stat = GdipDeleteBrush(brush);
+    expect(Ok, stat);
+
+    /* rotate transform */
+    stat = GdipRotateWorldTransform(graphics, 90.0, MatrixOrderAppend);
+    expect(Ok, stat);
+
+    stat = GdipGetWorldTransform(graphics, transform);
+    expect(Ok, stat);
+
+    stat = GdipGetMatrixElements(transform, elements);
+    expect(Ok, stat);
+    expectf(0.0, elements[0]);
+    expectf(2.0, elements[1]);
+    expectf(-1.0, elements[2]);
+    expectf(0.0, elements[3]);
+    expectf(0.0, elements[4]);
+    expectf(0.0, elements[5]);
+
+    stat = GdipCreateSolidFill((ARGB)0xffff00ff, (GpSolidFill**)&brush);
+    expect(Ok, stat);
+
+    stat = GdipFillRectangle(graphics, brush, 1.0, -1.0, 0.5, 1.0);
+    expect(Ok, stat);
+
+    stat = GdipDeleteBrush(brush);
+    expect(Ok, stat);
+
+    /* set transform */
+    stat = GdipSetMatrixElements(transform, 1.0, 0.0, 0.0, 3.0, 0.0, 0.0);
+    expect(Ok, stat);
+
+    stat = GdipSetWorldTransform(graphics, transform);
+    expect(Ok, stat);
+
+    stat = GdipGetWorldTransform(graphics, transform);
+    expect(Ok, stat);
+
+    stat = GdipGetMatrixElements(transform, elements);
+    expect(Ok, stat);
+    expectf(1.0, elements[0]);
+    expectf(0.0, elements[1]);
+    expectf(0.0, elements[2]);
+    expectf(3.0, elements[3]);
+    expectf(0.0, elements[4]);
+    expectf(0.0, elements[5]);
+
+    stat = GdipCreateSolidFill((ARGB)0xffffff00, (GpSolidFill**)&brush);
+    expect(Ok, stat);
+
+    stat = GdipFillRectangle(graphics, brush, 1.0, 1.0, 1.0, 1.0);
+    expect(Ok, stat);
+
+    stat = GdipDeleteBrush(brush);
+    expect(Ok, stat);
+
+    /* translate transform */
+    stat = GdipTranslateWorldTransform(graphics, -1.0, 0.0, MatrixOrderAppend);
+    expect(Ok, stat);
+
+    stat = GdipGetWorldTransform(graphics, transform);
+    expect(Ok, stat);
+
+    stat = GdipGetMatrixElements(transform, elements);
+    expect(Ok, stat);
+    expectf(1.0, elements[0]);
+    expectf(0.0, elements[1]);
+    expectf(0.0, elements[2]);
+    expectf(3.0, elements[3]);
+    expectf(-1.0, elements[4]);
+    expectf(0.0, elements[5]);
+
+    stat = GdipCreateSolidFill((ARGB)0xffffffff, (GpSolidFill**)&brush);
+    expect(Ok, stat);
+
+    stat = GdipFillRectangle(graphics, brush, 1.0, 1.0, 1.0, 1.0);
+    expect(Ok, stat);
+
+    stat = GdipDeleteBrush(brush);
+    expect(Ok, stat);
+
     stat = GdipDeleteMatrix(transform);
     expect(Ok, stat);
 
     stat = GdipDeleteMatrix(transform);
     expect(Ok, stat);
 
@@ -1387,7 +1651,7 @@ static void test_worldtransform(void)
 
     check_metafile(metafile, worldtransform_records, "worldtransform metafile", dst_points, &frame, UnitPixel);
 
 
     check_metafile(metafile, worldtransform_records, "worldtransform metafile", dst_points, &frame, UnitPixel);
 
-    save_metafile(metafile, "worldtransform.emf");
+    sync_metafile(&metafile, "worldtransform.emf");
 
     stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap);
     expect(Ok, stat);
 
     stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap);
     expect(Ok, stat);
@@ -1413,6 +1677,22 @@ static void test_worldtransform(void)
     expect(Ok, stat);
     expect(0xff00ffff, color);
 
     expect(Ok, stat);
     expect(0xff00ffff, color);
 
+    stat = GdipBitmapGetPixel(bitmap, 50, 30, &color);
+    expect(Ok, stat);
+    expect(0xffff0000, color);
+
+    stat = GdipBitmapGetPixel(bitmap, 10, 50, &color);
+    expect(Ok, stat);
+    expect(0xffff00ff, color);
+
+    stat = GdipBitmapGetPixel(bitmap, 30, 90, &color);
+    expect(Ok, stat);
+    expect(0xffffff00, color);
+
+    stat = GdipBitmapGetPixel(bitmap, 10, 90, &color);
+    expect(Ok, stat);
+    expect(0xffffffff, color);
+
     stat = GdipDeleteGraphics(graphics);
     expect(Ok, stat);
 
     stat = GdipDeleteGraphics(graphics);
     expect(Ok, stat);
 
@@ -1567,24 +1847,459 @@ static void test_frameunit(void)
     expect(Ok, stat);
 }
 
     expect(Ok, stat);
 }
 
-START_TEST(metafile)
+static const emfplus_record container_records[] = {
+    {0, EMR_HEADER},
+    {0, EmfPlusRecordTypeHeader},
+    {0, EmfPlusRecordTypeBeginContainerNoParams},
+    {0, EmfPlusRecordTypeScaleWorldTransform},
+    {0, EmfPlusRecordTypeFillRects},
+    {0, EmfPlusRecordTypeEndContainer},
+    {0, EmfPlusRecordTypeScaleWorldTransform},
+    {0, EmfPlusRecordTypeFillRects},
+    {0, EmfPlusRecordTypeSave},
+    {0, EmfPlusRecordTypeRestore},
+    {0, EmfPlusRecordTypeScaleWorldTransform},
+    {0, EmfPlusRecordTypeBeginContainerNoParams},
+    {0, EmfPlusRecordTypeScaleWorldTransform},
+    {0, EmfPlusRecordTypeBeginContainerNoParams},
+    {0, EmfPlusRecordTypeEndContainer},
+    {0, EmfPlusRecordTypeFillRects},
+    {0, EmfPlusRecordTypeBeginContainer},
+    {0, EmfPlusRecordTypeFillRects},
+    {0, EmfPlusRecordTypeEndContainer},
+    {0, EmfPlusRecordTypeBeginContainerNoParams},
+    {0, EmfPlusRecordTypeEndOfFile},
+    {0, EMR_EOF},
+    {0}
+};
+
+static void test_containers(void)
 {
 {
-    struct GdiplusStartupInput gdiplusStartupInput;
-    ULONG_PTR gdiplusToken;
-    int myARGC;
-    char **myARGV;
+    GpStatus stat;
+    GpMetafile *metafile;
+    GpGraphics *graphics;
+    GpBitmap *bitmap;
+    GpBrush *brush;
+    ARGB color;
+    HDC hdc;
+    static const GpRectF frame = {0.0, 0.0, 100.0, 100.0};
+    static const GpPointF dst_points[3] = {{0.0,0.0},{100.0,0.0},{0.0,100.0}};
+    static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0};
+    GraphicsContainer state1, state2;
+    GpRectF srcrect, dstrect;
+    REAL dpix, dpiy;
 
 
-    gdiplusStartupInput.GdiplusVersion              = 1;
-    gdiplusStartupInput.DebugEventCallback          = NULL;
-    gdiplusStartupInput.SuppressBackgroundThread    = 0;
+    hdc = CreateCompatibleDC(0);
+
+    stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, &metafile);
+    expect(Ok, stat);
+
+    DeleteDC(hdc);
+
+    if (stat != Ok)
+        return;
+
+    stat = GdipGetImageGraphicsContext((GpImage*)metafile, &graphics);
+    expect(Ok, stat);
+
+    /* Normal usage */
+    stat = GdipBeginContainer2(graphics, &state1);
+    expect(Ok, stat);
+
+    stat = GdipScaleWorldTransform(graphics, 2.0, 2.0, MatrixOrderPrepend);
+    expect(Ok, stat);
+
+    stat = GdipCreateSolidFill((ARGB)0xff000000, (GpSolidFill**)&brush);
+    expect(Ok, stat);
+
+    stat = GdipFillRectangle(graphics, brush, 5.0, 5.0, 5.0, 5.0);
+    expect(Ok, stat);
+
+    stat = GdipDeleteBrush(brush);
+    expect(Ok, stat);
+
+    stat = GdipEndContainer(graphics, state1);
+    expect(Ok, stat);
+
+    stat = GdipScaleWorldTransform(graphics, 1.0, 1.0, MatrixOrderPrepend);
+    expect(Ok, stat);
+
+    stat = GdipCreateSolidFill((ARGB)0xff0000ff, (GpSolidFill**)&brush);
+    expect(Ok, stat);
+
+    stat = GdipFillRectangle(graphics, brush, 5.0, 5.0, 5.0, 5.0);
+    expect(Ok, stat);
+
+    stat = GdipDeleteBrush(brush);
+    expect(Ok, stat);
+
+    stat = GdipSaveGraphics(graphics, &state1);
+    expect(Ok, stat);
+
+    stat = GdipRestoreGraphics(graphics, state1);
+    expect(Ok, stat);
+
+    /* Popping two states at once */
+    stat = GdipScaleWorldTransform(graphics, 2.0, 2.0, MatrixOrderPrepend);
+    expect(Ok, stat);
+
+    stat = GdipBeginContainer2(graphics, &state1);
+    expect(Ok, stat);
+
+    stat = GdipScaleWorldTransform(graphics, 4.0, 4.0, MatrixOrderPrepend);
+    expect(Ok, stat);
+
+    stat = GdipBeginContainer2(graphics, &state2);
+    expect(Ok, stat);
+
+    stat = GdipEndContainer(graphics, state1);
+    expect(Ok, stat);
+
+    stat = GdipCreateSolidFill((ARGB)0xff00ff00, (GpSolidFill**)&brush);
+    expect(Ok, stat);
+
+    stat = GdipFillRectangle(graphics, brush, 20.0, 20.0, 5.0, 5.0);
+    expect(Ok, stat);
+
+    stat = GdipDeleteBrush(brush);
+    expect(Ok, stat);
+
+    /* With transform applied */
+    stat = GdipGetDpiX(graphics, &dpix);
+    expect(Ok, stat);
+
+    stat = GdipGetDpiY(graphics, &dpiy);
+    expect(Ok, stat);
+
+    srcrect.X = 0.0;
+    srcrect.Y = 0.0;
+    srcrect.Width = 1.0;
+    srcrect.Height = 1.0;
+
+    dstrect.X = 25.0;
+    dstrect.Y = 0.0;
+    dstrect.Width = 5.0;
+    dstrect.Height = 5.0;
+
+    stat = GdipBeginContainer(graphics, &dstrect, &srcrect, UnitInch, &state1);
+    expect(Ok, stat);
+
+    stat = GdipCreateSolidFill((ARGB)0xff00ffff, (GpSolidFill**)&brush);
+    expect(Ok, stat);
+
+    stat = GdipFillRectangle(graphics, brush, 0.0, 0.0, dpix, dpiy);
+    expect(Ok, stat);
+
+    stat = GdipDeleteBrush(brush);
+    expect(Ok, stat);
+
+    stat = GdipEndContainer(graphics, state1);
+    expect(Ok, stat);
+
+    /* Restoring an invalid state seems to break the graphics object? */
+    if (0) {
+        stat = GdipEndContainer(graphics, state1);
+        expect(Ok, stat);
+    }
+
+    /* Ending metafile with a state open */
+    stat = GdipBeginContainer2(graphics, &state1);
+    expect(Ok, stat);
+
+    stat = GdipDeleteGraphics(graphics);
+    expect(Ok, stat);
+
+    check_metafile(metafile, container_records, "container metafile", dst_points, &frame, UnitPixel);
+
+    sync_metafile(&metafile, "container.emf");
+
+    stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap);
+    expect(Ok, stat);
+
+    stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
+    expect(Ok, stat);
+
+    play_metafile(metafile, graphics, container_records, "container playback", dst_points, &frame, UnitPixel);
+
+    stat = GdipBitmapGetPixel(bitmap, 80, 80, &color);
+    expect(Ok, stat);
+    expect(0, color);
+
+    stat = GdipBitmapGetPixel(bitmap, 12, 12, &color);
+    expect(Ok, stat);
+    expect(0xff000000, color);
+
+    stat = GdipBitmapGetPixel(bitmap, 8, 8, &color);
+    expect(Ok, stat);
+    expect(0xff0000ff, color);
+
+    stat = GdipBitmapGetPixel(bitmap, 42, 42, &color);
+    expect(Ok, stat);
+    expect(0xff00ff00, color);
+
+    stat = GdipBitmapGetPixel(bitmap, 55, 5, &color);
+    expect(Ok, stat);
+    expect(0xff00ffff, color);
+
+    stat = GdipDeleteGraphics(graphics);
+    expect(Ok, stat);
+
+    stat = GdipDisposeImage((GpImage*)bitmap);
+    expect(Ok, stat);
+
+    stat = GdipDisposeImage((GpImage*)metafile);
+    expect(Ok, stat);
+}
+
+static const emfplus_record clipping_records[] = {
+    {0, EMR_HEADER},
+    {0, EmfPlusRecordTypeHeader},
+    {0, EmfPlusRecordTypeSave},
+    {0, EmfPlusRecordTypeSetClipRect},
+    {0, EmfPlusRecordTypeFillRects},
+    {0, EmfPlusRecordTypeRestore},
+    {0, EmfPlusRecordTypeSetClipRect},
+    {0, EmfPlusRecordTypeFillRects},
+    {0, EmfPlusRecordTypeEndOfFile},
+    {0, EMR_EOF},
+    {0}
+};
+
+static void test_clipping(void)
+{
+    GpStatus stat;
+    GpMetafile *metafile;
+    GpGraphics *graphics;
+    GpBitmap *bitmap;
+    GpBrush *brush;
+    ARGB color;
+    HDC hdc;
+    static const GpRectF frame = {0.0, 0.0, 100.0, 100.0};
+    static const GpPointF dst_points[3] = {{0.0,0.0},{100.0,0.0},{0.0,100.0}};
+    static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0};
+    GraphicsState state;
+
+    hdc = CreateCompatibleDC(0);
+
+    stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, &metafile);
+    expect(Ok, stat);
+
+    DeleteDC(hdc);
+
+    if (stat != Ok)
+        return;
+
+    stat = GdipGetImageGraphicsContext((GpImage*)metafile, &graphics);
+    expect(Ok, stat);
+
+    stat = GdipSaveGraphics(graphics, &state);
+    expect(Ok, stat);
+
+    stat = GdipSetClipRect(graphics, 30, 30, 10, 10, CombineModeReplace);
+    expect(Ok, stat);
+
+    stat = GdipCreateSolidFill((ARGB)0xff000000, (GpSolidFill**)&brush);
+    expect(Ok, stat);
+
+    stat = GdipFillRectangle(graphics, brush, 0, 0, 100, 100);
+    expect(Ok, stat);
+
+    stat = GdipDeleteBrush(brush);
+    expect(Ok, stat);
+
+    stat = GdipRestoreGraphics(graphics, state);
+    expect(Ok, stat);
+
+    stat = GdipSetClipRect(graphics, 30, 30, 10, 10, CombineModeXor);
+    expect(Ok, stat);
+
+    stat = GdipCreateSolidFill((ARGB)0xff0000ff, (GpSolidFill**)&brush);
+    expect(Ok, stat);
+
+    stat = GdipFillRectangle(graphics, brush, 30, 30, 20, 10);
+    expect(Ok, stat);
+
+    stat = GdipDeleteBrush(brush);
+    expect(Ok, stat);
+
+    stat = GdipDeleteGraphics(graphics);
+    expect(Ok, stat);
+
+    check_metafile(metafile, clipping_records, "clipping metafile", dst_points, &frame, UnitPixel);
+
+    sync_metafile(&metafile, "clipping.emf");
+
+    stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap);
+    expect(Ok, stat);
+
+    stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
+    expect(Ok, stat);
+
+    play_metafile(metafile, graphics, clipping_records, "clipping playback", dst_points, &frame, UnitPixel);
+
+    stat = GdipBitmapGetPixel(bitmap, 80, 80, &color);
+    expect(Ok, stat);
+    expect(0, color);
+
+    stat = GdipBitmapGetPixel(bitmap, 35, 35, &color);
+    expect(Ok, stat);
+    expect(0xff000000, color);
+
+    stat = GdipBitmapGetPixel(bitmap, 45, 35, &color);
+    expect(Ok, stat);
+    expect(0xff0000ff, color);
+
+    stat = GdipDeleteGraphics(graphics);
+    expect(Ok, stat);
+
+    stat = GdipDisposeImage((GpImage*)bitmap);
+    expect(Ok, stat);
+
+    stat = GdipDisposeImage((GpImage*)metafile);
+    expect(Ok, stat);
+}
+
+static void test_gditransform_cb(GpMetafile* metafile, EmfPlusRecordType record_type,
+    unsigned int flags, unsigned int dataSize, const unsigned char *pStr)
+{
+    static const XFORM xform = {0.5, 0, 0, 0.5, 0, 0};
+    static const RECTL rectangle = {0,0,100,100};
+    GpStatus stat;
+
+    stat = GdipPlayMetafileRecord(metafile, EMR_SETWORLDTRANSFORM, 0, sizeof(xform), (void*)&xform);
+    expect(Ok, stat);
+
+    stat = GdipPlayMetafileRecord(metafile, EMR_RECTANGLE, 0, sizeof(rectangle), (void*)&rectangle);
+    expect(Ok, stat);
+}
+
+static const emfplus_record gditransform_records[] = {
+    {0, EMR_HEADER},
+    {0, EMR_CREATEBRUSHINDIRECT},
+    {0, EMR_SELECTOBJECT},
+    {0, EMR_GDICOMMENT, 0, test_gditransform_cb},
+    {0, EMR_SELECTOBJECT},
+    {0, EMR_DELETEOBJECT},
+    {0, EMR_EOF},
+    {0}
+};
+
+static void test_gditransform(void)
+{
+    GpStatus stat;
+    GpMetafile *metafile;
+    GpGraphics *graphics;
+    HDC hdc, metafile_dc;
+    HENHMETAFILE hemf;
+    MetafileHeader header;
+    static const GpRectF frame = {0.0, 0.0, 100.0, 100.0};
+    static const GpPointF dst_points[3] = {{0.0,0.0},{40.0,0.0},{0.0,40.0}};
+    static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0};
+    HBRUSH hbrush, holdbrush;
+    GpBitmap *bitmap;
+    ARGB color;
+
+    hdc = CreateCompatibleDC(0);
+
+    stat = GdipRecordMetafile(hdc, EmfTypeEmfOnly, &frame, MetafileFrameUnitPixel, description, &metafile);
+    expect(Ok, stat);
+
+    DeleteDC(hdc);
+
+    if (stat != Ok)
+            return;
+
+    stat = GdipGetHemfFromMetafile(metafile, &hemf);
+    expect(InvalidParameter, stat);
+
+    memset(&header, 0xaa, sizeof(header));
+    stat = GdipGetMetafileHeaderFromMetafile(metafile, &header);
+    expect(Ok, stat);
+    expect(MetafileTypeEmf, header.Type);
+    ok(header.Version == 0xdbc01001 || header.Version == 0xdbc01002, "Unexpected version %x\n", header.Version);
+
+    stat = GdipGetImageGraphicsContext((GpImage*)metafile, &graphics);
+    expect(Ok, stat);
+
+    stat = GdipGetDC(graphics, &metafile_dc);
+    expect(Ok, stat);
+
+    if (stat != Ok)
+    {
+        GdipDeleteGraphics(graphics);
+        GdipDisposeImage((GpImage*)metafile);
+        return;
+    }
+
+    hbrush = CreateSolidBrush(0xff);
+
+    holdbrush = SelectObject(metafile_dc, hbrush);
+
+    GdiComment(metafile_dc, 8, (const BYTE*)"winetest");
+
+    SelectObject(metafile_dc, holdbrush);
+
+    DeleteObject(hbrush);
+
+    stat = GdipReleaseDC(graphics, metafile_dc);
+    expect(Ok, stat);
+
+    stat = GdipDeleteGraphics(graphics);
+    expect(Ok, stat);
+
+    check_metafile(metafile, gditransform_records, "gditransform metafile", dst_points, &frame, UnitPixel);
+
+    sync_metafile(&metafile, "gditransform.emf");
+
+    stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap);
+    expect(Ok, stat);
+
+    stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
+    expect(Ok, stat);
+
+    play_metafile(metafile, graphics, gditransform_records, "gditransform playback", dst_points, &frame, UnitPixel);
+
+    stat = GdipBitmapGetPixel(bitmap, 10, 10, &color);
+    expect(Ok, stat);
+    expect(0xffff0000, color);
+
+    stat = GdipBitmapGetPixel(bitmap, 30, 30, &color);
+    expect(Ok, stat);
+    expect(0x00000000, color);
+
+    stat = GdipDeleteGraphics(graphics);
+    expect(Ok, stat);
+
+    stat = GdipDisposeImage((GpImage*)bitmap);
+    expect(Ok, stat);
+
+    stat = GdipDisposeImage((GpImage*)metafile);
+    expect(Ok, stat);
+}
+
+START_TEST(metafile)
+{
+    struct GdiplusStartupInput gdiplusStartupInput;
+    ULONG_PTR gdiplusToken;
+    int myARGC;
+    char **myARGV;
+
+    gdiplusStartupInput.GdiplusVersion              = 1;
+    gdiplusStartupInput.DebugEventCallback          = NULL;
+    gdiplusStartupInput.SuppressBackgroundThread    = 0;
     gdiplusStartupInput.SuppressExternalCodecs      = 0;
 
     GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
 
     myARGC = winetest_get_mainargs( &myARGV );
 
     gdiplusStartupInput.SuppressExternalCodecs      = 0;
 
     GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
 
     myARGC = winetest_get_mainargs( &myARGV );
 
-    if (myARGC >= 3 && !strcmp(myARGV[2], "save"))
-        save_metafiles = TRUE;
+    if (myARGC >= 3)
+    {
+        if (!strcmp(myARGV[2], "save"))
+            save_metafiles = TRUE;
+        else if (!strcmp(myARGV[2], "load"))
+            load_metafiles = TRUE;
+    }
 
     test_empty();
     test_getdc();
 
     test_empty();
     test_getdc();
@@ -1596,6 +2311,9 @@ START_TEST(metafile)
     test_worldtransform();
     test_converttoemfplus();
     test_frameunit();
     test_worldtransform();
     test_converttoemfplus();
     test_frameunit();
+    test_containers();
+    test_clipping();
+    test_gditransform();
 
     GdiplusShutdown(gdiplusToken);
 }
 
     GdiplusShutdown(gdiplusToken);
 }