1 /* 2 * Unit test suite for matrices 3 * 4 * Copyright (C) 2007 Google (Evan Stade) 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #include <math.h> 22 #include <limits.h> 23 24 #include "objbase.h" 25 #include "gdiplus.h" 26 #include "wine/test.h" 27 28 #define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got) 29 #define expectf(expected, got) ok(fabs(expected - got) < 0.0001, "Expected %.2f, got %.2f\n", expected, got) 30 31 static BOOL compare_float(float f, float g, unsigned int ulps) 32 { 33 int x = *(int *)&f; 34 int y = *(int *)&g; 35 36 if (x < 0) 37 x = INT_MIN - x; 38 if (y < 0) 39 y = INT_MIN - y; 40 41 if (abs(x - y) > ulps) 42 return FALSE; 43 44 return TRUE; 45 } 46 47 static void test_constructor_destructor(void) 48 { 49 GpStatus status; 50 GpMatrix *matrix = NULL; 51 52 status = GdipCreateMatrix2(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, &matrix); 53 expect(Ok, status); 54 ok(matrix != NULL, "Expected matrix to be initialized\n"); 55 56 status = GdipDeleteMatrix(NULL); 57 expect(InvalidParameter, status); 58 59 status = GdipDeleteMatrix(matrix); 60 expect(Ok, status); 61 } 62 63 typedef struct{ 64 REAL X; 65 REAL Y; 66 } real_point; 67 68 static real_point transform_points[] = { 69 {1000.00, 2600.00}, /*0*/ 70 {855.00, 2390.00}, /*1*/ 71 {700.00, 2200.00}, /*2*/ 72 {565.00, 1970.00}, /*3*/ 73 {400.00, 1800.00}, /*4*/ 74 {275.00, 1550.00}, /*5*/ 75 {100.00, 1400.00}, /*6*/ 76 {-15.00, 1130.00}, /*7*/ 77 {-200.00, 1000.00}, /*8*/ 78 {-305.00, 710.00} /*9*/ 79 }; 80 81 static void test_transform(void) 82 { 83 GpStatus status; 84 GpMatrix *matrix = NULL; 85 GpPointF pts[10]; 86 INT i; 87 BOOL match; 88 89 for(i = 0; i < 10; i ++){ 90 pts[i].X = i * 5.0 * (REAL)(i % 2); 91 pts[i].Y = 50.0 - i * 5.0; 92 } 93 94 GdipCreateMatrix2(1.0, -2.0, 30.0, 40.0, -500.0, 600.0, &matrix); 95 96 status = GdipTransformMatrixPoints(matrix, pts, 0); 97 expect(InvalidParameter, status); 98 99 status = GdipTransformMatrixPoints(matrix, pts, 10); 100 expect(Ok, status); 101 102 for(i = 0; i < 10; i ++){ 103 match = fabs(transform_points[i].X - pts[i].X) < 2.0 104 && fabs(transform_points[i].Y - pts[i].Y) < 2.0; 105 106 ok(match, "Expected #%d to be (%.2f, %.2f) but got (%.2f, %.2f)\n", i, 107 transform_points[i].X, transform_points[i].Y, pts[i].X, pts[i].Y); 108 } 109 110 GdipDeleteMatrix(matrix); 111 } 112 113 static void test_isinvertible(void) 114 { 115 GpStatus status; 116 GpMatrix *matrix = NULL; 117 BOOL result; 118 119 /* NULL arguments */ 120 status = GdipIsMatrixInvertible(NULL, &result); 121 expect(InvalidParameter, status); 122 status = GdipIsMatrixInvertible((GpMatrix*)0xdeadbeef, NULL); 123 expect(InvalidParameter, status); 124 status = GdipIsMatrixInvertible(NULL, NULL); 125 expect(InvalidParameter, status); 126 127 /* invertible */ 128 GdipCreateMatrix2(1.0, 1.2, 2.3, -1.0, 2.0, 3.0, &matrix); 129 status = GdipIsMatrixInvertible(matrix, &result); 130 expect(Ok, status); 131 expect(TRUE, result); 132 GdipDeleteMatrix(matrix); 133 134 /* noninvertible */ 135 GdipCreateMatrix2(2.0, -1.0, 6.0, -3.0, 2.2, 3.0, &matrix); 136 status = GdipIsMatrixInvertible(matrix, &result); 137 expect(Ok, status); 138 expect(FALSE, result); 139 GdipDeleteMatrix(matrix); 140 } 141 142 static void test_invert(void) 143 { 144 GpStatus status; 145 GpMatrix *matrix = NULL; 146 GpMatrix *inverted = NULL; 147 BOOL equal = FALSE; 148 REAL elems[6]; 149 150 /* NULL */ 151 status = GdipInvertMatrix(NULL); 152 expect(InvalidParameter, status); 153 154 /* noninvertible */ 155 GdipCreateMatrix2(2.0, -1.0, 6.0, -3.0, 2.2, 3.0, &matrix); 156 status = GdipInvertMatrix(matrix); 157 expect(InvalidParameter, status); 158 GdipDeleteMatrix(matrix); 159 160 /* invertible */ 161 GdipCreateMatrix2(3.0, -2.0, 5.0, 2.0, 6.0, 3.0, &matrix); 162 status = GdipInvertMatrix(matrix); 163 expect(Ok, status); 164 GdipCreateMatrix2(2.0/16.0, 2.0/16.0, -5.0/16.0, 3.0/16.0, 3.0/16.0, -21.0/16.0, &inverted); 165 GdipIsMatrixEqual(matrix, inverted, &equal); 166 expect(TRUE, equal); 167 GdipDeleteMatrix(matrix); 168 169 GdipCreateMatrix2(0.0006, 0, 0, 0.0006, 400, 400, &matrix); 170 status = GdipInvertMatrix(matrix); 171 expect(Ok, status); 172 status = GdipGetMatrixElements(matrix, elems); 173 expect(Ok, status); 174 ok(compare_float(elems[0], 1666.666504, 1), "elems[0] = %.10g\n", elems[0]); 175 ok(compare_float(elems[1], 0, 0), "elems[1] = %.10g\n", elems[1]); 176 ok(compare_float(elems[2], 0, 0), "elems[2] = %.10g\n", elems[2]); 177 ok(compare_float(elems[3], 1666.666504, 1), "elems[3] = %.10g\n", elems[3]); 178 ok(compare_float(elems[4], -666666.6875, 1), "elems[4] = %.10g\n", elems[4]); 179 ok(compare_float(elems[5], -666666.6875, 1), "elems[5] = %.10g\n", elems[5]); 180 181 GdipDeleteMatrix(inverted); 182 GdipDeleteMatrix(matrix); 183 } 184 185 static void test_shear(void) 186 { 187 GpStatus status; 188 GpMatrix *matrix = NULL; 189 GpMatrix *sheared = NULL; 190 BOOL equal; 191 192 /* NULL */ 193 status = GdipShearMatrix(NULL, 0.0, 0.0, MatrixOrderPrepend); 194 expect(InvalidParameter, status); 195 196 /* X only shearing, MatrixOrderPrepend */ 197 GdipCreateMatrix2(1.0, 2.0, 4.0, -1.0, 6.0, 3.0, &matrix); 198 status = GdipShearMatrix(matrix, 1.5, 0.0, MatrixOrderPrepend); 199 expect(Ok, status); 200 GdipCreateMatrix2(1.0, 2.0, 5.5, 2.0, 6.0, 3.0, &sheared); 201 GdipIsMatrixEqual(matrix, sheared, &equal); 202 expect(TRUE, equal); 203 GdipDeleteMatrix(sheared); 204 GdipDeleteMatrix(matrix); 205 206 /* X only shearing, MatrixOrderAppend */ 207 GdipCreateMatrix2(1.0, 2.0, 4.0, -1.0, 6.0, 3.0, &matrix); 208 status = GdipShearMatrix(matrix, 1.5, 0.0, MatrixOrderAppend); 209 expect(Ok, status); 210 GdipCreateMatrix2(4.0, 2.0, 2.5, -1.0, 10.5, 3.0, &sheared); 211 GdipIsMatrixEqual(matrix, sheared, &equal); 212 expect(TRUE, equal); 213 GdipDeleteMatrix(sheared); 214 GdipDeleteMatrix(matrix); 215 216 /* Y only shearing, MatrixOrderPrepend */ 217 GdipCreateMatrix2(1.0, 2.0, 4.0, -1.0, 6.0, 3.0, &matrix); 218 status = GdipShearMatrix(matrix, 0.0, 1.5, MatrixOrderPrepend); 219 expect(Ok, status); 220 GdipCreateMatrix2(7.0, 0.5, 4.0, -1.0, 6.0, 3.0, &sheared); 221 GdipIsMatrixEqual(matrix, sheared, &equal); 222 expect(TRUE, equal); 223 GdipDeleteMatrix(sheared); 224 GdipDeleteMatrix(matrix); 225 226 /* Y only shearing, MatrixOrderAppend */ 227 GdipCreateMatrix2(1.0, 2.0, 4.0, -1.0, 6.0, 3.0, &matrix); 228 status = GdipShearMatrix(matrix, 0.0, 1.5, MatrixOrderAppend); 229 expect(Ok, status); 230 GdipCreateMatrix2(1.0, 3.5, 4.0, 5.0, 6.0, 12.0, &sheared); 231 GdipIsMatrixEqual(matrix, sheared, &equal); 232 expect(TRUE, equal); 233 GdipDeleteMatrix(sheared); 234 GdipDeleteMatrix(matrix); 235 236 /* X,Y shearing, MatrixOrderPrepend */ 237 GdipCreateMatrix2(1.0, 2.0, 4.0, -1.0, 6.0, 3.0, &matrix); 238 status = GdipShearMatrix(matrix, 4.0, 1.5, MatrixOrderPrepend); 239 expect(Ok, status); 240 GdipCreateMatrix2(7.0, 0.5, 8.0, 7.0, 6.0, 3.0, &sheared); 241 GdipIsMatrixEqual(matrix, sheared, &equal); 242 expect(TRUE, equal); 243 GdipDeleteMatrix(sheared); 244 GdipDeleteMatrix(matrix); 245 246 /* X,Y shearing, MatrixOrderAppend */ 247 GdipCreateMatrix2(1.0, 2.0, 4.0, -1.0, 6.0, 3.0, &matrix); 248 status = GdipShearMatrix(matrix, 4.0, 1.5, MatrixOrderAppend); 249 expect(Ok, status); 250 GdipCreateMatrix2(9.0, 3.5, 0.0, 5.0, 18.0, 12.0, &sheared); 251 GdipIsMatrixEqual(matrix, sheared, &equal); 252 expect(TRUE, equal); 253 GdipDeleteMatrix(sheared); 254 GdipDeleteMatrix(matrix); 255 } 256 257 static void test_constructor3(void) 258 { 259 /* MSDN is on crack. GdipCreateMatrix3 makes a matrix that transforms the 260 * corners of the given rectangle to the three points given. */ 261 GpMatrix *matrix; 262 REAL values[6]; 263 GpRectF rc; 264 GpPointF pt[3]; 265 GpStatus stat; 266 267 rc.X = 10; 268 rc.Y = 10; 269 rc.Width = 10; 270 rc.Height = 10; 271 272 pt[0].X = 10; 273 pt[0].Y = 10; 274 pt[1].X = 20; 275 pt[1].Y = 10; 276 pt[2].X = 10; 277 pt[2].Y = 20; 278 279 stat = GdipCreateMatrix3(&rc, pt, &matrix); 280 expect(Ok, stat); 281 282 stat = GdipGetMatrixElements(matrix, values); 283 expect(Ok, stat); 284 285 expectf(1.0, values[0]); 286 expectf(0.0, values[1]); 287 expectf(0.0, values[2]); 288 expectf(1.0, values[3]); 289 expectf(0.0, values[4]); 290 expectf(0.0, values[5]); 291 292 GdipDeleteMatrix(matrix); 293 294 pt[0].X = 20; 295 pt[0].Y = 10; 296 pt[1].X = 40; 297 pt[1].Y = 10; 298 pt[2].X = 20; 299 pt[2].Y = 20; 300 301 stat = GdipCreateMatrix3(&rc, pt, &matrix); 302 expect(Ok, stat); 303 304 stat = GdipGetMatrixElements(matrix, values); 305 expect(Ok, stat); 306 307 expectf(2.0, values[0]); 308 expectf(0.0, values[1]); 309 expectf(0.0, values[2]); 310 expectf(1.0, values[3]); 311 expectf(0.0, values[4]); 312 expectf(0.0, values[5]); 313 314 GdipDeleteMatrix(matrix); 315 316 pt[0].X = 10; 317 pt[0].Y = 20; 318 pt[1].X = 20; 319 pt[1].Y = 30; 320 pt[2].X = 10; 321 pt[2].Y = 30; 322 323 stat = GdipCreateMatrix3(&rc, pt, &matrix); 324 expect(Ok, stat); 325 326 stat = GdipGetMatrixElements(matrix, values); 327 expect(Ok, stat); 328 329 expectf(1.0, values[0]); 330 expectf(1.0, values[1]); 331 expectf(0.0, values[2]); 332 expectf(1.0, values[3]); 333 expectf(0.0, values[4]); 334 expectf(0.0, values[5]); 335 336 GdipDeleteMatrix(matrix); 337 } 338 339 static void test_isidentity(void) 340 { 341 GpMatrix *matrix; 342 GpStatus stat; 343 BOOL result; 344 345 stat = GdipIsMatrixIdentity(NULL, NULL); 346 expect(InvalidParameter, stat); 347 348 stat = GdipIsMatrixIdentity(NULL, &result); 349 expect(InvalidParameter, stat); 350 351 stat = GdipCreateMatrix2(1.0, 0.0, 0.0, 1.0, 0.0, 0.0, &matrix); 352 expect(Ok, stat); 353 354 stat = GdipIsMatrixIdentity(matrix, NULL); 355 expect(InvalidParameter, stat); 356 357 result = FALSE; 358 stat = GdipIsMatrixIdentity(matrix, &result); 359 expect(Ok, stat); 360 ok(!!result, "got %d\n", result); 361 362 stat = GdipSetMatrixElements(matrix, 1.0, 0.0, 0.0, 1.0, 0.1, 0.0); 363 expect(Ok, stat); 364 365 result = TRUE; 366 stat = GdipIsMatrixIdentity(matrix, &result); 367 expect(Ok, stat); 368 ok(!result, "got %d\n", result); 369 370 GdipDeleteMatrix(matrix); 371 } 372 373 START_TEST(matrix) 374 { 375 struct GdiplusStartupInput gdiplusStartupInput; 376 ULONG_PTR gdiplusToken; 377 HMODULE hmsvcrt; 378 int (CDECL * _controlfp_s)(unsigned int *cur, unsigned int newval, unsigned int mask); 379 380 /* Enable all FP exceptions except _EM_INEXACT, which gdi32 can trigger */ 381 hmsvcrt = LoadLibraryA("msvcrt"); 382 _controlfp_s = (void*)GetProcAddress(hmsvcrt, "_controlfp_s"); 383 if (_controlfp_s) _controlfp_s(0, 0, 0x0008001e); 384 385 gdiplusStartupInput.GdiplusVersion = 1; 386 gdiplusStartupInput.DebugEventCallback = NULL; 387 gdiplusStartupInput.SuppressBackgroundThread = 0; 388 gdiplusStartupInput.SuppressExternalCodecs = 0; 389 390 GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); 391 392 test_constructor_destructor(); 393 test_transform(); 394 test_isinvertible(); 395 test_invert(); 396 test_shear(); 397 test_constructor3(); 398 test_isidentity(); 399 400 GdiplusShutdown(gdiplusToken); 401 } 402