Improve gpu path subdiv with perspective, remove tolerance scale, fix comment
Review URL: http://codereview.appspot.com/4993041/
git-svn-id: http://skia.googlecode.com/svn/trunk@2239 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index 639e25d..b857dda 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -692,11 +692,6 @@
GR_STATIC_ASSERT(4 == OFFSCREEN_SSAA_SCALE);
desc.fAALevel = kNone_GrAALevel;
}
- // Avoid overtesselating paths in AA buffers; may unduly reduce quality
- // of simple circles?
- if (pr) {
- //pr->scaleCurveTolerance(GrIntToScalar(record->fScale));
- }
desc.fWidth *= record->fScale;
desc.fHeight *= record->fScale;
@@ -881,10 +876,6 @@
void GrContext::cleanupOffscreenAA(GrDrawTarget* target,
GrPathRenderer* pr,
OffscreenRecord* record) {
- if (pr) {
- // Counterpart of scale() in prepareForOffscreenAA()
- //pr->scaleCurveTolerance(SkScalarInvert(SkIntToScalar(record->fScale)));
- }
target->restoreDrawState(record->fSavedState);
}
diff --git a/gpu/src/GrDefaultPathRenderer.cpp b/gpu/src/GrDefaultPathRenderer.cpp
index b70b863..3323c91 100644
--- a/gpu/src/GrDefaultPathRenderer.cpp
+++ b/gpu/src/GrDefaultPathRenderer.cpp
@@ -374,18 +374,9 @@
bool stencilOnly) {
GrMatrix viewM = fTarget->getViewMatrix();
- // 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();
- bool useStretch = stretch > 0;
- GrScalar tol = fCurveTolerance;
+ GrScalar tol = GR_Scalar1;
+ tol = GrPathUtils::scaleToleranceToSrc(tol, viewM);
- if (!useStretch) {
- // TODO: deal with perspective in some better way.
- tol /= 10;
- } else {
- tol = GrScalarDiv(tol, stretch);
- }
// 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
// us to change the vertex layout after reserveVertexSpace(). We won't
diff --git a/gpu/src/GrPathRenderer.cpp b/gpu/src/GrPathRenderer.cpp
index 06a00e4..929941a 100644
--- a/gpu/src/GrPathRenderer.cpp
+++ b/gpu/src/GrPathRenderer.cpp
@@ -9,8 +9,7 @@
#include "GrPathRenderer.h"
GrPathRenderer::GrPathRenderer()
- : fCurveTolerance (GR_Scalar1)
- , fPath(NULL)
+ : fPath(NULL)
, fTarget(NULL) {
}
diff --git a/gpu/src/GrPathRenderer.h b/gpu/src/GrPathRenderer.h
index 4694de5..815da47 100644
--- a/gpu/src/GrPathRenderer.h
+++ b/gpu/src/GrPathRenderer.h
@@ -36,8 +36,8 @@
* This is called to install custom path renderers in every GrContext at
* create time. The default implementation in GrCreatePathRenderer_none.cpp
* does not add any additional renderers. Link against another
- * implementation to install your own. The most recently added is the
- * most preferred path renderer.
+ * implementation to install your own. The first added is the most preferred
+ * path renderer, second is second most preferred, etc.
*
* @param context the context that will use the path renderer
* @param flags flags indicating how path renderers will be used
@@ -170,16 +170,6 @@
}
/**
- * Multiply curve tolerance by the given value, increasing or decreasing
- * the maximum error permitted in tesselating curves with short straight
- * line segments.
- */
- void scaleCurveTolerance(GrScalar multiplier) {
- GrAssert(multiplier > 0);
- fCurveTolerance = SkScalarMul(fCurveTolerance, multiplier);
- }
-
- /**
* Helper that sets a path and automatically remove it in destructor.
*/
class AutoClearPath {
@@ -224,7 +214,6 @@
virtual void pathWasSet() {}
virtual void pathWillClear() {}
- GrScalar fCurveTolerance;
const SkPath* fPath;
GrDrawTarget* fTarget;
GrPathFill fFill;
diff --git a/gpu/src/GrPathUtils.cpp b/gpu/src/GrPathUtils.cpp
index 429b294..b7dc4b6 100644
--- a/gpu/src/GrPathUtils.cpp
+++ b/gpu/src/GrPathUtils.cpp
@@ -8,10 +8,27 @@
#include "GrPathUtils.h"
+
#include "GrPoint.h"
+GrScalar GrPathUtils::scaleToleranceToSrc(GrScalar devTol,
+ const GrMatrix& viewM) {
+ // 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;
+ }
+ srcTol = GrScalarDiv(srcTol, stretch);
+ return srcTol;
+}
+
static const int MAX_POINTS_PER_CURVE = 1 << 10;
-const GrScalar GrPathUtils::gMinCurveTol (GrFloatToScalar(0.0001f));
+static const GrScalar gMinCurveTol = GrFloatToScalar(0.0001f);
uint32_t GrPathUtils::quadraticPointCount(const GrPoint points[],
GrScalar tol) {
diff --git a/gpu/src/GrPathUtils.h b/gpu/src/GrPathUtils.h
index cde5601..8d77982 100644
--- a/gpu/src/GrPathUtils.h
+++ b/gpu/src/GrPathUtils.h
@@ -10,41 +10,42 @@
#ifndef GrPathUtils_DEFINED
#define GrPathUtils_DEFINED
-#include "GrNoncopyable.h"
-#include "GrPoint.h"
+#include "GrMatrix.h"
#include "GrPath.h"
+class GrPoint;
+
/**
* Utilities for evaluating paths.
*/
-class GrPathUtils : public GrNoncopyable {
-public:
- /// Since we divide by tol if we're computing exact worst-case bounds,
- /// very small tolerances will be increased to gMinCurveTol.
- static int worstCasePointCount(const GrPath&,
- int* subpaths,
- GrScalar tol);
- /// Since we divide by tol if we're computing exact worst-case bounds,
- /// very small tolerances will be increased to gMinCurveTol.
- static uint32_t quadraticPointCount(const GrPoint points[], GrScalar tol);
- static uint32_t generateQuadraticPoints(const GrPoint& p0,
- const GrPoint& p1,
- const GrPoint& p2,
- GrScalar tolSqd,
- GrPoint** points,
- uint32_t pointsLeft);
- /// Since we divide by tol if we're computing exact worst-case bounds,
- /// very small tolerances will be increased to gMinCurveTol.
- static uint32_t cubicPointCount(const GrPoint points[], GrScalar tol);
- static uint32_t generateCubicPoints(const GrPoint& p0,
- const GrPoint& p1,
- const GrPoint& p2,
- const GrPoint& p3,
- GrScalar tolSqd,
- GrPoint** points,
- uint32_t pointsLeft);
+namespace GrPathUtils {
+ GrScalar scaleToleranceToSrc(GrScalar devTol,
+ const GrMatrix& viewM);
-private:
- static const GrScalar gMinCurveTol;
+ /// Since we divide by tol if we're computing exact worst-case bounds,
+ /// very small tolerances will be increased to gMinCurveTol.
+ int worstCasePointCount(const GrPath&,
+ int* subpaths,
+ GrScalar tol);
+ /// Since we divide by tol if we're computing exact worst-case bounds,
+ /// very small tolerances will be increased to gMinCurveTol.
+ uint32_t quadraticPointCount(const GrPoint points[], GrScalar tol);
+ uint32_t generateQuadraticPoints(const GrPoint& p0,
+ const GrPoint& p1,
+ const GrPoint& p2,
+ GrScalar tolSqd,
+ GrPoint** points,
+ uint32_t pointsLeft);
+ /// Since we divide by tol if we're computing exact worst-case bounds,
+ /// very small tolerances will be increased to gMinCurveTol.
+ uint32_t cubicPointCount(const GrPoint points[], GrScalar tol);
+ uint32_t generateCubicPoints(const GrPoint& p0,
+ const GrPoint& p1,
+ const GrPoint& p2,
+ const GrPoint& p3,
+ GrScalar tolSqd,
+ GrPoint** points,
+ uint32_t pointsLeft);
+
};
#endif
diff --git a/gpu/src/GrTesselatedPathRenderer.cpp b/gpu/src/GrTesselatedPathRenderer.cpp
index 15e3cc0..dd281ed 100644
--- a/gpu/src/GrTesselatedPathRenderer.cpp
+++ b/gpu/src/GrTesselatedPathRenderer.cpp
@@ -351,18 +351,9 @@
GrAssert(GrDrawTarget::kBoth_DrawFace == fTarget->getDrawFace());
GrMatrix viewM = fTarget->getViewMatrix();
- // 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();
- bool useStretch = stretch > 0;
- GrScalar tol = fCurveTolerance;
- if (!useStretch) {
- // TODO: deal with perspective in some better way.
- tol /= 10;
- } else {
- tol = GrScalarDiv(tol, stretch);
- }
+ GrScalar tol = GR_Scalar1;
+ tol = GrPathUtils::scaleToleranceToSrc(tol, viewM);
GrScalar tolSqd = GrMul(tol, tol);
int subpathCnt;
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
index 7c2109c..333749c 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -501,10 +501,12 @@
void toDumpString(SkString*) const;
/**
- * Calculates the maximum stretching factor of the matrix. Only defined if
- * the matrix does not have perspective.
+ * 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.
*
- * @return maximum strecthing factor or negative if matrix has perspective.
+ * @return maximum strecthing factor or negative max stretching factor at
+ * the origin if matrix has perspective.
*/
SkScalar getMaxStretch() const;
diff --git a/samplecode/SampleHairCurves.cpp b/samplecode/SampleHairCurves.cpp
index 152cbeb..a3ea5e1 100644
--- a/samplecode/SampleHairCurves.cpp
+++ b/samplecode/SampleHairCurves.cpp
@@ -28,6 +28,11 @@
virtual void onDrawContent(SkCanvas* canvas) {
+ SkMatrix m;
+ m.reset();
+ m.setPerspX(0.00020);
+ canvas->concat(m);
+
SkPaint paint;
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kStroke_Style);
@@ -38,6 +43,7 @@
SkPath curves;
SkPath hulls;
SkPath ctrlPts;
+
for (int i = 0; i < 100; ++i) {
SkScalar pts[] = {
rand.nextUScalar1(), rand.nextUScalar1(),
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
index a525f11..6fc6e5d 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -1674,10 +1674,6 @@
SkScalar SkMatrix::getMaxStretch() const {
TypeMask mask = this->getType();
- if (mask & kPerspective_Mask) {
- return -SK_Scalar1;
- }
-
SkScalar stretch;
if (this->isIdentity()) {
@@ -1702,7 +1698,8 @@
// and roots are guaraunteed to be pos and real).
SkScalar largerRoot;
SkScalar bSqd = SkScalarMul(b,b);
- if (bSqd <= SkFloatToScalar(1e-10)) { // will be true if upper left 2x2 is orthogonal, which is common, so save some math
+ // if upper left 2x2 is orthogonal save some math
+ if (bSqd <= SK_ScalarNearlyZero) {
largerRoot = SkMaxScalar(a, c);
} else {
SkScalar aminusc = a - c;
@@ -1710,8 +1707,17 @@
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
+ }
+ }
}
#if defined(SK_DEBUG) && 0
// test a bunch of vectors. None should be scaled by more than stretch