1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/dom/BindingUtils.h"
7 #include "mozilla/dom/DOMMatrixBinding.h"
8 #include "mozilla/dom/DOMPointBinding.h"
9 #include "mozilla/dom/BindingDeclarations.h"
10 #include "mozilla/dom/ToJSValue.h"
12 #include "mozilla/dom/DOMPoint.h"
13 #include "mozilla/dom/DOMMatrix.h"
15 #include "SVGTransformListParser.h"
16 #include "SVGTransform.h"
18 #include "nsAutoPtr.h"
24 static const double radPerDegree
= 2.0 * M_PI
/ 360.0;
26 already_AddRefed
<DOMMatrix
>
27 DOMMatrixReadOnly::Translate(double aTx
,
31 nsRefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
32 retval
->TranslateSelf(aTx
, aTy
, aTz
);
34 return retval
.forget();
37 already_AddRefed
<DOMMatrix
>
38 DOMMatrixReadOnly::Scale(double aScale
,
40 double aOriginY
) const
42 nsRefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
43 retval
->ScaleSelf(aScale
, aOriginX
, aOriginY
);
45 return retval
.forget();
48 already_AddRefed
<DOMMatrix
>
49 DOMMatrixReadOnly::Scale3d(double aScale
,
52 double aOriginZ
) const
54 nsRefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
55 retval
->Scale3dSelf(aScale
, aOriginX
, aOriginY
, aOriginZ
);
57 return retval
.forget();
60 already_AddRefed
<DOMMatrix
>
61 DOMMatrixReadOnly::ScaleNonUniform(double aScaleX
,
66 double aOriginZ
) const
68 nsRefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
69 retval
->ScaleNonUniformSelf(aScaleX
, aScaleY
, aScaleZ
, aOriginX
, aOriginY
, aOriginZ
);
71 return retval
.forget();
74 already_AddRefed
<DOMMatrix
>
75 DOMMatrixReadOnly::Rotate(double aAngle
,
77 double aOriginY
) const
79 nsRefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
80 retval
->RotateSelf(aAngle
, aOriginX
, aOriginY
);
82 return retval
.forget();
85 already_AddRefed
<DOMMatrix
>
86 DOMMatrixReadOnly::RotateFromVector(double x
,
89 nsRefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
90 retval
->RotateFromVectorSelf(x
, y
);
92 return retval
.forget();
95 already_AddRefed
<DOMMatrix
>
96 DOMMatrixReadOnly::RotateAxisAngle(double aX
,
101 nsRefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
102 retval
->RotateAxisAngleSelf(aX
, aY
, aZ
, aAngle
);
104 return retval
.forget();
107 already_AddRefed
<DOMMatrix
>
108 DOMMatrixReadOnly::SkewX(double aSx
) const
110 nsRefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
111 retval
->SkewXSelf(aSx
);
113 return retval
.forget();
116 already_AddRefed
<DOMMatrix
>
117 DOMMatrixReadOnly::SkewY(double aSy
) const
119 nsRefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
120 retval
->SkewYSelf(aSy
);
122 return retval
.forget();
125 already_AddRefed
<DOMMatrix
>
126 DOMMatrixReadOnly::Multiply(const DOMMatrix
& other
) const
128 nsRefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
129 retval
->MultiplySelf(other
);
131 return retval
.forget();
134 already_AddRefed
<DOMMatrix
>
135 DOMMatrixReadOnly::FlipX() const
137 nsRefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
141 retval
->mMatrix3D
= new gfx::Matrix4x4(m
* *mMatrix3D
);
145 retval
->mMatrix2D
= new gfx::Matrix(mMatrix2D
? m
* *mMatrix2D
: m
);
148 return retval
.forget();
151 already_AddRefed
<DOMMatrix
>
152 DOMMatrixReadOnly::FlipY() const
154 nsRefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
158 retval
->mMatrix3D
= new gfx::Matrix4x4(m
* *mMatrix3D
);
162 retval
->mMatrix2D
= new gfx::Matrix(mMatrix2D
? m
* *mMatrix2D
: m
);
165 return retval
.forget();
168 already_AddRefed
<DOMMatrix
>
169 DOMMatrixReadOnly::Inverse() const
171 nsRefPtr
<DOMMatrix
> retval
= new DOMMatrix(mParent
, *this);
172 retval
->InvertSelf();
174 return retval
.forget();
178 DOMMatrixReadOnly::Is2D() const
184 DOMMatrixReadOnly::Identity() const
187 return mMatrix3D
->IsIdentity();
190 return mMatrix2D
->IsIdentity();
193 already_AddRefed
<DOMPoint
>
194 DOMMatrixReadOnly::TransformPoint(const DOMPointInit
& point
) const
196 nsRefPtr
<DOMPoint
> retval
= new DOMPoint(mParent
);
199 gfx::Point4D transformedPoint
;
200 transformedPoint
.x
= point
.mX
;
201 transformedPoint
.y
= point
.mY
;
202 transformedPoint
.z
= point
.mZ
;
203 transformedPoint
.w
= point
.mW
;
205 transformedPoint
= *mMatrix3D
* transformedPoint
;
207 retval
->SetX(transformedPoint
.x
);
208 retval
->SetY(transformedPoint
.y
);
209 retval
->SetZ(transformedPoint
.z
);
210 retval
->SetW(transformedPoint
.w
);
211 } else if (point
.mZ
!= 0 || point
.mW
!= 1.0) {
212 gfx::Matrix4x4
tempMatrix(gfx::Matrix4x4::From2D(*mMatrix2D
));
214 gfx::Point4D transformedPoint
;
215 transformedPoint
.x
= point
.mX
;
216 transformedPoint
.y
= point
.mY
;
217 transformedPoint
.z
= point
.mZ
;
218 transformedPoint
.w
= point
.mW
;
220 transformedPoint
= tempMatrix
* transformedPoint
;
222 retval
->SetX(transformedPoint
.x
);
223 retval
->SetY(transformedPoint
.y
);
224 retval
->SetZ(transformedPoint
.z
);
225 retval
->SetW(transformedPoint
.w
);
227 gfx::Point transformedPoint
;
228 transformedPoint
.x
= point
.mX
;
229 transformedPoint
.y
= point
.mY
;
231 transformedPoint
= *mMatrix2D
* transformedPoint
;
233 retval
->SetX(transformedPoint
.x
);
234 retval
->SetY(transformedPoint
.y
);
235 retval
->SetZ(point
.mZ
);
236 retval
->SetW(point
.mW
);
238 return retval
.forget();
241 template <typename T
> void GetDataFromMatrix(const DOMMatrixReadOnly
* aMatrix
, T
* aData
)
243 aData
[0] = static_cast<T
>(aMatrix
->M11());
244 aData
[1] = static_cast<T
>(aMatrix
->M12());
245 aData
[2] = static_cast<T
>(aMatrix
->M13());
246 aData
[3] = static_cast<T
>(aMatrix
->M14());
247 aData
[4] = static_cast<T
>(aMatrix
->M21());
248 aData
[5] = static_cast<T
>(aMatrix
->M22());
249 aData
[6] = static_cast<T
>(aMatrix
->M23());
250 aData
[7] = static_cast<T
>(aMatrix
->M24());
251 aData
[8] = static_cast<T
>(aMatrix
->M31());
252 aData
[9] = static_cast<T
>(aMatrix
->M32());
253 aData
[10] = static_cast<T
>(aMatrix
->M33());
254 aData
[11] = static_cast<T
>(aMatrix
->M34());
255 aData
[12] = static_cast<T
>(aMatrix
->M41());
256 aData
[13] = static_cast<T
>(aMatrix
->M42());
257 aData
[14] = static_cast<T
>(aMatrix
->M43());
258 aData
[15] = static_cast<T
>(aMatrix
->M44());
262 DOMMatrixReadOnly::ToFloat32Array(JSContext
* aCx
, JS::MutableHandle
<JSObject
*> aResult
, ErrorResult
& aRv
) const
264 nsAutoTArray
<float, 16> arr
;
266 GetDataFromMatrix(this, arr
.Elements());
267 JS::Rooted
<JS::Value
> value(aCx
);
268 if (!ToJSValue(aCx
, TypedArrayCreator
<Float32Array
>(arr
), &value
)) {
269 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
272 aResult
.set(&value
.toObject());
276 DOMMatrixReadOnly::ToFloat64Array(JSContext
* aCx
, JS::MutableHandle
<JSObject
*> aResult
, ErrorResult
& aRv
) const
278 nsAutoTArray
<double, 16> arr
;
280 GetDataFromMatrix(this, arr
.Elements());
281 JS::Rooted
<JS::Value
> value(aCx
);
282 if (!ToJSValue(aCx
, TypedArrayCreator
<Float64Array
>(arr
), &value
)) {
283 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
286 aResult
.set(&value
.toObject());
290 DOMMatrixReadOnly::Stringify(nsAString
& aResult
)
292 nsAutoString matrixStr
;
294 matrixStr
.AppendPrintf("matrix3d(%g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g)",
295 M11(), M12(), M13(), M14(),
296 M21(), M22(), M23(), M24(),
297 M31(), M32(), M33(), M34(),
298 M41(), M42(), M43(), M44());
300 matrixStr
.AppendPrintf("matrix(%g, %g, %g, %g, %g, %g)", A(), B(), C(), D(), E(), F());
306 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMMatrix
, mParent
)
308 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMMatrix
, AddRef
)
309 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMMatrix
, Release
)
311 already_AddRefed
<DOMMatrix
>
312 DOMMatrix::Constructor(const GlobalObject
& aGlobal
, ErrorResult
& aRv
)
314 nsRefPtr
<DOMMatrix
> obj
= new DOMMatrix(aGlobal
.GetAsSupports());
318 already_AddRefed
<DOMMatrix
>
319 DOMMatrix::Constructor(const GlobalObject
& aGlobal
, const nsAString
& aTransformList
, ErrorResult
& aRv
)
321 nsRefPtr
<DOMMatrix
> obj
= new DOMMatrix(aGlobal
.GetAsSupports());
323 obj
= obj
->SetMatrixValue(aTransformList
, aRv
);
327 already_AddRefed
<DOMMatrix
>
328 DOMMatrix::Constructor(const GlobalObject
& aGlobal
, const DOMMatrixReadOnly
& aOther
, ErrorResult
& aRv
)
330 nsRefPtr
<DOMMatrix
> obj
= new DOMMatrix(aGlobal
.GetAsSupports(), aOther
);
334 template <typename T
> void SetDataInMatrix(DOMMatrix
* aMatrix
, const T
* aData
, int aLength
, ErrorResult
& aRv
)
337 aMatrix
->SetM11(aData
[0]);
338 aMatrix
->SetM12(aData
[1]);
339 aMatrix
->SetM13(aData
[2]);
340 aMatrix
->SetM14(aData
[3]);
341 aMatrix
->SetM21(aData
[4]);
342 aMatrix
->SetM22(aData
[5]);
343 aMatrix
->SetM23(aData
[6]);
344 aMatrix
->SetM24(aData
[7]);
345 aMatrix
->SetM31(aData
[8]);
346 aMatrix
->SetM32(aData
[9]);
347 aMatrix
->SetM33(aData
[10]);
348 aMatrix
->SetM34(aData
[11]);
349 aMatrix
->SetM41(aData
[12]);
350 aMatrix
->SetM42(aData
[13]);
351 aMatrix
->SetM43(aData
[14]);
352 aMatrix
->SetM44(aData
[15]);
353 } else if (aLength
== 6) {
354 aMatrix
->SetA(aData
[0]);
355 aMatrix
->SetB(aData
[1]);
356 aMatrix
->SetC(aData
[2]);
357 aMatrix
->SetD(aData
[3]);
358 aMatrix
->SetE(aData
[4]);
359 aMatrix
->SetF(aData
[5]);
361 aRv
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
365 already_AddRefed
<DOMMatrix
>
366 DOMMatrix::Constructor(const GlobalObject
& aGlobal
, const Float32Array
& aArray32
, ErrorResult
& aRv
)
368 nsRefPtr
<DOMMatrix
> obj
= new DOMMatrix(aGlobal
.GetAsSupports());
369 aArray32
.ComputeLengthAndData();
370 SetDataInMatrix(obj
, aArray32
.Data(), aArray32
.Length(), aRv
);
375 already_AddRefed
<DOMMatrix
>
376 DOMMatrix::Constructor(const GlobalObject
& aGlobal
, const Float64Array
& aArray64
, ErrorResult
& aRv
)
378 nsRefPtr
<DOMMatrix
> obj
= new DOMMatrix(aGlobal
.GetAsSupports());
379 aArray64
.ComputeLengthAndData();
380 SetDataInMatrix(obj
, aArray64
.Data(), aArray64
.Length(), aRv
);
385 already_AddRefed
<DOMMatrix
>
386 DOMMatrix::Constructor(const GlobalObject
& aGlobal
, const Sequence
<double>& aNumberSequence
, ErrorResult
& aRv
)
388 nsRefPtr
<DOMMatrix
> obj
= new DOMMatrix(aGlobal
.GetAsSupports());
389 SetDataInMatrix(obj
, aNumberSequence
.Elements(), aNumberSequence
.Length(), aRv
);
394 void DOMMatrix::Ensure3DMatrix()
397 mMatrix3D
= new gfx::Matrix4x4(gfx::Matrix4x4::From2D(*mMatrix2D
));
403 DOMMatrix::MultiplySelf(const DOMMatrix
& aOther
)
405 if (aOther
.Identity()) {
411 *mMatrix3D
= gfx::Matrix4x4::From2D(*aOther
.mMatrix2D
) * *mMatrix3D
;
413 *mMatrix2D
= *aOther
.mMatrix2D
* *mMatrix2D
;
417 *mMatrix3D
= *aOther
.mMatrix3D
* *mMatrix3D
;
424 DOMMatrix::PreMultiplySelf(const DOMMatrix
& aOther
)
426 if (aOther
.Identity()) {
432 *mMatrix3D
= *mMatrix3D
* gfx::Matrix4x4::From2D(*aOther
.mMatrix2D
);
434 *mMatrix2D
= *mMatrix2D
* *aOther
.mMatrix2D
;
438 *mMatrix3D
= *mMatrix3D
* *aOther
.mMatrix3D
;
445 DOMMatrix::TranslateSelf(double aTx
,
449 if (aTx
== 0 && aTy
== 0 && aTz
== 0) {
453 if (mMatrix3D
|| aTz
!= 0) {
455 mMatrix3D
->PreTranslate(aTx
, aTy
, aTz
);
457 mMatrix2D
->PreTranslate(aTx
, aTy
);
464 DOMMatrix::ScaleSelf(double aScale
, double aOriginX
, double aOriginY
)
466 ScaleNonUniformSelf(aScale
, aScale
, 1.0, aOriginX
, aOriginY
, 0);
472 DOMMatrix::Scale3dSelf(double aScale
, double aOriginX
,
473 double aOriginY
, double aOriginZ
)
475 ScaleNonUniformSelf(aScale
, aScale
, aScale
, aOriginX
, aOriginY
, aOriginZ
);
481 DOMMatrix::ScaleNonUniformSelf(double aScaleX
,
488 if (aScaleX
== 1.0 && aScaleY
== 1.0 && aScaleZ
== 1.0) {
492 TranslateSelf(aOriginX
, aOriginY
, aOriginZ
);
494 if (mMatrix3D
|| aScaleZ
!= 1.0 || aOriginZ
!= 0) {
500 *mMatrix3D
= m
* *mMatrix3D
;
505 *mMatrix2D
= m
* *mMatrix2D
;
508 TranslateSelf(-aOriginX
, -aOriginY
, -aOriginZ
);
514 DOMMatrix::RotateFromVectorSelf(double aX
, double aY
)
516 if (aX
== 0.0 || aY
== 0.0) {
520 RotateSelf(atan2(aY
, aX
) / radPerDegree
);
526 DOMMatrix::RotateSelf(double aAngle
, double aOriginX
, double aOriginY
)
528 if (fmod(aAngle
, 360) == 0) {
532 TranslateSelf(aOriginX
, aOriginY
);
535 RotateAxisAngleSelf(0, 0, 1, aAngle
);
537 *mMatrix2D
= mMatrix2D
->PreRotate(aAngle
* radPerDegree
);
540 TranslateSelf(-aOriginX
, -aOriginY
);
546 DOMMatrix::RotateAxisAngleSelf(double aX
, double aY
,
547 double aZ
, double aAngle
)
549 if (fmod(aAngle
, 360) == 0) {
553 aAngle
*= radPerDegree
;
554 // sin(aAngle / 2) * cos(aAngle / 2)
555 double sc
= sin(aAngle
) / 2;
556 // pow(sin(aAngle / 2), 2)
557 double sq
= (1 - cos(aAngle
)) / 2;
561 m
._11
= 1 - 2 * (aY
* aY
+ aZ
* aZ
) * sq
;
562 m
._12
= 2 * (aX
* aY
* sq
+ aZ
* sc
);
563 m
._13
= 2 * (aX
* aZ
* sq
- aY
* sc
);
564 m
._21
= 2 * (aX
* aY
* sq
- aZ
* sc
);
565 m
._22
= 1 - 2 * (aX
* aX
+ aZ
* aZ
) * sq
;
566 m
._23
= 2 * (aY
* aZ
* sq
+ aX
* sc
);
567 m
._31
= 2 * (aX
* aZ
* sq
+ aY
* sc
);
568 m
._32
= 2 * (aY
* aZ
* sq
- aX
* sc
);
569 m
._33
= 1 - 2 * (aX
* aX
+ aY
* aY
) * sq
;
571 *mMatrix3D
= m
* *mMatrix3D
;
577 DOMMatrix::SkewXSelf(double aSx
)
579 if (fmod(aSx
, 360) == 0) {
585 m
._21
= tan(aSx
* radPerDegree
);
586 *mMatrix3D
= m
* *mMatrix3D
;
589 m
._21
= tan(aSx
* radPerDegree
);
590 *mMatrix2D
= m
* *mMatrix2D
;
597 DOMMatrix::SkewYSelf(double aSy
)
599 if (fmod(aSy
, 360) == 0) {
605 m
._12
= tan(aSy
* radPerDegree
);
606 *mMatrix3D
= m
* *mMatrix3D
;
609 m
._12
= tan(aSy
* radPerDegree
);
610 *mMatrix2D
= m
* *mMatrix2D
;
617 DOMMatrix::InvertSelf()
620 if (!mMatrix3D
->Invert()) {
623 } else if (!mMatrix2D
->Invert()) {
626 mMatrix3D
= new gfx::Matrix4x4();
634 DOMMatrix::SetMatrixValue(const nsAString
& aTransformList
, ErrorResult
& aRv
)
636 SVGTransformListParser
parser(aTransformList
);
637 if (!parser
.Parse()) {
638 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
641 mMatrix2D
= new gfx::Matrix();
643 const nsTArray
<nsSVGTransform
>& mItems
= parser
.GetTransformList();
645 for (uint32_t i
= 0; i
< mItems
.Length(); ++i
) {
646 result
.PreMultiply(mItems
[i
].GetMatrix());
661 DOMMatrix::WrapObject(JSContext
* aCx
)
663 return DOMMatrixBinding::Wrap(aCx
, this);
667 } // namespace mozilla