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