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