From cab684d975836fca671d1dded8c3e31eab597aa3 Mon Sep 17 00:00:00 2001 From: Rik Cabanier Date: Tue, 24 Jun 2014 20:15:00 +0200 Subject: [PATCH] Bug 1018497 - Implementation of DOMMatrix. r=roc,bz --- content/base/src/DOMMatrix.cpp | 650 ++++++++++++++++ content/base/src/DOMMatrix.h | 256 +++++++ content/base/src/moz.build | 2 + dom/bindings/Bindings.conf | 5 + dom/tests/mochitest/general/mochitest.ini | 1 + .../mochitest/general/test_DOMMatrix.html | 714 ++++++++++++++++++ .../mochitest/general/test_interfaces.html | 4 + dom/webidl/DOMMatrix.webidl | 148 ++++ dom/webidl/moz.build | 1 + gfx/2d/Matrix.cpp | 70 ++ gfx/2d/Matrix.h | 4 + modules/libpref/src/init/all.js | 3 + 12 files changed, 1858 insertions(+) create mode 100644 content/base/src/DOMMatrix.cpp create mode 100644 content/base/src/DOMMatrix.h create mode 100644 dom/tests/mochitest/general/test_DOMMatrix.html create mode 100644 dom/webidl/DOMMatrix.webidl diff --git a/content/base/src/DOMMatrix.cpp b/content/base/src/DOMMatrix.cpp new file mode 100644 index 000000000000..8c508a5a72e9 --- /dev/null +++ b/content/base/src/DOMMatrix.cpp @@ -0,0 +1,650 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/DOMMatrixBinding.h" +#include "mozilla/dom/DOMPointBinding.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/ToJSValue.h" + +#include "mozilla/dom/DOMPoint.h" +#include "mozilla/dom/DOMMatrix.h" + +#include "SVGTransformListParser.h" +#include "SVGTransform.h" + +#include "nsAutoPtr.h" +#include + +namespace mozilla { +namespace dom { + +static const double radPerDegree = 2.0 * M_PI / 360.0; + +already_AddRefed +DOMMatrixReadOnly::Translate(double aTx, + double aTy, + double aTz) const +{ + nsRefPtr retval = new DOMMatrix(mParent, *this); + retval->TranslateSelf(aTx, aTy, aTz); + + return retval.forget(); +} + +already_AddRefed +DOMMatrixReadOnly::Scale(double aScale, + double aOriginX, + double aOriginY) const +{ + nsRefPtr retval = new DOMMatrix(mParent, *this); + retval->ScaleSelf(aScale, aOriginX, aOriginY); + + return retval.forget(); +} + +already_AddRefed +DOMMatrixReadOnly::Scale3d(double aScale, + double aOriginX, + double aOriginY, + double aOriginZ) const +{ + nsRefPtr retval = new DOMMatrix(mParent, *this); + retval->Scale3dSelf(aScale, aOriginX, aOriginY, aOriginZ); + + return retval.forget(); +} + +already_AddRefed +DOMMatrixReadOnly::ScaleNonUniform(double aScaleX, + double aScaleY, + double aScaleZ, + double aOriginX, + double aOriginY, + double aOriginZ) const +{ + nsRefPtr retval = new DOMMatrix(mParent, *this); + retval->ScaleNonUniformSelf(aScaleX, aScaleY, aScaleZ, aOriginX, aOriginY, aOriginZ); + + return retval.forget(); +} + +already_AddRefed +DOMMatrixReadOnly::Rotate(double aAngle, + double aOriginX , + double aOriginY) const +{ + nsRefPtr retval = new DOMMatrix(mParent, *this); + retval->RotateSelf(aAngle, aOriginX, aOriginY); + + return retval.forget(); +} + +already_AddRefed +DOMMatrixReadOnly::RotateFromVector(double x, + double y) const +{ + nsRefPtr retval = new DOMMatrix(mParent, *this); + retval->RotateFromVectorSelf(x, y); + + return retval.forget(); +} + +already_AddRefed +DOMMatrixReadOnly::RotateAxisAngle(double aX, + double aY, + double aZ, + double aAngle) const +{ + nsRefPtr retval = new DOMMatrix(mParent, *this); + retval->RotateAxisAngleSelf(aX, aY, aZ, aAngle); + + return retval.forget(); +} + +already_AddRefed +DOMMatrixReadOnly::SkewX(double aSx) const +{ + nsRefPtr retval = new DOMMatrix(mParent, *this); + retval->SkewXSelf(aSx); + + return retval.forget(); +} + +already_AddRefed +DOMMatrixReadOnly::SkewY(double aSy) const +{ + nsRefPtr retval = new DOMMatrix(mParent, *this); + retval->SkewYSelf(aSy); + + return retval.forget(); +} + +already_AddRefed +DOMMatrixReadOnly::Multiply(const DOMMatrix& other) const +{ + nsRefPtr retval = new DOMMatrix(mParent, *this); + retval->MultiplySelf(other); + + return retval.forget(); +} + +already_AddRefed +DOMMatrixReadOnly::FlipX() const +{ + nsRefPtr retval = new DOMMatrix(mParent, *this); + if (mMatrix3D) { + gfx::Matrix4x4 m; + m._11 = -1; + retval->mMatrix3D = new gfx::Matrix4x4(m * *mMatrix3D); + } else { + gfx::Matrix m; + m._11 = -1; + retval->mMatrix2D = new gfx::Matrix(mMatrix2D ? m * *mMatrix2D : m); + } + + return retval.forget(); +} + +already_AddRefed +DOMMatrixReadOnly::FlipY() const +{ + nsRefPtr retval = new DOMMatrix(mParent, *this); + if (mMatrix3D) { + gfx::Matrix4x4 m; + m._22 = -1; + retval->mMatrix3D = new gfx::Matrix4x4(m * *mMatrix3D); + } else { + gfx::Matrix m; + m._22 = -1; + retval->mMatrix2D = new gfx::Matrix(mMatrix2D ? m * *mMatrix2D : m); + } + + return retval.forget(); +} + +already_AddRefed +DOMMatrixReadOnly::Inverse() const +{ + nsRefPtr retval = new DOMMatrix(mParent, *this); + retval->InvertSelf(); + + return retval.forget(); +} + +bool +DOMMatrixReadOnly::Is2D() const +{ + return !mMatrix3D; +} + +bool +DOMMatrixReadOnly::Identity() const +{ + if (mMatrix3D) { + return mMatrix3D->IsIdentity(); + } + + return mMatrix2D->IsIdentity(); +} + +already_AddRefed +DOMMatrixReadOnly::TransformPoint(const DOMPointInit& point) const +{ + nsRefPtr retval = new DOMPoint(mParent); + + if (mMatrix3D) { + gfx::Point4D transformedPoint; + transformedPoint.x = point.mX; + transformedPoint.y = point.mY; + transformedPoint.z = point.mZ; + transformedPoint.w = point.mW; + + transformedPoint = *mMatrix3D * transformedPoint; + + retval->SetX(transformedPoint.x); + retval->SetY(transformedPoint.y); + retval->SetZ(transformedPoint.z); + retval->SetW(transformedPoint.w); + } else if (point.mZ != 0 || point.mW != 1.0) { + gfx::Matrix4x4 tempMatrix(gfx::Matrix4x4::From2D(*mMatrix2D)); + + gfx::Point4D transformedPoint; + transformedPoint.x = point.mX; + transformedPoint.y = point.mY; + transformedPoint.z = point.mZ; + transformedPoint.w = point.mW; + + transformedPoint = tempMatrix * transformedPoint; + + retval->SetX(transformedPoint.x); + retval->SetY(transformedPoint.y); + retval->SetZ(transformedPoint.z); + retval->SetW(transformedPoint.w); + } else { + gfx::Point transformedPoint; + transformedPoint.x = point.mX; + transformedPoint.y = point.mY; + + transformedPoint = *mMatrix2D * transformedPoint; + + retval->SetX(transformedPoint.x); + retval->SetY(transformedPoint.y); + retval->SetZ(point.mZ); + retval->SetW(point.mW); + } + return retval.forget(); +} + +template void GetDataFromMatrix(const DOMMatrixReadOnly* aMatrix, T* aData) +{ + aData[0] = static_cast(aMatrix->M11()); + aData[1] = static_cast(aMatrix->M12()); + aData[2] = static_cast(aMatrix->M13()); + aData[3] = static_cast(aMatrix->M14()); + aData[4] = static_cast(aMatrix->M21()); + aData[5] = static_cast(aMatrix->M22()); + aData[6] = static_cast(aMatrix->M23()); + aData[7] = static_cast(aMatrix->M24()); + aData[8] = static_cast(aMatrix->M31()); + aData[9] = static_cast(aMatrix->M32()); + aData[10] = static_cast(aMatrix->M33()); + aData[11] = static_cast(aMatrix->M34()); + aData[12] = static_cast(aMatrix->M41()); + aData[13] = static_cast(aMatrix->M42()); + aData[14] = static_cast(aMatrix->M43()); + aData[15] = static_cast(aMatrix->M44()); +} + +void +DOMMatrixReadOnly::ToFloat32Array(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) const +{ + nsAutoTArray arr; + arr.SetLength(16); + GetDataFromMatrix(this, arr.Elements()); + JS::Rooted value(aCx); + if (!ToJSValue(aCx, TypedArrayCreator(arr), &value)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + aResult.set(&value.toObject()); +} + +void +DOMMatrixReadOnly::ToFloat64Array(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) const +{ + nsAutoTArray arr; + arr.SetLength(16); + GetDataFromMatrix(this, arr.Elements()); + JS::Rooted value(aCx); + if (!ToJSValue(aCx, TypedArrayCreator(arr), &value)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + aResult.set(&value.toObject()); +} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMMatrix, mParent) + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMMatrix, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMMatrix, Release) + +already_AddRefed +DOMMatrix::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) +{ + nsRefPtr obj = new DOMMatrix(aGlobal.GetAsSupports()); + return obj.forget(); +} + +already_AddRefed +DOMMatrix::Constructor(const GlobalObject& aGlobal, const nsAString& aTransformList, ErrorResult& aRv) +{ + nsRefPtr obj = new DOMMatrix(aGlobal.GetAsSupports()); + + obj = obj->SetMatrixValue(aTransformList, aRv); + return obj.forget(); +} + +already_AddRefed +DOMMatrix::Constructor(const GlobalObject& aGlobal, const DOMMatrixReadOnly& aOther, ErrorResult& aRv) +{ + nsRefPtr obj = new DOMMatrix(aGlobal.GetAsSupports(), aOther); + return obj.forget(); +} + +template void SetDataInMatrix(DOMMatrix* aMatrix, const T* aData, int aLength, ErrorResult& aRv) +{ + if (aLength == 16) { + aMatrix->SetM11(aData[0]); + aMatrix->SetM12(aData[1]); + aMatrix->SetM13(aData[2]); + aMatrix->SetM14(aData[3]); + aMatrix->SetM21(aData[4]); + aMatrix->SetM22(aData[5]); + aMatrix->SetM23(aData[6]); + aMatrix->SetM24(aData[7]); + aMatrix->SetM31(aData[8]); + aMatrix->SetM32(aData[9]); + aMatrix->SetM33(aData[10]); + aMatrix->SetM34(aData[11]); + aMatrix->SetM41(aData[12]); + aMatrix->SetM42(aData[13]); + aMatrix->SetM43(aData[14]); + aMatrix->SetM44(aData[15]); + } else if (aLength == 6) { + aMatrix->SetA(aData[0]); + aMatrix->SetB(aData[1]); + aMatrix->SetC(aData[2]); + aMatrix->SetD(aData[3]); + aMatrix->SetE(aData[4]); + aMatrix->SetF(aData[5]); + } else { + aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + } +} + +already_AddRefed +DOMMatrix::Constructor(const GlobalObject& aGlobal, const Float32Array& aArray32, ErrorResult& aRv) +{ + nsRefPtr obj = new DOMMatrix(aGlobal.GetAsSupports()); + aArray32.ComputeLengthAndData(); + SetDataInMatrix(obj, aArray32.Data(), aArray32.Length(), aRv); + + return obj.forget(); +} + +already_AddRefed +DOMMatrix::Constructor(const GlobalObject& aGlobal, const Float64Array& aArray64, ErrorResult& aRv) +{ + nsRefPtr obj = new DOMMatrix(aGlobal.GetAsSupports()); + aArray64.ComputeLengthAndData(); + SetDataInMatrix(obj, aArray64.Data(), aArray64.Length(), aRv); + + return obj.forget(); +} + +already_AddRefed +DOMMatrix::Constructor(const GlobalObject& aGlobal, const Sequence& aNumberSequence, ErrorResult& aRv) +{ + nsRefPtr obj = new DOMMatrix(aGlobal.GetAsSupports()); + SetDataInMatrix(obj, aNumberSequence.Elements(), aNumberSequence.Length(), aRv); + + return obj.forget(); +} + +void DOMMatrix::Ensure3DMatrix() +{ + if (!mMatrix3D) { + mMatrix3D = new gfx::Matrix4x4(gfx::Matrix4x4::From2D(*mMatrix2D)); + mMatrix2D = nullptr; + } +} + +DOMMatrix* +DOMMatrix::MultiplySelf(const DOMMatrix& aOther) +{ + if (aOther.Identity()) { + return this; + } + + if (aOther.Is2D()) { + if (mMatrix3D) { + *mMatrix3D = gfx::Matrix4x4::From2D(*aOther.mMatrix2D) * *mMatrix3D; + } else { + *mMatrix2D = *aOther.mMatrix2D * *mMatrix2D; + } + } else { + Ensure3DMatrix(); + *mMatrix3D = *aOther.mMatrix3D * *mMatrix3D; + } + + return this; +} + +DOMMatrix* +DOMMatrix::PreMultiplySelf(const DOMMatrix& aOther) +{ + if (aOther.Identity()) { + return this; + } + + if (aOther.Is2D()) { + if (mMatrix3D) { + *mMatrix3D = *mMatrix3D * gfx::Matrix4x4::From2D(*aOther.mMatrix2D); + } else { + *mMatrix2D = *mMatrix2D * *aOther.mMatrix2D; + } + } else { + Ensure3DMatrix(); + *mMatrix3D = *mMatrix3D * *aOther.mMatrix3D; + } + + return this; +} + +DOMMatrix* +DOMMatrix::TranslateSelf(double aTx, + double aTy, + double aTz) +{ + if (aTx == 0 && aTy == 0 && aTz == 0) { + return this; + } + + if (mMatrix3D || aTz != 0) { + Ensure3DMatrix(); + mMatrix3D->Translate(aTx, aTy, aTz); + } else { + mMatrix2D->Translate(aTx, aTy); + } + + return this; +} + +DOMMatrix* +DOMMatrix::ScaleSelf(double aScale, double aOriginX, double aOriginY) +{ + ScaleNonUniformSelf(aScale, aScale, 1.0, aOriginX, aOriginY, 0); + + return this; +} + +DOMMatrix* +DOMMatrix::Scale3dSelf(double aScale, double aOriginX, + double aOriginY, double aOriginZ) +{ + ScaleNonUniformSelf(aScale, aScale, aScale, aOriginX, aOriginY, aOriginZ); + + return this; +} + +DOMMatrix* +DOMMatrix::ScaleNonUniformSelf(double aScaleX, + double aScaleY, + double aScaleZ, + double aOriginX, + double aOriginY, + double aOriginZ) +{ + if (aScaleX == 1.0 && aScaleY == 1.0 && aScaleZ == 1.0) { + return this; + } + + TranslateSelf(aOriginX, aOriginY, aOriginZ); + + if (mMatrix3D || aScaleZ != 1.0 || aOriginZ != 0) { + Ensure3DMatrix(); + gfx::Matrix4x4 m; + m._11 = aScaleX; + m._22 = aScaleY; + m._33 = aScaleZ; + *mMatrix3D = m * *mMatrix3D; + } else { + gfx::Matrix m; + m._11 = aScaleX; + m._22 = aScaleY; + *mMatrix2D = m * *mMatrix2D; + } + + TranslateSelf(-aOriginX, -aOriginY, -aOriginZ); + + return this; +} + +DOMMatrix* +DOMMatrix::RotateFromVectorSelf(double aX, double aY) +{ + if (aX == 0.0 || aY == 0.0) { + return this; + } + + RotateSelf(atan2(aY, aX) / radPerDegree); + + return this; +} + +DOMMatrix* +DOMMatrix::RotateSelf(double aAngle, double aOriginX, double aOriginY) +{ + if (fmod(aAngle, 360) == 0) { + return this; + } + + TranslateSelf(aOriginX, aOriginY); + + if (mMatrix3D) { + RotateAxisAngleSelf(0, 0, 1, aAngle); + } else { + *mMatrix2D = mMatrix2D->Rotate(aAngle * radPerDegree); + } + + TranslateSelf(-aOriginX, -aOriginY); + + return this; +} + +DOMMatrix* +DOMMatrix::RotateAxisAngleSelf(double aX, double aY, + double aZ, double aAngle) +{ + if (fmod(aAngle, 360) == 0) { + return this; + } + + aAngle *= radPerDegree; + // sin(aAngle / 2) * cos(aAngle / 2) + double sc = sin(aAngle) / 2; + // pow(sin(aAngle / 2), 2) + double sq = (1 - cos(aAngle)) / 2; + + Ensure3DMatrix(); + gfx::Matrix4x4 m; + m._11 = 1 - 2 * (aY * aY + aZ * aZ) * sq; + m._12 = 2 * (aX * aY * sq + aZ * sc); + m._13 = 2 * (aX * aZ * sq - aY * sc); + m._21 = 2 * (aX * aY * sq - aZ * sc); + m._22 = 1 - 2 * (aX * aX + aZ * aZ) * sq; + m._23 = 2 * (aY * aZ * sq + aX * sc); + m._31 = 2 * (aX * aZ * sq + aY * sc); + m._32 = 2 * (aY * aZ * sq - aX * sc); + m._33 = 1 - 2 * (aX * aX + aY * aY) * sq; + + *mMatrix3D = m * *mMatrix3D; + + return this; +} + +DOMMatrix* +DOMMatrix::SkewXSelf(double aSx) +{ + if (fmod(aSx, 360) == 0) { + return this; + } + + if (mMatrix3D) { + gfx::Matrix4x4 m; + m._21 = tan(aSx * radPerDegree); + *mMatrix3D = m * *mMatrix3D; + } else { + gfx::Matrix m; + m._21 = tan(aSx * radPerDegree); + *mMatrix2D = m * *mMatrix2D; + } + + return this; +} + +DOMMatrix* +DOMMatrix::SkewYSelf(double aSy) +{ + if (fmod(aSy, 360) == 0) { + return this; + } + + if (mMatrix3D) { + gfx::Matrix4x4 m; + m._12 = tan(aSy * radPerDegree); + *mMatrix3D = m * *mMatrix3D; + } else { + gfx::Matrix m; + m._12 = tan(aSy * radPerDegree); + *mMatrix2D = m * *mMatrix2D; + } + + return this; +} + +DOMMatrix* +DOMMatrix::InvertSelf() +{ + if (mMatrix3D) { + if (!mMatrix3D->Invert()) { + mMatrix3D->SetNAN(); + } + } else if (!mMatrix2D->Invert()) { + mMatrix2D = nullptr; + + mMatrix3D = new gfx::Matrix4x4(); + mMatrix3D->SetNAN(); + } + + return this; +} + +DOMMatrix* +DOMMatrix::SetMatrixValue(const nsAString& aTransformList, ErrorResult& aRv) +{ + SVGTransformListParser parser(aTransformList); + if (!parser.Parse()) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + } else { + mMatrix3D = nullptr; + mMatrix2D = new gfx::Matrix(); + gfxMatrix result; + const nsTArray& mItems = parser.GetTransformList(); + + for (uint32_t i = 0; i < mItems.Length(); ++i) { + result.PreMultiply(mItems[i].GetMatrix()); + } + + SetA(result._11); + SetB(result._12); + SetC(result._21); + SetD(result._22); + SetE(result._31); + SetF(result._32); + } + + return this; +} + +JSObject* +DOMMatrix::WrapObject(JSContext* aCx) +{ + return DOMMatrixBinding::Wrap(aCx, this); +} + +} // namespace dom +} // namespace mozilla diff --git a/content/base/src/DOMMatrix.h b/content/base/src/DOMMatrix.h new file mode 100644 index 000000000000..7ae3541f043c --- /dev/null +++ b/content/base/src/DOMMatrix.h @@ -0,0 +1,256 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_DOM_DOMMATRIX_H_ +#define MOZILLA_DOM_DOMMATRIX_H_ + +#include "nsWrapperCache.h" +#include "nsISupports.h" +#include "nsCycleCollectionParticipant.h" +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" +#include "nsCOMPtr.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/TypedArray.h" +#include "mozilla/gfx/Matrix.h" // for Matrix4x4 + +namespace mozilla { +namespace dom { + +class GlobalObject; +class DOMMatrix; + +class DOMMatrixReadOnly : public nsWrapperCache +{ +public: + DOMMatrixReadOnly(nsISupports* aParent) + : mParent(aParent), mMatrix2D(new gfx::Matrix()) + { + SetIsDOMBinding(); + } + + DOMMatrixReadOnly(nsISupports* aParent, const DOMMatrixReadOnly& other) + : mParent(aParent) + { + if (other.mMatrix2D) { + mMatrix2D = new gfx::Matrix(*other.mMatrix2D); + } else { + mMatrix3D = new gfx::Matrix4x4(*other.mMatrix3D); + } + + SetIsDOMBinding(); + } + + ~DOMMatrixReadOnly() + { + } + +#define GetMatrixMember(entry2D, entry3D, default) \ +{ \ + if (mMatrix3D) { \ + return mMatrix3D->entry3D; \ + } \ + return mMatrix2D->entry2D; \ +} + +#define Get3DMatrixMember(entry3D, default) \ +{ \ + if (mMatrix3D) { \ + return mMatrix3D->entry3D; \ + } \ + return default; \ +} + + double A() const GetMatrixMember(_11, _11, 1.0) + double B() const GetMatrixMember(_12, _12, 0) + double C() const GetMatrixMember(_21, _21, 0) + double D() const GetMatrixMember(_22, _22, 1.0) + double E() const GetMatrixMember(_31, _41, 0) + double F() const GetMatrixMember(_32, _42, 0) + + double M11() const GetMatrixMember(_11, _11, 1.0) + double M12() const GetMatrixMember(_12, _12, 0) + double M13() const Get3DMatrixMember(_13, 0) + double M14() const Get3DMatrixMember(_14, 0) + double M21() const GetMatrixMember(_21, _21, 0) + double M22() const GetMatrixMember(_22, _22, 1.0) + double M23() const Get3DMatrixMember(_23, 0) + double M24() const Get3DMatrixMember(_24, 0) + double M31() const Get3DMatrixMember(_31, 0) + double M32() const Get3DMatrixMember(_32, 0) + double M33() const Get3DMatrixMember(_33, 1.0) + double M34() const Get3DMatrixMember(_34, 0) + double M41() const GetMatrixMember(_31, _41, 0) + double M42() const GetMatrixMember(_32, _42, 0) + double M43() const Get3DMatrixMember(_43, 0) + double M44() const Get3DMatrixMember(_44, 1.0) + +#undef GetMatrixMember +#undef Get3DMatrixMember + + already_AddRefed Translate(double aTx, + double aTy, + double aTz = 0) const; + already_AddRefed Scale(double aScale, + double aOriginX = 0, + double aOriginY = 0) const; + already_AddRefed Scale3d(double aScale, + double aOriginX = 0, + double aOriginY = 0, + double aOriginZ = 0) const; + already_AddRefed ScaleNonUniform(double aScaleX, + double aScaleY = 1.0, + double aScaleZ = 1.0, + double aOriginX = 0, + double aOriginY = 0, + double aOriginZ = 0) const; + already_AddRefed Rotate(double aAngle, + double aOriginX = 0, + double aOriginY = 0) const; + already_AddRefed RotateFromVector(double aX, + double aY) const; + already_AddRefed RotateAxisAngle(double aX, + double aY, + double aZ, + double aAngle) const; + already_AddRefed SkewX(double aSx) const; + already_AddRefed SkewY(double aSy) const; + already_AddRefed Multiply(const DOMMatrix& aOther) const; + already_AddRefed FlipX() const; + already_AddRefed FlipY() const; + already_AddRefed Inverse() const; + + bool Is2D() const; + bool Identity() const; + already_AddRefed TransformPoint(const DOMPointInit& aPoint) const; + void ToFloat32Array(JSContext* aCx, + JS::MutableHandle aResult, + ErrorResult& aRv) const; + void ToFloat64Array(JSContext* aCx, + JS::MutableHandle aResult, + ErrorResult& aRv) const; +protected: + nsCOMPtr mParent; + nsAutoPtr mMatrix2D; + nsAutoPtr mMatrix3D; +private: + DOMMatrixReadOnly() MOZ_DELETE; + DOMMatrixReadOnly(const DOMMatrixReadOnly&) MOZ_DELETE; + DOMMatrixReadOnly& operator=(const DOMMatrixReadOnly&) MOZ_DELETE; +}; + +class DOMMatrix MOZ_FINAL : public DOMMatrixReadOnly +{ +public: + DOMMatrix(nsISupports* aParent) + : DOMMatrixReadOnly(aParent) + {} + + DOMMatrix(nsISupports* aParent, const DOMMatrixReadOnly& other) + : DOMMatrixReadOnly(aParent, other) + {} + + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DOMMatrix) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(DOMMatrix) + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, ErrorResult& aRv); + static already_AddRefed + Constructor(const GlobalObject& aGlobal, const nsAString& aTransformList, ErrorResult& aRv); + static already_AddRefed + Constructor(const GlobalObject& aGlobal, const DOMMatrixReadOnly& aOther, ErrorResult& aRv); + static already_AddRefed + Constructor(const GlobalObject& aGlobal, const Float32Array& aArray32, ErrorResult& aRv); + static already_AddRefed + Constructor(const GlobalObject& aGlobal, const Float64Array& aArray64, ErrorResult& aRv); + static already_AddRefed + Constructor(const GlobalObject& aGlobal, const Sequence& aNumberSequence, ErrorResult& aRv); + + nsISupports* GetParentObject() const { return mParent; } + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; + +#define Set2DMatrixMember(entry2D, entry3D) \ +{ \ + if (mMatrix3D) { \ + mMatrix3D->entry3D = v; \ + } else { \ + mMatrix2D->entry2D = v; \ + } \ +} + +#define Set3DMatrixMember(entry3D, default) \ +{ \ + if (mMatrix3D || (v != default)) { \ + Ensure3DMatrix(); \ + mMatrix3D->entry3D = v; \ + } \ +} + + void SetA(double v) Set2DMatrixMember(_11, _11) + void SetB(double v) Set2DMatrixMember(_12, _12) + void SetC(double v) Set2DMatrixMember(_21, _21) + void SetD(double v) Set2DMatrixMember(_22, _22) + void SetE(double v) Set2DMatrixMember(_31, _41) + void SetF(double v) Set2DMatrixMember(_32, _42) + + void SetM11(double v) Set2DMatrixMember(_11, _11) + void SetM12(double v) Set2DMatrixMember(_12, _12) + void SetM13(double v) Set3DMatrixMember(_13, 0) + void SetM14(double v) Set3DMatrixMember(_14, 0) + void SetM21(double v) Set2DMatrixMember(_21, _21) + void SetM22(double v) Set2DMatrixMember(_22, _22) + void SetM23(double v) Set3DMatrixMember(_23, 0) + void SetM24(double v) Set3DMatrixMember(_24, 0) + void SetM31(double v) Set3DMatrixMember(_31, 0) + void SetM32(double v) Set3DMatrixMember(_32, 0) + void SetM33(double v) Set3DMatrixMember(_33, 1.0) + void SetM34(double v) Set3DMatrixMember(_34, 0) + void SetM41(double v) Set2DMatrixMember(_31, _41) + void SetM42(double v) Set2DMatrixMember(_32, _42) + void SetM43(double v) Set3DMatrixMember(_43, 0) + void SetM44(double v) Set3DMatrixMember(_44, 1.0) + +#undef Set2DMatrixMember +#undef Set3DMatrixMember + + DOMMatrix* MultiplySelf(const DOMMatrix& aOther); + DOMMatrix* PreMultiplySelf(const DOMMatrix& aOther); + DOMMatrix* TranslateSelf(double aTx, + double aTy, + double aTz = 0); + DOMMatrix* ScaleSelf(double aScale, + double aOriginX = 0, + double aOriginY = 0); + DOMMatrix* Scale3dSelf(double aScale, + double aOriginX = 0, + double aOriginY = 0, + double aOriginZ = 0); + DOMMatrix* ScaleNonUniformSelf(double aScaleX, + double aScaleY = 1, + double aScaleZ = 1, + double aOriginX = 0, + double aOriginY = 0, + double aOriginZ = 0); + DOMMatrix* RotateSelf(double aAngle, + double aOriginX = 0, + double aOriginY = 0); + DOMMatrix* RotateFromVectorSelf(double aX, + double aY); + DOMMatrix* RotateAxisAngleSelf(double aX, + double aY, + double aZ, + double aAngle); + DOMMatrix* SkewXSelf(double aSx); + DOMMatrix* SkewYSelf(double aSy); + DOMMatrix* InvertSelf(); + DOMMatrix* SetMatrixValue(const nsAString& aTransformList, ErrorResult& aRv); +private: + void Ensure3DMatrix(); +}; + +} +} + +#endif /*MOZILLA_DOM_DOMMATRIX_H_*/ diff --git a/content/base/src/moz.build b/content/base/src/moz.build index 52ae2d156a98..7ff40bfd92be 100644 --- a/content/base/src/moz.build +++ b/content/base/src/moz.build @@ -65,6 +65,7 @@ EXPORTS.mozilla.dom += [ 'DocumentFragment.h', 'DocumentType.h', 'DOMImplementation.h', + 'DOMMatrix.h', 'DOMParser.h', 'DOMPoint.h', 'DOMQuad.h', @@ -89,6 +90,7 @@ UNIFIED_SOURCES += [ 'DocumentFragment.cpp', 'DocumentType.cpp', 'DOMImplementation.cpp', + 'DOMMatrix.cpp', 'DOMParser.cpp', 'DOMPoint.cpp', 'DOMQuad.cpp', diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 0386b60dbf57..f5a292940d51 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -352,6 +352,11 @@ DOMInterfaces = { }, }, +'DOMMatrixReadOnly': { + 'headerFile': 'mozilla/dom/DOMMatrix.h', + 'concrete': False, +}, + 'DOMPointReadOnly': { 'headerFile': 'mozilla/dom/DOMPoint.h', 'concrete': False, diff --git a/dom/tests/mochitest/general/mochitest.ini b/dom/tests/mochitest/general/mochitest.ini index 676ef77a90a8..5f26bcbf4fb8 100644 --- a/dom/tests/mochitest/general/mochitest.ini +++ b/dom/tests/mochitest/general/mochitest.ini @@ -29,6 +29,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec [test_clipboard_events.html] skip-if = buildapp == 'b2g' # b2g(clipboard undefined) b2g-debug(clipboard undefined) b2g-desktop(clipboard undefined) [test_consoleAPI.html] +[test_DOMMatrix.html] [test_domWindowUtils.html] [test_domWindowUtils_scrollXY.html] [test_domWindowUtils_scrollbarSize.html] diff --git a/dom/tests/mochitest/general/test_DOMMatrix.html b/dom/tests/mochitest/general/test_DOMMatrix.html new file mode 100644 index 000000000000..848b2862f108 --- /dev/null +++ b/dom/tests/mochitest/general/test_DOMMatrix.html @@ -0,0 +1,714 @@ + + + + Test DOMMatrix behavior + + + + + + + + diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index 61cbb68296d4..ff4fc160fd16 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -311,6 +311,10 @@ var interfaceNamesInGlobalScope = "DOMException", // IMPORTANT: Do not change this list without review from a DOM peer! "DOMImplementation", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMMatrix", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMMatrixReadOnly", // IMPORTANT: Do not change this list without review from a DOM peer! {name: "DOMMMIError", b2g: true, pref: "dom.mobileconnection.enabled"}, // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/dom/webidl/DOMMatrix.webidl b/dom/webidl/DOMMatrix.webidl new file mode 100644 index 000000000000..6b236ae666ef --- /dev/null +++ b/dom/webidl/DOMMatrix.webidl @@ -0,0 +1,148 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The origin of this IDL file is + * http://dev.w3.org/fxtf/geometry/ + * + * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C + * liability, trademark and document use rules apply. + */ + +[Pref="layout.css.DOMMatrix.enabled"] +interface DOMMatrixReadOnly { + // These attributes are simple aliases for certain elements of the 4x4 matrix + readonly attribute unrestricted double a; + readonly attribute unrestricted double b; + readonly attribute unrestricted double c; + readonly attribute unrestricted double d; + readonly attribute unrestricted double e; + readonly attribute unrestricted double f; + + readonly attribute unrestricted double m11; + readonly attribute unrestricted double m12; + readonly attribute unrestricted double m13; + readonly attribute unrestricted double m14; + readonly attribute unrestricted double m21; + readonly attribute unrestricted double m22; + readonly attribute unrestricted double m23; + readonly attribute unrestricted double m24; + readonly attribute unrestricted double m31; + readonly attribute unrestricted double m32; + readonly attribute unrestricted double m33; + readonly attribute unrestricted double m34; + readonly attribute unrestricted double m41; + readonly attribute unrestricted double m42; + readonly attribute unrestricted double m43; + readonly attribute unrestricted double m44; + + // Immutable transform methods + DOMMatrix translate(unrestricted double tx, + unrestricted double ty, + optional unrestricted double tz = 0); + DOMMatrix scale(unrestricted double scale, + optional unrestricted double originX = 0, + optional unrestricted double originY = 0); + DOMMatrix scale3d(unrestricted double scale, + optional unrestricted double originX = 0, + optional unrestricted double originY = 0, + optional unrestricted double originZ = 0); + DOMMatrix scaleNonUniform(unrestricted double scaleX, + optional unrestricted double scaleY = 1, + optional unrestricted double scaleZ = 1, + optional unrestricted double originX = 0, + optional unrestricted double originY = 0, + optional unrestricted double originZ = 0); + DOMMatrix rotate(unrestricted double angle, + optional unrestricted double originX = 0, + optional unrestricted double originY = 0); + DOMMatrix rotateFromVector(unrestricted double x, + unrestricted double y); + DOMMatrix rotateAxisAngle(unrestricted double x, + unrestricted double y, + unrestricted double z, + unrestricted double angle); + DOMMatrix skewX(unrestricted double sx); + DOMMatrix skewY(unrestricted double sy); + DOMMatrix multiply(DOMMatrix other); + DOMMatrix flipX(); + DOMMatrix flipY(); + DOMMatrix inverse(); + + // Helper methods + readonly attribute boolean is2D; + readonly attribute boolean identity; + DOMPoint transformPoint(optional DOMPointInit point); + [Throws] Float32Array toFloat32Array(); + [Throws] Float64Array toFloat64Array(); + stringifier; +}; + +[Pref="layout.css.DOMMatrix.enabled", + Constructor, + Constructor(DOMString transformList), + Constructor(DOMMatrixReadOnly other), + Constructor(Float32Array array32), + Constructor(Float64Array array64), + Constructor(sequence numberSequence)] +interface DOMMatrix : DOMMatrixReadOnly { + // These attributes are simple aliases for certain elements of the 4x4 matrix + inherit attribute unrestricted double a; + inherit attribute unrestricted double b; + inherit attribute unrestricted double c; + inherit attribute unrestricted double d; + inherit attribute unrestricted double e; + inherit attribute unrestricted double f; + + inherit attribute unrestricted double m11; + inherit attribute unrestricted double m12; + inherit attribute unrestricted double m13; + inherit attribute unrestricted double m14; + inherit attribute unrestricted double m21; + inherit attribute unrestricted double m22; + inherit attribute unrestricted double m23; + inherit attribute unrestricted double m24; + inherit attribute unrestricted double m31; + inherit attribute unrestricted double m32; + inherit attribute unrestricted double m33; + inherit attribute unrestricted double m34; + inherit attribute unrestricted double m41; + inherit attribute unrestricted double m42; + inherit attribute unrestricted double m43; + inherit attribute unrestricted double m44; + + // Mutable transform methods + DOMMatrix multiplySelf(DOMMatrix other); + DOMMatrix preMultiplySelf(DOMMatrix other); + DOMMatrix translateSelf(unrestricted double tx, + unrestricted double ty, + optional unrestricted double tz = 0); + DOMMatrix scaleSelf(unrestricted double scale, + optional unrestricted double originX = 0, + optional unrestricted double originY = 0); + DOMMatrix scale3dSelf(unrestricted double scale, + optional unrestricted double originX = 0, + optional unrestricted double originY = 0, + optional unrestricted double originZ = 0); + DOMMatrix scaleNonUniformSelf(unrestricted double scaleX, + optional unrestricted double scaleY = 1, + optional unrestricted double scaleZ = 1, + optional unrestricted double originX = 0, + optional unrestricted double originY = 0, + optional unrestricted double originZ = 0); + DOMMatrix rotateSelf(unrestricted double angle, + optional unrestricted double originX = 0, + optional unrestricted double originY = 0); + DOMMatrix rotateFromVectorSelf(unrestricted double x, + unrestricted double y); + DOMMatrix rotateAxisAngleSelf(unrestricted double x, + unrestricted double y, + unrestricted double z, + unrestricted double angle); + DOMMatrix skewXSelf(unrestricted double sx); + DOMMatrix skewYSelf(unrestricted double sy); + DOMMatrix invertSelf(); + [Throws] DOMMatrix setMatrixValue(DOMString transformList); +}; + diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 8985da5dafd9..6ac704ca329e 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -88,6 +88,7 @@ WEBIDL_FILES = [ 'DOMError.webidl', 'DOMException.webidl', 'DOMImplementation.webidl', + 'DOMMatrix.webidl', 'DOMMobileMessageError.webidl', 'DOMParser.webidl', 'DOMPoint.webidl', diff --git a/gfx/2d/Matrix.cpp b/gfx/2d/Matrix.cpp index a5be1327aecc..53eabe78cb2a 100644 --- a/gfx/2d/Matrix.cpp +++ b/gfx/2d/Matrix.cpp @@ -7,6 +7,8 @@ #include "Tools.h" #include +#include "mozilla/FloatingPoint.h" // for UnspecifiedNaN + namespace mozilla { namespace gfx { @@ -102,5 +104,73 @@ Matrix4x4::TransformBounds(const Rect& aRect) const return Rect(min_x, min_y, max_x - min_x, max_y - min_y); } +bool +Matrix4x4::Invert() +{ + Float det = Determinant(); + if (!det) { + return false; + } + + Matrix4x4 result; + result._11 = _23 * _34 * _42 - _24 * _33 * _42 + _24 * _32 * _43 - _22 * _34 * _43 - _23 * _32 * _44 + _22 * _33 * _44; + result._12 = _14 * _33 * _42 - _13 * _34 * _42 - _14 * _32 * _43 + _12 * _34 * _43 + _13 * _32 * _44 - _12 * _33 * _44; + result._13 = _13 * _24 * _42 - _14 * _23 * _42 + _14 * _22 * _43 - _12 * _24 * _43 - _13 * _22 * _44 + _12 * _23 * _44; + result._14 = _14 * _23 * _32 - _13 * _24 * _32 - _14 * _22 * _33 + _12 * _24 * _33 + _13 * _22 * _34 - _12 * _23 * _34; + result._21 = _24 * _33 * _41 - _23 * _34 * _41 - _24 * _31 * _43 + _21 * _34 * _43 + _23 * _31 * _44 - _21 * _33 * _44; + result._22 = _13 * _34 * _41 - _14 * _33 * _41 + _14 * _31 * _43 - _11 * _34 * _43 - _13 * _31 * _44 + _11 * _33 * _44; + result._23 = _14 * _23 * _41 - _13 * _24 * _41 - _14 * _21 * _43 + _11 * _24 * _43 + _13 * _21 * _44 - _11 * _23 * _44; + result._24 = _13 * _24 * _31 - _14 * _23 * _31 + _14 * _21 * _33 - _11 * _24 * _33 - _13 * _21 * _34 + _11 * _23 * _34; + result._31 = _22 * _34 * _41 - _24 * _32 * _41 + _24 * _31 * _42 - _21 * _34 * _42 - _22 * _31 * _44 + _21 * _32 * _44; + result._32 = _14 * _32 * _41 - _12 * _34 * _41 - _14 * _31 * _42 + _11 * _34 * _42 + _12 * _31 * _44 - _11 * _32 * _44; + result._33 = _12 * _24 * _41 - _14 * _22 * _41 + _14 * _21 * _42 - _11 * _24 * _42 - _12 * _21 * _44 + _11 * _22 * _44; + result._34 = _14 * _22 * _31 - _12 * _24 * _31 - _14 * _21 * _32 + _11 * _24 * _32 + _12 * _21 * _34 - _11 * _22 * _34; + result._41 = _23 * _32 * _41 - _22 * _33 * _41 - _23 * _31 * _42 + _21 * _33 * _42 + _22 * _31 * _43 - _21 * _32 * _43; + result._42 = _12 * _33 * _41 - _13 * _32 * _41 + _13 * _31 * _42 - _11 * _33 * _42 - _12 * _31 * _43 + _11 * _32 * _43; + result._43 = _13 * _22 * _41 - _12 * _23 * _41 - _13 * _21 * _42 + _11 * _23 * _42 + _12 * _21 * _43 - _11 * _22 * _43; + result._44 = _12 * _23 * _31 - _13 * _22 * _31 + _13 * _21 * _32 - _11 * _23 * _32 - _12 * _21 * _33 + _11 * _22 * _33; + + result._11 /= det; + result._12 /= det; + result._13 /= det; + result._14 /= det; + result._21 /= det; + result._22 /= det; + result._23 /= det; + result._24 /= det; + result._31 /= det; + result._32 /= det; + result._33 /= det; + result._34 /= det; + result._41 /= det; + result._42 /= det; + result._43 /= det; + result._44 /= det; + *this = result; + + return true; +} + +void +Matrix4x4::SetNAN() +{ + _11 = UnspecifiedNaN(); + _21 = UnspecifiedNaN(); + _31 = UnspecifiedNaN(); + _41 = UnspecifiedNaN(); + _12 = UnspecifiedNaN(); + _22 = UnspecifiedNaN(); + _32 = UnspecifiedNaN(); + _42 = UnspecifiedNaN(); + _13 = UnspecifiedNaN(); + _23 = UnspecifiedNaN(); + _33 = UnspecifiedNaN(); + _43 = UnspecifiedNaN(); + _14 = UnspecifiedNaN(); + _24 = UnspecifiedNaN(); + _34 = UnspecifiedNaN(); + _44 = UnspecifiedNaN(); +} + } } diff --git a/gfx/2d/Matrix.h b/gfx/2d/Matrix.h index 231580de77fc..6f295f3805a1 100644 --- a/gfx/2d/Matrix.h +++ b/gfx/2d/Matrix.h @@ -508,6 +508,10 @@ public: + _11 * _22 * _33 * _44; } + bool Invert(); + + // Set all the members of the matrix to NaN + void SetNAN(); }; class Matrix5x4 diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 8e84ac86f5fb..72fab9dc3767 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1889,6 +1889,9 @@ pref("layout.css.DOMPoint.enabled", true); // Is support for DOMQuad enabled? pref("layout.css.DOMQuad.enabled", true); +// Is support for DOMMatrix enabled? +pref("layout.css.DOMMatrix.enabled", true); + // Is support for GeometryUtils.getBoxQuads enabled? #ifdef RELEASE_BUILD pref("layout.css.getBoxQuads.enabled", false);