From 6e83b13c226246041a33dc7bf0e92626581b5e79 Mon Sep 17 00:00:00 2001 From: Jim Van Verth Date: Fri, 10 Feb 2017 15:24:39 -0500 Subject: Use SDF path miplevels based on the original path's size. Should produce sharper results than arbitrary fixed sizes. Adds a new test to pathfill GM. BUG=chromium:682918 Change-Id: I5a394098665d01e995a244fde278236f1471e6c9 Reviewed-on: https://skia-review.googlesource.com/8328 Reviewed-by: Brian Salomon Commit-Queue: Jim Van Verth --- src/core/SkMathPriv.h | 24 +++++++++++++ src/gpu/ops/GrAADistanceFieldPathRenderer.cpp | 52 ++++++++++++++++----------- 2 files changed, 56 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/core/SkMathPriv.h b/src/core/SkMathPriv.h index 14ebeb17b7..5ef0cb25e4 100644 --- a/src/core/SkMathPriv.h +++ b/src/core/SkMathPriv.h @@ -131,6 +131,16 @@ static inline int SkNextPow2(int value) { return 1 << (32 - SkCLZ(value - 1)); } +/** +* Returns the largest power-of-2 that is <= the specified value. If value +* is already a power of 2, then it is returned unchanged. It is undefined +* if value is <= 0. +*/ +static inline int SkPrevPow2(int value) { + SkASSERT(value > 0); + return 1 << (32 - SkCLZ(value >> 1)); +} + /** * Returns the log2 of the specified value, were that value to be rounded up * to the next power of 2. It is undefined to pass 0. Examples: @@ -145,6 +155,20 @@ static inline int SkNextLog2(uint32_t value) { return 32 - SkCLZ(value - 1); } +/** +* Returns the log2 of the specified value, were that value to be rounded down +* to the previous power of 2. It is undefined to pass 0. Examples: +* SkPrevLog2(1) -> 0 +* SkPrevLog2(2) -> 1 +* SkPrevLog2(3) -> 1 +* SkPrevLog2(4) -> 2 +* SkPrevLog2(5) -> 2 +*/ +static inline int SkPrevLog2(uint32_t value) { + SkASSERT(value != 0); + return 32 - SkCLZ(value >> 1); +} + /////////////////////////////////////////////////////////////////////////////// /** diff --git a/src/gpu/ops/GrAADistanceFieldPathRenderer.cpp b/src/gpu/ops/GrAADistanceFieldPathRenderer.cpp index f817f86357..e1a04396eb 100644 --- a/src/gpu/ops/GrAADistanceFieldPathRenderer.cpp +++ b/src/gpu/ops/GrAADistanceFieldPathRenderer.cpp @@ -10,6 +10,7 @@ #include "GrBuffer.h" #include "GrContext.h" +#include "GrDistanceFieldGenFromVector.h" #include "GrDrawOpTest.h" #include "GrOpFlushState.h" #include "GrPipelineBuilder.h" @@ -20,10 +21,10 @@ #include "effects/GrDistanceFieldGeoProc.h" #include "ops/GrMeshDrawOp.h" -#include "SkPathOps.h" #include "SkAutoMalloc.h" #include "SkDistanceFieldGen.h" -#include "GrDistanceFieldGenFromVector.h" +#include "SkMathPriv.h" +#include "SkPathOps.h" #define ATLAS_TEXTURE_WIDTH 2048 #define ATLAS_TEXTURE_HEIGHT 2048 @@ -39,9 +40,12 @@ static int g_NumFreedShapes = 0; #endif // mip levels -static const int kSmallMIP = 32; -static const int kMediumMIP = 73; -static const int kLargeMIP = 162; +static const int kMinMIP = 16; +static const int kMinMIPLog = 4; +static const int kMaxMIP = 162; + +static const int kMaxDim = 73; +static const int kMaxSize = 2*kMaxMIP; // Callback to clear out internal path cache when eviction occurs void GrAADistanceFieldPathRenderer::HandleEviction(GrDrawOpAtlas::AtlasID id, void* pr) { @@ -107,14 +111,14 @@ bool GrAADistanceFieldPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) c return false; } - // Only support paths with bounds within kMediumMIP by kMediumMIP, - // scaled to have bounds within 2.0f*kLargeMIP by 2.0f*kLargeMIP. + // Only support paths with bounds within kMaxDim by kMaxDim, + // scaled to have bounds within kMaxSize by kMaxSize. // The goal is to accelerate rendering of lots of small paths that may be scaling. SkScalar maxScale = args.fViewMatrix->getMaxScale(); SkRect bounds = args.fShape->styledBounds(); SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height()); - return maxDim <= kMediumMIP && maxDim * maxScale <= 2.0f*kLargeMIP; + return maxDim <= kMaxDim && maxDim * maxScale <= kMaxSize; } //////////////////////////////////////////////////////////////////////////////// @@ -241,20 +245,28 @@ private: const SkRect& bounds = args.fShape.bounds(); SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height()); SkScalar size = maxScale * maxDim; - SkScalar desiredDimension; - // For minimizing (or the common case of identity) transforms, we try to - // create the DF at the appropriately sized native src-space path resolution. + // We try to create the DF at a power of two scaled path resolution (1/2, 1, 2, 4, etc) // In the majority of cases this will yield a crisper rendering. - if (size <= maxDim && maxDim < kSmallMIP) { - desiredDimension = maxDim; - } else if (size <= kSmallMIP) { - desiredDimension = kSmallMIP; - } else if (size <= maxDim) { - desiredDimension = maxDim; - } else if (size <= kMediumMIP) { - desiredDimension = kMediumMIP; + SkScalar mipScale = 1.0f; + // for small paths, scale up to a min size between kMinMIP and 2*kMinMIP + if (maxDim < kMinMIP) { + mipScale = SkIntToScalar(1 << (kMinMIPLog - SkPrevLog2(maxDim))); + // for larger paths, initial scale is half the original + // gives us a size between kMinMIP and kMaxDim/2 (or close to (kMinMIP, 2*kMinMIP]) + } else if (maxDim > 2*kMinMIP) { + mipScale = 0.5f; + } + SkScalar smallMipSize = mipScale*maxDim; + + SkScalar desiredDimension; + if (size <= smallMipSize) { + desiredDimension = smallMipSize; + } else if (size <= 2*smallMipSize) { + desiredDimension = 2*smallMipSize; + } else if (size <= 4*smallMipSize) { + desiredDimension = 4*smallMipSize; } else { - desiredDimension = kLargeMIP; + desiredDimension = kMaxMIP; } // check to see if path is cached -- cgit v1.2.3