|  | 
 | /* | 
 |  * 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; | 
 | } |