Bug 505115 - Part 15 - Add 4D Vectors, Quaternions and gfx3DMatrix functions. r=derf
authorMatt Woodrow <mwoodrow@mozilla.com>
Sat, 27 Aug 2011 12:06:03 +1200
changeset 75972 be7cefceceac32401cf37147142aefec214a5ee6
parent 75971 7ccff3faa2a3a2655cfc6a6c6c8850b0aa86679a
child 75973 bbb80094e71ac267a31142e902f57568f63caa87
push id1522
push usermwoodrow@mozilla.com
push dateSat, 27 Aug 2011 00:08:57 +0000
treeherdermozilla-inbound@bbb80094e71a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersderf
bugs505115
milestone9.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 505115 - Part 15 - Add 4D Vectors, Quaternions and gfx3DMatrix functions. r=derf
gfx/2d/BasePoint3D.h
gfx/2d/BasePoint4D.h
gfx/2d/Makefile.in
gfx/thebes/Makefile.in
gfx/thebes/gfx3DMatrix.cpp
gfx/thebes/gfx3DMatrix.h
gfx/thebes/gfxPointH3D.h
gfx/thebes/gfxQuaternion.h
layout/base/FrameLayerBuilder.cpp
layout/base/nsDisplayList.cpp
layout/ipc/RenderFrameParent.cpp
xpcom/string/public/nsAlgorithm.h
--- a/gfx/2d/BasePoint3D.h
+++ b/gfx/2d/BasePoint3D.h
@@ -55,16 +55,26 @@ struct BasePoint3D {
   BasePoint3D(T aX, T aY, T aZ) : x(aX), y(aY), z(aZ) {}
 
   void MoveTo(T aX, T aY, T aZ) { x = aX; y = aY; z = aZ; }
   void MoveBy(T aDx, T aDy, T aDz) { x += aDx; y += aDy; z += aDz; }
 
   // Note that '=' isn't defined so we'll get the
   // compiler generated default assignment operator
 
+  T& operator[](int aIndex) {
+    NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 2, "Invalid array index");
+    return *((&x)+aIndex);
+  }
+
+  const T& operator[](int aIndex) const {
+    NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 2, "Invalid array index");
+    return *((&x)+aIndex);
+  }
+
   bool operator==(const Sub& aPoint) const {
     return x == aPoint.x && y == aPoint.y && z == aPoint.z;
   }
   bool operator!=(const Sub& aPoint) const {
     return x != aPoint.x || y != aPoint.y || z != aPoint.z;
   }
 
   Sub operator+(const Sub& aPoint) const {
new file mode 100644
--- /dev/null
+++ b/gfx/2d/BasePoint4D.h
@@ -0,0 +1,154 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Matt Woodrow <mwoodrow@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef MOZILLA_BASEPOINT4D_H_
+#define MOZILLA_BASEPOINT4D_H_
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Do not use this class directly. Subclass it, pass that subclass as the
+ * Sub parameter, and only use that subclass. This allows methods to safely
+ * cast 'this' to 'Sub*'.
+ */
+template <class T, class Sub>
+struct BasePoint4D {
+  T x, y, z, w;
+
+  // Constructors
+  BasePoint4D() : x(0), y(0), z(0), w(0) {}
+  BasePoint4D(T aX, T aY, T aZ, T aW) : x(aX), y(aY), z(aZ), w(aW) {}
+
+  void MoveTo(T aX, T aY, T aZ, T aW) { x = aX; y = aY; z = aZ; w = aW; }
+  void MoveBy(T aDx, T aDy, T aDz, T aDw) { x += aDx; y += aDy; z += aDz; w += aDw; }
+
+  // Note that '=' isn't defined so we'll get the
+  // compiler generated default assignment operator
+
+  bool operator==(const Sub& aPoint) const {
+    return x == aPoint.x && y == aPoint.y && 
+           z == aPoint.z && w == aPoint.w;
+  }
+  bool operator!=(const Sub& aPoint) const {
+    return x != aPoint.x || y != aPoint.y || 
+           z != aPoint.z || w != aPoint.w;
+  }
+
+  Sub operator+(const Sub& aPoint) const {
+    return Sub(x + aPoint.x, y + aPoint.y, z + aPoint.z, w + aPoint.w);
+  }
+  Sub operator-(const Sub& aPoint) const {
+    return Sub(x - aPoint.x, y - aPoint.y, z - aPoint.z, w - aPoint.w);
+  }
+  Sub& operator+=(const Sub& aPoint) {
+    x += aPoint.x;
+    y += aPoint.y;
+    z += aPoint.z;
+    w += aPoint.w;
+    return *static_cast<Sub*>(this);
+  }
+  Sub& operator-=(const Sub& aPoint) {
+    x -= aPoint.x;
+    y -= aPoint.y;
+    z -= aPoint.z;
+    w -= aPoint.w;
+    return *static_cast<Sub*>(this);
+  }
+
+  Sub operator*(T aScale) const {
+    return Sub(x * aScale, y * aScale, z * aScale, w * aScale);
+  }
+  Sub operator/(T aScale) const {
+    return Sub(x / aScale, y / aScale, z / aScale, w / aScale);
+  }
+
+  Sub& operator*=(T aScale) {
+    x *= aScale;
+    y *= aScale;
+    z *= aScale;
+    w *= aScale;
+    return *static_cast<Sub*>(this);
+  }
+
+  Sub& operator/=(T aScale) {
+    x /= aScale;
+    y /= aScale;
+    z /= aScale;
+    w /= aScale;
+    return *static_cast<Sub*>(this);
+  }
+
+  Sub operator-() const {
+    return Sub(-x, -y, -z, -w);
+  }
+
+  T& operator[](int aIndex) {
+    NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 3, "Invalid array index");
+    return *((&x)+aIndex);
+  }
+
+  const T& operator[](int aIndex) const {
+    NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 3, "Invalid array index");
+    return *((&x)+aIndex);
+  }
+
+  T DotProduct(const Sub& aPoint) const {
+    return x * aPoint.x + y * aPoint.y + z * aPoint.z + w * aPoint.w;
+  }
+
+  // Ignores the 4th component!
+  Sub CrossProduct(const Sub& aPoint) const {
+      return Sub(y * aPoint.z - aPoint.y * z,
+          z * aPoint.x - aPoint.z * x,
+          x * aPoint.y - aPoint.x * y, 
+          0);
+  }
+
+  T Length() const {
+    return sqrt(x*x + y*y + z*z + w*w);
+  }
+
+  void Normalize() {
+    *this /= Length();
+  }
+};
+
+}
+}
+
+#endif /* MOZILLA_BASEPOINT4D_H_ */
--- a/gfx/2d/Makefile.in
+++ b/gfx/2d/Makefile.in
@@ -46,17 +46,18 @@ MODULE		= gfx2d
 LIBRARY_NAME	= gfx2d
 LIBXUL_LIBRARY	= 1
 EXPORT_LIBRARY	= 1
 
 EXPORTS_NAMESPACES = mozilla/gfx
 EXPORTS_mozilla/gfx	= \
         2D.h \
         BasePoint.h \
-	BasePoint3D.h \
+        BasePoint3D.h \
+        BasePoint4D.h \
         BaseMargin.h \
         BaseRect.h \
         BaseSize.h \
         Point.h \
         Matrix.h \
         Rect.h \
         Types.h \
 	$(NULL)
--- a/gfx/thebes/Makefile.in
+++ b/gfx/thebes/Makefile.in
@@ -28,16 +28,18 @@ EXPORTS	= \
 	gfxFontTest.h \
 	gfxImageSurface.h \
 	gfxMatrix.h \
 	gfxPath.h \
 	gfxPattern.h \
 	gfxPlatform.h \
 	gfxPoint.h \
 	gfxPoint3D.h \
+	gfxPointH3D.h \
+	gfxQuaternion.h \
 	gfxRect.h \
 	gfxSkipChars.h \
 	gfxTeeSurface.h \
 	gfxTypes.h \
 	gfxTextRunCache.h \
 	gfxTextRunWordCache.h \
 	gfxUnicodeProperties.h \
 	gfxUtils.h \
--- a/gfx/thebes/gfx3DMatrix.cpp
+++ b/gfx/thebes/gfx3DMatrix.cpp
@@ -131,16 +131,51 @@ PRBool
 gfx3DMatrix::IsIdentity() const
 {
   return _11 == 1.0f && _12 == 0.0f && _13 == 0.0f && _14 == 0.0f &&
          _21 == 0.0f && _22 == 1.0f && _23 == 0.0f && _24 == 0.0f &&
          _31 == 0.0f && _32 == 0.0f && _33 == 1.0f && _34 == 0.0f &&
          _41 == 0.0f && _42 == 0.0f && _43 == 0.0f && _44 == 1.0f;
 }
 
+void
+gfx3DMatrix::Translate(const gfxPoint3D& aPoint)
+{
+    _41 += aPoint.x * _11 + aPoint.y * _21 + aPoint.z * _31;
+    _42 += aPoint.x * _12 + aPoint.y * _22 + aPoint.z * _32;
+    _43 += aPoint.x * _13 + aPoint.y * _23 + aPoint.z * _33;
+    _44 += aPoint.x * _14 + aPoint.y * _24 + aPoint.z * _34;
+}
+
+void
+gfx3DMatrix::SkewXY(float aSkew)
+{
+    (*this)[1] += (*this)[0] * aSkew;
+}
+
+void 
+gfx3DMatrix::SkewXZ(float aSkew)
+{
+    (*this)[2] += (*this)[0] * aSkew;
+}
+
+void
+gfx3DMatrix::SkewYZ(float aSkew)
+{
+    (*this)[2] += (*this)[1] * aSkew;
+}
+
+void
+gfx3DMatrix::Scale(float aX, float aY, float aZ)
+{
+    (*this)[0] *= aX;
+    (*this)[1] *= aY;
+    (*this)[2] *= aZ;
+}
+
 gfx3DMatrix
 gfx3DMatrix::Translation(float aX, float aY, float aZ)
 {
   gfx3DMatrix matrix;
 
   matrix._41 = aX;
   matrix._42 = aY;
   matrix._43 = aZ;
@@ -154,26 +189,26 @@ gfx3DMatrix::Translation(const gfxPoint3
 
   matrix._41 = aPoint.x;
   matrix._42 = aPoint.y;
   matrix._43 = aPoint.z;
   return matrix;
 }
 
 gfx3DMatrix
-gfx3DMatrix::Scale(float aFactor)
+gfx3DMatrix::ScalingMatrix(float aFactor)
 {
   gfx3DMatrix matrix;
 
   matrix._11 = matrix._22 = matrix._33 = aFactor;
   return matrix;
 }
 
 gfx3DMatrix
-gfx3DMatrix::Scale(float aX, float aY, float aZ)
+gfx3DMatrix::ScalingMatrix(float aX, float aY, float aZ)
 {
   gfx3DMatrix matrix;
 
   matrix._11 = aX;
   matrix._22 = aY;
   matrix._33 = aZ;
 
   return matrix;
@@ -203,83 +238,158 @@ gfx3DMatrix::Determinant() const
        - _13 * _22 * _31 * _44
        + _12 * _23 * _31 * _44
        + _13 * _21 * _32 * _44
        - _11 * _23 * _32 * _44
        - _12 * _21 * _33 * _44
        + _11 * _22 * _33 * _44;
 }
 
+gfxFloat
+gfx3DMatrix::Determinant3x3() const
+{
+    return _11 * (_22 * _33 - _23 * _32) +
+           _12 * (_23 * _31 - _33 * _21) +
+           _13 * (_21 * _32 - _22 * _31);
+}
+
+gfx3DMatrix
+gfx3DMatrix::Inverse3x3() const
+{
+    gfxFloat det = Determinant3x3();
+    if (det == 0.0) {
+        return *this;
+    }
+
+    gfxFloat detInv = 1/det;
+    gfx3DMatrix temp;
+
+    temp._11 = (_22 * _33 - _23 * _32) * detInv;
+    temp._12 = (_13 * _32 - _12 * _33) * detInv;
+    temp._13 = (_12 * _23 - _13 * _22) * detInv;
+    temp._21 = (_23 * _31 - _33 * _21) * detInv;
+    temp._22 = (_11 * _33 - _13 * _31) * detInv;
+    temp._23 = (_13 * _21 - _11 * _23) * detInv;
+    temp._31 = (_21 * _32 - _22 * _31) * detInv;
+    temp._32 = (_31 * _12 - _11 * _32) * detInv;
+    temp._33 = (_11 * _22 - _12 * _21) * detInv;
+    return temp;
+}
+
 PRBool
 gfx3DMatrix::IsSingular() const
 {
   return Determinant() == 0.0;
 }
 
-gfx3DMatrix&
-gfx3DMatrix::Invert()
+gfx3DMatrix
+gfx3DMatrix::Inverse() const
 {
+  if (TransposedVector(3) == gfxPointH3D(0, 0, 0, 1)) {
+    /** 
+     * When the matrix contains no perspective, the inverse
+     * is the same as the 3x3 inverse of the rotation components
+     * multiplied by the inverse of the translation components.
+     * Doing these steps separately is faster and more numerically
+     * stable.
+     *
+     * Inverse of the translation matrix is just negating
+     * the values.
+     */
+    gfx3DMatrix matrix3 = Inverse3x3();
+    matrix3.Translate(gfxPoint3D(-_41, -_42, -_43));
+    return matrix3;
+ }
+
   gfxFloat det = Determinant();
   if (det == 0.0) {
     return *this;
   }
 
-  gfx3DMatrix temp = *this;
+  gfx3DMatrix temp;
 
-  _11 = temp._23*temp._34*temp._42 - temp._24*temp._33*temp._42 
-      + temp._24*temp._32*temp._43 - temp._22*temp._34*temp._43 
-      - temp._23*temp._32*temp._44 + temp._22*temp._33*temp._44;
-  _12 = temp._14*temp._33*temp._42 - temp._13*temp._34*temp._42 
-      - temp._14*temp._32*temp._43 + temp._12*temp._34*temp._43 
-      + temp._13*temp._32*temp._44 - temp._12*temp._33*temp._44;
-  _13 = temp._13*temp._24*temp._42 - temp._14*temp._23*temp._42 
-      + temp._14*temp._22*temp._43 - temp._12*temp._24*temp._43 
-      - temp._13*temp._22*temp._44 + temp._12*temp._23*temp._44;
-  _14 = temp._14*temp._23*temp._32 - temp._13*temp._24*temp._32 
-      - temp._14*temp._22*temp._33 + temp._12*temp._24*temp._33 
-      + temp._13*temp._22*temp._34 - temp._12*temp._23*temp._34;
-  _21 = temp._24*temp._33*temp._41 - temp._23*temp._34*temp._41 
-      - temp._24*temp._31*temp._43 + temp._21*temp._34*temp._43 
-      + temp._23*temp._31*temp._44 - temp._21*temp._33*temp._44;
-  _22 = temp._13*temp._34*temp._41 - temp._14*temp._33*temp._41 
-      + temp._14*temp._31*temp._43 - temp._11*temp._34*temp._43 
-      - temp._13*temp._31*temp._44 + temp._11*temp._33*temp._44;
-  _23 = temp._14*temp._23*temp._41 - temp._13*temp._24*temp._41 
-      - temp._14*temp._21*temp._43 + temp._11*temp._24*temp._43 
-      + temp._13*temp._21*temp._44 - temp._11*temp._23*temp._44;
-  _24 = temp._13*temp._24*temp._31 - temp._14*temp._23*temp._31 
-      + temp._14*temp._21*temp._33 - temp._11*temp._24*temp._33 
-      - temp._13*temp._21*temp._34 + temp._11*temp._23*temp._34;
-  _31 = temp._22*temp._34*temp._41 - temp._24*temp._32*temp._41 
-      + temp._24*temp._31*temp._42 - temp._21*temp._34*temp._42 
-      - temp._22*temp._31*temp._44 + temp._21*temp._32*temp._44;
-  _32 = temp._14*temp._32*temp._41 - temp._12*temp._34*temp._41 
-      - temp._14*temp._31*temp._42 + temp._11*temp._34*temp._42 
-      + temp._12*temp._31*temp._44 - temp._11*temp._32*temp._44;
-  _33 = temp._12*temp._24*temp._41 - temp._14*temp._22*temp._41 
-      + temp._14*temp._21*temp._42 - temp._11*temp._24*temp._42 
-      - temp._12*temp._21*temp._44 + temp._11*temp._22*temp._44;
-  _34 = temp._14*temp._22*temp._31 - temp._12*temp._24*temp._31 
-      - temp._14*temp._21*temp._32 + temp._11*temp._24*temp._32 
-      + temp._12*temp._21*temp._34 - temp._11*temp._22*temp._34;
-  _41 = temp._23*temp._32*temp._41 - temp._22*temp._33*temp._41 
-      - temp._23*temp._31*temp._42 + temp._21*temp._33*temp._42 
-      + temp._22*temp._31*temp._43 - temp._21*temp._32*temp._43;
-  _42 = temp._12*temp._33*temp._41 - temp._13*temp._32*temp._41 
-      + temp._13*temp._31*temp._42 - temp._11*temp._33*temp._42 
-      - temp._12*temp._31*temp._43 + temp._11*temp._32*temp._43;
-  _43 = temp._13*temp._22*temp._41 - temp._12*temp._23*temp._41 
-      - temp._13*temp._21*temp._42 + temp._11*temp._23*temp._42 
-      + temp._12*temp._21*temp._43 - temp._11*temp._22*temp._43;
-  _44 = temp._12*temp._23*temp._31 - temp._13*temp._22*temp._31 
-      + temp._13*temp._21*temp._32 - temp._11*temp._23*temp._32 
-      - temp._12*temp._21*temp._33 + temp._11*temp._22*temp._33;
+  temp._11 = _23*_34*_42 - _24*_33*_42 + 
+             _24*_32*_43 - _22*_34*_43 - 
+             _23*_32*_44 + _22*_33*_44;
+  temp._12 = _14*_33*_42 - _13*_34*_42 -
+             _14*_32*_43 + _12*_34*_43 +
+             _13*_32*_44 - _12*_33*_44;
+  temp._13 = _13*_24*_42 - _14*_23*_42 +
+             _14*_22*_43 - _12*_24*_43 -
+             _13*_22*_44 + _12*_23*_44;
+  temp._14 = _14*_23*_32 - _13*_24*_32 -
+             _14*_22*_33 + _12*_24*_33 +
+             _13*_22*_34 - _12*_23*_34;
+  temp._21 = _24*_33*_41 - _23*_34*_41 -
+             _24*_31*_43 + _21*_34*_43 +
+             _23*_31*_44 - _21*_33*_44;
+  temp._22 = _13*_34*_41 - _14*_33*_41 +
+             _14*_31*_43 - _11*_34*_43 -
+             _13*_31*_44 + _11*_33*_44;
+  temp._23 = _14*_23*_41 - _13*_24*_41 -
+             _14*_21*_43 + _11*_24*_43 +
+             _13*_21*_44 - _11*_23*_44;
+  temp._24 = _13*_24*_31 - _14*_23*_31 +
+             _14*_21*_33 - _11*_24*_33 -
+             _13*_21*_34 + _11*_23*_34;
+  temp._31 = _22*_34*_41 - _24*_32*_41 +
+             _24*_31*_42 - _21*_34*_42 -
+             _22*_31*_44 + _21*_32*_44;
+  temp._32 = _14*_32*_41 - _12*_34*_41 -
+             _14*_31*_42 + _11*_34*_42 +
+             _12*_31*_44 - _11*_32*_44;
+  temp._33 = _12*_24*_41 - _14*_22*_41 +
+             _14*_21*_42 - _11*_24*_42 -
+             _12*_21*_44 + _11*_22*_44;
+  temp._34 = _14*_22*_31 - _12*_24*_31 -
+             _14*_21*_32 + _11*_24*_32 +
+             _12*_21*_34 - _11*_22*_34;
+  temp._41 = _23*_32*_41 - _22*_33*_41 -
+             _23*_31*_42 + _21*_33*_42 +
+             _22*_31*_43 - _21*_32*_43;
+  temp._42 = _12*_33*_41 - _13*_32*_41 +
+             _13*_31*_42 - _11*_33*_42 -
+             _12*_31*_43 + _11*_32*_43;
+  temp._43 = _13*_22*_41 - _12*_23*_41 -
+             _13*_21*_42 + _11*_23*_42 +
+             _12*_21*_43 - _11*_22*_43;
+  temp._44 = _12*_23*_31 - _13*_22*_31 +
+             _13*_21*_32 - _11*_23*_32 -
+             _12*_21*_33 + _11*_22*_33;
 
-  *this /= det;
-  return *this;
+  temp /= det;
+  return temp;
+}
+
+gfx3DMatrix&
+gfx3DMatrix::Normalize()
+{
+    for (int i = 0; i < 4; i++) {
+        for (int j = 0; j < 4; j++) {
+            (*this)[i][j] /= (*this)[3][3];
+       }
+    }
+    return *this;
+}
+
+gfx3DMatrix&
+gfx3DMatrix::Transpose()
+{
+    *this = Transposed();
+    return *this;
+}
+
+gfx3DMatrix
+gfx3DMatrix::Transposed() const
+{
+    gfx3DMatrix temp;
+    for (int i = 0; i < 4; i++) {
+        temp[i] = TransposedVector(i);
+    }
+    return temp;
 }
 
 gfxPoint
 gfx3DMatrix::Transform(const gfxPoint& point) const
 {
   gfxPoint3D vec3d(point.x, point.y, 0);
   vec3d = Transform3D(vec3d);
   return gfxPoint(vec3d.x, vec3d.y);
@@ -295,16 +405,38 @@ gfx3DMatrix::Transform3D(const gfxPoint3
 
   x /= w;
   y /= w;
   z /= w;
 
   return gfxPoint3D(x, y, z);
 }
 
+gfxPointH3D
+gfx3DMatrix::Transform4D(const gfxPointH3D& aPoint) const
+{
+    gfxFloat x = aPoint.x * _11 + aPoint.y * _21 + aPoint.z * _31 + aPoint.w * _41;
+    gfxFloat y = aPoint.x * _12 + aPoint.y * _22 + aPoint.z * _32 + aPoint.w * _42;
+    gfxFloat z = aPoint.x * _13 + aPoint.y * _23 + aPoint.z * _33 + aPoint.w * _43;
+    gfxFloat w = aPoint.x * _14 + aPoint.y * _24 + aPoint.z * _34 + aPoint.w * _44;
+
+    return gfxPointH3D(x, y, z, w);
+}
+
+gfxPointH3D
+gfx3DMatrix::TransposeTransform4D(const gfxPointH3D& aPoint) const
+{
+    gfxFloat x = aPoint.x * _11 + aPoint.y * _12 + aPoint.z * _13 + aPoint.w * _14;
+    gfxFloat y = aPoint.x * _21 + aPoint.y * _22 + aPoint.z * _23 + aPoint.w * _24;
+    gfxFloat z = aPoint.x * _31 + aPoint.y * _32 + aPoint.z * _33 + aPoint.w * _34;
+    gfxFloat w = aPoint.x * _41 + aPoint.y * _42 + aPoint.z * _43 + aPoint.w * _44;
+
+    return gfxPointH3D(x, y, z, w);
+}
+
 gfxRect
 gfx3DMatrix::TransformBounds(const gfxRect& rect) const
 {
   gfxPoint points[4];
 
   points[0] = Transform(rect.TopLeft());
   points[1] = Transform(gfxPoint(rect.X() + rect.Width(), rect.Y()));
   points[2] = Transform(gfxPoint(rect.X(), rect.Y() + rect.Height()));
--- a/gfx/thebes/gfx3DMatrix.h
+++ b/gfx/thebes/gfx3DMatrix.h
@@ -36,16 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef GFX_3DMATRIX_H
 #define GFX_3DMATRIX_H
 
 #include <gfxTypes.h>
 #include <gfxPoint3D.h>
+#include <gfxPointH3D.h>
 #include <gfxMatrix.h>
 
 /**
  * This class represents a 3D transformation. The matrix is laid
  * out as follows:
  *
  * _11 _12 _13 _14
  * _21 _22 _23 _24
@@ -67,16 +68,27 @@ public:
   gfx3DMatrix(void);
 
   /**
    * Matrix multiplication.
    */
   gfx3DMatrix operator*(const gfx3DMatrix &aMatrix) const;
   gfx3DMatrix& operator*=(const gfx3DMatrix &aMatrix);
 
+  gfxPointH3D& operator[](int aIndex)
+  {
+      NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
+      return *reinterpret_cast<gfxPointH3D*>((&_11)+4*aIndex);
+  }
+  const gfxPointH3D& operator[](int aIndex) const
+  {
+      NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
+      return *reinterpret_cast<const gfxPointH3D*>((&_11)+4*aIndex);
+  }
+
   /**
    * Return true if this matrix and |aMatrix| are the same matrix.
    */
   bool operator==(const gfx3DMatrix& aMatrix) const;
   
   /**
    * Divide all values in the matrix by a scalar value
    */
@@ -109,47 +121,81 @@ public:
 
   /**
    * Returns true if the matrix is the identity matrix. The most important
    * property we require is that gfx3DMatrix().IsIdentity() returns true.
    */
   PRBool IsIdentity() const;
 
   /**
+   * Add a translation by aPoint to the matrix.
+   * This is functionally equivalent to:
+   * gfx3DMatrix::Translation(aPoint) * matrix
+   */
+  void Translate(const gfxPoint3D& aPoint);
+
+  void SkewXY(float aSkew);
+  void SkewXZ(float aSkew);
+  void SkewYZ(float aSkew);
+
+  void Scale(float aX, float aY, float aZ);
+
+  /**
    * Transforms a point according to this matrix.
    */
   gfxPoint Transform(const gfxPoint& point) const;
 
   /**
    * Transforms a rectangle according to this matrix
    */
   gfxRect TransformBounds(const gfxRect& rect) const;
 
   /** 
    * Transforms a 3D vector according to this matrix.
    */
   gfxPoint3D Transform3D(const gfxPoint3D& point) const;
+  gfxPointH3D Transform4D(const gfxPointH3D& aPoint) const;
+  gfxPointH3D TransposeTransform4D(const gfxPointH3D& aPoint) const;
 
   gfxPoint ProjectPoint(const gfxPoint& aPoint) const;
   gfxRect ProjectRectBounds(const gfxRect& aRect) const;
 
 
   /**
    * Inverts this matrix, if possible. Otherwise, the matrix is left
    * unchanged.
    */
-  gfx3DMatrix& Invert();
+  gfx3DMatrix Inverse() const;
 
-  inline gfx3DMatrix Inverse() const
+  gfx3DMatrix& Invert()
+  {
+      *this = Inverse();
+      return *this;
+  }
+
+  gfx3DMatrix& Normalize();
+
+  gfxPointH3D TransposedVector(int aIndex) const
   {
-    gfx3DMatrix temp = *this;
-    temp.Invert();
-    return temp;
+      NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
+      return gfxPointH3D(*((&_11)+aIndex), *((&_21)+aIndex), *((&_31)+aIndex), *((&_41)+aIndex));
   }
 
+  void SetTransposedVector(int aIndex, gfxPointH3D &aVector)
+  {
+      NS_ABORT_IF_FALSE(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
+      *((&_11)+aIndex) = aVector.x;
+      *((&_21)+aIndex) = aVector.y;
+      *((&_31)+aIndex) = aVector.z;
+      *((&_41)+aIndex) = aVector.w;
+  }
+
+  gfx3DMatrix& Transpose();
+  gfx3DMatrix Transposed() const;
+
   /**
    * Returns a unit vector that is perpendicular to the plane formed
    * by transform the screen plane (z=0) by this matrix.
    */
   gfxPoint3D GetNormalVector() const;
 
   /**
    * Check if matrix is singular (no inverse exists).
@@ -166,26 +212,29 @@ public:
   static gfx3DMatrix Translation(float aX, float aY, float aZ);
   static gfx3DMatrix Translation(const gfxPoint3D& aPoint);
 
   /**
    * Create a scale matrix. Scales uniformly along all axes.
    *
    * \param aScale Scale factor
    */
-  static gfx3DMatrix Scale(float aFactor);
+  static gfx3DMatrix ScalingMatrix(float aFactor);
 
   /**
    * Create a scale matrix.
    */
-  static gfx3DMatrix Scale(float aX, float aY, float aZ);
+  static gfx3DMatrix ScalingMatrix(float aX, float aY, float aZ);
+
+  gfxFloat Determinant() const;
 
 private:
 
-  gfxFloat Determinant() const;
+  gfxFloat Determinant3x3() const;
+  gfx3DMatrix Inverse3x3() const;
 
 public:
 
   /** Matrix elements */
   float _11, _12, _13, _14;
   float _21, _22, _23, _24;
   float _31, _32, _33, _34;
   float _41, _42, _43, _44;
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxPointH3D.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Oracle Corporation code.
+ *
+ * The Initial Developer of the Original Code is Oracle Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Matt Woodrow <mwoodrow@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef GFX_POINTH3D_H
+#define GFX_POINTH3D_H
+
+#include "mozilla/gfx/BasePoint4D.h"
+#include "gfxTypes.h"
+
+struct THEBES_API gfxPointH3D : public mozilla::gfx::BasePoint4D<float, gfxPointH3D> {
+    typedef mozilla::gfx::BasePoint4D<float, gfxPointH3D> Super;
+
+    gfxPointH3D() : Super() {}
+    gfxPointH3D(float aX, float aY, float aZ, float aW) : Super(aX, aY, aZ, aW) {}
+};
+
+#endif /* GFX_POINTH3D_H */ 
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxQuaternion.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Matt Woodrow <mwoodrow@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef GFX_QUATERNION_H
+#define GFX_QUATERNION_H
+
+#include "mozilla/gfx/BasePoint4D.h"
+#include "gfx3DMatrix.h"
+
+struct THEBES_API gfxQuaternion : public mozilla::gfx::BasePoint4D<gfxFloat, gfxQuaternion> {
+    typedef mozilla::gfx::BasePoint4D<gfxFloat, gfxQuaternion> Super;
+
+    gfxQuaternion() : Super() {}
+    gfxQuaternion(gfxFloat aX, gfxFloat aY, gfxFloat aZ, gfxFloat aW) : Super(aX, aY, aZ, aW) {}
+
+    gfxQuaternion(const gfx3DMatrix& aMatrix) {
+        w = 0.5 * sqrt(NS_MAX(1 + aMatrix[0][0] + aMatrix[1][1] + aMatrix[2][2], 0.0f));
+        x = 0.5 * sqrt(NS_MAX(1 + aMatrix[0][0] - aMatrix[1][1] - aMatrix[2][2], 0.0f));
+        y = 0.5 * sqrt(NS_MAX(1 - aMatrix[0][0] + aMatrix[1][1] - aMatrix[2][2], 0.0f));
+        z = 0.5 * sqrt(NS_MAX(1 - aMatrix[0][0] - aMatrix[1][1] + aMatrix[2][2], 0.0f));
+
+        if(aMatrix[2][1] > aMatrix[1][2])
+            x = -x;
+        if(aMatrix[0][2] > aMatrix[2][0])
+            y = -y;
+        if(aMatrix[1][0] > aMatrix[0][1])
+            z = -z;
+    }
+
+    gfxQuaternion Slerp(const gfxQuaternion &aOther, gfxFloat aCoeff) {
+        gfxFloat dot = NS_CLAMP(DotProduct(aOther), -1.0, 1.0);
+        if (dot == 1.0) {
+            return *this;
+        }
+
+        gfxFloat theta = acos(dot);
+        gfxFloat rsintheta = 1/sqrt(1 - dot*dot);
+        gfxFloat w = sin(aCoeff*theta)*rsintheta;
+
+        gfxQuaternion left = *this;
+        gfxQuaternion right = aOther;
+
+        left *= cos(aCoeff*theta) - dot*w;
+        right *= w;
+
+        return left + right;
+    }
+
+    gfx3DMatrix ToMatrix() {
+        gfx3DMatrix temp;
+
+        temp[0][0] = 1 - 2 * (y * y + z * z);
+        temp[0][1] = 2 * (x * y + w * z);
+        temp[0][2] = 2 * (x * z - w * y);
+        temp[1][0] = 2 * (x * y - w * z);
+        temp[1][1] = 1 - 2 * (x * x + z * z);
+        temp[1][2] = 2 * (y * z + w * x);
+        temp[2][0] = 2 * (x * z + w * y);
+        temp[2][1] = 2 * (y * z - w * x);
+        temp[2][2] = 1 - 2 * (x * x + y * y);
+
+        return temp;
+    }
+
+};
+
+#endif /* GFX_QUATERNION_H */
\ No newline at end of file
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -1407,17 +1407,17 @@ ContainerState::ProcessDisplayItems(cons
         continue;
       }
 
       // If it's not a ContainerLayer, we need to apply the scale transform
       // ourselves.
       if (!ownLayer->AsContainerLayer()) {
         // The layer's current transform is applied first, then the result is scaled.
         gfx3DMatrix transform = ownLayer->GetTransform()*
-            gfx3DMatrix::Scale(mParameters.mXScale, mParameters.mYScale, 1.0f);
+            gfx3DMatrix::ScalingMatrix(mParameters.mXScale, mParameters.mYScale, 1.0f);
         ownLayer->SetTransform(transform);
       }
 
       ownLayer->SetIsFixedPosition(!nsLayoutUtils::ScrolledByViewportScrolling(
                                       activeScrolledRoot, mBuilder));
 
       // Update that layer's clip and visible rects.
       NS_ASSERTION(ownLayer->Manager() == mManager, "Wrong manager");
@@ -1672,17 +1672,17 @@ ContainerState::Finish(PRUint32* aTextCo
 static FrameLayerBuilder::ContainerParameters
 ChooseScaleAndSetTransform(FrameLayerBuilder* aLayerBuilder,
                            nsIFrame* aContainerFrame,
                            const gfx3DMatrix* aTransform,
                            const FrameLayerBuilder::ContainerParameters& aIncomingScale,
                            ContainerLayer* aLayer)
 {
   gfx3DMatrix transform =
-    gfx3DMatrix::Scale(aIncomingScale.mXScale, aIncomingScale.mYScale, 1.0);
+    gfx3DMatrix::ScalingMatrix(aIncomingScale.mXScale, aIncomingScale.mYScale, 1.0);
   if (aTransform) {
     // aTransform is applied first, then the scale is applied to the result
     transform = (*aTransform)*transform;
   }
 
   gfxMatrix transform2d;
   gfxSize scale;
   // Only fiddle with scale factors for the retaining layer manager, since
@@ -1711,17 +1711,17 @@ ChooseScaleAndSetTransform(FrameLayerBui
     if (fabs(scale.width) < 1e-8 || fabs(scale.height) < 1e-8) {
       scale.width = scale.height = 1.0;
     }
   } else {
     scale = gfxSize(1.0, 1.0);
   }
 
   // Apply the inverse of our resolution-scale before the rest of our transform
-  transform = gfx3DMatrix::Scale(1.0/scale.width, 1.0/scale.height, 1.0)*transform;
+  transform = gfx3DMatrix::ScalingMatrix(1.0/scale.width, 1.0/scale.height, 1.0)*transform;
   aLayer->SetTransform(transform);
 
   FrameLayerBuilder::ContainerParameters
     result(scale.width, scale.height, aIncomingScale);
   if (aTransform) {
     result.mInTransformedSubtree = true;
     if (aContainerFrame->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer)) {
       result.mInActiveTransformedSubtree = true;
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -595,18 +595,18 @@ void nsDisplayList::PaintForFrame(nsDisp
     (presShell->GetXResolution(), presShell->GetYResolution());
   nsRefPtr<ContainerLayer> root = aBuilder->LayerBuilder()->
     BuildContainerLayerFor(aBuilder, layerManager, aForFrame, nsnull, *this,
                            containerParameters, nsnull);
   if (!root)
     return;
   // Root is being scaled up by the X/Y resolution. Scale it back down.
   gfx3DMatrix rootTransform = root->GetTransform()*
-    gfx3DMatrix::Scale(1.0f/containerParameters.mXScale,
-                       1.0f/containerParameters.mYScale, 1.0f);
+    gfx3DMatrix::ScalingMatrix(1.0f/containerParameters.mXScale,
+                               1.0f/containerParameters.mYScale, 1.0f);
   root->SetTransform(rootTransform);
 
   ViewID id = presContext->IsRootContentDocument() ? FrameMetrics::ROOT_SCROLL_ID
                                                    : FrameMetrics::NULL_SCROLL_ID;
 
   nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
   nsRect displayport;
   bool usingDisplayport = false;
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -90,17 +90,17 @@ struct ViewTransform {
     : mTranslation(aTranslation)
     , mXScale(aXScale)
     , mYScale(aYScale)
   {}
 
   operator gfx3DMatrix() const
   {
     return
-      gfx3DMatrix::Scale(mXScale, mYScale, 1) *
+      gfx3DMatrix::ScalingMatrix(mXScale, mYScale, 1) *
       gfx3DMatrix::Translation(mTranslation.x, mTranslation.y, 0);
   }
 
   nsIntPoint mTranslation;
   float mXScale;
   float mYScale;
 };
 
--- a/xpcom/string/public/nsAlgorithm.h
+++ b/xpcom/string/public/nsAlgorithm.h
@@ -66,16 +66,17 @@ NS_ROUNDUP( const T& a, const T& b )
 template <class T>
 inline
 const T&
 NS_MIN( const T& a, const T& b )
   {
     return b < a ? b : a;
   }
 
+// Must return b when a == b in case a is -0
 template <class T>
 inline
 const T&
 NS_MAX( const T& a, const T& b )
   {
     return a > b ? a : b;
   }