- Synchronize with wine head
[reactos.git] / reactos / dll / win32 / 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)
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 ptsF = GdipAlloc(sizeof(GpPointF) * count);
269 if(!ptsF)
270 return OutOfMemory;
271
272 for(i = 0; i < count; i++){
273 ptsF[i].X = (REAL)pts[i].X;
274 ptsF[i].Y = (REAL)pts[i].Y;
275 }
276
277 ret = GdipTransformMatrixPoints(matrix, ptsF, count);
278
279 if(ret == Ok)
280 for(i = 0; i < count; i++){
281 pts[i].X = roundr(ptsF[i].X);
282 pts[i].Y = roundr(ptsF[i].Y);
283 }
284 GdipFree(ptsF);
285
286 return ret;
287 }
288
289 GpStatus WINGDIPAPI GdipTranslateMatrix(GpMatrix *matrix, REAL offsetX,
290 REAL offsetY, GpMatrixOrder order)
291 {
292 REAL translate[6];
293
294 if(!matrix)
295 return InvalidParameter;
296
297 translate[0] = 1.0;
298 translate[1] = 0.0;
299 translate[2] = 0.0;
300 translate[3] = 1.0;
301 translate[4] = offsetX;
302 translate[5] = offsetY;
303
304 if(order == MatrixOrderAppend)
305 matrix_multiply(matrix->matrix, translate, matrix->matrix);
306 else
307 matrix_multiply(translate, matrix->matrix, matrix->matrix);
308
309 return Ok;
310 }
311
312 GpStatus WINGDIPAPI GdipVectorTransformMatrixPoints(GpMatrix *matrix, GpPointF *pts, INT count)
313 {
314 REAL x, y;
315 INT i;
316
317 if(!matrix || !pts)
318 return InvalidParameter;
319
320 for(i = 0; i < count; i++)
321 {
322 x = pts[i].X;
323 y = pts[i].Y;
324
325 pts[i].X = x * matrix->matrix[0] + y * matrix->matrix[2];
326 pts[i].Y = x * matrix->matrix[1] + y * matrix->matrix[3];
327 }
328
329 return Ok;
330 }
331
332 GpStatus WINGDIPAPI GdipVectorTransformMatrixPointsI(GpMatrix *matrix, GpPoint *pts, INT count)
333 {
334 GpPointF *ptsF;
335 GpStatus ret;
336 INT i;
337
338 ptsF = GdipAlloc(sizeof(GpPointF) * count);
339 if(!ptsF)
340 return OutOfMemory;
341
342 for(i = 0; i < count; i++){
343 ptsF[i].X = (REAL)pts[i].X;
344 ptsF[i].Y = (REAL)pts[i].Y;
345 }
346
347 ret = GdipVectorTransformMatrixPoints(matrix, ptsF, count);
348 /* store back */
349 if(ret == Ok)
350 for(i = 0; i < count; i++){
351 pts[i].X = roundr(ptsF[i].X);
352 pts[i].Y = roundr(ptsF[i].Y);
353 }
354 GdipFree(ptsF);
355
356 return ret;
357 }
358
359 GpStatus WINGDIPAPI GdipIsMatrixEqual(GDIPCONST GpMatrix *matrix, GDIPCONST GpMatrix *matrix2,
360 BOOL *result)
361 {
362 if(!matrix || !matrix2 || !result)
363 return InvalidParameter;
364 /* based on single array member of GpMatrix */
365 *result = (memcmp(matrix->matrix, matrix2->matrix, sizeof(GpMatrix)) == 0);
366
367 return Ok;
368 }
369
370 GpStatus WINGDIPAPI GdipIsMatrixIdentity(GDIPCONST GpMatrix *matrix, BOOL *result)
371 {
372 GpMatrix *e;
373 GpStatus ret;
374 BOOL isIdentity;
375
376 if(!matrix || !result)
377 return InvalidParameter;
378
379 ret = GdipCreateMatrix(&e);
380 if(ret != Ok) return ret;
381
382 ret = GdipIsMatrixEqual(matrix, e, &isIdentity);
383 if(ret == Ok)
384 *result = isIdentity;
385
386 GdipFree(e);
387
388 return ret;
389 }