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