- Add wine test for gdiplus
authorDmitry Chapyshev <dmitry@reactos.org>
Sun, 8 Jun 2008 09:47:47 +0000 (09:47 +0000)
committerDmitry Chapyshev <dmitry@reactos.org>
Sun, 8 Jun 2008 09:47:47 +0000 (09:47 +0000)
svn path=/trunk/; revision=33897

rostests/winetests/directory.rbuild
rostests/winetests/gdiplus/brush.c [new file with mode: 0644]
rostests/winetests/gdiplus/font.c [new file with mode: 0644]
rostests/winetests/gdiplus/gdiplus.rbuild [new file with mode: 0644]
rostests/winetests/gdiplus/graphics.c [new file with mode: 0644]
rostests/winetests/gdiplus/graphicspath.c [new file with mode: 0644]
rostests/winetests/gdiplus/image.c [new file with mode: 0644]
rostests/winetests/gdiplus/matrix.c [new file with mode: 0644]
rostests/winetests/gdiplus/pen.c [new file with mode: 0644]
rostests/winetests/gdiplus/stringformat.c [new file with mode: 0644]
rostests/winetests/gdiplus/testlist.c [new file with mode: 0644]

index f35fb77..99192fc 100644 (file)
@@ -25,6 +25,9 @@
        <directory name="gdi32">
                <xi:include href="gdi32/gdi32.rbuild" />
        </directory>
+       <directory name="gdiplus">
+               <xi:include href="gdiplus/gdiplus.rbuild" />
+       </directory>
        <directory name="hlink">
                <xi:include href="hlink/hlink.rbuild" />
        </directory>
diff --git a/rostests/winetests/gdiplus/brush.c b/rostests/winetests/gdiplus/brush.c
new file mode 100644 (file)
index 0000000..70eac03
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Unit test suite for brushes
+ *
+ * Copyright (C) 2007 Google (Evan Stade)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "windows.h"
+#include "gdiplus.h"
+#include "wine/test.h"
+
+#define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
+
+static void test_constructor_destructor(void)
+{
+    GpStatus status;
+    GpSolidFill *brush = NULL;
+
+    status = GdipCreateSolidFill((ARGB)0xdeadbeef, &brush);
+    expect(Ok, status);
+    ok(brush != NULL, "Expected brush to be initialized\n");
+
+    status = GdipDeleteBrush(NULL);
+    expect(InvalidParameter, status);
+
+    status = GdipDeleteBrush((GpBrush*) brush);
+    expect(Ok, status);
+}
+
+static void test_type(void)
+{
+    GpStatus status;
+    GpBrushType bt;
+    GpSolidFill *brush = NULL;
+
+    GdipCreateSolidFill((ARGB)0xdeadbeef, &brush);
+
+    status = GdipGetBrushType((GpBrush*)brush, &bt);
+    expect(status, Ok);
+    expect(bt, BrushTypeSolidColor);
+
+    GdipDeleteBrush((GpBrush*) brush);
+}
+
+START_TEST(brush)
+{
+    struct GdiplusStartupInput gdiplusStartupInput;
+    ULONG_PTR gdiplusToken;
+
+    gdiplusStartupInput.GdiplusVersion              = 1;
+    gdiplusStartupInput.DebugEventCallback          = NULL;
+    gdiplusStartupInput.SuppressBackgroundThread    = 0;
+    gdiplusStartupInput.SuppressExternalCodecs      = 0;
+
+    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
+
+    test_constructor_destructor();
+    test_type();
+
+    GdiplusShutdown(gdiplusToken);
+}
diff --git a/rostests/winetests/gdiplus/font.c b/rostests/winetests/gdiplus/font.c
new file mode 100644 (file)
index 0000000..71e6f9a
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Unit test suite for fonts
+ *
+ * Copyright (C) 2007 Google (Evan Stade)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "windows.h"
+#include "gdiplus.h"
+#include "wine/test.h"
+
+#define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
+
+static WCHAR arial[] = {'A','r','i','a','l','\0'};
+
+static void test_logfont(void)
+{
+    LOGFONTW lfw, lfw2;
+    GpFont *font;
+    GpStatus stat;
+    GpGraphics *graphics;
+    HDC hdc = GetDC(0);
+
+    GdipCreateFromHDC(hdc, &graphics);
+    memset(&lfw, 0, sizeof(LOGFONTW));
+    memset(&lfw2, 0xff, sizeof(LOGFONTW));
+    memcpy(&lfw.lfFaceName, arial, 6 * sizeof(WCHAR));
+
+    stat = GdipCreateFontFromLogfontW(hdc, &lfw, &font);
+    expect(Ok, stat);
+    stat = GdipGetLogFontW(font, graphics, &lfw2);
+    expect(Ok, stat);
+
+    ok(lfw2.lfHeight < 0, "Expected negative height\n");
+    expect(0, lfw2.lfWidth);
+    expect(0, lfw2.lfEscapement);
+    expect(0, lfw2.lfOrientation);
+    ok((lfw2.lfWeight >= 100) && (lfw2.lfWeight <= 900), "Expected weight to be set\n");
+    expect(0, lfw2.lfItalic);
+    expect(0, lfw2.lfUnderline);
+    expect(0, lfw2.lfStrikeOut);
+    expect(0, lfw2.lfCharSet);
+    expect(0, lfw2.lfOutPrecision);
+    expect(0, lfw2.lfClipPrecision);
+    expect(0, lfw2.lfQuality);
+    expect(0, lfw2.lfPitchAndFamily);
+
+    GdipDeleteFont(font);
+
+    memset(&lfw, 0, sizeof(LOGFONTW));
+    lfw.lfHeight = 25;
+    lfw.lfWidth = 25;
+    lfw.lfEscapement = lfw.lfOrientation = 50;
+    lfw.lfItalic = lfw.lfUnderline = lfw.lfStrikeOut = TRUE;
+
+    memset(&lfw2, 0xff, sizeof(LOGFONTW));
+    memcpy(&lfw.lfFaceName, arial, 6 * sizeof(WCHAR));
+
+    stat = GdipCreateFontFromLogfontW(hdc, &lfw, &font);
+    expect(Ok, stat);
+    stat = GdipGetLogFontW(font, graphics, &lfw2);
+    expect(Ok, stat);
+
+    ok(lfw2.lfHeight < 0, "Expected negative height\n");
+    expect(0, lfw2.lfWidth);
+    expect(0, lfw2.lfEscapement);
+    expect(0, lfw2.lfOrientation);
+    ok((lfw2.lfWeight >= 100) && (lfw2.lfWeight <= 900), "Expected weight to be set\n");
+    expect(TRUE, lfw2.lfItalic);
+    expect(TRUE, lfw2.lfUnderline);
+    expect(TRUE, lfw2.lfStrikeOut);
+    expect(0, lfw2.lfCharSet);
+    expect(0, lfw2.lfOutPrecision);
+    expect(0, lfw2.lfClipPrecision);
+    expect(0, lfw2.lfQuality);
+    expect(0, lfw2.lfPitchAndFamily);
+
+    GdipDeleteFont(font);
+
+    GdipDeleteGraphics(graphics);
+    ReleaseDC(0, hdc);
+}
+
+START_TEST(font)
+{
+    struct GdiplusStartupInput gdiplusStartupInput;
+    ULONG_PTR gdiplusToken;
+
+    gdiplusStartupInput.GdiplusVersion              = 1;
+    gdiplusStartupInput.DebugEventCallback          = NULL;
+    gdiplusStartupInput.SuppressBackgroundThread    = 0;
+    gdiplusStartupInput.SuppressExternalCodecs      = 0;
+
+    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
+
+    test_logfont();
+
+    GdiplusShutdown(gdiplusToken);
+}
diff --git a/rostests/winetests/gdiplus/gdiplus.rbuild b/rostests/winetests/gdiplus/gdiplus.rbuild
new file mode 100644 (file)
index 0000000..06a1242
--- /dev/null
@@ -0,0 +1,22 @@
+<module name="gdiplus_winetest" type="win32cui" installbase="bin" installname="gdiplus_winetest.exe" allowwarnings="true">
+        <include base="gdiplus_winetest">.</include>
+        <define name="__USE_W32API" />
+        <define name="_WIN32_IE">0x600</define>
+        <define name="_WIN32_WINNT">0x501</define>
+        <define name="WINVER">0x501</define>
+        <library>wine</library>
+        <library>gdiplus</library>
+        <library>user32</library>
+        <library>gdi32</library>
+        <library>kernel32</library>
+        <library>ntdll</library>
+        <file>brush.c</file>
+        <file>font.c</file>
+        <file>graphics.c</file>
+        <file>graphicspath.c</file>
+        <file>image.c</file>
+        <file>matrix.c</file>
+        <file>pen.c</file>
+        <file>stringformat.c</file>
+        <file>testlist.c</file>
+</module>
\ No newline at end of file
diff --git a/rostests/winetests/gdiplus/graphics.c b/rostests/winetests/gdiplus/graphics.c
new file mode 100644 (file)
index 0000000..168eae3
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ * Unit test suite for graphics objects
+ *
+ * Copyright (C) 2007 Google (Evan Stade)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "windows.h"
+#include "gdiplus.h"
+#include "wingdi.h"
+#include "wine/test.h"
+
+#define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
+#define TABLE_LEN (23)
+
+static void test_constructor_destructor(void)
+{
+    GpStatus stat;
+    GpGraphics *graphics = NULL;
+    HDC hdc = GetDC(0);
+
+    stat = GdipCreateFromHDC(NULL, &graphics);
+    expect(OutOfMemory, stat);
+    stat = GdipDeleteGraphics(graphics);
+    expect(InvalidParameter, stat);
+
+    stat = GdipCreateFromHDC(hdc, &graphics);
+    expect(Ok, stat);
+    stat = GdipDeleteGraphics(graphics);
+    expect(Ok, stat);
+
+    stat = GdipCreateFromHWND(NULL, &graphics);
+    expect(Ok, stat);
+    stat = GdipDeleteGraphics(graphics);
+    expect(Ok, stat);
+
+    stat = GdipCreateFromHWNDICM(NULL, &graphics);
+    expect(Ok, stat);
+    stat = GdipDeleteGraphics(graphics);
+    expect(Ok, stat);
+
+    stat = GdipDeleteGraphics(NULL);
+    expect(InvalidParameter, stat);
+    ReleaseDC(0, hdc);
+}
+
+typedef struct node{
+    GraphicsState data;
+    struct node * next;
+} node;
+
+/* Linked list prepend function. */
+static void log_state(GraphicsState data, node ** log)
+{
+    node * new_entry = HeapAlloc(GetProcessHeap(), 0, sizeof(node));
+
+    new_entry->data = data;
+    new_entry->next = *log;
+    *log = new_entry;
+}
+
+/* Checks if there are duplicates in the list, and frees it. */
+static void check_no_duplicates(node * log)
+{
+    INT dups = 0;
+    node * temp = NULL;
+    node * temp2 = NULL;
+    node * orig = log;
+
+    if(!log)
+        goto end;
+
+    do{
+        temp = log;
+        while((temp = temp->next)){
+            if(log->data == temp->data){
+                dups++;
+                break;
+            }
+            if(dups > 0)
+                break;
+        }
+    }while((log = log->next));
+
+    temp = orig;
+    do{
+        temp2 = temp->next;
+        HeapFree(GetProcessHeap(), 0, temp);
+        temp = temp2;
+    }while(temp);
+
+end:
+    expect(0, dups);
+}
+
+static void test_save_restore(void)
+{
+    GpStatus stat;
+    GraphicsState state_a, state_b, state_c;
+    InterpolationMode mode;
+    GpGraphics *graphics1, *graphics2;
+    node * state_log = NULL;
+    HDC hdc = GetDC(0);
+    state_a = state_b = state_c = 0xdeadbeef;
+
+    /* Invalid saving. */
+    GdipCreateFromHDC(hdc, &graphics1);
+    stat = GdipSaveGraphics(graphics1, NULL);
+    expect(InvalidParameter, stat);
+    stat = GdipSaveGraphics(NULL, &state_a);
+    expect(InvalidParameter, stat);
+    GdipDeleteGraphics(graphics1);
+
+    log_state(state_a, &state_log);
+
+    /* Basic save/restore. */
+    GdipCreateFromHDC(hdc, &graphics1);
+    GdipSetInterpolationMode(graphics1, InterpolationModeBilinear);
+    stat = GdipSaveGraphics(graphics1, &state_a);
+    todo_wine
+        expect(Ok, stat);
+    GdipSetInterpolationMode(graphics1, InterpolationModeBicubic);
+    stat = GdipRestoreGraphics(graphics1, state_a);
+    todo_wine
+        expect(Ok, stat);
+    GdipGetInterpolationMode(graphics1, &mode);
+    todo_wine
+        expect(InterpolationModeBilinear, mode);
+    GdipDeleteGraphics(graphics1);
+
+    log_state(state_a, &state_log);
+
+    /* Restoring garbage doesn't affect saves. */
+    GdipCreateFromHDC(hdc, &graphics1);
+    GdipSetInterpolationMode(graphics1, InterpolationModeBilinear);
+    GdipSaveGraphics(graphics1, &state_a);
+    GdipSetInterpolationMode(graphics1, InterpolationModeBicubic);
+    GdipSaveGraphics(graphics1, &state_b);
+    GdipSetInterpolationMode(graphics1, InterpolationModeNearestNeighbor);
+    stat = GdipRestoreGraphics(graphics1, 0xdeadbeef);
+    todo_wine
+        expect(Ok, stat);
+    GdipRestoreGraphics(graphics1, state_b);
+    GdipGetInterpolationMode(graphics1, &mode);
+    todo_wine
+        expect(InterpolationModeBicubic, mode);
+    GdipRestoreGraphics(graphics1, state_a);
+    GdipGetInterpolationMode(graphics1, &mode);
+    todo_wine
+        expect(InterpolationModeBilinear, mode);
+    GdipDeleteGraphics(graphics1);
+
+    log_state(state_a, &state_log);
+    log_state(state_b, &state_log);
+
+    /* Restoring older state invalidates newer saves (but not older saves). */
+    GdipCreateFromHDC(hdc, &graphics1);
+    GdipSetInterpolationMode(graphics1, InterpolationModeBilinear);
+    GdipSaveGraphics(graphics1, &state_a);
+    GdipSetInterpolationMode(graphics1, InterpolationModeBicubic);
+    GdipSaveGraphics(graphics1, &state_b);
+    GdipSetInterpolationMode(graphics1, InterpolationModeNearestNeighbor);
+    GdipSaveGraphics(graphics1, &state_c);
+    GdipSetInterpolationMode(graphics1, InterpolationModeHighQualityBilinear);
+    GdipRestoreGraphics(graphics1, state_b);
+    GdipGetInterpolationMode(graphics1, &mode);
+    todo_wine
+        expect(InterpolationModeBicubic, mode);
+    GdipRestoreGraphics(graphics1, state_c);
+    GdipGetInterpolationMode(graphics1, &mode);
+    todo_wine
+        expect(InterpolationModeBicubic, mode);
+    GdipRestoreGraphics(graphics1, state_a);
+    GdipGetInterpolationMode(graphics1, &mode);
+    todo_wine
+        expect(InterpolationModeBilinear, mode);
+    GdipDeleteGraphics(graphics1);
+
+    log_state(state_a, &state_log);
+    log_state(state_b, &state_log);
+    log_state(state_c, &state_log);
+
+    /* Restoring older save from one graphics object does not invalidate
+     * newer save from other graphics object. */
+    GdipCreateFromHDC(hdc, &graphics1);
+    GdipCreateFromHDC(hdc, &graphics2);
+    GdipSetInterpolationMode(graphics1, InterpolationModeBilinear);
+    GdipSaveGraphics(graphics1, &state_a);
+    GdipSetInterpolationMode(graphics2, InterpolationModeBicubic);
+    GdipSaveGraphics(graphics2, &state_b);
+    GdipSetInterpolationMode(graphics1, InterpolationModeNearestNeighbor);
+    GdipSetInterpolationMode(graphics2, InterpolationModeNearestNeighbor);
+    GdipRestoreGraphics(graphics1, state_a);
+    GdipGetInterpolationMode(graphics1, &mode);
+    todo_wine
+        expect(InterpolationModeBilinear, mode);
+    GdipRestoreGraphics(graphics2, state_b);
+    GdipGetInterpolationMode(graphics2, &mode);
+    todo_wine
+        expect(InterpolationModeBicubic, mode);
+    GdipDeleteGraphics(graphics1);
+    GdipDeleteGraphics(graphics2);
+
+    /* You can't restore a state to a graphics object that didn't save it. */
+    GdipCreateFromHDC(hdc, &graphics1);
+    GdipCreateFromHDC(hdc, &graphics2);
+    GdipSetInterpolationMode(graphics1, InterpolationModeBilinear);
+    GdipSaveGraphics(graphics1, &state_a);
+    GdipSetInterpolationMode(graphics1, InterpolationModeNearestNeighbor);
+    GdipSetInterpolationMode(graphics2, InterpolationModeNearestNeighbor);
+    GdipRestoreGraphics(graphics2, state_a);
+    GdipGetInterpolationMode(graphics2, &mode);
+    expect(InterpolationModeNearestNeighbor, mode);
+    GdipDeleteGraphics(graphics1);
+    GdipDeleteGraphics(graphics2);
+
+    log_state(state_a, &state_log);
+
+    /* The same state value should never be returned twice. */
+    todo_wine
+        check_no_duplicates(state_log);
+
+    ReleaseDC(0, hdc);
+}
+
+static void test_GdipDrawArc(void)
+{
+    GpStatus status;
+    GpGraphics *graphics = NULL;
+    GpPen *pen = NULL;
+    HDC hdc = GetDC(0);
+
+    /* make a graphics object and pen object */
+    status = GdipCreateFromHDC(hdc, &graphics);
+    expect(Ok, status);
+    ok(hdc != NULL, "Expected HDC to be initialized\n");
+
+    status = GdipCreateFromHDC(hdc, &graphics);
+    expect(Ok, status);
+    ok(graphics != NULL, "Expected graphics to be initialized\n");
+
+    status = GdipCreatePen1((ARGB)0xffff00ff, 10.0f, UnitPixel, &pen);
+    expect(Ok, status);
+    ok(pen != NULL, "Expected pen to be initialized\n");
+
+    /* InvalidParameter cases: null graphics, null pen, non-positive width, non-positive height */
+    status = GdipDrawArc(NULL, NULL, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+    expect(InvalidParameter, status);
+
+    status = GdipDrawArc(graphics, NULL, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0);
+    expect(InvalidParameter, status);
+
+    status = GdipDrawArc(NULL, pen, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0);
+    expect(InvalidParameter, status);
+
+    status = GdipDrawArc(graphics, pen, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0);
+    expect(InvalidParameter, status);
+
+    status = GdipDrawArc(graphics, pen, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0);
+    expect(InvalidParameter, status);
+
+    /* successful case */
+    status = GdipDrawArc(graphics, pen, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0);
+    expect(Ok, status);
+
+    GdipDeletePen(pen);
+    ReleaseDC(0, hdc);
+}
+
+static void test_GdipDrawArcI(void)
+{
+    GpStatus status;
+    GpGraphics *graphics = NULL;
+    GpPen *pen = NULL;
+    HDC hdc = GetDC(0);
+
+    /* make a graphics object and pen object */
+    status = GdipCreateFromHDC(hdc, &graphics);
+    expect(Ok, status);
+    ok(hdc != NULL, "Expected HDC to be initialized\n");
+
+    status = GdipCreateFromHDC(hdc, &graphics);
+    expect(Ok, status);
+    ok(graphics != NULL, "Expected graphics to be initialized\n");
+
+    status = GdipCreatePen1((ARGB)0xffff00ff, 10.0f, UnitPixel, &pen);
+    expect(Ok, status);
+    ok(pen != NULL, "Expected pen to be initialized\n");
+
+    /* InvalidParameter cases: null graphics, null pen, non-positive width, non-positive height */
+    status = GdipDrawArcI(NULL, NULL, 0, 0, 0, 0, 0, 0);
+    expect(InvalidParameter, status);
+
+    status = GdipDrawArcI(graphics, NULL, 0, 0, 1, 1, 0, 0);
+    expect(InvalidParameter, status);
+
+    status = GdipDrawArcI(NULL, pen, 0, 0, 1, 1, 0, 0);
+    expect(InvalidParameter, status);
+
+    status = GdipDrawArcI(graphics, pen, 0, 0, 1, 0, 0, 0);
+    expect(InvalidParameter, status);
+
+    status = GdipDrawArcI(graphics, pen, 0, 0, 0, 1, 0, 0);
+    expect(InvalidParameter, status);
+
+    /* successful case */
+    status = GdipDrawArcI(graphics, pen, 0, 0, 1, 1, 0, 0);
+    expect(Ok, status);
+
+    GdipDeletePen(pen);
+    ReleaseDC(0, hdc);
+}
+
+static void test_GdipDrawBezierI(void)
+{
+    GpStatus status;
+    GpGraphics *graphics = NULL;
+    GpPen *pen = NULL;
+    HDC hdc = GetDC(0);
+
+    /* make a graphics object and pen object */
+    status = GdipCreateFromHDC(hdc, &graphics);
+    expect(Ok, status);
+    ok(hdc != NULL, "Expected HDC to be initialized\n");
+
+    status = GdipCreateFromHDC(hdc, &graphics);
+    expect(Ok, status);
+    ok(graphics != NULL, "Expected graphics to be initialized\n");
+
+    status = GdipCreatePen1((ARGB)0xffff00ff, 10.0f, UnitPixel, &pen);
+    expect(Ok, status);
+    ok(pen != NULL, "Expected pen to be initialized\n");
+
+    /* InvalidParameter cases: null graphics, null pen */
+    status = GdipDrawBezierI(NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0);
+    expect(InvalidParameter, status);
+
+    status = GdipDrawBezierI(graphics, NULL, 0, 0, 0, 0, 0, 0, 0, 0);
+    expect(InvalidParameter, status);
+
+    status = GdipDrawBezierI(NULL, pen, 0, 0, 0, 0, 0, 0, 0, 0);
+    expect(InvalidParameter, status);
+
+    /* successful case */
+    status = GdipDrawBezierI(graphics, pen, 0, 0, 0, 0, 0, 0, 0, 0);
+    expect(Ok, status);
+
+    GdipDeletePen(pen);
+    ReleaseDC(0, hdc);
+}
+
+static void test_GdipDrawLineI(void)
+{
+    GpStatus status;
+    GpGraphics *graphics = NULL;
+    GpPen *pen = NULL;
+    HDC hdc = GetDC(0);
+
+    /* make a graphics object and pen object */
+    status = GdipCreateFromHDC(hdc, &graphics);
+    expect(Ok, status);
+    ok(hdc != NULL, "Expected HDC to be initialized\n");
+
+    status = GdipCreateFromHDC(hdc, &graphics);
+    expect(Ok, status);
+    ok(graphics != NULL, "Expected graphics to be initialized\n");
+
+    status = GdipCreatePen1((ARGB)0xffff00ff, 10.0f, UnitPixel, &pen);
+    expect(Ok, status);
+    ok(pen != NULL, "Expected pen to be initialized\n");
+
+    /* InvalidParameter cases: null graphics, null pen */
+    status = GdipDrawLineI(NULL, NULL, 0, 0, 0, 0);
+    expect(InvalidParameter, status);
+
+    status = GdipDrawLineI(graphics, NULL, 0, 0, 0, 0);
+    expect(InvalidParameter, status);
+
+    status = GdipDrawLineI(NULL, pen, 0, 0, 0, 0);
+    expect(InvalidParameter, status);
+
+    /* successful case */
+    status = GdipDrawLineI(graphics, pen, 0, 0, 0, 0);
+    expect(Ok, status);
+
+    GdipDeletePen(pen);
+    ReleaseDC(0, hdc);
+}
+
+static void test_GdipDrawLinesI(void)
+{
+    GpStatus status;
+    GpGraphics *graphics = NULL;
+    GpPen *pen = NULL;
+    GpPoint *ptf = NULL;
+    HDC hdc = GetDC(0);
+
+    /* make a graphics object and pen object */
+    status = GdipCreateFromHDC(hdc, &graphics);
+    expect(Ok, status);
+    ok(hdc != NULL, "Expected HDC to be initialized\n");
+
+    status = GdipCreateFromHDC(hdc, &graphics);
+    expect(Ok, status);
+    ok(graphics != NULL, "Expected graphics to be initialized\n");
+
+    status = GdipCreatePen1((ARGB)0xffff00ff, 10.0f, UnitPixel, &pen);
+    expect(Ok, status);
+    ok(pen != NULL, "Expected pen to be initialized\n");
+
+    /* make some arbitrary valid points*/
+    ptf = GdipAlloc(2 * sizeof(GpPointF));
+
+    ptf[0].X = 1;
+    ptf[0].Y = 1;
+
+    ptf[1].X = 2;
+    ptf[1].Y = 2;
+
+    /* InvalidParameter cases: null graphics, null pen, null points, count < 2*/
+    status = GdipDrawLinesI(NULL, NULL, NULL, 0);
+    expect(InvalidParameter, status);
+
+    status = GdipDrawLinesI(graphics, pen, ptf, 0);
+    expect(InvalidParameter, status);
+
+    status = GdipDrawLinesI(graphics, NULL, ptf, 2);
+    expect(InvalidParameter, status);
+
+    status = GdipDrawLinesI(NULL, pen, ptf, 2);
+    expect(InvalidParameter, status);
+
+    /* successful case */
+    status = GdipDrawLinesI(graphics, pen, ptf, 2);
+    expect(Ok, status);
+
+    GdipFree(ptf);
+    GdipDeletePen(pen);
+    ReleaseDC(0, hdc);
+}
+
+START_TEST(graphics)
+{
+    struct GdiplusStartupInput gdiplusStartupInput;
+    ULONG_PTR gdiplusToken;
+
+    gdiplusStartupInput.GdiplusVersion              = 1;
+    gdiplusStartupInput.DebugEventCallback          = NULL;
+    gdiplusStartupInput.SuppressBackgroundThread    = 0;
+    gdiplusStartupInput.SuppressExternalCodecs      = 0;
+
+    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
+
+    test_constructor_destructor();
+    test_save_restore();
+    test_GdipDrawBezierI();
+    test_GdipDrawArc();
+    test_GdipDrawArcI();
+    test_GdipDrawLineI();
+    test_GdipDrawLinesI();
+
+    GdiplusShutdown(gdiplusToken);
+}
diff --git a/rostests/winetests/gdiplus/graphicspath.c b/rostests/winetests/gdiplus/graphicspath.c
new file mode 100644 (file)
index 0000000..e797b44
--- /dev/null
@@ -0,0 +1,598 @@
+/*
+ * Unit test suite for paths
+ *
+ * Copyright (C) 2007 Google (Evan Stade)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "windows.h"
+#include "gdiplus.h"
+#include "wine/test.h"
+#include <math.h>
+
+#define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
+#define expectf(expected, got) ok(fabs(expected - got) < 2.0, "Expected %.2f, got %.2f\n", expected, got)
+#define POINT_TYPE_MAX_LEN (75)
+
+static void stringify_point_type(PathPointType type, char * name)
+{
+    *name = '\0';
+
+    switch(type & PathPointTypePathTypeMask){
+        case PathPointTypeStart:
+            strcat(name, "PathPointTypeStart");
+            break;
+        case PathPointTypeLine:
+            strcat(name, "PathPointTypeLine");
+            break;
+        case PathPointTypeBezier:
+            strcat(name, "PathPointTypeBezier");
+            break;
+        default:
+            strcat(name, "Unknown type");
+            return;
+    }
+
+    type &= ~PathPointTypePathTypeMask;
+    if(type & ~((PathPointTypePathMarker | PathPointTypeCloseSubpath))){
+        *name = '\0';
+        strcat(name, "Unknown type");
+        return;
+    }
+
+    if(type & PathPointTypePathMarker)
+        strcat(name, " | PathPointTypePathMarker");
+    if(type & PathPointTypeCloseSubpath)
+        strcat(name, " | PathPointTypeCloseSubpath");
+}
+
+/* this helper structure and function modeled after gdi path.c test */
+typedef struct
+{
+    REAL X, Y;
+    BYTE type;
+
+    /* How many extra entries before this one only on wine
+     * but not on native? */
+    int wine_only_entries_preceding;
+
+    /* 0 - This entry matches on wine.
+     * 1 - This entry corresponds to a single entry on wine that does not match the native entry.
+     * 2 - This entry is currently skipped on wine but present on native. */
+    int todo;
+} path_test_t;
+
+static void ok_path(GpPath* path, const path_test_t *expected, INT expected_size, BOOL todo_size)
+{
+    BYTE * types;
+    INT size, idx = 0, eidx = 0, numskip;
+    GpPointF * points;
+    char ename[POINT_TYPE_MAX_LEN], name[POINT_TYPE_MAX_LEN];
+
+    if(GdipGetPointCount(path, &size) != Ok){
+        skip("Cannot perform path comparisons due to failure to retrieve path.\n");
+        return;
+    }
+
+    if(todo_size) todo_wine
+        ok(size == expected_size, "Path size %d does not match expected size %d\n",
+            size, expected_size);
+    else
+        ok(size == expected_size, "Path size %d does not match expected size %d\n",
+            size, expected_size);
+
+    points = HeapAlloc(GetProcessHeap(), 0, size * sizeof(GpPointF));
+    types = HeapAlloc(GetProcessHeap(), 0, size);
+
+    if(GdipGetPathPoints(path, points, size) != Ok || GdipGetPathTypes(path, types, size) != Ok){
+        skip("Cannot perform path comparisons due to failure to retrieve path.\n");
+        goto end;
+    }
+
+    numskip = expected_size ? expected[eidx].wine_only_entries_preceding : 0;
+    while (idx < size && eidx < expected_size){
+        /* We allow a few pixels fudge in matching X and Y coordinates to account for imprecision in
+         * floating point to integer conversion */
+        BOOL match = (types[idx] == expected[eidx].type) &&
+            fabs(points[idx].X - expected[eidx].X) <= 2.0 &&
+            fabs(points[idx].Y - expected[eidx].Y) <= 2.0;
+
+        stringify_point_type(expected[eidx].type, ename);
+        stringify_point_type(types[idx], name);
+
+        if (expected[eidx].todo || numskip) todo_wine
+            ok(match, "Expected #%d: %s (%.1f,%.1f) but got %s (%.1f,%.1f)\n", eidx,
+               ename, expected[eidx].X, expected[eidx].Y,
+               name, points[idx].X, points[idx].Y);
+        else
+            ok(match, "Expected #%d: %s (%.1f,%.1f) but got %s (%.1f,%.1f)\n", eidx,
+               ename, expected[eidx].X, expected[eidx].Y,
+               name, points[idx].X, points[idx].Y);
+
+        if (match || expected[eidx].todo != 2)
+            idx++;
+        if (match || !numskip--)
+            numskip = expected[++eidx].wine_only_entries_preceding;
+    }
+
+end:
+    HeapFree(GetProcessHeap(), 0, types);
+    HeapFree(GetProcessHeap(), 0, points);
+}
+
+static void test_constructor_destructor(void)
+{
+    GpStatus status;
+    GpPath* path = NULL;
+
+    status = GdipCreatePath(FillModeAlternate, &path);
+    expect(Ok, status);
+    ok(path != NULL, "Expected path to be initialized\n");
+
+    status = GdipDeletePath(NULL);
+    expect(InvalidParameter, status);
+
+    status = GdipDeletePath(path);
+    expect(Ok, status);
+}
+
+static path_test_t line2_path[] = {
+    {0.0, 50.0, PathPointTypeStart, 0, 0}, /*0*/
+    {5.0, 45.0, PathPointTypeLine, 0, 0}, /*1*/
+    {0.0, 40.0, PathPointTypeLine, 0, 0}, /*2*/
+    {15.0, 35.0, PathPointTypeLine, 0, 0}, /*3*/
+    {0.0, 30.0, PathPointTypeLine, 0, 0}, /*4*/
+    {25.0, 25.0, PathPointTypeLine | PathPointTypeCloseSubpath, 0, 0}, /*5*/
+    {0.0, 20.0, PathPointTypeStart, 0, 0}, /*6*/
+    {35.0, 15.0, PathPointTypeLine, 0, 0}, /*7*/
+    {0.0, 10.0, PathPointTypeLine, 0, 0} /*8*/
+    };
+
+static void test_line2(void)
+{
+    GpStatus status;
+    GpPath* path;
+    int i;
+    GpPointF line2_points[9];
+
+    for(i = 0; i < 9; i ++){
+        line2_points[i].X = i * 5.0 * (REAL)(i % 2);
+        line2_points[i].Y = 50.0 - i * 5.0;
+    }
+
+    GdipCreatePath(FillModeAlternate, &path);
+    status = GdipAddPathLine2(path, line2_points, 3);
+    expect(Ok, status);
+    status = GdipAddPathLine2(path, &(line2_points[3]), 3);
+    expect(Ok, status);
+    status = GdipClosePathFigure(path);
+    expect(Ok, status);
+    status = GdipAddPathLine2(path, &(line2_points[6]), 3);
+    expect(Ok, status);
+
+    ok_path(path, line2_path, sizeof(line2_path)/sizeof(path_test_t), FALSE);
+}
+
+static path_test_t arc_path[] = {
+    {600.0, 450.0, PathPointTypeStart, 0, 0}, /*0*/
+    {600.0, 643.3, PathPointTypeBezier, 0, 0}, /*1*/
+    {488.1, 800.0, PathPointTypeBezier, 0, 0}, /*2*/
+    {350.0, 800.0, PathPointTypeBezier, 0, 0}, /*3*/
+    {600.0, 450.0, PathPointTypeLine, 0, 0}, /*4*/
+    {600.0, 643.3, PathPointTypeBezier, 0, 0}, /*5*/
+    {488.1, 800.0, PathPointTypeBezier, 0, 0}, /*6*/
+    {350.0, 800.0, PathPointTypeBezier, 0, 0}, /*7*/
+    {329.8, 800.0, PathPointTypeBezier, 0, 0}, /*8*/
+    {309.7, 796.6, PathPointTypeBezier, 0, 0}, /*9*/
+    {290.1, 789.8, PathPointTypeBezier, 0, 0}, /*10*/
+    {409.9, 110.2, PathPointTypeLine, 0, 0}, /*11*/
+    {544.0, 156.5, PathPointTypeBezier, 0, 0}, /*12*/
+    {625.8, 346.2, PathPointTypeBezier, 0, 0}, /*13*/
+    {592.7, 533.9, PathPointTypeBezier, 0, 0}, /*14*/
+    {592.5, 535.3, PathPointTypeBezier, 0, 0}, /*15*/
+    {592.2, 536.7, PathPointTypeBezier, 0, 0}, /*16*/
+    {592.0, 538.1, PathPointTypeBezier, 0, 0}, /*17*/
+    {409.9, 789.8, PathPointTypeLine, 0, 0}, /*18*/
+    {544.0, 743.5, PathPointTypeBezier, 0, 0}, /*19*/
+    {625.8, 553.8, PathPointTypeBezier, 0, 0}, /*20*/
+    {592.7, 366.1, PathPointTypeBezier, 0, 0}, /*21*/
+    {592.5, 364.7, PathPointTypeBezier, 0, 0}, /*22*/
+    {592.2, 363.3, PathPointTypeBezier, 0, 0}, /*23*/
+    {592.0, 361.9, PathPointTypeBezier, 0, 0}, /*24*/
+    {540.4, 676.9, PathPointTypeLine, 0, 0}, /*25*/
+    {629.9, 529.7, PathPointTypeBezier, 0, 0}, /*26*/
+    {617.2, 308.8, PathPointTypeBezier, 0, 0}, /*27*/
+    {512.1, 183.5, PathPointTypeBezier, 0, 0}, /*28*/
+    {406.9, 58.2, PathPointTypeBezier, 0, 0}, /*29*/
+    {249.1, 75.9, PathPointTypeBezier, 0, 0}, /*30*/
+    {159.6, 223.1, PathPointTypeBezier, 0, 0}, /*31*/
+    {70.1, 370.3, PathPointTypeBezier, 0, 0}, /*32*/
+    {82.8, 591.2, PathPointTypeBezier, 0, 0}, /*33*/
+    {187.9, 716.5, PathPointTypeBezier, 0, 0}, /*34*/
+    {293.1, 841.8, PathPointTypeBezier, 0, 0}, /*35*/
+    {450.9, 824.1, PathPointTypeBezier, 0, 0}, /*36*/
+    {540.4, 676.9, PathPointTypeBezier | PathPointTypeCloseSubpath, 0, 1} /*37*/
+    };
+
+static void test_arc(void)
+{
+    GpStatus status;
+    GpPath* path;
+
+    GdipCreatePath(FillModeAlternate, &path);
+    /* Exactly 90 degrees */
+    status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 90.0);
+    expect(Ok, status);
+    /* Over 90 degrees */
+    status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 100.0);
+    expect(Ok, status);
+    /* Negative start angle */
+    status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, -80.0, 100.0);
+    expect(Ok, status);
+    /* Negative sweep angle */
+    status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 80.0, -100.0);
+    expect(Ok, status);
+    /* More than a full revolution */
+    status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 50.0, -400.0);
+    expect(Ok, status);
+    /* 0 sweep angle */
+    status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 50.0, 0.0);
+    expect(Ok, status);
+
+    ok_path(path, arc_path, sizeof(arc_path)/sizeof(path_test_t), FALSE);
+}
+
+static void test_worldbounds(void)
+{
+    GpStatus status;
+    GpPath *path;
+    GpPen *pen;
+    GpMatrix *matrix;
+    GpRectF bounds;
+    GpPointF line2_points[10];
+    int i;
+
+    for(i = 0; i < 10; i ++){
+        line2_points[i].X = 200.0 + i * 50.0 * (i % 2);
+        line2_points[i].Y = 200.0 + i * 50.0 * !(i % 2);
+    }
+    GdipCreatePen1((ARGB)0xdeadbeef, 20.0, UnitWorld, &pen);
+    GdipSetPenEndCap(pen, LineCapSquareAnchor);
+    GdipCreateMatrix2(1.5, 0.0, 1.0, 1.2, 10.4, 10.2, &matrix);
+
+    GdipCreatePath(FillModeAlternate, &path);
+    GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 100.0);
+    GdipAddPathLine2(path, &(line2_points[0]), 10);
+    status = GdipGetPathWorldBounds(path, &bounds, NULL, NULL);
+    expect(Ok, status);
+    GdipDeletePath(path);
+
+    expectf(200.0, bounds.X);
+    expectf(200.0, bounds.Y);
+    expectf(450.0, bounds.Width);
+    expectf(600.0, bounds.Height);
+
+    GdipCreatePath(FillModeAlternate, &path);
+    GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 100.0);
+    GdipAddPathLine2(path, &(line2_points[0]), 10);
+    status = GdipGetPathWorldBounds(path, &bounds, matrix, NULL);
+    expect(Ok, status);
+    GdipDeletePath(path);
+
+    expectf(510.4, bounds.X);
+    expectf(250.2, bounds.Y);
+    expectf(1275.0, bounds.Width);
+    expectf(720.0, bounds.Height);
+
+    GdipCreatePath(FillModeAlternate, &path);
+    GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 100.0);
+    GdipAddPathLine2(path, &(line2_points[0]), 10);
+    status = GdipGetPathWorldBounds(path, &bounds, NULL, pen);
+    expect(Ok, status);
+    GdipDeletePath(path);
+
+    expectf(100.0, bounds.X);
+    expectf(100.0, bounds.Y);
+    expectf(650.0, bounds.Width);
+    expectf(800.0, bounds.Height);
+
+    GdipCreatePath(FillModeAlternate, &path);
+    GdipAddPathLine2(path, &(line2_points[0]), 2);
+    status = GdipGetPathWorldBounds(path, &bounds, NULL, pen);
+    expect(Ok, status);
+    GdipDeletePath(path);
+
+    expectf(156.0, bounds.X);
+    expectf(156.0, bounds.Y);
+    expectf(138.0, bounds.Width);
+    expectf(88.0, bounds.Height);
+
+    line2_points[2].X = 2 * line2_points[1].X - line2_points[0].X;
+    line2_points[2].Y = 2 * line2_points[1].Y - line2_points[0].Y;
+
+    GdipCreatePath(FillModeAlternate, &path);
+    GdipAddPathLine2(path, &(line2_points[0]), 3);
+    status = GdipGetPathWorldBounds(path, &bounds, NULL, pen);
+    expect(Ok, status);
+    GdipDeletePath(path);
+
+    expectf(100.0, bounds.X);
+    expectf(100.0, bounds.Y);
+    expectf(300.0, bounds.Width);
+    expectf(200.0, bounds.Height);
+
+    GdipCreatePath(FillModeAlternate, &path);
+    GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 45.0, 20.0);
+    status = GdipGetPathWorldBounds(path, &bounds, NULL, pen);
+    expect(Ok, status);
+    GdipDeletePath(path);
+
+    expectf(386.7, bounds.X);
+    expectf(553.4, bounds.Y);
+    expectf(266.8, bounds.Width);
+    expectf(289.6, bounds.Height);
+
+    GdipCreatePath(FillModeAlternate, &path);
+    status = GdipGetPathWorldBounds(path, &bounds, matrix, pen);
+    expect(Ok, status);
+    GdipDeletePath(path);
+
+    expectf(0.0, bounds.X);
+    expectf(0.0, bounds.Y);
+    expectf(0.0, bounds.Width);
+    expectf(0.0, bounds.Height);
+
+    GdipCreatePath(FillModeAlternate, &path);
+    GdipAddPathLine2(path, &(line2_points[0]), 2);
+    status = GdipGetPathWorldBounds(path, &bounds, matrix, pen);
+    expect(Ok, status);
+    GdipDeletePath(path);
+
+    todo_wine{
+        expectf(427.9, bounds.X);
+        expectf(167.7, bounds.Y);
+        expectf(239.9, bounds.Width);
+        expectf(164.9, bounds.Height);
+    }
+
+    GdipDeleteMatrix(matrix);
+    GdipCreateMatrix2(0.9, -0.5, -0.5, -1.2, 10.4, 10.2, &matrix);
+    GdipCreatePath(FillModeAlternate, &path);
+    GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 100.0);
+    GdipAddPathLine2(path, &(line2_points[0]), 10);
+    status = GdipGetPathWorldBounds(path, &bounds, matrix, NULL);
+    expect(Ok, status);
+    GdipDeletePath(path);
+
+    expectf(-209.6, bounds.X);
+    expectf(-1274.8, bounds.Y);
+    expectf(705.0, bounds.Width);
+    expectf(945.0, bounds.Height);
+}
+
+static path_test_t pathpath_path[] = {
+    {600.00, 450.00, PathPointTypeStart, 0, 0}, /*0*/
+    {600.00, 643.30, PathPointTypeBezier, 0, 0}, /*1*/
+    {488.07, 800.00, PathPointTypeBezier, 0, 0}, /*2*/
+    {350.00, 800.00, PathPointTypeBezier, 0, 0}, /*3*/
+    {319.61, 797.40, PathPointTypeStart, 0, 0}, /*4*/
+    {182.56, 773.90, PathPointTypeBezier, 0, 0}, /*5*/
+    {85.07, 599.31, PathPointTypeBezier, 0, 0}, /*6*/
+    {101.85, 407.45, PathPointTypeBezier, 0, 0}, /*7*/
+    {102.54, 399.66, PathPointTypeBezier, 0, 0}, /*8*/
+    {103.40, 391.91, PathPointTypeBezier, 0, 0}, /*9*/
+    {104.46, 384.21, PathPointTypeBezier, 0, 0}, /*10*/
+    {409.92, 110.20, PathPointTypeLine, 0, 0}, /*11*/
+    {543.96, 156.53, PathPointTypeBezier, 0, 0}, /*12*/
+    {625.80, 346.22, PathPointTypeBezier, 0, 0}, /*13*/
+    {592.71, 533.88, PathPointTypeBezier, 0, 0}, /*14*/
+    {592.47, 535.28, PathPointTypeBezier, 0, 0}, /*15*/
+    {592.22, 536.67, PathPointTypeBezier, 0, 0}, /*16*/
+    {591.96, 538.06, PathPointTypeBezier, 0, 0}, /*17*/
+    {319.61, 797.40, PathPointTypeLine, 0, 0}, /*18*/
+    {182.56, 773.90, PathPointTypeBezier, 0, 0}, /*19*/
+    {85.07, 599.31, PathPointTypeBezier, 0, 0}, /*20*/
+    {101.85, 407.45, PathPointTypeBezier, 0, 0}, /*21*/
+    {102.54, 399.66, PathPointTypeBezier, 0, 0}, /*22*/
+    {103.40, 391.91, PathPointTypeBezier, 0, 0}, /*23*/
+    {104.46, 384.21, PathPointTypeBezier, 0, 0} /*24*/
+    };
+
+static void test_pathpath(void)
+{
+    GpStatus status;
+    GpPath* path1, *path2;
+
+    GdipCreatePath(FillModeAlternate, &path2);
+    GdipAddPathArc(path2, 100.0, 100.0, 500.0, 700.0, 95.0, 100.0);
+
+    GdipCreatePath(FillModeAlternate, &path1);
+    GdipAddPathArc(path1, 100.0, 100.0, 500.0, 700.0, 0.0, 90.0);
+    status = GdipAddPathPath(path1, path2, FALSE);
+    expect(Ok, status);
+    GdipAddPathArc(path1, 100.0, 100.0, 500.0, 700.0, -80.0, 100.0);
+    status = GdipAddPathPath(path1, path2, TRUE);
+    expect(Ok, status);
+
+    ok_path(path1, pathpath_path, sizeof(pathpath_path)/sizeof(path_test_t), FALSE);
+
+    GdipDeletePath(path1);
+    GdipDeletePath(path2);
+}
+
+static path_test_t ellipse_path[] = {
+    {30.00, 125.25, PathPointTypeStart, 0, 0}, /*0*/
+    {30.00, 139.20, PathPointTypeBezier, 0, 0}, /*1*/
+    {25.52, 150.50, PathPointTypeBezier, 0, 0}, /*2*/
+    {20.00, 150.50, PathPointTypeBezier, 0, 0}, /*3*/
+    {14.48, 150.50, PathPointTypeBezier, 0, 0}, /*4*/
+    {10.00, 139.20, PathPointTypeBezier, 0, 0}, /*5*/
+    {10.00, 125.25, PathPointTypeBezier, 0, 0}, /*6*/
+    {10.00, 111.30, PathPointTypeBezier, 0, 0}, /*7*/
+    {14.48, 100.00, PathPointTypeBezier, 0, 0}, /*8*/
+    {20.00, 100.00, PathPointTypeBezier, 0, 0}, /*9*/
+    {25.52, 100.00, PathPointTypeBezier, 0, 0}, /*10*/
+    {30.00, 111.30, PathPointTypeBezier, 0, 0}, /*11*/
+    {30.00, 125.25, PathPointTypeBezier | PathPointTypeCloseSubpath, 0, 0}, /*12*/
+    {7.00, 11.00, PathPointTypeStart, 0, 0}, /*13*/
+    {13.00, 17.00, PathPointTypeLine, 0, 0}, /*14*/
+    {5.00, 195.00, PathPointTypeStart, 0, 0}, /*15*/
+    {5.00, 192.24, PathPointTypeBezier, 0, 0}, /*16*/
+    {6.12, 190.00, PathPointTypeBezier, 0, 0}, /*17*/
+    {7.50, 190.00, PathPointTypeBezier, 0, 0}, /*18*/
+    {8.88, 190.00, PathPointTypeBezier, 0, 0}, /*19*/
+    {10.00, 192.24, PathPointTypeBezier, 0, 0}, /*20*/
+    {10.00, 195.00, PathPointTypeBezier, 0, 0}, /*21*/
+    {10.00, 197.76, PathPointTypeBezier, 0, 0}, /*22*/
+    {8.88, 200.00, PathPointTypeBezier, 0, 0}, /*23*/
+    {7.50, 200.00, PathPointTypeBezier, 0, 0}, /*24*/
+    {6.12, 200.00, PathPointTypeBezier, 0, 0}, /*25*/
+    {5.00, 197.76, PathPointTypeBezier, 0, 0}, /*26*/
+    {5.00, 195.00, PathPointTypeBezier | PathPointTypeCloseSubpath, 0, 0}, /*27*/
+    {10.00, 300.50, PathPointTypeStart, 0, 0}, /*28*/
+    {10.00, 300.78, PathPointTypeBezier, 0, 0}, /*29*/
+    {10.00, 301.00, PathPointTypeBezier, 0, 0}, /*30*/
+    {10.00, 301.00, PathPointTypeBezier, 0, 0}, /*31*/
+    {10.00, 301.00, PathPointTypeBezier, 0, 0}, /*32*/
+    {10.00, 300.78, PathPointTypeBezier, 0, 0}, /*33*/
+    {10.00, 300.50, PathPointTypeBezier, 0, 0}, /*34*/
+    {10.00, 300.22, PathPointTypeBezier, 0, 0}, /*35*/
+    {10.00, 300.00, PathPointTypeBezier, 0, 0}, /*36*/
+    {10.00, 300.00, PathPointTypeBezier, 0, 0}, /*37*/
+    {10.00, 300.00, PathPointTypeBezier, 0, 0}, /*38*/
+    {10.00, 300.22, PathPointTypeBezier, 0, 0}, /*39*/
+    {10.00, 300.50, PathPointTypeBezier | PathPointTypeCloseSubpath, 0, 0} /*40*/
+    };
+
+static void test_ellipse(void)
+{
+    GpStatus status;
+    GpPath *path;
+    GpPointF points[2];
+
+    points[0].X = 7.0;
+    points[0].Y = 11.0;
+    points[1].X = 13.0;
+    points[1].Y = 17.0;
+
+    GdipCreatePath(FillModeAlternate, &path);
+    status = GdipAddPathEllipse(path, 10.0, 100.0, 20.0, 50.5);
+    expect(Ok, status);
+    GdipAddPathLine2(path, points, 2);
+    status = GdipAddPathEllipse(path, 10.0, 200.0, -5.0, -10.0);
+    expect(Ok, status);
+    GdipClosePathFigure(path);
+    status = GdipAddPathEllipse(path, 10.0, 300.0, 0.0, 1.0);
+    expect(Ok, status);
+
+    ok_path(path, ellipse_path, sizeof(ellipse_path)/sizeof(path_test_t), FALSE);
+
+    GdipDeletePath(path);
+}
+
+static path_test_t linei_path[] = {
+    {5.00, 5.00, PathPointTypeStart, 0, 0}, /*0*/
+    {6.00, 8.00, PathPointTypeLine, 0, 0}, /*1*/
+    {409.92, 110.20, PathPointTypeLine, 0, 0}, /*2*/
+    {543.96, 156.53, PathPointTypeBezier, 0, 0}, /*3*/
+    {625.80, 346.22, PathPointTypeBezier, 0, 0}, /*4*/
+    {592.71, 533.88, PathPointTypeBezier, 0, 0}, /*5*/
+    {592.47, 535.28, PathPointTypeBezier, 0, 0}, /*6*/
+    {592.22, 536.67, PathPointTypeBezier, 0, 0}, /*7*/
+    {591.96, 538.06, PathPointTypeBezier, 0, 0}, /*8*/
+    {15.00, 15.00, PathPointTypeLine, 0, 0}, /*9*/
+    {26.00, 28.00, PathPointTypeLine | PathPointTypeCloseSubpath, 0, 0}, /*10*/
+    {35.00, 35.00, PathPointTypeStart, 0, 0}, /*11*/
+    {36.00, 38.00, PathPointTypeLine, 0, 0} /*12*/
+    };
+
+static void test_linei(void)
+{
+    GpStatus status;
+    GpPath *path;
+    GpPointF points[2];
+
+    points[0].X = 7.0;
+    points[0].Y = 11.0;
+    points[1].X = 13.0;
+    points[1].Y = 17.0;
+
+    GdipCreatePath(FillModeAlternate, &path);
+    status = GdipAddPathLineI(path, 5.0, 5.0, 6.0, 8.0);
+    expect(Ok, status);
+    GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, -80.0, 100.0);
+    status = GdipAddPathLineI(path, 15.0, 15.0, 26.0, 28.0);
+    expect(Ok, status);
+    GdipClosePathFigure(path);
+    status = GdipAddPathLineI(path, 35.0, 35.0, 36.0, 38.0);
+    expect(Ok, status);
+
+    ok_path(path, linei_path, sizeof(linei_path)/sizeof(path_test_t), FALSE);
+
+    GdipDeletePath(path);
+}
+
+static path_test_t rect_path[] = {
+    {5.0, 5.0,       PathPointTypeStart, 0, 0}, /*0*/
+    {105.0, 5.0,     PathPointTypeLine,  0, 0}, /*1*/
+    {105.0, 55.0,    PathPointTypeLine,  0, 0}, /*2*/
+    {5.0, 55.0,      PathPointTypeLine | PathPointTypeCloseSubpath, 0, 0}, /*3*/
+
+    {100.0, 50.0,    PathPointTypeStart, 0, 0}, /*4*/
+    {220.0, 50.0,    PathPointTypeLine,  0, 0}, /*5*/
+    {220.0, 80.0,    PathPointTypeLine,  0, 0}, /*6*/
+    {100.0, 80.0,    PathPointTypeLine | PathPointTypeCloseSubpath, 0, 0}  /*7*/
+    };
+
+static void test_rect(void)
+{
+    GpStatus status;
+    GpPath *path;
+
+    GdipCreatePath(FillModeAlternate, &path);
+    status = GdipAddPathRectangle(path, 5.0, 5.0, 100.0, 50.0);
+    expect(Ok, status);
+    status = GdipAddPathRectangle(path, 100.0, 50.0, 120.0, 30.0);
+    expect(Ok, status);
+
+    ok_path(path, rect_path, sizeof(rect_path)/sizeof(path_test_t), FALSE);
+
+    GdipDeletePath(path);
+}
+
+START_TEST(graphicspath)
+{
+    struct GdiplusStartupInput gdiplusStartupInput;
+    ULONG_PTR gdiplusToken;
+
+    gdiplusStartupInput.GdiplusVersion              = 1;
+    gdiplusStartupInput.DebugEventCallback          = NULL;
+    gdiplusStartupInput.SuppressBackgroundThread    = 0;
+    gdiplusStartupInput.SuppressExternalCodecs      = 0;
+
+    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
+
+    test_constructor_destructor();
+    test_line2();
+    test_arc();
+    test_worldbounds();
+    test_pathpath();
+    test_ellipse();
+    test_linei();
+    test_rect();
+
+    GdiplusShutdown(gdiplusToken);
+}
diff --git a/rostests/winetests/gdiplus/image.c b/rostests/winetests/gdiplus/image.c
new file mode 100644 (file)
index 0000000..a3e7676
--- /dev/null
@@ -0,0 +1,477 @@
+/*
+ * Unit test suite for images
+ *
+ * Copyright (C) 2007 Google (Evan Stade)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "windows.h"
+#include "gdiplus.h"
+#include "wine/test.h"
+#include <math.h>
+#include "wingdi.h"
+
+#define expect(expected, got) ok(((UINT)got) == ((UINT)expected), "Expected %.8x, got %.8x\n", (UINT)expected, (UINT)got)
+
+static void test_Scan0(void)
+{
+    GpBitmap *bm;
+    GpStatus stat;
+    BYTE buff[360];
+
+    bm = NULL;
+    stat = GdipCreateBitmapFromScan0(10, 10, 10, PixelFormat24bppRGB, NULL, &bm);
+    expect(Ok, stat);
+    ok(NULL != bm, "Expected bitmap to be initialized\n");
+    if (stat == Ok)
+        GdipDisposeImage((GpImage*)bm);
+
+    bm = (GpBitmap*)0xdeadbeef;
+    stat = GdipCreateBitmapFromScan0(10, -10, 10, PixelFormat24bppRGB, NULL, &bm);
+    expect(InvalidParameter, stat);
+
+    expect(NULL, bm);
+
+    bm = (GpBitmap*)0xdeadbeef;
+    stat = GdipCreateBitmapFromScan0(-10, 10, 10, PixelFormat24bppRGB, NULL, &bm);
+    expect(InvalidParameter, stat);
+
+    expect(NULL, bm);
+
+    bm = (GpBitmap*)0xdeadbeef;
+    stat = GdipCreateBitmapFromScan0(10, 0, 10, PixelFormat24bppRGB, NULL, &bm);
+    expect(InvalidParameter, stat);
+
+    expect(NULL, bm);
+
+    bm = NULL;
+    stat = GdipCreateBitmapFromScan0(10, 10, 12, PixelFormat24bppRGB, buff, &bm);
+    expect(Ok, stat);
+    ok(NULL != bm, "Expected bitmap to be initialized\n");
+    if (stat == Ok)
+        GdipDisposeImage((GpImage*)bm);
+
+    bm = (GpBitmap*) 0xdeadbeef;
+    stat = GdipCreateBitmapFromScan0(10, 10, 10, PixelFormat24bppRGB, buff, &bm);
+    expect(InvalidParameter, stat);
+    expect(NULL, bm);
+
+    bm = (GpBitmap*)0xdeadbeef;
+    stat = GdipCreateBitmapFromScan0(10, 10, 0, PixelFormat24bppRGB, buff, &bm);
+    expect(InvalidParameter, stat);
+    expect(0xdeadbeef, bm);
+}
+
+static void test_GetImageDimension(void)
+{
+    GpBitmap *bm;
+    GpStatus stat;
+    const REAL WIDTH = 10.0, HEIGHT = 20.0;
+    REAL w,h;
+
+    bm = (GpBitmap*)0xdeadbeef;
+    stat = GdipCreateBitmapFromScan0(WIDTH, HEIGHT, 0, PixelFormat24bppRGB,NULL, &bm);
+    expect(Ok,stat);
+    ok((GpBitmap*)0xdeadbeef != bm, "Expected bitmap to not be 0xdeadbeef\n");
+    ok(NULL != bm, "Expected bitmap to not be NULL\n");
+
+    stat = GdipGetImageDimension(NULL,&w,&h);
+    expect(InvalidParameter, stat);
+
+    stat = GdipGetImageDimension((GpImage*)bm,NULL,&h);
+    expect(InvalidParameter, stat);
+
+    stat = GdipGetImageDimension((GpImage*)bm,&w,NULL);
+    expect(InvalidParameter, stat);
+
+    w = -1;
+    h = -1;
+    stat = GdipGetImageDimension((GpImage*)bm,&w,&h);
+    expect(Ok, stat);
+    ok(fabs(WIDTH - w) < 0.0001, "Width wrong\n");
+    ok(fabs(HEIGHT - h) < 0.0001, "Height wrong\n");
+    GdipDisposeImage((GpImage*)bm);
+}
+
+static void test_LoadingImages(void)
+{
+    GpStatus stat;
+
+    stat = GdipCreateBitmapFromFile(0, 0);
+    expect(InvalidParameter, stat);
+
+    stat = GdipCreateBitmapFromFile(0, (GpBitmap**)0xdeadbeef);
+    expect(InvalidParameter, stat);
+
+    stat = GdipLoadImageFromFile(0, 0);
+    expect(InvalidParameter, stat);
+
+    stat = GdipLoadImageFromFile(0, (GpImage**)0xdeadbeef);
+    expect(InvalidParameter, stat);
+
+    stat = GdipLoadImageFromFileICM(0, 0);
+    expect(InvalidParameter, stat);
+
+    stat = GdipLoadImageFromFileICM(0, (GpImage**)0xdeadbeef);
+    expect(InvalidParameter, stat);
+}
+
+static void test_SavingImages(void)
+{
+    GpStatus stat;
+    GpBitmap *bm;
+    UINT n;
+    UINT s;
+    const REAL WIDTH = 10.0, HEIGHT = 20.0;
+    REAL w, h;
+    ImageCodecInfo *codecs;
+    static const WCHAR filename[] = { 'a','.','b','m','p',0 };
+
+    codecs = NULL;
+
+    stat = GdipSaveImageToFile(0, 0, 0, 0);
+    expect(InvalidParameter, stat);
+
+    bm = NULL;
+    stat = GdipCreateBitmapFromScan0(WIDTH, HEIGHT, 0, PixelFormat24bppRGB, NULL, &bm);
+    expect(Ok, stat);
+    if (!bm)
+        return;
+
+    /* invalid params */
+    stat = GdipSaveImageToFile((GpImage*)bm, 0, 0, 0);
+    expect(InvalidParameter, stat);
+
+    stat = GdipSaveImageToFile((GpImage*)bm, filename, 0, 0);
+    expect(InvalidParameter, stat);
+
+    /* encoder tests should succeed -- already tested */
+    stat = GdipGetImageEncodersSize(&n, &s);
+    if (stat != Ok || n == 0) goto cleanup;
+
+    codecs = GdipAlloc(s);
+    if (!codecs) goto cleanup;
+
+    stat = GdipGetImageEncoders(n, s, codecs);
+    if (stat != Ok) goto cleanup;
+
+    stat = GdipSaveImageToFile((GpImage*)bm, filename, &codecs[0].Clsid, 0);
+    expect(stat, Ok);
+
+    GdipDisposeImage((GpImage*)bm);
+    bm = 0;
+
+    /* re-load and check image stats */
+    stat = GdipLoadImageFromFile(filename, (GpImage**)&bm);
+    expect(stat, Ok);
+    if (stat != Ok) goto cleanup;
+
+    stat = GdipGetImageDimension((GpImage*)bm, &w, &h);
+    if (stat != Ok) goto cleanup;
+
+    ok((fabs(w - WIDTH) < 0.01) && (fabs(h - HEIGHT) < 0.01),
+       "Saved image dimensions are different!\n");
+
+ cleanup:
+    GdipFree(codecs);
+    if (bm)
+        GdipDisposeImage((GpImage*)bm);
+    ok(DeleteFileW(filename), "Delete failed.\n");
+}
+
+static void test_encoders(void)
+{
+    GpStatus stat;
+    UINT n;
+    UINT s;
+    ImageCodecInfo *codecs;
+    int i;
+    int bmp_found;
+
+    static const WCHAR bmp_format[] = {'B', 'M', 'P', 0};
+
+    stat = GdipGetImageEncodersSize(&n, &s);
+    expect(stat, Ok);
+
+    codecs = GdipAlloc(s);
+    if (!codecs)
+        return;
+
+    stat = GdipGetImageEncoders(n, s, NULL);
+    expect(GenericError, stat);
+
+    stat = GdipGetImageEncoders(0, s, codecs);
+    expect(GenericError, stat);
+
+    stat = GdipGetImageEncoders(n, s-1, codecs);
+    expect(GenericError, stat);
+
+    stat = GdipGetImageEncoders(n, s+1, codecs);
+    expect(GenericError, stat);
+
+    stat = GdipGetImageEncoders(n, s, codecs);
+    expect(stat, Ok);
+
+    bmp_found = FALSE;
+    for (i = 0; i < n; i++)
+        {
+            if (CompareStringW(LOCALE_SYSTEM_DEFAULT, 0,
+                               codecs[i].FormatDescription, -1,
+                               bmp_format, -1) == CSTR_EQUAL) {
+                bmp_found = TRUE;
+                break;
+            }
+        }
+    if (!bmp_found)
+        ok(FALSE, "No BMP codec found.\n");
+
+    GdipFree(codecs);
+}
+
+static void test_LockBits(void)
+{
+    GpStatus stat;
+    GpBitmap *bm;
+    GpRect rect;
+    BitmapData bd;
+    const INT WIDTH = 10, HEIGHT = 20;
+
+    bm = NULL;
+    stat = GdipCreateBitmapFromScan0(WIDTH, HEIGHT, 0, PixelFormat24bppRGB, NULL, &bm);
+    expect(Ok, stat);
+
+    rect.X = 2;
+    rect.Y = 3;
+    rect.Width = 4;
+    rect.Height = 5;
+
+    /* read-only */
+    stat = GdipBitmapLockBits(bm, &rect, ImageLockModeRead, PixelFormat24bppRGB, &bd);
+    expect(Ok, stat);
+
+    if (stat == Ok) {
+        stat = GdipBitmapUnlockBits(bm, &bd);
+        expect(Ok, stat);
+    }
+
+    /* read-only, with NULL rect -> whole bitmap lock */
+    stat = GdipBitmapLockBits(bm, NULL, ImageLockModeRead, PixelFormat24bppRGB, &bd);
+    expect(Ok, stat);
+    expect(bd.Width,  WIDTH);
+    expect(bd.Height, HEIGHT);
+
+    if (stat == Ok) {
+        stat = GdipBitmapUnlockBits(bm, &bd);
+        expect(Ok, stat);
+    }
+
+    /* read-only, consecutive */
+    stat = GdipBitmapLockBits(bm, &rect, ImageLockModeRead, PixelFormat24bppRGB, &bd);
+    expect(Ok, stat);
+
+    if (stat == Ok) {
+        stat = GdipBitmapUnlockBits(bm, &bd);
+        expect(Ok, stat);
+    }
+
+    stat = GdipDisposeImage((GpImage*)bm);
+    expect(Ok, stat);
+    stat = GdipCreateBitmapFromScan0(WIDTH, HEIGHT, 0, PixelFormat24bppRGB, NULL, &bm);
+    expect(Ok, stat);
+
+    /* read x2 */
+    stat = GdipBitmapLockBits(bm, &rect, ImageLockModeRead, PixelFormat24bppRGB, &bd);
+    expect(Ok, stat);
+    stat = GdipBitmapLockBits(bm, &rect, ImageLockModeRead, PixelFormat24bppRGB, &bd);
+    expect(WrongState, stat);
+
+    stat = GdipBitmapUnlockBits(bm, &bd);
+    expect(Ok, stat);
+
+    stat = GdipDisposeImage((GpImage*)bm);
+    expect(Ok, stat);
+    stat = GdipCreateBitmapFromScan0(WIDTH, HEIGHT, 0, PixelFormat24bppRGB, NULL, &bm);
+    expect(Ok, stat);
+
+    /* write, no modification */
+    stat = GdipBitmapLockBits(bm, &rect, ImageLockModeWrite, PixelFormat24bppRGB, &bd);
+    expect(Ok, stat);
+
+    if (stat == Ok) {
+        stat = GdipBitmapUnlockBits(bm, &bd);
+        expect(Ok, stat);
+    }
+
+    /* write, consecutive */
+    stat = GdipBitmapLockBits(bm, &rect, ImageLockModeWrite, PixelFormat24bppRGB, &bd);
+    expect(Ok, stat);
+
+    if (stat == Ok) {
+        stat = GdipBitmapUnlockBits(bm, &bd);
+        expect(Ok, stat);
+    }
+
+    stat = GdipDisposeImage((GpImage*)bm);
+    expect(Ok, stat);
+    stat = GdipCreateBitmapFromScan0(WIDTH, HEIGHT, 0, PixelFormat24bppRGB, NULL, &bm);
+    expect(Ok, stat);
+
+    /* write, modify */
+    stat = GdipBitmapLockBits(bm, &rect, ImageLockModeWrite, PixelFormat24bppRGB, &bd);
+    expect(Ok, stat);
+
+    if (stat == Ok) {
+        if (bd.Scan0)
+            ((char*)bd.Scan0)[2] = 0xff;
+
+        stat = GdipBitmapUnlockBits(bm, &bd);
+        expect(Ok, stat);
+    }
+
+    stat = GdipDisposeImage((GpImage*)bm);
+    expect(Ok, stat);
+
+    /* dispose locked */
+    stat = GdipCreateBitmapFromScan0(WIDTH, HEIGHT, 0, PixelFormat24bppRGB, NULL, &bm);
+    expect(Ok, stat);
+    stat = GdipBitmapLockBits(bm, &rect, ImageLockModeRead, PixelFormat24bppRGB, &bd);
+    expect(Ok, stat);
+    stat = GdipDisposeImage((GpImage*)bm);
+    expect(Ok, stat);
+}
+
+static void test_GdipCreateBitmapFromHBITMAP(void)
+{
+    GpBitmap* gpbm = NULL;
+    HBITMAP hbm = NULL;
+    HPALETTE hpal = NULL;
+    GpStatus stat;
+    BYTE buff[1000];
+    LOGPALETTE* LogPal = NULL;
+    REAL width, height;
+    const REAL WIDTH1 = 5;
+    const REAL HEIGHT1 = 15;
+    const REAL WIDTH2 = 10;
+    const REAL HEIGHT2 = 20;
+    HDC hdc;
+    BITMAPINFO bmi;
+
+    stat = GdipCreateBitmapFromHBITMAP(NULL, NULL, NULL);
+    expect(InvalidParameter, stat);
+
+    hbm = CreateBitmap(WIDTH1, HEIGHT1, 1, 1, NULL);
+    stat = GdipCreateBitmapFromHBITMAP(hbm, NULL, NULL);
+    expect(InvalidParameter, stat);
+
+    stat = GdipCreateBitmapFromHBITMAP(hbm, NULL, &gpbm);
+    expect(Ok, stat);
+    expect(Ok, GdipGetImageDimension((GpImage*) gpbm, &width, &height));
+    ok(fabs(WIDTH1 - width) < .0001, "width wrong\n");
+    ok(fabs(HEIGHT1 - height) < .0001, "height wrong\n");
+    if (stat == Ok)
+        GdipDisposeImage((GpImage*)gpbm);
+    GlobalFree(hbm);
+
+    hbm = CreateBitmap(WIDTH2, HEIGHT2, 1, 1, &buff);
+    stat = GdipCreateBitmapFromHBITMAP(hbm, NULL, &gpbm);
+    expect(Ok, stat);
+    expect(Ok, GdipGetImageDimension((GpImage*) gpbm, &width, &height));
+    ok(fabs(WIDTH2 - width) < .0001, "width wrong\n");
+    ok(fabs(HEIGHT2 - height) < .0001, "height wrong\n");
+    if (stat == Ok)
+        GdipDisposeImage((GpImage*)gpbm);
+    GlobalFree(hbm);
+
+    hdc = CreateCompatibleDC(0);
+    ok(hdc != NULL, "CreateCompatibleDC failed\n");
+    bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
+    bmi.bmiHeader.biHeight = HEIGHT1;
+    bmi.bmiHeader.biWidth = WIDTH1;
+    bmi.bmiHeader.biBitCount = 24;
+    bmi.bmiHeader.biPlanes = 1;
+    bmi.bmiHeader.biCompression = BI_RGB;
+
+    hbm = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, NULL, NULL, 0);
+    ok(hbm != NULL, "CreateDIBSection failed\n");
+
+    stat = GdipCreateBitmapFromHBITMAP(hbm, NULL, &gpbm);
+    expect(Ok, stat);
+    expect(Ok, GdipGetImageDimension((GpImage*) gpbm, &width, &height));
+    ok(fabs(WIDTH1 - width) < .0001, "width wrong\n");
+    ok(fabs(HEIGHT1 - height) < .0001, "height wrong\n");
+    if (stat == Ok)
+        GdipDisposeImage((GpImage*)gpbm);
+
+    LogPal = GdipAlloc(sizeof(LOGPALETTE));
+    ok(LogPal != NULL, "unable to allocate LOGPALETTE\n");
+    LogPal->palVersion = 0x300;
+    hpal = CreatePalette((const LOGPALETTE*) LogPal);
+    ok(hpal != NULL, "CreatePalette failed\n");
+    GdipFree(LogPal);
+
+    stat = GdipCreateBitmapFromHBITMAP(hbm, hpal, &gpbm);
+    todo_wine
+    {
+        expect(Ok, stat);
+    }
+    if (stat == Ok)
+        GdipDisposeImage((GpImage*)gpbm);
+
+    GlobalFree(hpal);
+    GlobalFree(hbm);
+}
+
+static void test_GdipGetImageFlags(void)
+{
+    GpImage *img;
+    GpStatus stat;
+    UINT flags;
+
+    img = (GpImage*)0xdeadbeef;
+
+    stat = GdipGetImageFlags(NULL, NULL);
+    expect(InvalidParameter, stat);
+
+    stat = GdipGetImageFlags(NULL, &flags);
+    expect(InvalidParameter, stat);
+
+    stat = GdipGetImageFlags(img, NULL);
+    expect(InvalidParameter, stat);
+}
+
+START_TEST(image)
+{
+    struct GdiplusStartupInput gdiplusStartupInput;
+    ULONG_PTR gdiplusToken;
+
+    gdiplusStartupInput.GdiplusVersion              = 1;
+    gdiplusStartupInput.DebugEventCallback          = NULL;
+    gdiplusStartupInput.SuppressBackgroundThread    = 0;
+    gdiplusStartupInput.SuppressExternalCodecs      = 0;
+
+    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
+
+    test_Scan0();
+    test_GetImageDimension();
+    test_LoadingImages();
+    test_SavingImages();
+    test_encoders();
+    test_LockBits();
+    test_GdipCreateBitmapFromHBITMAP();
+    test_GdipGetImageFlags();
+
+    GdiplusShutdown(gdiplusToken);
+}
diff --git a/rostests/winetests/gdiplus/matrix.c b/rostests/winetests/gdiplus/matrix.c
new file mode 100644 (file)
index 0000000..cc53488
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Unit test suite for matrices
+ *
+ * Copyright (C) 2007 Google (Evan Stade)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <math.h>
+
+#include "windows.h"
+#include "gdiplus.h"
+#include "wine/test.h"
+
+#define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
+
+static void test_constructor_destructor(void)
+{
+    GpStatus status;
+    GpMatrix *matrix = NULL;
+
+    status = GdipCreateMatrix2(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, &matrix);
+    expect(Ok, status);
+    ok(matrix != NULL, "Expected matrix to be initialized\n");
+
+    status = GdipDeleteMatrix(NULL);
+    expect(InvalidParameter, status);
+
+    status = GdipDeleteMatrix(matrix);
+    expect(Ok, status);
+}
+
+typedef struct{
+    REAL X;
+    REAL Y;
+} real_point;
+
+static real_point transform_points[] = {
+    {1000.00, 2600.00}, /*0*/
+    {855.00, 2390.00}, /*1*/
+    {700.00, 2200.00}, /*2*/
+    {565.00, 1970.00}, /*3*/
+    {400.00, 1800.00}, /*4*/
+    {275.00, 1550.00}, /*5*/
+    {100.00, 1400.00}, /*6*/
+    {-15.00, 1130.00}, /*7*/
+    {-200.00, 1000.00}, /*8*/
+    {-305.00, 710.00} /*9*/
+    };
+
+static void test_transform(void)
+{
+    GpStatus status;
+    GpMatrix *matrix = NULL;
+    GpPointF pts[10];
+    INT i;
+    BOOL match;
+
+    for(i = 0; i < 10; i ++){
+        pts[i].X = i * 5.0 * (REAL)(i % 2);
+        pts[i].Y = 50.0 - i * 5.0;
+    }
+
+    GdipCreateMatrix2(1.0, -2.0, 30.0, 40.0, -500.0, 600.0, &matrix);
+
+    status = GdipTransformMatrixPoints(matrix, pts, 10);
+    expect(Ok, status);
+
+    for(i = 0; i < 10; i ++){
+        match = fabs(transform_points[i].X - pts[i].X) < 2.0
+            && fabs(transform_points[i].Y -  pts[i].Y) < 2.0;
+
+        ok(match, "Expected #%d to be (%.2f, %.2f) but got (%.2f, %.2f)\n", i,
+           transform_points[i].X, transform_points[i].Y, pts[i].X, pts[i].Y);
+    }
+
+    GdipDeleteMatrix(matrix);
+}
+
+START_TEST(matrix)
+{
+    struct GdiplusStartupInput gdiplusStartupInput;
+    ULONG_PTR gdiplusToken;
+
+    gdiplusStartupInput.GdiplusVersion              = 1;
+    gdiplusStartupInput.DebugEventCallback          = NULL;
+    gdiplusStartupInput.SuppressBackgroundThread    = 0;
+    gdiplusStartupInput.SuppressExternalCodecs      = 0;
+
+    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
+
+    test_constructor_destructor();
+    test_transform();
+
+    GdiplusShutdown(gdiplusToken);
+}
diff --git a/rostests/winetests/gdiplus/pen.c b/rostests/winetests/gdiplus/pen.c
new file mode 100644 (file)
index 0000000..ad484ce
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * Unit test suite for pens (and init)
+ *
+ * Copyright (C) 2007 Google (Evan Stade)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <math.h>
+
+#include "windows.h"
+#include "gdiplus.h"
+#include "wine/test.h"
+
+#define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
+#define expectf(expected, got) ok(fabs(got - expected) < 0.1, "Expected %.2f, got %.2f\n", expected, got)
+
+static void test_startup(void)
+{
+    GpPen *pen = NULL;
+    Status status;
+    struct GdiplusStartupInput gdiplusStartupInput;
+    ULONG_PTR gdiplusToken;
+
+    gdiplusStartupInput.GdiplusVersion              = 1;
+    gdiplusStartupInput.DebugEventCallback          = NULL;
+    gdiplusStartupInput.SuppressBackgroundThread    = 0;
+    gdiplusStartupInput.SuppressExternalCodecs      = 0;
+
+    status = GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
+    expect(Ok, status);
+    GdiplusShutdown(gdiplusToken);
+
+    gdiplusStartupInput.GdiplusVersion = 2;
+
+    status = GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
+    expect(UnsupportedGdiplusVersion, status);
+    GdiplusShutdown(gdiplusToken);
+
+    status = GdipCreatePen1((ARGB)0xffff00ff, 10.0f, UnitPixel, &pen);
+
+    todo_wine
+        expect(GdiplusNotInitialized, status);
+
+    GdipDeletePen(pen);
+}
+
+static void test_constructor_destructor(void)
+{
+    GpStatus status;
+    GpPen *pen = NULL;
+
+    status = GdipCreatePen1((ARGB)0xffff00ff, 10.0f, UnitPixel, NULL);
+    expect(InvalidParameter, status);
+    ok(pen == NULL, "Expected pen to be NULL\n");
+
+    status = GdipCreatePen1((ARGB)0xffff00ff, 10.0f, UnitPixel, &pen);
+    expect(Ok, status);
+    ok(pen != NULL, "Expected pen to be initialized\n");
+
+    status = GdipDeletePen(NULL);
+    expect(InvalidParameter, status);
+
+    status = GdipDeletePen(pen);
+    expect(Ok, status);
+}
+
+static void test_constructor_destructor2(void)
+{
+    GpStatus status;
+    GpPen *pen = NULL;
+    GpBrush *brush = NULL;
+    GpPointF points[2];
+
+    status = GdipCreatePen2(NULL, 10.0f, UnitPixel, &pen);
+    expect(InvalidParameter, status);
+    ok(pen == NULL, "Expected pen to be NULL\n");
+
+    points[0].X = 7.0;
+    points[0].Y = 11.0;
+    points[1].X = 13.0;
+    points[1].Y = 17.0;
+
+    status = GdipCreateLineBrush(&points[0], &points[1], (ARGB)0xffff00ff,
+                    (ARGB)0xff0000ff, WrapModeTile, (GpLineGradient **)&brush);
+    expect(Ok, status);
+    ok(brush != NULL, "Expected brush to be initialized\n");
+
+    status = GdipCreatePen2(brush, 10.0f, UnitPixel, &pen);
+    expect(Ok, status);
+    ok(pen != NULL, "Expected pen to be initialized\n");
+
+    status = GdipDeletePen(pen);
+    expect(Ok, status);
+
+    status = GdipDeleteBrush(brush);
+    expect(Ok, status);
+}
+
+static void test_brushfill(void)
+{
+    GpStatus status;
+    GpPen *pen;
+    GpBrush *brush, *brush2;
+    GpBrushType type;
+    ARGB color = 0;
+
+    /* default solid */
+    GdipCreatePen1(0xdeadbeef, 4.5, UnitWorld, &pen);
+    status = GdipGetPenBrushFill(pen, &brush);
+    expect(Ok, status);
+    GdipGetBrushType(brush, &type);
+    expect(BrushTypeSolidColor, type);
+    GdipGetPenColor(pen, &color);
+    expect(0xdeadbeef, color);
+    GdipDeleteBrush(brush);
+
+    /* color controlled by brush */
+    GdipCreateSolidFill(0xabaddeed, (GpSolidFill**)&brush);
+    status = GdipSetPenBrushFill(pen, brush);
+    expect(Ok, status);
+    GdipGetPenColor(pen, &color);
+    expect(0xabaddeed, color);
+    GdipDeleteBrush(brush);
+    color = 0;
+
+    /* get returns a clone, not a reference */
+    GdipGetPenBrushFill(pen, &brush);
+    GdipSetSolidFillColor((GpSolidFill*)brush, 0xbeadfeed);
+    GdipGetPenBrushFill(pen, &brush2);
+    ok(brush != brush2, "Expected to get a clone, not a copy of the reference\n");
+    GdipGetSolidFillColor((GpSolidFill*)brush2, &color);
+    expect(0xabaddeed, color);
+    GdipDeleteBrush(brush);
+    GdipDeleteBrush(brush2);
+
+    /* brush cannot be NULL */
+    status = GdipSetPenBrushFill(pen, NULL);
+    expect(InvalidParameter, status);
+
+    GdipDeletePen(pen);
+}
+
+static void test_dasharray(void)
+{
+    GpPen *pen;
+    GpDashStyle style;
+    GpStatus status;
+    REAL dashes[12];
+
+    GdipCreatePen1(0xdeadbeef, 10.0, UnitWorld, &pen);
+    dashes[0] = 10.0;
+    dashes[1] = 11.0;
+    dashes[2] = 12.0;
+    dashes[3] = 13.0;
+    dashes[4] = 14.0;
+    dashes[5] = -100.0;
+    dashes[6] = -100.0;
+    dashes[7] = dashes[8] = dashes[9] = dashes[10] = dashes[11] = 0.0;
+
+    /* setting the array sets the type to custom */
+    GdipGetPenDashStyle(pen, &style);
+    expect(DashStyleSolid, style);
+    status = GdipSetPenDashArray(pen, dashes, 2);
+    expect(Ok, status);
+    GdipGetPenDashStyle(pen, &style);
+    expect(DashStyleCustom, style);
+
+    /* Getting the array on a non-custom pen returns invalid parameter (unless
+     * you are getting 0 elements).*/
+    GdipSetPenDashStyle(pen, DashStyleSolid);
+    status = GdipGetPenDashArray(pen, &dashes[5], 2);
+    expect(InvalidParameter, status);
+    status = GdipGetPenDashArray(pen, &dashes[5], 0);
+    expect(Ok, status);
+
+    /* What does setting DashStyleCustom do to the array length? */
+    GdipSetPenDashArray(pen, dashes, 2);
+    GdipSetPenDashStyle(pen, DashStyleCustom);
+    status = GdipGetPenDashArray(pen, &dashes[5], 2);
+    expect(Ok, status);
+    expectf(10.0, dashes[5]);
+    expectf(11.0, dashes[6]);
+
+    /* Set the array, then get with different sized buffers. */
+    status = GdipSetPenDashArray(pen, dashes, 5);
+    expect(Ok, status);
+    dashes[5] = -100.0;
+    dashes[6] = -100.0;
+    status = GdipGetPenDashArray(pen, &dashes[5], 1);
+    expect(Ok, status); /* not InsufficientBuffer! */
+    expectf(10.0, dashes[5]);
+    expectf(-100.0, dashes[6]);
+    dashes[5] = -100.0;
+    status = GdipGetPenDashArray(pen, &dashes[5], 6);
+    expect(InvalidParameter, status); /* not Ok! */
+    expectf(-100.0, dashes[5]);
+    expectf(-100.0, dashes[6]);
+
+    /* Some invalid array values. */
+    status = GdipSetPenDashArray(pen, &dashes[7], 5);
+    expect(InvalidParameter, status);
+    dashes[9] = -1.0;
+    status = GdipSetPenDashArray(pen, &dashes[7], 5);
+    expect(InvalidParameter, status);
+
+    /* Try to set with count = 0. */
+    GdipSetPenDashStyle(pen, DashStyleDot);
+    status = GdipSetPenDashArray(pen, dashes, 0);
+    expect(OutOfMemory, status);
+    GdipGetPenDashStyle(pen, &style);
+    expect(DashStyleDot, style);
+
+    GdipDeletePen(pen);
+}
+
+START_TEST(pen)
+{
+    struct GdiplusStartupInput gdiplusStartupInput;
+    ULONG_PTR gdiplusToken;
+
+    test_startup();
+
+    gdiplusStartupInput.GdiplusVersion              = 1;
+    gdiplusStartupInput.DebugEventCallback          = NULL;
+    gdiplusStartupInput.SuppressBackgroundThread    = 0;
+    gdiplusStartupInput.SuppressExternalCodecs      = 0;
+
+    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
+
+    test_constructor_destructor();
+    test_constructor_destructor2();
+    test_brushfill();
+    test_dasharray();
+
+    GdiplusShutdown(gdiplusToken);
+}
diff --git a/rostests/winetests/gdiplus/stringformat.c b/rostests/winetests/gdiplus/stringformat.c
new file mode 100644 (file)
index 0000000..9cf46a4
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Unit test suite for string format
+ *
+ * Copyright (C) 2007 Google (Evan Stade)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "windows.h"
+#include "gdiplus.h"
+#include "wine/test.h"
+
+#define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
+
+static void test_constructor(void)
+{
+    GpStringFormat *format;
+    GpStatus stat;
+    INT n;
+    StringAlignment align, valign;
+    StringTrimming trimming;
+
+    stat = GdipCreateStringFormat(0, 0, &format);
+    expect(Ok, stat);
+
+    GdipGetStringFormatAlign(format, &align);
+    GdipGetStringFormatLineAlign(format, &valign);
+    GdipGetStringFormatHotkeyPrefix(format, &n);
+    GdipGetStringFormatTrimming(format, &trimming);
+
+    expect(HotkeyPrefixNone, n);
+    expect(StringAlignmentNear, align);
+    expect(StringAlignmentNear, align);
+    expect(StringTrimmingCharacter, trimming);
+
+    stat = GdipDeleteStringFormat(format);
+    expect(Ok, stat);
+}
+
+START_TEST(stringformat)
+{
+    struct GdiplusStartupInput gdiplusStartupInput;
+    ULONG_PTR gdiplusToken;
+
+    gdiplusStartupInput.GdiplusVersion              = 1;
+    gdiplusStartupInput.DebugEventCallback          = NULL;
+    gdiplusStartupInput.SuppressBackgroundThread    = 0;
+    gdiplusStartupInput.SuppressExternalCodecs      = 0;
+
+    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
+
+    test_constructor();
+
+    GdiplusShutdown(gdiplusToken);
+}
diff --git a/rostests/winetests/gdiplus/testlist.c b/rostests/winetests/gdiplus/testlist.c
new file mode 100644 (file)
index 0000000..dfb3f46
--- /dev/null
@@ -0,0 +1,29 @@
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_brush(void);
+extern void func_graphics(void);
+extern void func_graphicspath(void);
+extern void func_image(void);
+extern void func_matrix(void);
+extern void func_pen(void);
+extern void func_stringformat(void);
+extern void func_font(void);
+
+const struct test winetest_testlist[] =
+{
+    { "brush", func_brush },
+    { "graphics", func_graphics },
+    { "graphicspath", func_graphicspath },
+    { "font", func_font },
+    { "image", func_image },
+    { "matrix", func_matrix },
+    { "stringformat", func_stringformat },
+    { "pen", func_pen },
+    { 0, 0 }
+};