Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(190)

Unified Diff: src/gpu/GrAADistanceFieldPathRenderer.cpp

Issue 589103004: Add GrAASmallPathRenderer. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Address nits Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/gpu/GrAADistanceFieldPathRenderer.h ('k') | src/gpu/GrAddPathRenderers_default.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/gpu/GrAADistanceFieldPathRenderer.cpp
diff --git a/src/gpu/GrAADistanceFieldPathRenderer.cpp b/src/gpu/GrAADistanceFieldPathRenderer.cpp
new file mode 100755
index 0000000000000000000000000000000000000000..364217c5768eba348f9dcad255eff548c22609be
--- /dev/null
+++ b/src/gpu/GrAADistanceFieldPathRenderer.cpp
@@ -0,0 +1,340 @@
+
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrAADistanceFieldPathRenderer.h"
+
+#include "GrAtlas.h"
+#include "GrContext.h"
+#include "GrDrawState.h"
+#include "GrSurfacePriv.h"
+#include "GrSWMaskHelper.h"
+#include "GrTexturePriv.h"
+#include "effects/GrDistanceFieldTextureEffect.h"
+
+#include "SkDistanceFieldGen.h"
+#include "SkRTConf.h"
+
+#define ATLAS_TEXTURE_WIDTH 1024
+#define ATLAS_TEXTURE_HEIGHT 1024
+
+#define PLOT_WIDTH 256
+#define PLOT_HEIGHT 256
+
+#define NUM_PLOTS_X (ATLAS_TEXTURE_WIDTH / PLOT_WIDTH)
+#define NUM_PLOTS_Y (ATLAS_TEXTURE_HEIGHT / PLOT_HEIGHT)
+
+SK_CONF_DECLARE(bool, c_DumpPathCache, "gpu.dumpPathCache", false,
+ "Dump the contents of the path cache before every purge.");
+
+////////////////////////////////////////////////////////////////////////////////
+GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() {
+ PathDataList::Iter iter;
+ iter.init(fPathList, PathDataList::Iter::kHead_IterStart);
+ PathData* pathData;
+ while ((pathData = iter.get())) {
+ iter.next();
+ fPathList.remove(pathData);
+ SkDELETE(pathData);
+ }
+
+ SkDELETE(fAtlas);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+bool GrAADistanceFieldPathRenderer::canDrawPath(const SkPath& path,
+ const SkStrokeRec& stroke,
+ const GrDrawTarget* target,
+ bool antiAlias) const {
+ // TODO: Support inverse fill
+ // TODO: Support strokes
+ if (!target->caps()->shaderDerivativeSupport() || !antiAlias || path.isInverseFillType()
+ || SkStrokeRec::kFill_Style != stroke.getStyle()) {
+ return false;
+ }
+
+ // currently don't support perspective or scaling more than 3x
+ const GrDrawState& drawState = target->getDrawState();
+ const SkMatrix& vm = drawState.getViewMatrix();
+ if (vm.hasPerspective() || vm.getMaxScale() > 3.0f) {
+ return false;
+ }
+
+ // only support paths smaller than 64 x 64
+ const SkRect& bounds = path.getBounds();
+ return bounds.width() < 64.f && bounds.height() < 64.f;
+}
+
+
+GrPathRenderer::StencilSupport GrAADistanceFieldPathRenderer::onGetStencilSupport(
+ const SkPath&,
+ const SkStrokeRec&,
+ const GrDrawTarget*) const {
+ return GrPathRenderer::kNoSupport_StencilSupport;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// position + texture coord
+extern const GrVertexAttrib gSDFPathVertexAttribs[] = {
+ { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding },
+ { kVec2f_GrVertexAttribType, sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding }
+};
+static const size_t kSDFPathVASize = 2 * sizeof(SkPoint);
+
+bool GrAADistanceFieldPathRenderer::onDrawPath(const SkPath& path,
+ const SkStrokeRec& stroke,
+ GrDrawTarget* target,
+ bool antiAlias) {
+ // we've already bailed on inverse filled paths, so this is safe
+ if (path.isEmpty()) {
+ return true;
+ }
+
+ SkASSERT(fContext);
+
+ // check to see if path is cached
+ // TODO: handle stroked vs. filled version of same path
+ PathData* pathData = fPathCache.find(path.getGenerationID());
+ if (NULL == pathData) {
+ pathData = this->addPathToAtlas(path, stroke, antiAlias);
+ if (NULL == pathData) {
+ return false;
+ }
+ }
+
+ // use signed distance field to render
+ 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) {
+
+ // generate distance field and add to atlas
+ if (NULL == fAtlas) {
+ SkISize textureSize = SkISize::Make(ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT);
+ fAtlas = SkNEW_ARGS(GrAtlas, (fContext->getGpu(), kAlpha_8_GrPixelConfig,
+ kNone_GrTextureFlags, textureSize,
+ NUM_PLOTS_X, NUM_PLOTS_Y, false));
+ if (NULL == fAtlas) {
+ return NULL;
+ }
+ }
+
+ const SkRect& bounds = path.getBounds();
+
+ // 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;
+ // move the origin to an integer boundary (gives better results)
+ SkScalar dx = SkScalarFraction(scaledBounds.fLeft);
+ SkScalar dy = SkScalarFraction(scaledBounds.fTop);
+ scaledBounds.offset(-dx, -dy);
+ // get integer boundary
+ SkIRect devPathBounds;
+ scaledBounds.roundOut(&devPathBounds);
+ // pad to allow room for antialiasing
+ devPathBounds.outset(SkScalarCeilToInt(kAntiAliasPad), SkScalarCeilToInt(kAntiAliasPad));
+ // move origin to upper left corner
+ devPathBounds.offsetTo(0,0);
+
+ // draw path to bitmap
+ SkMatrix drawMatrix;
+ drawMatrix.setTranslate(-bounds.left(), -bounds.top());
+ drawMatrix.postScale(kScaleFactor, kScaleFactor);
+ drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad);
+ GrSWMaskHelper helper(fContext);
+
+ if (!helper.init(devPathBounds, &drawMatrix)) {
+ return NULL;
+ }
+ helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF);
+
+ // generate signed distance field
+ devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad);
+ int width = devPathBounds.width();
+ int height = devPathBounds.height();
+ SkAutoSMalloc<1024> dfStorage(width*height*sizeof(unsigned char));
+ helper.toSDF((unsigned char*) dfStorage.get());
+
+ // add to atlas
+ SkIPoint16 atlasLocation;
+ GrPlot* plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage.get(),
+ &atlasLocation);
+
+ // if atlas full
+ if (NULL == plot) {
+ if (this->freeUnusedPlot()) {
+ plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage.get(),
+ &atlasLocation);
+ if (plot) {
+ goto HAS_ATLAS;
+ }
+ }
+
+ if (c_DumpPathCache) {
+#ifdef SK_DEVELOPER
+ GrTexture* texture = fAtlas->getTexture();
+ texture->surfacePriv().savePixels("pathcache.png");
+#endif
+ }
+
+ // before we purge the cache, we must flush any accumulated draws
+ fContext->flush();
+
+ if (this->freeUnusedPlot()) {
+ plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage.get(),
+ &atlasLocation);
+ if (plot) {
+ goto HAS_ATLAS;
+ }
+ }
+
+ return NULL;
+ }
+
+HAS_ATLAS:
+ // add to cache
+ PathData* pathData = SkNEW(PathData);
+ pathData->fGenID = path.getGenerationID();
+ pathData->fPlot = plot;
+ // change the scaled rect to match the size of the inset distance field
+ scaledBounds.fRight = scaledBounds.fLeft +
+ SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset);
+ scaledBounds.fBottom = scaledBounds.fTop +
+ SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset);
+ // shift the origin to the correct place relative to the distance field
+ // need to also restore the fractional translation
+ scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx,
+ -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy);
+ pathData->fBounds = scaledBounds;
+ // origin we render from is inset from distance field edge
+ atlasLocation.fX += SK_DistanceFieldInset;
+ atlasLocation.fY += SK_DistanceFieldInset;
+ pathData->fAtlasLocation = atlasLocation;
+
+ fPathCache.add(pathData);
+ fPathList.addToTail(pathData);
+
+ return pathData;
+}
+
+bool GrAADistanceFieldPathRenderer::freeUnusedPlot() {
+ // find an unused plot
+ GrPlot* plot = fAtlas->getUnusedPlot();
+ if (NULL == plot) {
+ return false;
+ }
+ plot->resetRects();
+
+ // remove any paths that use this plot
+ PathDataList::Iter iter;
+ iter.init(fPathList, PathDataList::Iter::kHead_IterStart);
+ PathData* pathData;
+ while ((pathData = iter.get())) {
+ iter.next();
+ if (plot == pathData->fPlot) {
+ fPathCache.remove(pathData->fGenID);
+ fPathList.remove(pathData);
+ SkDELETE(pathData);
+ }
+ }
+
+ // tell the atlas to free the plot
+ GrAtlas::RemovePlot(&fPlotUsage, plot);
+
+ return true;
+}
+
+bool GrAADistanceFieldPathRenderer::internalDrawPath(const SkPath& path,
+ const PathData* pathData,
+ GrDrawTarget* target) {
+
+ GrTexture* texture = fAtlas->getTexture();
+ GrDrawState* drawState = target->drawState();
+
+ SkASSERT(pathData->fPlot);
+ GrDrawTarget::DrawToken drawToken = target->getCurrentDrawToken();
+ pathData->fPlot->setDrawToken(drawToken);
+
+ // make me some vertices
+ drawState->setVertexAttribs<gSDFPathVertexAttribs>(SK_ARRAY_COUNT(gSDFPathVertexAttribs),
+ kSDFPathVASize);
+ void* vertices = NULL;
+ void* indices = NULL;
+ bool success = target->reserveVertexAndIndexSpace(4, 6, &vertices, &indices);
+ GrAlwaysAssert(success);
+
+ SkScalar dx = pathData->fBounds.fLeft;
+ SkScalar dy = pathData->fBounds.fTop;
+ SkScalar width = pathData->fBounds.width();
+ SkScalar height = pathData->fBounds.height();
+
+ SkScalar invScale = 1.0f/kScaleFactor;
+ dx *= invScale;
+ dy *= invScale;
+ width *= invScale;
+ height *= invScale;
+
+ SkFixed tx = SkIntToFixed(pathData->fAtlasLocation.fX);
+ SkFixed ty = SkIntToFixed(pathData->fAtlasLocation.fY);
+ SkFixed tw = SkScalarToFixed(pathData->fBounds.width());
+ SkFixed th = SkScalarToFixed(pathData->fBounds.height());
+
+ // vertex positions
+ SkRect r = SkRect::MakeXYWH(dx, dy, width, height);
+ size_t vertSize = 2 * sizeof(SkPoint);
+ SkPoint* positions = reinterpret_cast<SkPoint*>(vertices);
+ positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertSize);
+
+ // vertex texture coords
+ intptr_t intPtr = reinterpret_cast<intptr_t>(positions);
+ SkPoint* textureCoords = reinterpret_cast<SkPoint*>(intPtr + vertSize - sizeof(SkPoint));
+ textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
+ SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
+ SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)),
+ SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)),
+ vertSize);
+
+ uint16_t* indexPtr = reinterpret_cast<uint16_t*>(indices);
+ *indexPtr++ = 0;
+ *indexPtr++ = 1;
+ *indexPtr++ = 2;
+ *indexPtr++ = 0;
+ *indexPtr++ = 2;
+ *indexPtr++ = 3;
+
+ // set up any flags
+ uint32_t flags = 0;
+ const SkMatrix& vm = drawState->getViewMatrix();
+ flags |= vm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
+
+ GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
+ drawState->setGeometryProcessor(GrDistanceFieldNoGammaTextureEffect::Create(texture,
+ params,
+ flags))->unref();
+
+
+ vm.mapRect(&r);
+ target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6, &r);
+ target->resetVertexSource();
+ target->resetIndexSource();
+
+ return true;
+}
+
« no previous file with comments | « src/gpu/GrAADistanceFieldPathRenderer.h ('k') | src/gpu/GrAddPathRenderers_default.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698