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 
163     GdipCreateMatrix2(0.0006, 0, 0, 0.0006, 400, 400, &matrix);
164     status = GdipInvertMatrix(matrix);
165     expect(Ok, status);
166     status = GdipGetMatrixElements(matrix, elems);
167     expect(Ok, status);
168     ok(compare_float(elems[0], 1666.666504, 1), "elems[0] = %.10g\n", elems[0]);
169     ok(compare_float(elems[1], 0, 0), "elems[1] = %.10g\n", elems[1]);
170     ok(compare_float(elems[2], 0, 0), "elems[2] = %.10g\n", elems[2]);
171     ok(compare_float(elems[3], 1666.666504, 1), "elems[3] = %.10g\n", elems[3]);
172     ok(compare_float(elems[4], -666666.6875, 1), "elems[4] = %.10g\n", elems[4]);
173     ok(compare_float(elems[5], -666666.6875, 1), "elems[5] = %.10g\n", elems[5]);
174 
175     GdipDeleteMatrix(inverted);
176     GdipDeleteMatrix(matrix);
177 }
178 
179 static void test_shear(void)
180 {
181     GpStatus status;
182     GpMatrix *matrix  = NULL;
183     GpMatrix *sheared = NULL;
184     BOOL equal;
185 
186     /* NULL */
187     status = GdipShearMatrix(NULL, 0.0, 0.0, MatrixOrderPrepend);
188     expect(InvalidParameter, status);
189 
190     /* X only shearing, MatrixOrderPrepend */
191     GdipCreateMatrix2(1.0, 2.0, 4.0, -1.0, 6.0, 3.0, &matrix);
192     status = GdipShearMatrix(matrix, 1.5, 0.0, MatrixOrderPrepend);
193     expect(Ok, status);
194     GdipCreateMatrix2(1.0, 2.0, 5.5, 2.0, 6.0, 3.0, &sheared);
195     GdipIsMatrixEqual(matrix, sheared, &equal);
196     expect(TRUE, equal);
197     GdipDeleteMatrix(sheared);
198     GdipDeleteMatrix(matrix);
199 
200     /* X only shearing, MatrixOrderAppend */
201     GdipCreateMatrix2(1.0, 2.0, 4.0, -1.0, 6.0, 3.0, &matrix);
202     status = GdipShearMatrix(matrix, 1.5, 0.0, MatrixOrderAppend);
203     expect(Ok, status);
204     GdipCreateMatrix2(4.0, 2.0, 2.5, -1.0, 10.5, 3.0, &sheared);
205     GdipIsMatrixEqual(matrix, sheared, &equal);
206     expect(TRUE, equal);
207     GdipDeleteMatrix(sheared);
208     GdipDeleteMatrix(matrix);
209 
210     /* Y only shearing, MatrixOrderPrepend */
211     GdipCreateMatrix2(1.0, 2.0, 4.0, -1.0, 6.0, 3.0, &matrix);
212     status = GdipShearMatrix(matrix, 0.0, 1.5, MatrixOrderPrepend);
213     expect(Ok, status);
214     GdipCreateMatrix2(7.0, 0.5, 4.0, -1.0, 6.0, 3.0, &sheared);
215     GdipIsMatrixEqual(matrix, sheared, &equal);
216     expect(TRUE, equal);
217     GdipDeleteMatrix(sheared);
218     GdipDeleteMatrix(matrix);
219 
220     /* Y only shearing, MatrixOrderAppend */
221     GdipCreateMatrix2(1.0, 2.0, 4.0, -1.0, 6.0, 3.0, &matrix);
222     status = GdipShearMatrix(matrix, 0.0, 1.5, MatrixOrderAppend);
223     expect(Ok, status);
224     GdipCreateMatrix2(1.0, 3.5, 4.0, 5.0, 6.0, 12.0, &sheared);
225     GdipIsMatrixEqual(matrix, sheared, &equal);
226     expect(TRUE, equal);
227     GdipDeleteMatrix(sheared);
228     GdipDeleteMatrix(matrix);
229 
230     /* X,Y shearing, MatrixOrderPrepend */
231     GdipCreateMatrix2(1.0, 2.0, 4.0, -1.0, 6.0, 3.0, &matrix);
232     status = GdipShearMatrix(matrix, 4.0, 1.5, MatrixOrderPrepend);
233     expect(Ok, status);
234     GdipCreateMatrix2(7.0, 0.5, 8.0, 7.0, 6.0, 3.0, &sheared);
235     GdipIsMatrixEqual(matrix, sheared, &equal);
236     expect(TRUE, equal);
237     GdipDeleteMatrix(sheared);
238     GdipDeleteMatrix(matrix);
239 
240     /* X,Y shearing, MatrixOrderAppend */
241     GdipCreateMatrix2(1.0, 2.0, 4.0, -1.0, 6.0, 3.0, &matrix);
242     status = GdipShearMatrix(matrix, 4.0, 1.5, MatrixOrderAppend);
243     expect(Ok, status);
244     GdipCreateMatrix2(9.0, 3.5, 0.0, 5.0, 18.0, 12.0, &sheared);
245     GdipIsMatrixEqual(matrix, sheared, &equal);
246     expect(TRUE, equal);
247     GdipDeleteMatrix(sheared);
248     GdipDeleteMatrix(matrix);
249 }
250 
251 static void test_constructor3(void)
252 {
253     /* MSDN is on crack. GdipCreateMatrix3 makes a matrix that transforms the
254      * corners of the given rectangle to the three points given. */
255     GpMatrix *matrix;
256     REAL values[6];
257     GpRectF rc;
258     GpPointF pt[3];
259     GpStatus stat;
260 
261     rc.X = 10;
262     rc.Y = 10;
263     rc.Width = 10;
264     rc.Height = 10;
265 
266     pt[0].X = 10;
267     pt[0].Y = 10;
268     pt[1].X = 20;
269     pt[1].Y = 10;
270     pt[2].X = 10;
271     pt[2].Y = 20;
272 
273     stat = GdipCreateMatrix3(&rc, pt, &matrix);
274     expect(Ok, stat);
275 
276     stat = GdipGetMatrixElements(matrix, values);
277     expect(Ok, stat);
278 
279     expectf(1.0, values[0]);
280     expectf(0.0, values[1]);
281     expectf(0.0, values[2]);
282     expectf(1.0, values[3]);
283     expectf(0.0, values[4]);
284     expectf(0.0, values[5]);
285 
286     GdipDeleteMatrix(matrix);
287 
288     pt[0].X = 20;
289     pt[0].Y = 10;
290     pt[1].X = 40;
291     pt[1].Y = 10;
292     pt[2].X = 20;
293     pt[2].Y = 20;
294 
295     stat = GdipCreateMatrix3(&rc, pt, &matrix);
296     expect(Ok, stat);
297 
298     stat = GdipGetMatrixElements(matrix, values);
299     expect(Ok, stat);
300 
301     expectf(2.0, values[0]);
302     expectf(0.0, values[1]);
303     expectf(0.0, values[2]);
304     expectf(1.0, values[3]);
305     expectf(0.0, values[4]);
306     expectf(0.0, values[5]);
307 
308     GdipDeleteMatrix(matrix);
309 
310     pt[0].X = 10;
311     pt[0].Y = 20;
312     pt[1].X = 20;
313     pt[1].Y = 30;
314     pt[2].X = 10;
315     pt[2].Y = 30;
316 
317     stat = GdipCreateMatrix3(&rc, pt, &matrix);
318     expect(Ok, stat);
319 
320     stat = GdipGetMatrixElements(matrix, values);
321     expect(Ok, stat);
322 
323     expectf(1.0, values[0]);
324     expectf(1.0, values[1]);
325     expectf(0.0, values[2]);
326     expectf(1.0, values[3]);
327     expectf(0.0, values[4]);
328     expectf(0.0, values[5]);
329 
330     GdipDeleteMatrix(matrix);
331 }
332 
333 static void test_isidentity(void)
334 {
335     GpMatrix *matrix;
336     GpStatus stat;
337     BOOL result;
338 
339     stat = GdipIsMatrixIdentity(NULL, NULL);
340     expect(InvalidParameter, stat);
341 
342     stat = GdipIsMatrixIdentity(NULL, &result);
343     expect(InvalidParameter, stat);
344 
345     stat = GdipCreateMatrix2(1.0, 0.0, 0.0, 1.0, 0.0, 0.0, &matrix);
346     expect(Ok, stat);
347 
348     stat = GdipIsMatrixIdentity(matrix, NULL);
349     expect(InvalidParameter, stat);
350 
351     result = FALSE;
352     stat = GdipIsMatrixIdentity(matrix, &result);
353     expect(Ok, stat);
354     ok(!!result, "got %d\n", result);
355 
356     stat = GdipSetMatrixElements(matrix, 1.0, 0.0, 0.0, 1.0, 0.1, 0.0);
357     expect(Ok, stat);
358 
359     result = TRUE;
360     stat = GdipIsMatrixIdentity(matrix, &result);
361     expect(Ok, stat);
362     ok(!result, "got %d\n", result);
363 
364     GdipDeleteMatrix(matrix);
365 }
366 
367 START_TEST(matrix)
368 {
369     struct GdiplusStartupInput gdiplusStartupInput;
370     ULONG_PTR gdiplusToken;
371     HMODULE hmsvcrt;
372     int (CDECL * _controlfp_s)(unsigned int *cur, unsigned int newval, unsigned int mask);
373 
374     /* Enable all FP exceptions except _EM_INEXACT, which gdi32 can trigger */
375     hmsvcrt = LoadLibraryA("msvcrt");
376     _controlfp_s = (void*)GetProcAddress(hmsvcrt, "_controlfp_s");
377     if (_controlfp_s) _controlfp_s(0, 0, 0x0008001e);
378 
379     gdiplusStartupInput.GdiplusVersion              = 1;
380     gdiplusStartupInput.DebugEventCallback          = NULL;
381     gdiplusStartupInput.SuppressBackgroundThread    = 0;
382     gdiplusStartupInput.SuppressExternalCodecs      = 0;
383 
384     GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
385 
386     test_constructor_destructor();
387     test_transform();
388     test_isinvertible();
389     test_invert();
390     test_shear();
391     test_constructor3();
392     test_isidentity();
393 
394     GdiplusShutdown(gdiplusToken);
395 }
396