diff options
Diffstat (limited to 'src/gpu/GrMatrix.cpp')
-rw-r--r-- | src/gpu/GrMatrix.cpp | 713 |
1 files changed, 713 insertions, 0 deletions
diff --git a/src/gpu/GrMatrix.cpp b/src/gpu/GrMatrix.cpp new file mode 100644 index 0000000..e71636b --- /dev/null +++ b/src/gpu/GrMatrix.cpp @@ -0,0 +1,713 @@ + +/* + * Copyright 2010 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + + +#include "GrMatrix.h" +#include "GrRect.h" +#include <stddef.h> + +#if 0 +#if GR_SCALAR_IS_FLOAT + const GrScalar GrMatrix::gRESCALE(GR_Scalar1); +#else + GR_STATIC_ASSERT(GR_SCALAR_IS_FIXED); + // fixed point isn't supported right now + GR_STATIC_ASSERT(false); +const GrScalar GrMatrix::gRESCALE(1 << 30); +#endif + +const GrMatrix::MapProc GrMatrix::gMapProcs[] = { +// Scales are not both zero + &GrMatrix::mapIdentity, + &GrMatrix::mapScale, + &GrMatrix::mapTranslate, + &GrMatrix::mapScaleAndTranslate, + &GrMatrix::mapSkew, + &GrMatrix::mapScaleAndSkew, + &GrMatrix::mapSkewAndTranslate, + &GrMatrix::mapNonPerspective, + // no optimizations for perspective matrices + &GrMatrix::mapPerspective, + &GrMatrix::mapPerspective, + &GrMatrix::mapPerspective, + &GrMatrix::mapPerspective, + &GrMatrix::mapPerspective, + &GrMatrix::mapPerspective, + &GrMatrix::mapPerspective, + &GrMatrix::mapPerspective, + +// Scales are zero (every other is invalid because kScale_TypeBit must be set if +// kZeroScale_TypeBit is set) + &GrMatrix::mapInvalid, + &GrMatrix::mapZero, + &GrMatrix::mapInvalid, + &GrMatrix::mapSetToTranslate, + &GrMatrix::mapInvalid, + &GrMatrix::mapSwappedScale, + &GrMatrix::mapInvalid, + &GrMatrix::mapSwappedScaleAndTranslate, + + // no optimizations for perspective matrices + &GrMatrix::mapInvalid, + &GrMatrix::mapZero, + &GrMatrix::mapInvalid, + &GrMatrix::mapPerspective, + &GrMatrix::mapInvalid, + &GrMatrix::mapPerspective, + &GrMatrix::mapInvalid, + &GrMatrix::mapPerspective, +}; + +void GrMatrix::setIdentity() { + fM[0] = GR_Scalar1; fM[1] = 0; fM[2] = 0; + fM[3] = 0; fM[4] = GR_Scalar1; fM[5] = 0; + fM[6] = 0; fM[7] = 0; fM[8] = gRESCALE; + fTypeMask = 0; +} + +void GrMatrix::setTranslate(GrScalar dx, GrScalar dy) { + fM[0] = GR_Scalar1; fM[1] = 0; fM[2] = dx; + fM[3] = 0; fM[4] = GR_Scalar1; fM[5] = dy; + fM[6] = 0; fM[7] = 0; fM[8] = gRESCALE; + fTypeMask = (0 != dx || 0 != dy) ? kTranslate_TypeBit : 0; +} + +void GrMatrix::setScale(GrScalar sx, GrScalar sy) { + fM[0] = sx; fM[1] = 0; fM[2] = 0; + fM[3] = 0; fM[4] = sy; fM[5] = 0; + fM[6] = 0; fM[7] = 0; fM[8] = gRESCALE; + fTypeMask = (GR_Scalar1 != sx || GR_Scalar1 != sy) ? kScale_TypeBit : 0; +} + +void GrMatrix::setSkew(GrScalar skx, GrScalar sky) { + fM[0] = GR_Scalar1; fM[1] = skx; fM[2] = 0; + fM[3] = sky; fM[4] = GR_Scalar1; fM[5] = 0; + fM[6] = 0; fM[7] = 0; fM[8] = gRESCALE; + fTypeMask = (0 != skx || 0 != sky) ? kSkew_TypeBit : 0; +} + +void GrMatrix::setConcat(const GrMatrix& a, const GrMatrix& b) { + if (a.isIdentity()) { + if (this != &b) { + for (int i = 0; i < 9; ++i) { + fM[i] = b.fM[i]; + } + fTypeMask = b.fTypeMask; + } + return; + } + + if (b.isIdentity()) { + GrAssert(!a.isIdentity()); + if (this != &a) { + for (int i = 0; i < 9; ++i) { + fM[i] = a.fM[i]; + } + fTypeMask = a.fTypeMask; + } + return; + } + + // a and/or b could be this + GrMatrix tmp; + + // could do more optimizations based on type bits. Hopefully this call is + // low frequency. + // TODO: make this work for fixed point + if (!((b.fTypeMask | a.fTypeMask) & kPerspective_TypeBit)) { + tmp.fM[0] = a.fM[0] * b.fM[0] + a.fM[1] * b.fM[3]; + tmp.fM[1] = a.fM[0] * b.fM[1] + a.fM[1] * b.fM[4]; + tmp.fM[2] = a.fM[0] * b.fM[2] + a.fM[1] * b.fM[5] + a.fM[2] * gRESCALE; + + tmp.fM[3] = a.fM[3] * b.fM[0] + a.fM[4] * b.fM[3]; + tmp.fM[4] = a.fM[3] * b.fM[1] + a.fM[4] * b.fM[4]; + tmp.fM[5] = a.fM[3] * b.fM[2] + a.fM[4] * b.fM[5] + a.fM[5] * gRESCALE; + + tmp.fM[6] = 0; + tmp.fM[7] = 0; + tmp.fM[8] = gRESCALE * gRESCALE; + } else { + tmp.fM[0] = a.fM[0] * b.fM[0] + a.fM[1] * b.fM[3] + a.fM[2] * b.fM[6]; + tmp.fM[1] = a.fM[0] * b.fM[1] + a.fM[1] * b.fM[4] + a.fM[2] * b.fM[7]; + tmp.fM[2] = a.fM[0] * b.fM[2] + a.fM[1] * b.fM[5] + a.fM[2] * b.fM[8]; + + tmp.fM[3] = a.fM[3] * b.fM[0] + a.fM[4] * b.fM[3] + a.fM[5] * b.fM[6]; + tmp.fM[4] = a.fM[3] * b.fM[1] + a.fM[4] * b.fM[4] + a.fM[5] * b.fM[7]; + tmp.fM[5] = a.fM[3] * b.fM[2] + a.fM[4] * b.fM[5] + a.fM[5] * b.fM[8]; + + tmp.fM[6] = a.fM[6] * b.fM[0] + a.fM[7] * b.fM[3] + a.fM[8] * b.fM[6]; + tmp.fM[7] = a.fM[6] * b.fM[1] + a.fM[7] * b.fM[4] + a.fM[8] * b.fM[7]; + tmp.fM[8] = a.fM[6] * b.fM[2] + a.fM[7] * b.fM[5] + a.fM[8] * b.fM[8]; + } + *this = tmp; + this->computeTypeMask(); +} + +void GrMatrix::preConcat(const GrMatrix& m) { + setConcat(*this, m); +} + +void GrMatrix::postConcat(const GrMatrix& m) { + setConcat(m, *this); +} + +double GrMatrix::determinant() const { + if (fTypeMask & kPerspective_TypeBit) { + return fM[0]*((double)fM[4]*fM[8] - (double)fM[5]*fM[7]) + + fM[1]*((double)fM[5]*fM[6] - (double)fM[3]*fM[8]) + + fM[2]*((double)fM[3]*fM[7] - (double)fM[4]*fM[6]); + } else { + return (double)fM[0]*fM[4]*gRESCALE - + (double)fM[1]*fM[3]*gRESCALE; + } +} + +bool GrMatrix::invert(GrMatrix* inverted) const { + + if (isIdentity()) { + if (inverted != this) { + inverted->setIdentity(); + } + return true; + } + static const double MIN_DETERMINANT_SQUARED = 1.e-16; + + // could do more optimizations based on type bits. Hopefully this call is + // low frequency. + + double det = determinant(); + + // check if we can't be inverted + if (det*det <= MIN_DETERMINANT_SQUARED) { + return false; + } else if (NULL == inverted) { + return true; + } + + double t[9]; + + if (fTypeMask & kPerspective_TypeBit) { + t[0] = ((double)fM[4]*fM[8] - (double)fM[5]*fM[7]); + t[1] = ((double)fM[2]*fM[7] - (double)fM[1]*fM[8]); + t[2] = ((double)fM[1]*fM[5] - (double)fM[2]*fM[4]); + t[3] = ((double)fM[5]*fM[6] - (double)fM[3]*fM[8]); + t[4] = ((double)fM[0]*fM[8] - (double)fM[2]*fM[6]); + t[5] = ((double)fM[2]*fM[3] - (double)fM[0]*fM[5]); + t[6] = ((double)fM[3]*fM[7] - (double)fM[4]*fM[6]); + t[7] = ((double)fM[1]*fM[6] - (double)fM[0]*fM[7]); + t[8] = ((double)fM[0]*fM[4] - (double)fM[1]*fM[3]); + det = 1.0 / det; + for (int i = 0; i < 9; ++i) { + inverted->fM[i] = (GrScalar)(t[i] * det); + } + } else { + t[0] = (double)fM[4]*gRESCALE; + t[1] = -(double)fM[1]*gRESCALE; + t[2] = (double)fM[1]*fM[5] - (double)fM[2]*fM[4]; + t[3] = -(double)fM[3]*gRESCALE; + t[4] = (double)fM[0]*gRESCALE; + t[5] = (double)fM[2]*fM[3] - (double)fM[0]*fM[5]; + //t[6] = 0.0; + //t[7] = 0.0; + t[8] = (double)fM[0]*fM[4] - (double)fM[1]*fM[3]; + det = 1.0 / det; + for (int i = 0; i < 6; ++i) { + inverted->fM[i] = (GrScalar)(t[i] * det); + } + inverted->fM[6] = 0; + inverted->fM[7] = 0; + inverted->fM[8] = (GrScalar)(t[8] * det); + } + inverted->computeTypeMask(); + return true; +} + +void GrMatrix::mapRect(GrRect* dst, const GrRect& src) const { + GrPoint srcPts[4], dstPts[4]; + srcPts[0].set(src.fLeft, src.fTop); + srcPts[1].set(src.fRight, src.fTop); + srcPts[2].set(src.fRight, src.fBottom); + srcPts[3].set(src.fLeft, src.fBottom); + this->mapPoints(dstPts, srcPts, 4); + dst->setBounds(dstPts, 4); +} + +bool GrMatrix::hasPerspective() const { + GrAssert(!!(kPerspective_TypeBit & fTypeMask) == + (fM[kPersp0] != 0 || fM[kPersp1] != 0 || fM[kPersp2] != gRESCALE)); + return 0 != (kPerspective_TypeBit & fTypeMask); +} + +bool GrMatrix::isIdentity() const { + GrAssert((0 == fTypeMask) == + (GR_Scalar1 == fM[kScaleX] && 0 == fM[kSkewX] && 0 == fM[kTransX] && + 0 == fM[kSkewY] && GR_Scalar1 == fM[kScaleY] && 0 == fM[kTransY] && + 0 == fM[kPersp0] && 0 == fM[kPersp1] && gRESCALE == fM[kPersp2])); + return (0 == fTypeMask); +} + + +bool GrMatrix::preservesAxisAlignment() const { + + // check if matrix is trans and scale only + static const int gAllowedMask1 = kScale_TypeBit | kTranslate_TypeBit; + + if (!(~gAllowedMask1 & fTypeMask)) { + return true; + } + + // check matrix is trans and skew only (0 scale) + static const int gAllowedMask2 = kScale_TypeBit | kSkew_TypeBit | + kTranslate_TypeBit | kZeroScale_TypeBit; + + if (!(~gAllowedMask2 & fTypeMask) && (kZeroScale_TypeBit & fTypeMask)) { + return true; + } + + return false; +} + +GrScalar GrMatrix::getMaxStretch() const { + + if (fTypeMask & kPerspective_TypeBit) { + return -GR_Scalar1; + } + + GrScalar stretch; + + if (isIdentity()) { + stretch = GR_Scalar1; + } else if (!(fTypeMask & kSkew_TypeBit)) { + stretch = GrMax(GrScalarAbs(fM[kScaleX]), GrScalarAbs(fM[kScaleY])); + } else if (fTypeMask & kZeroScale_TypeBit) { + stretch = GrMax(GrScalarAbs(fM[kSkewX]), GrScalarAbs(fM[kSkewY])); + } else { + // ignore the translation part of the matrix, just look at 2x2 portion. + // compute singular values, take largest abs value. + // [a b; b c] = A^T*A + GrScalar a = GrMul(fM[kScaleX], fM[kScaleX]) + GrMul(fM[kSkewY], fM[kSkewY]); + GrScalar b = GrMul(fM[kScaleX], fM[kSkewX]) + GrMul(fM[kScaleY], fM[kSkewY]); + GrScalar c = GrMul(fM[kSkewX], fM[kSkewX]) + GrMul(fM[kScaleY], fM[kScaleY]); + // eigenvalues of A^T*A are the squared singular values of A. + // characteristic equation is det((A^T*A) - l*I) = 0 + // l^2 - (a + c)l + (ac-b^2) + // solve using quadratic equation (divisor is non-zero since l^2 has 1 coeff + // and roots are guaraunteed to be pos and real). + GrScalar largerRoot; + GrScalar bSqd = GrMul(b,b); + // TODO: fixed point tolerance value. + if (bSqd < 1e-10) { // will be true if upper left 2x2 is orthogonal, which is common, so save some math + largerRoot = GrMax(a, c); + } else { + GrScalar aminusc = a - c; + GrScalar apluscdiv2 = (a + c) / 2; + GrScalar x = sqrtf(GrMul(aminusc,aminusc) + GrMul(4,(bSqd))) / 2; + largerRoot = apluscdiv2 + x; + } + + stretch = sqrtf(largerRoot); + } +#if GR_DEBUG && 0 + // test a bunch of vectors. None should be scaled by more than stretch + // (modulo some error) and we should find a vector that is scaled by almost + // stretch. + GrPoint pt; + GrScalar max = 0; + for (int i = 0; i < 1000; ++i) { + GrScalar x = (float)rand() / RAND_MAX; + GrScalar y = sqrtf(1 - (x*x)); + pt.fX = fM[kScaleX]*x + fM[kSkewX]*y; + pt.fY = fM[kSkewY]*x + fM[kScaleY]*y; + GrScalar d = pt.distanceToOrigin(); + GrAssert(d <= (1.0001 * stretch)); + max = GrMax(max, pt.distanceToOrigin()); + } + GrAssert((stretch - max) < .05*stretch); +#endif + return stretch; +} + +bool GrMatrix::operator == (const GrMatrix& m) const { + if (fTypeMask != m.fTypeMask) { + return false; + } + if (!fTypeMask) { + return true; + } + for (int i = 0; i < 9; ++i) { + if (m.fM[i] != fM[i]) { + return false; + } + } + return true; +} + +bool GrMatrix::operator != (const GrMatrix& m) const { + return !(*this == m); +} + +//////////////////////////////////////////////////////////////////////////////// +// Matrix transformation procs +////// + +void GrMatrix::mapIdentity(GrPoint* dst, const GrPoint* src, uint32_t count) const { + if (src != dst) { + for (uint32_t i = 0; i < count; ++i) { + dst[i] = src[i]; + } + } +} + +void GrMatrix::mapScale(GrPoint* dst, const GrPoint* src, uint32_t count) const { + for (uint32_t i = 0; i < count; ++i) { + dst[i].fX = GrMul(src[i].fX, fM[kScaleX]); + dst[i].fY = GrMul(src[i].fY, fM[kScaleY]); + } +} + + +void GrMatrix::mapTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const { + for (uint32_t i = 0; i < count; ++i) { + dst[i].fX = src[i].fX + fM[kTransX]; + dst[i].fY = src[i].fY + fM[kTransY]; + } +} + +void GrMatrix::mapScaleAndTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const { + for (uint32_t i = 0; i < count; ++i) { + dst[i].fX = GrMul(src[i].fX, fM[kScaleX]) + fM[kTransX]; + dst[i].fY = GrMul(src[i].fY, fM[kScaleY]) + fM[kTransY]; + } +} + +void GrMatrix::mapSkew(GrPoint* dst, const GrPoint* src, uint32_t count) const { + if (src != dst) { + for (uint32_t i = 0; i < count; ++i) { + dst[i].fX = src[i].fX + GrMul(src[i].fY, fM[kSkewX]); + dst[i].fY = src[i].fY + GrMul(src[i].fX, fM[kSkewY]); + } + } else { + for (uint32_t i = 0; i < count; ++i) { + GrScalar newX = src[i].fX + GrMul(src[i].fY, fM[kSkewX]); + dst[i].fY = src[i].fY + GrMul(src[i].fX, fM[kSkewY]); + dst[i].fX = newX; + } + } +} + +void GrMatrix::mapScaleAndSkew(GrPoint* dst, const GrPoint* src, uint32_t count) const { + if (src != dst) { + for (uint32_t i = 0; i < count; ++i) { + dst[i].fX = GrMul(src[i].fX, fM[kScaleX]) + GrMul(src[i].fY, fM[kSkewX]); + dst[i].fY = GrMul(src[i].fY, fM[kScaleY]) + GrMul(src[i].fX, fM[kSkewY]); + } + } else { + for (uint32_t i = 0; i < count; ++i) { + GrScalar newX = GrMul(src[i].fX, fM[kScaleX]) + GrMul(src[i].fY, fM[kSkewX]); + dst[i].fY = GrMul(src[i].fY, fM[kScaleY]) + GrMul(src[i].fX, fM[kSkewY]); + dst[i].fX = newX; + } + } +} + +void GrMatrix::mapSkewAndTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const { + if (src != dst) { + for (uint32_t i = 0; i < count; ++i) { + dst[i].fX = src[i].fX + GrMul(src[i].fY, fM[kSkewX]) + fM[kTransX]; + dst[i].fY = src[i].fY + GrMul(src[i].fX, fM[kSkewY]) + fM[kTransY]; + } + } else { + for (uint32_t i = 0; i < count; ++i) { + GrScalar newX = src[i].fX + GrMul(src[i].fY, fM[kSkewX]) + fM[kTransX]; + dst[i].fY = src[i].fY + GrMul(src[i].fX, fM[kSkewY]) + fM[kTransY]; + dst[i].fX = newX; + } + } +} + +void GrMatrix::mapNonPerspective(GrPoint* dst, const GrPoint* src, uint32_t count) const { + if (src != dst) { + for (uint32_t i = 0; i < count; ++i) { + dst[i].fX = GrMul(fM[kScaleX], src[i].fX) + GrMul(fM[kSkewX], src[i].fY) + fM[kTransX]; + dst[i].fY = GrMul(fM[kSkewY], src[i].fX) + GrMul(fM[kScaleY], src[i].fY) + fM[kTransY]; + } + } else { + for (uint32_t i = 0; i < count; ++i) { + GrScalar newX = GrMul(fM[kScaleX], src[i].fX) + GrMul(fM[kSkewX], src[i].fY) + fM[kTransX]; + dst[i].fY = GrMul(fM[kSkewY], src[i].fX) + GrMul(fM[kScaleY], src[i].fY) + fM[kTransY]; + dst[i].fX = newX; + } + } +} + +void GrMatrix::mapPerspective(GrPoint* dst, const GrPoint* src, uint32_t count) const { + for (uint32_t i = 0; i < count; ++i) { + GrScalar x, y, w; + x = GrMul(fM[kScaleX], src[i].fX) + GrMul(fM[kSkewX], src[i].fY) + fM[kTransX]; + y = GrMul(fM[kSkewY], src[i].fX) + GrMul(fM[kScaleY], src[i].fY) + fM[kTransY]; + w = GrMul(fM[kPersp0], src[i].fX) + GrMul(fM[kPersp1], src[i].fY) + fM[kPersp2]; + // TODO need fixed point invert + if (w) { + w = 1 / w; + } + dst[i].fX = GrMul(x, w); + dst[i].fY = GrMul(y, w); + } +} + +void GrMatrix::mapInvalid(GrPoint* dst, const GrPoint* src, uint32_t count) const { + GrAssert(0); +} + +void GrMatrix::mapZero(GrPoint* dst, const GrPoint* src, uint32_t count) const { + memset(dst, 0, sizeof(GrPoint)*count); +} + +void GrMatrix::mapSetToTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const { + for (uint32_t i = 0; i < count; ++i) { + dst[i].fX = fM[kTransX]; + dst[i].fY = fM[kTransY]; + } +} + +void GrMatrix::mapSwappedScale(GrPoint* dst, const GrPoint* src, uint32_t count) const { + if (src != dst) { + for (uint32_t i = 0; i < count; ++i) { + dst[i].fX = GrMul(src[i].fY, fM[kSkewX]); + dst[i].fY = GrMul(src[i].fX, fM[kSkewY]); + } + } else { + for (uint32_t i = 0; i < count; ++i) { + GrScalar newX = GrMul(src[i].fY, fM[kSkewX]); + dst[i].fY = GrMul(src[i].fX, fM[kSkewY]); + dst[i].fX = newX; + } + } +} + +void GrMatrix::mapSwappedScaleAndTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const { + if (src != dst) { + for (uint32_t i = 0; i < count; ++i) { + dst[i].fX = GrMul(src[i].fY, fM[kSkewX]) + fM[kTransX]; + dst[i].fY = GrMul(src[i].fX, fM[kSkewY]) + fM[kTransY]; + } + } else { + for (uint32_t i = 0; i < count; ++i) { + GrScalar newX = GrMul(src[i].fY, fM[kSkewX]) + fM[kTransX]; + dst[i].fY = GrMul(src[i].fX, fM[kSkewY]) + fM[kTransY]; + dst[i].fX = newX; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Unit test +////// + +#include "GrRandom.h" + +#if GR_DEBUG +enum MatrixType { + kRotate_MatrixType, + kScaleX_MatrixType, + kScaleY_MatrixType, + kSkewX_MatrixType, + kSkewY_MatrixType, + kTranslateX_MatrixType, + kTranslateY_MatrixType, + kSwapScaleXY_MatrixType, + kPersp_MatrixType, + + kMatrixTypeCount +}; + +static void create_matrix(GrMatrix* matrix, GrRandom& rand) { + MatrixType type = (MatrixType)(rand.nextU() % kMatrixTypeCount); + switch (type) { + case kRotate_MatrixType: { + float angle = rand.nextF() * 2 *3.14159265358979323846f; + GrScalar cosa = GrFloatToScalar(cosf(angle)); + GrScalar sina = GrFloatToScalar(sinf(angle)); + matrix->setAll(cosa, -sina, 0, + sina, cosa, 0, + 0, 0, GrMatrix::I()[8]); + } break; + case kScaleX_MatrixType: { + GrScalar scale = GrFloatToScalar(rand.nextF(-2, 2)); + matrix->setAll(scale, 0, 0, + 0, GR_Scalar1, 0, + 0, 0, GrMatrix::I()[8]); + } break; + case kScaleY_MatrixType: { + GrScalar scale = GrFloatToScalar(rand.nextF(-2, 2)); + matrix->setAll(GR_Scalar1, 0, 0, + 0, scale, 0, + 0, 0, GrMatrix::I()[8]); + } break; + case kSkewX_MatrixType: { + GrScalar skew = GrFloatToScalar(rand.nextF(-2, 2)); + matrix->setAll(GR_Scalar1, skew, 0, + 0, GR_Scalar1, 0, + 0, 0, GrMatrix::I()[8]); + } break; + case kSkewY_MatrixType: { + GrScalar skew = GrFloatToScalar(rand.nextF(-2, 2)); + matrix->setAll(GR_Scalar1, 0, 0, + skew, GR_Scalar1, 0, + 0, 0, GrMatrix::I()[8]); + } break; + case kTranslateX_MatrixType: { + GrScalar trans = GrFloatToScalar(rand.nextF(-10, 10)); + matrix->setAll(GR_Scalar1, 0, trans, + 0, GR_Scalar1, 0, + 0, 0, GrMatrix::I()[8]); + } break; + case kTranslateY_MatrixType: { + GrScalar trans = GrFloatToScalar(rand.nextF(-10, 10)); + matrix->setAll(GR_Scalar1, 0, 0, + 0, GR_Scalar1, trans, + 0, 0, GrMatrix::I()[8]); + } break; + case kSwapScaleXY_MatrixType: { + GrScalar xy = GrFloatToScalar(rand.nextF(-2, 2)); + GrScalar yx = GrFloatToScalar(rand.nextF(-2, 2)); + matrix->setAll(0, xy, 0, + yx, 0, 0, + 0, 0, GrMatrix::I()[8]); + } break; + case kPersp_MatrixType: { + GrScalar p0 = GrFloatToScalar(rand.nextF(-2, 2)); + GrScalar p1 = GrFloatToScalar(rand.nextF(-2, 2)); + GrScalar p2 = GrFloatToScalar(rand.nextF(-0.5f, 0.75f)); + matrix->setAll(GR_Scalar1, 0, 0, + 0, GR_Scalar1, 0, + p0, p1, GrMul(p2,GrMatrix::I()[8])); + } break; + default: + GrAssert(0); + break; + } +} +#endif + +void GrMatrix::UnitTest() { + GrRandom rand; + + // Create a bunch of matrices and test point mapping, max stretch calc, + // inversion and multiply-by-inverse. +#if GR_DEBUG + for (int i = 0; i < 10000; ++i) { + GrMatrix a, b; + a.setIdentity(); + int num = rand.nextU() % 6; + // force testing of I and swapXY + if (0 == i) { + num = 0; + GrAssert(a.isIdentity()); + } else if (1 == i) { + num = 0; + a.setAll(0, GR_Scalar1, 0, + GR_Scalar1, 0, 0, + 0, 0, I()[8]); + } + for (int j = 0; j < num; ++j) { + create_matrix(&b, rand); + a.preConcat(b); + } + + GrScalar maxStretch = a.getMaxStretch(); + if (maxStretch > 0) { + maxStretch = GrMul(GR_Scalar1 + GR_Scalar1 / 100, maxStretch); + } + GrPoint origin = a.mapPoint(GrPoint::Make(0,0)); + + for (int j = 0; j < 9; ++j) { + int mask, origMask = a.fTypeMask; + GrScalar old = a[j]; + + a.set(j, GR_Scalar1); + mask = a.fTypeMask; + a.computeTypeMask(); + GrAssert(mask == a.fTypeMask); + + a.set(j, 0); + mask = a.fTypeMask; + a.computeTypeMask(); + GrAssert(mask == a.fTypeMask); + + a.set(j, 10 * GR_Scalar1); + mask = a.fTypeMask; + a.computeTypeMask(); + GrAssert(mask == a.fTypeMask); + + a.set(j, old); + GrAssert(a.fTypeMask == origMask); + } + + for (int j = 0; j < 100; ++j) { + GrPoint pt; + pt.fX = GrFloatToScalar(rand.nextF(-10, 10)); + pt.fY = GrFloatToScalar(rand.nextF(-10, 10)); + + GrPoint t0, t1, t2; + t0 = a.mapPoint(pt); // map to a new point + t1 = pt; + a.mapPoints(&t1, &t1, 1); // in place + a.mapPerspective(&t2, &pt, 1); // full mult + GrAssert(t0 == t1 && t1 == t2); + if (maxStretch >= 0.f) { + GrVec vec = origin - t0; +// vec.setBetween(t0, origin); + GrScalar stretch = vec.length() / pt.distanceToOrigin(); + GrAssert(stretch <= maxStretch); + } + } + double det = a.determinant(); + if (fabs(det) > 1e-3 && a.invert(&b)) { + GrMatrix c; + c.setConcat(a,b); + for (int i = 0; i < 9; ++i) { + GrScalar diff = GrScalarAbs(c[i] - I()[i]); + GrAssert(diff < (5*GR_Scalar1 / 100)); + } + } + } +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +#endif + +int Gr_clz(uint32_t n) { + if (0 == n) { + return 32; + } + + int count = 0; + if (0 == (n & 0xFFFF0000)) { + count += 16; + n <<= 16; + } + if (0 == (n & 0xFF000000)) { + count += 8; + n <<= 8; + } + if (0 == (n & 0xF0000000)) { + count += 4; + n <<= 4; + } + if (0 == (n & 0xC0000000)) { + count += 2; + n <<= 2; + } + if (0 == (n & 0x80000000)) { + count += 1; + } + return count; +} |