michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef MOZILLA_GFX_MATRIX_H_ michael@0: #define MOZILLA_GFX_MATRIX_H_ michael@0: michael@0: #include "Types.h" michael@0: #include "Rect.h" michael@0: #include "Point.h" michael@0: #include michael@0: michael@0: namespace mozilla { michael@0: namespace gfx { michael@0: michael@0: class Matrix michael@0: { michael@0: public: michael@0: Matrix() michael@0: : _11(1.0f), _12(0) michael@0: , _21(0), _22(1.0f) michael@0: , _31(0), _32(0) michael@0: {} michael@0: Matrix(Float a11, Float a12, Float a21, Float a22, Float a31, Float a32) michael@0: : _11(a11), _12(a12) michael@0: , _21(a21), _22(a22) michael@0: , _31(a31), _32(a32) michael@0: {} michael@0: Float _11, _12; michael@0: Float _21, _22; michael@0: Float _31, _32; michael@0: michael@0: Point operator *(const Point &aPoint) const michael@0: { michael@0: Point retPoint; michael@0: michael@0: retPoint.x = aPoint.x * _11 + aPoint.y * _21 + _31; michael@0: retPoint.y = aPoint.x * _12 + aPoint.y * _22 + _32; michael@0: michael@0: return retPoint; michael@0: } michael@0: michael@0: Size operator *(const Size &aSize) const michael@0: { michael@0: Size retSize; michael@0: michael@0: retSize.width = aSize.width * _11 + aSize.height * _21; michael@0: retSize.height = aSize.width * _12 + aSize.height * _22; michael@0: michael@0: return retSize; michael@0: } michael@0: michael@0: GFX2D_API Rect TransformBounds(const Rect& rect) const; michael@0: michael@0: // Apply a scale to this matrix. This scale will be applied -before- the michael@0: // existing transformation of the matrix. michael@0: Matrix &Scale(Float aX, Float aY) michael@0: { michael@0: _11 *= aX; michael@0: _12 *= aX; michael@0: _21 *= aY; michael@0: _22 *= aY; michael@0: michael@0: return *this; michael@0: } michael@0: michael@0: Matrix &Translate(Float aX, Float aY) michael@0: { michael@0: _31 += _11 * aX + _21 * aY; michael@0: _32 += _12 * aX + _22 * aY; michael@0: michael@0: return *this; michael@0: } michael@0: michael@0: Matrix &PostTranslate(Float aX, Float aY) michael@0: { michael@0: _31 += aX; michael@0: _32 += aY; michael@0: return *this; michael@0: } michael@0: michael@0: Matrix &Rotate(Float aAngle) michael@0: { michael@0: return *this = Matrix::Rotation(aAngle) * *this; michael@0: } michael@0: michael@0: bool Invert() michael@0: { michael@0: // Compute co-factors. michael@0: Float A = _22; michael@0: Float B = -_21; michael@0: Float C = _21 * _32 - _22 * _31; michael@0: Float D = -_12; michael@0: Float E = _11; michael@0: Float F = _31 * _12 - _11 * _32; michael@0: michael@0: Float det = Determinant(); michael@0: michael@0: if (!det) { michael@0: return false; michael@0: } michael@0: michael@0: Float inv_det = 1 / det; michael@0: michael@0: _11 = inv_det * A; michael@0: _12 = inv_det * D; michael@0: _21 = inv_det * B; michael@0: _22 = inv_det * E; michael@0: _31 = inv_det * C; michael@0: _32 = inv_det * F; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: Float Determinant() const michael@0: { michael@0: return _11 * _22 - _12 * _21; michael@0: } michael@0: michael@0: static Matrix Translation(Float aX, Float aY) michael@0: { michael@0: return Matrix(1.0f, 0.0f, 0.0f, 1.0f, aX, aY); michael@0: } michael@0: michael@0: static Matrix Translation(Point aPoint) michael@0: { michael@0: return Translation(aPoint.x, aPoint.y); michael@0: } michael@0: michael@0: GFX2D_API static Matrix Rotation(Float aAngle); michael@0: michael@0: static Matrix Scaling(Float aX, Float aY) michael@0: { michael@0: return Matrix(aX, 0.0f, 0.0f, aY, 0.0f, 0.0f); michael@0: } michael@0: michael@0: Matrix operator*(const Matrix &aMatrix) const michael@0: { michael@0: Matrix resultMatrix; michael@0: michael@0: resultMatrix._11 = this->_11 * aMatrix._11 + this->_12 * aMatrix._21; michael@0: resultMatrix._12 = this->_11 * aMatrix._12 + this->_12 * aMatrix._22; michael@0: resultMatrix._21 = this->_21 * aMatrix._11 + this->_22 * aMatrix._21; michael@0: resultMatrix._22 = this->_21 * aMatrix._12 + this->_22 * aMatrix._22; michael@0: resultMatrix._31 = this->_31 * aMatrix._11 + this->_32 * aMatrix._21 + aMatrix._31; michael@0: resultMatrix._32 = this->_31 * aMatrix._12 + this->_32 * aMatrix._22 + aMatrix._32; michael@0: michael@0: return resultMatrix; michael@0: } michael@0: michael@0: Matrix& operator*=(const Matrix &aMatrix) michael@0: { michael@0: Matrix resultMatrix = *this * aMatrix; michael@0: return *this = resultMatrix; michael@0: } michael@0: michael@0: /* Returns true if the other matrix is fuzzy-equal to this matrix. michael@0: * Note that this isn't a cheap comparison! michael@0: */ michael@0: bool operator==(const Matrix& other) const michael@0: { michael@0: return FuzzyEqual(_11, other._11) && FuzzyEqual(_12, other._12) && michael@0: FuzzyEqual(_21, other._21) && FuzzyEqual(_22, other._22) && michael@0: FuzzyEqual(_31, other._31) && FuzzyEqual(_32, other._32); michael@0: } michael@0: michael@0: bool operator!=(const Matrix& other) const michael@0: { michael@0: return !(*this == other); michael@0: } michael@0: michael@0: /* Returns true if the matrix is a rectilinear transformation (i.e. michael@0: * grid-aligned rectangles are transformed to grid-aligned rectangles) michael@0: */ michael@0: bool IsRectilinear() const { michael@0: if (FuzzyEqual(_12, 0) && FuzzyEqual(_21, 0)) { michael@0: return true; michael@0: } else if (FuzzyEqual(_22, 0) && FuzzyEqual(_11, 0)) { michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: /** michael@0: * Returns true if the matrix is anything other than a straight michael@0: * translation by integers. michael@0: */ michael@0: bool HasNonIntegerTranslation() const { michael@0: return HasNonTranslation() || michael@0: !FuzzyEqual(_31, floor(_31 + 0.5)) || michael@0: !FuzzyEqual(_32, floor(_32 + 0.5)); michael@0: } michael@0: michael@0: /** michael@0: * Returns true if the matrix has any transform other michael@0: * than a straight translation. michael@0: */ michael@0: bool HasNonTranslation() const { michael@0: return !FuzzyEqual(_11, 1.0) || !FuzzyEqual(_22, 1.0) || michael@0: !FuzzyEqual(_12, 0.0) || !FuzzyEqual(_21, 0.0); michael@0: } michael@0: michael@0: /* Returns true if the matrix is an identity matrix. michael@0: */ michael@0: bool IsIdentity() const michael@0: { michael@0: return _11 == 1.0f && _12 == 0.0f && michael@0: _21 == 0.0f && _22 == 1.0f && michael@0: _31 == 0.0f && _32 == 0.0f; michael@0: } michael@0: michael@0: /* Returns true if the matrix is singular. michael@0: */ michael@0: bool IsSingular() const michael@0: { michael@0: return Determinant() == 0; michael@0: } michael@0: michael@0: GFX2D_API void NudgeToIntegers(); michael@0: michael@0: bool IsTranslation() const michael@0: { michael@0: return FuzzyEqual(_11, 1.0f) && FuzzyEqual(_12, 0.0f) && michael@0: FuzzyEqual(_21, 0.0f) && FuzzyEqual(_22, 1.0f); michael@0: } michael@0: michael@0: bool IsIntegerTranslation() const michael@0: { michael@0: return IsTranslation() && michael@0: FuzzyEqual(_31, floorf(_31 + 0.5f)) && michael@0: FuzzyEqual(_32, floorf(_32 + 0.5f)); michael@0: } michael@0: michael@0: Point GetTranslation() const { michael@0: return Point(_31, _32); michael@0: } michael@0: michael@0: /** michael@0: * Returns true if matrix is multiple of 90 degrees rotation with flipping, michael@0: * scaling and translation. michael@0: */ michael@0: bool PreservesAxisAlignedRectangles() const { michael@0: return ((FuzzyEqual(_11, 0.0) && FuzzyEqual(_22, 0.0)) michael@0: || (FuzzyEqual(_12, 0.0) && FuzzyEqual(_21, 0.0))); michael@0: } michael@0: michael@0: /** michael@0: * Returns true if the matrix has any transform other michael@0: * than a translation or scale; this is, if there is michael@0: * no rotation. michael@0: */ michael@0: bool HasNonAxisAlignedTransform() const { michael@0: return !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0); michael@0: } michael@0: michael@0: /** michael@0: * Returns true if the matrix has non-integer scale michael@0: */ michael@0: bool HasNonIntegerScale() const { michael@0: return !FuzzyEqual(_11, floor(_11 + 0.5)) || michael@0: !FuzzyEqual(_22, floor(_22 + 0.5)); michael@0: } michael@0: michael@0: private: michael@0: static bool FuzzyEqual(Float aV1, Float aV2) { michael@0: // XXX - Check if fabs does the smart thing and just negates the sign bit. michael@0: return fabs(aV2 - aV1) < 1e-6; michael@0: } michael@0: }; michael@0: michael@0: class Matrix4x4 michael@0: { michael@0: public: michael@0: Matrix4x4() michael@0: : _11(1.0f), _12(0.0f), _13(0.0f), _14(0.0f) michael@0: , _21(0.0f), _22(1.0f), _23(0.0f), _24(0.0f) michael@0: , _31(0.0f), _32(0.0f), _33(1.0f), _34(0.0f) michael@0: , _41(0.0f), _42(0.0f), _43(0.0f), _44(1.0f) michael@0: {} michael@0: michael@0: Float _11, _12, _13, _14; michael@0: Float _21, _22, _23, _24; michael@0: Float _31, _32, _33, _34; michael@0: Float _41, _42, _43, _44; michael@0: michael@0: /** michael@0: * Returns true if the matrix is isomorphic to a 2D affine transformation. michael@0: */ michael@0: bool Is2D() const michael@0: { michael@0: if (_13 != 0.0f || _14 != 0.0f || michael@0: _23 != 0.0f || _24 != 0.0f || michael@0: _31 != 0.0f || _32 != 0.0f || _33 != 1.0f || _34 != 0.0f || michael@0: _43 != 0.0f || _44 != 1.0f) { michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool Is2D(Matrix* aMatrix) const { michael@0: if (!Is2D()) { michael@0: return false; michael@0: } michael@0: if (aMatrix) { michael@0: aMatrix->_11 = _11; michael@0: aMatrix->_12 = _12; michael@0: aMatrix->_21 = _21; michael@0: aMatrix->_22 = _22; michael@0: aMatrix->_31 = _41; michael@0: aMatrix->_32 = _42; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: Matrix As2D() const michael@0: { michael@0: MOZ_ASSERT(Is2D(), "Matrix is not a 2D affine transform"); michael@0: michael@0: return Matrix(_11, _12, _21, _22, _41, _42); michael@0: } michael@0: michael@0: bool CanDraw2D(Matrix* aMatrix = nullptr) const { michael@0: if (_14 != 0.0f || michael@0: _24 != 0.0f || michael@0: _44 != 1.0f) { michael@0: return false; michael@0: } michael@0: if (aMatrix) { michael@0: aMatrix->_11 = _11; michael@0: aMatrix->_12 = _12; michael@0: aMatrix->_21 = _21; michael@0: aMatrix->_22 = _22; michael@0: aMatrix->_31 = _41; michael@0: aMatrix->_32 = _42; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: Matrix4x4& ProjectTo2D() { michael@0: _31 = 0.0f; michael@0: _32 = 0.0f; michael@0: _13 = 0.0f; michael@0: _23 = 0.0f; michael@0: _33 = 1.0f; michael@0: _43 = 0.0f; michael@0: _34 = 0.0f; michael@0: return *this; michael@0: } michael@0: michael@0: static Matrix4x4 From2D(const Matrix &aMatrix) { michael@0: Matrix4x4 matrix; michael@0: matrix._11 = aMatrix._11; michael@0: matrix._12 = aMatrix._12; michael@0: matrix._21 = aMatrix._21; michael@0: matrix._22 = aMatrix._22; michael@0: matrix._41 = aMatrix._31; michael@0: matrix._42 = aMatrix._32; michael@0: return matrix; michael@0: } michael@0: michael@0: bool Is2DIntegerTranslation() const michael@0: { michael@0: return Is2D() && As2D().IsIntegerTranslation(); michael@0: } michael@0: michael@0: Point4D operator *(const Point4D& aPoint) const michael@0: { michael@0: Point4D retPoint; michael@0: michael@0: retPoint.x = aPoint.x * _11 + aPoint.y * _21 + aPoint.z * _31 + _41; michael@0: retPoint.y = aPoint.x * _12 + aPoint.y * _22 + aPoint.z * _32 + _42; michael@0: retPoint.z = aPoint.x * _13 + aPoint.y * _23 + aPoint.z * _33 + _43; michael@0: retPoint.w = aPoint.x * _14 + aPoint.y * _24 + aPoint.z * _34 + _44; michael@0: michael@0: return retPoint; michael@0: } michael@0: michael@0: Point3D operator *(const Point3D& aPoint) const michael@0: { michael@0: Point4D temp(aPoint.x, aPoint.y, aPoint.z, 1); michael@0: michael@0: temp = *this * temp; michael@0: temp /= temp.w; michael@0: michael@0: return Point3D(temp.x, temp.y, temp.z); michael@0: } michael@0: michael@0: Point operator *(const Point &aPoint) const michael@0: { michael@0: Point4D temp(aPoint.x, aPoint.y, 0, 1); michael@0: michael@0: temp = *this * temp; michael@0: temp /= temp.w; michael@0: michael@0: return Point(temp.x, temp.y); michael@0: } michael@0: michael@0: GFX2D_API Rect TransformBounds(const Rect& rect) const; michael@0: michael@0: // Apply a scale to this matrix. This scale will be applied -before- the michael@0: // existing transformation of the matrix. michael@0: Matrix4x4 &Scale(Float aX, Float aY, Float aZ) michael@0: { michael@0: _11 *= aX; michael@0: _12 *= aX; michael@0: _13 *= aX; michael@0: _21 *= aY; michael@0: _22 *= aY; michael@0: _23 *= aY; michael@0: _31 *= aZ; michael@0: _32 *= aZ; michael@0: _33 *= aZ; michael@0: michael@0: return *this; michael@0: } michael@0: michael@0: Matrix4x4 &Translate(Float aX, Float aY, Float aZ) michael@0: { michael@0: _41 += aX * _11 + aY * _21 + aZ * _31; michael@0: _42 += aX * _12 + aY * _22 + aZ * _32; michael@0: _43 += aX * _13 + aY * _23 + aZ * _33; michael@0: _44 += aX * _14 + aY * _24 + aZ * _34; michael@0: michael@0: return *this; michael@0: } michael@0: michael@0: bool operator==(const Matrix4x4& o) const michael@0: { michael@0: // XXX would be nice to memcmp here, but that breaks IEEE 754 semantics michael@0: return _11 == o._11 && _12 == o._12 && _13 == o._13 && _14 == o._14 && michael@0: _21 == o._21 && _22 == o._22 && _23 == o._23 && _24 == o._24 && michael@0: _31 == o._31 && _32 == o._32 && _33 == o._33 && _34 == o._34 && michael@0: _41 == o._41 && _42 == o._42 && _43 == o._43 && _44 == o._44; michael@0: } michael@0: michael@0: bool operator!=(const Matrix4x4& o) const michael@0: { michael@0: return !((*this) == o); michael@0: } michael@0: michael@0: Matrix4x4 operator*(const Matrix4x4 &aMatrix) const michael@0: { michael@0: Matrix4x4 matrix; michael@0: michael@0: matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21 + _13 * aMatrix._31 + _14 * aMatrix._41; michael@0: matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21 + _23 * aMatrix._31 + _24 * aMatrix._41; michael@0: matrix._31 = _31 * aMatrix._11 + _32 * aMatrix._21 + _33 * aMatrix._31 + _34 * aMatrix._41; michael@0: matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + _43 * aMatrix._31 + _44 * aMatrix._41; michael@0: matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22 + _13 * aMatrix._32 + _14 * aMatrix._42; michael@0: matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22 + _23 * aMatrix._32 + _24 * aMatrix._42; michael@0: matrix._32 = _31 * aMatrix._12 + _32 * aMatrix._22 + _33 * aMatrix._32 + _34 * aMatrix._42; michael@0: matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + _43 * aMatrix._32 + _44 * aMatrix._42; michael@0: matrix._13 = _11 * aMatrix._13 + _12 * aMatrix._23 + _13 * aMatrix._33 + _14 * aMatrix._43; michael@0: matrix._23 = _21 * aMatrix._13 + _22 * aMatrix._23 + _23 * aMatrix._33 + _24 * aMatrix._43; michael@0: matrix._33 = _31 * aMatrix._13 + _32 * aMatrix._23 + _33 * aMatrix._33 + _34 * aMatrix._43; michael@0: matrix._43 = _41 * aMatrix._13 + _42 * aMatrix._23 + _43 * aMatrix._33 + _44 * aMatrix._43; michael@0: matrix._14 = _11 * aMatrix._14 + _12 * aMatrix._24 + _13 * aMatrix._34 + _14 * aMatrix._44; michael@0: matrix._24 = _21 * aMatrix._14 + _22 * aMatrix._24 + _23 * aMatrix._34 + _24 * aMatrix._44; michael@0: matrix._34 = _31 * aMatrix._14 + _32 * aMatrix._24 + _33 * aMatrix._34 + _34 * aMatrix._44; michael@0: matrix._44 = _41 * aMatrix._14 + _42 * aMatrix._24 + _43 * aMatrix._34 + _44 * aMatrix._44; michael@0: michael@0: return matrix; michael@0: } michael@0: michael@0: michael@0: /* Returns true if the matrix is an identity matrix. michael@0: */ michael@0: bool IsIdentity() const michael@0: { michael@0: return _11 == 1.0f && _12 == 0.0f && _13 == 0.0f && _14 == 0.0f && michael@0: _21 == 0.0f && _22 == 1.0f && _23 == 0.0f && _24 == 0.0f && michael@0: _31 == 0.0f && _32 == 0.0f && _33 == 1.0f && _34 == 0.0f && michael@0: _41 == 0.0f && _42 == 0.0f && _43 == 0.0f && _44 == 1.0f; michael@0: } michael@0: michael@0: bool IsSingular() const michael@0: { michael@0: return Determinant() == 0.0; michael@0: } michael@0: michael@0: Float Determinant() const michael@0: { michael@0: return _14 * _23 * _32 * _41 michael@0: - _13 * _24 * _32 * _41 michael@0: - _14 * _22 * _33 * _41 michael@0: + _12 * _24 * _33 * _41 michael@0: + _13 * _22 * _34 * _41 michael@0: - _12 * _23 * _34 * _41 michael@0: - _14 * _23 * _31 * _42 michael@0: + _13 * _24 * _31 * _42 michael@0: + _14 * _21 * _33 * _42 michael@0: - _11 * _24 * _33 * _42 michael@0: - _13 * _21 * _34 * _42 michael@0: + _11 * _23 * _34 * _42 michael@0: + _14 * _22 * _31 * _43 michael@0: - _12 * _24 * _31 * _43 michael@0: - _14 * _21 * _32 * _43 michael@0: + _11 * _24 * _32 * _43 michael@0: + _12 * _21 * _34 * _43 michael@0: - _11 * _22 * _34 * _43 michael@0: - _13 * _22 * _31 * _44 michael@0: + _12 * _23 * _31 * _44 michael@0: + _13 * _21 * _32 * _44 michael@0: - _11 * _23 * _32 * _44 michael@0: - _12 * _21 * _33 * _44 michael@0: + _11 * _22 * _33 * _44; michael@0: } michael@0: michael@0: }; michael@0: michael@0: class Matrix5x4 michael@0: { michael@0: public: michael@0: Matrix5x4() michael@0: : _11(1.0f), _12(0), _13(0), _14(0) michael@0: , _21(0), _22(1.0f), _23(0), _24(0) michael@0: , _31(0), _32(0), _33(1.0f), _34(0) michael@0: , _41(0), _42(0), _43(0), _44(1.0f) michael@0: , _51(0), _52(0), _53(0), _54(0) michael@0: {} michael@0: Matrix5x4(Float a11, Float a12, Float a13, Float a14, michael@0: Float a21, Float a22, Float a23, Float a24, michael@0: Float a31, Float a32, Float a33, Float a34, michael@0: Float a41, Float a42, Float a43, Float a44, michael@0: Float a51, Float a52, Float a53, Float a54) michael@0: : _11(a11), _12(a12), _13(a13), _14(a14) michael@0: , _21(a21), _22(a22), _23(a23), _24(a24) michael@0: , _31(a31), _32(a32), _33(a33), _34(a34) michael@0: , _41(a41), _42(a42), _43(a43), _44(a44) michael@0: , _51(a51), _52(a52), _53(a53), _54(a54) michael@0: {} michael@0: Float _11, _12, _13, _14; michael@0: Float _21, _22, _23, _24; michael@0: Float _31, _32, _33, _34; michael@0: Float _41, _42, _43, _44; michael@0: Float _51, _52, _53, _54; michael@0: }; michael@0: michael@0: } michael@0: } michael@0: michael@0: #endif /* MOZILLA_GFX_MATRIX_H_ */