|
|
/**************************************************************************\
* * Copyright (c) 1998 Microsoft Corporation * * Module Name: * * Matrix.hpp * * Abstract: * * Matrix used by GDI+ implementation * * Revision History: * * 12/01/1998 davidx * Created it. * \**************************************************************************/
#ifndef __MATRIX_HPP
#define __MATRIX_HPP
// Common constants used for conversions.
#define DEGREESPERRADIAN 57.2957795130823
//--------------------------------------------------------------------------
// Represents a 2D affine transformation matrix
//--------------------------------------------------------------------------
enum MatrixRotate { MatrixRotateBy0, MatrixRotateBy90, MatrixRotateBy180, MatrixRotateBy270, MatrixRotateByOther };
class GpMatrix { private: // We now use an ObjectTag to determine if the object is valid
// instead of using a BOOL. This is much more robust and helps
// with debugging. It also enables us to version our objects
// more easily with a version number in the ObjectTag.
ObjectTag Tag; // Keep this as the 1st value in the object!
protected: VOID SetValid(BOOL valid) { Tag = valid ? ObjectTagMatrix : ObjectTagInvalid; }
// This method is here so that we have a virtual function table so
// that we can add virtual methods in V2 without shifting the position
// of the Tag value within the data structure.
virtual VOID DontCallThis() { DontCallThis(); }
public:
// Default constructor - set to identity matrix
GpMatrix() { Reset(); }
~GpMatrix() { SetValid(FALSE); // so we don't use a deleted object
}
// Reset the matrix object to identity
VOID Reset() { M11 = M22 = 1; M12 = M21 = Dx = Dy = 0; Complexity = IdentityMask; SetValid(TRUE); }
// Construct a GpMatrix object with the specified elements
GpMatrix(REAL m11, REAL m12, REAL m21, REAL m22, REAL dx, REAL dy) { SetValid(TRUE);
M11 = m11; M12 = m12; M21 = m21; M22 = m22; Dx = dx; Dy = dy;
Complexity = ComputeComplexity(); }
GpMatrix(REAL *elems) { SetValid(TRUE);
M11 = elems[0]; M12 = elems[1]; M21 = elems[2]; M22 = elems[3]; Dx = elems[4]; Dy = elems[5];
Complexity = ComputeComplexity(); }
GpMatrix(const GpMatrix &matrix) { SetValid(TRUE);
M11 = matrix.M11; M12 = matrix.M12; M21 = matrix.M21; M22 = matrix.M22; Dx = matrix.Dx; Dy = matrix.Dy;
Complexity = matrix.Complexity; }
GpLockable *GetObjectLock() const { return &Lockable; }
// If the matrix came from a different version of GDI+, its tag
// will not match, and it won't be considered valid.
BOOL IsValid() const { #ifdef _X86_
// We have to guarantee that the Tag field doesn't move for
// versioning to work between releases of GDI+.
ASSERT(offsetof(GpMatrix, Tag) == 4); #endif
ASSERT((Tag == ObjectTagMatrix) || (Tag == ObjectTagInvalid)); #if DBG
if (Tag == ObjectTagInvalid) { WARNING1("Invalid Matrix"); } #endif
return (Tag == ObjectTagMatrix); }
// Construct a GpMatrix object by inferring from a rectangle-to-
// parallelogram mapping:
GpMatrix(const GpPointF* destPoints, const GpRectF& srcRect);
GpStatus InferAffineMatrix(const GpPointF* destPoints, const GpRectF& srcRect);
GpStatus InferAffineMatrix(const GpRectF& destRect, const GpRectF& srcRect);
GpMatrix* Clone() { return new GpMatrix(*this); }
// Transform an array of points using the matrix v' = v M:
//
// ( M11 M12 0 )
// (vx', vy', 1) = (vx, vy, 1) ( M21 M22 0 )
// ( dx dy 1 )
VOID Transform(GpPointF* points, INT count = 1) const;
VOID Transform( const GpPointF* srcPoints, GpPointF* destPoints, INT count = 1 ) const;
VOID Transform( const GpPointF* srcPoints, POINT * destPoints, INT count = 1 ) const;
VOID TransformRect(GpRectF & rect) const;
VOID VectorTransform(GpPointF* points, INT count = 1) const;
// XTransform returns only the x-component result of a vector
// transformation:
REAL XTransform(REAL x, REAL y) { return(x*M11 + y*M21 + Dx); }
BOOL IsEqual(const GpMatrix * m) const { // Note that we can't assert here that the two cached
// complexity's are equal, since it's perfectly legal to
// have a cache complexity that indicates more complexity
// than is actually there (such as the case of a sequence
// of rotations that ends up back in an identity transform):
// !!![andrewgo] This should probably be using an epsilon compare
return((M11 == m->M11) && (M12 == m->M12) && (M21 == m->M21) && (M22 == m->M22) && (Dx == m->Dx) && (Dy == m->Dy)); }
REAL GetDeterminant() const { return (M11*M22 - M12*M21); }
// Compare to a scaled value of epsilon used for matrix complexity
// calculations. Must scale maximum value of matrix members to be
// in the right range for comparison.
BOOL IsInvertible() const { return !IsCloseReal(0.0f, GetDeterminant()); }
GpStatus Invert();
VOID Scale(REAL scaleX, REAL scaleY, GpMatrixOrder order = MatrixOrderPrepend);
VOID Rotate(REAL angle, GpMatrixOrder order = MatrixOrderPrepend);
VOID Translate(REAL offsetX, REAL offsetY, GpMatrixOrder order = MatrixOrderPrepend);
VOID Shear(REAL shearX, REAL shearY, GpMatrixOrder order = MatrixOrderPrepend);
VOID AppendScale(REAL scaleX, REAL scaleY) { M11 *= scaleX; M21 *= scaleX; M12 *= scaleY; M22 *= scaleY; Dx *= scaleX; Dy *= scaleY;
Complexity = ComputeComplexity(); }
VOID AppendTranslate(REAL offsetX, REAL offsetY) { Dx += offsetX; Dy += offsetY;
Complexity |= TranslationMask; AssertComplexity(); }
// Scale the entire matrix by the scale value
static VOID ScaleMatrix(GpMatrix& m, const GpMatrix& m1, REAL scaleX, REAL scaleY);
static VOID MultiplyMatrix(GpMatrix& m, const GpMatrix& m1, const GpMatrix& m2);
VOID Append(const GpMatrix& m) { MultiplyMatrix(*this, *this, m); }
VOID Prepend(const GpMatrix& m) { MultiplyMatrix(*this, m, *this); }
// Get/set matrix elements
VOID GetMatrix(REAL* m) const { m[0] = M11; m[1] = M12; m[2] = M21; m[3] = M22; m[4] = Dx; m[5] = Dy; }
// This is for metafiles -- we don't save the complexity in the metafile
VOID WriteMatrix(IStream * stream) const { REAL m[6]; GetMatrix(m); stream->Write(m, 6 * sizeof(REAL), NULL); }
VOID SetMatrix(const REAL* m) { M11 = m[0]; M12 = m[1]; M21 = m[2]; M22 = m[3]; Dx = m[4]; Dy = m[5];
Complexity = ComputeComplexity(); }
VOID SetMatrix(REAL m11, REAL m12, REAL m21, REAL m22, REAL dx, REAL dy) { M11 = m11; M12 = m12; M21 = m21; M22 = m22; Dx = dx; Dy = dy;
Complexity = ComputeComplexity(); } VOID RemoveTranslation() { Dx = 0; Dy = 0;
Complexity &= ~TranslationMask; AssertComplexity(); }
// Determine matrix complexity
enum { IdentityMask = 0x0000, TranslationMask = 0x0001, ScaleMask = 0x0002, RotationMask = 0x0004, ShearMask = 0x0008, ComplexMask = 0x000f };
INT GetComplexity() const { return Complexity; }
MatrixRotate GetRotation() const; RotateFlipType AnalyzeRotateFlip() const;
// Returns TRUE if the matrix does not have anything other than
// translation and/or scale (and/or identity). i.e. there is no rotation
// or shear in the matrix. Returns FALSE if there is a rotation or shear.
BOOL IsTranslateScale() const { return (!(Complexity & ~(GpMatrix::TranslationMask | GpMatrix::ScaleMask))); }
BOOL IsIdentity() const { return (Complexity == IdentityMask); }
BOOL IsTranslate() const { return ((Complexity & ~TranslationMask) == 0); } // Returns true if the matrix is a translate-only transform by an
// integer number of pixels.
BOOL IsIntegerTranslate() const { return (IsTranslate() && (REALABS(static_cast<REAL>(GpRound(Dx)) - Dx) <= PIXEL_EPSILON) && (REALABS(static_cast<REAL>(GpRound(Dy)) - Dy) <= PIXEL_EPSILON)); }
BOOL IsMinification() const { return (IsTranslateScale() && ((M11-1.0f < -REAL_EPSILON) || (M22-1.0f < -REAL_EPSILON))); } // Returns true if there is any minification or if the transform involves
// rotation/shear.
BOOL IsRotateOrMinification() const { return ( ((Complexity & ~(ScaleMask | TranslationMask))!= 0) || IsMinification() ); }
BOOL IsRotateOrShear() const { return ( (Complexity & ~(ScaleMask | TranslationMask))!= 0); }
BOOL IsShear() const { return ((Complexity & ShearMask) != 0); } REAL GetM11() const { return M11; } REAL GetM12() const { return M12; } REAL GetM21() const { return M21; } REAL GetM22() const { return M22; } REAL GetDx() const { return Dx; } REAL GetDy() const { return Dy; }
// On checked builds, verify that the Matrix flags are correct.
#if DBG
VOID AssertComplexity() const; #else
VOID AssertComplexity() const {} #endif
protected:
mutable GpLockable Lockable;
REAL M11; REAL M12; REAL M21; REAL M22; REAL Dx; REAL Dy; INT Complexity; // Bit-mask short-cut
INT ComputeComplexity() const; };
#endif
|