Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 | |
| 2 /* | |
| 3 * Copyright 2014 Google Inc. | |
| 4 * | |
| 5 * Use of this source code is governed by a BSD-style license that can be | |
| 6 * found in the LICENSE file. | |
| 7 */ | |
| 8 | |
| 9 #include "GrAADistanceFieldPathRenderer.h" | |
| 10 | |
| 11 #include "GrAtlas.h" | |
| 12 #include "GrContext.h" | |
| 13 #include "GrDrawState.h" | |
| 14 #include "GrSurfacePriv.h" | |
| 15 #include "GrSWMaskHelper.h" | |
| 16 #include "GrTexturePriv.h" | |
| 17 #include "effects/GrDistanceFieldTextureEffect.h" | |
| 18 | |
| 19 #include "SkDistanceFieldGen.h" | |
| 20 #include "SkRTConf.h" | |
| 21 | |
| 22 #define ATLAS_TEXTURE_WIDTH 1024 | |
| 23 #define ATLAS_TEXTURE_HEIGHT 1024 | |
| 24 | |
| 25 #define PLOT_WIDTH 256 | |
| 26 #define PLOT_HEIGHT 256 | |
| 27 | |
| 28 #define NUM_PLOTS_X (ATLAS_TEXTURE_WIDTH / PLOT_WIDTH) | |
| 29 #define NUM_PLOTS_Y (ATLAS_TEXTURE_HEIGHT / PLOT_HEIGHT) | |
| 30 | |
| 31 SK_CONF_DECLARE(bool, c_DumpPathCache, "gpu.dumpPathCache", false, | |
| 32 "Dump the contents of the path cache before every purge."); | |
| 33 | |
| 34 //////////////////////////////////////////////////////////////////////////////// | |
| 35 GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() { | |
| 36 PathDataList::Iter iter; | |
| 37 iter.init(fPathList, PathDataList::Iter::kHead_IterStart); | |
| 38 PathData* pathData; | |
| 39 while ((pathData = iter.get())) { | |
| 40 iter.next(); | |
| 41 fPathList.remove(pathData); | |
| 42 SkDELETE(pathData); | |
| 43 } | |
| 44 | |
| 45 SkDELETE(fAtlas); | |
| 46 } | |
| 47 | |
| 48 //////////////////////////////////////////////////////////////////////////////// | |
| 49 bool GrAADistanceFieldPathRenderer::canDrawPath(const SkPath& path, | |
| 50 const SkStrokeRec& stroke, | |
| 51 const GrDrawTarget* target, | |
| 52 bool antiAlias) const { | |
| 53 // TODO: Support inverse fill | |
| 54 // TODO: Support strokes | |
| 55 if (!target->caps()->shaderDerivativeSupport() || !antiAlias || path.isInver seFillType() | |
| 56 || SkStrokeRec::kFill_Style != stroke.getStyle()) { | |
| 57 return false; | |
| 58 } | |
| 59 | |
| 60 // currently don't support perspective or scaling more than 3x | |
| 61 const GrDrawState& drawState = target->getDrawState(); | |
| 62 const SkMatrix& vm = drawState.getViewMatrix(); | |
| 63 if (vm.hasPerspective() || vm.getMaxScale() > 3.0f) { | |
| 64 return false; | |
| 65 } | |
| 66 | |
| 67 // only support paths smaller than 64 x 64 | |
| 68 const SkRect& bounds = path.getBounds(); | |
| 69 return bounds.width() < 64.f && bounds.height() < 64.f; | |
| 70 } | |
| 71 | |
| 72 | |
| 73 GrPathRenderer::StencilSupport GrAADistanceFieldPathRenderer::onGetStencilSuppor t( | |
| 74 const SkP ath&, | |
| 75 const SkS trokeRec&, | |
| 76 const GrD rawTarget*) const { | |
| 77 return GrPathRenderer::kNoSupport_StencilSupport; | |
| 78 } | |
| 79 | |
| 80 //////////////////////////////////////////////////////////////////////////////// | |
| 81 | |
| 82 // position + texture coord | |
| 83 extern const GrVertexAttrib gSDFPathVertexAttribs[] = { | |
| 84 { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding }, | |
| 85 { kVec2f_GrVertexAttribType, sizeof(SkPoint), kGeometryProcessor_GrVertexAtt ribBinding } | |
| 86 }; | |
| 87 static const size_t kSDFPathVASize = 2 * sizeof(SkPoint); | |
| 88 | |
| 89 bool GrAADistanceFieldPathRenderer::onDrawPath(const SkPath& path, | |
| 90 const SkStrokeRec& stroke, | |
| 91 GrDrawTarget* target, | |
| 92 bool antiAlias) { | |
| 93 // we've already bailed on inverse filled paths, so this is safe | |
| 94 if (path.isEmpty()) { | |
| 95 return true; | |
| 96 } | |
| 97 | |
| 98 SkASSERT(fContext); | |
| 99 | |
| 100 // check to see if path is cached | |
| 101 // TODO: handle stroked vs. filled version of same path | |
| 102 PathData* pathData = fPathCache.find(path.getGenerationID()); | |
| 103 if (NULL == pathData) { | |
| 104 pathData = this->addPathToAtlas(path, stroke, antiAlias); | |
| 105 if (NULL == pathData) { | |
| 106 return false; | |
| 107 } | |
| 108 } | |
| 109 | |
| 110 // use signed distance field to render | |
| 111 return this->internalDrawPath(path, pathData, target); | |
| 112 } | |
| 113 | |
| 114 // factor used to scale the path prior to building distance field | |
| 115 const SkScalar kScaleFactor = 2.0f; | |
| 116 // padding around path bounds to allow for antialiased pixels | |
| 117 const SkScalar kAntiAliasPad = 1.0f; | |
| 118 | |
| 119 GrAADistanceFieldPathRenderer::PathData* GrAADistanceFieldPathRenderer::addPathT oAtlas( | |
| 120 const Sk Path& path, | |
| 121 const Sk StrokeRec& stroke, | |
| 122 bool ant iAlias) { | |
| 123 | |
| 124 // generate distance field and add to atlas | |
| 125 if (NULL == fAtlas) { | |
|
robertphillips
2014/10/03 19:23:40
one line ?
| |
| 126 SkISize textureSize = SkISize::Make(ATLAS_TEXTURE_WIDTH, | |
| 127 ATLAS_TEXTURE_HEIGHT); | |
| 128 fAtlas = SkNEW_ARGS(GrAtlas, (fContext->getGpu(), kAlpha_8_GrPixelConfig , | |
| 129 kNone_GrTextureFlags, textureSize, | |
| 130 NUM_PLOTS_X, NUM_PLOTS_Y, false)); | |
| 131 if (NULL == fAtlas) { | |
| 132 return NULL; | |
| 133 } | |
| 134 } | |
| 135 | |
| 136 const SkRect& bounds = path.getBounds(); | |
| 137 | |
| 138 // generate bounding rect for bitmap draw | |
| 139 SkRect scaledBounds = bounds; | |
| 140 // scale up to improve maxification range | |
| 141 scaledBounds.fLeft *= kScaleFactor; | |
| 142 scaledBounds.fTop *= kScaleFactor; | |
| 143 scaledBounds.fRight *= kScaleFactor; | |
| 144 scaledBounds.fBottom *= kScaleFactor; | |
| 145 // move the origin to an integer boundary (gives better results) | |
| 146 SkScalar dx = SkScalarFraction(scaledBounds.fLeft); | |
| 147 SkScalar dy = SkScalarFraction(scaledBounds.fTop); | |
| 148 scaledBounds.offset(-dx, -dy); | |
| 149 // get integer boundary | |
| 150 SkIRect devPathBounds; | |
| 151 scaledBounds.roundOut(&devPathBounds); | |
| 152 // pad to allow room for antialiasing | |
| 153 devPathBounds.outset(SkScalarCeilToInt(kAntiAliasPad), SkScalarCeilToInt(kAn tiAliasPad)); | |
| 154 // move origin to upper left corner | |
| 155 devPathBounds.offsetTo(0,0); | |
| 156 | |
| 157 // draw path to bitmap | |
| 158 SkMatrix drawMatrix; | |
| 159 drawMatrix.setTranslate(-bounds.left(), -bounds.top()); | |
| 160 drawMatrix.postScale(kScaleFactor, kScaleFactor); | |
| 161 drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad); | |
| 162 GrSWMaskHelper helper(fContext); | |
| 163 | |
| 164 if (!helper.init(devPathBounds, &drawMatrix)) { | |
| 165 return NULL; | |
| 166 } | |
| 167 helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF); | |
| 168 | |
| 169 // generate signed distance field | |
| 170 devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad); | |
| 171 int width = devPathBounds.width(); | |
| 172 int height = devPathBounds.height(); | |
| 173 SkAutoSMalloc<1024> dfStorage(width*height*sizeof(unsigned char)); | |
| 174 helper.toSDF((unsigned char*) dfStorage.get()); | |
| 175 | |
| 176 // add to atlas | |
| 177 SkIPoint16 atlasLocation; | |
| 178 GrPlot* plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage.get( ), | |
| 179 &atlasLocation); | |
| 180 | |
| 181 // if atlas full | |
| 182 if (NULL == plot) { | |
| 183 if (this->freeUnusedPlot()) { | |
| 184 plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage.get( ), | |
| 185 &atlasLocation); | |
| 186 if (plot) { | |
| 187 goto HAS_ATLAS; | |
| 188 } | |
| 189 } | |
| 190 | |
| 191 if (c_DumpPathCache) { | |
| 192 #ifdef SK_DEVELOPER | |
| 193 GrTexture* texture = fAtlas->getTexture(); | |
| 194 texture->surfacePriv().savePixels("pathcache.png"); | |
| 195 #endif | |
| 196 } | |
| 197 | |
| 198 // before we purge the cache, we must flush any accumulated draws | |
| 199 fContext->flush(); | |
| 200 | |
| 201 if (this->freeUnusedPlot()) { | |
| 202 plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage.get( ), | |
| 203 &atlasLocation); | |
| 204 if (plot) { | |
| 205 goto HAS_ATLAS; | |
| 206 } | |
| 207 } | |
| 208 | |
| 209 return NULL; | |
| 210 } | |
| 211 | |
| 212 HAS_ATLAS: | |
| 213 // add to cache | |
| 214 PathData* pathData = SkNEW(PathData); | |
| 215 pathData->fGenID = path.getGenerationID(); | |
| 216 pathData->fPlot = plot; | |
| 217 // change the scaled rect to match the size of the inset distance field | |
| 218 scaledBounds.fRight = scaledBounds.fLeft + | |
| 219 SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset); | |
| 220 scaledBounds.fBottom = scaledBounds.fTop + | |
| 221 SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset); | |
| 222 // shift the origin to the correct place relative to the distance field | |
| 223 // need to also restore the fractional translation | |
| 224 scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx, | |
| 225 -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy); | |
| 226 pathData->fBounds = scaledBounds; | |
| 227 // origin we render from is inset from distance field edge | |
| 228 atlasLocation.fX += SK_DistanceFieldInset; | |
| 229 atlasLocation.fY += SK_DistanceFieldInset; | |
| 230 pathData->fAtlasLocation = atlasLocation; | |
| 231 | |
| 232 fPathCache.add(pathData); | |
| 233 fPathList.addToTail(pathData); | |
| 234 | |
| 235 return pathData; | |
| 236 } | |
| 237 | |
| 238 bool GrAADistanceFieldPathRenderer::freeUnusedPlot() { | |
| 239 // find an unused plot | |
| 240 GrPlot* plot = fAtlas->getUnusedPlot(); | |
| 241 if (NULL == plot) { | |
| 242 return false; | |
| 243 } | |
| 244 plot->resetRects(); | |
| 245 | |
| 246 // remove any paths that use this plot | |
| 247 PathDataList::Iter iter; | |
| 248 iter.init(fPathList, PathDataList::Iter::kHead_IterStart); | |
| 249 PathData* pathData; | |
| 250 while ((pathData = iter.get())) { | |
| 251 iter.next(); | |
| 252 if (plot == pathData->fPlot) { | |
| 253 fPathCache.remove(pathData->fGenID); | |
| 254 fPathList.remove(pathData); | |
| 255 SkDELETE(pathData); | |
| 256 } | |
| 257 } | |
| 258 | |
| 259 // tell the atlas to free the plot | |
| 260 GrAtlas::RemovePlot(&fPlotUsage, plot); | |
| 261 | |
| 262 return true; | |
| 263 } | |
| 264 | |
| 265 bool GrAADistanceFieldPathRenderer::internalDrawPath(const SkPath& path, | |
| 266 const PathData* pathData, | |
| 267 GrDrawTarget* target) { | |
| 268 | |
| 269 GrTexture* texture = fAtlas->getTexture(); | |
| 270 GrDrawState* drawState = target->drawState(); | |
| 271 | |
| 272 SkASSERT(pathData->fPlot); | |
| 273 GrDrawTarget::DrawToken drawToken = target->getCurrentDrawToken(); | |
| 274 pathData->fPlot->setDrawToken(drawToken); | |
| 275 | |
| 276 // make me some vertices | |
| 277 drawState->setVertexAttribs<gSDFPathVertexAttribs>(SK_ARRAY_COUNT(gSDFPathVe rtexAttribs), | |
| 278 kSDFPathVASize); | |
| 279 void* vertices = NULL; | |
| 280 void* indices = NULL; | |
| 281 bool success = target->reserveVertexAndIndexSpace(4, 6, &vertices, &indices) ; | |
| 282 GrAlwaysAssert(success); | |
| 283 | |
| 284 SkScalar dx = pathData->fBounds.fLeft; | |
| 285 SkScalar dy = pathData->fBounds.fTop; | |
| 286 SkScalar width = pathData->fBounds.width(); | |
| 287 SkScalar height = pathData->fBounds.height(); | |
| 288 | |
| 289 SkScalar invScale = 1.0f/kScaleFactor; | |
| 290 dx *= invScale; | |
| 291 dy *= invScale; | |
| 292 width *= invScale; | |
| 293 height *= invScale; | |
| 294 | |
| 295 SkFixed tx = SkIntToFixed(pathData->fAtlasLocation.fX); | |
| 296 SkFixed ty = SkIntToFixed(pathData->fAtlasLocation.fY); | |
| 297 SkFixed tw = SkScalarToFixed(pathData->fBounds.width()); | |
| 298 SkFixed th = SkScalarToFixed(pathData->fBounds.height()); | |
| 299 | |
| 300 // vertex positions | |
| 301 SkRect r = SkRect::MakeXYWH(dx, dy, width, height); | |
| 302 size_t vertSize = 2 * sizeof(SkPoint); | |
| 303 SkPoint* positions = reinterpret_cast<SkPoint*>(vertices); | |
| 304 positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertSize); | |
| 305 | |
| 306 // vertex texture coords | |
| 307 intptr_t intPtr = reinterpret_cast<intptr_t>(positions); | |
| 308 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(intPtr + vertSize - size of(SkPoint)); | |
| 309 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFix edX(tx)), | |
| 310 SkFixedToFloat(texture->texturePriv().normalizeFix edY(ty)), | |
| 311 SkFixedToFloat(texture->texturePriv().normalizeFix edX(tx + tw)), | |
| 312 SkFixedToFloat(texture->texturePriv().normalizeFix edY(ty + th)), | |
| 313 vertSize); | |
| 314 | |
| 315 uint16_t* indexPtr = reinterpret_cast<uint16_t*>(indices); | |
| 316 *indexPtr++ = 0; | |
| 317 *indexPtr++ = 1; | |
| 318 *indexPtr++ = 2; | |
| 319 *indexPtr++ = 0; | |
| 320 *indexPtr++ = 2; | |
| 321 *indexPtr++ = 3; | |
| 322 | |
| 323 // set up any flags | |
| 324 uint32_t flags = 0; | |
| 325 const SkMatrix& vm = drawState->getViewMatrix(); | |
| 326 flags |= vm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0; | |
| 327 | |
| 328 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_ FilterMode); | |
| 329 drawState->setGeometryProcessor(GrDistanceFieldNoGammaTextureEffect::Create( texture, | |
| 330 params, | |
| 331 flags))->unref(); | |
| 332 | |
| 333 | |
| 334 vm.mapRect(&r); | |
| 335 target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6, &r); | |
| 336 target->resetVertexSource(); | |
| 337 target->resetIndexSource(); | |
| 338 | |
| 339 return true; | |
| 340 } | |
| 341 | |
| OLD | NEW |