From: Amine Khaldi Date: Tue, 11 Dec 2012 21:40:34 +0000 (+0000) Subject: [GDIPLUS_WINETEST] X-Git-Tag: backups/ros-csrss@60644~104^2~149 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=fec6076d61bac26dcff33080ca29ad6a94ef15ce [GDIPLUS_WINETEST] * Sync with Wine 1.5.19. svn path=/trunk/; revision=57883 --- diff --git a/rostests/winetests/gdiplus/CMakeLists.txt b/rostests/winetests/gdiplus/CMakeLists.txt index 702d55410f3..b9ba8909d35 100644 --- a/rostests/winetests/gdiplus/CMakeLists.txt +++ b/rostests/winetests/gdiplus/CMakeLists.txt @@ -1,7 +1,5 @@ -add_definitions( - -D__ROS_LONG64__ - -D_DLL -D__USE_CRTIMP) +add_definitions(-D__ROS_LONG64__) list(APPEND SOURCE brush.c diff --git a/rostests/winetests/gdiplus/font.c b/rostests/winetests/gdiplus/font.c index 3b23d0ac442..eb850a88d4e 100644 --- a/rostests/winetests/gdiplus/font.c +++ b/rostests/winetests/gdiplus/font.c @@ -26,7 +26,9 @@ #include "wine/test.h" #define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got) -#define expectf(expected, got) ok(fabs(expected - got) < 0.0001, "Expected %f, got %f\n", expected, got) +#define expect_(expected, got, precision) ok(abs((expected) - (got)) <= (precision), "Expected %d, got %d\n", (expected), (got)) +#define expectf_(expected, got, precision) ok(fabs((expected) - (got)) <= (precision), "Expected %f, got %f\n", (expected), (got)) +#define expectf(expected, got) expectf_((expected), (got), 0.001) static const WCHAR nonexistent[] = {'T','h','i','s','F','o','n','t','s','h','o','u','l','d','N','o','t','E','x','i','s','t','\0'}; static const WCHAR MSSansSerif[] = {'M','S',' ','S','a','n','s',' ','S','e','r','i','f','\0'}; @@ -36,6 +38,14 @@ static const WCHAR CourierNew[] = {'C','o','u','r','i','e','r',' ','N','e','w',' static const WCHAR Tahoma[] = {'T','a','h','o','m','a',0}; static const WCHAR LiberationSerif[] = {'L','i','b','e','r','a','t','i','o','n',' ','S','e','r','i','f',0}; +static void set_rect_empty(RectF *rc) +{ + rc->X = 0.0; + rc->Y = 0.0; + rc->Width = 0.0; + rc->Height = 0.0; +} + static void test_createfont(void) { GpFontFamily* fontfamily = NULL, *fontfamily2; @@ -105,12 +115,6 @@ static void test_logfont(void) memset(&lfa, 0, sizeof(LOGFONTA)); memset(&lfa2, 0xff, sizeof(LOGFONTA)); - - /* empty FaceName */ - lfa.lfFaceName[0] = 0; - stat = GdipCreateFontFromLogfontA(hdc, &lfa, &font); - expect(NotTrueTypeFont, stat); - lstrcpyA(lfa.lfFaceName, "Tahoma"); stat = GdipCreateFontFromLogfontA(hdc, &lfa, &font); @@ -726,6 +730,387 @@ static void test_font_metrics(void) } #endif // CORE_6660_IS_FIXED +static void test_font_substitution(void) +{ + WCHAR ms_shell_dlg[LF_FACESIZE]; + HDC hdc; + HFONT hfont; + LOGFONT lf; + GpStatus status; + GpGraphics *graphics; + GpFont *font; + GpFontFamily *family; + int ret; + + hdc = CreateCompatibleDC(0); + status = GdipCreateFromHDC(hdc, &graphics); + expect(Ok, status); + + hfont = GetStockObject(DEFAULT_GUI_FONT); + ok(hfont != 0, "GetStockObject(DEFAULT_GUI_FONT) failed\n"); + + memset(&lf, 0xfe, sizeof(lf)); + ret = GetObject(hfont, sizeof(lf), &lf); + ok(ret == sizeof(lf), "GetObject failed\n"); + ok(!lstrcmp(lf.lfFaceName, "MS Shell Dlg"), "wrong face name %s\n", lf.lfFaceName); + MultiByteToWideChar(CP_ACP, 0, lf.lfFaceName, -1, ms_shell_dlg, LF_FACESIZE); + + status = GdipCreateFontFromLogfontA(hdc, &lf, &font); + expect(Ok, status); + memset(&lf, 0xfe, sizeof(lf)); + status = GdipGetLogFontA(font, graphics, &lf); + expect(Ok, status); + ok(!lstrcmp(lf.lfFaceName, "Microsoft Sans Serif") || + !lstrcmp(lf.lfFaceName, "Tahoma"), "wrong face name %s\n", lf.lfFaceName); + GdipDeleteFont(font); + + status = GdipCreateFontFamilyFromName(ms_shell_dlg, NULL, &family); + expect(Ok, status); + status = GdipCreateFont(family, 12, FontStyleRegular, UnitPoint, &font); + expect(Ok, status); + memset(&lf, 0xfe, sizeof(lf)); + status = GdipGetLogFontA(font, graphics, &lf); + expect(Ok, status); + ok(!lstrcmp(lf.lfFaceName, "Microsoft Sans Serif") || + !lstrcmp(lf.lfFaceName, "Tahoma"), "wrong face name %s\n", lf.lfFaceName); + GdipDeleteFont(font); + GdipDeleteFontFamily(family); + + status = GdipCreateFontFamilyFromName(nonexistent, NULL, &family); + ok(status == FontFamilyNotFound, "expected FontFamilyNotFound, got %d\n", status); + + lstrcpy(lf.lfFaceName, "ThisFontShouldNotExist"); + status = GdipCreateFontFromLogfontA(hdc, &lf, &font); + expect(Ok, status); + memset(&lf, 0xfe, sizeof(lf)); + status = GdipGetLogFontA(font, graphics, &lf); + expect(Ok, status); + ok(!lstrcmp(lf.lfFaceName, "Arial"), "wrong face name %s\n", lf.lfFaceName); + GdipDeleteFont(font); + + /* empty FaceName */ + lf.lfFaceName[0] = 0; + status = GdipCreateFontFromLogfontA(hdc, &lf, &font); + expect(Ok, status); + memset(&lf, 0xfe, sizeof(lf)); + status = GdipGetLogFontA(font, graphics, &lf); + expect(Ok, status); + ok(!lstrcmp(lf.lfFaceName, "Arial"), "wrong face name %s\n", lf.lfFaceName); + GdipDeleteFont(font); + + /* zeroing out lfWeight and lfCharSet leads to font creation failure */ + lf.lfWeight = 0; + lf.lfCharSet = 0; + lstrcpy(lf.lfFaceName, "ThisFontShouldNotExist"); + status = GdipCreateFontFromLogfontA(hdc, &lf, &font); +todo_wine + ok(status == NotTrueTypeFont || broken(status == FileNotFound), /* before XP */ + "expected NotTrueTypeFont, got %d\n", status); + + /* empty FaceName */ + lf.lfFaceName[0] = 0; + status = GdipCreateFontFromLogfontA(hdc, &lf, &font); +todo_wine + ok(status == NotTrueTypeFont || broken(status == FileNotFound), /* before XP */ + "expected NotTrueTypeFont, got %d\n", status); + + GdipDeleteGraphics(graphics); + DeleteDC(hdc); +} + +static void test_font_transform(void) +{ + static const WCHAR string[] = { 'A',0 }; + GpStatus status; + HDC hdc; + LOGFONT lf; + GpFont *font; + GpGraphics *graphics; + GpMatrix *matrix; + GpStringFormat *format, *typographic; + PointF pos[1] = { { 0,0 } }; + REAL height, margin_y; + RectF bounds, rect; + + hdc = CreateCompatibleDC(0); + status = GdipCreateFromHDC(hdc, &graphics); + expect(Ok, status); + + status = GdipSetPageUnit(graphics, UnitPixel); + expect(Ok, status); + + status = GdipCreateStringFormat(0, LANG_NEUTRAL, &format); + expect(Ok, status); + status = GdipStringFormatGetGenericTypographic(&typographic); + expect(Ok, status); + + memset(&lf, 0, sizeof(lf)); + lstrcpy(lf.lfFaceName, "Tahoma"); + lf.lfHeight = -100; + lf.lfWidth = 100; + status = GdipCreateFontFromLogfontA(hdc, &lf, &font); + expect(Ok, status); + + margin_y = 100.0 / 8.0; + + /* identity matrix */ + status = GdipCreateMatrix(&matrix); + expect(Ok, status); + status = GdipSetWorldTransform(graphics, matrix); + expect(Ok, status); + status = GdipGetLogFontA(font, graphics, &lf); + expect(Ok, status); + expect(-100, lf.lfHeight); + expect(0, lf.lfWidth); + expect(0, lf.lfEscapement); + expect(0, lf.lfOrientation); + status = GdipGetFontHeight(font, graphics, &height); + expect(Ok, status); + expectf(120.703125, height); + set_rect_empty(&rect); + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, format, &bounds, NULL, NULL); + expect(Ok, status); + expectf(0.0, bounds.X); + expectf(0.0, bounds.Y); +todo_wine + expectf(height + margin_y, bounds.Height); + set_rect_empty(&rect); + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, typographic, &bounds, NULL, NULL); + expect(Ok, status); + expectf(0.0, bounds.X); + expectf(0.0, bounds.Y); +todo_wine + expectf(height, bounds.Height); + set_rect_empty(&bounds); + status = GdipMeasureDriverString(graphics, (const UINT16 *)string, -1, font, pos, + DriverStringOptionsCmapLookup, NULL, &bounds); + expect(Ok, status); + expectf(0.0, bounds.X); +todo_wine + expectf_(-100.0, bounds.Y, 0.05); +todo_wine + expectf(height, bounds.Height); + set_rect_empty(&bounds); + status = GdipMeasureDriverString(graphics, (const UINT16 *)string, -1, font, pos, + DriverStringOptionsCmapLookup, matrix, &bounds); + expect(Ok, status); + expectf(0.0, bounds.X); +todo_wine + expectf_(-100.0, bounds.Y, 0.05); +todo_wine + expectf(height, bounds.Height); + + /* scale matrix */ + status = GdipScaleMatrix(matrix, 2.0, 3.0, MatrixOrderAppend); + expect(Ok, status); + status = GdipSetWorldTransform(graphics, matrix); + expect(Ok, status); + status = GdipGetLogFontA(font, graphics, &lf); + expect(Ok, status); + expect(-300, lf.lfHeight); + expect(0, lf.lfWidth); + expect(0, lf.lfEscapement); + expect(0, lf.lfOrientation); + status = GdipGetFontHeight(font, graphics, &height); + expect(Ok, status); + expectf(120.703125, height); + set_rect_empty(&rect); + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, format, &bounds, NULL, NULL); + expect(Ok, status); + expectf(0.0, bounds.X); + expectf(0.0, bounds.Y); +todo_wine + expectf(height + margin_y, bounds.Height); + set_rect_empty(&rect); + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, typographic, &bounds, NULL, NULL); + expect(Ok, status); + expectf(0.0, bounds.X); + expectf(0.0, bounds.Y); +todo_wine + expectf(height, bounds.Height); + set_rect_empty(&bounds); + status = GdipMeasureDriverString(graphics, (const UINT16 *)string, -1, font, pos, + DriverStringOptionsCmapLookup, NULL, &bounds); + expect(Ok, status); + expectf(0.0, bounds.X); +todo_wine + expectf_(-100.0, bounds.Y, 0.05); +todo_wine + expectf(height, bounds.Height); + set_rect_empty(&bounds); + status = GdipMeasureDriverString(graphics, (const UINT16 *)string, -1, font, pos, + DriverStringOptionsCmapLookup, matrix, &bounds); + expect(Ok, status); + expectf(0.0, bounds.X); +todo_wine + expectf_(-300.0, bounds.Y, 0.15); +todo_wine + expectf(height * 3.0, bounds.Height); + + /* scale + ratate matrix */ + status = GdipRotateMatrix(matrix, 45.0, MatrixOrderAppend); + expect(Ok, status); + status = GdipSetWorldTransform(graphics, matrix); + expect(Ok, status); + status = GdipGetLogFontA(font, graphics, &lf); + expect(Ok, status); + expect(-300, lf.lfHeight); + expect(0, lf.lfWidth); + expect_(3151, lf.lfEscapement, 1); + expect_(3151, lf.lfOrientation, 1); + status = GdipGetFontHeight(font, graphics, &height); + expect(Ok, status); + expectf(120.703125, height); + set_rect_empty(&rect); + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, format, &bounds, NULL, NULL); + expect(Ok, status); + expectf(0.0, bounds.X); + expectf(0.0, bounds.Y); +todo_wine + expectf(height + margin_y, bounds.Height); + set_rect_empty(&rect); + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, typographic, &bounds, NULL, NULL); + expect(Ok, status); + expectf(0.0, bounds.X); + expectf(0.0, bounds.Y); +todo_wine + expectf(height, bounds.Height); + set_rect_empty(&bounds); + status = GdipMeasureDriverString(graphics, (const UINT16 *)string, -1, font, pos, + DriverStringOptionsCmapLookup, NULL, &bounds); + expect(Ok, status); + expectf(0.0, bounds.X); +todo_wine + expectf_(-100.0, bounds.Y, 0.05); +todo_wine + expectf(height, bounds.Height); + set_rect_empty(&bounds); + status = GdipMeasureDriverString(graphics, (const UINT16 *)string, -1, font, pos, + DriverStringOptionsCmapLookup, matrix, &bounds); + expect(Ok, status); +todo_wine + expectf_(-43.814377, bounds.X, 0.05); +todo_wine + expectf_(-212.235611, bounds.Y, 0.05); +todo_wine + expectf_(340.847534, bounds.Height, 0.05); + + /* scale + ratate + shear matrix */ + status = GdipShearMatrix(matrix, 4.0, 5.0, MatrixOrderAppend); + expect(Ok, status); + status = GdipSetWorldTransform(graphics, matrix); + expect(Ok, status); + status = GdipGetLogFontA(font, graphics, &lf); + expect(Ok, status); +todo_wine + expect(1032, lf.lfHeight); + expect(0, lf.lfWidth); + expect_(3099, lf.lfEscapement, 1); + expect_(3099, lf.lfOrientation, 1); + status = GdipGetFontHeight(font, graphics, &height); + expect(Ok, status); + expectf(120.703125, height); + set_rect_empty(&rect); + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, format, &bounds, NULL, NULL); + expect(Ok, status); + expectf(0.0, bounds.X); + expectf(0.0, bounds.Y); +todo_wine + expectf(height + margin_y, bounds.Height); + set_rect_empty(&rect); + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, typographic, &bounds, NULL, NULL); + expect(Ok, status); + expectf(0.0, bounds.X); + expectf(0.0, bounds.Y); +todo_wine + expectf(height, bounds.Height); + set_rect_empty(&bounds); + status = GdipMeasureDriverString(graphics, (const UINT16 *)string, -1, font, pos, + DriverStringOptionsCmapLookup, NULL, &bounds); + expect(Ok, status); + expectf(0.0, bounds.X); +todo_wine + expectf_(-100.0, bounds.Y, 0.05); +todo_wine + expectf(height, bounds.Height); + set_rect_empty(&bounds); + status = GdipMeasureDriverString(graphics, (const UINT16 *)string, -1, font, pos, + DriverStringOptionsCmapLookup, matrix, &bounds); + expect(Ok, status); +todo_wine + expectf_(-636.706848, bounds.X, 0.05); +todo_wine + expectf_(-175.257523, bounds.Y, 0.05); +todo_wine + expectf_(1532.984985, bounds.Height, 0.05); + + /* scale + ratate + shear + translate matrix */ + status = GdipTranslateMatrix(matrix, 10.0, 20.0, MatrixOrderAppend); + expect(Ok, status); + status = GdipSetWorldTransform(graphics, matrix); + expect(Ok, status); + status = GdipGetLogFontA(font, graphics, &lf); + expect(Ok, status); +todo_wine + expect(1032, lf.lfHeight); + expect(0, lf.lfWidth); + expect_(3099, lf.lfEscapement, 1); + expect_(3099, lf.lfOrientation, 1); + status = GdipGetFontHeight(font, graphics, &height); + expect(Ok, status); + expectf(120.703125, height); + set_rect_empty(&rect); + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, format, &bounds, NULL, NULL); + expect(Ok, status); + expectf(0.0, bounds.X); + expectf(0.0, bounds.Y); +todo_wine + expectf(height + margin_y, bounds.Height); + set_rect_empty(&rect); + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, typographic, &bounds, NULL, NULL); + expect(Ok, status); + expectf(0.0, bounds.X); + expectf(0.0, bounds.Y); +todo_wine + expectf(height, bounds.Height); + set_rect_empty(&bounds); + status = GdipMeasureDriverString(graphics, (const UINT16 *)string, -1, font, pos, + DriverStringOptionsCmapLookup, NULL, &bounds); + expect(Ok, status); + expectf(0.0, bounds.X); +todo_wine + expectf_(-100.0, bounds.Y, 0.05); +todo_wine + expectf(height, bounds.Height); + set_rect_empty(&bounds); + status = GdipMeasureDriverString(graphics, (const UINT16 *)string, -1, font, pos, + DriverStringOptionsCmapLookup, matrix, &bounds); + expect(Ok, status); +todo_wine + expectf_(-626.706848, bounds.X, 0.05); +todo_wine + expectf_(-155.257523, bounds.Y, 0.05); +todo_wine + expectf_(1532.984985, bounds.Height, 0.05); + + GdipDeleteMatrix(matrix); + GdipDeleteFont(font); + GdipDeleteGraphics(graphics); + GdipDeleteStringFormat(typographic); + GdipDeleteStringFormat(format); + DeleteDC(hdc); +} + START_TEST(font) { struct GdiplusStartupInput gdiplusStartupInput; @@ -738,6 +1123,8 @@ START_TEST(font) GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); + test_font_transform(); + test_font_substitution(); #if CORE_6660_IS_FIXED test_font_metrics(); #endif diff --git a/rostests/winetests/gdiplus/graphics.c b/rostests/winetests/gdiplus/graphics.c index 453773ca0b2..94bd7165c6e 100644 --- a/rostests/winetests/gdiplus/graphics.c +++ b/rostests/winetests/gdiplus/graphics.c @@ -2,6 +2,7 @@ * Unit test suite for graphics objects * * Copyright (C) 2007 Google (Evan Stade) + * Copyright (C) 2012 Dmitry Timoshkov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,19 +19,127 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include +#include + #include "windows.h" #include "gdiplus.h" #include "wingdi.h" #include "wine/test.h" -#include -#define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got) -#define expectf_(expected, got, precision) ok(fabs(expected - got) < precision, "Expected %.2f, got %.2f\n", expected, got) -#define expectf(expected, got) expectf_(expected, got, 0.0001) +#define expect(expected, got) ok((got) == (expected), "Expected %d, got %d\n", (INT)(expected), (INT)(got)) +#define expectf_(expected, got, precision) ok(fabs((expected) - (got)) <= (precision), "Expected %f, got %f\n", (expected), (got)) +#define expectf(expected, got) expectf_((expected), (got), 0.001) #define TABLE_LEN (23) +static const REAL mm_per_inch = 25.4; +static const REAL point_per_inch = 72.0; static HWND hwnd; +static void set_rect_empty(RectF *rc) +{ + rc->X = 0.0; + rc->Y = 0.0; + rc->Width = 0.0; + rc->Height = 0.0; +} + +/* converts a given unit to its value in pixels */ +static REAL units_to_pixels(REAL units, GpUnit unit, REAL dpi) +{ + switch (unit) + { + case UnitPixel: + case UnitDisplay: + return units; + case UnitPoint: + return units * dpi / point_per_inch; + case UnitInch: + return units * dpi; + case UnitDocument: + return units * dpi / 300.0; /* Per MSDN */ + case UnitMillimeter: + return units * dpi / mm_per_inch; + default: + assert(0); + return 0; + } +} + +/* converts value in pixels to a given unit */ +static REAL pixels_to_units(REAL pixels, GpUnit unit, REAL dpi) +{ + switch (unit) + { + case UnitPixel: + case UnitDisplay: + return pixels; + case UnitPoint: + return pixels * point_per_inch / dpi; + case UnitInch: + return pixels / dpi; + case UnitDocument: + return pixels * 300.0 / dpi; + case UnitMillimeter: + return pixels * mm_per_inch / dpi; + default: + assert(0); + return 0; + } +} + +static REAL units_scale(GpUnit from, GpUnit to, REAL dpi) +{ + REAL pixels = units_to_pixels(1.0, from, dpi); + return pixels_to_units(pixels, to, dpi); +} + +static GpGraphics *create_graphics(REAL res_x, REAL res_y, GpUnit unit, REAL scale) +{ + GpStatus status; + union + { + GpBitmap *bitmap; + GpImage *image; + } u; + GpGraphics *graphics = NULL; + REAL res; + + status = GdipCreateBitmapFromScan0(1, 1, 4, PixelFormat24bppRGB, NULL, &u.bitmap); + expect(Ok, status); + + status = GdipBitmapSetResolution(u.bitmap, res_x, res_y); + expect(Ok, status); + status = GdipGetImageHorizontalResolution(u.image, &res); + expect(Ok, status); + expectf(res_x, res); + status = GdipGetImageVerticalResolution(u.image, &res); + expect(Ok, status); + expectf(res_y, res); + + status = GdipGetImageGraphicsContext(u.image, &graphics); + expect(Ok, status); + /* image is intentionally leaked to make sure that there is no + side effects after its destruction. + status = GdipDisposeImage(u.image); + expect(Ok, status); + */ + + status = GdipGetDpiX(graphics, &res); + expect(Ok, status); + expectf(res_x, res); + status = GdipGetDpiY(graphics, &res); + expect(Ok, status); + expectf(res_y, res); + + status = GdipSetPageUnit(graphics, unit); + expect(Ok, status); + status = GdipSetPageScale(graphics, scale); + expect(Ok, status); + + return graphics; +} + static void test_constructor_destructor(void) { GpStatus stat; @@ -1970,7 +2079,7 @@ static void test_GdipDrawString(void) expect(Ok, status); status = GdipCreateFontFromLogfontA(hdc, &logfont, &fnt); - if (status == FileNotFound) + if (status == NotTrueTypeFont || status == FileNotFound) { skip("Arial not installed.\n"); return; @@ -2837,10 +2946,10 @@ static void test_string_functions(void) INT codepointsfitted, linesfilled; GpStringFormat *format; CharacterRange ranges[3] = {{0, 1}, {1, 3}, {5, 1}}; - GpRegion *regions[4] = {0}; + GpRegion *regions[4]; BOOL region_isempty[4]; int i; - PointF position; + PointF positions[8]; GpMatrix *identity; ok(hdc != NULL, "Expected HDC to be initialized\n"); @@ -2970,15 +3079,32 @@ static void test_string_functions(void) expect(6, codepointsfitted); todo_wine expect(4, linesfilled); + for (i = 0; i < 4; i++) + regions[i] = (GpRegion *)0xdeadbeef; + + status = GdipMeasureCharacterRanges(graphics, teststring, 6, font, &rc, format, 0, regions); + expect(Ok, status); + + for (i = 0; i < 4; i++) + ok(regions[i] == (GpRegion *)0xdeadbeef, "expected 0xdeadbeef, got %p\n", regions[i]); + + status = GdipMeasureCharacterRanges(graphics, teststring, 6, font, &rc, format, 3, regions); + expect(Ok, status); + + for (i = 0; i < 4; i++) + ok(regions[i] == (GpRegion *)0xdeadbeef, "expected 0xdeadbeef, got %p\n", regions[i]); + status = GdipSetStringFormatMeasurableCharacterRanges(format, 3, ranges); expect(Ok, status); - rc.Width = 100.0; + set_rect_empty(&rc); for (i=0; i<4; i++) { status = GdipCreateRegion(®ions[i]); expect(Ok, status); + status = GdipSetEmpty(regions[i]); + expect(Ok, status); } status = GdipMeasureCharacterRanges(NULL, teststring, 6, font, &rc, format, 3, regions); @@ -3006,6 +3132,23 @@ static void test_string_functions(void) status = GdipMeasureCharacterRanges(graphics, teststring, 6, font, &rc, format, 2, regions); expect(InvalidParameter, status); + status = GdipMeasureCharacterRanges(graphics, teststring, 6, font, &rc, format, 3, regions); + expect(Ok, status); + + for (i = 0; i < 4; i++) + { + status = GdipIsEmptyRegion(regions[i], graphics, ®ion_isempty[i]); + expect(Ok, status); + } + + ok(region_isempty[0], "region should be empty\n"); + ok(region_isempty[1], "region should be empty\n"); + ok(region_isempty[2], "region should be empty\n"); + ok(region_isempty[3], "region should be empty\n"); + + rc.Width = 100.0; + rc.Height = 100.0; + status = GdipMeasureCharacterRanges(graphics, teststring, 6, font, &rc, format, 4, regions); expect(Ok, status); @@ -3018,7 +3161,7 @@ static void test_string_functions(void) ok(!region_isempty[0], "region shouldn't be empty\n"); ok(!region_isempty[1], "region shouldn't be empty\n"); ok(!region_isempty[2], "region shouldn't be empty\n"); - ok(!region_isempty[3], "region shouldn't be empty\n"); + ok(region_isempty[3], "region should be empty\n"); /* Cut off everything after the first space, and the second line. */ rc.Width = char_bounds.Width + char_width * 2.1; @@ -3036,7 +3179,7 @@ static void test_string_functions(void) ok(!region_isempty[0], "region shouldn't be empty\n"); ok(!region_isempty[1], "region shouldn't be empty\n"); ok(region_isempty[2], "region should be empty\n"); - ok(!region_isempty[3], "region shouldn't be empty\n"); + ok(region_isempty[3], "region should be empty\n"); for (i=0; i<4; i++) GdipDeleteRegion(regions[i]); @@ -3044,24 +3187,22 @@ static void test_string_functions(void) status = GdipCreateMatrix(&identity); expect(Ok, status); - position.X = 0; - position.Y = 0; - rc.X = 0; rc.Y = 0; rc.Width = 0; rc.Height = 0; - status = GdipMeasureDriverString(NULL, teststring, 6, font, &position, + memset(positions, 0, sizeof(positions)); + status = GdipMeasureDriverString(NULL, teststring, 6, font, positions, DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, identity, &rc); expect(InvalidParameter, status); - status = GdipMeasureDriverString(graphics, NULL, 6, font, &position, + status = GdipMeasureDriverString(graphics, NULL, 6, font, positions, DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, identity, &rc); expect(InvalidParameter, status); - status = GdipMeasureDriverString(graphics, teststring, 6, NULL, &position, + status = GdipMeasureDriverString(graphics, teststring, 6, NULL, positions, DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, identity, &rc); expect(InvalidParameter, status); @@ -3071,16 +3212,16 @@ static void test_string_functions(void) identity, &rc); expect(InvalidParameter, status); - status = GdipMeasureDriverString(graphics, teststring, 6, font, &position, + status = GdipMeasureDriverString(graphics, teststring, 6, font, positions, 0x100, identity, &rc); expect(Ok, status); - status = GdipMeasureDriverString(graphics, teststring, 6, font, &position, + status = GdipMeasureDriverString(graphics, teststring, 6, font, positions, DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL, &rc); expect(Ok, status); - status = GdipMeasureDriverString(graphics, teststring, 6, font, &position, + status = GdipMeasureDriverString(graphics, teststring, 6, font, positions, DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, identity, NULL); expect(InvalidParameter, status); @@ -3089,7 +3230,7 @@ static void test_string_functions(void) rc.Y = 0; rc.Width = 0; rc.Height = 0; - status = GdipMeasureDriverString(graphics, teststring, 6, font, &position, + status = GdipMeasureDriverString(graphics, teststring, 6, font, positions, DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, identity, &rc); expect(Ok, status); @@ -3106,7 +3247,7 @@ static void test_string_functions(void) rc.Y = 0; rc.Width = 0; rc.Height = 0; - status = GdipMeasureDriverString(graphics, teststring, 4, font, &position, + status = GdipMeasureDriverString(graphics, teststring, 4, font, positions, DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, identity, &rc); expect(Ok, status); @@ -3120,7 +3261,7 @@ static void test_string_functions(void) rc.Y = 0; rc.Width = 0; rc.Height = 0; - status = GdipMeasureDriverString(graphics, teststring2, 1, font, &position, + status = GdipMeasureDriverString(graphics, teststring2, 1, font, positions, DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, identity, &rc); expect(Ok, status); @@ -3306,6 +3447,701 @@ static void test_getdc_scaled(void) GdipDisposeImage((GpImage*)bitmap); } +static void test_GdipMeasureString(void) +{ + static const struct test_data + { + REAL res_x, res_y, page_scale; + GpUnit unit; + } td[] = + { + { 200.0, 200.0, 1.0, UnitPixel }, /* base */ + { 200.0, 200.0, 2.0, UnitPixel }, + { 200.0, 200.0, 1.0, UnitDisplay }, + { 200.0, 200.0, 2.0, UnitDisplay }, + { 200.0, 200.0, 1.0, UnitInch }, + { 200.0, 200.0, 2.0, UnitInch }, + { 200.0, 600.0, 1.0, UnitPoint }, + { 200.0, 600.0, 2.0, UnitPoint }, + { 200.0, 600.0, 1.0, UnitDocument }, + { 200.0, 600.0, 2.0, UnitDocument }, + { 200.0, 600.0, 1.0, UnitMillimeter }, + { 200.0, 600.0, 2.0, UnitMillimeter }, + { 200.0, 600.0, 1.0, UnitDisplay }, + { 200.0, 600.0, 2.0, UnitDisplay }, + { 200.0, 600.0, 1.0, UnitPixel }, + { 200.0, 600.0, 2.0, UnitPixel }, + }; + static const WCHAR tahomaW[] = { 'T','a','h','o','m','a',0 }; + static const WCHAR string[] = { '1','2','3','4','5','6','7',0 }; + GpStatus status; + GpGraphics *graphics; + GpFontFamily *family; + GpFont *font; + GpStringFormat *format; + RectF bounds, rc; + REAL base_cx = 0, base_cy = 0, height; + INT chars, lines; + LOGFONTW lf; + UINT i; + REAL font_size; + GpUnit font_unit, unit; + + status = GdipCreateStringFormat(0, LANG_NEUTRAL, &format); + expect(Ok, status); + status = GdipCreateFontFamilyFromName(tahomaW, NULL, &family); + expect(Ok, status); + + /* font size in pixels */ + status = GdipCreateFont(family, 100.0, FontStyleRegular, UnitPixel, &font); + expect(Ok, status); + status = GdipGetFontSize(font, &font_size); + expect(Ok, status); + expectf(100.0, font_size); + status = GdipGetFontUnit(font, &font_unit); + expect(Ok, status); + expect(UnitPixel, font_unit); + + for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) + { + graphics = create_graphics(td[i].res_x, td[i].res_y, td[i].unit, td[i].page_scale); + + lf.lfHeight = 0xdeadbeef; + status = GdipGetLogFontW(font, graphics, &lf); + expect(Ok, status); + height = units_to_pixels(font_size, td[i].unit, td[i].res_y); + if (td[i].unit != UnitDisplay) + height *= td[i].page_scale; + ok(-lf.lfHeight == (LONG)(height + 0.5), "%u: expected %d (%f), got %d\n", + i, (LONG)(height + 0.5), height, lf.lfHeight); + + height = font_size + 2.0 * font_size / 6.0; + + set_rect_empty(&rc); + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rc, format, &bounds, &chars, &lines); + expect(Ok, status); + + if (i == 0) + { + base_cx = bounds.Width; + base_cy = bounds.Height; + } + + expectf(0.0, bounds.X); + expectf(0.0, bounds.Y); +todo_wine + expectf_(height, bounds.Height, height / 100.0); + expectf_(bounds.Height / base_cy, bounds.Width / base_cx, 0.1); + expect(7, chars); + expect(1, lines); + + /* make sure it really fits */ + bounds.Width += 1.0; + bounds.Height += 1.0; + rc = bounds; + rc.X = 50.0; + rc.Y = 50.0; + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rc, format, &bounds, &chars, &lines); + expect(Ok, status); + expectf(50.0, bounds.X); + expectf(50.0, bounds.Y); +todo_wine + expectf_(height, bounds.Height, height / 100.0); + expectf_(bounds.Height / base_cy, bounds.Width / base_cx, 0.1); + expect(7, chars); + expect(1, lines); + + status = GdipDeleteGraphics(graphics); + expect(Ok, status); + } + + GdipDeleteFont(font); + + /* font size in logical units */ + /* UnitPoint = 3, UnitInch = 4, UnitDocument = 5, UnitMillimeter = 6 */ + for (unit = 3; unit <= 6; unit++) + { + /* create a font which final height is 100.0 pixels with 200 dpi device */ + /* height + 2 * (height/6) = 100 => height = 100 * 3 / 4 => 75 */ + height = pixels_to_units(75.0, unit, 200.0); + status = GdipCreateFont(family, height, FontStyleRegular, unit, &font); + expect(Ok, status); + status = GdipGetFontSize(font, &font_size); + expect(Ok, status); + expectf(height, font_size); + status = GdipGetFontUnit(font, &font_unit); + expect(Ok, status); + expect(unit, font_unit); + + for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) + { + REAL unit_scale; + + graphics = create_graphics(td[i].res_x, td[i].res_y, td[i].unit, td[i].page_scale); + + lf.lfHeight = 0xdeadbeef; + status = GdipGetLogFontW(font, graphics, &lf); + expect(Ok, status); + if (td[i].unit == UnitDisplay || td[i].unit == UnitPixel) + height = units_to_pixels(font_size, font_unit, td[i].res_x); + else + height = units_to_pixels(font_size, font_unit, td[i].res_y); + /*trace("%.1f font units = %f pixels with %.1f dpi, page_scale %.1f\n", font_size, height, td[i].res_y, td[i].page_scale);*/ + ok(-lf.lfHeight == (LONG)(height + 0.5), "%u: expected %d (%f), got %d\n", + i, (LONG)(height + 0.5), height, lf.lfHeight); + + if (td[i].unit == UnitDisplay || td[i].unit == UnitPixel) + unit_scale = units_scale(font_unit, td[i].unit, td[i].res_x); + else + unit_scale = units_scale(font_unit, td[i].unit, td[i].res_y); + /*trace("%u: %d to %d, %.1f dpi => unit_scale %f\n", i, font_unit, td[i].unit, td[i].res_y, unit_scale);*/ + height = (font_size + 2.0 * font_size / 6.0) * unit_scale; + if (td[i].unit != UnitDisplay) + height /= td[i].page_scale; + /*trace("%u: %.1f font units = %f units with %.1f dpi, page_scale %.1f\n", i, font_size, height, td[i].res_y, td[i].page_scale);*/ + + set_rect_empty(&rc); + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rc, format, &bounds, &chars, &lines); + expect(Ok, status); + + if (i == 0) + { + base_cx = bounds.Width; + base_cy = bounds.Height; + } + + expectf(0.0, bounds.X); + expectf(0.0, bounds.Y); +todo_wine + expectf_(height, bounds.Height, height / 85.0); + expectf_(bounds.Height / base_cy, bounds.Width / base_cx, 0.1); + expect(7, chars); + expect(1, lines); + + /* make sure it really fits */ + bounds.Width += 1.0; + bounds.Height += 1.0; + rc = bounds; + rc.X = 50.0; + rc.Y = 50.0; + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rc, format, &bounds, &chars, &lines); + expect(Ok, status); + expectf(50.0, bounds.X); + expectf(50.0, bounds.Y); +todo_wine + expectf_(height, bounds.Height, height / 85.0); + expectf_(bounds.Height / base_cy, bounds.Width / base_cx, 0.1); + expect(7, chars); + expect(1, lines); + + /* verify the result */ + height = units_to_pixels(bounds.Height, td[i].unit, td[i].res_x); + if (td[i].unit != UnitDisplay) + height *= td[i].page_scale; + /*trace("%u: unit %u, %.1fx%.1f dpi, scale %.1f, height %f, pixels %f\n", + i, td[i].unit, td[i].res_x, td[i].res_y, td[i].page_scale, bounds.Height, height);*/ +todo_wine + expectf_(100.0, height, 1.1); + + status = GdipDeleteGraphics(graphics); + expect(Ok, status); + } + + GdipDeleteFont(font); + } + + GdipDeleteFontFamily(family); + GdipDeleteStringFormat(format); +} + +static void test_transform(void) +{ + static const struct test_data + { + REAL res_x, res_y, scale; + GpUnit unit; + GpPointF in[2], out[2]; + } td[] = + { + { 96.0, 96.0, 1.0, UnitPixel, + { { 100.0, 0.0 }, { 0.0, 100.0 } }, { { 100.0, 0.0 }, { 0.0, 100.0 } } }, + { 96.0, 96.0, 1.0, UnitDisplay, + { { 100.0, 0.0 }, { 0.0, 100.0 } }, { { 100.0, 0.0 }, { 0.0, 100.0 } } }, + { 96.0, 96.0, 1.0, UnitInch, + { { 100.0, 0.0 }, { 0.0, 100.0 } }, { { 9600.0, 0.0 }, { 0.0, 9600.0 } } }, + { 123.0, 456.0, 1.0, UnitPoint, + { { 100.0, 0.0 }, { 0.0, 100.0 } }, { { 170.833313, 0.0 }, { 0.0, 633.333252 } } }, + { 123.0, 456.0, 1.0, UnitDocument, + { { 100.0, 0.0 }, { 0.0, 100.0 } }, { { 40.999996, 0.0 }, { 0.0, 151.999985 } } }, + { 123.0, 456.0, 2.0, UnitMillimeter, + { { 100.0, 0.0 }, { 0.0, 100.0 } }, { { 968.503845, 0.0 }, { 0.0, 3590.550781 } } }, + { 196.0, 296.0, 1.0, UnitDisplay, + { { 100.0, 0.0 }, { 0.0, 100.0 } }, { { 100.0, 0.0 }, { 0.0, 100.0 } } }, + { 196.0, 296.0, 1.0, UnitPixel, + { { 100.0, 0.0 }, { 0.0, 100.0 } }, { { 100.0, 0.0 }, { 0.0, 100.0 } } }, + }; + GpStatus status; + GpGraphics *graphics; + GpPointF ptf[2]; + UINT i; + + for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) + { + graphics = create_graphics(td[i].res_x, td[i].res_y, td[i].unit, td[i].scale); + ptf[0].X = td[i].in[0].X; + ptf[0].Y = td[i].in[0].Y; + ptf[1].X = td[i].in[1].X; + ptf[1].Y = td[i].in[1].Y; + status = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, ptf, 2); + expect(Ok, status); + expectf(td[i].out[0].X, ptf[0].X); + expectf(td[i].out[0].Y, ptf[0].Y); + expectf(td[i].out[1].X, ptf[1].X); + expectf(td[i].out[1].Y, ptf[1].Y); + status = GdipTransformPoints(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, ptf, 2); + expect(Ok, status); + expectf(td[i].in[0].X, ptf[0].X); + expectf(td[i].in[0].Y, ptf[0].Y); + expectf(td[i].in[1].X, ptf[1].X); + expectf(td[i].in[1].Y, ptf[1].Y); + status = GdipDeleteGraphics(graphics); + expect(Ok, status); + } +} + +/* Many people on the net ask why there is so much difference in rendered + * text height between gdiplus and gdi32, this test suggests an answer to + * that question. Important: this test assumes that font dpi == device dpi. + */ +static void test_font_height_scaling(void) +{ + static const WCHAR tahomaW[] = { 'T','a','h','o','m','a',0 }; + static const WCHAR string[] = { '1','2','3','4','5','6','7',0 }; + HDC hdc; + GpStringFormat *format; + CharacterRange range = { 0, 7 }; + GpRegion *region; + GpGraphics *graphics; + GpFontFamily *family; + GpFont *font; + GpStatus status; + RectF bounds, rect; + REAL height, dpi, scale; + PointF ptf; + GpUnit gfx_unit, font_unit; + + status = GdipCreateStringFormat(StringFormatFlagsNoWrap, LANG_NEUTRAL, &format); + expect(Ok, status); + status = GdipSetStringFormatMeasurableCharacterRanges(format, 1, &range); + expect(Ok, status); + status = GdipCreateRegion(®ion); + expect(Ok, status); + + status = GdipCreateFontFamilyFromName(tahomaW, NULL, &family); + expect(Ok, status); + + hdc = CreateCompatibleDC(0); + status = GdipCreateFromHDC(hdc, &graphics); + + status = GdipGetDpiY(graphics, &dpi); + expect(Ok, status); + + /* First check if tested functionality works: + * under XP if font and graphics units differ then GdipTransformPoints + * followed by GdipSetPageUnit to change the graphics units breaks region + * scaling in GdipMeasureCharacterRanges called later. + */ + status = GdipSetPageUnit(graphics, UnitDocument); + expect(Ok, status); + + ptf.X = 0.0; + ptf.Y = 0.0; + status = GdipTransformPoints(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, &ptf, 1); + expect(Ok, status); + + status = GdipSetPageUnit(graphics, UnitInch); + expect(Ok, status); + + status = GdipCreateFont(family, 720.0, FontStyleRegular, UnitPoint, &font); + expect(Ok, status); + + set_rect_empty(&rect); + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, format, &bounds, NULL, NULL); + expect(Ok, status); + trace("test bounds: %f,%f,%f,%f\n", bounds.X, bounds.Y, bounds.Width, bounds.Height); + + set_rect_empty(&rect); + rect.Width = 32000.0; + rect.Height = 32000.0; + status = GdipMeasureCharacterRanges(graphics, string, -1, font, &rect, format, 1, ®ion); + expect(Ok, status); + + set_rect_empty(&rect); + status = GdipGetRegionBounds(region, graphics, &rect); + expect(Ok, status); + trace("test region: %f,%f,%f,%f\n", rect.X, rect.Y, rect.Width, rect.Height); + + GdipDeleteFont(font); + + scale = rect.Height / bounds.Height; + if (fabs(scale - 1.0) > 0.1) + { + win_skip("GdipGetRegionBounds is broken, scale %f (should be near 1.0)\n", scale); + goto cleanup; + } + + status = GdipScaleWorldTransform(graphics, 0.01, 0.01, MatrixOrderAppend); + expect(Ok, status); + + /* UnitPixel = 2, UnitPoint = 3, UnitInch = 4, UnitDocument = 5, UnitMillimeter = 6 */ + /* UnitPixel as a font base unit is not tested because it drastically + differs in behaviour */ + for (font_unit = 3; font_unit <= 6; font_unit++) + { + /* create a font for the final text height of 100 pixels */ + /* height + 2 * (height/6) = 100 => height = 100 * 3 / 4 => 75 */ + status = GdipSetPageUnit(graphics, font_unit); + expect(Ok, status); + ptf.X = 0; + ptf.Y = 75.0; + status = GdipTransformPoints(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, &ptf, 1); + expect(Ok, status); + height = ptf.Y; + /*trace("height %f units\n", height);*/ + status = GdipCreateFont(family, height, FontStyleRegular, font_unit, &font); + expect(Ok, status); + + /* UnitPixel = 2, UnitPoint = 3, UnitInch = 4, UnitDocument = 5, UnitMillimeter = 6 */ + for (gfx_unit = 2; gfx_unit <= 6; gfx_unit++) + { + static const WCHAR doubleW[2] = { 'W','W' }; + RectF bounds_1, bounds_2; + REAL margin, margin_y, font_height; + int match; + + status = GdipSetPageUnit(graphics, gfx_unit); + expect(Ok, status); + + margin_y = units_to_pixels(height / 8.0, font_unit, dpi); + margin_y = pixels_to_units(margin_y, gfx_unit, dpi); + + status = GdipGetFontHeight(font, graphics, &font_height); + expect(Ok, status); + + set_rect_empty(&rect); + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, format, &bounds, NULL, NULL); + expect(Ok, status); + /*trace("bounds: %f,%f,%f,%f\n", bounds.X, bounds.Y, bounds.Width, bounds.Height);*/ +todo_wine + expectf_(font_height + margin_y, bounds.Height, 0.005); + + ptf.X = 0; + ptf.Y = bounds.Height; + status = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &ptf, 1); + expect(Ok, status); + match = fabs(100.0 - ptf.Y) <= 1.0; +todo_wine + ok(match, "Expected 100.0, got %f\n", ptf.Y); + + /* verify the result */ + ptf.Y = units_to_pixels(bounds.Height, gfx_unit, dpi); + ptf.Y /= 100.0; + match = fabs(100.0 - ptf.Y) <= 1.0; +todo_wine + ok(match, "Expected 100.0, got %f\n", ptf.Y); + + /* bounds.width of 1 glyph: [margin]+[width]+[margin] */ + set_rect_empty(&rect); + set_rect_empty(&bounds_1); + status = GdipMeasureString(graphics, doubleW, 1, font, &rect, format, &bounds_1, NULL, NULL); + expect(Ok, status); + /* bounds.width of 2 identical glyphs: [margin]+[width]+[width]+[margin] */ + set_rect_empty(&rect); + set_rect_empty(&bounds_2); + status = GdipMeasureString(graphics, doubleW, 2, font, &rect, format, &bounds_2, NULL, NULL); + expect(Ok, status); + + /* margin = [bounds.width of 1] - [bounds.width of 2] / 2*/ + margin = bounds_1.Width - bounds_2.Width / 2.0; + /*trace("margin %f\n", margin);*/ + ok(margin > 0.0, "wrong margin %f\n", margin); + + set_rect_empty(&rect); + rect.Width = 320000.0; + rect.Height = 320000.0; + status = GdipMeasureCharacterRanges(graphics, string, -1, font, &rect, format, 1, ®ion); + expect(Ok, status); + set_rect_empty(&rect); + status = GdipGetRegionBounds(region, graphics, &rect); + expect(Ok, status); + /*trace("region: %f,%f,%f,%f\n", rect.X, rect.Y, rect.Width, rect.Height);*/ + ok(rect.X > 0.0, "wrong rect.X %f\n", rect.X); + expectf(0.0, rect.Y); + match = fabs(1.0 - margin / rect.X) <= 0.05; + ok(match, "Expected %f, got %f\n", margin, rect.X); + match = fabs(1.0 - font_height / rect.Height) <= 0.1; +todo_wine + ok(match, "Expected %f, got %f\n", font_height, rect.Height); + match = fabs(1.0 - bounds.Width / (rect.Width + margin * 2.0)) <= 0.05; + ok(match, "Expected %f, got %f\n", bounds.Width, rect.Width + margin * 2.0); + } + + GdipDeleteFont(font); + } + +cleanup: + status = GdipDeleteGraphics(graphics); + expect(Ok, status); + DeleteDC(hdc); + + GdipDeleteFontFamily(family); + GdipDeleteRegion(region); + GdipDeleteStringFormat(format); +} + +static void test_measure_string(void) +{ + static const WCHAR tahomaW[] = { 'T','a','h','o','m','a',0 }; + static const WCHAR string[] = { 'A','0','1',0 }; + HDC hdc; + GpStringFormat *format; + GpGraphics *graphics; + GpFontFamily *family; + GpFont *font; + GpStatus status; + RectF bounds, rect; + REAL width, height, width_1, width_2; + int lines, glyphs; + + status = GdipCreateStringFormat(StringFormatFlagsNoWrap, LANG_NEUTRAL, &format); + expect(Ok, status); + + status = GdipCreateFontFamilyFromName(tahomaW, NULL, &family); + expect(Ok, status); + + hdc = CreateCompatibleDC(0); + status = GdipCreateFromHDC(hdc, &graphics); + + status = GdipCreateFont(family, 20, FontStyleRegular, UnitPixel, &font); + expect(Ok, status); + + set_rect_empty(&rect); + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, format, &bounds, &glyphs, &lines); + expect(Ok, status); + expect(3, glyphs); + expect(1, lines); + width = bounds.Width; + height = bounds.Height; + + set_rect_empty(&rect); + rect.Height = height / 2.0; + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, format, &bounds, &glyphs, &lines); + expect(Ok, status); + expect(3, glyphs); + expect(1, lines); + expectf(width, bounds.Width); +todo_wine + expectf(height / 2.0, bounds.Height); + + set_rect_empty(&rect); + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, 1, font, &rect, format, &bounds, &glyphs, &lines); + expect(Ok, status); + expect(1, glyphs); + expect(1, lines); + ok(bounds.Width < width / 2.0, "width of 1 glyph is wrong\n"); + expectf(height, bounds.Height); + width_1 = bounds.Width; + + set_rect_empty(&rect); + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, 2, font, &rect, format, &bounds, &glyphs, &lines); + expect(Ok, status); + expect(2, glyphs); + expect(1, lines); + ok(bounds.Width < width, "width of 2 glyphs is wrong\n"); + ok(bounds.Width > width_1, "width of 2 glyphs is wrong\n"); + expectf(height, bounds.Height); + width_2 = bounds.Width; + + set_rect_empty(&rect); + rect.Width = width / 2.0; + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, format, &bounds, &glyphs, &lines); + expect(Ok, status); + expect(1, glyphs); + expect(1, lines); + expectf_(width_1, bounds.Width, 0.01); + expectf(height, bounds.Height); + + set_rect_empty(&rect); + rect.Height = height; + rect.Width = width - 0.05; + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, format, &bounds, &glyphs, &lines); + expect(Ok, status); + expect(2, glyphs); + expect(1, lines); + expectf_(width_2, bounds.Width, 0.01); + expectf(height, bounds.Height); + + set_rect_empty(&rect); + rect.Height = height; + rect.Width = width_2 - 0.05; + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, format, &bounds, &glyphs, &lines); + expect(Ok, status); + expect(1, glyphs); + expect(1, lines); + expectf_(width_1, bounds.Width, 0.01); + expectf(height, bounds.Height); + + status = GdipDeleteFont(font); + expect(Ok, status); + + status = GdipDeleteGraphics(graphics); + expect(Ok, status); + DeleteDC(hdc); + + GdipDeleteFontFamily(family); + GdipDeleteStringFormat(format); +} + +static void test_measured_extra_space(void) +{ + static const WCHAR tahomaW[] = { 'T','a','h','o','m','a',0 }; + static const WCHAR string[2] = { 'W','W' }; + GpStringFormat *format; + HDC hdc; + GpGraphics *graphics; + GpFontFamily *family; + GpFont *font; + GpStatus status; + GpUnit gfx_unit, font_unit; + RectF bounds_1, bounds_2, rect; + REAL margin, font_size, dpi; + + status = GdipCreateStringFormat(0, LANG_NEUTRAL, &format); + expect(Ok, status); + + status = GdipCreateFontFamilyFromName(tahomaW, NULL, &family); + expect(Ok, status); + hdc = CreateCompatibleDC(0); + status = GdipCreateFromHDC(hdc, &graphics); + expect(Ok, status); + + status = GdipGetDpiX(graphics, &dpi); + expect(Ok, status); + + /* UnitPixel = 2, UnitPoint = 3, UnitInch = 4, UnitDocument = 5, UnitMillimeter = 6 */ + /* UnitPixel as a font base unit is not tested because it differs in behaviour */ + for (font_unit = 3; font_unit <= 6; font_unit++) + { + status = GdipCreateFont(family, 1234.0, FontStyleRegular, font_unit, &font); + expect(Ok, status); + + status = GdipGetFontSize(font, &font_size); + expect(Ok, status); + font_size = units_to_pixels(font_size, font_unit, dpi); + /*trace("font size/6 = %f pixels\n", font_size / 6.0);*/ + + /* UnitPixel = 2, UnitPoint = 3, UnitInch = 4, UnitDocument = 5, UnitMillimeter = 6 */ + for (gfx_unit = 2; gfx_unit <= 6; gfx_unit++) + { + status = GdipSetPageUnit(graphics, gfx_unit); + expect(Ok, status); + + /* bounds.width of 1 glyph: [margin]+[width]+[margin] */ + set_rect_empty(&rect); + set_rect_empty(&bounds_1); + status = GdipMeasureString(graphics, string, 1, font, &rect, format, &bounds_1, NULL, NULL); + expect(Ok, status); + /* bounds.width of 2 identical glyphs: [margin]+[width]+[width]+[margin] */ + set_rect_empty(&rect); + set_rect_empty(&bounds_2); + status = GdipMeasureString(graphics, string, 2, font, &rect, format, &bounds_2, NULL, NULL); + expect(Ok, status); + + /* margin = [bounds.width of 1] - [bounds.width of 2] / 2*/ + margin = units_to_pixels(bounds_1.Width - bounds_2.Width / 2.0, gfx_unit, dpi); + /*trace("margin %f pixels\n", margin);*/ + expectf_(font_size / 6.0, margin, font_size / 100.0); + } + + GdipDeleteFont(font); + } + + GdipDeleteGraphics(graphics); + DeleteDC(hdc); + GdipDeleteFontFamily(family); + GdipDeleteStringFormat(format); +} + +static void test_alpha_hdc(void) +{ + GpStatus status; + HDC hdc; + HBITMAP hbm, old_hbm; + GpGraphics *graphics; + ULONG *bits; + BITMAPINFO bmi; + GpRectF bounds; + + hdc = CreateCompatibleDC(0); + ok(hdc != NULL, "CreateCompatibleDC failed\n"); + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + bmi.bmiHeader.biHeight = 5; + bmi.bmiHeader.biWidth = 5; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biClrUsed = 0; + + hbm = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**)&bits, NULL, 0); + ok(hbm != NULL, "CreateDIBSection failed\n"); + + old_hbm = SelectObject(hdc, hbm); + + status = GdipCreateFromHDC(hdc, &graphics); + expect(Ok, status); + + status = GdipGetVisibleClipBounds(graphics, &bounds); + expect(Ok, status); + expectf(0.0, bounds.X); + expectf(0.0, bounds.Y); + expectf(5.0, bounds.Width); + expectf(5.0, bounds.Height); + + bits[0] = 0xdeadbeef; + + status = GdipGraphicsClear(graphics, 0xffaaaaaa); + expect(Ok, status); + + expect(0xffaaaaaa, bits[0]); + + SelectObject(hdc, old_hbm); + + bits[0] = 0xdeadbeef; + + status = GdipGraphicsClear(graphics, 0xffbbbbbb); + expect(Ok, status); + + todo_wine expect(0xffbbbbbb, bits[0]); + + GdipDeleteGraphics(graphics); + + DeleteObject(hbm); + DeleteDC(hdc); +} + START_TEST(graphics) { struct GdiplusStartupInput gdiplusStartupInput; @@ -3332,6 +4168,11 @@ START_TEST(graphics) GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); + test_measured_extra_space(); + test_measure_string(); + test_font_height_scaling(); + test_transform(); + test_GdipMeasureString(); test_constructor_destructor(); test_save_restore(); test_GdipFillClosedCurve2(); @@ -3369,6 +4210,7 @@ START_TEST(graphics) test_get_set_interpolation(); test_get_set_textrenderinghint(); test_getdc_scaled(); + test_alpha_hdc(); GdiplusShutdown(gdiplusToken); DestroyWindow( hwnd ); diff --git a/rostests/winetests/gdiplus/image.c b/rostests/winetests/gdiplus/image.c index b6262d13f03..46e250d9bde 100644 --- a/rostests/winetests/gdiplus/image.c +++ b/rostests/winetests/gdiplus/image.c @@ -2,6 +2,7 @@ * Unit test suite for images * * Copyright (C) 2007 Google (Evan Stade) + * Copyright (C) 2012 Dmitry Timoshkov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,14 +22,16 @@ #define COBJMACROS #include +#include +#include #include "initguid.h" #include "windows.h" #include "gdiplus.h" #include "wine/test.h" -#define expect(expected, got) ok((UINT)(got) == (UINT)(expected), "Expected %.8x, got %.8x\n", (UINT)(expected), (UINT)(got)) -#define expectf(expected, got) ok(fabs(expected - got) < 0.0001, "Expected %.2f, got %.2f\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)) static BOOL color_match(ARGB c1, ARGB c2, BYTE max_diff) { @@ -432,14 +435,14 @@ static void test_SavingImages(void) if (stat != Ok) goto cleanup; stat = GdipSaveImageToFile((GpImage*)bm, filename, &codecs[0].Clsid, 0); - expect(stat, Ok); + expect(Ok, stat); GdipDisposeImage((GpImage*)bm); bm = 0; /* re-load and check image stats */ stat = GdipLoadImageFromFile(filename, (GpImage**)&bm); - expect(stat, Ok); + expect(Ok, stat); if (stat != Ok) goto cleanup; stat = GdipGetImageDimension((GpImage*)bm, &w, &h); @@ -1515,6 +1518,7 @@ static void test_resolution(void) { GpStatus stat; GpBitmap *bitmap; + GpGraphics *graphics; REAL res=-1.0; HDC screendc; int screenxres, screenyres; @@ -1558,6 +1562,15 @@ static void test_resolution(void) expect(Ok, stat); expectf((REAL)screenyres, res); + stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics); + expect(Ok, stat); + stat = GdipGetDpiX(graphics, &res); + expect(Ok, stat); + expectf((REAL)screenxres, res); + stat = GdipGetDpiY(graphics, &res); + expect(Ok, stat); + expectf((REAL)screenyres, res); + /* test changing the resolution */ stat = GdipBitmapSetResolution(bitmap, screenxres*2.0, screenyres*3.0); expect(Ok, stat); @@ -1570,6 +1583,27 @@ static void test_resolution(void) expect(Ok, stat); expectf(screenyres*3.0, res); + stat = GdipGetDpiX(graphics, &res); + expect(Ok, stat); + expectf((REAL)screenxres, res); + stat = GdipGetDpiY(graphics, &res); + expect(Ok, stat); + expectf((REAL)screenyres, res); + + stat = GdipDeleteGraphics(graphics); + expect(Ok, stat); + + stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics); + expect(Ok, stat); + stat = GdipGetDpiX(graphics, &res); + expect(Ok, stat); + expectf(screenxres*2.0, res); + stat = GdipGetDpiY(graphics, &res); + expect(Ok, stat); + expectf(screenyres*3.0, res); + stat = GdipDeleteGraphics(graphics); + expect(Ok, stat); + stat = GdipDisposeImage((GpImage*)bitmap); expect(Ok, stat); } @@ -1786,10 +1820,13 @@ static void test_getsetpixel(void) broken(stat == Ok), /* Older gdiplus */ "Expected InvalidParameter, got %.8x\n", stat); +if (0) /* crashes some gdiplus implementations */ +{ stat = GdipBitmapSetPixel(bitmap, 1, -1, 0); ok(stat == InvalidParameter || broken(stat == Ok), /* Older gdiplus */ "Expected InvalidParameter, got %.8x\n", stat); +} stat = GdipBitmapGetPixel(bitmap, 2, 1, &color); expect(InvalidParameter, stat); @@ -2298,7 +2335,7 @@ static void test_multiframegif(void) count = 12345; stat = GdipImageGetFrameCount((GpImage*)bmp, &dimension, &count); expect(Ok, stat); - todo_wine expect(2, count); + expect(2, count); /* SelectActiveFrame overwrites our current data */ stat = GdipImageSelectActiveFrame((GpImage*)bmp, &dimension, 1); @@ -2307,7 +2344,7 @@ static void test_multiframegif(void) color = 0xdeadbeef; GdipBitmapGetPixel(bmp, 0, 0, &color); expect(Ok, stat); - todo_wine expect(0xff000000, color); + expect(0xff000000, color); stat = GdipImageSelectActiveFrame((GpImage*)bmp, &dimension, 0); expect(Ok, stat); @@ -2338,7 +2375,18 @@ static void test_multiframegif(void) stat = GdipBitmapGetPixel(bmp, 0, 0, &color); expect(Ok, stat); - todo_wine expect(0xffffffff, color); + expect(0xffffffff, color); + + /* rotate/flip discards the information about other frames */ + stat = GdipImageRotateFlip((GpImage*)bmp, Rotate90FlipNone); + expect(Ok, stat); + + count = 12345; + stat = GdipImageGetFrameCount((GpImage*)bmp, &dimension, &count); + expect(Ok, stat); + expect(1, count); + + expect_rawformat(&ImageFormatMemoryBMP, (GpImage*)bmp, __LINE__, FALSE); GdipDisposeImage((GpImage*)bmp); IStream_Release(stream); @@ -2656,6 +2704,1409 @@ static void test_dispose(void) expect(ObjectBusy, stat); } +static LONG obj_refcount(void *obj) +{ + IUnknown_AddRef((IUnknown *)obj); + return IUnknown_Release((IUnknown *)obj); +} + +static GpImage *load_image(const BYTE *image_data, UINT image_size) +{ + IStream *stream; + HGLOBAL hmem; + BYTE *data; + HRESULT hr; + GpStatus status; + GpImage *image = NULL, *clone; + ImageType image_type; + LONG refcount, old_refcount; + + hmem = GlobalAlloc(0, image_size); + data = GlobalLock(hmem); + memcpy(data, image_data, image_size); + GlobalUnlock(hmem); + + hr = CreateStreamOnHGlobal(hmem, TRUE, &stream); + ok(hr == S_OK, "CreateStreamOnHGlobal error %#x\n", hr); + if (hr != S_OK) return NULL; + + refcount = obj_refcount(stream); + ok(refcount == 1, "expected stream refcount 1, got %d\n", refcount); + + status = GdipLoadImageFromStream(stream, &image); + ok(status == Ok || broken(status == InvalidParameter), /* XP */ + "GdipLoadImageFromStream error %d\n", status); + if (status != Ok) + { + IStream_Release(stream); + return NULL; + } + + status = GdipGetImageType(image, &image_type); + ok(status == Ok, "GdipGetImageType error %d\n", status); + + refcount = obj_refcount(stream); + if (image_type == ImageTypeBitmap) + ok(refcount > 1, "expected stream refcount > 1, got %d\n", refcount); + else + ok(refcount == 1, "expected stream refcount 1, got %d\n", refcount); + old_refcount = refcount; + + status = GdipCloneImage(image, &clone); + ok(status == Ok, "GdipCloneImage error %d\n", status); + refcount = obj_refcount(stream); + ok(refcount == old_refcount, "expected stream refcount %d, got %d\n", old_refcount, refcount); + status = GdipDisposeImage(clone); + ok(status == Ok, "GdipDisposeImage error %d\n", status); + refcount = obj_refcount(stream); + ok(refcount == old_refcount, "expected stream refcount %d, got %d\n", old_refcount, refcount); + + refcount = IStream_Release(stream); + if (image_type == ImageTypeBitmap) + ok(refcount >= 1, "expected stream refcount != 0\n"); + else + ok(refcount == 0, "expected stream refcount 0, got %d\n", refcount); + + return image; +} + +static void test_image_properties(void) +{ + static const struct test_data + { + const BYTE *image_data; + UINT image_size; + ImageType image_type; + UINT prop_count; + UINT prop_count2; /* if win7 behaves differently */ + /* 1st property attributes */ + UINT prop_size; + UINT prop_size2; /* if win7 behaves differently */ + UINT prop_id; + UINT prop_id2; /* if win7 behaves differently */ + } + td[] = + { + { pngimage, sizeof(pngimage), ImageTypeBitmap, 4, ~0, 1, 20, 0x5110, 0x132 }, + { jpgimage, sizeof(jpgimage), ImageTypeBitmap, 2, ~0, 128, 0, 0x5090, 0x5091 }, + { tiffimage, sizeof(tiffimage), ImageTypeBitmap, 16, 0, 4, 0, 0xfe, 0 }, + { bmpimage, sizeof(bmpimage), ImageTypeBitmap, 0, 0, 0, 0, 0, 0 }, + { wmfimage, sizeof(wmfimage), ImageTypeMetafile, 0, 0, 0, 0, 0, 0 } + }; + GpStatus status; + GpImage *image; + UINT prop_count, prop_size, i; + PROPID prop_id[16] = { 0 }; + ImageType image_type; + union + { + PropertyItem data; + char buf[256]; + } item; + + for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) + { + image = load_image(td[i].image_data, td[i].image_size); + if (!image) + { + trace("%u: failed to load image data\n", i); + continue; + } + + status = GdipGetImageType(image, &image_type); + ok(status == Ok, "%u: GdipGetImageType error %d\n", i, status); + ok(td[i].image_type == image_type, "%u: expected image_type %d, got %d\n", + i, td[i].image_type, image_type); + + status = GdipGetPropertyCount(image, &prop_count); + ok(status == Ok, "%u: GdipGetPropertyCount error %d\n", i, status); + if (td[i].image_data == pngimage || td[i].image_data == jpgimage) + todo_wine + ok(td[i].prop_count == prop_count || td[i].prop_count2 == prop_count, + " %u: expected property count %u or %u, got %u\n", + i, td[i].prop_count, td[i].prop_count2, prop_count); + else + ok(td[i].prop_count == prop_count || td[i].prop_count2 == prop_count, + " %u: expected property count %u or %u, got %u\n", + i, td[i].prop_count, td[i].prop_count2, prop_count); + + status = GdipGetPropertyItemSize(NULL, 0, &prop_size); + expect(InvalidParameter, status); + status = GdipGetPropertyItemSize(image, 0, NULL); + expect(InvalidParameter, status); + status = GdipGetPropertyItemSize(image, 0, &prop_size); + if (image_type == ImageTypeMetafile) + expect(NotImplemented, status); + else + expect(PropertyNotFound, status); + + status = GdipGetPropertyItem(NULL, 0, 0, &item.data); + expect(InvalidParameter, status); + status = GdipGetPropertyItem(image, 0, 0, NULL); + expect(InvalidParameter, status); + status = GdipGetPropertyItem(image, 0, 0, &item.data); + if (image_type == ImageTypeMetafile) + expect(NotImplemented, status); + else + expect(PropertyNotFound, status); + + /* FIXME: remove once Wine is fixed */ + if (td[i].prop_count != prop_count) + { + GdipDisposeImage(image); + continue; + } + + status = GdipGetPropertyIdList(NULL, prop_count, prop_id); + expect(InvalidParameter, status); + status = GdipGetPropertyIdList(image, prop_count, NULL); + expect(InvalidParameter, status); + status = GdipGetPropertyIdList(image, 0, prop_id); + if (image_type == ImageTypeMetafile) + expect(NotImplemented, status); + else if (prop_count == 0) + expect(Ok, status); + else + expect(InvalidParameter, status); + status = GdipGetPropertyIdList(image, prop_count - 1, prop_id); + if (image_type == ImageTypeMetafile) + expect(NotImplemented, status); + else + expect(InvalidParameter, status); + status = GdipGetPropertyIdList(image, prop_count + 1, prop_id); + if (image_type == ImageTypeMetafile) + expect(NotImplemented, status); + else + expect(InvalidParameter, status); + status = GdipGetPropertyIdList(image, prop_count, prop_id); + if (image_type == ImageTypeMetafile) + expect(NotImplemented, status); + else + { + expect(Ok, status); + if (prop_count != 0) + ok(td[i].prop_id == prop_id[0] || td[i].prop_id2 == prop_id[0], + " %u: expected property id %#x or %#x, got %#x\n", + i, td[i].prop_id, td[i].prop_id2, prop_id[0]); + } + + if (status == Ok) + { + status = GdipGetPropertyItemSize(image, prop_id[0], &prop_size); + if (prop_count == 0) + expect(PropertyNotFound, status); + else + { + expect(Ok, status); + + assert(sizeof(item) >= prop_size); + ok(prop_size > sizeof(PropertyItem), "%u: got too small prop_size %u\n", + i, prop_size); + ok(td[i].prop_size + sizeof(PropertyItem) == prop_size || + td[i].prop_size2 + sizeof(PropertyItem) == prop_size, + " %u: expected property size %u or %u, got %u\n", + i, td[i].prop_size, td[i].prop_size2, prop_size); + + status = GdipGetPropertyItem(image, prop_id[0], 0, &item.data); + ok(status == InvalidParameter || status == GenericError /* Win7 */, + "%u: expected InvalidParameter, got %d\n", i, status); + status = GdipGetPropertyItem(image, prop_id[0], prop_size - 1, &item.data); + ok(status == InvalidParameter || status == GenericError /* Win7 */, + "%u: expected InvalidParameter, got %d\n", i, status); + status = GdipGetPropertyItem(image, prop_id[0], prop_size + 1, &item.data); + ok(status == InvalidParameter || status == GenericError /* Win7 */, + "%u: expected InvalidParameter, got %d\n", i, status); + status = GdipGetPropertyItem(image, prop_id[0], prop_size, &item.data); + expect(Ok, status); + ok(prop_id[0] == item.data.id, + "%u: expected property id %#x, got %#x\n", i, prop_id[0], item.data.id); + } + } + + GdipDisposeImage(image); + } +} + +#define IFD_BYTE 1 +#define IFD_ASCII 2 +#define IFD_SHORT 3 +#define IFD_LONG 4 +#define IFD_RATIONAL 5 +#define IFD_SBYTE 6 +#define IFD_UNDEFINED 7 +#define IFD_SSHORT 8 +#define IFD_SLONG 9 +#define IFD_SRATIONAL 10 +#define IFD_FLOAT 11 +#define IFD_DOUBLE 12 + +#ifndef PropertyTagTypeSByte +#define PropertyTagTypeSByte 6 +#define PropertyTagTypeSShort 8 +#define PropertyTagTypeFloat 11 +#define PropertyTagTypeDouble 12 +#endif + +static UINT documented_type(UINT type) +{ + switch (type) + { + case PropertyTagTypeSByte: return PropertyTagTypeByte; + case PropertyTagTypeSShort: return PropertyTagTypeShort; + case PropertyTagTypeFloat: return PropertyTagTypeUndefined; + case PropertyTagTypeDouble: return PropertyTagTypeUndefined; + default: return type; + } +} + +#include "pshpack2.h" +struct IFD_entry +{ + SHORT id; + SHORT type; + ULONG count; + LONG value; +}; + +struct IFD_rational +{ + LONG numerator; + LONG denominator; +}; + +static const struct tiff_data +{ + USHORT byte_order; + USHORT version; + ULONG dir_offset; + USHORT number_of_entries; + struct IFD_entry entry[40]; + ULONG next_IFD; + struct IFD_rational xres; + DOUBLE double_val; + struct IFD_rational srational_val; + char string[14]; + SHORT short_val[4]; + LONG long_val[2]; + FLOAT float_val[2]; + struct IFD_rational rational[3]; + BYTE pixel_data[4]; +} TIFF_data = +{ +#ifdef WORDS_BIGENDIAN + 'M' | 'M' << 8, +#else + 'I' | 'I' << 8, +#endif + 42, + FIELD_OFFSET(struct tiff_data, number_of_entries), + 31, + { + { 0xff, IFD_SHORT, 1, 0 }, /* SUBFILETYPE */ + { 0x100, IFD_LONG, 1, 1 }, /* IMAGEWIDTH */ + { 0x101, IFD_LONG, 1, 1 }, /* IMAGELENGTH */ + { 0x102, IFD_SHORT, 1, 1 }, /* BITSPERSAMPLE */ + { 0x103, IFD_SHORT, 1, 1 }, /* COMPRESSION: XP doesn't accept IFD_LONG here */ + { 0x106, IFD_SHORT, 1, 1 }, /* PHOTOMETRIC */ + { 0x111, IFD_LONG, 1, FIELD_OFFSET(struct tiff_data, pixel_data) }, /* STRIPOFFSETS */ + { 0x115, IFD_SHORT, 1, 1 }, /* SAMPLESPERPIXEL */ + { 0x116, IFD_LONG, 1, 1 }, /* ROWSPERSTRIP */ + { 0x117, IFD_LONG, 1, 1 }, /* STRIPBYTECOUNT */ + { 0x11a, IFD_RATIONAL, 1, FIELD_OFFSET(struct tiff_data, xres) }, + { 0x11b, IFD_RATIONAL, 1, FIELD_OFFSET(struct tiff_data, xres) }, + { 0x128, IFD_SHORT, 1, 2 }, /* RESOLUTIONUNIT */ + { 0xf001, IFD_BYTE, 1, 0x11223344 }, + { 0xf002, IFD_BYTE, 4, 0x11223344 }, + { 0xf003, IFD_SBYTE, 1, 0x11223344 }, + { 0xf004, IFD_SSHORT, 1, 0x11223344 }, + { 0xf005, IFD_SSHORT, 2, 0x11223344 }, + { 0xf006, IFD_SLONG, 1, 0x11223344 }, + { 0xf007, IFD_FLOAT, 1, 0x11223344 }, + { 0xf008, IFD_DOUBLE, 1, FIELD_OFFSET(struct tiff_data, double_val) }, + { 0xf009, IFD_SRATIONAL, 1, FIELD_OFFSET(struct tiff_data, srational_val) }, + { 0xf00a, IFD_BYTE, 13, FIELD_OFFSET(struct tiff_data, string) }, + { 0xf00b, IFD_SSHORT, 4, FIELD_OFFSET(struct tiff_data, short_val) }, + { 0xf00c, IFD_SLONG, 2, FIELD_OFFSET(struct tiff_data, long_val) }, + { 0xf00e, IFD_ASCII, 13, FIELD_OFFSET(struct tiff_data, string) }, + { 0xf00f, IFD_ASCII, 4, 'a' | 'b' << 8 | 'c' << 16 | 'd' << 24 }, + { 0xf010, IFD_UNDEFINED, 13, FIELD_OFFSET(struct tiff_data, string) }, + { 0xf011, IFD_UNDEFINED, 4, 'a' | 'b' << 8 | 'c' << 16 | 'd' << 24 }, + /* Some gdiplus versions ignore these fields. + { 0xf012, IFD_BYTE, 0, 0x11223344 }, + { 0xf013, IFD_SHORT, 0, 0x11223344 }, + { 0xf014, IFD_LONG, 0, 0x11223344 }, + { 0xf015, IFD_FLOAT, 0, 0x11223344 },*/ + { 0xf016, IFD_SRATIONAL, 3, FIELD_OFFSET(struct tiff_data, rational) }, + /* Win7 before SP1 doesn't recognize this field, everybody else does. */ + { 0xf017, IFD_FLOAT, 2, FIELD_OFFSET(struct tiff_data, float_val) }, + }, + 0, + { 900, 3 }, + 1234567890.0987654321, + { 0x1a2b3c4d, 0x5a6b7c8d }, + "Hello World!", + { 0x0101, 0x0202, 0x0303, 0x0404 }, + { 0x11223344, 0x55667788 }, + { (FLOAT)1234.5678, (FLOAT)8765.4321 }, + { { 0x01020304, 0x05060708 }, { 0x10203040, 0x50607080 }, { 0x11223344, 0x55667788 } }, + { 0x11, 0x22, 0x33, 0 } +}; +#include "poppack.h" + +static void test_tiff_properties(void) +{ + static const struct test_data + { + ULONG type, id, length; + const BYTE value[24]; + } td[31] = + { + { PropertyTagTypeShort, 0xff, 2, { 0 } }, + { PropertyTagTypeLong, 0x100, 4, { 1 } }, + { PropertyTagTypeLong, 0x101, 4, { 1 } }, + { PropertyTagTypeShort, 0x102, 2, { 1 } }, + { PropertyTagTypeShort, 0x103, 2, { 1 } }, + { PropertyTagTypeShort, 0x106, 2, { 1 } }, + { PropertyTagTypeLong, 0x111, 4, { 0x44,0x02 } }, + { PropertyTagTypeShort, 0x115, 2, { 1 } }, + { PropertyTagTypeLong, 0x116, 4, { 1 } }, + { PropertyTagTypeLong, 0x117, 4, { 1 } }, + { PropertyTagTypeRational, 0x11a, 8, { 0x84,0x03,0,0,0x03 } }, + { PropertyTagTypeRational, 0x11b, 8, { 0x84,0x03,0,0,0x03 } }, + { PropertyTagTypeShort, 0x128, 2, { 2 } }, + { PropertyTagTypeByte, 0xf001, 1, { 0x44 } }, + { PropertyTagTypeByte, 0xf002, 4, { 0x44,0x33,0x22,0x11 } }, + { PropertyTagTypeSByte, 0xf003, 1, { 0x44 } }, + { PropertyTagTypeSShort, 0xf004, 2, { 0x44,0x33 } }, + { PropertyTagTypeSShort, 0xf005, 4, { 0x44,0x33,0x22,0x11 } }, + { PropertyTagTypeSLONG, 0xf006, 4, { 0x44,0x33,0x22,0x11 } }, + { PropertyTagTypeFloat, 0xf007, 4, { 0x44,0x33,0x22,0x11 } }, + { PropertyTagTypeDouble, 0xf008, 8, { 0x2c,0x52,0x86,0xb4,0x80,0x65,0xd2,0x41 } }, + { PropertyTagTypeSRational, 0xf009, 8, { 0x4d, 0x3c, 0x2b, 0x1a, 0x8d, 0x7c, 0x6b, 0x5a } }, + { PropertyTagTypeByte, 0xf00a, 13, { 'H','e','l','l','o',' ','W','o','r','l','d','!',0 } }, + { PropertyTagTypeSShort, 0xf00b, 8, { 0x01,0x01,0x02,0x02,0x03,0x03,0x04,0x04 } }, + { PropertyTagTypeSLONG, 0xf00c, 8, { 0x44,0x33,0x22,0x11,0x88,0x77,0x66,0x55 } }, + { PropertyTagTypeASCII, 0xf00e, 13, { 'H','e','l','l','o',' ','W','o','r','l','d','!',0 } }, + { PropertyTagTypeASCII, 0xf00f, 5, { 'a','b','c','d' } }, + { PropertyTagTypeUndefined, 0xf010, 13, { 'H','e','l','l','o',' ','W','o','r','l','d','!',0 } }, + { PropertyTagTypeUndefined, 0xf011, 4, { 'a','b','c','d' } }, + { PropertyTagTypeSRational, 0xf016, 24, + { 0x04,0x03,0x02,0x01,0x08,0x07,0x06,0x05, + 0x40,0x30,0x20,0x10,0x80,0x70,0x60,0x50, + 0x44,0x33,0x22,0x11,0x88,0x77,0x66,0x55 } }, + /* Win7 before SP1 doesn't recognize this field, everybody else does. */ + { PropertyTagTypeFloat, 0xf017, 8, { 0x2b,0x52,0x9a,0x44,0xba,0xf5,0x08,0x46 } }, + }; + GpStatus status; + GpImage *image; + GUID guid; + UINT dim_count, frame_count, prop_count, prop_size, i; + PROPID *prop_id; + PropertyItem *prop_item; + + image = load_image((const BYTE *)&TIFF_data, sizeof(TIFF_data)); + ok(image != 0, "Failed to load TIFF image data\n"); + if (!image) return; + + status = GdipImageGetFrameDimensionsCount(image, &dim_count); + expect(Ok, status); + expect(1, dim_count); + + status = GdipImageGetFrameDimensionsList(image, &guid, 1); + expect(Ok, status); + expect_guid(&FrameDimensionPage, &guid, __LINE__, FALSE); + + frame_count = 0xdeadbeef; + status = GdipImageGetFrameCount(image, &guid, &frame_count); + expect(Ok, status); + expect(1, frame_count); + + prop_count = 0xdeadbeef; + status = GdipGetPropertyCount(image, &prop_count); + expect(Ok, status); + ok(prop_count == sizeof(td)/sizeof(td[0]) || + broken(prop_count == sizeof(td)/sizeof(td[0]) - 1) /* Win7 SP0 */, + "expected property count %u, got %u\n", (UINT)(sizeof(td)/sizeof(td[0])), prop_count); + + prop_id = HeapAlloc(GetProcessHeap(), 0, prop_count * sizeof(*prop_id)); + + status = GdipGetPropertyIdList(image, prop_count, prop_id); + expect(Ok, status); + + for (i = 0; i < prop_count; i++) + { + status = GdipGetPropertyItemSize(image, prop_id[i], &prop_size); + expect(Ok, status); + if (status != Ok) break; + ok(prop_size > sizeof(*prop_item), "%u: too small item length %u\n", i, prop_size); + + prop_item = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, prop_size); + status = GdipGetPropertyItem(image, prop_id[i], prop_size, prop_item); + expect(Ok, status); + ok(prop_item->value == prop_item + 1, "expected item->value %p, got %p\n", prop_item + 1, prop_item->value); + ok(td[i].type == prop_item->type || + /* Win7 stopped using proper but not documented types, and it + looks broken since TypeFloat and TypeDouble now reported as + TypeUndefined, and signed types reported as unsigned. */ + broken(prop_item->type == documented_type(td[i].type)), + "%u: expected type %u, got %u\n", i, td[i].type, prop_item->type); + ok(td[i].id == prop_item->id, "%u: expected id %#x, got %#x\n", i, td[i].id, prop_item->id); + prop_size -= sizeof(*prop_item); + ok(prop_item->length == prop_size, "%u: expected length %u, got %u\n", i, prop_size, prop_item->length); + ok(td[i].length == prop_item->length, "%u: expected length %u, got %u\n", i, td[i].length, prop_item->length); + ok(td[i].length == prop_size, "%u: expected length %u, got %u\n", i, td[i].length, prop_size); + if (td[i].length == prop_item->length) + { + int match = memcmp(td[i].value, prop_item->value, td[i].length) == 0; + ok(match || broken(td[i].length <= 4 && !match), "%u: data mismatch\n", i); + if (!match) + { + UINT j; + BYTE *data = prop_item->value; + printf("id %#x:", prop_item->id); + for (j = 0; j < prop_item->length; j++) + printf(" %02x", data[j]); + printf("\n"); + } + } + HeapFree(GetProcessHeap(), 0, prop_item); + } + + HeapFree(GetProcessHeap(), 0, prop_id); + + GdipDisposeImage(image); +} + +static void test_GdipGetAllPropertyItems(void) +{ + static const struct test_data + { + ULONG type, id, length; + BYTE value[32]; + } td[16] = + { + { PropertyTagTypeLong, 0xfe, 4, { 0 } }, + { PropertyTagTypeShort, 0x100, 2, { 1 } }, + { PropertyTagTypeShort, 0x101, 2, { 1 } }, + { PropertyTagTypeShort, 0x102, 6, { 8,0,8,0,8,0 } }, + { PropertyTagTypeShort, 0x103, 2, { 1 } }, + { PropertyTagTypeShort, 0x106, 2, { 2,0 } }, + { PropertyTagTypeASCII, 0x10d, 27, "/home/meh/Desktop/test.tif" }, + { PropertyTagTypeLong, 0x111, 4, { 8,0,0,0 } }, + { PropertyTagTypeShort, 0x112, 2, { 1 } }, + { PropertyTagTypeShort, 0x115, 2, { 3,0 } }, + { PropertyTagTypeShort, 0x116, 2, { 0x40,0 } }, + { PropertyTagTypeLong, 0x117, 4, { 3,0,0,0 } }, + { PropertyTagTypeRational, 0x11a, 8, { 0,0,0,72,0,0,0,1 } }, + { PropertyTagTypeRational, 0x11b, 8, { 0,0,0,72,0,0,0,1 } }, + { PropertyTagTypeShort, 0x11c, 2, { 1 } }, + { PropertyTagTypeShort, 0x128, 2, { 2 } } + }; + GpStatus status; + GpImage *image; + GUID guid; + UINT dim_count, frame_count, prop_count, prop_size, i; + UINT total_size, total_count; + PROPID *prop_id; + PropertyItem *prop_item; + const char *item_data; + + image = load_image(tiffimage, sizeof(tiffimage)); + ok(image != 0, "Failed to load TIFF image data\n"); + if (!image) return; + + dim_count = 0xdeadbeef; + status = GdipImageGetFrameDimensionsCount(image, &dim_count); + expect(Ok, status); + expect(1, dim_count); + + status = GdipImageGetFrameDimensionsList(image, &guid, 1); + expect(Ok, status); + expect_guid(&FrameDimensionPage, &guid, __LINE__, FALSE); + + frame_count = 0xdeadbeef; + status = GdipImageGetFrameCount(image, &guid, &frame_count); + expect(Ok, status); + expect(1, frame_count); + + prop_count = 0xdeadbeef; + status = GdipGetPropertyCount(image, &prop_count); + expect(Ok, status); + ok(prop_count == sizeof(td)/sizeof(td[0]), + "expected property count %u, got %u\n", (UINT)(sizeof(td)/sizeof(td[0])), prop_count); + + prop_id = HeapAlloc(GetProcessHeap(), 0, prop_count * sizeof(*prop_id)); + + status = GdipGetPropertyIdList(image, prop_count, prop_id); + expect(Ok, status); + + prop_size = 0; + for (i = 0; i < prop_count; i++) + { + UINT size; + status = GdipGetPropertyItemSize(image, prop_id[i], &size); + expect(Ok, status); + if (status != Ok) break; + ok(size > sizeof(*prop_item), "%u: too small item length %u\n", i, size); + + prop_size += size; + + prop_item = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); + status = GdipGetPropertyItem(image, prop_id[i], size, prop_item); + expect(Ok, status); + ok(prop_item->value == prop_item + 1, "expected item->value %p, got %p\n", prop_item + 1, prop_item->value); + ok(td[i].type == prop_item->type, + "%u: expected type %u, got %u\n", i, td[i].type, prop_item->type); + ok(td[i].id == prop_item->id, "%u: expected id %#x, got %#x\n", i, td[i].id, prop_item->id); + size -= sizeof(*prop_item); + ok(prop_item->length == size, "%u: expected length %u, got %u\n", i, size, prop_item->length); + ok(td[i].length == prop_item->length, "%u: expected length %u, got %u\n", i, td[i].length, prop_item->length); + if (td[i].length == prop_item->length) + { + int match = memcmp(td[i].value, prop_item->value, td[i].length) == 0; + ok(match, "%u: data mismatch\n", i); + if (!match) + { + UINT j; + BYTE *data = prop_item->value; + printf("id %#x:", prop_item->id); + for (j = 0; j < prop_item->length; j++) + printf(" %02x", data[j]); + printf("\n"); + } + } + HeapFree(GetProcessHeap(), 0, prop_item); + } + + HeapFree(GetProcessHeap(), 0, prop_id); + + status = GdipGetPropertySize(NULL, &total_size, &total_count); + expect(InvalidParameter, status); + status = GdipGetPropertySize(image, &total_size, NULL); + expect(InvalidParameter, status); + status = GdipGetPropertySize(image, NULL, &total_count); + expect(InvalidParameter, status); + status = GdipGetPropertySize(image, NULL, NULL); + expect(InvalidParameter, status); + total_size = 0xdeadbeef; + total_count = 0xdeadbeef; + status = GdipGetPropertySize(image, &total_size, &total_count); + expect(Ok, status); + ok(prop_count == total_count, + "expected total property count %u, got %u\n", prop_count, total_count); + ok(prop_size == total_size, + "expected total property size %u, got %u\n", prop_size, total_size); + + prop_item = HeapAlloc(GetProcessHeap(), 0, prop_size); + + status = GdipGetAllPropertyItems(image, 0, prop_count, prop_item); + expect(InvalidParameter, status); + status = GdipGetAllPropertyItems(image, prop_size, 1, prop_item); + expect(InvalidParameter, status); + status = GdipGetAllPropertyItems(image, prop_size, prop_count, NULL); + expect(InvalidParameter, status); + status = GdipGetAllPropertyItems(image, prop_size, prop_count, NULL); + expect(InvalidParameter, status); + status = GdipGetAllPropertyItems(image, 0, 0, NULL); + expect(InvalidParameter, status); + status = GdipGetAllPropertyItems(image, prop_size + 1, prop_count, prop_item); + expect(InvalidParameter, status); + status = GdipGetAllPropertyItems(image, prop_size, prop_count, prop_item); + expect(Ok, status); + + item_data = (const char *)(prop_item + prop_count); + for (i = 0; i < prop_count; i++) + { + ok(prop_item[i].value == item_data, "%u: expected value %p, got %p\n", + i, item_data, prop_item[i].value); + ok(td[i].type == prop_item[i].type, + "%u: expected type %u, got %u\n", i, td[i].type, prop_item[i].type); + ok(td[i].id == prop_item[i].id, "%u: expected id %#x, got %#x\n", i, td[i].id, prop_item[i].id); + ok(td[i].length == prop_item[i].length, "%u: expected length %u, got %u\n", i, td[i].length, prop_item[i].length); + if (td[i].length == prop_item[i].length) + { + int match = memcmp(td[i].value, prop_item[i].value, td[i].length) == 0; + ok(match, "%u: data mismatch\n", i); + if (!match) + { + UINT j; + BYTE *data = prop_item[i].value; + printf("id %#x:", prop_item[i].id); + for (j = 0; j < prop_item[i].length; j++) + printf(" %02x", data[j]); + printf("\n"); + } + } + item_data += prop_item[i].length; + } + + HeapFree(GetProcessHeap(), 0, prop_item); + + GdipDisposeImage(image); +} + +static void test_tiff_palette(void) +{ + GpStatus status; + GpImage *image; + PixelFormat format; + INT size; + struct + { + ColorPalette pal; + ARGB entry[256]; + } palette; + ARGB *entries = palette.pal.Entries; + + /* 1bpp TIFF without palette */ + image = load_image((const BYTE *)&TIFF_data, sizeof(TIFF_data)); + ok(image != 0, "Failed to load TIFF image data\n"); + if (!image) return; + + status = GdipGetImagePixelFormat(image, &format); + expect(Ok, status); + ok(format == PixelFormat1bppIndexed, "expected PixelFormat1bppIndexed, got %#x\n", format); + + status = GdipGetImagePaletteSize(image, &size); + ok(status == Ok || broken(status == GenericError), /* XP */ + "GdipGetImagePaletteSize error %d\n", status); + if (status == GenericError) + { + GdipDisposeImage(image); + return; + } + expect(sizeof(ColorPalette) + sizeof(ARGB), size); + + status = GdipGetImagePalette(image, &palette.pal, size); + expect(Ok, status); + expect(0, palette.pal.Flags); + expect(2, palette.pal.Count); + if (palette.pal.Count == 2) + { + ok(entries[0] == 0xff000000, "expected 0xff000000, got %#x\n", entries[0]); + ok(entries[1] == 0xffffffff, "expected 0xffffffff, got %#x\n", entries[1]); + } + + GdipDisposeImage(image); +} + +static void test_bitmapbits(void) +{ + /* 8 x 2 bitmap */ + static const BYTE pixels_24[48] = + { + 0xff,0xff,0xff, 0,0,0, 0xff,0xff,0xff, 0,0,0, + 0xff,0xff,0xff, 0,0,0, 0xff,0xff,0xff, 0,0,0, + 0xff,0xff,0xff, 0,0,0, 0xff,0xff,0xff, 0,0,0, + 0xff,0xff,0xff, 0,0,0, 0xff,0xff,0xff, 0,0,0 + }; + static const BYTE pixels_00[48] = + { + 0,0,0, 0,0,0, 0,0,0, 0,0,0, + 0,0,0, 0,0,0, 0,0,0, 0,0,0, + 0,0,0, 0,0,0, 0,0,0, 0,0,0, + 0,0,0, 0,0,0, 0,0,0, 0,0,0 + }; + static const BYTE pixels_24_77[64] = + { + 0xff,0xff,0xff, 0,0,0, 0xff,0xff,0xff, 0,0,0, + 0xff,0xff,0xff, 0,0,0, 0xff,0xff,0xff, 0,0,0, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0xff,0xff,0xff, 0,0,0, 0xff,0xff,0xff, 0,0,0, + 0xff,0xff,0xff, 0,0,0, 0xff,0xff,0xff, 0,0,0, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77 + }; + static const BYTE pixels_77[64] = + { + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77 + }; + static const BYTE pixels_8[16] = + { + 0x01,0,0x01,0,0x01,0,0x01,0, + 0x01,0,0x01,0,0x01,0,0x01,0 + }; + static const BYTE pixels_8_77[64] = + { + 0x01,0,0x01,0,0x01,0,0x01,0, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0x01,0,0x01,0,0x01,0,0x01,0, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77 + }; + static const BYTE pixels_1_77[64] = + { + 0xaa,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0xaa,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77, + 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77 + }; + static const BYTE pixels_1[8] = {0xaa,0,0,0,0xaa,0,0,0}; + static const struct test_data + { + PixelFormat format; + UINT bpp; + ImageLockMode mode; + UINT stride, size; + const BYTE *pixels; + const BYTE *pixels_unlocked; + } td[] = + { + /* 0 */ + { PixelFormat24bppRGB, 24, 0xfff0, 24, 48, pixels_24, pixels_00 }, + + { PixelFormat24bppRGB, 24, 0, 24, 48, pixels_24, pixels_00 }, + { PixelFormat24bppRGB, 24, ImageLockModeRead, 24, 48, pixels_24, pixels_00 }, + { PixelFormat24bppRGB, 24, ImageLockModeWrite, 24, 48, pixels_24, pixels_00 }, + { PixelFormat24bppRGB, 24, ImageLockModeRead|ImageLockModeWrite, 24, 48, pixels_24, pixels_00 }, + { PixelFormat24bppRGB, 24, ImageLockModeRead|ImageLockModeUserInputBuf, 32, 64, pixels_24_77, pixels_24 }, + { PixelFormat24bppRGB, 24, ImageLockModeWrite|ImageLockModeUserInputBuf, 32, 64, pixels_77, pixels_00 }, + { PixelFormat24bppRGB, 24, ImageLockModeUserInputBuf, 32, 64, pixels_77, pixels_24 }, + /* 8 */ + { PixelFormat8bppIndexed, 8, 0, 8, 16, pixels_8, pixels_24 }, + { PixelFormat8bppIndexed, 8, ImageLockModeRead, 8, 16, pixels_8, pixels_24 }, + { PixelFormat8bppIndexed, 8, ImageLockModeWrite, 8, 16, pixels_8, pixels_00 }, + { PixelFormat8bppIndexed, 8, ImageLockModeRead|ImageLockModeWrite, 8, 16, pixels_8, pixels_00 }, + { PixelFormat8bppIndexed, 8, ImageLockModeRead|ImageLockModeUserInputBuf, 32, 64, pixels_8_77, pixels_24 }, + { PixelFormat8bppIndexed, 8, ImageLockModeWrite|ImageLockModeUserInputBuf, 32, 64, pixels_77, pixels_00 }, + { PixelFormat8bppIndexed, 8, ImageLockModeUserInputBuf, 32, 64, pixels_77, pixels_24 }, + /* 15 */ + { PixelFormat1bppIndexed, 1, 0, 4, 8, pixels_1, pixels_24 }, + { PixelFormat1bppIndexed, 1, ImageLockModeRead, 4, 8, pixels_1, pixels_24 }, + { PixelFormat1bppIndexed, 1, ImageLockModeWrite, 4, 8, pixels_1, pixels_00 }, + { PixelFormat1bppIndexed, 1, ImageLockModeRead|ImageLockModeWrite, 4, 8, pixels_1, pixels_00 }, + { PixelFormat1bppIndexed, 1, ImageLockModeRead|ImageLockModeUserInputBuf, 32, 64, pixels_1_77, pixels_24 }, + { PixelFormat1bppIndexed, 1, ImageLockModeWrite|ImageLockModeUserInputBuf, 32, 64, pixels_77, pixels_00 }, + { PixelFormat1bppIndexed, 1, ImageLockModeUserInputBuf, 32, 64, pixels_77, pixels_24 }, + }; + BYTE buf[64]; + GpStatus status; + GpBitmap *bitmap; + UINT i; + BitmapData data; + struct + { + ColorPalette pal; + ARGB entries[1]; + } palette; + ARGB *entries = palette.pal.Entries; + + for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) + { + BYTE pixels[sizeof(pixels_24)]; + memcpy(pixels, pixels_24, sizeof(pixels_24)); + status = GdipCreateBitmapFromScan0(8, 2, 24, PixelFormat24bppRGB, pixels, &bitmap); + expect(Ok, status); + + /* associate known palette with pixel data */ + palette.pal.Flags = PaletteFlagsGrayScale; + palette.pal.Count = 2; + entries[0] = 0xff000000; + entries[1] = 0xffffffff; + status = GdipSetImagePalette((GpImage *)bitmap, &palette.pal); + expect(Ok, status); + + memset(&data, 0xfe, sizeof(data)); + if (td[i].mode & ImageLockModeUserInputBuf) + { + memset(buf, 0x77, sizeof(buf)); + data.Scan0 = buf; + data.Stride = 32; + } + status = GdipBitmapLockBits(bitmap, NULL, td[i].mode, td[i].format, &data); + ok(status == Ok || broken(status == InvalidParameter) /* XP */, "%u: GdipBitmapLockBits error %d\n", i, status); + if (status != Ok) + { + GdipDisposeImage((GpImage *)bitmap); + continue; + } + ok(data.Width == 8, "%u: expected 8, got %d\n", i, data.Width); + ok(data.Height == 2, "%u: expected 2, got %d\n", i, data.Height); + ok(td[i].stride == data.Stride, "%u: expected %d, got %d\n", i, td[i].stride, data.Stride); + ok(td[i].format == data.PixelFormat, "%u: expected %d, got %d\n", i, td[i].format, data.PixelFormat); + ok(td[i].size == data.Height * data.Stride, "%u: expected %d, got %d\n", i, td[i].size, data.Height * data.Stride); + if (td[i].mode & ImageLockModeUserInputBuf) + ok(data.Scan0 == buf, "%u: got wrong buffer\n", i); + if (td[i].size == data.Height * data.Stride) + { + UINT j, match, width_bytes = (data.Width * td[i].bpp) / 8; + + match = 1; + for (j = 0; j < data.Height; j++) + { + if (memcmp((const BYTE *)data.Scan0 + j * data.Stride, td[i].pixels + j * data.Stride, width_bytes) != 0) + { + match = 0; + break; + } + } + if ((td[i].mode & (ImageLockModeRead|ImageLockModeUserInputBuf)) || td[i].format == PixelFormat24bppRGB) + { + ok(match, + "%u: data should match\n", i); + if (!match) + { + BYTE *bits = data.Scan0; + printf("%u: data mismatch for format %#x:", i, td[i].format); + for (j = 0; j < td[i].size; j++) + printf(" %02x", bits[j]); + printf("\n"); + } + } + else + ok(!match, "%u: data shouldn't match\n", i); + + memset(data.Scan0, 0, td[i].size); + } + + status = GdipBitmapUnlockBits(bitmap, &data); + ok(status == Ok, "%u: GdipBitmapUnlockBits error %d\n", i, status); + + memset(&data, 0xfe, sizeof(data)); + status = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, PixelFormat24bppRGB, &data); + ok(status == Ok, "%u: GdipBitmapLockBits error %d\n", i, status); + ok(data.Width == 8, "%u: expected 8, got %d\n", i, data.Width); + ok(data.Height == 2, "%u: expected 2, got %d\n", i, data.Height); + ok(data.Stride == 24, "%u: expected 24, got %d\n", i, data.Stride); + ok(data.PixelFormat == PixelFormat24bppRGB, "%u: got wrong pixel format %d\n", i, data.PixelFormat); + ok(data.Height * data.Stride == 48, "%u: expected 48, got %d\n", i, data.Height * data.Stride); + if (data.Height * data.Stride == 48) + { + int match = memcmp(data.Scan0, td[i].pixels_unlocked, 48) == 0; + ok(match, "%u: data should match\n", i); + if (!match) + { + UINT j; + BYTE *bits = data.Scan0; + printf("%u: data mismatch for format %#x:", i, td[i].format); + for (j = 0; j < 48; j++) + printf(" %02x", bits[j]); + printf("\n"); + } + } + + status = GdipBitmapUnlockBits(bitmap, &data); + ok(status == Ok, "%u: GdipBitmapUnlockBits error %d\n", i, status); + + status = GdipDisposeImage((GpImage *)bitmap); + expect(Ok, status); + } +} + +static void test_DrawImage(void) +{ + BYTE black_1x1[4] = { 0,0,0,0 }; + BYTE white_2x2[16] = { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }; + BYTE black_2x2[16] = { 0,0,0,0,0,0,0xff,0xff, + 0,0,0,0,0,0,0xff,0xff }; + GpStatus status; + union + { + GpBitmap *bitmap; + GpImage *image; + } u1, u2; + GpGraphics *graphics; + int match; + + status = GdipCreateBitmapFromScan0(1, 1, 4, PixelFormat24bppRGB, black_1x1, &u1.bitmap); + expect(Ok, status); + status = GdipBitmapSetResolution(u1.bitmap, 100.0, 100.0); + expect(Ok, status); + + status = GdipCreateBitmapFromScan0(2, 2, 8, PixelFormat24bppRGB, white_2x2, &u2.bitmap); + expect(Ok, status); + status = GdipBitmapSetResolution(u2.bitmap, 300.0, 300.0); + expect(Ok, status); + status = GdipGetImageGraphicsContext(u2.image, &graphics); + expect(Ok, status); + status = GdipSetInterpolationMode(graphics, InterpolationModeNearestNeighbor); + expect(Ok, status); + + status = GdipDrawImageI(graphics, u1.image, 0, 0); + expect(Ok, status); + + match = memcmp(white_2x2, black_2x2, sizeof(black_2x2)) == 0; + ok(match, "data should match\n"); + if (!match) + { + UINT i, size = sizeof(white_2x2); + BYTE *bits = white_2x2; + for (i = 0; i < size; i++) + printf(" %02x", bits[i]); + printf("\n"); + } + + status = GdipDeleteGraphics(graphics); + expect(Ok, status); + status = GdipDisposeImage(u1.image); + expect(Ok, status); + status = GdipDisposeImage(u2.image); + expect(Ok, status); +} + +static void test_GdipDrawImagePointRect(void) +{ + BYTE black_1x1[4] = { 0,0,0,0 }; + BYTE white_2x2[16] = { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }; + BYTE black_2x2[16] = { 0,0,0,0,0,0,0xff,0xff, + 0,0,0,0,0,0,0xff,0xff }; + GpStatus status; + union + { + GpBitmap *bitmap; + GpImage *image; + } u1, u2; + GpGraphics *graphics; + int match; + + status = GdipCreateBitmapFromScan0(1, 1, 4, PixelFormat24bppRGB, black_1x1, &u1.bitmap); + expect(Ok, status); + status = GdipBitmapSetResolution(u1.bitmap, 100.0, 100.0); + expect(Ok, status); + + status = GdipCreateBitmapFromScan0(2, 2, 8, PixelFormat24bppRGB, white_2x2, &u2.bitmap); + expect(Ok, status); + status = GdipBitmapSetResolution(u2.bitmap, 300.0, 300.0); + expect(Ok, status); + status = GdipGetImageGraphicsContext(u2.image, &graphics); + expect(Ok, status); + status = GdipSetInterpolationMode(graphics, InterpolationModeNearestNeighbor); + expect(Ok, status); + + status = GdipDrawImagePointRectI(graphics, u1.image, 0, 0, 0, 0, 1, 1, UnitPixel); + expect(Ok, status); + + match = memcmp(white_2x2, black_2x2, sizeof(black_2x2)) == 0; + ok(match, "data should match\n"); + if (!match) + { + UINT i, size = sizeof(white_2x2); + BYTE *bits = white_2x2; + for (i = 0; i < size; i++) + printf(" %02x", bits[i]); + printf("\n"); + } + + status = GdipDeleteGraphics(graphics); + expect(Ok, status); + status = GdipDisposeImage(u1.image); + expect(Ok, status); + status = GdipDisposeImage(u2.image); + expect(Ok, status); +} + +static void test_image_format(void) +{ + static const PixelFormat fmt[] = + { + PixelFormat1bppIndexed, PixelFormat4bppIndexed, PixelFormat8bppIndexed, + PixelFormat16bppGrayScale, PixelFormat16bppRGB555, PixelFormat16bppRGB565, + PixelFormat16bppARGB1555, PixelFormat24bppRGB, PixelFormat32bppRGB, + PixelFormat32bppARGB, PixelFormat32bppPARGB, PixelFormat48bppRGB, + PixelFormat64bppARGB, PixelFormat64bppPARGB, PixelFormat32bppCMYK + }; + GpStatus status; + GpBitmap *bitmap; + GpImage *thumb; + HBITMAP hbitmap; + BITMAP bm; + PixelFormat format; + BitmapData data; + UINT i, ret; + + for (i = 0; i < sizeof(fmt)/sizeof(fmt[0]); i++) + { + status = GdipCreateBitmapFromScan0(1, 1, 0, fmt[i], NULL, &bitmap); + ok(status == Ok || broken(status == InvalidParameter) /* before win7 */, + "GdipCreateBitmapFromScan0 error %d\n", status); + if (status != Ok) continue; + + status = GdipGetImagePixelFormat((GpImage *)bitmap, &format); + expect(Ok, status); + expect(fmt[i], format); + + status = GdipCreateHBITMAPFromBitmap(bitmap, &hbitmap, 0); + if (fmt[i] == PixelFormat16bppGrayScale || fmt[i] == PixelFormat32bppCMYK) + todo_wine expect(InvalidParameter, status); + else + { + expect(Ok, status); + ret = GetObject(hbitmap, sizeof(bm), &bm); + expect(sizeof(bm), ret); + expect(0, bm.bmType); + expect(1, bm.bmWidth); + expect(1, bm.bmHeight); + expect(4, bm.bmWidthBytes); + expect(1, bm.bmPlanes); + expect(32, bm.bmBitsPixel); + DeleteObject(hbitmap); + } + + status = GdipGetImageThumbnail((GpImage *)bitmap, 0, 0, &thumb, NULL, NULL); + if (fmt[i] == PixelFormat16bppGrayScale || fmt[i] == PixelFormat32bppCMYK) + todo_wine + ok(status == OutOfMemory || broken(status == InvalidParameter) /* before win7 */, + "expected OutOfMemory, got %d\n", status); + else + { + expect(Ok, status); + status = GdipGetImagePixelFormat(thumb, &format); + expect(Ok, status); + ok(format == PixelFormat32bppPARGB || broken(format != PixelFormat32bppPARGB) /* before win7 */, + "expected PixelFormat32bppPARGB, got %#x\n", format); + status = GdipDisposeImage(thumb); + expect(Ok, status); + } + + status = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, PixelFormat32bppPARGB, &data); + if (fmt[i] == PixelFormat16bppGrayScale || fmt[i] == PixelFormat32bppCMYK) + todo_wine expect(InvalidParameter, status); + else + { + expect(Ok, status); + status = GdipBitmapUnlockBits(bitmap, &data); + expect(Ok, status); + } + + status = GdipDisposeImage((GpImage *)bitmap); + expect(Ok, status); + } +} + +static void test_DrawImage_scale(void) +{ + static const BYTE back_8x1[24] = { 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40 }; + static const BYTE image_080[24] = { 0x40,0x40,0x40,0x80,0x80,0x80,0x40,0x40,0x40,0x40,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40 }; + static const BYTE image_100[24] = { 0x40,0x40,0x40,0x80,0x80,0x80,0x80,0x80,0x80,0x40,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40 }; + static const BYTE image_120[24] = { 0x40,0x40,0x40,0x40,0x40,0x40,0x80,0x80,0x80,0x40,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40 }; + static const BYTE image_150[24] = { 0x40,0x40,0x40,0x40,0x40,0x40,0x80,0x80,0x80,0x80,0x80,0x80, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40 }; + static const BYTE image_180[24] = { 0x40,0x40,0x40,0x40,0x40,0x40,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40 }; + static const BYTE image_200[24] = { 0x40,0x40,0x40,0x40,0x40,0x40,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40 }; + static const BYTE image_250[24] = { 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x40,0x40,0x40 }; + static const BYTE image_120_half[24] = { 0x40,0x40,0x40,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40 }; + static const BYTE image_150_half[24] = { 0x40,0x40,0x40,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40 }; + static const BYTE image_200_half[24] = { 0x40,0x40,0x40,0x40,0x40,0x40,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x40,0x40,0x40,0x40,0x40,0x40 }; + static const BYTE image_250_half[24] = { 0x40,0x40,0x40,0x40,0x40,0x40,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x40,0x40,0x40 }; + static const struct test_data + { + REAL scale_x; + PixelOffsetMode pixel_offset_mode; + const BYTE *image; + BOOL todo; + } td[] = + { + { 0.8, PixelOffsetModeNone, image_080 }, /* 0 */ + { 1.0, PixelOffsetModeNone, image_100 }, + { 1.2, PixelOffsetModeNone, image_120 }, + { 1.5, PixelOffsetModeNone, image_150 }, + { 1.8, PixelOffsetModeNone, image_180 }, + { 2.0, PixelOffsetModeNone, image_200 }, + { 2.5, PixelOffsetModeNone, image_250 }, + + { 0.8, PixelOffsetModeHighSpeed, image_080 }, /* 7 */ + { 1.0, PixelOffsetModeHighSpeed, image_100 }, + { 1.2, PixelOffsetModeHighSpeed, image_120 }, + { 1.5, PixelOffsetModeHighSpeed, image_150 }, + { 1.8, PixelOffsetModeHighSpeed, image_180 }, + { 2.0, PixelOffsetModeHighSpeed, image_200 }, + { 2.5, PixelOffsetModeHighSpeed, image_250 }, + + { 0.8, PixelOffsetModeHalf, image_080 }, /* 14 */ + { 1.0, PixelOffsetModeHalf, image_100 }, + { 1.2, PixelOffsetModeHalf, image_120_half, TRUE }, + { 1.5, PixelOffsetModeHalf, image_150_half, TRUE }, + { 1.8, PixelOffsetModeHalf, image_180 }, + { 2.0, PixelOffsetModeHalf, image_200_half, TRUE }, + { 2.5, PixelOffsetModeHalf, image_250_half, TRUE }, + + { 0.8, PixelOffsetModeHighQuality, image_080 }, /* 21 */ + { 1.0, PixelOffsetModeHighQuality, image_100 }, + { 1.2, PixelOffsetModeHighQuality, image_120_half, TRUE }, + { 1.5, PixelOffsetModeHighQuality, image_150_half, TRUE }, + { 1.8, PixelOffsetModeHighQuality, image_180 }, + { 2.0, PixelOffsetModeHighQuality, image_200_half, TRUE }, + { 2.5, PixelOffsetModeHighQuality, image_250_half, TRUE }, + }; + BYTE src_2x1[6] = { 0x80,0x80,0x80,0x80,0x80,0x80 }; + BYTE dst_8x1[24]; + GpStatus status; + union + { + GpBitmap *bitmap; + GpImage *image; + } u1, u2; + GpGraphics *graphics; + GpMatrix *matrix; + int i, match; + + status = GdipCreateBitmapFromScan0(2, 1, 4, PixelFormat24bppRGB, src_2x1, &u1.bitmap); + expect(Ok, status); + status = GdipBitmapSetResolution(u1.bitmap, 100.0, 100.0); + expect(Ok, status); + + status = GdipCreateBitmapFromScan0(8, 1, 24, PixelFormat24bppRGB, dst_8x1, &u2.bitmap); + expect(Ok, status); + status = GdipBitmapSetResolution(u2.bitmap, 100.0, 100.0); + expect(Ok, status); + status = GdipGetImageGraphicsContext(u2.image, &graphics); + expect(Ok, status); + status = GdipSetInterpolationMode(graphics, InterpolationModeNearestNeighbor); + expect(Ok, status); + + for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) + { + status = GdipSetPixelOffsetMode(graphics, td[i].pixel_offset_mode); + expect(Ok, status); + + status = GdipCreateMatrix2(td[i].scale_x, 0.0, 0.0, 1.0, 0.0, 0.0, &matrix); + expect(Ok, status); + status = GdipSetWorldTransform(graphics, matrix); + expect(Ok, status); + GdipDeleteMatrix(matrix); + + memcpy(dst_8x1, back_8x1, sizeof(dst_8x1)); + status = GdipDrawImageI(graphics, u1.image, 1, 0); + expect(Ok, status); + + match = memcmp(dst_8x1, td[i].image, sizeof(dst_8x1)) == 0; + if (!match && td[i].todo) + todo_wine ok(match, "%d: data should match\n", i); + else + ok(match, "%d: data should match\n", i); + if (!match) + { + UINT i, size = sizeof(dst_8x1); + const BYTE *bits = dst_8x1; + for (i = 0; i < size; i++) + printf(" %02x", bits[i]); + printf("\n"); + } + } + + status = GdipDeleteGraphics(graphics); + expect(Ok, status); + status = GdipDisposeImage(u1.image); + expect(Ok, status); + status = GdipDisposeImage(u2.image); + expect(Ok, status); +} + +static const BYTE animatedgif[] = { +'G','I','F','8','9','a',0x01,0x00,0x01,0x00,0xA1,0x02,0x00, +0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c, +/*0x21,0xFF,0x0B,'A','N','I','M','E','X','T','S','1','.','0',*/ +0x21,0xFF,0x0B,'N','E','T','S','C','A','P','E','2','.','0', +0x03,0x01,0x05,0x00,0x00, +0x21,0xFE,0x0C,'H','e','l','l','o',' ','W','o','r','l','d','!',0x00, +0x21,0x01,0x0D,'a','n','i','m','a','t','i','o','n','.','g','i','f',0x00, +0x21,0xF9,0x04,0xff,0x0A,0x00,0x08,0x00, +0x2C,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x81, +0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac, +0x02,0x02,0x4C,0x01,0x00, +0x21,0xFE,0x08,'i','m','a','g','e',' ','#','1',0x00, +0x21,0x01,0x0C,'p','l','a','i','n','t','e','x','t',' ','#','1',0x00, +0x21,0xF9,0x04,0x00,0x14,0x00,0x01,0x00, +0x2C,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x81, +0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc, +0x02,0x02,0x44,0x01,0x00, +0x21,0xFE,0x08,'i','m','a','g','e',' ','#','2',0x00, +0x21,0x01,0x0C,'p','l','a','i','n','t','e','x','t',' ','#','2',0x00,0x3B +}; + +static void test_gif_properties(void) +{ + static const struct test_data + { + ULONG type, id, length; + const BYTE value[13]; + } td[] = + { + { PropertyTagTypeLong, PropertyTagFrameDelay, 8, { 10,0,0,0,20,0,0,0 } }, + { PropertyTagTypeASCII, PropertyTagExifUserComment, 13, { 'H','e','l','l','o',' ','W','o','r','l','d','!',0 } }, + { PropertyTagTypeShort, PropertyTagLoopCount, 2, { 5,0 } }, + { PropertyTagTypeByte, PropertyTagGlobalPalette, 12, { 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c } }, + { PropertyTagTypeByte, PropertyTagIndexBackground, 1, { 2 } }, + { PropertyTagTypeByte, PropertyTagIndexTransparent, 1, { 8 } } + }; + GpStatus status; + GpImage *image; + GUID guid; + UINT dim_count, frame_count, prop_count, prop_size, i; + UINT total_size, total_count; + PROPID *prop_id; + PropertyItem *prop_item; + const char *item_data; + + image = load_image(animatedgif, sizeof(animatedgif)); + if (!image) /* XP fails to load this GIF image */ + { + trace("Failed to load GIF image data\n"); + return; + } + + status = GdipImageGetFrameDimensionsCount(image, &dim_count); + expect(Ok, status); + expect(1, dim_count); + + status = GdipImageGetFrameDimensionsList(image, &guid, 1); + expect(Ok, status); + expect_guid(&FrameDimensionTime, &guid, __LINE__, FALSE); + + status = GdipImageGetFrameCount(image, &guid, &frame_count); + expect(Ok, status); + expect(2, frame_count); + + status = GdipImageSelectActiveFrame(image, &guid, 1); + expect(Ok, status); + + status = GdipGetPropertyCount(image, &prop_count); + expect(Ok, status); + ok(prop_count == sizeof(td)/sizeof(td[0]) || broken(prop_count == 1) /* before win7 */, + "expected property count %u, got %u\n", (UINT)(sizeof(td)/sizeof(td[0])), prop_count); + + if (prop_count != sizeof(td)/sizeof(td[0])) + { + GdipDisposeImage(image); + return; + } + + prop_id = HeapAlloc(GetProcessHeap(), 0, prop_count * sizeof(*prop_id)); + + status = GdipGetPropertyIdList(image, prop_count, prop_id); + expect(Ok, status); + + prop_size = 0; + for (i = 0; i < prop_count; i++) + { + UINT size; + status = GdipGetPropertyItemSize(image, prop_id[i], &size); + expect(Ok, status); + if (status != Ok) break; + ok(size > sizeof(*prop_item), "%u: too small item length %u\n", i, size); + + prop_size += size; + + prop_item = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); + status = GdipGetPropertyItem(image, prop_id[i], size, prop_item); + expect(Ok, status); + ok(prop_item->value == prop_item + 1, "expected item->value %p, got %p\n", prop_item + 1, prop_item->value); + ok(td[i].type == prop_item->type, + "%u: expected type %u, got %u\n", i, td[i].type, prop_item->type); + ok(td[i].id == prop_item->id, "%u: expected id %#x, got %#x\n", i, td[i].id, prop_item->id); + size -= sizeof(*prop_item); + ok(prop_item->length == size, "%u: expected length %u, got %u\n", i, size, prop_item->length); + ok(td[i].length == prop_item->length, "%u: expected length %u, got %u\n", i, td[i].length, prop_item->length); + if (td[i].length == prop_item->length) + { + int match = memcmp(td[i].value, prop_item->value, td[i].length) == 0; + ok(match, "%u: data mismatch\n", i); + if (!match) + { + UINT j; + BYTE *data = prop_item->value; + printf("id %#x:", prop_item->id); + for (j = 0; j < prop_item->length; j++) + printf(" %02x", data[j]); + printf("\n"); + } + } + HeapFree(GetProcessHeap(), 0, prop_item); + } + + HeapFree(GetProcessHeap(), 0, prop_id); + + status = GdipGetPropertySize(NULL, &total_size, &total_count); + expect(InvalidParameter, status); + status = GdipGetPropertySize(image, &total_size, NULL); + expect(InvalidParameter, status); + status = GdipGetPropertySize(image, NULL, &total_count); + expect(InvalidParameter, status); + status = GdipGetPropertySize(image, NULL, NULL); + expect(InvalidParameter, status); + total_size = 0xdeadbeef; + total_count = 0xdeadbeef; + status = GdipGetPropertySize(image, &total_size, &total_count); + expect(Ok, status); + ok(prop_count == total_count, + "expected total property count %u, got %u\n", prop_count, total_count); + ok(prop_size == total_size, + "expected total property size %u, got %u\n", prop_size, total_size); + + prop_item = HeapAlloc(GetProcessHeap(), 0, prop_size); + + status = GdipGetAllPropertyItems(image, 0, prop_count, prop_item); + expect(InvalidParameter, status); + status = GdipGetAllPropertyItems(image, prop_size, 1, prop_item); + expect(InvalidParameter, status); + status = GdipGetAllPropertyItems(image, prop_size, prop_count, NULL); + expect(InvalidParameter, status); + status = GdipGetAllPropertyItems(image, prop_size, prop_count, NULL); + expect(InvalidParameter, status); + status = GdipGetAllPropertyItems(image, 0, 0, NULL); + expect(InvalidParameter, status); + status = GdipGetAllPropertyItems(image, prop_size + 1, prop_count, prop_item); + expect(InvalidParameter, status); + status = GdipGetAllPropertyItems(image, prop_size, prop_count, prop_item); + expect(Ok, status); + + item_data = (const char *)(prop_item + prop_count); + for (i = 0; i < prop_count; i++) + { + ok(prop_item[i].value == item_data, "%u: expected value %p, got %p\n", + i, item_data, prop_item[i].value); + ok(td[i].type == prop_item[i].type, + "%u: expected type %u, got %u\n", i, td[i].type, prop_item[i].type); + ok(td[i].id == prop_item[i].id, "%u: expected id %#x, got %#x\n", i, td[i].id, prop_item[i].id); + ok(td[i].length == prop_item[i].length, "%u: expected length %u, got %u\n", i, td[i].length, prop_item[i].length); + if (td[i].length == prop_item[i].length) + { + int match = memcmp(td[i].value, prop_item[i].value, td[i].length) == 0; + ok(match, "%u: data mismatch\n", i); + if (!match) + { + UINT j; + BYTE *data = prop_item[i].value; + printf("id %#x:", prop_item[i].id); + for (j = 0; j < prop_item[i].length; j++) + printf(" %02x", data[j]); + printf("\n"); + } + } + item_data += prop_item[i].length; + } + + HeapFree(GetProcessHeap(), 0, prop_item); + + GdipDisposeImage(image); +} + START_TEST(image) { struct GdiplusStartupInput gdiplusStartupInput; @@ -2668,6 +4119,16 @@ START_TEST(image) GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); + test_DrawImage_scale(); + test_image_format(); + test_DrawImage(); + test_GdipDrawImagePointRect(); + test_bitmapbits(); + test_tiff_palette(); + test_GdipGetAllPropertyItems(); + test_tiff_properties(); + test_gif_properties(); + test_image_properties(); test_Scan0(); test_FromGdiDib(); test_GetImageDimension(); diff --git a/rostests/winetests/gdiplus/metafile.c b/rostests/winetests/gdiplus/metafile.c index 9530b0c540b..3366f0eabb7 100644 --- a/rostests/winetests/gdiplus/metafile.c +++ b/rostests/winetests/gdiplus/metafile.c @@ -426,6 +426,21 @@ static void test_getdc(void) expect(Ok, stat); expect(0, color); + stat = GdipBitmapSetPixel(bitmap, 15, 15, 0); + expect(Ok, stat); + + stat = GdipDrawImagePointsRect(graphics, (GpImage*)metafile, dst_points, 3, + 0.0, 0.0, 100.0, 100.0, UnitPixel, NULL, NULL, NULL); + expect(Ok, stat); + + stat = GdipBitmapGetPixel(bitmap, 15, 15, &color); + expect(Ok, stat); + expect(0, color); + + stat = GdipBitmapGetPixel(bitmap, 50, 50, &color); + expect(Ok, stat); + expect(0xff0000ff, color); + stat = GdipDeleteGraphics(graphics); expect(Ok, stat); @@ -459,6 +474,7 @@ static void test_emfonly(void) { GpStatus stat; GpMetafile *metafile; + GpImage *clone; GpGraphics *graphics; HDC hdc, metafile_dc; HENHMETAFILE hemf; @@ -530,6 +546,44 @@ static void test_emfonly(void) expect(Ok, stat); expect(0xff0000ff, color); + stat = GdipBitmapSetPixel(bitmap, 50, 50, 0); + expect(Ok, stat); + + stat = GdipDrawImagePointsRect(graphics, (GpImage*)metafile, dst_points, 3, + 0.0, 0.0, 100.0, 100.0, UnitPixel, NULL, NULL, NULL); + expect(Ok, stat); + + stat = GdipBitmapGetPixel(bitmap, 15, 15, &color); + expect(Ok, stat); + expect(0, color); + + stat = GdipBitmapGetPixel(bitmap, 50, 50, &color); + expect(Ok, stat); + expect(0xff0000ff, color); + + stat = GdipCloneImage((GpImage*)metafile, &clone); + expect(Ok, stat); + + if (stat == Ok) + { + stat = GdipBitmapSetPixel(bitmap, 50, 50, 0); + expect(Ok, stat); + + stat = GdipDrawImagePointsRect(graphics, clone, dst_points, 3, + 0.0, 0.0, 100.0, 100.0, UnitPixel, NULL, NULL, NULL); + expect(Ok, stat); + + stat = GdipBitmapGetPixel(bitmap, 15, 15, &color); + expect(Ok, stat); + expect(0, color); + + stat = GdipBitmapGetPixel(bitmap, 50, 50, &color); + expect(Ok, stat); + expect(0xff0000ff, color); + + GdipDisposeImage(clone); + } + stat = GdipDeleteGraphics(graphics); expect(Ok, stat);