You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1170 lines
27 KiB
1170 lines
27 KiB
/**************************************************************************\
|
|
*
|
|
* Copyright (c) 1998 Microsoft Corporation
|
|
*
|
|
* Module Name:
|
|
*
|
|
* Matrix.cpp
|
|
*
|
|
* Abstract:
|
|
*
|
|
* Implementation of matrix class
|
|
*
|
|
* Revision History:
|
|
*
|
|
* 12/02/1998 davidx
|
|
* Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.hpp"
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Infer an affine transformation matrix
|
|
* from a rectangle-to-rectangle mapping
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] destRect - Specifies the destination rectangle
|
|
* [IN] srcRect - Specifies the source rectangle
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 3/10/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus
|
|
GpMatrix::InferAffineMatrix(
|
|
const GpRectF & destRect,
|
|
const GpRectF & srcRect
|
|
)
|
|
{
|
|
REAL srcLeft = srcRect.X;
|
|
REAL srcRight = srcRect.GetRight();
|
|
REAL srcTop = srcRect.Y;
|
|
REAL srcBottom = srcRect.GetBottom();
|
|
|
|
REAL destLeft = destRect.X;
|
|
REAL destRight = destRect.GetRight();
|
|
REAL destTop = destRect.Y;
|
|
REAL destBottom = destRect.GetBottom();
|
|
|
|
if ((srcLeft == srcRight) || (srcTop == srcBottom))
|
|
{
|
|
return InvalidParameter;
|
|
}
|
|
|
|
M12 = 0;
|
|
M21 = 0;
|
|
M11 = (destRight - destLeft) / (srcRight - srcLeft);
|
|
M22 = (destBottom - destTop) / (srcBottom - srcTop);
|
|
Dx = destRight - (M11 * srcRight);
|
|
Dy = destBottom - (M22 * srcBottom);
|
|
|
|
Complexity = ComputeComplexity();
|
|
return Ok;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Infer an affine transformation matrix
|
|
* from a rectangle-to-parallelogram mapping
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] rect - Specifies the source rectangle
|
|
* [IN] destPoints - Specifies the destination parallelogram
|
|
* The array must contain at least 3 points.
|
|
* destPoints[0] <=> top-left corner of the source rectangle
|
|
* destPoints[1] <=> top-right corner
|
|
* destPoints[2] <=> bottom-left corner
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status code (error when 3 points for the destination
|
|
* parallelogram is colinear).
|
|
*
|
|
* Reference:
|
|
*
|
|
* Digital Image Warping
|
|
* by George Wolberg
|
|
* pp. 50-51
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus
|
|
GpMatrix::InferAffineMatrix(
|
|
const GpPointF* destPoints,
|
|
const GpRectF& srcRect
|
|
)
|
|
{
|
|
REAL x0, y0, x1, y1, x2, y2;
|
|
REAL u0, v0, u1, v1, u2, v2;
|
|
REAL d;
|
|
|
|
x0 = destPoints[0].X;
|
|
y0 = destPoints[0].Y;
|
|
x1 = destPoints[1].X;
|
|
y1 = destPoints[1].Y;
|
|
x2 = destPoints[2].X;
|
|
y2 = destPoints[2].Y;
|
|
|
|
u0 = srcRect.X;
|
|
v0 = srcRect.Y;
|
|
u1 = u0 + srcRect.Width;
|
|
v1 = v0;
|
|
u2 = u0;
|
|
v2 = v0 + srcRect.Height;
|
|
|
|
d = u0*(v1-v2) - v0*(u1-u2) + (u1*v2-u2*v1);
|
|
|
|
if (REALABS(d) < REAL_EPSILON)
|
|
{
|
|
WARNING(("Colinear points in inferAffineMatrix"));
|
|
return InvalidParameter;
|
|
}
|
|
|
|
d = TOREAL(1.0) / d;
|
|
|
|
REAL t0, t1, t2;
|
|
|
|
t0 = v1-v2;
|
|
t1 = v2-v0;
|
|
t2 = v0-v1;
|
|
M11 = d * (x0*t0 + x1*t1 + x2*t2);
|
|
M12 = d * (y0*t0 + y1*t1 + y2*t2);
|
|
|
|
t0 = u2-u1;
|
|
t1 = u0-u2;
|
|
t2 = u1-u0;
|
|
M21 = d * (x0*t0 + x1*t1 + x2*t2);
|
|
M22 = d * (y0*t0 + y1*t1 + y2*t2);
|
|
|
|
t0 = u1*v2-u2*v1;
|
|
t1 = u2*v0-u0*v2;
|
|
t2 = u2*v1-u1*v0;
|
|
Dx = d * (x0*t0 + x1*t1 + x2*t2);
|
|
Dy = d * (y0*t0 + y1*t1 + y2*t2);
|
|
|
|
Complexity = ComputeComplexity();
|
|
return Ok;
|
|
}
|
|
|
|
GpMatrix::GpMatrix(
|
|
const GpPointF* destPoints,
|
|
const GpRectF& srcRect
|
|
)
|
|
{
|
|
// !!!
|
|
// Should we throw an exception if inferAffineMatrix fails?
|
|
|
|
SetValid(InferAffineMatrix(destPoints, srcRect) == Ok);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Invert the matrix (in place)
|
|
*
|
|
* Arguments:
|
|
*
|
|
* NONE
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status code (error if the matrix is not invertible)
|
|
*
|
|
* Reference:
|
|
*
|
|
* Digital Image Warping
|
|
* by George Wolberg
|
|
* pp. 52-53
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus
|
|
GpMatrix::Invert()
|
|
{
|
|
if(IsIdentity())
|
|
{
|
|
// Invert the identity matrix - this is easy.
|
|
return Ok;
|
|
}
|
|
|
|
if (!IsInvertible())
|
|
{
|
|
WARNING(("Matrix is non-invertible"));
|
|
return InvalidParameter;
|
|
}
|
|
|
|
REAL t11, t12, t21, t22, tx, ty;
|
|
REAL d = (M11*M22 - M12*M21);
|
|
|
|
d = TOREAL(1.0) / d;
|
|
|
|
t11 = M22;
|
|
t12 = -M12;
|
|
t21 = -M21;
|
|
t22 = M11;
|
|
tx = M21*Dy - M22*Dx;
|
|
ty = M12*Dx - M11*Dy;
|
|
|
|
M11 = d*t11;
|
|
M12 = d*t12;
|
|
M21 = d*t21;
|
|
M22 = d*t22;
|
|
Dx = d*tx;
|
|
Dy = d*ty;
|
|
|
|
Complexity = ComputeComplexity();
|
|
return Ok;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Prepend or append a scale matrix to the current matrix, i.e.
|
|
*
|
|
* | scaleX 0 0 |
|
|
* m = | 0 scaleY 0 |
|
|
* | 0 0 1 |
|
|
*
|
|
* matrix = m * matrix // for prepend
|
|
* matrix = matrix * m // for append
|
|
*
|
|
* Arguments:
|
|
*
|
|
* scaleX - scale factor along x-axis
|
|
* scaleY - scale factor along y-axis
|
|
* order - prepend or append.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
GpMatrix::Scale(
|
|
REAL scaleX,
|
|
REAL scaleY,
|
|
GpMatrixOrder order
|
|
)
|
|
{
|
|
if (order == MatrixOrderPrepend)
|
|
{
|
|
M11 *= scaleX;
|
|
M12 *= scaleX;
|
|
M21 *= scaleY;
|
|
M22 *= scaleY;
|
|
}
|
|
else // Append
|
|
{
|
|
M11 *= scaleX;
|
|
M21 *= scaleX;
|
|
M12 *= scaleY;
|
|
M22 *= scaleY;
|
|
Dx *= scaleX;
|
|
Dy *= scaleY;
|
|
}
|
|
|
|
// Scaling can magnify the error of other components.
|
|
// So it is safest to always recompute the complexity always.
|
|
|
|
Complexity = ComputeComplexity();
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Prepend or append a rotation matrix to the current matrix, i.e.
|
|
*
|
|
* | cos(angle) sin(angle) 0 |
|
|
* m = | -sin(angle) cos(angle) 0 |
|
|
* | 0 0 1 |
|
|
*
|
|
* matrix = m * matrix // for prepend
|
|
* matrix = matrix * m // for append
|
|
*
|
|
* Arguments:
|
|
*
|
|
* angle - Specify the rotation angle
|
|
* order - prepend or append.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#define PI 3.1415926535897932384626433832795
|
|
#define DEGREES_TO_RADIANS (PI / 180.0)
|
|
|
|
VOID
|
|
GpMatrix::Rotate(
|
|
REAL angle,
|
|
GpMatrixOrder order
|
|
)
|
|
{
|
|
REAL s, c;
|
|
REAL t11, t12, t21, t22;
|
|
|
|
angle *= (REAL)DEGREES_TO_RADIANS;
|
|
|
|
s = REALSIN(angle);
|
|
c = REALCOS(angle);
|
|
|
|
if (order == MatrixOrderPrepend)
|
|
{
|
|
t11 = c*M11 + s*M21;
|
|
t12 = c*M12 + s*M22;
|
|
t21 = c*M21 - s*M11;
|
|
t22 = c*M22 - s*M12;
|
|
}
|
|
else // Append
|
|
{
|
|
t11 = c*M11 - s*M12;
|
|
t12 = s*M11 + c*M12;
|
|
t21 = c*M21 - s*M22;
|
|
t22 = s*M21 + c*M22;
|
|
|
|
REAL tx, ty;
|
|
tx = c*Dx - s*Dy;
|
|
ty = s*Dx + c*Dy;
|
|
Dx = tx;
|
|
Dy = ty;
|
|
}
|
|
|
|
M11 = t11;
|
|
M12 = t12;
|
|
M21 = t21;
|
|
M22 = t22;
|
|
|
|
// Rotation is very complex; we choose to simply recalculate the
|
|
// complexity:
|
|
|
|
Complexity = ComputeComplexity();
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Prepend or append a translation matrix to the current matrix, i.e.
|
|
*
|
|
* | 1 0 0 |
|
|
* m = | 0 1 0 |
|
|
* | offsetX offsetY 1 |
|
|
*
|
|
* matrix = m * matrix // for prepend
|
|
* matrix = matrix * m // for append
|
|
*
|
|
* Arguments:
|
|
*
|
|
* offsetX - offset along x-axis
|
|
* offsetY - offset along y-axis
|
|
* order - prepend or append.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
GpMatrix::Translate(
|
|
REAL offsetX,
|
|
REAL offsetY,
|
|
GpMatrixOrder order
|
|
)
|
|
{
|
|
if (order == MatrixOrderPrepend)
|
|
{
|
|
Dx += (offsetX * M11) + (offsetY * M21);
|
|
Dy += (offsetX * M12) + (offsetY * M22);
|
|
}
|
|
else // Append
|
|
{
|
|
Dx += offsetX;
|
|
Dy += offsetY;
|
|
}
|
|
|
|
Complexity |= TranslationMask;
|
|
AssertComplexity();
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Prepend or append a shear matrix to the current matrix, i.e.
|
|
*
|
|
* | 1 shearY 0 |
|
|
* m = | shearX 1 0 |
|
|
* | 0 0 1 |
|
|
*
|
|
* matrix = m * matrix // for prepend
|
|
* matrix = matrix * m // for append
|
|
*
|
|
* Arguments:
|
|
*
|
|
* shearX - Amount to shear along x-axis
|
|
* shearY - Amount to shear along y-axis
|
|
* order - prepend or append.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
GpMatrix::Shear(
|
|
REAL shearX,
|
|
REAL shearY,
|
|
GpMatrixOrder order
|
|
)
|
|
{
|
|
REAL t;
|
|
|
|
if (order == MatrixOrderPrepend)
|
|
{
|
|
t = M11;
|
|
M11 += shearY*M21;
|
|
M21 += shearX*t;
|
|
|
|
t = M12;
|
|
M12 += shearY*M22;
|
|
M22 += shearX*t;
|
|
}
|
|
else // Append
|
|
{
|
|
t = M11;
|
|
M11 += shearX*M12;
|
|
M12 += shearY*t;
|
|
|
|
t = M21;
|
|
M21 += shearX*M22;
|
|
M22 += shearY*t;
|
|
|
|
t= Dx;
|
|
Dx += shearX*Dy;
|
|
Dy += shearY*t;
|
|
}
|
|
|
|
// Shear is very complex; we choose to simply recalculate the
|
|
// complexity:
|
|
|
|
Complexity = ComputeComplexity();
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Multiply two matrices and place the result in the 3rd one:
|
|
* m = m1 * m2
|
|
*
|
|
* Arguments:
|
|
*
|
|
* m - Destination matrix
|
|
* m1, m2 - Source matrices
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Notes:
|
|
*
|
|
* m can be the same matrix as m1 and/or m2.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
GpMatrix::MultiplyMatrix(
|
|
GpMatrix& m,
|
|
const GpMatrix& m1,
|
|
const GpMatrix& m2
|
|
)
|
|
{
|
|
REAL t11, t12, t21, t22, tx, ty;
|
|
|
|
t11 = m1.M11 * m2.M11 + m1.M12 * m2.M21;
|
|
t12 = m1.M11 * m2.M12 + m1.M12 * m2.M22;
|
|
t21 = m1.M21 * m2.M11 + m1.M22 * m2.M21;
|
|
t22 = m1.M21 * m2.M12 + m1.M22 * m2.M22;
|
|
tx = m1.Dx * m2.M11 + m1.Dy * m2.M21 + m2.Dx;
|
|
ty = m1.Dx * m2.M12 + m1.Dy * m2.M22 + m2.Dy;
|
|
|
|
m.M11 = t11;
|
|
m.M12 = t12;
|
|
m.M21 = t21;
|
|
m.M22 = t22;
|
|
m.Dx = tx;
|
|
m.Dy = ty;
|
|
|
|
// Multiply can be very complex; we choose to simply recalculate the
|
|
// complexity:
|
|
|
|
m.Complexity = m.ComputeComplexity();
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Scale the entire matrix by the scale value.
|
|
*
|
|
* +-- --+ +-- --+
|
|
* | M11 M12 0 | | Sx 0 0 |
|
|
* | M21 M22 0 | x | 0 Sy 0 | => dest matrix
|
|
* | Dx Dy 1 | | 0 0 1 |
|
|
* +-- --+ +-- --+
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [OUT] m - destination matrix
|
|
* [IN] m1 - source matrix
|
|
* [IN] scaleX - dest = source * scaleValue
|
|
* [IN] scaleY - dest = source * scaleValue
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 3/1/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
VOID
|
|
GpMatrix::ScaleMatrix(
|
|
GpMatrix& m,
|
|
const GpMatrix& m1,
|
|
REAL scaleX,
|
|
REAL scaleY
|
|
)
|
|
{
|
|
// !!! some kind of epsilon checking maybe?
|
|
if ((scaleX != 1) || (scaleY != 1))
|
|
{
|
|
m.M11 = scaleX * m1.M11;
|
|
m.M12 = scaleY * m1.M12;
|
|
m.M21 = scaleX * m1.M21;
|
|
m.M22 = scaleY * m1.M22;
|
|
m.Dx = scaleX * m1.Dx;
|
|
m.Dy = scaleY * m1.Dy;
|
|
|
|
//!!! Since the scaling can magnify the other component,
|
|
// it is safer to recompute the complexity.
|
|
|
|
m.Complexity = m.ComputeComplexity();
|
|
/*
|
|
if(m1.IsTranslateScale())
|
|
{
|
|
m.Complexity = m1.Complexity | ScaleMask;
|
|
}
|
|
else
|
|
{
|
|
// Scaling a rotation by different scale factors in x and y
|
|
// results in a shear. Instead of working out the correct
|
|
// optimized complexity, we just recompute - this is a rotation
|
|
// or shear already anyway.
|
|
m.Complexity = m.ComputeComplexity();
|
|
}
|
|
m.AssertComplexity();
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
m = m1;
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Query for special types of transformation matrices
|
|
*
|
|
* Arguments:
|
|
*
|
|
* Return Value:
|
|
*
|
|
* MatrixRotate enum indicating type of rotation
|
|
*
|
|
\**************************************************************************/
|
|
|
|
MatrixRotate
|
|
GpMatrix::GetRotation() const
|
|
{
|
|
// Check for no rotate.
|
|
|
|
if(IsTranslateScale())
|
|
{
|
|
return MatrixRotateBy0;
|
|
}
|
|
|
|
// Check for Rotate by 90 degrees
|
|
|
|
if (REALABS(M12) < REAL_EPSILON &&
|
|
REALABS(M21) < REAL_EPSILON &&
|
|
(M11 < 0.0f) && (M22 < 0.0f) )
|
|
{
|
|
return MatrixRotateBy180;
|
|
}
|
|
else if (REALABS(M11) < REAL_EPSILON &&
|
|
REALABS(M22) < REAL_EPSILON)
|
|
{
|
|
if (M12 > 0.0f)
|
|
{
|
|
return MatrixRotateBy90;
|
|
}
|
|
else
|
|
{
|
|
return MatrixRotateBy270;
|
|
}
|
|
}
|
|
|
|
return MatrixRotateByOther;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Query for special types of transformation matrices.
|
|
* This will return a RotateFlipType for the rotation. If the rotation
|
|
* is Identity or an arbitrary non supported format, return value is
|
|
* RotateNoneFlipNone
|
|
*
|
|
\**************************************************************************/
|
|
|
|
RotateFlipType GpMatrix::AnalyzeRotateFlip() const
|
|
{
|
|
// Early out the identity case because we have a flag for it in the matrix.
|
|
|
|
if(IsIntegerTranslate())
|
|
{
|
|
return RotateNoneFlipNone;
|
|
}
|
|
|
|
// Main Diagonal is zero.
|
|
|
|
if( (REALABS(M11) < REAL_EPSILON) && // M11 == 0.0
|
|
(REALABS(M22) < REAL_EPSILON) ) // M22 == 0.0
|
|
{
|
|
// Rotate 270 or Rotate 90 + Flip X
|
|
|
|
if( REALABS(M21-1) < REAL_EPSILON ) // M21 == 1.0
|
|
{
|
|
if( REALABS(M12-1) < REAL_EPSILON ) // M12 == 1.0
|
|
{
|
|
return Rotate90FlipX;
|
|
}
|
|
if( REALABS(M12+1) < REAL_EPSILON ) // M21 == -1.0
|
|
{
|
|
return Rotate270FlipNone;
|
|
}
|
|
}
|
|
|
|
// Rotate 90 or Rotate 270 + Flip X
|
|
|
|
if( REALABS(M21+1) < REAL_EPSILON ) // M21 == -1.0
|
|
{
|
|
if( REALABS(M12-1) < REAL_EPSILON ) // M12 == 1.0
|
|
{
|
|
return Rotate90FlipNone;
|
|
}
|
|
if( REALABS(M12+1) < REAL_EPSILON ) // M12 == -1.0
|
|
{
|
|
return Rotate270FlipX;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Main Diagonal matrix (non zero).
|
|
|
|
if( (REALABS(M12) < REAL_EPSILON) && // M12 == 0.0
|
|
(REALABS(M21) < REAL_EPSILON) ) // M21 == 0.0
|
|
{
|
|
// Identity or Flip Y
|
|
|
|
if( REALABS(M11-1) < REAL_EPSILON ) // M11 == 1.0
|
|
{
|
|
// Identity is handled already.
|
|
// if( REALABS(M22-1) < REAL_EPSILON ) // M22 == 1.0
|
|
|
|
if( REALABS(M22+1) < REAL_EPSILON ) // M22 == -1.0
|
|
{
|
|
return RotateNoneFlipY;
|
|
}
|
|
}
|
|
|
|
// Flip X or Rotate 180
|
|
|
|
if( REALABS(M11+1) < REAL_EPSILON ) // M11 == -1.0
|
|
{
|
|
if( REALABS(M22-1) < REAL_EPSILON ) // M22 == 1.0
|
|
{
|
|
return RotateNoneFlipX;
|
|
}
|
|
if( REALABS(M22+1) < REAL_EPSILON ) // M22 == -1.0
|
|
{
|
|
return Rotate180FlipNone;
|
|
}
|
|
}
|
|
}
|
|
|
|
// We couldn't find a rotate/flip type.
|
|
|
|
return RotateNoneFlipNone;
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Transform the specified array of points using the current matrix
|
|
*
|
|
* Arguments:
|
|
*
|
|
* points - Array of points to be transformed
|
|
* The resulting points are stored back into the same array
|
|
*
|
|
* count - Number of points in the array
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
GpMatrix::Transform(
|
|
GpPointF* points,
|
|
INT count
|
|
) const
|
|
{
|
|
if (count <= 0)
|
|
return;
|
|
|
|
ASSERT(points != NULL);
|
|
|
|
// On checked builds, verify that the Complexity flags are correct:
|
|
|
|
AssertComplexity();
|
|
|
|
if(IsIdentity())
|
|
{
|
|
return;
|
|
}
|
|
else if(IsTranslate())
|
|
{
|
|
do {
|
|
points->X += Dx;
|
|
points->Y += Dy;
|
|
|
|
} while (points++, --count != 0);
|
|
}
|
|
else if(IsTranslateScale())
|
|
{
|
|
do {
|
|
points->X = points->X * M11 + Dx;
|
|
points->Y = points->Y * M22 + Dy;
|
|
|
|
} while (points++, --count != 0);
|
|
}
|
|
else
|
|
{
|
|
do {
|
|
REAL x = points->X;
|
|
REAL y = points->Y;
|
|
|
|
points->X = (M11 * x) + (M21 * y) + Dx;
|
|
points->Y = (M12 * x) + (M22 * y) + Dy;
|
|
|
|
} while (points++, --count != 0);
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Transform the specified array of points using the current matrix,
|
|
* with the destination an array of integer POINTS.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* srcPoints - Array of REAL points to be transformed
|
|
*
|
|
* destPoints - Array of REAL points to store the results
|
|
*
|
|
* count - Number of points in the array
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
GpMatrix::Transform(
|
|
const GpPointF* srcPoints,
|
|
GpPointF* destPoints,
|
|
INT count
|
|
) const
|
|
{
|
|
if (count <= 0)
|
|
return;
|
|
|
|
ASSERT((srcPoints != NULL) && (destPoints != NULL));
|
|
|
|
// On checked builds, verify that the Complexity flags are correct:
|
|
|
|
AssertComplexity();
|
|
|
|
if(IsIdentity())
|
|
{
|
|
GpMemcpy(destPoints, srcPoints, count*sizeof(GpPointF));
|
|
}
|
|
else if (IsTranslate())
|
|
{
|
|
do {
|
|
destPoints->X = srcPoints->X + Dx;
|
|
destPoints->Y = srcPoints->Y + Dy;
|
|
|
|
} while (destPoints++, srcPoints++, --count != 0);
|
|
}
|
|
else if (IsTranslateScale())
|
|
{
|
|
do {
|
|
destPoints->X = srcPoints->X * M11 + Dx;
|
|
destPoints->Y = srcPoints->Y * M22 + Dy;
|
|
|
|
} while (destPoints++, srcPoints++, --count != 0);
|
|
}
|
|
else
|
|
{
|
|
do {
|
|
REAL x = srcPoints->X;
|
|
REAL y = srcPoints->Y;
|
|
|
|
destPoints->X = (M11 * x) + (M21 * y) + Dx;
|
|
destPoints->Y = (M12 * x) + (M22 * y) + Dy;
|
|
|
|
} while (destPoints++, srcPoints++, --count != 0);
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Transform the specified array of points using the current matrix,
|
|
* with the destination an array of integer POINTS.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* srcPoints - Array of REAL points to be transformed
|
|
*
|
|
* destPoints - Array of INT points to store the results
|
|
*
|
|
* count - Number of points in the array
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
GpMatrix::Transform(
|
|
const GpPointF* srcPoints,
|
|
POINT * destPoints,
|
|
INT count
|
|
) const
|
|
{
|
|
if (count <= 0)
|
|
return;
|
|
|
|
ASSERT((srcPoints != NULL) && (destPoints != NULL));
|
|
|
|
// On checked builds, verify that the Complexity flags are correct:
|
|
|
|
AssertComplexity();
|
|
|
|
// NOTE: This code should call RasterizeCeiling() to be consistent
|
|
// with our aliased line drawing rasterizer.
|
|
|
|
if (IsTranslate())
|
|
{
|
|
do {
|
|
destPoints->x = RasterizerCeiling(srcPoints->X + Dx);
|
|
destPoints->y = RasterizerCeiling(srcPoints->Y + Dy);
|
|
|
|
} while (destPoints++, srcPoints++, --count != 0);
|
|
}
|
|
else if (IsTranslateScale())
|
|
{
|
|
do {
|
|
destPoints->x = RasterizerCeiling(srcPoints->X * M11 + Dx);
|
|
destPoints->y = RasterizerCeiling(srcPoints->Y * M22 + Dy);
|
|
|
|
} while (destPoints++, srcPoints++, --count != 0);
|
|
}
|
|
else
|
|
{
|
|
do {
|
|
REAL x = srcPoints->X;
|
|
REAL y = srcPoints->Y;
|
|
|
|
destPoints->x = RasterizerCeiling((M11 * x) + (M21 * y) + Dx);
|
|
destPoints->y = RasterizerCeiling((M12 * x) + (M22 * y) + Dy);
|
|
|
|
} while (destPoints++, srcPoints++, --count != 0);
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Transform a rect and return the resulting rect.
|
|
* This only works if the matrix is a translate-scale matrix, but we're
|
|
* assuming here that you've already checked for that.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN/OUT] rect - the rect to transform
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
* Created:
|
|
*
|
|
* 3/5/1999 DCurtis
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
GpMatrix::TransformRect(
|
|
GpRectF & rect
|
|
) const
|
|
{
|
|
if (IsIdentity())
|
|
return;
|
|
|
|
// NTRAID#NTBUG9-407211-2001-05-31-gillessk "Bad assert triggers when it shouldn't"
|
|
// loose the condition to allow rotation by multiple of 90 degrees
|
|
ASSERT(IsTranslateScale() || (GetRotation()==MatrixRotateBy90) || (GetRotation()==MatrixRotateBy270));
|
|
|
|
REAL xMin = rect.X;
|
|
REAL yMin = rect.Y;
|
|
REAL xMax = xMin + rect.Width;
|
|
REAL yMax = yMin + rect.Height;
|
|
REAL x;
|
|
|
|
x = xMin;
|
|
xMin = (M11 * x) + (M21 * yMin) + Dx;
|
|
yMin = (M12 * x) + (M22 * yMin) + Dy;
|
|
|
|
x = xMax;
|
|
xMax = (M11 * x) + (M21 * yMax) + Dx;
|
|
yMax = (M12 * x) + (M22 * yMax) + Dy;
|
|
|
|
if (xMin > xMax)
|
|
{
|
|
x = xMin;
|
|
xMin = xMax;
|
|
xMax = x;
|
|
}
|
|
|
|
if (yMin > yMax)
|
|
{
|
|
x = yMin;
|
|
yMin = yMax;
|
|
yMax = x;
|
|
}
|
|
|
|
rect.X = xMin;
|
|
rect.Y = yMin;
|
|
rect.Width = xMax - xMin;
|
|
rect.Height = yMax - yMin;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Transform the specified array of points using the current matrix,
|
|
* ignoring translation.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* points - Array of points to be transformed
|
|
* The resulting points are stored back into the same array
|
|
*
|
|
* count - Number of points in the array
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NONE
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
GpMatrix::VectorTransform(
|
|
GpPointF* points,
|
|
INT count
|
|
) const
|
|
{
|
|
if (IsIdentity())
|
|
return;
|
|
|
|
REAL x;
|
|
|
|
for (INT i=0; i < count; i++)
|
|
{
|
|
x = points[i].X;
|
|
points[i].X = M11*x + M21*points[i].Y;
|
|
points[i].Y = M12*x + M22*points[i].Y;
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Determine matrix complexity.
|
|
*
|
|
* NOTE: This function is fairly expensive. It shouldn't be called
|
|
* after every matrix operation. (If it was, I would argue
|
|
* that's a good reason to get rid of 'Complexity' entirely,
|
|
* which is intended as a short-cut and should not be expensive
|
|
* to keep updated.)
|
|
*
|
|
* Arguments:
|
|
*
|
|
* NONE
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Returns a bitmask representing the matrix complexity.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
INT
|
|
GpMatrix::ComputeComplexity() const
|
|
{
|
|
INT complexity = ComplexMask;
|
|
|
|
REAL maxM = max(
|
|
max(REALABS(M11), REALABS(M22)),
|
|
max(REALABS(M12), REALABS(M21)));
|
|
REAL epsilon = CPLX_EPSILON*maxM;
|
|
|
|
// M12==0 && M21==0
|
|
|
|
if ((REALABS(M12) < epsilon) && (REALABS(M21) < epsilon))
|
|
{
|
|
complexity &= ~(ShearMask | RotationMask);
|
|
|
|
// M11==1 && M22==1
|
|
|
|
if ((REALABS(M11 - 1.0f) < CPLX_EPSILON) &&
|
|
(REALABS(M22 - 1.0f) < CPLX_EPSILON))
|
|
{
|
|
complexity &= ~ScaleMask;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Check if this is a pure rotation
|
|
|
|
// M11==M22 && M12==-M21
|
|
|
|
if((REALABS(M11 - M22) < epsilon) &&
|
|
(REALABS(M12 + M21) < epsilon))
|
|
{
|
|
complexity &= ~ShearMask;
|
|
|
|
// M11*M11+M12*M12==1
|
|
|
|
if (REALABS(M11*M11 + M12*M12 - 1.0f) < CPLX_EPSILON)
|
|
{
|
|
complexity &= ~ScaleMask;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Dx==0 && Dy==0
|
|
|
|
// We don't know the real scaling of the translational part.
|
|
// So we use the exact value.
|
|
|
|
// if ((REALABS(Dx) < CPLX_EPSILON) && (REALABS(Dy) < CPLX_EPSILON))
|
|
if(Dx == 0 && Dy == 0)
|
|
{
|
|
complexity &= ~TranslationMask;
|
|
}
|
|
|
|
return(complexity);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Verify the matrix complexity.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#if DBG
|
|
|
|
VOID
|
|
GpMatrix::AssertComplexity() const
|
|
{
|
|
INT computed = ComputeComplexity();
|
|
|
|
// The new complexity can be less complex than the old
|
|
// (a sequence or rotations may end up with an identity
|
|
// transform, for example), but that's okay - complexity
|
|
// is intended as a short cut, and as such keeping it
|
|
// updated sholud be light weight.
|
|
//
|
|
// But the calculated complexity SHOULD NOT be more complex
|
|
// than the old - if it is, we have a bug someplace -
|
|
// we're updating the matrix but not the complexity...
|
|
//
|
|
// Note: under certain circumstances - such as excessive
|
|
// repeated matrix appending or scaling up by very large
|
|
// factors - we could end up with a more complex computed
|
|
// complexity just due to rounding errors.
|
|
// Under these circumstances, the cause should be determined
|
|
// and most likely the algorithm re-evaluated because when
|
|
// rounding errors are propagated to such large proportions,
|
|
// no operations are going to have predictable results anyway.
|
|
|
|
ASSERTMSG((Complexity & computed) == computed,
|
|
(("Matrix more complex than cached Complexity indicates")));
|
|
}
|
|
|
|
#endif
|