/*
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#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;
}