#include "SkRTConf.h"
#define ATLAS_TEXTURE_WIDTH 1024
-#define ATLAS_TEXTURE_HEIGHT 1024
+#define ATLAS_TEXTURE_HEIGHT 2048
#define PLOT_WIDTH 256
#define PLOT_HEIGHT 256
static int g_NumFreedPaths = 0;
#endif
+// mip levels
+static const int kSmallMIP = 32;
+static const int kMediumMIP = 64;
+static const int kLargeMIP = 128;
+
////////////////////////////////////////////////////////////////////////////////
GrAADistanceFieldPathRenderer::GrAADistanceFieldPathRenderer(GrContext* context)
: fContext(context)
return false;
}
- // currently don't support perspective or scaling more than 3x
+ // currently don't support perspective
const GrDrawState& drawState = target->getDrawState();
const SkMatrix& vm = drawState.getViewMatrix();
- if (vm.hasPerspective() || vm.getMaxScale() > 3.0f) {
+ if (vm.hasPerspective()) {
return false;
}
- // only support paths smaller than 64 x 64
+ // only support paths smaller than 64x64, scaled to less than 256x256
+ // the goal is to accelerate rendering of lots of small paths that may be scaling
+ SkScalar maxScale = vm.getMaxScale();
const SkRect& bounds = path.getBounds();
- return bounds.width() < 64.f && bounds.height() < 64.f;
+ SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
+ return maxDim < 64.f && maxDim*maxScale < 256.f;
}
SkASSERT(fContext);
+ // get mip level
+ const GrDrawState& drawState = target->getDrawState();
+ const SkMatrix& vm = drawState.getViewMatrix();
+ SkScalar maxScale = vm.getMaxScale();
+ const SkRect& bounds = path.getBounds();
+ SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
+ SkScalar size = maxScale*maxDim;
+ uint32_t desiredDimension;
+ if (size <= kSmallMIP) {
+ desiredDimension = kSmallMIP;
+ } else if (size <= kMediumMIP) {
+ desiredDimension = kMediumMIP;
+ } else {
+ desiredDimension = kLargeMIP;
+ }
+
// check to see if path is cached
// TODO: handle stroked vs. filled version of same path
- PathData* pathData = fPathCache.find(path.getGenerationID());
+ PathData::Key key = { path.getGenerationID(), desiredDimension };
+ PathData* pathData = fPathCache.find(key);
if (NULL == pathData) {
- pathData = this->addPathToAtlas(path, stroke, antiAlias);
+ SkScalar scale = desiredDimension/maxDim;
+ pathData = this->addPathToAtlas(path, stroke, antiAlias, desiredDimension, scale);
if (NULL == pathData) {
return false;
}
return this->internalDrawPath(path, pathData, target);
}
-// factor used to scale the path prior to building distance field
-const SkScalar kScaleFactor = 2.0f;
// padding around path bounds to allow for antialiased pixels
const SkScalar kAntiAliasPad = 1.0f;
GrAADistanceFieldPathRenderer::PathData* GrAADistanceFieldPathRenderer::addPathToAtlas(
const SkPath& path,
const SkStrokeRec& stroke,
- bool antiAlias) {
+ bool antiAlias,
+ uint32_t dimension,
+ SkScalar scale) {
// generate distance field and add to atlas
if (NULL == fAtlas) {
// generate bounding rect for bitmap draw
SkRect scaledBounds = bounds;
- // scale up to improve maxification range
- scaledBounds.fLeft *= kScaleFactor;
- scaledBounds.fTop *= kScaleFactor;
- scaledBounds.fRight *= kScaleFactor;
- scaledBounds.fBottom *= kScaleFactor;
+ // scale to mip level size
+ scaledBounds.fLeft *= scale;
+ scaledBounds.fTop *= scale;
+ scaledBounds.fRight *= scale;
+ scaledBounds.fBottom *= scale;
// move the origin to an integer boundary (gives better results)
SkScalar dx = SkScalarFraction(scaledBounds.fLeft);
SkScalar dy = SkScalarFraction(scaledBounds.fTop);
// draw path to bitmap
SkMatrix drawMatrix;
drawMatrix.setTranslate(-bounds.left(), -bounds.top());
- drawMatrix.postScale(kScaleFactor, kScaleFactor);
+ drawMatrix.postScale(scale, scale);
drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad);
GrSWMaskHelper helper(fContext);
HAS_ATLAS:
// add to cache
PathData* pathData = SkNEW(PathData);
- pathData->fGenID = path.getGenerationID();
+ pathData->fKey.fGenID = path.getGenerationID();
+ pathData->fKey.fDimension = dimension;
+ pathData->fScale = scale;
pathData->fPlot = plot;
// change the scaled rect to match the size of the inset distance field
scaledBounds.fRight = scaledBounds.fLeft +
while ((pathData = iter.get())) {
iter.next();
if (plot == pathData->fPlot) {
- fPathCache.remove(pathData->fGenID);
+ fPathCache.remove(pathData->fKey);
fPathList.remove(pathData);
SkDELETE(pathData);
#ifdef DF_PATH_TRACKING
SkScalar width = pathData->fBounds.width();
SkScalar height = pathData->fBounds.height();
- SkScalar invScale = 1.0f/kScaleFactor;
+ SkScalar invScale = 1.0f/pathData->fScale;
dx *= invScale;
dy *= invScale;
width *= invScale;
private:
struct PathData {
- uint32_t fGenID;
+ struct Key {
+ uint32_t fGenID;
+ // rendered size for stored path (32x32 max, 64x64 max, 128x128 max)
+ uint32_t fDimension;
+ bool operator==(const Key& other) const {
+ return other.fGenID == fGenID && other.fDimension == fDimension;
+ }
+ };
+ Key fKey;
+ SkScalar fScale;
GrPlot* fPlot;
SkRect fBounds;
SkIPoint16 fAtlasLocation;
SK_DECLARE_INTERNAL_LLIST_INTERFACE(PathData);
- static inline const uint32_t& GetKey(const PathData& data) {
- return data.fGenID;
+ static inline const Key& GetKey(const PathData& data) {
+ return data.fKey;
}
- static inline uint32_t Hash(uint32_t key) {
- return SkChecksum::Murmur3(&key, sizeof(key));
+ static inline uint32_t Hash(Key key) {
+ return SkChecksum::Murmur3(reinterpret_cast<const uint32_t*>(&key), sizeof(key));
}
};
typedef SkTInternalLList<PathData> PathDataList;
// current set of flags used to create the cached geometry processor
uint32_t fEffectFlags;
GrAtlas::ClientPlotUsage fPlotUsage;
- SkTDynamicHash<PathData, uint32_t> fPathCache;
+ SkTDynamicHash<PathData, PathData::Key> fPathCache;
PathDataList fPathList;
bool internalDrawPath(const SkPath& path, const PathData* pathData, GrDrawTarget* target);
- PathData* addPathToAtlas(const SkPath& path, const SkStrokeRec& stroke, bool antiAlias);
+ PathData* addPathToAtlas(const SkPath& path, const SkStrokeRec& stroke, bool antiAlias,
+ uint32_t dimension, SkScalar scale);
bool freeUnusedPlot();
typedef GrPathRenderer INHERITED;