Bug 1018497 - Implementation of DOMMatrix. r=roc,bz

This commit is contained in:
Rik Cabanier 2014-06-24 20:15:00 +02:00
parent 8a23d2f4a9
commit 5d9f208b1b
12 changed files with 1858 additions and 0 deletions

View File

@ -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 <math.h>
namespace mozilla {
namespace dom {
static const double radPerDegree = 2.0 * M_PI / 360.0;
already_AddRefed<DOMMatrix>
DOMMatrixReadOnly::Translate(double aTx,
double aTy,
double aTz) const
{
nsRefPtr<DOMMatrix> retval = new DOMMatrix(mParent, *this);
retval->TranslateSelf(aTx, aTy, aTz);
return retval.forget();
}
already_AddRefed<DOMMatrix>
DOMMatrixReadOnly::Scale(double aScale,
double aOriginX,
double aOriginY) const
{
nsRefPtr<DOMMatrix> retval = new DOMMatrix(mParent, *this);
retval->ScaleSelf(aScale, aOriginX, aOriginY);
return retval.forget();
}
already_AddRefed<DOMMatrix>
DOMMatrixReadOnly::Scale3d(double aScale,
double aOriginX,
double aOriginY,
double aOriginZ) const
{
nsRefPtr<DOMMatrix> retval = new DOMMatrix(mParent, *this);
retval->Scale3dSelf(aScale, aOriginX, aOriginY, aOriginZ);
return retval.forget();
}
already_AddRefed<DOMMatrix>
DOMMatrixReadOnly::ScaleNonUniform(double aScaleX,
double aScaleY,
double aScaleZ,
double aOriginX,
double aOriginY,
double aOriginZ) const
{
nsRefPtr<DOMMatrix> retval = new DOMMatrix(mParent, *this);
retval->ScaleNonUniformSelf(aScaleX, aScaleY, aScaleZ, aOriginX, aOriginY, aOriginZ);
return retval.forget();
}
already_AddRefed<DOMMatrix>
DOMMatrixReadOnly::Rotate(double aAngle,
double aOriginX ,
double aOriginY) const
{
nsRefPtr<DOMMatrix> retval = new DOMMatrix(mParent, *this);
retval->RotateSelf(aAngle, aOriginX, aOriginY);
return retval.forget();
}
already_AddRefed<DOMMatrix>
DOMMatrixReadOnly::RotateFromVector(double x,
double y) const
{
nsRefPtr<DOMMatrix> retval = new DOMMatrix(mParent, *this);
retval->RotateFromVectorSelf(x, y);
return retval.forget();
}
already_AddRefed<DOMMatrix>
DOMMatrixReadOnly::RotateAxisAngle(double aX,
double aY,
double aZ,
double aAngle) const
{
nsRefPtr<DOMMatrix> retval = new DOMMatrix(mParent, *this);
retval->RotateAxisAngleSelf(aX, aY, aZ, aAngle);
return retval.forget();
}
already_AddRefed<DOMMatrix>
DOMMatrixReadOnly::SkewX(double aSx) const
{
nsRefPtr<DOMMatrix> retval = new DOMMatrix(mParent, *this);
retval->SkewXSelf(aSx);
return retval.forget();
}
already_AddRefed<DOMMatrix>
DOMMatrixReadOnly::SkewY(double aSy) const
{
nsRefPtr<DOMMatrix> retval = new DOMMatrix(mParent, *this);
retval->SkewYSelf(aSy);
return retval.forget();
}
already_AddRefed<DOMMatrix>
DOMMatrixReadOnly::Multiply(const DOMMatrix& other) const
{
nsRefPtr<DOMMatrix> retval = new DOMMatrix(mParent, *this);
retval->MultiplySelf(other);
return retval.forget();
}
already_AddRefed<DOMMatrix>
DOMMatrixReadOnly::FlipX() const
{
nsRefPtr<DOMMatrix> 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<DOMMatrix>
DOMMatrixReadOnly::FlipY() const
{
nsRefPtr<DOMMatrix> 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<DOMMatrix>
DOMMatrixReadOnly::Inverse() const
{
nsRefPtr<DOMMatrix> 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<DOMPoint>
DOMMatrixReadOnly::TransformPoint(const DOMPointInit& point) const
{
nsRefPtr<DOMPoint> 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 <typename T> void GetDataFromMatrix(const DOMMatrixReadOnly* aMatrix, T* aData)
{
aData[0] = static_cast<T>(aMatrix->M11());
aData[1] = static_cast<T>(aMatrix->M12());
aData[2] = static_cast<T>(aMatrix->M13());
aData[3] = static_cast<T>(aMatrix->M14());
aData[4] = static_cast<T>(aMatrix->M21());
aData[5] = static_cast<T>(aMatrix->M22());
aData[6] = static_cast<T>(aMatrix->M23());
aData[7] = static_cast<T>(aMatrix->M24());
aData[8] = static_cast<T>(aMatrix->M31());
aData[9] = static_cast<T>(aMatrix->M32());
aData[10] = static_cast<T>(aMatrix->M33());
aData[11] = static_cast<T>(aMatrix->M34());
aData[12] = static_cast<T>(aMatrix->M41());
aData[13] = static_cast<T>(aMatrix->M42());
aData[14] = static_cast<T>(aMatrix->M43());
aData[15] = static_cast<T>(aMatrix->M44());
}
void
DOMMatrixReadOnly::ToFloat32Array(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv) const
{
nsAutoTArray<float, 16> arr;
arr.SetLength(16);
GetDataFromMatrix(this, arr.Elements());
JS::Rooted<JS::Value> value(aCx);
if (!ToJSValue(aCx, TypedArrayCreator<Float32Array>(arr), &value)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
aResult.set(&value.toObject());
}
void
DOMMatrixReadOnly::ToFloat64Array(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv) const
{
nsAutoTArray<double, 16> arr;
arr.SetLength(16);
GetDataFromMatrix(this, arr.Elements());
JS::Rooted<JS::Value> value(aCx);
if (!ToJSValue(aCx, TypedArrayCreator<Float64Array>(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>
DOMMatrix::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
{
nsRefPtr<DOMMatrix> obj = new DOMMatrix(aGlobal.GetAsSupports());
return obj.forget();
}
already_AddRefed<DOMMatrix>
DOMMatrix::Constructor(const GlobalObject& aGlobal, const nsAString& aTransformList, ErrorResult& aRv)
{
nsRefPtr<DOMMatrix> obj = new DOMMatrix(aGlobal.GetAsSupports());
obj = obj->SetMatrixValue(aTransformList, aRv);
return obj.forget();
}
already_AddRefed<DOMMatrix>
DOMMatrix::Constructor(const GlobalObject& aGlobal, const DOMMatrixReadOnly& aOther, ErrorResult& aRv)
{
nsRefPtr<DOMMatrix> obj = new DOMMatrix(aGlobal.GetAsSupports(), aOther);
return obj.forget();
}
template <typename T> 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>
DOMMatrix::Constructor(const GlobalObject& aGlobal, const Float32Array& aArray32, ErrorResult& aRv)
{
nsRefPtr<DOMMatrix> obj = new DOMMatrix(aGlobal.GetAsSupports());
aArray32.ComputeLengthAndData();
SetDataInMatrix(obj, aArray32.Data(), aArray32.Length(), aRv);
return obj.forget();
}
already_AddRefed<DOMMatrix>
DOMMatrix::Constructor(const GlobalObject& aGlobal, const Float64Array& aArray64, ErrorResult& aRv)
{
nsRefPtr<DOMMatrix> obj = new DOMMatrix(aGlobal.GetAsSupports());
aArray64.ComputeLengthAndData();
SetDataInMatrix(obj, aArray64.Data(), aArray64.Length(), aRv);
return obj.forget();
}
already_AddRefed<DOMMatrix>
DOMMatrix::Constructor(const GlobalObject& aGlobal, const Sequence<double>& aNumberSequence, ErrorResult& aRv)
{
nsRefPtr<DOMMatrix> 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<nsSVGTransform>& 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

View File

@ -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<DOMMatrix> Translate(double aTx,
double aTy,
double aTz = 0) const;
already_AddRefed<DOMMatrix> Scale(double aScale,
double aOriginX = 0,
double aOriginY = 0) const;
already_AddRefed<DOMMatrix> Scale3d(double aScale,
double aOriginX = 0,
double aOriginY = 0,
double aOriginZ = 0) const;
already_AddRefed<DOMMatrix> ScaleNonUniform(double aScaleX,
double aScaleY = 1.0,
double aScaleZ = 1.0,
double aOriginX = 0,
double aOriginY = 0,
double aOriginZ = 0) const;
already_AddRefed<DOMMatrix> Rotate(double aAngle,
double aOriginX = 0,
double aOriginY = 0) const;
already_AddRefed<DOMMatrix> RotateFromVector(double aX,
double aY) const;
already_AddRefed<DOMMatrix> RotateAxisAngle(double aX,
double aY,
double aZ,
double aAngle) const;
already_AddRefed<DOMMatrix> SkewX(double aSx) const;
already_AddRefed<DOMMatrix> SkewY(double aSy) const;
already_AddRefed<DOMMatrix> Multiply(const DOMMatrix& aOther) const;
already_AddRefed<DOMMatrix> FlipX() const;
already_AddRefed<DOMMatrix> FlipY() const;
already_AddRefed<DOMMatrix> Inverse() const;
bool Is2D() const;
bool Identity() const;
already_AddRefed<DOMPoint> TransformPoint(const DOMPointInit& aPoint) const;
void ToFloat32Array(JSContext* aCx,
JS::MutableHandle<JSObject*> aResult,
ErrorResult& aRv) const;
void ToFloat64Array(JSContext* aCx,
JS::MutableHandle<JSObject*> aResult,
ErrorResult& aRv) const;
protected:
nsCOMPtr<nsISupports> mParent;
nsAutoPtr<gfx::Matrix> mMatrix2D;
nsAutoPtr<gfx::Matrix4x4> 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<DOMMatrix>
Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
static already_AddRefed<DOMMatrix>
Constructor(const GlobalObject& aGlobal, const nsAString& aTransformList, ErrorResult& aRv);
static already_AddRefed<DOMMatrix>
Constructor(const GlobalObject& aGlobal, const DOMMatrixReadOnly& aOther, ErrorResult& aRv);
static already_AddRefed<DOMMatrix>
Constructor(const GlobalObject& aGlobal, const Float32Array& aArray32, ErrorResult& aRv);
static already_AddRefed<DOMMatrix>
Constructor(const GlobalObject& aGlobal, const Float64Array& aArray64, ErrorResult& aRv);
static already_AddRefed<DOMMatrix>
Constructor(const GlobalObject& aGlobal, const Sequence<double>& 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_*/

View File

@ -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',

View File

@ -352,6 +352,11 @@ DOMInterfaces = {
},
},
'DOMMatrixReadOnly': {
'headerFile': 'mozilla/dom/DOMMatrix.h',
'concrete': False,
},
'DOMPointReadOnly': {
'headerFile': 'mozilla/dom/DOMPoint.h',
'concrete': False,

View File

@ -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]

View File

@ -0,0 +1,714 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test DOMMatrix behavior</title>
<scriptsrc="/MochiKit/packed.js"></script>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
</head>
<script>
function createMatrix(a, b, c, d, e, f)
{
var m = new DOMMatrix();
m.a = a;
m.b = b;
m.c = c;
m.d = d;
m.e = e;
m.f = f;
return m;
}
function create3DMatrix(a, b, c, d, e, f)
{
var m = new DOMMatrix();
m.a = a;
m.b = b;
m.c = c;
m.d = d;
m.e = e;
m.f = f;
m.m13 = 0;
return m;
}
function cmpMatrix(a, b, msg)
{
if (Array.isArray(a))
a = new DOMMatrix(a);
if (Array.isArray(b))
b = new DOMMatrix(b);
ok(CompareDOMMatrix(a, b),
msg + " - got " + formatMatrix(a)
+ ", expected " + formatMatrix(b));
}
function roughCmpMatrix(a, b, msg)
{
if (Array.isArray(a))
a = new DOMMatrix(a);
if (Array.isArray(b))
b = new DOMMatrix(b);
ok(RoughCompareDOMMatrix(a, b),
msg + " - got " + formatMatrix(a)
+ ", expected " + formatMatrix(b));
}
function formatMatrix(m)
{
m = new DOMMatrix(m);
if (m.is2D)
return "(" + [m.a, m.b, m.c, m.d, m.e, m.f].join(', ') + ")";
else
return "(" + [m.m11, m.m12, m.m13, m.m14,
m.m21, m.m22, m.m23, m.m24,
m.m31, m.m32, m.m33, m.m34,
m.m41, m.m42, m.m43, m.m44,].join(', ') + ")";
}
function CompareMatrix(dm, m)
{
var ma = m.toFloat32Array();
for (var x = 0; x < ma.length; x++) {
if (Math.abs(ma[x] - dm.m[x]) > 0.000001)
return false;
}
return true;
}
function CompareDOMMatrix(dm1, dm2)
{
var m1 = dm1.toFloat32Array();
var m2 = dm2.toFloat32Array();
if (m1.length != m2.length)
return false;
for (var x = 0; x < m1.length; x++) {
if (Math.abs(m1[x] - m2[x]) > 0.000001)
return false;
}
return true;
}
function RoughCompareDOMMatrix(dm1, dm2)
{
var m1 = dm1.toFloat32Array();
var m2 = dm2.toFloat32Array();
if (m1.length != m2.length)
return false;
const tolerance = 1 / 65535;
for (var x = 0; x < m1.length; x++) {
if (Math.abs(m1[x] - m2[x]) > tolerance)
return false;
}
return true;
}
SimpleTest.waitForExplicitFinish();
function main()
{
var tests = [
testCreateMatrix,
testMultiply,
testInverse,
testTranslate,
testScale,
testScaleNonUniform,
testRotate,
testRotateFromVector,
testFlipX,
testFlipY,
testSkewX,
testSkewY,
testMultiplyInPlace,
testInverseInPlace,
testTranslateInPlace,
testScaleInPlace,
testScaleNonUniformInPlace,
testRotateInPlace,
testRotateFromVectorInPlace,
testSkewXInPlace,
testSkewYInPlace,
testCreateMatrix3D,
testMultiply3D,
testInverse3D,
testTranslate3D,
testScale3D,
test3D,
testParsing
];
for (var i = 0; i < tests.length; i++) {
try{
tests[i]();
} catch (e) {
ok(false, "uncaught exception in test " + i + ": " + e.name);
}
}
SimpleTest.finish();
}
function testCreateMatrix()
{
var m = new DOMMatrix();
// Should be initialised to identity
cmpMatrix(m, [1, 0, 0, 1, 0, 0],
"DOMMatrix should produce identity matrix");
m = new DOMMatrix([1,2,3,4,5,6]);
cmpMatrix(m, [1,2,3,4,5,6],
"DOMMatrix should produce the same matrix");
ok(m.is2D, "Failed to mark matrix as 2D.");
m = new DOMMatrix([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]);
cmpMatrix(m, [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],
"DOMMatrix should produce the same matrix");
ok(!m.is2D, "Failed to mark matrix as 3D.");
var n = new DOMMatrix(m.toFloat32Array());
cmpMatrix(n, [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],
"DOMMatrix should produce the same matrix with float32array constructor");
ok(!n.is2D, "Failed to mark matrix as 3D.");
var n = new DOMMatrix(m.toFloat64Array());
cmpMatrix(n, [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],
"DOMMatrix should produce the same matrix with float64array constructor");
ok(!n.is2D, "Failed to mark matrix as 3D.");
var exn = null;
try {
m = new DOMMatrix([0]);
} catch (e) {
exn = e;
}
ok(exn, "did throw exception with bad DOMMatrix constructor with 1 parameter");
exn = null;
try {
m = new DOMMatrix([1,2,3,4,5,6,7,8,9]);
} catch (e) {
exn = e;
}
ok(exn, "did throw exception with bad DOMMatrix constructor with 9 parameters");
exn = null;
try {
m = new DOMMatrix([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]);
} catch (e) {
exn = e;
}
ok(exn, "did throw exception with bad DOMMatrix constructor with 17 parameters");
}
// DOMMatrix multiply(in DOMMatrix secondMatrix);
function testMultiply()
{
var m1 = createMatrix(1, 0, 0, 1, 50, 90);
var m2 = createMatrix(Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 0, 0);
var m3 = createMatrix(1, 0, 0, 1, 130, 160);
var result = m1.multiply(m2).multiply(m3);
roughCmpMatrix(result, [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 255.060974, 111.213203],
"Unexpected result after multiplying matrices");
// Check orig matrices are unchanged
cmpMatrix(m1, [1, 0, 0, 1, 50, 90], "Matrix changed after multiplication");
roughCmpMatrix(m2, [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 0, 0],
"Matrix changed after multiplication");
cmpMatrix(m3, [1, 0, 0, 1, 130, 160], "Matrix changed after multiplication");
}
// DOMMatrix inverse() raises(SVGException);
function testInverse()
{
// Test inversion
var m = createMatrix(2, 0, 0, 4, 110, -50);
roughCmpMatrix(m.inverse(), [0.5, 0, 0, 0.25, -55, 12.5],
"Unexpected result after inverting matrix");
// Test non-invertable
m = createMatrix(0, 0, 1, 0, 0, 0);
m = m.inverse();
ok(isNaN(m.a), "Failed to invalidate inverted singular matrix, got " + m.a);
ok(!m.is2D, "Failed to mark invalidated inverted singular matrix as 3D.");
}
// DOMMatrix translate(in float x, in float y);
function testTranslate()
{
var m = createMatrix(2, 0, 0, 1, 120, 100);
roughCmpMatrix(m.translate(100, -50), [2, 0, 0, 1, 320, 50],
"Unexpected result after translate");
}
// DOMMatrix scale(in float scaleFactor);
function testScale()
{
var m = createMatrix(2, 0, 0, 1, 120, 100);
roughCmpMatrix(m.scale(0.5), [1, 0, 0, 0.5, 120, 100],
"Unexpected result after scale");
}
// DOMMatrix scaleNonUniform(in float scaleFactorX, in float scaleFactorY);
function testScaleNonUniform()
{
var m = createMatrix(2, 0, 0, 1, 120, 100);
roughCmpMatrix(m.scaleNonUniform(0.5, -3), [1, 0, 0, -3, 120, 100],
"Unexpected result after scaleNonUniform");
}
// DOMMatrix rotate(in float angle);
function testRotate()
{
var m = createMatrix(2, 0, 0, 1, 120, 100);
roughCmpMatrix(m.rotate(45),
[2*Math.cos(Math.PI/4), Math.sin(Math.PI/4),
2*-Math.sin(Math.PI/4), Math.cos(Math.PI/4),
120, 100],
"Unexpected result after rotate");
}
// DOMMatrix rotateFromVector(in float x, in float y) raises(SVGException);
function testRotateFromVector()
{
var m = createMatrix(2, 0, 0, 1, 120, 100);
// Make a 150 degree angle
var result = m.rotateFromVector(-2, 1.1547);
roughCmpMatrix(result,
[2*Math.cos(5*Math.PI/6), Math.sin(5*Math.PI/6),
2*-Math.sin(5*Math.PI/6), Math.cos(5*Math.PI/6),
120, 100],
"Unexpected result after rotateFromVector");
// Test bad input (1)
var exn = null;
try {
m.rotateFromVector(1, 0);
} catch (e) {
exn = e;
}
ise(exn, null, "did not throw exception with zero coord for rotateFromVector");
// Test bad input (2)
exn = null;
try {
m.rotateFromVector(0, 1);
} catch (e) {
exn = e;
}
ise(exn, null, "did not throw exception with zero coord for rotateFromVector");
}
// DOMMatrix flipX();
function testFlipX()
{
var m = createMatrix(1, 2, 3, 4, 5, 6);
cmpMatrix(m.flipX(), [-1, -2, 3, 4, 5, 6], "Unexpected result after flipX");
}
// DOMMatrix flipY();
function testFlipY()
{
var m = createMatrix(1, 2, 3, 4, 5, 6);
cmpMatrix(m.flipY(), [1, 2, -3, -4, 5, 6], "Unexpected result after flipY");
}
// DOMMatrix skewX(in float angle);
function testSkewX()
{
var m = createMatrix(2, 0, 0, 1, 120, 100);
roughCmpMatrix(m.skewX(30), [2, 0, 2*Math.tan(Math.PI/6), 1, 120, 100],
"Unexpected result after skewX");
}
// DOMMatrix skewY(in float angle);
function testSkewY()
{
var m = createMatrix(2, 0, 0, 1, 120, 100);
roughCmpMatrix(m.skewY(30), [2, Math.tan(Math.PI/6), 0, 1, 120, 100],
"Unexpected result after skewY");
}
// DOMMatrix multiply(in DOMMatrix secondMatrix);
function testMultiplyInPlace()
{
var m1 = createMatrix(1, 0, 0, 1, 50, 90);
var m2 = createMatrix(Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 0, 0);
var m3 = createMatrix(1, 0, 0, 1, 130, 160);
m1.multiplySelf(m2).multiplySelf(m3);
roughCmpMatrix(m1, [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 255.060974, 111.213203],
"Unexpected result after multiplying matrices");
// Check orig matrices are unchanged
roughCmpMatrix(m2, [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 0, 0],
"Matrix changed after multiplication");
cmpMatrix(m3, [1, 0, 0, 1, 130, 160], "Matrix changed after multiplication");
}
// DOMMatrix inverse() raises(SVGException);
function testInverseInPlace()
{
// Test inversion
var m = createMatrix(2, 0, 0, 4, 110, -50);
m.invertSelf();
roughCmpMatrix(m, [0.5, 0, 0, 0.25, -55, 12.5],
"Unexpected result after inverting matrix");
// Test non-invertable
m = createMatrix(0, 0, 1, 0, 0, 0);
m.invertSelf();
ok(isNaN(m.a), "Failed to invalidate inverted singular matrix, got " + m.a);
ok(!m.is2D, "Failed to mark invalidated inverted singular matrix as 3D.");
}
// DOMMatrix translate(in float x, in float y);
function testTranslateInPlace()
{
var m = createMatrix(2, 0, 0, 1, 120, 100);
m.translateSelf(100, -50)
roughCmpMatrix(m, [2, 0, 0, 1, 320, 50],
"Unexpected result after translate");
}
// DOMMatrix scale(in float scaleFactor);
function testScaleInPlace()
{
var m = createMatrix(2, 0, 0, 1, 120, 100);
m.scaleSelf(0.5);
roughCmpMatrix(m, [1, 0, 0, 0.5, 120, 100],
"Unexpected result after scale");
}
// DOMMatrix scaleNonUniform(in float scaleFactorX, in float scaleFactorY);
function testScaleNonUniformInPlace()
{
var m = createMatrix(2, 0, 0, 1, 120, 100);
m.scaleNonUniformSelf(0.5, -3);
roughCmpMatrix(m, [1, 0, 0, -3, 120, 100],
"Unexpected result after scaleNonUniform");
}
// DOMMatrix rotate(in float angle);
function testRotateInPlace()
{
var m = createMatrix(2, 0, 0, 1, 120, 100);
m.rotateSelf(45);
roughCmpMatrix(m,
[2*Math.cos(Math.PI/4), Math.sin(Math.PI/4),
2*-Math.sin(Math.PI/4), Math.cos(Math.PI/4),
120, 100],
"Unexpected result after rotate");
}
// DOMMatrix rotateFromVector(in float x, in float y) raises(SVGException);
function testRotateFromVectorInPlace()
{
var m = createMatrix(2, 0, 0, 1, 120, 100);
// Make a 150 degree angle
m.rotateFromVectorSelf(-2, 1.1547);
roughCmpMatrix(m,
[2*Math.cos(5*Math.PI/6), Math.sin(5*Math.PI/6),
2*-Math.sin(5*Math.PI/6), Math.cos(5*Math.PI/6),
120, 100],
"Unexpected result after rotateFromVector");
// Test bad input (1)
try {
m.rotateFromVectorSelf(1, 0);
ok(true, "did not throw exception with zero coord for rotateFromVector");
} catch (e) {
ok(false,
"Got unexpected exception " + e + ", expected NotSupportedError");
}
// Test bad input (2)
try {
m.rotateFromVectorSelf(0, 1);
ok(true, "did not throw exception with zero coord for rotateFromVector");
} catch (e) { }
}
// DOMMatrix skewX(in float angle);
function testSkewXInPlace()
{
var m = createMatrix(2, 0, 0, 1, 120, 100);
m.skewXSelf(30);
roughCmpMatrix(m, [2, 0, 2*Math.tan(Math.PI/6), 1, 120, 100],
"Unexpected result after skewX");
}
// DOMMatrix skewY(in float angle);
function testSkewYInPlace()
{
var m = createMatrix(2, 0, 0, 1, 120, 100);
m.skewYSelf(30);
roughCmpMatrix(m, [2, Math.tan(Math.PI/6), 0, 1, 120, 100],
"Unexpected result after skewY");
}
function testCreateMatrix3D()
{
var m = new DOMMatrix([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
// Should be initialised to identity
cmpMatrix(m, [1, 0, 0, 1, 0, 0],
"DOMMatrix should produce identity matrix");
is(m.is2D, true, "should not produce 3d matrix");
m = new DOMMatrix([1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]);
// Should be initialised to identity
cmpMatrix(m, [1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
"DOMMatrix should produce identity matrix");
ise(m.is2D, false, "should produce 3d matrix");
}
// DOMMatrix multiply(in DOMMatrix secondMatrix);
function testMultiply3D()
{
var m1 = createMatrix(1, 0, 0, 1, 50, 90);
var m2 = create3DMatrix(Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 0, 0);
var m3 = create3DMatrix(1, 0, 0, 1, 130, 160);
var result = m1.multiply(m2).multiply(m3);
ok(m1.is2D == true, "should produce 3d matrix");
roughCmpMatrix(result, [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 255.060974, 111.213203],
"Unexpected result after multiplying matrices");
// Check orig matrices are unchanged
cmpMatrix(m1, [1, 0, 0, 1, 50, 90], "Matrix changed after multiplication");
roughCmpMatrix(m2, [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 0, 0],
"Matrix changed after multiplication");
cmpMatrix(m3, [1, 0, 0, 1, 130, 160], "Matrix changed after multiplication");
}
// DOMMatrix inverse() raises(SVGException);
function testInverse3D()
{
// Test inversion
var m = create3DMatrix(2, 0, 0, 4, 110, -50);
roughCmpMatrix(m.inverse(), [0.5, 0, 0, 0.25, -55, 12.5],
"Unexpected result after inverting matrix");
// Test non-invertable
m = createMatrix(0, 0, 1, 0, 0, 0);
m = m.inverse();
ok(isNaN(m.a), "Failed to invalidate inverted singular matrix, got " + m.a);
ok(!m.is2D, "Failed to mark invalidated inverted singular matrix as 3D.");
}
// DOMMatrix translate(in float x, in float y);
function testTranslate3D()
{
var m = create3DMatrix(2, 0, 0, 1, 120, 100);
roughCmpMatrix(m.translate(100, -50), [2, 0, 0, 1, 320, 50],
"Unexpected result after translate");
}
// DOMMatrix scale(in float scaleFactor);
function testScale3D()
{
var m = create3DMatrix(2, 0, 0, 1, 120, 100);
roughCmpMatrix(m.scale(0.5), [1, 0, 0, 0.5, 120, 100],
"Unexpected result after scale");
}
function Matrix3D() {
this.m = new Float32Array([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]);
}
Matrix3D.prototype = {
translate: function(x, y, z, result) {
result = result || new Matrix3D();
var m = result.m;
m[0] = 1;
m[1] = 0;
m[2] = 0;
m[3] = x;
m[4] = 0;
m[5] = 1;
m[6] = 0;
m[7] = y;
m[8] = 0;
m[9] = 0;
m[10] = 1;
m[11] = z;
m[12] = 0;
m[13] = 0;
m[14] = 0;
m[15] = 1;
return result;
},
inverse: function(matrix, result) {
result = result || new Matrix3D();
var m = matrix.m, r = result.m;
r[0] = m[5]*m[10]*m[15] - m[5]*m[14]*m[11] - m[6]*m[9]*m[15] + m[6]*m[13]*m[11] + m[7]*m[9]*m[14] - m[7]*m[13]*m[10];
r[1] = -m[1]*m[10]*m[15] + m[1]*m[14]*m[11] + m[2]*m[9]*m[15] - m[2]*m[13]*m[11] - m[3]*m[9]*m[14] + m[3]*m[13]*m[10];
r[2] = m[1]*m[6]*m[15] - m[1]*m[14]*m[7] - m[2]*m[5]*m[15] + m[2]*m[13]*m[7] + m[3]*m[5]*m[14] - m[3]*m[13]*m[6];
r[3] = -m[1]*m[6]*m[11] + m[1]*m[10]*m[7] + m[2]*m[5]*m[11] - m[2]*m[9]*m[7] - m[3]*m[5]*m[10] + m[3]*m[9]*m[6];
r[4] = -m[4]*m[10]*m[15] + m[4]*m[14]*m[11] + m[6]*m[8]*m[15] - m[6]*m[12]*m[11] - m[7]*m[8]*m[14] + m[7]*m[12]*m[10];
r[5] = m[0]*m[10]*m[15] - m[0]*m[14]*m[11] - m[2]*m[8]*m[15] + m[2]*m[12]*m[11] + m[3]*m[8]*m[14] - m[3]*m[12]*m[10];
r[6] = -m[0]*m[6]*m[15] + m[0]*m[14]*m[7] + m[2]*m[4]*m[15] - m[2]*m[12]*m[7] - m[3]*m[4]*m[14] + m[3]*m[12]*m[6];
r[7] = m[0]*m[6]*m[11] - m[0]*m[10]*m[7] - m[2]*m[4]*m[11] + m[2]*m[8]*m[7] + m[3]*m[4]*m[10] - m[3]*m[8]*m[6];
r[8] = m[4]*m[9]*m[15] - m[4]*m[13]*m[11] - m[5]*m[8]*m[15] + m[5]*m[12]*m[11] + m[7]*m[8]*m[13] - m[7]*m[12]*m[9];
r[9] = -m[0]*m[9]*m[15] + m[0]*m[13]*m[11] + m[1]*m[8]*m[15] - m[1]*m[12]*m[11] - m[3]*m[8]*m[13] + m[3]*m[12]*m[9];
r[10] = m[0]*m[5]*m[15] - m[0]*m[13]*m[7] - m[1]*m[4]*m[15] + m[1]*m[12]*m[7] + m[3]*m[4]*m[13] - m[3]*m[12]*m[5];
r[11] = -m[0]*m[5]*m[11] + m[0]*m[9]*m[7] + m[1]*m[4]*m[11] - m[1]*m[8]*m[7] - m[3]*m[4]*m[9] + m[3]*m[8]*m[5];
r[12] = -m[4]*m[9]*m[14] + m[4]*m[13]*m[10] + m[5]*m[8]*m[14] - m[5]*m[12]*m[10] - m[6]*m[8]*m[13] + m[6]*m[12]*m[9];
r[13] = m[0]*m[9]*m[14] - m[0]*m[13]*m[10] - m[1]*m[8]*m[14] + m[1]*m[12]*m[10] + m[2]*m[8]*m[13] - m[2]*m[12]*m[9];
r[14] = -m[0]*m[5]*m[14] + m[0]*m[13]*m[6] + m[1]*m[4]*m[14] - m[1]*m[12]*m[6] - m[2]*m[4]*m[13] + m[2]*m[12]*m[5];
r[15] = m[0]*m[5]*m[10] - m[0]*m[9]*m[6] - m[1]*m[4]*m[10] + m[1]*m[8]*m[6] + m[2]*m[4]*m[9] - m[2]*m[8]*m[5];
var det = m[0]*r[0] + m[1]*r[4] + m[2]*r[8] + m[3]*r[12];
for (var i = 0; i < 16; i++) r[i] /= det;
return result;
},
multiply: function(left, result) {
result = result || new Matrix3D();
var a = this.m, b = left.m, r = result.m;
r[0] = a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12];
r[1] = a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13];
r[2] = a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14];
r[3] = a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15];
r[4] = a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12];
r[5] = a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13];
r[6] = a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14];
r[7] = a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15];
r[8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12];
r[9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13];
r[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14];
r[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15];
r[12] = a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12];
r[13] = a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13];
r[14] = a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14];
r[15] = a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15];
return result;
},
scale: function(x, y, z, result) {
result = result || new Matrix3D();
var m = result.m;
m[0] = x;
m[1] = 0;
m[2] = 0;
m[3] = 0;
m[4] = 0;
m[5] = y;
m[6] = 0;
m[7] = 0;
m[8] = 0;
m[9] = 0;
m[10] = z;
m[11] = 0;
m[12] = 0;
m[13] = 0;
m[14] = 0;
m[15] = 1;
return result;
},
rotate: function(a, x, y, z, result) {
result = result || new Matrix3D();
var m = result.m;
var d = Math.sqrt(x*x + y*y + z*z);
a *= Math.PI / 180; x /= d; y /= d; z /= d;
var c = Math.cos(a), s = Math.sin(a), t = 1 - c;
m[0] = x * x * t + c;
m[1] = x * y * t - z * s;
m[2] = x * z * t + y * s;
m[3] = 0;
m[4] = y * x * t + z * s;
m[5] = y * y * t + c;
m[6] = y * z * t - x * s;
m[7] = 0;
m[8] = z * x * t - y * s;
m[9] = z * y * t + x * s;
m[10] = z * z * t + c;
m[11] = 0;
m[12] = 0;
m[13] = 0;
m[14] = 0;
m[15] = 1;
return result;
},
swap: function(result) {
result = result || new Matrix3D();
for (var x = 0; x < 16; x++)
result.m[x] = this.m[Math.floor(x/4) + (x%4)*4];
return result;
}
};
function test3D()
{
var m = new DOMMatrix()
var m2 = new Matrix3D();
m.translateSelf(2,3,4).scaleNonUniformSelf(1.2, 2.3, 3.4, 0, 0, 0);
m2 = m2.multiply(m2.translate(2,3,4)).multiply(m2.scale(1.2, 2.3, 3.4)).swap();
ok(CompareMatrix(m2, m), "translate + scale in 3d didn't match, expected: " + formatMatrix(m2.m) + ", got: " + formatMatrix(m));
m.invertSelf();
m2 = new Matrix3D();
m2 = m2.multiply(m2.translate(2,3,4)).multiply(m2.scale(1.2, 2.3, 3.4));
m2 = m2.inverse(m2).swap();
ok(CompareMatrix(m2, m), "translate + scale in inverted 3d didn't match, expected: " + formatMatrix(m2.m) + ", got: " + formatMatrix(m));
}
function testParsing()
{
var m = new DOMMatrix("translate(10, 20) scale(.5, 2) rotate(45)");
var m2 = new DOMMatrix();
m2.translateSelf(10, 20).scaleNonUniformSelf(.5,2).rotateSelf(45);
ok(CompareDOMMatrix(m2, m), "string parsing didn't match");
m = new DOMMatrix();
m.setMatrixValue("translate(10, 20) scale(.5, 2) rotate(45)");
ok(CompareDOMMatrix(m2, m), "string parsing didn't match");
}
window.addEventListener("load", main, false);
</script>
</pre>
</body>
</html>

View File

@ -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!

148
dom/webidl/DOMMatrix.webidl Normal file
View File

@ -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<unrestricted double> 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);
};

View File

@ -88,6 +88,7 @@ WEBIDL_FILES = [
'DOMError.webidl',
'DOMException.webidl',
'DOMImplementation.webidl',
'DOMMatrix.webidl',
'DOMMobileMessageError.webidl',
'DOMParser.webidl',
'DOMPoint.webidl',

View File

@ -7,6 +7,8 @@
#include "Tools.h"
#include <math.h>
#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<Float>();
_21 = UnspecifiedNaN<Float>();
_31 = UnspecifiedNaN<Float>();
_41 = UnspecifiedNaN<Float>();
_12 = UnspecifiedNaN<Float>();
_22 = UnspecifiedNaN<Float>();
_32 = UnspecifiedNaN<Float>();
_42 = UnspecifiedNaN<Float>();
_13 = UnspecifiedNaN<Float>();
_23 = UnspecifiedNaN<Float>();
_33 = UnspecifiedNaN<Float>();
_43 = UnspecifiedNaN<Float>();
_14 = UnspecifiedNaN<Float>();
_24 = UnspecifiedNaN<Float>();
_34 = UnspecifiedNaN<Float>();
_44 = UnspecifiedNaN<Float>();
}
}
}

View File

@ -508,6 +508,10 @@ public:
+ _11 * _22 * _33 * _44;
}
bool Invert();
// Set all the members of the matrix to NaN
void SetNAN();
};
class Matrix5x4

View File

@ -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);