[gpu] Remove getMaxStretch for perspective, use mapRadius for perspective path subdiv tol, add test
Review URL: http://codereview.appspot.com/4975063/ git-svn-id: http://skia.googlecode.com/svn/trunk@2246 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
99edd43813
commit
383963280d
@ -375,7 +375,7 @@ void GrDefaultPathRenderer::onDrawPath(GrDrawTarget::StageBitfield stages,
|
||||
|
||||
GrMatrix viewM = fTarget->getViewMatrix();
|
||||
GrScalar tol = GR_Scalar1;
|
||||
tol = GrPathUtils::scaleToleranceToSrc(tol, viewM);
|
||||
tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, fPath->getBounds());
|
||||
|
||||
// FIXME: It's really dumb that we recreate the verts for a new vertex
|
||||
// layout. We only do that because the GrDrawTarget API doesn't allow
|
||||
|
@ -12,16 +12,23 @@
|
||||
#include "GrPoint.h"
|
||||
|
||||
GrScalar GrPathUtils::scaleToleranceToSrc(GrScalar devTol,
|
||||
const GrMatrix& viewM) {
|
||||
const GrMatrix& viewM,
|
||||
const GrRect& pathBounds) {
|
||||
// In order to tesselate the path we get a bound on how much the matrix can
|
||||
// stretch when mapping to screen coordinates.
|
||||
GrScalar stretch = viewM.getMaxStretch();
|
||||
GrScalar srcTol = devTol;
|
||||
|
||||
if (stretch < 0) {
|
||||
// TODO: deal with perspective in some better way.
|
||||
srcTol /= 5;
|
||||
stretch = -stretch;
|
||||
// take worst case mapRadius amoung four corners.
|
||||
// (less than perfect)
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
GrMatrix mat;
|
||||
mat.setTranslate((i % 2) ? pathBounds.fLeft : pathBounds.fRight,
|
||||
(i < 2) ? pathBounds.fTop : pathBounds.fBottom);
|
||||
mat.postConcat(viewM);
|
||||
stretch = SkMaxScalar(stretch, mat.mapRadius(SK_Scalar1));
|
||||
}
|
||||
}
|
||||
srcTol = GrScalarDiv(srcTol, stretch);
|
||||
return srcTol;
|
||||
|
@ -20,7 +20,8 @@ class GrPoint;
|
||||
*/
|
||||
namespace GrPathUtils {
|
||||
GrScalar scaleToleranceToSrc(GrScalar devTol,
|
||||
const GrMatrix& viewM);
|
||||
const GrMatrix& viewM,
|
||||
const GrRect& pathBounds);
|
||||
|
||||
/// Since we divide by tol if we're computing exact worst-case bounds,
|
||||
/// very small tolerances will be increased to gMinCurveTol.
|
||||
|
@ -353,7 +353,7 @@ void GrTesselatedPathRenderer::drawPath(GrDrawTarget::StageBitfield stages) {
|
||||
GrMatrix viewM = fTarget->getViewMatrix();
|
||||
|
||||
GrScalar tol = GR_Scalar1;
|
||||
tol = GrPathUtils::scaleToleranceToSrc(tol, viewM);
|
||||
tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, fPath->getBounds());
|
||||
GrScalar tolSqd = GrMul(tol, tol);
|
||||
|
||||
int subpathCnt;
|
||||
|
@ -502,11 +502,9 @@ public:
|
||||
|
||||
/**
|
||||
* Calculates the maximum stretching factor of the matrix. If the matrix has
|
||||
* perspective the max stretch at the origin (in the pre-matrix space) is
|
||||
* computed and returned as a negative.
|
||||
* perspective -1 is returned.
|
||||
*
|
||||
* @return maximum strecthing factor or negative max stretching factor at
|
||||
* the origin if matrix has perspective.
|
||||
* @return maximum strecthing factor
|
||||
*/
|
||||
SkScalar getMaxStretch() const;
|
||||
|
||||
|
@ -1674,71 +1674,42 @@ bool SkMatrix::setPolyToPoly(const SkPoint src[], const SkPoint dst[],
|
||||
SkScalar SkMatrix::getMaxStretch() const {
|
||||
TypeMask mask = this->getType();
|
||||
|
||||
SkScalar stretch;
|
||||
|
||||
if (this->hasPerspective()) {
|
||||
return -SK_Scalar1;
|
||||
}
|
||||
if (this->isIdentity()) {
|
||||
stretch = SK_Scalar1;
|
||||
} else if (!(mask & kAffine_Mask)) {
|
||||
stretch = SkMaxScalar(SkScalarAbs(fMat[kMScaleX]), SkScalarAbs(fMat[kMScaleY]));
|
||||
#if 0 // don't have this bit
|
||||
} else if (mask & kZeroScale_TypeBit) {
|
||||
stretch = SkMaxScalar(SkScalarAbs(fM[kSkewX]), SkScalarAbs(fM[kSkewY]));
|
||||
#endif
|
||||
return SK_Scalar1;
|
||||
}
|
||||
if (!(mask & kAffine_Mask)) {
|
||||
return SkMaxScalar(SkScalarAbs(fMat[kMScaleX]),
|
||||
SkScalarAbs(fMat[kMScaleY]));
|
||||
}
|
||||
// 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
|
||||
SkScalar a = SkScalarMul(fMat[kMScaleX], fMat[kMScaleX]) +
|
||||
SkScalarMul(fMat[kMSkewY], fMat[kMSkewY]);
|
||||
SkScalar b = SkScalarMul(fMat[kMScaleX], fMat[kMSkewX]) +
|
||||
SkScalarMul(fMat[kMScaleY], fMat[kMSkewY]);
|
||||
SkScalar c = SkScalarMul(fMat[kMSkewX], fMat[kMSkewX]) +
|
||||
SkScalarMul(fMat[kMScaleY], fMat[kMScaleY]);
|
||||
// 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).
|
||||
SkScalar largerRoot;
|
||||
SkScalar bSqd = SkScalarMul(b,b);
|
||||
// if upper left 2x2 is orthogonal save some math
|
||||
if (bSqd <= SK_ScalarNearlyZero) {
|
||||
largerRoot = SkMaxScalar(a, c);
|
||||
} 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
|
||||
SkScalar a = SkScalarMul(fMat[kMScaleX], fMat[kMScaleX]) + SkScalarMul(fMat[kMSkewY], fMat[kMSkewY]);
|
||||
SkScalar b = SkScalarMul(fMat[kMScaleX], fMat[kMSkewX]) + SkScalarMul(fMat[kMScaleY], fMat[kMSkewY]);
|
||||
SkScalar c = SkScalarMul(fMat[kMSkewX], fMat[kMSkewX]) + SkScalarMul(fMat[kMScaleY], fMat[kMScaleY]);
|
||||
// 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).
|
||||
SkScalar largerRoot;
|
||||
SkScalar bSqd = SkScalarMul(b,b);
|
||||
// if upper left 2x2 is orthogonal save some math
|
||||
if (bSqd <= SK_ScalarNearlyZero) {
|
||||
largerRoot = SkMaxScalar(a, c);
|
||||
} else {
|
||||
SkScalar aminusc = a - c;
|
||||
SkScalar apluscdiv2 = (a + c) / 2;
|
||||
SkScalar x = SkScalarSqrt(SkScalarMul(aminusc, aminusc) + 4 * bSqd) / 2;
|
||||
largerRoot = apluscdiv2 + x;
|
||||
}
|
||||
stretch = SkScalarSqrt(largerRoot);
|
||||
if (mask & kPerspective_Mask) {
|
||||
stretch = -stretch;
|
||||
if (fMat[kMPersp2] != kMatrix22Elem) {
|
||||
#if defined(SK_SCALAR_IS_FLOAT)
|
||||
stretch /= fMat[kMPersp2];
|
||||
#else
|
||||
stretch = SkFractDiv(stretch, fMat[kMPersp2]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
SkScalar aminusc = a - c;
|
||||
SkScalar apluscdiv2 = SkScalarHalf(a + c);
|
||||
SkScalar x = SkScalarHalf(SkScalarSqrt(SkScalarMul(aminusc, aminusc) + 4 * bSqd));
|
||||
largerRoot = apluscdiv2 + x;
|
||||
}
|
||||
#if defined(SK_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.
|
||||
SkPoint pt;
|
||||
SkScalar max = 0;
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
SkScalar x = (float)rand() / RAND_MAX;
|
||||
SkScalar y = sqrtf(1 - (x*x));
|
||||
pt.fX = fMat[kMScaleX]*x + fMat[kMSkewX]*y;
|
||||
pt.fY = fMat[kMSkewY]*x + fMat[kMScaleY]*y;
|
||||
SkScalar d = pt.distanceToOrigin();
|
||||
SkASSERT(d <= (1.0001 * stretch));
|
||||
if (max < pt.distanceToOrigin()) {
|
||||
max = pt.distanceToOrigin();
|
||||
}
|
||||
}
|
||||
SkASSERT((stretch - max) < .05*stretch);
|
||||
#endif
|
||||
return stretch;
|
||||
return SkScalarSqrt(largerRoot);
|
||||
}
|
||||
|
||||
const SkMatrix& SkMatrix::I() {
|
||||
|
@ -7,6 +7,7 @@
|
||||
*/
|
||||
#include "Test.h"
|
||||
#include "SkMatrix.h"
|
||||
#include "SkRandom.h"
|
||||
|
||||
static bool nearly_equal_scalar(SkScalar a, SkScalar b) {
|
||||
// Note that we get more compounded error for multiple operations when
|
||||
@ -55,6 +56,94 @@ static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) {
|
||||
REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
|
||||
}
|
||||
|
||||
void test_matrix_max_stretch(skiatest::Reporter* reporter) {
|
||||
SkMatrix identity;
|
||||
identity.reset();
|
||||
REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxStretch());
|
||||
|
||||
SkMatrix scale;
|
||||
scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4);
|
||||
REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxStretch());
|
||||
|
||||
SkMatrix rot90Scale;
|
||||
rot90Scale.setRotate(90 * SK_Scalar1);
|
||||
rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2);
|
||||
REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxStretch());
|
||||
|
||||
SkMatrix rotate;
|
||||
rotate.setRotate(128 * SK_Scalar1);
|
||||
REPORTER_ASSERT(reporter, SkScalarAbs(SK_Scalar1 - rotate.getMaxStretch()) <= SK_ScalarNearlyZero);
|
||||
|
||||
SkMatrix translate;
|
||||
translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1);
|
||||
REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxStretch());
|
||||
|
||||
SkMatrix perspX;
|
||||
perspX.reset();
|
||||
perspX.setPerspX(SK_Scalar1 / 1000);
|
||||
REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxStretch());
|
||||
|
||||
SkMatrix perspY;
|
||||
perspY.reset();
|
||||
perspY.setPerspX(-SK_Scalar1 / 500);
|
||||
REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxStretch());
|
||||
|
||||
SkMatrix baseMats[] = {scale, rot90Scale, rotate,
|
||||
translate, perspX, perspY};
|
||||
SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)];
|
||||
for (int i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) {
|
||||
mats[i] = baseMats[i];
|
||||
bool invertable = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]);
|
||||
REPORTER_ASSERT(reporter, invertable);
|
||||
}
|
||||
SkRandom rand;
|
||||
for (int m = 0; m < 1000; ++m) {
|
||||
SkMatrix mat;
|
||||
mat.reset();
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
int x = rand.nextU() % SK_ARRAY_COUNT(mats);
|
||||
mat.postConcat(mats[x]);
|
||||
}
|
||||
SkScalar stretch = mat.getMaxStretch();
|
||||
|
||||
if ((stretch < 0) != mat.hasPerspective()) {
|
||||
stretch = mat.getMaxStretch();
|
||||
}
|
||||
|
||||
REPORTER_ASSERT(reporter, (stretch < 0) == mat.hasPerspective());
|
||||
|
||||
if (mat.hasPerspective()) {
|
||||
m -= 1; // try another non-persp matrix
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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.
|
||||
static const SkScalar gStretchTol = (105 * SK_Scalar1) / 100;
|
||||
static const SkScalar gMaxStretchTol = (97 * SK_Scalar1) / 100;
|
||||
SkScalar max = 0;
|
||||
SkVector vectors[1000];
|
||||
for (int i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
|
||||
vectors[i].fX = rand.nextSScalar1();
|
||||
vectors[i].fY = rand.nextSScalar1();
|
||||
if (!vectors[i].normalize()) {
|
||||
i -= 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors));
|
||||
for (int i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
|
||||
SkScalar d = vectors[i].length();
|
||||
REPORTER_ASSERT(reporter, SkScalarDiv(d, stretch) < gStretchTol);
|
||||
if (max < d) {
|
||||
max = d;
|
||||
}
|
||||
}
|
||||
REPORTER_ASSERT(reporter, SkScalarDiv(max, stretch) >= gMaxStretchTol);
|
||||
}
|
||||
}
|
||||
|
||||
void TestMatrix(skiatest::Reporter* reporter) {
|
||||
SkMatrix mat, inverse, iden1, iden2;
|
||||
|
||||
@ -146,6 +235,8 @@ void TestMatrix(skiatest::Reporter* reporter) {
|
||||
|
||||
mat.set(SkMatrix::kMPersp1, SkIntToScalar(1));
|
||||
REPORTER_ASSERT(reporter, !mat.asAffine(affine));
|
||||
|
||||
test_matrix_max_stretch(reporter);
|
||||
}
|
||||
|
||||
#include "TestClassDef.h"
|
||||
|
Loading…
Reference in New Issue
Block a user