[gpu] Remove getMaxStretch for perspective, use mapRadius for perspective path subdiv...
authorbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 9 Sep 2011 19:32:04 +0000 (19:32 +0000)
committerbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 9 Sep 2011 19:32:04 +0000 (19:32 +0000)
Review URL: http://codereview.appspot.com/4975063/

git-svn-id: http://skia.googlecode.com/svn/trunk@2246 2bbb7eff-a529-9590-31e7-b0007b416f81

gpu/src/GrDefaultPathRenderer.cpp
gpu/src/GrPathUtils.cpp
gpu/src/GrPathUtils.h
gpu/src/GrTesselatedPathRenderer.cpp
include/core/SkMatrix.h
src/core/SkMatrix.cpp
tests/MatrixTest.cpp

index 3323c91..278c751 100644 (file)
@@ -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
index b7dc4b6..0a7759d 100644 (file)
 #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;
index 8d77982..a5f80d9 100644 (file)
@@ -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.
index dd281ed..a3da707 100644 (file)
@@ -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;
index 333749c..8c7f54b 100644 (file)
@@ -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;
 
index 6fc6e5d..6e0bf02 100644 (file)
@@ -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() {
index df24a5a..4638fe1 100644 (file)
@@ -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"