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