gdiplus: GdipIsMatrixInvertible implementation with tests.
[wine/multimedia.git] / dlls / gdiplus / matrix.c
blobdcdc2bdb3c199370f1bbc1e49ad16cec6fdcb8ba
1 /*
2 * Copyright (C) 2007 Google (Evan Stade)
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.
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.
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
19 #include <stdarg.h>
20 #include <math.h>
22 #include "windef.h"
23 #include "winbase.h"
24 #include "wingdi.h"
26 #include "objbase.h"
28 #include "gdiplus.h"
29 #include "gdiplus_private.h"
31 /* Multiplies two matrices of the form
33 * idx:0 idx:1 0
34 * idx:2 idx:3 0
35 * idx:4 idx:5 1
37 * and puts the output in out.
38 * */
39 static void matrix_multiply(GDIPCONST REAL * left, GDIPCONST REAL * right, REAL * out)
41 REAL temp[6];
42 int i, odd;
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);
50 memcpy(out, temp, 6 * sizeof(REAL));
53 GpStatus WINGDIPAPI GdipCreateMatrix2(REAL m11, REAL m12, REAL m21, REAL m22,
54 REAL dx, REAL dy, GpMatrix **matrix)
56 if(!matrix)
57 return InvalidParameter;
59 *matrix = GdipAlloc(sizeof(GpMatrix));
60 if(!*matrix) return OutOfMemory;
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;
72 return Ok;
75 GpStatus WINGDIPAPI GdipCreateMatrix3(GDIPCONST GpRectF *rect,
76 GDIPCONST GpPointF *pt, GpMatrix **matrix)
78 if(!matrix)
79 return InvalidParameter;
81 *matrix = GdipAlloc(sizeof(GpMatrix));
82 if(!*matrix) return OutOfMemory;
84 memcpy((*matrix)->matrix, rect, 4 * sizeof(REAL));
86 (*matrix)->matrix[4] = pt->X;
87 (*matrix)->matrix[5] = pt->Y;
89 return Ok;
92 GpStatus WINGDIPAPI GdipCreateMatrix3I(GDIPCONST GpRect *rect, GDIPCONST GpPoint *pt,
93 GpMatrix **matrix)
95 GpRectF rectF;
96 GpPointF ptF;
98 rectF.X = (REAL)rect->X;
99 rectF.Y = (REAL)rect->Y;
100 rectF.Width = (REAL)rect->Width;
101 rectF.Height = (REAL)rect->Height;
103 ptF.X = (REAL)pt->X;
104 ptF.Y = (REAL)pt->Y;
106 return GdipCreateMatrix3(&rectF, &ptF, matrix);
109 GpStatus WINGDIPAPI GdipCloneMatrix(GpMatrix *matrix, GpMatrix **clone)
111 if(!matrix || !clone)
112 return InvalidParameter;
114 *clone = GdipAlloc(sizeof(GpMatrix));
115 if(!*clone) return OutOfMemory;
117 **clone = *matrix;
119 return Ok;
122 GpStatus WINGDIPAPI GdipCreateMatrix(GpMatrix **matrix)
124 if(!matrix)
125 return InvalidParameter;
127 *matrix = GdipAlloc(sizeof(GpMatrix));
128 if(!*matrix) return OutOfMemory;
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;
137 return Ok;
140 GpStatus WINGDIPAPI GdipDeleteMatrix(GpMatrix *matrix)
142 if(!matrix)
143 return InvalidParameter;
145 GdipFree(matrix);
147 return Ok;
150 GpStatus WINGDIPAPI GdipGetMatrixElements(GDIPCONST GpMatrix *matrix,
151 REAL *out)
153 if(!matrix || !out)
154 return InvalidParameter;
156 memcpy(out, matrix->matrix, sizeof(matrix->matrix));
158 return Ok;
161 GpStatus WINGDIPAPI GdipIsMatrixInvertible(GDIPCONST GpMatrix *matrix, BOOL *result)
163 REAL det;
165 if(!matrix || !result)
166 return InvalidParameter;
168 det = matrix->matrix[0]*matrix->matrix[3] - matrix->matrix[1]*matrix->matrix[2];
170 *result = (fabs(det) >= 1e-5);
172 return Ok;
175 GpStatus WINGDIPAPI GdipMultiplyMatrix(GpMatrix *matrix, GpMatrix* matrix2,
176 GpMatrixOrder order)
178 if(!matrix || !matrix2)
179 return InvalidParameter;
181 if(order == MatrixOrderAppend)
182 matrix_multiply(matrix->matrix, matrix2->matrix, matrix->matrix);
183 else
184 matrix_multiply(matrix2->matrix, matrix->matrix, matrix->matrix);
186 return Ok;
189 GpStatus WINGDIPAPI GdipRotateMatrix(GpMatrix *matrix, REAL angle,
190 GpMatrixOrder order)
192 REAL cos_theta, sin_theta, rotate[6];
194 if(!matrix)
195 return InvalidParameter;
197 angle = deg2rad(angle);
198 cos_theta = cos(angle);
199 sin_theta = sin(angle);
201 rotate[0] = cos_theta;
202 rotate[1] = sin_theta;
203 rotate[2] = -sin_theta;
204 rotate[3] = cos_theta;
205 rotate[4] = 0.0;
206 rotate[5] = 0.0;
208 if(order == MatrixOrderAppend)
209 matrix_multiply(matrix->matrix, rotate, matrix->matrix);
210 else
211 matrix_multiply(rotate, matrix->matrix, matrix->matrix);
213 return Ok;
216 GpStatus WINGDIPAPI GdipScaleMatrix(GpMatrix *matrix, REAL scaleX, REAL scaleY,
217 GpMatrixOrder order)
219 REAL scale[6];
221 if(!matrix)
222 return InvalidParameter;
224 scale[0] = scaleX;
225 scale[1] = 0.0;
226 scale[2] = 0.0;
227 scale[3] = scaleY;
228 scale[4] = 0.0;
229 scale[5] = 0.0;
231 if(order == MatrixOrderAppend)
232 matrix_multiply(matrix->matrix, scale, matrix->matrix);
233 else
234 matrix_multiply(scale, matrix->matrix, matrix->matrix);
236 return Ok;
239 GpStatus WINGDIPAPI GdipSetMatrixElements(GpMatrix *matrix, REAL m11, REAL m12,
240 REAL m21, REAL m22, REAL dx, REAL dy)
242 if(!matrix)
243 return InvalidParameter;
245 matrix->matrix[0] = m11;
246 matrix->matrix[1] = m12;
247 matrix->matrix[2] = m21;
248 matrix->matrix[3] = m22;
249 matrix->matrix[4] = dx;
250 matrix->matrix[5] = dy;
252 return Ok;
255 GpStatus WINGDIPAPI GdipTransformMatrixPoints(GpMatrix *matrix, GpPointF *pts,
256 INT count)
258 REAL x, y;
259 INT i;
261 if(!matrix || !pts || count <= 0)
262 return InvalidParameter;
264 for(i = 0; i < count; i++)
266 x = pts[i].X;
267 y = pts[i].Y;
269 pts[i].X = x * matrix->matrix[0] + y * matrix->matrix[2] + matrix->matrix[4];
270 pts[i].Y = x * matrix->matrix[1] + y * matrix->matrix[3] + matrix->matrix[5];
273 return Ok;
276 GpStatus WINGDIPAPI GdipTransformMatrixPointsI(GpMatrix *matrix, GpPoint *pts, INT count)
278 GpPointF *ptsF;
279 GpStatus ret;
280 INT i;
282 if(count <= 0)
283 return InvalidParameter;
285 ptsF = GdipAlloc(sizeof(GpPointF) * count);
286 if(!ptsF)
287 return OutOfMemory;
289 for(i = 0; i < count; i++){
290 ptsF[i].X = (REAL)pts[i].X;
291 ptsF[i].Y = (REAL)pts[i].Y;
294 ret = GdipTransformMatrixPoints(matrix, ptsF, count);
296 if(ret == Ok)
297 for(i = 0; i < count; i++){
298 pts[i].X = roundr(ptsF[i].X);
299 pts[i].Y = roundr(ptsF[i].Y);
301 GdipFree(ptsF);
303 return ret;
306 GpStatus WINGDIPAPI GdipTranslateMatrix(GpMatrix *matrix, REAL offsetX,
307 REAL offsetY, GpMatrixOrder order)
309 REAL translate[6];
311 if(!matrix)
312 return InvalidParameter;
314 translate[0] = 1.0;
315 translate[1] = 0.0;
316 translate[2] = 0.0;
317 translate[3] = 1.0;
318 translate[4] = offsetX;
319 translate[5] = offsetY;
321 if(order == MatrixOrderAppend)
322 matrix_multiply(matrix->matrix, translate, matrix->matrix);
323 else
324 matrix_multiply(translate, matrix->matrix, matrix->matrix);
326 return Ok;
329 GpStatus WINGDIPAPI GdipVectorTransformMatrixPoints(GpMatrix *matrix, GpPointF *pts, INT count)
331 REAL x, y;
332 INT i;
334 if(!matrix || !pts || count <= 0)
335 return InvalidParameter;
337 for(i = 0; i < count; i++)
339 x = pts[i].X;
340 y = pts[i].Y;
342 pts[i].X = x * matrix->matrix[0] + y * matrix->matrix[2];
343 pts[i].Y = x * matrix->matrix[1] + y * matrix->matrix[3];
346 return Ok;
349 GpStatus WINGDIPAPI GdipVectorTransformMatrixPointsI(GpMatrix *matrix, GpPoint *pts, INT count)
351 GpPointF *ptsF;
352 GpStatus ret;
353 INT i;
355 if(count <= 0)
356 return InvalidParameter;
358 ptsF = GdipAlloc(sizeof(GpPointF) * count);
359 if(!ptsF)
360 return OutOfMemory;
362 for(i = 0; i < count; i++){
363 ptsF[i].X = (REAL)pts[i].X;
364 ptsF[i].Y = (REAL)pts[i].Y;
367 ret = GdipVectorTransformMatrixPoints(matrix, ptsF, count);
368 /* store back */
369 if(ret == Ok)
370 for(i = 0; i < count; i++){
371 pts[i].X = roundr(ptsF[i].X);
372 pts[i].Y = roundr(ptsF[i].Y);
374 GdipFree(ptsF);
376 return ret;
379 GpStatus WINGDIPAPI GdipIsMatrixEqual(GDIPCONST GpMatrix *matrix, GDIPCONST GpMatrix *matrix2,
380 BOOL *result)
382 if(!matrix || !matrix2 || !result)
383 return InvalidParameter;
384 /* based on single array member of GpMatrix */
385 *result = (memcmp(matrix->matrix, matrix2->matrix, sizeof(GpMatrix)) == 0);
387 return Ok;
390 GpStatus WINGDIPAPI GdipIsMatrixIdentity(GDIPCONST GpMatrix *matrix, BOOL *result)
392 GpMatrix *e;
393 GpStatus ret;
394 BOOL isIdentity;
396 if(!matrix || !result)
397 return InvalidParameter;
399 ret = GdipCreateMatrix(&e);
400 if(ret != Ok) return ret;
402 ret = GdipIsMatrixEqual(matrix, e, &isIdentity);
403 if(ret == Ok)
404 *result = isIdentity;
406 GdipFree(e);
408 return ret;