/* * Copyright (C) 2007 Google (Evan Stade) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include "windef.h" #include "winbase.h" #include "wingdi.h" #include "objbase.h" #include "gdiplus.h" #include "gdiplus_private.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(gdiplus); /* Multiplies two matrices of the form * * idx:0 idx:1 0 * idx:2 idx:3 0 * idx:4 idx:5 1 * * and puts the output in out. * */ static void matrix_multiply(GDIPCONST REAL * left, GDIPCONST REAL * right, REAL * out) { REAL temp[6]; int i, odd; for(i = 0; i < 6; i++){ odd = i % 2; temp[i] = left[i - odd] * right[odd] + left[i - odd + 1] * right[odd + 2] + (i >= 4 ? right[odd + 4] : 0.0); } memcpy(out, temp, 6 * sizeof(REAL)); } static REAL matrix_det(GDIPCONST GpMatrix *matrix) { return matrix->matrix[0]*matrix->matrix[3] - matrix->matrix[1]*matrix->matrix[2]; } GpStatus WINGDIPAPI GdipCreateMatrix2(REAL m11, REAL m12, REAL m21, REAL m22, REAL dx, REAL dy, GpMatrix **matrix) { TRACE("(%.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %p)\n", m11, m12, m21, m22, dx, dy, matrix); if(!matrix) return InvalidParameter; *matrix = heap_alloc_zero(sizeof(GpMatrix)); if(!*matrix) return OutOfMemory; /* first row */ (*matrix)->matrix[0] = m11; (*matrix)->matrix[1] = m12; /* second row */ (*matrix)->matrix[2] = m21; (*matrix)->matrix[3] = m22; /* third row */ (*matrix)->matrix[4] = dx; (*matrix)->matrix[5] = dy; return Ok; } GpStatus WINGDIPAPI GdipCreateMatrix3(GDIPCONST GpRectF *rect, GDIPCONST GpPointF *pt, GpMatrix **matrix) { REAL m11, m12, m21, m22, dx, dy; TRACE("(%s, %p, %p)\n", debugstr_rectf(rect), pt, matrix); if(!matrix || !pt) return InvalidParameter; m11 = (pt[1].X - pt[0].X) / rect->Width; m21 = (pt[2].X - pt[0].X) / rect->Height; dx = pt[0].X - m11 * rect->X - m21 * rect->Y; m12 = (pt[1].Y - pt[0].Y) / rect->Width; m22 = (pt[2].Y - pt[0].Y) / rect->Height; dy = pt[0].Y - m12 * rect->X - m22 * rect->Y; return GdipCreateMatrix2(m11, m12, m21, m22, dx, dy, matrix); } GpStatus WINGDIPAPI GdipCreateMatrix3I(GDIPCONST GpRect *rect, GDIPCONST GpPoint *pt, GpMatrix **matrix) { GpRectF rectF; GpPointF ptF[3]; int i; TRACE("(%p, %p, %p)\n", rect, pt, matrix); rectF.X = (REAL)rect->X; rectF.Y = (REAL)rect->Y; rectF.Width = (REAL)rect->Width; rectF.Height = (REAL)rect->Height; for (i = 0; i < 3; i++) { ptF[i].X = (REAL)pt[i].X; ptF[i].Y = (REAL)pt[i].Y; } return GdipCreateMatrix3(&rectF, ptF, matrix); } GpStatus WINGDIPAPI GdipCloneMatrix(GpMatrix *matrix, GpMatrix **clone) { TRACE("(%p, %p)\n", matrix, clone); if(!matrix || !clone) return InvalidParameter; *clone = heap_alloc_zero(sizeof(GpMatrix)); if(!*clone) return OutOfMemory; **clone = *matrix; return Ok; } GpStatus WINGDIPAPI GdipCreateMatrix(GpMatrix **matrix) { TRACE("(%p)\n", matrix); if(!matrix) return InvalidParameter; *matrix = heap_alloc_zero(sizeof(GpMatrix)); if(!*matrix) return OutOfMemory; (*matrix)->matrix[0] = 1.0; (*matrix)->matrix[1] = 0.0; (*matrix)->matrix[2] = 0.0; (*matrix)->matrix[3] = 1.0; (*matrix)->matrix[4] = 0.0; (*matrix)->matrix[5] = 0.0; return Ok; } GpStatus WINGDIPAPI GdipDeleteMatrix(GpMatrix *matrix) { TRACE("(%p)\n", matrix); if(!matrix) return InvalidParameter; heap_free(matrix); return Ok; } GpStatus WINGDIPAPI GdipGetMatrixElements(GDIPCONST GpMatrix *matrix, REAL *out) { TRACE("(%p, %p)\n", matrix, out); if(!matrix || !out) return InvalidParameter; memcpy(out, matrix->matrix, sizeof(matrix->matrix)); return Ok; } GpStatus WINGDIPAPI GdipInvertMatrix(GpMatrix *matrix) { GpMatrix copy; REAL det; BOOL invertible; TRACE("(%p)\n", matrix); if(!matrix) return InvalidParameter; GdipIsMatrixInvertible(matrix, &invertible); if(!invertible) return InvalidParameter; /* optimize inverting simple scaling and translation matrices */ if(matrix->matrix[1] == 0 && matrix->matrix[2] == 0) { matrix->matrix[4] = -matrix->matrix[4] / matrix->matrix[0]; matrix->matrix[5] = -matrix->matrix[5] / matrix->matrix[3]; matrix->matrix[0] = 1 / matrix->matrix[0]; matrix->matrix[3] = 1 / matrix->matrix[3]; return Ok; } det = matrix_det(matrix); copy = *matrix; /* store result */ matrix->matrix[0] = copy.matrix[3] / det; matrix->matrix[1] = -copy.matrix[1] / det; matrix->matrix[2] = -copy.matrix[2] / det; matrix->matrix[3] = copy.matrix[0] / det; matrix->matrix[4] = (copy.matrix[2]*copy.matrix[5]-copy.matrix[3]*copy.matrix[4]) / det; matrix->matrix[5] = -(copy.matrix[0]*copy.matrix[5]-copy.matrix[1]*copy.matrix[4]) / det; return Ok; } GpStatus WINGDIPAPI GdipIsMatrixInvertible(GDIPCONST GpMatrix *matrix, BOOL *result) { TRACE("(%p, %p)\n", matrix, result); if(!matrix || !result) return InvalidParameter; if(matrix->matrix[1] == 0 && matrix->matrix[2] == 0) *result = matrix->matrix[0] != 0 && matrix->matrix[3] != 0; else *result = (fabs(matrix_det(matrix)) >= 1e-5); return Ok; } GpStatus WINGDIPAPI GdipMultiplyMatrix(GpMatrix *matrix, GDIPCONST GpMatrix* matrix2, GpMatrixOrder order) { TRACE("(%p, %p, %d)\n", matrix, matrix2, order); if(!matrix || !matrix2) return InvalidParameter; if(order == MatrixOrderAppend) matrix_multiply(matrix->matrix, matrix2->matrix, matrix->matrix); else if (order == MatrixOrderPrepend) matrix_multiply(matrix2->matrix, matrix->matrix, matrix->matrix); else return InvalidParameter; return Ok; } GpStatus WINGDIPAPI GdipRotateMatrix(GpMatrix *matrix, REAL angle, GpMatrixOrder order) { REAL cos_theta, sin_theta, rotate[6]; TRACE("(%p, %.2f, %d)\n", matrix, angle, order); if(!matrix) return InvalidParameter; angle = deg2rad(angle); cos_theta = cos(angle); sin_theta = sin(angle); rotate[0] = cos_theta; rotate[1] = sin_theta; rotate[2] = -sin_theta; rotate[3] = cos_theta; rotate[4] = 0.0; rotate[5] = 0.0; if(order == MatrixOrderAppend) matrix_multiply(matrix->matrix, rotate, matrix->matrix); else if (order == MatrixOrderPrepend) matrix_multiply(rotate, matrix->matrix, matrix->matrix); else return InvalidParameter; return Ok; } GpStatus WINGDIPAPI GdipScaleMatrix(GpMatrix *matrix, REAL scaleX, REAL scaleY, GpMatrixOrder order) { REAL scale[6]; TRACE("(%p, %.2f, %.2f, %d)\n", matrix, scaleX, scaleY, order); if(!matrix) return InvalidParameter; scale[0] = scaleX; scale[1] = 0.0; scale[2] = 0.0; scale[3] = scaleY; scale[4] = 0.0; scale[5] = 0.0; if(order == MatrixOrderAppend) matrix_multiply(matrix->matrix, scale, matrix->matrix); else if (order == MatrixOrderPrepend) matrix_multiply(scale, matrix->matrix, matrix->matrix); else return InvalidParameter; return Ok; } GpStatus WINGDIPAPI GdipSetMatrixElements(GpMatrix *matrix, REAL m11, REAL m12, REAL m21, REAL m22, REAL dx, REAL dy) { TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", matrix, m11, m12, m21, m22, dx, dy); if(!matrix) return InvalidParameter; matrix->matrix[0] = m11; matrix->matrix[1] = m12; matrix->matrix[2] = m21; matrix->matrix[3] = m22; matrix->matrix[4] = dx; matrix->matrix[5] = dy; return Ok; } GpStatus WINGDIPAPI GdipShearMatrix(GpMatrix *matrix, REAL shearX, REAL shearY, GpMatrixOrder order) { REAL shear[6]; TRACE("(%p, %.2f, %.2f, %d)\n", matrix, shearX, shearY, order); if(!matrix) return InvalidParameter; /* prepare transformation matrix */ shear[0] = 1.0; shear[1] = shearY; shear[2] = shearX; shear[3] = 1.0; shear[4] = 0.0; shear[5] = 0.0; if(order == MatrixOrderAppend) matrix_multiply(matrix->matrix, shear, matrix->matrix); else if (order == MatrixOrderPrepend) matrix_multiply(shear, matrix->matrix, matrix->matrix); else return InvalidParameter; return Ok; } GpStatus WINGDIPAPI GdipTransformMatrixPoints(GpMatrix *matrix, GpPointF *pts, INT count) { REAL x, y; INT i; TRACE("(%p, %p, %d)\n", matrix, pts, count); if(!matrix || !pts || count <= 0) return InvalidParameter; for(i = 0; i < count; i++) { x = pts[i].X; y = pts[i].Y; pts[i].X = x * matrix->matrix[0] + y * matrix->matrix[2] + matrix->matrix[4]; pts[i].Y = x * matrix->matrix[1] + y * matrix->matrix[3] + matrix->matrix[5]; } return Ok; } GpStatus WINGDIPAPI GdipTransformMatrixPointsI(GpMatrix *matrix, GpPoint *pts, INT count) { GpPointF *ptsF; GpStatus ret; INT i; TRACE("(%p, %p, %d)\n", matrix, pts, count); if(count <= 0) return InvalidParameter; ptsF = heap_alloc_zero(sizeof(GpPointF) * count); if(!ptsF) return OutOfMemory; for(i = 0; i < count; i++){ ptsF[i].X = (REAL)pts[i].X; ptsF[i].Y = (REAL)pts[i].Y; } ret = GdipTransformMatrixPoints(matrix, ptsF, count); if(ret == Ok) for(i = 0; i < count; i++){ pts[i].X = gdip_round(ptsF[i].X); pts[i].Y = gdip_round(ptsF[i].Y); } heap_free(ptsF); return ret; } GpStatus WINGDIPAPI GdipTranslateMatrix(GpMatrix *matrix, REAL offsetX, REAL offsetY, GpMatrixOrder order) { REAL translate[6]; TRACE("(%p, %.2f, %.2f, %d)\n", matrix, offsetX, offsetY, order); if(!matrix) return InvalidParameter; translate[0] = 1.0; translate[1] = 0.0; translate[2] = 0.0; translate[3] = 1.0; translate[4] = offsetX; translate[5] = offsetY; if(order == MatrixOrderAppend) matrix_multiply(matrix->matrix, translate, matrix->matrix); else if (order == MatrixOrderPrepend) matrix_multiply(translate, matrix->matrix, matrix->matrix); else return InvalidParameter; return Ok; } GpStatus WINGDIPAPI GdipVectorTransformMatrixPoints(GpMatrix *matrix, GpPointF *pts, INT count) { REAL x, y; INT i; TRACE("(%p, %p, %d)\n", matrix, pts, count); if(!matrix || !pts || count <= 0) return InvalidParameter; for(i = 0; i < count; i++) { x = pts[i].X; y = pts[i].Y; pts[i].X = x * matrix->matrix[0] + y * matrix->matrix[2]; pts[i].Y = x * matrix->matrix[1] + y * matrix->matrix[3]; } return Ok; } GpStatus WINGDIPAPI GdipVectorTransformMatrixPointsI(GpMatrix *matrix, GpPoint *pts, INT count) { GpPointF *ptsF; GpStatus ret; INT i; TRACE("(%p, %p, %d)\n", matrix, pts, count); if(count <= 0) return InvalidParameter; ptsF = heap_alloc_zero(sizeof(GpPointF) * count); if(!ptsF) return OutOfMemory; for(i = 0; i < count; i++){ ptsF[i].X = (REAL)pts[i].X; ptsF[i].Y = (REAL)pts[i].Y; } ret = GdipVectorTransformMatrixPoints(matrix, ptsF, count); /* store back */ if(ret == Ok) for(i = 0; i < count; i++){ pts[i].X = gdip_round(ptsF[i].X); pts[i].Y = gdip_round(ptsF[i].Y); } heap_free(ptsF); return ret; } GpStatus WINGDIPAPI GdipIsMatrixEqual(GDIPCONST GpMatrix *matrix, GDIPCONST GpMatrix *matrix2, BOOL *result) { TRACE("(%p, %p, %p)\n", matrix, matrix2, result); if(!matrix || !matrix2 || !result) return InvalidParameter; /* based on single array member of GpMatrix */ *result = (memcmp(matrix->matrix, matrix2->matrix, sizeof(GpMatrix)) == 0); return Ok; } GpStatus WINGDIPAPI GdipIsMatrixIdentity(GDIPCONST GpMatrix *matrix, BOOL *result) { static const GpMatrix identity = { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 } }; TRACE("(%p, %p)\n", matrix, result); if(!matrix || !result) return InvalidParameter; return GdipIsMatrixEqual(matrix, &identity, result); }