[GDIPLUS] Sync with Wine Staging 2.16. CORE-13762
[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 "gdiplus_private.h"
20
21 /* Multiplies two matrices of the form
22 *
23 * idx:0 idx:1 0
24 * idx:2 idx:3 0
25 * idx:4 idx:5 1
26 *
27 * and puts the output in out.
28 * */
29 static void matrix_multiply(GDIPCONST REAL * left, GDIPCONST REAL * right, REAL * out)
30 {
31 REAL temp[6];
32 int i, odd;
33
34 for(i = 0; i < 6; i++){
35 odd = i % 2;
36 temp[i] = left[i - odd] * right[odd] + left[i - odd + 1] * right[odd + 2] +
37 (i >= 4 ? right[odd + 4] : 0.0);
38 }
39
40 memcpy(out, temp, 6 * sizeof(REAL));
41 }
42
43 static REAL matrix_det(GDIPCONST GpMatrix *matrix)
44 {
45 return matrix->matrix[0]*matrix->matrix[3] - matrix->matrix[1]*matrix->matrix[2];
46 }
47
48 GpStatus WINGDIPAPI GdipCreateMatrix2(REAL m11, REAL m12, REAL m21, REAL m22,
49 REAL dx, REAL dy, GpMatrix **matrix)
50 {
51 TRACE("(%.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %p)\n", m11, m12, m21, m22, dx, dy, matrix);
52
53 if(!matrix)
54 return InvalidParameter;
55
56 *matrix = heap_alloc_zero(sizeof(GpMatrix));
57 if(!*matrix) return OutOfMemory;
58
59 /* first row */
60 (*matrix)->matrix[0] = m11;
61 (*matrix)->matrix[1] = m12;
62 /* second row */
63 (*matrix)->matrix[2] = m21;
64 (*matrix)->matrix[3] = m22;
65 /* third row */
66 (*matrix)->matrix[4] = dx;
67 (*matrix)->matrix[5] = dy;
68
69 return Ok;
70 }
71
72 GpStatus WINGDIPAPI GdipCreateMatrix3(GDIPCONST GpRectF *rect,
73 GDIPCONST GpPointF *pt, GpMatrix **matrix)
74 {
75 REAL m11, m12, m21, m22, dx, dy;
76 TRACE("(%p, %p, %p)\n", rect, pt, matrix);
77
78 if(!matrix || !pt)
79 return InvalidParameter;
80
81 m11 = (pt[1].X - pt[0].X) / rect->Width;
82 m21 = (pt[2].X - pt[0].X) / rect->Height;
83 dx = pt[0].X - m11 * rect->X - m21 * rect->Y;
84 m12 = (pt[1].Y - pt[0].Y) / rect->Width;
85 m22 = (pt[2].Y - pt[0].Y) / rect->Height;
86 dy = pt[0].Y - m12 * rect->X - m22 * rect->Y;
87
88 return GdipCreateMatrix2(m11, m12, m21, m22, dx, dy, matrix);
89 }
90
91 GpStatus WINGDIPAPI GdipCreateMatrix3I(GDIPCONST GpRect *rect, GDIPCONST GpPoint *pt,
92 GpMatrix **matrix)
93 {
94 GpRectF rectF;
95 GpPointF ptF[3];
96 int i;
97
98 TRACE("(%p, %p, %p)\n", rect, pt, matrix);
99
100 rectF.X = (REAL)rect->X;
101 rectF.Y = (REAL)rect->Y;
102 rectF.Width = (REAL)rect->Width;
103 rectF.Height = (REAL)rect->Height;
104
105 for (i = 0; i < 3; i++) {
106 ptF[i].X = (REAL)pt[i].X;
107 ptF[i].Y = (REAL)pt[i].Y;
108 }
109 return GdipCreateMatrix3(&rectF, ptF, matrix);
110 }
111
112 GpStatus WINGDIPAPI GdipCloneMatrix(GpMatrix *matrix, GpMatrix **clone)
113 {
114 TRACE("(%p, %p)\n", matrix, clone);
115
116 if(!matrix || !clone)
117 return InvalidParameter;
118
119 *clone = heap_alloc_zero(sizeof(GpMatrix));
120 if(!*clone) return OutOfMemory;
121
122 **clone = *matrix;
123
124 return Ok;
125 }
126
127 GpStatus WINGDIPAPI GdipCreateMatrix(GpMatrix **matrix)
128 {
129 TRACE("(%p)\n", matrix);
130
131 if(!matrix)
132 return InvalidParameter;
133
134 *matrix = heap_alloc_zero(sizeof(GpMatrix));
135 if(!*matrix) return OutOfMemory;
136
137 (*matrix)->matrix[0] = 1.0;
138 (*matrix)->matrix[1] = 0.0;
139 (*matrix)->matrix[2] = 0.0;
140 (*matrix)->matrix[3] = 1.0;
141 (*matrix)->matrix[4] = 0.0;
142 (*matrix)->matrix[5] = 0.0;
143
144 return Ok;
145 }
146
147 GpStatus WINGDIPAPI GdipDeleteMatrix(GpMatrix *matrix)
148 {
149 TRACE("(%p)\n", matrix);
150
151 if(!matrix)
152 return InvalidParameter;
153
154 heap_free(matrix);
155
156 return Ok;
157 }
158
159 GpStatus WINGDIPAPI GdipGetMatrixElements(GDIPCONST GpMatrix *matrix,
160 REAL *out)
161 {
162 TRACE("(%p, %p)\n", matrix, out);
163
164 if(!matrix || !out)
165 return InvalidParameter;
166
167 memcpy(out, matrix->matrix, sizeof(matrix->matrix));
168
169 return Ok;
170 }
171
172 GpStatus WINGDIPAPI GdipInvertMatrix(GpMatrix *matrix)
173 {
174 GpMatrix copy;
175 REAL det;
176 BOOL invertible;
177
178 TRACE("(%p)\n", matrix);
179
180 if(!matrix)
181 return InvalidParameter;
182
183 GdipIsMatrixInvertible(matrix, &invertible);
184 if(!invertible)
185 return InvalidParameter;
186
187 /* optimize inverting simple scaling and translation matrices */
188 if(matrix->matrix[1] == 0 && matrix->matrix[2] == 0)
189 {
190 matrix->matrix[4] = -matrix->matrix[4] / matrix->matrix[0];
191 matrix->matrix[5] = -matrix->matrix[5] / matrix->matrix[3];
192 matrix->matrix[0] = 1 / matrix->matrix[0];
193 matrix->matrix[3] = 1 / matrix->matrix[3];
194
195 return Ok;
196 }
197
198 det = matrix_det(matrix);
199
200 copy = *matrix;
201 /* store result */
202 matrix->matrix[0] = copy.matrix[3] / det;
203 matrix->matrix[1] = -copy.matrix[1] / det;
204 matrix->matrix[2] = -copy.matrix[2] / det;
205 matrix->matrix[3] = copy.matrix[0] / det;
206 matrix->matrix[4] = (copy.matrix[2]*copy.matrix[5]-copy.matrix[3]*copy.matrix[4]) / det;
207 matrix->matrix[5] = -(copy.matrix[0]*copy.matrix[5]-copy.matrix[1]*copy.matrix[4]) / det;
208
209 return Ok;
210 }
211
212 GpStatus WINGDIPAPI GdipIsMatrixInvertible(GDIPCONST GpMatrix *matrix, BOOL *result)
213 {
214 TRACE("(%p, %p)\n", matrix, result);
215
216 if(!matrix || !result)
217 return InvalidParameter;
218
219 if(matrix->matrix[1] == 0 && matrix->matrix[2] == 0)
220 *result = matrix->matrix[0] != 0 && matrix->matrix[3] != 0;
221 else
222 *result = (fabs(matrix_det(matrix)) >= 1e-5);
223
224 return Ok;
225 }
226
227 GpStatus WINGDIPAPI GdipMultiplyMatrix(GpMatrix *matrix, GDIPCONST GpMatrix* matrix2,
228 GpMatrixOrder order)
229 {
230 TRACE("(%p, %p, %d)\n", matrix, matrix2, order);
231
232 if(!matrix || !matrix2)
233 return InvalidParameter;
234
235 if(order == MatrixOrderAppend)
236 matrix_multiply(matrix->matrix, matrix2->matrix, matrix->matrix);
237 else if (order == MatrixOrderPrepend)
238 matrix_multiply(matrix2->matrix, matrix->matrix, matrix->matrix);
239 else
240 return InvalidParameter;
241
242 return Ok;
243 }
244
245 GpStatus WINGDIPAPI GdipRotateMatrix(GpMatrix *matrix, REAL angle,
246 GpMatrixOrder order)
247 {
248 REAL cos_theta, sin_theta, rotate[6];
249
250 TRACE("(%p, %.2f, %d)\n", matrix, angle, order);
251
252 if(!matrix)
253 return InvalidParameter;
254
255 angle = deg2rad(angle);
256 cos_theta = cos(angle);
257 sin_theta = sin(angle);
258
259 rotate[0] = cos_theta;
260 rotate[1] = sin_theta;
261 rotate[2] = -sin_theta;
262 rotate[3] = cos_theta;
263 rotate[4] = 0.0;
264 rotate[5] = 0.0;
265
266 if(order == MatrixOrderAppend)
267 matrix_multiply(matrix->matrix, rotate, matrix->matrix);
268 else if (order == MatrixOrderPrepend)
269 matrix_multiply(rotate, matrix->matrix, matrix->matrix);
270 else
271 return InvalidParameter;
272
273 return Ok;
274 }
275
276 GpStatus WINGDIPAPI GdipScaleMatrix(GpMatrix *matrix, REAL scaleX, REAL scaleY,
277 GpMatrixOrder order)
278 {
279 REAL scale[6];
280
281 TRACE("(%p, %.2f, %.2f, %d)\n", matrix, scaleX, scaleY, order);
282
283 if(!matrix)
284 return InvalidParameter;
285
286 scale[0] = scaleX;
287 scale[1] = 0.0;
288 scale[2] = 0.0;
289 scale[3] = scaleY;
290 scale[4] = 0.0;
291 scale[5] = 0.0;
292
293 if(order == MatrixOrderAppend)
294 matrix_multiply(matrix->matrix, scale, matrix->matrix);
295 else if (order == MatrixOrderPrepend)
296 matrix_multiply(scale, matrix->matrix, matrix->matrix);
297 else
298 return InvalidParameter;
299
300 return Ok;
301 }
302
303 GpStatus WINGDIPAPI GdipSetMatrixElements(GpMatrix *matrix, REAL m11, REAL m12,
304 REAL m21, REAL m22, REAL dx, REAL dy)
305 {
306 TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", matrix, m11, m12,
307 m21, m22, dx, dy);
308
309 if(!matrix)
310 return InvalidParameter;
311
312 matrix->matrix[0] = m11;
313 matrix->matrix[1] = m12;
314 matrix->matrix[2] = m21;
315 matrix->matrix[3] = m22;
316 matrix->matrix[4] = dx;
317 matrix->matrix[5] = dy;
318
319 return Ok;
320 }
321
322 GpStatus WINGDIPAPI GdipShearMatrix(GpMatrix *matrix, REAL shearX, REAL shearY,
323 GpMatrixOrder order)
324 {
325 REAL shear[6];
326
327 TRACE("(%p, %.2f, %.2f, %d)\n", matrix, shearX, shearY, order);
328
329 if(!matrix)
330 return InvalidParameter;
331
332 /* prepare transformation matrix */
333 shear[0] = 1.0;
334 shear[1] = shearY;
335 shear[2] = shearX;
336 shear[3] = 1.0;
337 shear[4] = 0.0;
338 shear[5] = 0.0;
339
340 if(order == MatrixOrderAppend)
341 matrix_multiply(matrix->matrix, shear, matrix->matrix);
342 else if (order == MatrixOrderPrepend)
343 matrix_multiply(shear, matrix->matrix, matrix->matrix);
344 else
345 return InvalidParameter;
346
347 return Ok;
348 }
349
350 GpStatus WINGDIPAPI GdipTransformMatrixPoints(GpMatrix *matrix, GpPointF *pts,
351 INT count)
352 {
353 REAL x, y;
354 INT i;
355
356 TRACE("(%p, %p, %d)\n", matrix, pts, count);
357
358 if(!matrix || !pts || count <= 0)
359 return InvalidParameter;
360
361 for(i = 0; i < count; i++)
362 {
363 x = pts[i].X;
364 y = pts[i].Y;
365
366 pts[i].X = x * matrix->matrix[0] + y * matrix->matrix[2] + matrix->matrix[4];
367 pts[i].Y = x * matrix->matrix[1] + y * matrix->matrix[3] + matrix->matrix[5];
368 }
369
370 return Ok;
371 }
372
373 GpStatus WINGDIPAPI GdipTransformMatrixPointsI(GpMatrix *matrix, GpPoint *pts, INT count)
374 {
375 GpPointF *ptsF;
376 GpStatus ret;
377 INT i;
378
379 TRACE("(%p, %p, %d)\n", matrix, pts, count);
380
381 if(count <= 0)
382 return InvalidParameter;
383
384 ptsF = heap_alloc_zero(sizeof(GpPointF) * count);
385 if(!ptsF)
386 return OutOfMemory;
387
388 for(i = 0; i < count; i++){
389 ptsF[i].X = (REAL)pts[i].X;
390 ptsF[i].Y = (REAL)pts[i].Y;
391 }
392
393 ret = GdipTransformMatrixPoints(matrix, ptsF, count);
394
395 if(ret == Ok)
396 for(i = 0; i < count; i++){
397 pts[i].X = gdip_round(ptsF[i].X);
398 pts[i].Y = gdip_round(ptsF[i].Y);
399 }
400 heap_free(ptsF);
401
402 return ret;
403 }
404
405 GpStatus WINGDIPAPI GdipTranslateMatrix(GpMatrix *matrix, REAL offsetX,
406 REAL offsetY, GpMatrixOrder order)
407 {
408 REAL translate[6];
409
410 TRACE("(%p, %.2f, %.2f, %d)\n", matrix, offsetX, offsetY, order);
411
412 if(!matrix)
413 return InvalidParameter;
414
415 translate[0] = 1.0;
416 translate[1] = 0.0;
417 translate[2] = 0.0;
418 translate[3] = 1.0;
419 translate[4] = offsetX;
420 translate[5] = offsetY;
421
422 if(order == MatrixOrderAppend)
423 matrix_multiply(matrix->matrix, translate, matrix->matrix);
424 else if (order == MatrixOrderPrepend)
425 matrix_multiply(translate, matrix->matrix, matrix->matrix);
426 else
427 return InvalidParameter;
428
429 return Ok;
430 }
431
432 GpStatus WINGDIPAPI GdipVectorTransformMatrixPoints(GpMatrix *matrix, GpPointF *pts, INT count)
433 {
434 REAL x, y;
435 INT i;
436
437 TRACE("(%p, %p, %d)\n", matrix, pts, count);
438
439 if(!matrix || !pts || count <= 0)
440 return InvalidParameter;
441
442 for(i = 0; i < count; i++)
443 {
444 x = pts[i].X;
445 y = pts[i].Y;
446
447 pts[i].X = x * matrix->matrix[0] + y * matrix->matrix[2];
448 pts[i].Y = x * matrix->matrix[1] + y * matrix->matrix[3];
449 }
450
451 return Ok;
452 }
453
454 GpStatus WINGDIPAPI GdipVectorTransformMatrixPointsI(GpMatrix *matrix, GpPoint *pts, INT count)
455 {
456 GpPointF *ptsF;
457 GpStatus ret;
458 INT i;
459
460 TRACE("(%p, %p, %d)\n", matrix, pts, count);
461
462 if(count <= 0)
463 return InvalidParameter;
464
465 ptsF = heap_alloc_zero(sizeof(GpPointF) * count);
466 if(!ptsF)
467 return OutOfMemory;
468
469 for(i = 0; i < count; i++){
470 ptsF[i].X = (REAL)pts[i].X;
471 ptsF[i].Y = (REAL)pts[i].Y;
472 }
473
474 ret = GdipVectorTransformMatrixPoints(matrix, ptsF, count);
475 /* store back */
476 if(ret == Ok)
477 for(i = 0; i < count; i++){
478 pts[i].X = gdip_round(ptsF[i].X);
479 pts[i].Y = gdip_round(ptsF[i].Y);
480 }
481 heap_free(ptsF);
482
483 return ret;
484 }
485
486 GpStatus WINGDIPAPI GdipIsMatrixEqual(GDIPCONST GpMatrix *matrix, GDIPCONST GpMatrix *matrix2,
487 BOOL *result)
488 {
489 TRACE("(%p, %p, %p)\n", matrix, matrix2, result);
490
491 if(!matrix || !matrix2 || !result)
492 return InvalidParameter;
493 /* based on single array member of GpMatrix */
494 *result = (memcmp(matrix->matrix, matrix2->matrix, sizeof(GpMatrix)) == 0);
495
496 return Ok;
497 }
498
499 GpStatus WINGDIPAPI GdipIsMatrixIdentity(GDIPCONST GpMatrix *matrix, BOOL *result)
500 {
501 static const GpMatrix identity =
502 {
503 { 1.0, 0.0,
504 0.0, 1.0,
505 0.0, 0.0 }
506 };
507
508 TRACE("(%p, %p)\n", matrix, result);
509
510 if(!matrix || !result)
511 return InvalidParameter;
512
513 return GdipIsMatrixEqual(matrix, &identity, result);
514 }