Bug 1181240 - Part 1: Copy methods from gfx3DMatrix,r=vlad

- Copied methods from gfx3DMatrix to Matrix4x4, gfxPoint, and gfxRect that
  with at double-precision floating point.
This commit is contained in:
Kearwood (Kip) Gilbert 2015-07-09 16:27:38 -07:00
parent c1614a1621
commit 60dfc52e22
7 changed files with 317 additions and 0 deletions

View File

@ -9,11 +9,56 @@
#include <algorithm>
#include <ostream>
#include <math.h>
#include <float.h> // for FLT_EPSILON
#include "mozilla/FloatingPoint.h" // for UnspecifiedNaN
using namespace std;
namespace {
/* Force small values to zero. We do this to avoid having sin(360deg)
* evaluate to a tiny but nonzero value.
*/
double
FlushToZero(double aVal)
{
// XXX Is double precision really necessary here
if (-FLT_EPSILON < aVal && aVal < FLT_EPSILON) {
return 0.0f;
} else {
return aVal;
}
}
/* Computes tan(aTheta). For values of aTheta such that tan(aTheta) is
* undefined or very large, SafeTangent returns a manageably large value
* of the correct sign.
*/
double
SafeTangent(double aTheta)
{
// XXX Is double precision really necessary here
const double kEpsilon = 0.0001;
/* tan(theta) = sin(theta)/cos(theta); problems arise when
* cos(theta) is too close to zero. Limit cos(theta) to the
* range [-1, -epsilon] U [epsilon, 1].
*/
double sinTheta = sin(aTheta);
double cosTheta = cos(aTheta);
if (cosTheta >= 0 && cosTheta < kEpsilon) {
cosTheta = kEpsilon;
} else if (cosTheta < 0 && cosTheta >= -kEpsilon) {
cosTheta = -kEpsilon;
}
return FlushToZero(sinTheta / cosTheta);
}
} // namespace
namespace mozilla {
namespace gfx {
@ -397,5 +442,134 @@ Matrix4x4::SetRotationFromQuaternion(const Quaternion& q)
_44 = 1.0f;
}
void
Matrix4x4::SkewXY(double aXSkew, double aYSkew)
{
// XXX Is double precision really necessary here
float tanX = SafeTangent(aXSkew);
float tanY = SafeTangent(aYSkew);
float temp;
temp = _11;
_11 += tanY * _21;
_21 += tanX * temp;
temp = _12;
_12 += tanY * _22;
_22 += tanX * temp;
temp = _13;
_13 += tanY * _23;
_23 += tanX * temp;
temp = _14;
_14 += tanY * _24;
_24 += tanX * temp;
}
void
Matrix4x4::RotateX(double aTheta)
{
// XXX Is double precision really necessary here
double cosTheta = FlushToZero(cos(aTheta));
double sinTheta = FlushToZero(sin(aTheta));
float temp;
temp = _21;
_21 = cosTheta * _21 + sinTheta * _31;
_31 = -sinTheta * temp + cosTheta * _31;
temp = _22;
_22 = cosTheta * _22 + sinTheta * _32;
_32 = -sinTheta * temp + cosTheta * _32;
temp = _23;
_23 = cosTheta * _23 + sinTheta * _33;
_33 = -sinTheta * temp + cosTheta * _33;
temp = _24;
_24 = cosTheta * _24 + sinTheta * _34;
_34 = -sinTheta * temp + cosTheta * _34;
}
void
Matrix4x4::RotateY(double aTheta)
{
// XXX Is double precision really necessary here
double cosTheta = FlushToZero(cos(aTheta));
double sinTheta = FlushToZero(sin(aTheta));
float temp;
temp = _11;
_11 = cosTheta * _11 + -sinTheta * _31;
_31 = sinTheta * temp + cosTheta * _31;
temp = _12;
_12 = cosTheta * _12 + -sinTheta * _32;
_32 = sinTheta * temp + cosTheta * _32;
temp = _13;
_13 = cosTheta * _13 + -sinTheta * _33;
_33 = sinTheta * temp + cosTheta * _33;
temp = _14;
_14 = cosTheta * _14 + -sinTheta * _34;
_34 = sinTheta * temp + cosTheta * _34;
}
void
Matrix4x4::RotateZ(double aTheta)
{
// XXX Is double precision really necessary here
double cosTheta = FlushToZero(cos(aTheta));
double sinTheta = FlushToZero(sin(aTheta));
float temp;
temp = _11;
_11 = cosTheta * _11 + sinTheta * _21;
_21 = -sinTheta * temp + cosTheta * _21;
temp = _12;
_12 = cosTheta * _12 + sinTheta * _22;
_22 = -sinTheta * temp + cosTheta * _22;
temp = _13;
_13 = cosTheta * _13 + sinTheta * _23;
_23 = -sinTheta * temp + cosTheta * _23;
temp = _14;
_14 = cosTheta * _14 + sinTheta * _24;
_24 = -sinTheta * temp + cosTheta * _24;
}
void
Matrix4x4::Perspective(float aDepth)
{
MOZ_ASSERT(aDepth > 0.0f, "Perspective must be positive!");
_31 += -1.0/aDepth * _41;
_32 += -1.0/aDepth * _42;
_33 += -1.0/aDepth * _43;
_34 += -1.0/aDepth * _44;
}
Point3D
Matrix4x4::GetNormalVector() const
{
// Define a plane in transformed space as the transformations
// of 3 points on the z=0 screen plane.
Point3D a = *this * Point3D(0, 0, 0);
Point3D b = *this * Point3D(0, 1, 0);
Point3D c = *this * Point3D(1, 0, 0);
// Convert to two vectors on the surface of the plane.
Point3D ab = b - a;
Point3D ac = c - a;
return ac.CrossProduct(ab);
}
} // namespace gfx
} // namespace mozilla

View File

@ -716,6 +716,11 @@ public:
(*this)[2] += (*this)[1] * aSkew;
}
Matrix4x4 &ChangeBasis(const Point3D& aOrigin)
{
return ChangeBasis(aOrigin.x, aOrigin.y, aOrigin.z);
}
Matrix4x4 &ChangeBasis(Float aX, Float aY, Float aZ)
{
// Translate to the origin before applying this matrix
@ -950,6 +955,18 @@ public:
// Set all the members of the matrix to NaN
void SetNAN();
void SkewXY(double aXSkew, double aYSkew);
void RotateX(double aTheta);
void RotateY(double aTheta);
void RotateZ(double aTheta);
void Perspective(float aDepth);
Point3D GetNormalVector() const;
};
class Matrix5x4

View File

@ -6,6 +6,7 @@
#include "gfxMatrix.h"
#include "cairo.h"
#include "mozilla/gfx/Tools.h"
#include "mozilla/gfx/Matrix.h" // for Matrix4x4
#define CAIRO_MATRIX(x) reinterpret_cast<cairo_matrix_t*>((x))
#define CONST_CAIRO_MATRIX(x) reinterpret_cast<const cairo_matrix_t*>((x))
@ -158,3 +159,31 @@ gfxMatrix::NudgeToIntegers(void)
NudgeToInteger(&_32);
return *this;
}
mozilla::gfx::Matrix4x4
gfxMatrix::operator *(const mozilla::gfx::Matrix4x4& aMatrix) const
{
Matrix4x4 resultMatrix;
resultMatrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21;
resultMatrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22;
resultMatrix._13 = _11 * aMatrix._13 + _12 * aMatrix._23;
resultMatrix._14 = _11 * aMatrix._14 + _12 * aMatrix._24;
resultMatrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21;
resultMatrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22;
resultMatrix._23 = _21 * aMatrix._13 + _22 * aMatrix._23;
resultMatrix._24 = _21 * aMatrix._14 + _22 * aMatrix._24;
resultMatrix._31 = aMatrix._31;
resultMatrix._32 = aMatrix._32;
resultMatrix._33 = aMatrix._33;
resultMatrix._34 = aMatrix._34;
resultMatrix._41 = _31 * aMatrix._11 + _32 * aMatrix._21 + aMatrix._41;
resultMatrix._42 = _31 * aMatrix._12 + _32 * aMatrix._22 + aMatrix._42;
resultMatrix._43 = _31 * aMatrix._13 + _32 * aMatrix._23 + aMatrix._43;
resultMatrix._44 = _31 * aMatrix._14 + _32 * aMatrix._24 + aMatrix._44;
return resultMatrix;
}

View File

@ -11,6 +11,12 @@
#include "gfxRect.h"
#include "mozilla/Attributes.h"
namespace mozilla {
namespace gfx {
class Matrix4x4;
} // namespace gfx
} // namespace mozilla
// XX - I don't think this class should use gfxFloat at all,
// but should use 'double' and be called gfxDoubleMatrix;
// we can then typedef that to gfxMatrix where we typedef
@ -79,6 +85,11 @@ public:
return gfxMatrix(*this) *= m;
}
/**
* Multiplies *this with aMatrix and returns the result.
*/
mozilla::gfx::Matrix4x4 operator * (const mozilla::gfx::Matrix4x4& aMatrix) const;
/* Returns true if the other matrix is fuzzy-equal to this matrix.
* Note that this isn't a cheap comparison!
*/

View File

@ -9,6 +9,7 @@
#include "nsMathUtils.h"
#include "mozilla/gfx/BaseSize.h"
#include "mozilla/gfx/BasePoint.h"
#include "mozilla/gfx/Matrix.h"
#include "nsSize.h"
#include "nsPoint.h"
@ -32,6 +33,20 @@ struct gfxPoint : public mozilla::gfx::BasePoint<gfxFloat, gfxPoint> {
bool WithinEpsilonOf(const gfxPoint& aPoint, gfxFloat aEpsilon) {
return fabs(aPoint.x - x) < aEpsilon && fabs(aPoint.y - y) < aEpsilon;
}
void Transform(const mozilla::gfx::Matrix4x4 &aMatrix)
{
// Transform this point with aMatrix
double px = x;
double py = y;
x = px * aMatrix._11 + py * aMatrix._21 + aMatrix._41;
y = px * aMatrix._12 + py * aMatrix._22 + aMatrix._42;
double w = px * aMatrix._14 + py * aMatrix._24 + aMatrix._44;
x /= w;
y /= w;
}
};
inline gfxPoint

View File

@ -7,6 +7,59 @@
#include "nsMathUtils.h"
#include "mozilla/gfx/Matrix.h"
#include "gfxQuad.h"
gfxQuad
gfxRect::TransformToQuad(const mozilla::gfx::Matrix4x4 &aMatrix) const
{
gfxPoint points[4];
points[0] = TopLeft();
points[1] = TopRight();
points[2] = BottomRight();
points[3] = BottomLeft();
points[0].Transform(aMatrix);
points[1].Transform(aMatrix);
points[2].Transform(aMatrix);
points[3].Transform(aMatrix);
// Could this ever result in lines that intersect? I don't think so.
return gfxQuad(points[0], points[1], points[2], points[3]);
}
void
gfxRect::TransformBounds(const mozilla::gfx::Matrix4x4 &aMatrix)
{
gfxPoint quad[4];
quad[0] = TopLeft();
quad[1] = TopRight();
quad[2] = BottomLeft();
quad[3] = BottomRight();
quad[0].Transform(aMatrix);
double min_x = quad[0].x;
double max_x = quad[0].x;
double min_y = quad[0].y;
double max_y = quad[0].y;
for (int i=1; i<4; i++) {
quad[i].Transform(aMatrix);
min_x = std::min(quad[i].x, min_x);
max_x = std::max(quad[i].x, max_x);
min_y = std::min(quad[i].y, min_y);
max_y = std::max(quad[i].y, max_y);
}
x = min_x;
y = min_y;
width = max_x - min_x;
height = max_y - min_y;
}
static bool
WithinEpsilonOfInteger(gfxFloat aX, gfxFloat aEpsilon)
{

View File

@ -14,6 +14,14 @@
#include "mozilla/gfx/BaseRect.h"
#include "mozilla/Assertions.h"
namespace mozilla {
namespace gfx {
class Matrix4x4;
} // namepsace gfx
} // namespace mozilla
struct gfxQuad;
struct gfxMargin : public mozilla::gfx::BaseMargin<gfxFloat, gfxMargin> {
typedef mozilla::gfx::BaseMargin<gfxFloat, gfxMargin> Super;
@ -137,6 +145,16 @@ struct gfxRect :
width /= k;
height /= k;
}
/*
* Transform this rectangle with aMatrix, resulting in a gfxQuad.
*/
gfxQuad TransformToQuad(const mozilla::gfx::Matrix4x4 &aMatrix) const;
/*
* Transform this rectangle with aMatrix, as an axis-aligned bounding box
*/
void TransformBounds(const mozilla::gfx::Matrix4x4 &aMatrix);
};
#endif /* GFX_RECT_H */