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