gdiplus: Fix a typo in the parameter validation check.
[wine] / dlls / gdiplus / matrix.c
1 /*
2  * Copyright (C) 2007 Google (Evan Stade)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include <stdarg.h>
20 #include <math.h>
21
22 #include "windef.h"
23 #include "winbase.h"
24 #include "wingdi.h"
25
26 #include "objbase.h"
27
28 #include "gdiplus.h"
29 #include "gdiplus_private.h"
30
31 /* Multiplies two matrices of the form
32  *
33  * idx:0 idx:1     0
34  * idx:2 idx:3     0
35  * idx:4 idx:5     1
36  *
37  * and puts the output in out.
38  * */
39 static void matrix_multiply(GDIPCONST REAL * left, GDIPCONST REAL * right, REAL * out)
40 {
41     REAL temp[6];
42     int i, odd;
43
44     for(i = 0; i < 6; i++){
45         odd = i % 2;
46         temp[i] = left[i - odd] * right[odd] + left[i - odd + 1] * right[odd + 2] +
47                   (i >= 4 ? right[odd + 4] : 0.0);
48     }
49
50     memcpy(out, temp, 6 * sizeof(REAL));
51 }
52
53 GpStatus WINGDIPAPI GdipCreateMatrix2(REAL m11, REAL m12, REAL m21, REAL m22,
54     REAL dx, REAL dy, GpMatrix **matrix)
55 {
56     if(!matrix)
57         return InvalidParameter;
58
59     *matrix = GdipAlloc(sizeof(GpMatrix));
60     if(!*matrix)    return OutOfMemory;
61
62     /* first row */
63     (*matrix)->matrix[0] = m11;
64     (*matrix)->matrix[1] = m12;
65     /* second row */
66     (*matrix)->matrix[2] = m21;
67     (*matrix)->matrix[3] = m22;
68     /* third row */
69     (*matrix)->matrix[4] = dx;
70     (*matrix)->matrix[5] = dy;
71
72     return Ok;
73 }
74
75 GpStatus WINGDIPAPI GdipCreateMatrix3(GDIPCONST GpRectF *rect,
76     GDIPCONST GpPointF *pt, GpMatrix **matrix)
77 {
78     if(!matrix)
79         return InvalidParameter;
80
81     *matrix = GdipAlloc(sizeof(GpMatrix));
82     if(!*matrix)    return OutOfMemory;
83
84     memcpy((*matrix)->matrix, rect, 4 * sizeof(REAL));
85
86     (*matrix)->matrix[4] = pt->X;
87     (*matrix)->matrix[5] = pt->Y;
88
89     return Ok;
90 }
91
92 GpStatus WINGDIPAPI GdipCreateMatrix3I(GDIPCONST GpRect *rect, GDIPCONST GpPoint *pt,
93     GpMatrix **matrix)
94 {
95     GpRectF rectF;
96     GpPointF ptF;
97
98     rectF.X = (REAL)rect->X;
99     rectF.Y = (REAL)rect->Y;
100     rectF.Width = (REAL)rect->Width;
101     rectF.Height = (REAL)rect->Height;
102
103     ptF.X = (REAL)pt->X;
104     ptF.Y = (REAL)pt->Y;
105
106     return GdipCreateMatrix3(&rectF, &ptF, matrix);
107 }
108
109 GpStatus WINGDIPAPI GdipCloneMatrix(GpMatrix *matrix, GpMatrix **clone)
110 {
111     if(!matrix || !clone)
112         return InvalidParameter;
113
114     *clone = GdipAlloc(sizeof(GpMatrix));
115     if(!*clone)    return OutOfMemory;
116
117     **clone = *matrix;
118
119     return Ok;
120 }
121
122 GpStatus WINGDIPAPI GdipCreateMatrix(GpMatrix **matrix)
123 {
124     if(!matrix)
125         return InvalidParameter;
126
127     *matrix = GdipAlloc(sizeof(GpMatrix));
128     if(!*matrix)    return OutOfMemory;
129
130     (*matrix)->matrix[0] = 1.0;
131     (*matrix)->matrix[1] = 0.0;
132     (*matrix)->matrix[2] = 0.0;
133     (*matrix)->matrix[3] = 1.0;
134     (*matrix)->matrix[4] = 0.0;
135     (*matrix)->matrix[5] = 0.0;
136
137     return Ok;
138 }
139
140 GpStatus WINGDIPAPI GdipDeleteMatrix(GpMatrix *matrix)
141 {
142     if(!matrix)
143         return InvalidParameter;
144
145     GdipFree(matrix);
146
147     return Ok;
148 }
149
150 GpStatus WINGDIPAPI GdipGetMatrixElements(GDIPCONST GpMatrix *matrix,
151     REAL *out)
152 {
153     if(!matrix || !out)
154         return InvalidParameter;
155
156     memcpy(out, matrix->matrix, sizeof(matrix->matrix));
157
158     return Ok;
159 }
160
161 GpStatus WINGDIPAPI GdipMultiplyMatrix(GpMatrix *matrix, GpMatrix* matrix2,
162     GpMatrixOrder order)
163 {
164     if(!matrix || !matrix2)
165         return InvalidParameter;
166
167     if(order == MatrixOrderAppend)
168         matrix_multiply(matrix->matrix, matrix2->matrix, matrix->matrix);
169     else
170         matrix_multiply(matrix2->matrix, matrix->matrix, matrix->matrix);
171
172     return Ok;
173 }
174
175 GpStatus WINGDIPAPI GdipRotateMatrix(GpMatrix *matrix, REAL angle,
176     GpMatrixOrder order)
177 {
178     REAL cos_theta, sin_theta, rotate[6];
179
180     if(!matrix)
181         return InvalidParameter;
182
183     angle = deg2rad(angle);
184     cos_theta = cos(angle);
185     sin_theta = sin(angle);
186
187     rotate[0] = cos_theta;
188     rotate[1] = sin_theta;
189     rotate[2] = -sin_theta;
190     rotate[3] = cos_theta;
191     rotate[4] = 0.0;
192     rotate[5] = 0.0;
193
194     if(order == MatrixOrderAppend)
195         matrix_multiply(matrix->matrix, rotate, matrix->matrix);
196     else
197         matrix_multiply(rotate, matrix->matrix, matrix->matrix);
198
199     return Ok;
200 }
201
202 GpStatus WINGDIPAPI GdipScaleMatrix(GpMatrix *matrix, REAL scaleX, REAL scaleY,
203     GpMatrixOrder order)
204 {
205     REAL scale[6];
206
207     if(!matrix)
208         return InvalidParameter;
209
210     scale[0] = scaleX;
211     scale[1] = 0.0;
212     scale[2] = 0.0;
213     scale[3] = scaleY;
214     scale[4] = 0.0;
215     scale[5] = 0.0;
216
217     if(order == MatrixOrderAppend)
218         matrix_multiply(matrix->matrix, scale, matrix->matrix);
219     else
220         matrix_multiply(scale, matrix->matrix, matrix->matrix);
221
222     return Ok;
223 }
224
225 GpStatus WINGDIPAPI GdipSetMatrixElements(GpMatrix *matrix, REAL m11, REAL m12,
226     REAL m21, REAL m22, REAL dx, REAL dy)
227 {
228     if(!matrix)
229         return InvalidParameter;
230
231     matrix->matrix[0] = m11;
232     matrix->matrix[1] = m12;
233     matrix->matrix[2] = m21;
234     matrix->matrix[3] = m22;
235     matrix->matrix[4] = dx;
236     matrix->matrix[5] = dy;
237
238     return Ok;
239 }
240
241 GpStatus WINGDIPAPI GdipTransformMatrixPoints(GpMatrix *matrix, GpPointF *pts,
242                                               INT count)
243 {
244     REAL x, y;
245     INT i;
246
247     if(!matrix || !pts || count <= 0)
248         return InvalidParameter;
249
250     for(i = 0; i < count; i++)
251     {
252         x = pts[i].X;
253         y = pts[i].Y;
254
255         pts[i].X = x * matrix->matrix[0] + y * matrix->matrix[2] + matrix->matrix[4];
256         pts[i].Y = x * matrix->matrix[1] + y * matrix->matrix[3] + matrix->matrix[5];
257     }
258
259     return Ok;
260 }
261
262 GpStatus WINGDIPAPI GdipTransformMatrixPointsI(GpMatrix *matrix, GpPoint *pts, INT count)
263 {
264     GpPointF *ptsF;
265     GpStatus ret;
266     INT i;
267
268     if(count <= 0)
269         return InvalidParameter;
270
271     ptsF = GdipAlloc(sizeof(GpPointF) * count);
272     if(!ptsF)
273         return OutOfMemory;
274
275     for(i = 0; i < count; i++){
276         ptsF[i].X = (REAL)pts[i].X;
277         ptsF[i].Y = (REAL)pts[i].Y;
278     }
279
280     ret = GdipTransformMatrixPoints(matrix, ptsF, count);
281
282     if(ret == Ok)
283         for(i = 0; i < count; i++){
284             pts[i].X = roundr(ptsF[i].X);
285             pts[i].Y = roundr(ptsF[i].Y);
286         }
287     GdipFree(ptsF);
288
289     return ret;
290 }
291
292 GpStatus WINGDIPAPI GdipTranslateMatrix(GpMatrix *matrix, REAL offsetX,
293     REAL offsetY, GpMatrixOrder order)
294 {
295     REAL translate[6];
296
297     if(!matrix)
298         return InvalidParameter;
299
300     translate[0] = 1.0;
301     translate[1] = 0.0;
302     translate[2] = 0.0;
303     translate[3] = 1.0;
304     translate[4] = offsetX;
305     translate[5] = offsetY;
306
307     if(order == MatrixOrderAppend)
308         matrix_multiply(matrix->matrix, translate, matrix->matrix);
309     else
310         matrix_multiply(translate, matrix->matrix, matrix->matrix);
311
312     return Ok;
313 }
314
315 GpStatus WINGDIPAPI GdipVectorTransformMatrixPoints(GpMatrix *matrix, GpPointF *pts, INT count)
316 {
317     REAL x, y;
318     INT i;
319
320     if(!matrix || !pts || count <= 0)
321         return InvalidParameter;
322
323     for(i = 0; i < count; i++)
324     {
325         x = pts[i].X;
326         y = pts[i].Y;
327
328         pts[i].X = x * matrix->matrix[0] + y * matrix->matrix[2];
329         pts[i].Y = x * matrix->matrix[1] + y * matrix->matrix[3];
330     }
331
332     return Ok;
333 }
334
335 GpStatus WINGDIPAPI GdipVectorTransformMatrixPointsI(GpMatrix *matrix, GpPoint *pts, INT count)
336 {
337     GpPointF *ptsF;
338     GpStatus ret;
339     INT i;
340
341     if(count <= 0)
342         return InvalidParameter;
343
344     ptsF = GdipAlloc(sizeof(GpPointF) * count);
345     if(!ptsF)
346         return OutOfMemory;
347
348     for(i = 0; i < count; i++){
349         ptsF[i].X = (REAL)pts[i].X;
350         ptsF[i].Y = (REAL)pts[i].Y;
351     }
352
353     ret = GdipVectorTransformMatrixPoints(matrix, ptsF, count);
354     /* store back */
355     if(ret == Ok)
356         for(i = 0; i < count; i++){
357             pts[i].X = roundr(ptsF[i].X);
358             pts[i].Y = roundr(ptsF[i].Y);
359         }
360     GdipFree(ptsF);
361
362     return ret;
363 }
364
365 GpStatus WINGDIPAPI GdipIsMatrixEqual(GDIPCONST GpMatrix *matrix, GDIPCONST GpMatrix *matrix2,
366     BOOL *result)
367 {
368     if(!matrix || !matrix2 || !result)
369         return InvalidParameter;
370     /* based on single array member of GpMatrix */
371     *result = (memcmp(matrix->matrix, matrix2->matrix, sizeof(GpMatrix)) == 0);
372
373     return Ok;
374 }
375
376 GpStatus WINGDIPAPI GdipIsMatrixIdentity(GDIPCONST GpMatrix *matrix, BOOL *result)
377 {
378     GpMatrix *e;
379     GpStatus ret;
380     BOOL isIdentity;
381
382     if(!matrix || !result)
383         return InvalidParameter;
384
385     ret = GdipCreateMatrix(&e);
386     if(ret != Ok) return ret;
387
388     ret = GdipIsMatrixEqual(matrix, e, &isIdentity);
389     if(ret == Ok)
390         *result = isIdentity;
391
392     GdipFree(e);
393
394     return ret;
395 }