gdiplus: Improve performance of GdipTranslateMatrix.
[wine.git] / dlls / gdiplus / tests / matrix.c
blobdea08c1312026e99d0b990813ae68288dff93437
1 /*
2 * Unit test suite for matrices
4 * Copyright (C) 2007 Google (Evan Stade)
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.
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.
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
21 #include <math.h>
22 #include <limits.h>
24 #include "objbase.h"
25 #include "gdiplus.h"
26 #include "wine/test.h"
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)
31 static BOOL compare_float(float f, float g, unsigned int ulps)
33 int x = *(int *)&f;
34 int y = *(int *)&g;
36 if (x < 0)
37 x = INT_MIN - x;
38 if (y < 0)
39 y = INT_MIN - y;
41 if (abs(x - y) > ulps)
42 return FALSE;
44 return TRUE;
47 static void test_constructor_destructor(void)
49 GpStatus status;
50 GpMatrix *matrix = NULL;
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");
56 status = GdipDeleteMatrix(NULL);
57 expect(InvalidParameter, status);
59 status = GdipDeleteMatrix(matrix);
60 expect(Ok, status);
63 typedef struct{
64 REAL X;
65 REAL Y;
66 } real_point;
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*/
81 static void test_transform(void)
83 GpStatus status;
84 GpMatrix *matrix = NULL;
85 GpPointF pts[10];
86 INT i;
87 BOOL match;
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;
94 GdipCreateMatrix2(1.0, -2.0, 30.0, 40.0, -500.0, 600.0, &matrix);
96 status = GdipTransformMatrixPoints(matrix, pts, 0);
97 expect(InvalidParameter, status);
99 status = GdipTransformMatrixPoints(matrix, pts, 10);
100 expect(Ok, status);
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;
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);
110 GdipDeleteMatrix(matrix);
113 static void test_translate(void)
115 GpStatus status;
116 GpMatrix *matrix = NULL;
117 REAL elems[6];
119 static const REAL expected_elem_append[] = {1.0, -2.0, 30.0, 40.0, -470.0, 620.0};
120 static const REAL expected_elem_prepend[] = {1.0, -2.0, 30.0, 40.0, 130.0, 1340.0};
122 GdipCreateMatrix2(1.0, -2.0, 30.0, 40.0, -500.0, 600.0, &matrix);
124 status = GdipTranslateMatrix(NULL, 30.0, 20.0, MatrixOrderAppend);
125 expect(InvalidParameter, status);
126 status = GdipTranslateMatrix(matrix, 30.0, 20.0, MatrixOrderAppend);
127 expect(Ok, status);
129 status = GdipGetMatrixElements(matrix, elems);
130 expect(Ok, status);
131 GdipDeleteMatrix(matrix);
133 for(INT i = 0; i < 6; i++)
134 ok(expected_elem_append[i] == elems[i], "Expected #%d to be (%.1f) but got (%.1f)\n", i,
135 expected_elem_append[i], elems[i]);
137 GdipCreateMatrix2(1.0, -2.0, 30.0, 40.0, -500.0, 600.0, &matrix);
139 status = GdipTranslateMatrix(matrix, 30.0, 20.0, MatrixOrderPrepend);
140 expect(Ok, status);
142 status = GdipGetMatrixElements(matrix, elems);
143 expect(Ok, status);
144 GdipDeleteMatrix(matrix);
146 for(INT i = 0; i < 6; i++)
147 ok(expected_elem_prepend[i] == elems[i], "Expected #%d to be (%.1f) but got (%.1f)\n", i,
148 expected_elem_prepend[i], elems[i]);
152 static void test_scale(void)
154 GpStatus status;
155 GpMatrix *matrix = NULL;
156 REAL elems[6];
157 int i;
159 static const REAL expected_elem[] = {3.0, -4.0, 90.0, 80.0, -1500.0, 1200.0};
160 static const REAL expected_elem2[] = {3.0, -6.0, 60.0, 80.0, -500.0, 600.0};
162 GdipCreateMatrix2(1.0, -2.0, 30.0, 40.0, -500.0, 600.0, &matrix);
164 status = GdipScaleMatrix(NULL, 3, 2, MatrixOrderAppend);
165 expect(InvalidParameter, status);
166 status = GdipScaleMatrix(matrix, 3, 2, MatrixOrderAppend);
167 expect(Ok, status);
169 status = GdipGetMatrixElements(matrix, elems);
170 expect(Ok, status);
171 GdipDeleteMatrix(matrix);
173 for(i = 0; i < 6; i++)
174 ok(expected_elem[i] == elems[i], "Expected #%d to be (%.1f) but got (%.1f)\n", i,
175 expected_elem[i], elems[i]);
177 GdipCreateMatrix2(1.0, -2.0, 30.0, 40.0, -500.0, 600.0, &matrix);
179 status = GdipScaleMatrix(matrix, 3, 2, MatrixOrderPrepend);
180 expect(Ok, status);
182 status = GdipGetMatrixElements(matrix, elems);
183 expect(Ok, status);
184 GdipDeleteMatrix(matrix);
186 for(i = 0; i < 6; i++)
187 ok(expected_elem2[i] == elems[i], "Expected #%d to be (%.1f) but got (%.1f)\n", i,
188 expected_elem2[i], elems[i]);
192 static void test_isinvertible(void)
194 GpStatus status;
195 GpMatrix *matrix = NULL;
196 BOOL result;
198 /* NULL arguments */
199 status = GdipIsMatrixInvertible(NULL, &result);
200 expect(InvalidParameter, status);
201 status = GdipIsMatrixInvertible((GpMatrix*)0xdeadbeef, NULL);
202 expect(InvalidParameter, status);
203 status = GdipIsMatrixInvertible(NULL, NULL);
204 expect(InvalidParameter, status);
206 /* invertible */
207 GdipCreateMatrix2(1.0, 1.2, 2.3, -1.0, 2.0, 3.0, &matrix);
208 status = GdipIsMatrixInvertible(matrix, &result);
209 expect(Ok, status);
210 expect(TRUE, result);
211 GdipDeleteMatrix(matrix);
213 /* noninvertible */
214 GdipCreateMatrix2(2.0, -1.0, 6.0, -3.0, 2.2, 3.0, &matrix);
215 status = GdipIsMatrixInvertible(matrix, &result);
216 expect(Ok, status);
217 expect(FALSE, result);
218 GdipDeleteMatrix(matrix);
221 static void test_invert(void)
223 GpStatus status;
224 GpMatrix *matrix = NULL;
225 GpMatrix *inverted = NULL;
226 BOOL equal = FALSE;
227 REAL elems[6];
229 /* NULL */
230 status = GdipInvertMatrix(NULL);
231 expect(InvalidParameter, status);
233 /* noninvertible */
234 GdipCreateMatrix2(2.0, -1.0, 6.0, -3.0, 2.2, 3.0, &matrix);
235 status = GdipInvertMatrix(matrix);
236 expect(InvalidParameter, status);
237 GdipDeleteMatrix(matrix);
239 /* invertible */
240 GdipCreateMatrix2(3.0, -2.0, 5.0, 2.0, 6.0, 3.0, &matrix);
241 status = GdipInvertMatrix(matrix);
242 expect(Ok, status);
243 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);
244 GdipIsMatrixEqual(matrix, inverted, &equal);
245 expect(TRUE, equal);
246 GdipDeleteMatrix(matrix);
248 GdipCreateMatrix2(0.0006, 0, 0, 0.0006, 400, 400, &matrix);
249 status = GdipInvertMatrix(matrix);
250 expect(Ok, status);
251 status = GdipGetMatrixElements(matrix, elems);
252 expect(Ok, status);
253 ok(compare_float(elems[0], 1666.666504, 1), "elems[0] = %.10g\n", elems[0]);
254 ok(compare_float(elems[1], 0, 0), "elems[1] = %.10g\n", elems[1]);
255 ok(compare_float(elems[2], 0, 0), "elems[2] = %.10g\n", elems[2]);
256 ok(compare_float(elems[3], 1666.666504, 1), "elems[3] = %.10g\n", elems[3]);
257 ok(compare_float(elems[4], -666666.6875, 1), "elems[4] = %.10g\n", elems[4]);
258 ok(compare_float(elems[5], -666666.6875, 1), "elems[5] = %.10g\n", elems[5]);
260 GdipDeleteMatrix(inverted);
261 GdipDeleteMatrix(matrix);
264 static void test_shear(void)
266 GpStatus status;
267 GpMatrix *matrix = NULL;
268 GpMatrix *sheared = NULL;
269 BOOL equal;
271 /* NULL */
272 status = GdipShearMatrix(NULL, 0.0, 0.0, MatrixOrderPrepend);
273 expect(InvalidParameter, status);
275 /* X only shearing, MatrixOrderPrepend */
276 GdipCreateMatrix2(1.0, 2.0, 4.0, -1.0, 6.0, 3.0, &matrix);
277 status = GdipShearMatrix(matrix, 1.5, 0.0, MatrixOrderPrepend);
278 expect(Ok, status);
279 GdipCreateMatrix2(1.0, 2.0, 5.5, 2.0, 6.0, 3.0, &sheared);
280 GdipIsMatrixEqual(matrix, sheared, &equal);
281 expect(TRUE, equal);
282 GdipDeleteMatrix(sheared);
283 GdipDeleteMatrix(matrix);
285 /* X only shearing, MatrixOrderAppend */
286 GdipCreateMatrix2(1.0, 2.0, 4.0, -1.0, 6.0, 3.0, &matrix);
287 status = GdipShearMatrix(matrix, 1.5, 0.0, MatrixOrderAppend);
288 expect(Ok, status);
289 GdipCreateMatrix2(4.0, 2.0, 2.5, -1.0, 10.5, 3.0, &sheared);
290 GdipIsMatrixEqual(matrix, sheared, &equal);
291 expect(TRUE, equal);
292 GdipDeleteMatrix(sheared);
293 GdipDeleteMatrix(matrix);
295 /* Y only shearing, MatrixOrderPrepend */
296 GdipCreateMatrix2(1.0, 2.0, 4.0, -1.0, 6.0, 3.0, &matrix);
297 status = GdipShearMatrix(matrix, 0.0, 1.5, MatrixOrderPrepend);
298 expect(Ok, status);
299 GdipCreateMatrix2(7.0, 0.5, 4.0, -1.0, 6.0, 3.0, &sheared);
300 GdipIsMatrixEqual(matrix, sheared, &equal);
301 expect(TRUE, equal);
302 GdipDeleteMatrix(sheared);
303 GdipDeleteMatrix(matrix);
305 /* Y only shearing, MatrixOrderAppend */
306 GdipCreateMatrix2(1.0, 2.0, 4.0, -1.0, 6.0, 3.0, &matrix);
307 status = GdipShearMatrix(matrix, 0.0, 1.5, MatrixOrderAppend);
308 expect(Ok, status);
309 GdipCreateMatrix2(1.0, 3.5, 4.0, 5.0, 6.0, 12.0, &sheared);
310 GdipIsMatrixEqual(matrix, sheared, &equal);
311 expect(TRUE, equal);
312 GdipDeleteMatrix(sheared);
313 GdipDeleteMatrix(matrix);
315 /* X,Y shearing, MatrixOrderPrepend */
316 GdipCreateMatrix2(1.0, 2.0, 4.0, -1.0, 6.0, 3.0, &matrix);
317 status = GdipShearMatrix(matrix, 4.0, 1.5, MatrixOrderPrepend);
318 expect(Ok, status);
319 GdipCreateMatrix2(7.0, 0.5, 8.0, 7.0, 6.0, 3.0, &sheared);
320 GdipIsMatrixEqual(matrix, sheared, &equal);
321 expect(TRUE, equal);
322 GdipDeleteMatrix(sheared);
323 GdipDeleteMatrix(matrix);
325 /* X,Y shearing, MatrixOrderAppend */
326 GdipCreateMatrix2(1.0, 2.0, 4.0, -1.0, 6.0, 3.0, &matrix);
327 status = GdipShearMatrix(matrix, 4.0, 1.5, MatrixOrderAppend);
328 expect(Ok, status);
329 GdipCreateMatrix2(9.0, 3.5, 0.0, 5.0, 18.0, 12.0, &sheared);
330 GdipIsMatrixEqual(matrix, sheared, &equal);
331 expect(TRUE, equal);
332 GdipDeleteMatrix(sheared);
333 GdipDeleteMatrix(matrix);
336 static void test_constructor3(void)
338 /* MSDN is on crack. GdipCreateMatrix3 makes a matrix that transforms the
339 * corners of the given rectangle to the three points given. */
340 GpMatrix *matrix;
341 REAL values[6];
342 GpRectF rc;
343 GpPointF pt[3];
344 GpStatus stat;
346 rc.X = 10;
347 rc.Y = 10;
348 rc.Width = 10;
349 rc.Height = 10;
351 pt[0].X = 10;
352 pt[0].Y = 10;
353 pt[1].X = 20;
354 pt[1].Y = 10;
355 pt[2].X = 10;
356 pt[2].Y = 20;
358 stat = GdipCreateMatrix3(&rc, pt, &matrix);
359 expect(Ok, stat);
361 stat = GdipGetMatrixElements(matrix, values);
362 expect(Ok, stat);
364 expectf(1.0, values[0]);
365 expectf(0.0, values[1]);
366 expectf(0.0, values[2]);
367 expectf(1.0, values[3]);
368 expectf(0.0, values[4]);
369 expectf(0.0, values[5]);
371 GdipDeleteMatrix(matrix);
373 pt[0].X = 20;
374 pt[0].Y = 10;
375 pt[1].X = 40;
376 pt[1].Y = 10;
377 pt[2].X = 20;
378 pt[2].Y = 20;
380 stat = GdipCreateMatrix3(&rc, pt, &matrix);
381 expect(Ok, stat);
383 stat = GdipGetMatrixElements(matrix, values);
384 expect(Ok, stat);
386 expectf(2.0, values[0]);
387 expectf(0.0, values[1]);
388 expectf(0.0, values[2]);
389 expectf(1.0, values[3]);
390 expectf(0.0, values[4]);
391 expectf(0.0, values[5]);
393 GdipDeleteMatrix(matrix);
395 pt[0].X = 10;
396 pt[0].Y = 20;
397 pt[1].X = 20;
398 pt[1].Y = 30;
399 pt[2].X = 10;
400 pt[2].Y = 30;
402 stat = GdipCreateMatrix3(&rc, pt, &matrix);
403 expect(Ok, stat);
405 stat = GdipGetMatrixElements(matrix, values);
406 expect(Ok, stat);
408 expectf(1.0, values[0]);
409 expectf(1.0, values[1]);
410 expectf(0.0, values[2]);
411 expectf(1.0, values[3]);
412 expectf(0.0, values[4]);
413 expectf(0.0, values[5]);
415 GdipDeleteMatrix(matrix);
418 static void test_isidentity(void)
420 GpMatrix *matrix;
421 GpStatus stat;
422 BOOL result;
424 stat = GdipIsMatrixIdentity(NULL, NULL);
425 expect(InvalidParameter, stat);
427 stat = GdipIsMatrixIdentity(NULL, &result);
428 expect(InvalidParameter, stat);
430 stat = GdipCreateMatrix2(1.0, 0.0, 0.0, 1.0, 0.0, 0.0, &matrix);
431 expect(Ok, stat);
433 stat = GdipIsMatrixIdentity(matrix, NULL);
434 expect(InvalidParameter, stat);
436 result = FALSE;
437 stat = GdipIsMatrixIdentity(matrix, &result);
438 expect(Ok, stat);
439 ok(!!result, "got %d\n", result);
441 stat = GdipSetMatrixElements(matrix, 1.0, 0.0, 0.0, 1.0, 0.1, 0.0);
442 expect(Ok, stat);
444 result = TRUE;
445 stat = GdipIsMatrixIdentity(matrix, &result);
446 expect(Ok, stat);
447 ok(!result, "got %d\n", result);
449 GdipDeleteMatrix(matrix);
452 START_TEST(matrix)
454 struct GdiplusStartupInput gdiplusStartupInput;
455 ULONG_PTR gdiplusToken;
456 HMODULE hmsvcrt;
457 int (CDECL * _controlfp_s)(unsigned int *cur, unsigned int newval, unsigned int mask);
459 /* Enable all FP exceptions except _EM_INEXACT, which gdi32 can trigger */
460 hmsvcrt = LoadLibraryA("msvcrt");
461 _controlfp_s = (void*)GetProcAddress(hmsvcrt, "_controlfp_s");
462 if (_controlfp_s) _controlfp_s(0, 0, 0x0008001e);
464 gdiplusStartupInput.GdiplusVersion = 1;
465 gdiplusStartupInput.DebugEventCallback = NULL;
466 gdiplusStartupInput.SuppressBackgroundThread = 0;
467 gdiplusStartupInput.SuppressExternalCodecs = 0;
469 GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
471 test_constructor_destructor();
472 test_translate();
473 test_scale();
474 test_transform();
475 test_isinvertible();
476 test_invert();
477 test_shear();
478 test_constructor3();
479 test_isidentity();
481 GdiplusShutdown(gdiplusToken);