/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */
staticinlinebool FuzzyEqual(Float aV1, Float aV2) { // XXX - Check if fabs does the smart thing and just negates the sign bit. return fabs(aV2 - aV1) < 1e-6;
}
template <class T> using BaseMatrixScales = BaseScaleFactors2D<UnknownUnits, UnknownUnits, T>;
using MatrixScales = BaseMatrixScales<float>; using MatrixScalesDouble = BaseMatrixScales<double>;
template <class T> class BaseMatrix { // Alias that maps to either Point or PointDouble depending on whether T is a // float or a double. typedef PointTyped<UnknownUnits, T> MatrixPoint; // Same for size and rect typedef SizeTyped<UnknownUnits, T> MatrixSize; typedef RectTyped<UnknownUnits, T> MatrixRect;
public:
BaseMatrix() : _11(1.0f), _12(0), _21(0), _22(1.0f), _31(0), _32(0) {}
BaseMatrix(T a11, T a12, T a21, T a22, T a31, T a32)
: _11(a11), _12(a12), _21(a21), _22(a22), _31(a31), _32(a32) {} union { struct {
T _11, _12;
T _21, _22;
T _31, _32;
};
T components[6];
};
/** * In most cases you probably want to use TransformBounds. This function * just transforms the top-left and size separately and constructs a rect * from those results.
*/
MatrixRect TransformRect(const MatrixRect& aRect) const { return MatrixRect(TransformPoint(aRect.TopLeft()),
TransformSize(aRect.Size()));
}
GFX2D_API MatrixRect TransformBounds(const MatrixRect& aRect) const { int i;
MatrixPoint quad[4];
T min_x, max_x;
T min_y, max_y;
/** * Apply a translation to this matrix. * * The "Pre" in this method's name means that the translation is applied * -before- this matrix's existing transformation. That is, any vector that * is multiplied by the resulting matrix will first be translated, then be * transformed by the original transform. * * Calling this method will result in this matrix having the same value as * the result of: * * BaseMatrix<T>::Translation(x, y) * this * * (Note that in performance critical code multiplying by the result of a * Translation()/Scaling() call is not recommended since that results in a * full matrix multiply involving 12 floating-point multiplications. Calling * this method would be preferred since it only involves four floating-point * multiplications.)
*/
BaseMatrix<T>& PreTranslate(T aX, T aY) {
_31 += _11 * aX + _21 * aY;
_32 += _12 * aX + _22 * aY;
/** * Similar to PreTranslate, but the translation is applied -after- this * matrix's existing transformation instead of before it. * * This method is generally less used than PreTranslate since typically code * want to adjust an existing user space to device space matrix to create a * transform to device space from a -new- user space (translated from the * previous user space). In that case consumers will need to use the Pre* * variants of the matrix methods rather than using the Post* methods, since * the Post* methods add a transform to the device space end of the * transformation.
*/
BaseMatrix<T>& PostTranslate(T aX, T aY) {
_31 += aX;
_32 += aY; return *this;
}
/** * Similar to PreTranslate, but applies a scale instead of a translation.
*/
BaseMatrix<T>& PreScale(T aX, T aY) {
_11 *= aX;
_12 *= aX;
_21 *= aY;
_22 *= aY;
/** * Similar to PreTranslate, but applies a rotation instead of a translation.
*/
BaseMatrix<T>& PreRotate(T aAngle) { return *this = BaseMatrix<T>::Rotation(aAngle) * *this;
}
bool Invert() { // Compute co-factors.
T A = _22;
T B = -_21;
T C = _21 * _32 - _22 * _31;
T D = -_12;
T E = _11;
T F = _31 * _12 - _11 * _32;
BaseMatrix<T> Inverse() const {
BaseMatrix<T> clone = *this;
DebugOnly<bool> inverted = clone.Invert();
MOZ_ASSERT(inverted, "Attempted to get the inverse of a non-invertible matrix"); return clone;
}
/* Verifies that the matrix contains no Infs or NaNs. */ bool IsFinite() const { return std::isfinite(_11) && std::isfinite(_12) && std::isfinite(_21) &&
std::isfinite(_22) && std::isfinite(_31) && std::isfinite(_32);
}
/* Returns true if the matrix is a rectilinear transformation (i.e. * grid-aligned rectangles are transformed to grid-aligned rectangles)
*/ bool IsRectilinear() const { if (FuzzyEqual(_12, 0) && FuzzyEqual(_21, 0)) { returntrue;
} elseif (FuzzyEqual(_22, 0) && FuzzyEqual(_11, 0)) { returntrue;
}
returnfalse;
}
/** * Returns true if the matrix is anything other than a straight * translation by integers.
*/ bool HasNonIntegerTranslation() const { return HasNonTranslation() || !FuzzyEqual(_31, floor(_31 + 0.5f)) ||
!FuzzyEqual(_32, floor(_32 + 0.5f));
}
/** * Returns true if the matrix only has an integer translation.
*/ bool HasOnlyIntegerTranslation() const { return !HasNonIntegerTranslation(); }
/** * Returns true if the matrix has any transform other * than a straight translation.
*/ bool HasNonTranslation() const { return !FuzzyEqual(_11, 1.0) || !FuzzyEqual(_22, 1.0) ||
!FuzzyEqual(_12, 0.0) || !FuzzyEqual(_21, 0.0);
}
/** * Returns true if the matrix has any transform other * than a translation or a -1 y scale (y axis flip)
*/ bool HasNonTranslationOrFlip() const { return !FuzzyEqual(_11, 1.0) ||
(!FuzzyEqual(_22, 1.0) && !FuzzyEqual(_22, -1.0)) ||
!FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0);
}
/* Returns true if the matrix is an identity matrix.
*/ bool IsIdentity() const { return _11 == 1.0f && _12 == 0.0f && _21 == 0.0f && _22 == 1.0f &&
_31 == 0.0f && _32 == 0.0f;
}
/* Returns true if the matrix is singular.
*/ bool IsSingular() const {
T det = Determinant(); return !std::isfinite(det) || det == 0;
}
/** * Returns true if matrix is multiple of 90 degrees rotation with flipping, * scaling and translation.
*/ bool PreservesAxisAlignedRectangles() const { return ((FuzzyEqual(_11, 0.0) && FuzzyEqual(_22, 0.0)) ||
(FuzzyEqual(_12, 0.0) && FuzzyEqual(_21, 0.0)));
}
/** * Returns true if the matrix has any transform other * than a translation or scale; this is, if there is * rotation.
*/ bool HasNonAxisAlignedTransform() const { return !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0);
}
/** * Returns true if the matrix has negative scaling (i.e. flip).
*/ bool HasNegativeScaling() const { return (_11 < 0.0) || (_22 < 0.0); }
/** * Computes the scale factors of this matrix; that is, * the amounts each basis vector is scaled by.
*/
BaseMatrixScales<T> ScaleFactors() const {
T det = Determinant();
if (det == 0.0) { return BaseMatrixScales<T>(0.0, 0.0);
}
// Helper functions used by Matrix4x4Typed defined in Matrix.cpp double SafeTangent(double aTheta); double FlushToZero(double aVal);
template <class Units, class F>
Point4DTyped<Units, F> ComputePerspectivePlaneIntercept( const Point4DTyped<Units, F>& aFirst, const Point4DTyped<Units, F>& aSecond) { // This function will always return a point with a w value of 0. // The X, Y, and Z components will point towards an infinite vanishing // point.
// We want to interpolate aFirst and aSecond to find the point intersecting // with the w=0 plane.
// Since we know what we want the w component to be, we can rearrange the // interpolation equation and solve for t. float t = -aFirst.w / (aSecond.w - aFirst.w);
// Use t to find the remainder of the components return aFirst + (aSecond - aFirst) * t;
}
Matrix4x4Typed(T a11, T a12, T a13, T a14, T a21, T a22, T a23, T a24, T a31,
T a32, T a33, T a34, T a41, T a42, T a43, T a44)
: _11(a11),
_12(a12),
_13(a13),
_14(a14),
_21(a21),
_22(a22),
_23(a23),
_24(a24),
_31(a31),
_32(a32),
_33(a33),
_34(a34),
_41(a41),
_42(a42),
_43(a43),
_44(a44) {}
explicit Matrix4x4Typed(const T aArray[16]) {
memcpy(components, aArray, sizeof(components));
}
Matrix4x4Typed& ProjectTo2D() {
_31 = 0.0f;
_32 = 0.0f;
_13 = 0.0f;
_23 = 0.0f;
_33 = 1.0f;
_43 = 0.0f;
_34 = 0.0f; // Some matrices, such as those derived from perspective transforms, // can modify _44 from 1, while leaving the rest of the fourth column // (_14, _24) at 0. In this case, after resetting the third row and // third column above, the value of _44 functions only to scale the // coordinate transform divide by W. The matrix can be converted to // a true 2D matrix by normalizing out the scaling effect of _44 on // the remaining components ahead of time. if (_14 == 0.0f && _24 == 0.0f && _44 != 1.0f && _44 != 0.0f) {
T scale = 1.0f / _44;
_11 *= scale;
_12 *= scale;
_21 *= scale;
_22 *= scale;
_41 *= scale;
_42 *= scale;
_44 = 1.0f;
} return *this;
}
template <class F>
Point4DTyped<TargetUnits, F> ProjectPoint( const PointTyped<SourceUnits, F>& aPoint) const { // Find a value for z that will transform to 0.
// The transformed value of z is computed as: // z' = aPoint.x * _13 + aPoint.y * _23 + z * _33 + _43;
// Solving for z when z' = 0 gives us:
F z = -(aPoint.x * _13 + aPoint.y * _23 + _43) / _33;
// Compute the transformed point return this->TransformPoint(
Point4DTyped<SourceUnits, F>(aPoint.x, aPoint.y, z, 1));
}
template <class F>
RectTyped<TargetUnits, F> ProjectRectBounds( const RectTyped<SourceUnits, F>& aRect, const RectTyped<TargetUnits, F>& aClip) const { // This function must never return std::numeric_limits<Float>::max() or any // other arbitrary large value in place of inifinity. This often occurs // when aRect is an inversed projection matrix or when aRect is transformed // to be partly behind and in front of the camera (w=0 plane in homogenous // coordinates) - See Bug 1035611
// Some call-sites will call RoundGfxRectToAppRect which clips both the // extents and dimensions of the rect to be bounded by nscoord_MAX. // If we return a Rect that, when converted to nscoords, has a width or // height greater than nscoord_MAX, RoundGfxRectToAppRect will clip the // overflow off both the min and max end of the rect after clipping the // extents of the rect, resulting in a translation of the rect towards the // infinite end.
// The bounds returned by ProjectRectBounds are expected to be clipped only // on the edges beyond the bounds of the coordinate system; otherwise, the // clipped bounding box would be smaller than the correct one and result // bugs such as incorrect culling (eg. Bug 1073056)
// To address this without requiring all code to work in homogenous // coordinates or interpret infinite values correctly, a specialized // clipping function is integrated into ProjectRectBounds.
// Callers should pass an aClip value that represents the extents to clip // the result to, in the same coordinate system as aRect.
Point4DTyped<TargetUnits, F> points[4];
F min_x = std::numeric_limits<F>::max();
F min_y = std::numeric_limits<F>::max();
F max_x = -std::numeric_limits<F>::max();
F max_y = -std::numeric_limits<F>::max();
for (int i = 0; i < 4; i++) { // Only use points that exist above the w=0 plane if (points[i].HasPositiveWCoord()) {
PointTyped<TargetUnits, F> point2d =
aClip.ClampPoint(points[i].As2DPoint());
min_x = std::min<F>(point2d.x, min_x);
max_x = std::max<F>(point2d.x, max_x);
min_y = std::min<F>(point2d.y, min_y);
max_y = std::max<F>(point2d.y, max_y);
}
int next = (i == 3) ? 0 : i + 1; if (points[i].HasPositiveWCoord() != points[next].HasPositiveWCoord()) { // If the line between two points crosses the w=0 plane, then // interpolate to find the point of intersection with the w=0 plane and // use that instead.
Point4DTyped<TargetUnits, F> intercept =
ComputePerspectivePlaneIntercept(points[i], points[next]); // Since intercept.w will always be 0 here, we interpret x,y,z as a // direction towards an infinite vanishing point. if (intercept.x < 0.0f) {
min_x = aClip.X();
} elseif (intercept.x > 0.0f) {
max_x = aClip.XMost();
} if (intercept.y < 0.0f) {
min_y = aClip.Y();
} elseif (intercept.y > 0.0f) {
max_y = aClip.YMost();
}
}
}
/** * TransformAndClipRect projects a rectangle and clips against view frustum * clipping planes in homogenous space so that its projected vertices are * constrained within the 2d rectangle passed in aClip. * The resulting vertices are populated in aVerts. aVerts must be * pre-allocated to hold at least kTransformAndClipRectMaxVerts Points. * The vertex count is returned by TransformAndClipRect. It is possible to * emit fewer than 3 vertices, indicating that aRect will not be visible * within aClip.
*/ template <class F>
size_t TransformAndClipRect(const RectTyped<SourceUnits, F>& aRect, const RectTyped<TargetUnits, F>& aClip,
PointTyped<TargetUnits, F>* aVerts) const { typedef Point4DTyped<UnknownUnits, F> P4D;
// The initial polygon is made up by the corners of aRect in homogenous // space, mapped into the destination space of this transform.
P4D rectCorners[] = {
TransformPoint(P4D(aRect.X(), aRect.Y(), 0, 1)),
TransformPoint(P4D(aRect.XMost(), aRect.Y(), 0, 1)),
TransformPoint(P4D(aRect.XMost(), aRect.YMost(), 0, 1)),
TransformPoint(P4D(aRect.X(), aRect.YMost(), 0, 1)),
};
// Cut off pieces of the polygon that are outside of aClip (the "view // frustrum"), by consecutively intersecting the polygon with the half space // induced by the clipping plane for each side of aClip. // View frustum clipping planes are described as normals originating from // the 0,0,0,0 origin. // Each pass can increase or decrease the number of points that make up the // current clipped polygon. We double buffer the set of points, alternating // between polygonBufA and polygonBufB. Duplicated points in the polygons // are kept around until all clipping is done. The loop at the end filters // out any consecutive duplicates.
P4D polygonBufA[kTransformAndClipRectMaxVerts];
P4D polygonBufB[kTransformAndClipRectMaxVerts];
size_t vertCount = 0; for (constauto& srcPoint : polygon) {
PointTyped<TargetUnits, F> p; if (srcPoint.w == 0.0) { // If a point lies on the intersection of the clipping planes at // (0,0,0,0), we must avoid a division by zero w component.
p = PointTyped<TargetUnits, F>(0.0, 0.0);
} else {
p = srcPoint.As2DPoint();
} // Emit only unique points if (vertCount == 0 || p != aVerts[vertCount - 1]) {
aVerts[vertCount++] = p;
}
}
template <class F>
GFX2D_API RectTyped<TargetUnits, F> TransformBounds( const RectTyped<SourceUnits, F>& aRect) const { // If you change this also change Matrix4x4TypedFlagged::TransformBounds to // match.
PointTyped<TargetUnits, F> quad[4];
F min_x, max_x;
F min_y, max_y;
/** * Apply a translation to this matrix. * * The "Pre" in this method's name means that the translation is applied * -before- this matrix's existing transformation. That is, any vector that * is multiplied by the resulting matrix will first be translated, then be * transformed by the original transform. * * Calling this method will result in this matrix having the same value as * the result of: * * Matrix4x4::Translation(x, y) * this * * (Note that in performance critical code multiplying by the result of a * Translation()/Scaling() call is not recommended since that results in a * full matrix multiply involving 64 floating-point multiplications. Calling * this method would be preferred since it only involves 12 floating-point * multiplications.)
*/
Matrix4x4Typed& PreTranslate(T aX, T aY, T aZ) {
_41 += aX * _11 + aY * _21 + aZ * _31;
_42 += aX * _12 + aY * _22 + aZ * _32;
_43 += aX * _13 + aY * _23 + aZ * _33;
_44 += aX * _14 + aY * _24 + aZ * _34;
/** * Similar to PreTranslate, but the translation is applied -after- this * matrix's existing transformation instead of before it. * * This method is generally less used than PreTranslate since typically code * wants to adjust an existing user space to device space matrix to create a * transform to device space from a -new- user space (translated from the * previous user space). In that case consumers will need to use the Pre* * variants of the matrix methods rather than using the Post* methods, since * the Post* methods add a transform to the device space end of the * transformation.
*/
Matrix4x4Typed& PostTranslate(T aX, T aY, T aZ) {
_11 += _14 * aX;
_21 += _24 * aX;
_31 += _34 * aX;
_41 += _44 * aX;
_12 += _14 * aY;
_22 += _24 * aY;
_32 += _34 * aY;
_42 += _44 * aY;
_13 += _14 * aZ;
_23 += _24 * aZ;
_33 += _34 * aZ;
_43 += _44 * aZ;
bool Decompose(Point3DTyped<UnknownUnits, T>& translation,
BaseQuaternion<T>& rotation,
Point3DTyped<UnknownUnits, T>& scale) const { // Ensure matrix can be normalized if (gfx::FuzzyEqual(_44, 0.0f)) { returnfalse;
}
Matrix4x4Typed mat = *this;
mat.Normalize(); if (HasPerspectiveComponent()) { // We do not support projection matrices returnfalse;
}
// Remove scale if (gfx::FuzzyEqual(scale.x, 0.0f) || gfx::FuzzyEqual(scale.y, 0.0f) ||
gfx::FuzzyEqual(scale.z, 0.0f)) { // We do not support matrices with a zero scale component returnfalse;
}
void SkewXY(double aXSkew, double aYSkew) { // XXX Is double precision really necessary here
T tanX = SafeTangent(aXSkew);
T tanY = SafeTangent(aYSkew);
T temp;
// Sets this matrix to a rotation matrix about a // vector [x,y,z] by angle theta. The vector is normalized // to a unit vector. // https://drafts.csswg.org/css-transforms-2/#Rotate3dDefined void SetRotateAxisAngle(double aX, double aY, double aZ, double aTheta) {
Point3DTyped<UnknownUnits, T> vector(aX, aY, aZ); if (!vector.Length()) { return;
}
vector.RobustNormalize();
double x = vector.x; double y = vector.y; double z = vector.z;
Point3D GetNormalVector() const { // Define a plane in transformed space as the transformations // of 3 points on the z=0 screen plane.
Point3DTyped<UnknownUnits, T> a =
TransformPoint(Point3DTyped<UnknownUnits, T>(0, 0, 0));
Point3DTyped<UnknownUnits, T> b =
TransformPoint(Point3DTyped<UnknownUnits, T>(0, 1, 0));
Point3DTyped<UnknownUnits, T> c =
TransformPoint(Point3DTyped<UnknownUnits, T>(1, 0, 0));
// Convert to two vectors on the surface of the plane.
Point3DTyped<UnknownUnits, T> ab = b - a;
Point3DTyped<UnknownUnits, T> ac = c - a;
return ac.CrossProduct(ab);
}
/** * Returns true if the matrix has any transform other * than a straight translation.
*/ bool HasNonTranslation() const { return !gfx::FuzzyEqual(_11, 1.0) || !gfx::FuzzyEqual(_22, 1.0) ||
!gfx::FuzzyEqual(_12, 0.0) || !gfx::FuzzyEqual(_21, 0.0) ||
!gfx::FuzzyEqual(_13, 0.0) || !gfx::FuzzyEqual(_23, 0.0) ||
!gfx::FuzzyEqual(_31, 0.0) || !gfx::FuzzyEqual(_32, 0.0) ||
!gfx::FuzzyEqual(_33, 1.0);
}
/** * Returns true if the matrix is anything other than a straight * translation by integers.
*/ bool HasNonIntegerTranslation() const { return HasNonTranslation() || !gfx::FuzzyEqual(_41, floor(_41 + 0.5)) ||
!gfx::FuzzyEqual(_42, floor(_42 + 0.5)) ||
!gfx::FuzzyEqual(_43, floor(_43 + 0.5));
}
/** * Return true if the matrix is with perspective (w).
*/ bool HasPerspectiveComponent() const { return _14 != 0 || _24 != 0 || _34 != 0 || _44 != 1;
}
/* Returns true if the matrix is a rectilinear transformation (i.e. * grid-aligned rectangles are transformed to grid-aligned rectangles). * This should only be called on 2D matrices.
*/ bool IsRectilinear() const {
MOZ_ASSERT(Is2D()); if (gfx::FuzzyEqual(_12, 0) && gfx::FuzzyEqual(_21, 0)) { returntrue;
} elseif (gfx::FuzzyEqual(_22, 0) && gfx::FuzzyEqual(_11, 0)) { returntrue;
} returnfalse;
}
/* This Matrix class will carry one additional type field in order to * track what type of 4x4 matrix we're dealing with, it can then execute * simplified versions of certain operations when applicable. * This does not allow access to the parent class directly, as a caller
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.29 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.