LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/gpu/ops - GrSmallPathRenderer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 418 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 35 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright 2014 Google Inc.
       3             :  * Copyright 2017 ARM Ltd.
       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 "GrSmallPathRenderer.h"
      10             : 
      11             : #include "GrBuffer.h"
      12             : #include "GrContext.h"
      13             : #include "GrDistanceFieldGenFromVector.h"
      14             : #include "GrDrawOpTest.h"
      15             : #include "GrOpFlushState.h"
      16             : #include "GrPipelineBuilder.h"
      17             : #include "GrResourceProvider.h"
      18             : #include "GrSWMaskHelper.h"
      19             : #include "GrSurfacePriv.h"
      20             : #include "GrSurfaceProxyPriv.h"
      21             : #include "GrTexturePriv.h"
      22             : #include "effects/GrBitmapTextGeoProc.h"
      23             : #include "effects/GrDistanceFieldGeoProc.h"
      24             : #include "ops/GrMeshDrawOp.h"
      25             : 
      26             : #include "SkAutoMalloc.h"
      27             : #include "SkDistanceFieldGen.h"
      28             : #include "SkPathOps.h"
      29             : 
      30             : #define ATLAS_TEXTURE_WIDTH 2048
      31             : #define ATLAS_TEXTURE_HEIGHT 2048
      32             : #define PLOT_WIDTH  512
      33             : #define PLOT_HEIGHT 256
      34             : 
      35             : #define NUM_PLOTS_X   (ATLAS_TEXTURE_WIDTH / PLOT_WIDTH)
      36             : #define NUM_PLOTS_Y   (ATLAS_TEXTURE_HEIGHT / PLOT_HEIGHT)
      37             : 
      38             : #ifdef DF_PATH_TRACKING
      39             : static int g_NumCachedShapes = 0;
      40             : static int g_NumFreedShapes = 0;
      41             : #endif
      42             : 
      43             : // mip levels
      44             : static const SkScalar kIdealMinMIP = 12;
      45             : static const SkScalar kMaxMIP = 162;
      46             : 
      47             : static const SkScalar kMaxDim = 73;
      48             : static const SkScalar kMinSize = SK_ScalarHalf;
      49             : static const SkScalar kMaxSize = 2*kMaxMIP;
      50             : 
      51             : // Callback to clear out internal path cache when eviction occurs
      52           0 : void GrSmallPathRenderer::HandleEviction(GrDrawOpAtlas::AtlasID id, void* pr) {
      53           0 :     GrSmallPathRenderer* dfpr = (GrSmallPathRenderer*)pr;
      54             :     // remove any paths that use this plot
      55           0 :     ShapeDataList::Iter iter;
      56           0 :     iter.init(dfpr->fShapeList, ShapeDataList::Iter::kHead_IterStart);
      57             :     ShapeData* shapeData;
      58           0 :     while ((shapeData = iter.get())) {
      59           0 :         iter.next();
      60           0 :         if (id == shapeData->fID) {
      61           0 :             dfpr->fShapeCache.remove(shapeData->fKey);
      62           0 :             dfpr->fShapeList.remove(shapeData);
      63           0 :             delete shapeData;
      64             : #ifdef DF_PATH_TRACKING
      65             :             ++g_NumFreedPaths;
      66             : #endif
      67             :         }
      68             :     }
      69           0 : }
      70             : 
      71             : ////////////////////////////////////////////////////////////////////////////////
      72           0 : GrSmallPathRenderer::GrSmallPathRenderer() : fAtlas(nullptr) {}
      73             : 
      74           0 : GrSmallPathRenderer::~GrSmallPathRenderer() {
      75           0 :     ShapeDataList::Iter iter;
      76           0 :     iter.init(fShapeList, ShapeDataList::Iter::kHead_IterStart);
      77             :     ShapeData* shapeData;
      78           0 :     while ((shapeData = iter.get())) {
      79           0 :         iter.next();
      80           0 :         delete shapeData;
      81             :     }
      82             : 
      83             : #ifdef DF_PATH_TRACKING
      84             :     SkDebugf("Cached shapes: %d, freed shapes: %d\n", g_NumCachedShapes, g_NumFreedShapes);
      85             : #endif
      86           0 : }
      87             : 
      88             : ////////////////////////////////////////////////////////////////////////////////
      89           0 : bool GrSmallPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
      90           0 :     if (!args.fShaderCaps->shaderDerivativeSupport()) {
      91           0 :         return false;
      92             :     }
      93             :     // If the shape has no key then we won't get any reuse.
      94           0 :     if (!args.fShape->hasUnstyledKey()) {
      95           0 :         return false;
      96             :     }
      97             :     // This only supports filled paths, however, the caller may apply the style to make a filled
      98             :     // path and try again.
      99           0 :     if (!args.fShape->style().isSimpleFill()) {
     100           0 :         return false;
     101             :     }
     102             :     // This does non-inverse coverage-based antialiased fills.
     103           0 :     if (GrAAType::kCoverage != args.fAAType) {
     104           0 :         return false;
     105             :     }
     106             :     // TODO: Support inverse fill
     107           0 :     if (args.fShape->inverseFilled()) {
     108           0 :         return false;
     109             :     }
     110             :     // currently don't support perspective
     111           0 :     if (args.fViewMatrix->hasPerspective()) {
     112           0 :         return false;
     113             :     }
     114             : 
     115             :     // Only support paths with bounds within kMaxDim by kMaxDim,
     116             :     // scaled to have bounds within kMaxSize by kMaxSize.
     117             :     // The goal is to accelerate rendering of lots of small paths that may be scaling.
     118             :     SkScalar scaleFactors[2];
     119           0 :     if (!args.fViewMatrix->getMinMaxScales(scaleFactors)) {
     120           0 :         return false;
     121             :     }
     122           0 :     SkRect bounds = args.fShape->styledBounds();
     123           0 :     SkScalar minDim = SkMinScalar(bounds.width(), bounds.height());
     124           0 :     SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
     125           0 :     SkScalar minSize = minDim * SkScalarAbs(scaleFactors[0]);
     126           0 :     SkScalar maxSize = maxDim * SkScalarAbs(scaleFactors[1]);
     127             : 
     128           0 :     return maxDim <= kMaxDim && kMinSize <= minSize && maxSize <= kMaxSize;
     129             : }
     130             : 
     131             : ////////////////////////////////////////////////////////////////////////////////
     132             : 
     133             : // padding around path bounds to allow for antialiased pixels
     134             : static const SkScalar kAntiAliasPad = 1.0f;
     135             : 
     136           0 : class SmallPathOp final : public GrLegacyMeshDrawOp {
     137             : public:
     138           0 :     DEFINE_OP_CLASS_ID
     139             : 
     140             :     using ShapeData = GrSmallPathRenderer::ShapeData;
     141             :     using ShapeCache = SkTDynamicHash<ShapeData, ShapeData::Key>;
     142             :     using ShapeDataList = GrSmallPathRenderer::ShapeDataList;
     143             : 
     144           0 :     static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const GrShape& shape,
     145             :                                                     const SkMatrix& viewMatrix,
     146             :                                                     GrDrawOpAtlas* atlas, ShapeCache* shapeCache,
     147             :                                                     ShapeDataList* shapeList, bool gammaCorrect) {
     148             :         return std::unique_ptr<GrLegacyMeshDrawOp>(new SmallPathOp(
     149           0 :                 color, shape, viewMatrix, atlas, shapeCache, shapeList, gammaCorrect));
     150             :     }
     151             : 
     152           0 :     const char* name() const override { return "SmallPathOp"; }
     153             : 
     154           0 :     SkString dumpInfo() const override {
     155           0 :         SkString string;
     156           0 :         for (const auto& geo : fShapes) {
     157           0 :             string.appendf("Color: 0x%08x\n", geo.fColor);
     158             :         }
     159           0 :         string.append(DumpPipelineInfo(*this->pipeline()));
     160           0 :         string.append(INHERITED::dumpInfo());
     161           0 :         return string;
     162             :     }
     163             : 
     164             : private:
     165           0 :     SmallPathOp(GrColor color, const GrShape& shape, const SkMatrix& viewMatrix,
     166             :                 GrDrawOpAtlas* atlas, ShapeCache* shapeCache, ShapeDataList* shapeList,
     167             :                 bool gammaCorrect)
     168           0 :             : INHERITED(ClassID()) {
     169           0 :         SkASSERT(shape.hasUnstyledKey());
     170             :         // Compute bounds
     171           0 :         this->setTransformedBounds(shape.bounds(), viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
     172             : 
     173             : #if defined(SK_BUILD_FOR_ANDROID) && !defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
     174             :         fUsesDistanceField = true;
     175             : #else
     176             :         // only use distance fields on desktop and Android framework to save space in the atlas
     177           0 :         fUsesDistanceField = this->bounds().width() > kMaxMIP || this->bounds().height() > kMaxMIP;
     178             : #endif
     179           0 :         fViewMatrix = viewMatrix;
     180           0 :         SkVector translate = SkVector::Make(0, 0);
     181           0 :         if (!fUsesDistanceField) {
     182             :             // In this case we don't apply a view matrix, so we need to remove the non-subpixel
     183             :             // translation and add it back when we generate the quad for the path
     184           0 :             SkScalar translateX = viewMatrix.getTranslateX();
     185           0 :             SkScalar translateY = viewMatrix.getTranslateY();
     186             :             translate = SkVector::Make(SkScalarFloorToScalar(translateX),
     187           0 :                                        SkScalarFloorToScalar(translateY));
     188             :             // Only store the fractional part of the translation in the view matrix
     189           0 :             fViewMatrix.setTranslateX(translateX - translate.fX);
     190           0 :             fViewMatrix.setTranslateY(translateY - translate.fY);
     191             :         }
     192             : 
     193           0 :         fShapes.emplace_back(Entry{color, shape, translate});
     194             : 
     195           0 :         fAtlas = atlas;
     196           0 :         fShapeCache = shapeCache;
     197           0 :         fShapeList = shapeList;
     198           0 :         fGammaCorrect = gammaCorrect;
     199             : 
     200           0 :     }
     201             : 
     202           0 :     void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
     203             :                                     GrProcessorAnalysisCoverage* coverage) const override {
     204           0 :         color->setToConstant(fShapes[0].fColor);
     205           0 :         *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     206           0 :     }
     207             : 
     208           0 :     void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
     209           0 :         optimizations.getOverrideColorIfSet(&fShapes[0].fColor);
     210           0 :         fUsesLocalCoords = optimizations.readsLocalCoords();
     211           0 :     }
     212             : 
     213           0 :     struct FlushInfo {
     214             :         sk_sp<const GrBuffer> fVertexBuffer;
     215             :         sk_sp<const GrBuffer> fIndexBuffer;
     216             :         sk_sp<GrGeometryProcessor>   fGeometryProcessor;
     217             :         int fVertexOffset;
     218             :         int fInstancesToFlush;
     219             :     };
     220             : 
     221           0 :     void onPrepareDraws(Target* target) const override {
     222           0 :         int instanceCount = fShapes.count();
     223             : 
     224           0 :         const SkMatrix& ctm = this->viewMatrix();
     225             : 
     226           0 :         FlushInfo flushInfo;
     227             : 
     228             :         // Setup GrGeometryProcessor
     229           0 :         GrDrawOpAtlas* atlas = fAtlas;
     230           0 :         if (fUsesDistanceField) {
     231           0 :             GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kBilerp_FilterMode);
     232             : 
     233           0 :             uint32_t flags = 0;
     234           0 :             flags |= ctm.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
     235           0 :             flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
     236           0 :             flags |= fGammaCorrect ? kGammaCorrect_DistanceFieldEffectFlag : 0;
     237             : 
     238           0 :             flushInfo.fGeometryProcessor = GrDistanceFieldPathGeoProc::Make(
     239             :                 atlas->context()->resourceProvider(),
     240           0 :                 this->color(), this->viewMatrix(), atlas->getProxy(), params, flags,
     241           0 :                 this->usesLocalCoords());
     242             :         } else {
     243           0 :             GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kNone_FilterMode);
     244             : 
     245             :             SkMatrix invert;
     246           0 :             if (this->usesLocalCoords()) {
     247           0 :                 if (!this->viewMatrix().invert(&invert)) {
     248           0 :                     SkDebugf("Could not invert viewmatrix\n");
     249           0 :                     return;
     250             :                 }
     251             :                 // for local coords, we need to add the translation back in that we removed
     252             :                 // from the stored view matrix
     253           0 :                 invert.preTranslate(-fShapes[0].fTranslate.fX, -fShapes[0].fTranslate.fY);
     254             :             }
     255             : 
     256           0 :             flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
     257             :                 atlas->context()->resourceProvider(),
     258           0 :                 this->color(), atlas->getProxy(), params, kA8_GrMaskFormat, invert,
     259           0 :                 this->usesLocalCoords());
     260             :         }
     261             : 
     262             :         // allocate vertices
     263           0 :         size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride();
     264           0 :         SkASSERT(vertexStride == sizeof(SkPoint) + sizeof(GrColor) + 2*sizeof(uint16_t));
     265             : 
     266             :         const GrBuffer* vertexBuffer;
     267           0 :         void* vertices = target->makeVertexSpace(vertexStride,
     268             :                                                  kVerticesPerQuad * instanceCount,
     269             :                                                  &vertexBuffer,
     270           0 :                                                  &flushInfo.fVertexOffset);
     271           0 :         flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
     272           0 :         flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
     273           0 :         if (!vertices || !flushInfo.fIndexBuffer) {
     274           0 :             SkDebugf("Could not allocate vertices\n");
     275           0 :             return;
     276             :         }
     277             : 
     278           0 :         flushInfo.fInstancesToFlush = 0;
     279             :         // Pointer to the next set of vertices to write.
     280           0 :         intptr_t offset = reinterpret_cast<intptr_t>(vertices);
     281           0 :         for (int i = 0; i < instanceCount; i++) {
     282           0 :             const Entry& args = fShapes[i];
     283             : 
     284             :             ShapeData* shapeData;
     285             :             SkScalar maxScale;
     286           0 :             if (fUsesDistanceField) {
     287             :                 // get mip level
     288           0 :                 maxScale = SkScalarAbs(this->viewMatrix().getMaxScale());
     289           0 :                 const SkRect& bounds = args.fShape.bounds();
     290           0 :                 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
     291             :                 // We try to create the DF at a 2^n scaled path resolution (1/2, 1, 2, 4, etc.)
     292             :                 // In the majority of cases this will yield a crisper rendering.
     293           0 :                 SkScalar mipScale = 1.0f;
     294             :                 // Our mipscale is the maxScale clamped to the next highest power of 2
     295           0 :                 if (maxScale <= SK_ScalarHalf) {
     296           0 :                     SkScalar log = SkScalarFloorToScalar(SkScalarLog2(SkScalarInvert(maxScale)));
     297           0 :                     mipScale = SkScalarPow(2, -log);
     298           0 :                 } else if (maxScale > SK_Scalar1) {
     299           0 :                     SkScalar log = SkScalarCeilToScalar(SkScalarLog2(maxScale));
     300           0 :                     mipScale = SkScalarPow(2, log);
     301             :                 }
     302           0 :                 SkASSERT(maxScale <= mipScale);
     303             : 
     304           0 :                 SkScalar mipSize = mipScale*SkScalarAbs(maxDim);
     305             :                 // For sizes less than kIdealMinMIP we want to use as large a distance field as we can
     306             :                 // so we can preserve as much detail as possible. However, we can't scale down more
     307             :                 // than a 1/4 of the size without artifacts. So the idea is that we pick the mipsize
     308             :                 // just bigger than the ideal, and then scale down until we are no more than 4x the
     309             :                 // original mipsize.
     310           0 :                 if (mipSize < kIdealMinMIP) {
     311           0 :                     SkScalar newMipSize = mipSize;
     312           0 :                     do {
     313           0 :                         newMipSize *= 2;
     314           0 :                     } while (newMipSize < kIdealMinMIP);
     315           0 :                     while (newMipSize > 4 * mipSize) {
     316           0 :                         newMipSize *= 0.25f;
     317             :                     }
     318           0 :                     mipSize = newMipSize;
     319             :                 }
     320           0 :                 SkScalar desiredDimension = SkTMin(mipSize, kMaxMIP);
     321             : 
     322             :                 // check to see if df path is cached
     323           0 :                 ShapeData::Key key(args.fShape, SkScalarCeilToInt(desiredDimension));
     324           0 :                 shapeData = fShapeCache->find(key);
     325           0 :                 if (nullptr == shapeData || !atlas->hasID(shapeData->fID)) {
     326             :                     // Remove the stale cache entry
     327           0 :                     if (shapeData) {
     328           0 :                         fShapeCache->remove(shapeData->fKey);
     329           0 :                         fShapeList->remove(shapeData);
     330           0 :                         delete shapeData;
     331             :                     }
     332           0 :                     SkScalar scale = desiredDimension / maxDim;
     333             : 
     334           0 :                     shapeData = new ShapeData;
     335           0 :                     if (!this->addDFPathToAtlas(target,
     336             :                                                 &flushInfo,
     337             :                                                 atlas,
     338             :                                                 shapeData,
     339             :                                                 args.fShape,
     340           0 :                                                 SkScalarCeilToInt(desiredDimension),
     341             :                                                 scale)) {
     342           0 :                         delete shapeData;
     343           0 :                         SkDebugf("Can't rasterize path\n");
     344           0 :                         continue;
     345             :                     }
     346             :                 }
     347             :             } else {
     348             :                 // check to see if bitmap path is cached
     349           0 :                 ShapeData::Key key(args.fShape, this->viewMatrix());
     350           0 :                 shapeData = fShapeCache->find(key);
     351           0 :                 if (nullptr == shapeData || !atlas->hasID(shapeData->fID)) {
     352             :                     // Remove the stale cache entry
     353           0 :                     if (shapeData) {
     354           0 :                         fShapeCache->remove(shapeData->fKey);
     355           0 :                         fShapeList->remove(shapeData);
     356           0 :                         delete shapeData;
     357             :                     }
     358             : 
     359           0 :                     shapeData = new ShapeData;
     360           0 :                     if (!this->addBMPathToAtlas(target,
     361             :                                               &flushInfo,
     362             :                                               atlas,
     363             :                                               shapeData,
     364             :                                               args.fShape,
     365             :                                               this->viewMatrix())) {
     366           0 :                         delete shapeData;
     367           0 :                         SkDebugf("Can't rasterize path\n");
     368           0 :                         continue;
     369             :                     }
     370             :                 }
     371           0 :                 maxScale = 1;
     372             :             }
     373             : 
     374           0 :             atlas->setLastUseToken(shapeData->fID, target->nextDrawToken());
     375             : 
     376           0 :             this->writePathVertices(target,
     377             :                                     atlas,
     378             :                                     offset,
     379           0 :                                     args.fColor,
     380             :                                     vertexStride,
     381             :                                     maxScale,
     382             :                                     args.fTranslate,
     383           0 :                                     shapeData);
     384           0 :             offset += kVerticesPerQuad * vertexStride;
     385           0 :             flushInfo.fInstancesToFlush++;
     386             :         }
     387             : 
     388           0 :         this->flush(target, &flushInfo);
     389             :     }
     390             : 
     391           0 :     bool addDFPathToAtlas(GrLegacyMeshDrawOp::Target* target, FlushInfo* flushInfo,
     392             :                           GrDrawOpAtlas* atlas, ShapeData* shapeData, const GrShape& shape,
     393             :                           uint32_t dimension, SkScalar scale) const {
     394           0 :         const SkRect& bounds = shape.bounds();
     395             : 
     396             :         // generate bounding rect for bitmap draw
     397           0 :         SkRect scaledBounds = bounds;
     398             :         // scale to mip level size
     399           0 :         scaledBounds.fLeft *= scale;
     400           0 :         scaledBounds.fTop *= scale;
     401           0 :         scaledBounds.fRight *= scale;
     402           0 :         scaledBounds.fBottom *= scale;
     403             :         // subtract out integer portion of origin
     404             :         // (SDF created will be placed with fractional offset burnt in)
     405           0 :         SkScalar dx = SkScalarFloorToScalar(scaledBounds.fLeft);
     406           0 :         SkScalar dy = SkScalarFloorToScalar(scaledBounds.fTop);
     407           0 :         scaledBounds.offset(-dx, -dy);
     408             :         // get integer boundary
     409             :         SkIRect devPathBounds;
     410           0 :         scaledBounds.roundOut(&devPathBounds);
     411             :         // pad to allow room for antialiasing
     412           0 :         const int intPad = SkScalarCeilToInt(kAntiAliasPad);
     413             :         // place devBounds at origin
     414           0 :         int width = devPathBounds.width() + 2*intPad;
     415           0 :         int height = devPathBounds.height() + 2*intPad;
     416           0 :         devPathBounds = SkIRect::MakeWH(width, height);
     417             : 
     418             :         // draw path to bitmap
     419             :         SkMatrix drawMatrix;
     420           0 :         drawMatrix.setScale(scale, scale);
     421           0 :         drawMatrix.postTranslate(intPad - dx, intPad - dy);
     422             : 
     423           0 :         SkASSERT(devPathBounds.fLeft == 0);
     424           0 :         SkASSERT(devPathBounds.fTop == 0);
     425           0 :         SkASSERT(devPathBounds.width() > 0);
     426           0 :         SkASSERT(devPathBounds.height() > 0);
     427             : 
     428             :         // setup signed distance field storage
     429           0 :         SkIRect dfBounds = devPathBounds.makeOutset(SK_DistanceFieldPad, SK_DistanceFieldPad);
     430           0 :         width = dfBounds.width();
     431           0 :         height = dfBounds.height();
     432             :         // TODO We should really generate this directly into the plot somehow
     433           0 :         SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char));
     434             : 
     435           0 :         SkPath path;
     436           0 :         shape.asPath(&path);
     437             : #ifndef SK_USE_LEGACY_DISTANCE_FIELDS
     438             :         // Generate signed distance field directly from SkPath
     439             :         bool succeed = GrGenerateDistanceFieldFromPath((unsigned char*)dfStorage.get(),
     440             :                                         path, drawMatrix,
     441             :                                         width, height, width * sizeof(unsigned char));
     442             :         if (!succeed) {
     443             : #endif
     444             :             // setup bitmap backing
     445           0 :             SkAutoPixmapStorage dst;
     446           0 :             if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(),
     447             :                                                   devPathBounds.height()))) {
     448           0 :                 return false;
     449             :             }
     450           0 :             sk_bzero(dst.writable_addr(), dst.getSafeSize());
     451             : 
     452             :             // rasterize path
     453           0 :             SkPaint paint;
     454           0 :             paint.setStyle(SkPaint::kFill_Style);
     455           0 :             paint.setAntiAlias(true);
     456             : 
     457           0 :             SkDraw draw;
     458           0 :             sk_bzero(&draw, sizeof(draw));
     459             : 
     460           0 :             SkRasterClip rasterClip;
     461           0 :             rasterClip.setRect(devPathBounds);
     462           0 :             draw.fRC = &rasterClip;
     463           0 :             draw.fMatrix = &drawMatrix;
     464           0 :             draw.fDst = dst;
     465             : 
     466           0 :             draw.drawPathCoverage(path, paint);
     467             : 
     468             :             // Generate signed distance field
     469           0 :             SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(),
     470           0 :                                                (const unsigned char*)dst.addr(),
     471           0 :                                                dst.width(), dst.height(), dst.rowBytes());
     472             : #ifndef SK_USE_LEGACY_DISTANCE_FIELDS
     473             :         }
     474             : #endif
     475             : 
     476             :         // add to atlas
     477             :         SkIPoint16 atlasLocation;
     478             :         GrDrawOpAtlas::AtlasID id;
     479           0 :         if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) {
     480           0 :             this->flush(target, flushInfo);
     481           0 :             if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) {
     482           0 :                 return false;
     483             :             }
     484             :         }
     485             : 
     486             :         // add to cache
     487           0 :         shapeData->fKey.set(shape, dimension);
     488           0 :         shapeData->fID = id;
     489             : 
     490             :         // set the bounds rect to the original bounds
     491           0 :         shapeData->fBounds = bounds;
     492             : 
     493             :         // set up path to texture coordinate transform
     494           0 :         shapeData->fScale = scale;
     495           0 :         dx -= SK_DistanceFieldPad + kAntiAliasPad;
     496           0 :         dy -= SK_DistanceFieldPad + kAntiAliasPad;
     497           0 :         shapeData->fTranslate.fX = atlasLocation.fX - dx;
     498           0 :         shapeData->fTranslate.fY = atlasLocation.fY - dy;
     499             : 
     500           0 :         fShapeCache->add(shapeData);
     501           0 :         fShapeList->addToTail(shapeData);
     502             : #ifdef DF_PATH_TRACKING
     503             :         ++g_NumCachedPaths;
     504             : #endif
     505           0 :         return true;
     506             :     }
     507             : 
     508           0 :     bool addBMPathToAtlas(GrLegacyMeshDrawOp::Target* target, FlushInfo* flushInfo,
     509             :                           GrDrawOpAtlas* atlas, ShapeData* shapeData, const GrShape& shape,
     510             :                           const SkMatrix& ctm) const {
     511           0 :         const SkRect& bounds = shape.bounds();
     512           0 :         if (bounds.isEmpty()) {
     513           0 :             return false;
     514             :         }
     515           0 :         SkMatrix drawMatrix(ctm);
     516           0 :         drawMatrix.set(SkMatrix::kMTransX, SkScalarFraction(ctm.get(SkMatrix::kMTransX)));
     517           0 :         drawMatrix.set(SkMatrix::kMTransY, SkScalarFraction(ctm.get(SkMatrix::kMTransY)));
     518             :         SkRect shapeDevBounds;
     519           0 :         drawMatrix.mapRect(&shapeDevBounds, bounds);
     520           0 :         SkScalar dx = SkScalarFloorToScalar(shapeDevBounds.fLeft);
     521           0 :         SkScalar dy = SkScalarFloorToScalar(shapeDevBounds.fTop);
     522             : 
     523             :         // get integer boundary
     524             :         SkIRect devPathBounds;
     525           0 :         shapeDevBounds.roundOut(&devPathBounds);
     526             :         // pad to allow room for antialiasing
     527           0 :         const int intPad = SkScalarCeilToInt(kAntiAliasPad);
     528             :         // place devBounds at origin
     529           0 :         int width = devPathBounds.width() + 2 * intPad;
     530           0 :         int height = devPathBounds.height() + 2 * intPad;
     531           0 :         devPathBounds = SkIRect::MakeWH(width, height);
     532           0 :         SkScalar translateX = intPad - dx;
     533           0 :         SkScalar translateY = intPad - dy;
     534             : 
     535           0 :         SkASSERT(devPathBounds.fLeft == 0);
     536           0 :         SkASSERT(devPathBounds.fTop == 0);
     537           0 :         SkASSERT(devPathBounds.width() > 0);
     538           0 :         SkASSERT(devPathBounds.height() > 0);
     539             : 
     540           0 :         SkPath path;
     541           0 :         shape.asPath(&path);
     542             :         // setup bitmap backing
     543           0 :         SkAutoPixmapStorage dst;
     544           0 :         if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(),
     545             :                                               devPathBounds.height()))) {
     546           0 :             return false;
     547             :         }
     548           0 :         sk_bzero(dst.writable_addr(), dst.getSafeSize());
     549             : 
     550             :         // rasterize path
     551           0 :         SkPaint paint;
     552           0 :         paint.setStyle(SkPaint::kFill_Style);
     553           0 :         paint.setAntiAlias(true);
     554             : 
     555           0 :         SkDraw draw;
     556           0 :         sk_bzero(&draw, sizeof(draw));
     557             : 
     558           0 :         SkRasterClip rasterClip;
     559           0 :         rasterClip.setRect(devPathBounds);
     560           0 :         draw.fRC = &rasterClip;
     561           0 :         drawMatrix.postTranslate(translateX, translateY);
     562           0 :         draw.fMatrix = &drawMatrix;
     563           0 :         draw.fDst = dst;
     564             : 
     565           0 :         draw.drawPathCoverage(path, paint);
     566             : 
     567             :         // add to atlas
     568             :         SkIPoint16 atlasLocation;
     569             :         GrDrawOpAtlas::AtlasID id;
     570           0 :         if (!atlas->addToAtlas(&id, target, dst.width(), dst.height(), dst.addr(),
     571             :                                &atlasLocation)) {
     572           0 :             this->flush(target, flushInfo);
     573           0 :             if (!atlas->addToAtlas(&id, target, dst.width(), dst.height(), dst.addr(),
     574             :                                    &atlasLocation)) {
     575           0 :                 return false;
     576             :             }
     577             :         }
     578             : 
     579             :         // add to cache
     580           0 :         shapeData->fKey.set(shape, ctm);
     581           0 :         shapeData->fID = id;
     582             : 
     583             :         // set the bounds rect to the original bounds
     584           0 :         shapeData->fBounds = SkRect::Make(devPathBounds);
     585           0 :         shapeData->fBounds.offset(-translateX, -translateY);
     586             : 
     587             :         // set up path to texture coordinate transform
     588           0 :         shapeData->fScale = SK_Scalar1;
     589           0 :         shapeData->fTranslate.fX = atlasLocation.fX + translateX;
     590           0 :         shapeData->fTranslate.fY = atlasLocation.fY + translateY;
     591             : 
     592           0 :         fShapeCache->add(shapeData);
     593           0 :         fShapeList->addToTail(shapeData);
     594             : #ifdef DF_PATH_TRACKING
     595             :         ++g_NumCachedPaths;
     596             : #endif
     597           0 :         return true;
     598             :     }
     599             : 
     600           0 :     void writePathVertices(GrDrawOp::Target* target,
     601             :                            GrDrawOpAtlas* atlas,
     602             :                            intptr_t offset,
     603             :                            GrColor color,
     604             :                            size_t vertexStride,
     605             :                            SkScalar maxScale,
     606             :                            const SkVector& preTranslate,
     607             :                            const ShapeData* shapeData) const {
     608           0 :         SkPoint* positions = reinterpret_cast<SkPoint*>(offset);
     609             : 
     610           0 :         SkRect bounds = shapeData->fBounds;
     611           0 :         if (fUsesDistanceField) {
     612             :             // outset bounds to include ~1 pixel of AA in device space
     613           0 :             SkScalar outset = SkScalarInvert(maxScale);
     614           0 :             bounds.outset(outset, outset);
     615             :         }
     616             : 
     617             :         // vertex positions
     618             :         // TODO make the vertex attributes a struct
     619           0 :         positions->setRectFan(bounds.left() + preTranslate.fX,
     620           0 :                               bounds.top() + preTranslate.fY,
     621           0 :                               bounds.right() + preTranslate.fX,
     622           0 :                               bounds.bottom() + preTranslate.fY,
     623           0 :                               vertexStride);
     624             : 
     625             :         // colors
     626           0 :         for (int i = 0; i < kVerticesPerQuad; i++) {
     627           0 :             GrColor* colorPtr = (GrColor*)(offset + sizeof(SkPoint) + i * vertexStride);
     628           0 :             *colorPtr = color;
     629             :         }
     630             : 
     631             :         // set up texture coordinates
     632           0 :         SkScalar texLeft = bounds.fLeft;
     633           0 :         SkScalar texTop = bounds.fTop;
     634           0 :         SkScalar texRight = bounds.fRight;
     635           0 :         SkScalar texBottom = bounds.fBottom;
     636             : 
     637             :         // transform original path's bounds to texture space
     638           0 :         SkScalar scale = shapeData->fScale;
     639           0 :         const SkVector& translate = shapeData->fTranslate;
     640           0 :         texLeft *= scale;
     641           0 :         texTop *= scale;
     642           0 :         texRight *= scale;
     643           0 :         texBottom *= scale;
     644           0 :         texLeft += translate.fX;
     645           0 :         texTop += translate.fY;
     646           0 :         texRight += translate.fX;
     647           0 :         texBottom += translate.fY;
     648             : 
     649             :         // convert texcoords to unsigned short format
     650           0 :         sk_sp<GrTextureProxy> proxy = atlas->getProxy();
     651             : 
     652             :         // The proxy must be functionally exact for this normalization to work correctly
     653           0 :         SkASSERT(GrResourceProvider::IsFunctionallyExact(proxy.get()));
     654           0 :         SkScalar uFactor = 65535.f / proxy->width();
     655           0 :         SkScalar vFactor = 65535.f / proxy->height();
     656           0 :         uint16_t l = (uint16_t)(texLeft*uFactor);
     657           0 :         uint16_t t = (uint16_t)(texTop*vFactor);
     658           0 :         uint16_t r = (uint16_t)(texRight*uFactor);
     659           0 :         uint16_t b = (uint16_t)(texBottom*vFactor);
     660             : 
     661             :         // set vertex texture coords
     662           0 :         intptr_t textureCoordOffset = offset + sizeof(SkPoint) + sizeof(GrColor);
     663           0 :         uint16_t* textureCoords = (uint16_t*) textureCoordOffset;
     664           0 :         textureCoords[0] = l;
     665           0 :         textureCoords[1] = t;
     666           0 :         textureCoordOffset += vertexStride;
     667           0 :         textureCoords = (uint16_t*)textureCoordOffset;
     668           0 :         textureCoords[0] = l;
     669           0 :         textureCoords[1] = b;
     670           0 :         textureCoordOffset += vertexStride;
     671           0 :         textureCoords = (uint16_t*)textureCoordOffset;
     672           0 :         textureCoords[0] = r;
     673           0 :         textureCoords[1] = b;
     674           0 :         textureCoordOffset += vertexStride;
     675           0 :         textureCoords = (uint16_t*)textureCoordOffset;
     676           0 :         textureCoords[0] = r;
     677           0 :         textureCoords[1] = t;
     678           0 :     }
     679             : 
     680           0 :     void flush(GrLegacyMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
     681           0 :         if (flushInfo->fInstancesToFlush) {
     682           0 :             GrMesh mesh;
     683             :             int maxInstancesPerDraw =
     684           0 :                 static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6);
     685           0 :             mesh.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer.get(),
     686             :                 flushInfo->fIndexBuffer.get(), flushInfo->fVertexOffset, kVerticesPerQuad,
     687           0 :                 kIndicesPerQuad, flushInfo->fInstancesToFlush, maxInstancesPerDraw);
     688           0 :             target->draw(flushInfo->fGeometryProcessor.get(), this->pipeline(), mesh);
     689           0 :             flushInfo->fVertexOffset += kVerticesPerQuad * flushInfo->fInstancesToFlush;
     690           0 :             flushInfo->fInstancesToFlush = 0;
     691             :         }
     692           0 :     }
     693             : 
     694           0 :     GrColor color() const { return fShapes[0].fColor; }
     695           0 :     const SkMatrix& viewMatrix() const { return fViewMatrix; }
     696           0 :     bool usesLocalCoords() const { return fUsesLocalCoords; }
     697           0 :     bool usesDistanceField() const { return fUsesDistanceField; }
     698             : 
     699           0 :     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
     700           0 :         SmallPathOp* that = t->cast<SmallPathOp>();
     701           0 :         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
     702             :                                     that->bounds(), caps)) {
     703           0 :             return false;
     704             :         }
     705             : 
     706           0 :         if (this->usesDistanceField() != that->usesDistanceField()) {
     707           0 :             return false;
     708             :         }
     709             : 
     710             :         // TODO We can position on the cpu for distance field paths
     711           0 :         if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
     712           0 :             return false;
     713             :         }
     714             : 
     715           0 :         if (!this->usesDistanceField() && this->usesLocalCoords() &&
     716           0 :             !this->fShapes[0].fTranslate.equalsWithinTolerance(that->fShapes[0].fTranslate)) {
     717           0 :             return false;
     718             :         }
     719             : 
     720           0 :         fShapes.push_back_n(that->fShapes.count(), that->fShapes.begin());
     721           0 :         this->joinBounds(*that);
     722           0 :         return true;
     723             :     }
     724             : 
     725             :     SkMatrix fViewMatrix;
     726             :     bool fUsesLocalCoords;
     727             :     bool fUsesDistanceField;
     728             : 
     729           0 :     struct Entry {
     730             :         GrColor  fColor;
     731             :         GrShape  fShape;
     732             :         SkVector fTranslate;
     733             :     };
     734             : 
     735             :     SkSTArray<1, Entry> fShapes;
     736             :     GrDrawOpAtlas* fAtlas;
     737             :     ShapeCache* fShapeCache;
     738             :     ShapeDataList* fShapeList;
     739             :     bool fGammaCorrect;
     740             : 
     741             :     typedef GrLegacyMeshDrawOp INHERITED;
     742             : };
     743             : 
     744           0 : bool GrSmallPathRenderer::onDrawPath(const DrawPathArgs& args) {
     745           0 :     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
     746             :                               "GrSmallPathRenderer::onDrawPath");
     747             : 
     748             :     // we've already bailed on inverse filled paths, so this is safe
     749           0 :     SkASSERT(!args.fShape->isEmpty());
     750           0 :     SkASSERT(args.fShape->hasUnstyledKey());
     751           0 :     if (!fAtlas) {
     752           0 :         fAtlas = GrDrawOpAtlas::Make(args.fContext,
     753             :                                      kAlpha_8_GrPixelConfig,
     754             :                                      ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
     755             :                                      NUM_PLOTS_X, NUM_PLOTS_Y,
     756             :                                      &GrSmallPathRenderer::HandleEviction,
     757           0 :                                      (void*)this);
     758           0 :         if (!fAtlas) {
     759           0 :             return false;
     760             :         }
     761             :     }
     762             : 
     763             :     std::unique_ptr<GrLegacyMeshDrawOp> op =
     764           0 :             SmallPathOp::Make(args.fPaint.getColor(), *args.fShape, *args.fViewMatrix, fAtlas.get(),
     765           0 :                               &fShapeCache, &fShapeList, args.fGammaCorrect);
     766           0 :     GrPipelineBuilder pipelineBuilder(std::move(args.fPaint), args.fAAType);
     767           0 :     pipelineBuilder.setUserStencil(args.fUserStencilSettings);
     768             : 
     769           0 :     args.fRenderTargetContext->addLegacyMeshDrawOp(std::move(pipelineBuilder), *args.fClip,
     770           0 :                                                    std::move(op));
     771             : 
     772           0 :     return true;
     773             : }
     774             : 
     775             : ///////////////////////////////////////////////////////////////////////////////////////////////////
     776             : 
     777             : #if GR_TEST_UTILS
     778             : 
     779             : struct PathTestStruct {
     780             :     typedef GrSmallPathRenderer::ShapeCache ShapeCache;
     781             :     typedef GrSmallPathRenderer::ShapeData ShapeData;
     782             :     typedef GrSmallPathRenderer::ShapeDataList ShapeDataList;
     783           0 :     PathTestStruct() : fContextID(SK_InvalidGenID), fAtlas(nullptr) {}
     784           0 :     ~PathTestStruct() { this->reset(); }
     785             : 
     786           0 :     void reset() {
     787           0 :         ShapeDataList::Iter iter;
     788           0 :         iter.init(fShapeList, ShapeDataList::Iter::kHead_IterStart);
     789             :         ShapeData* shapeData;
     790           0 :         while ((shapeData = iter.get())) {
     791           0 :             iter.next();
     792           0 :             fShapeList.remove(shapeData);
     793           0 :             delete shapeData;
     794             :         }
     795           0 :         fAtlas = nullptr;
     796           0 :         fShapeCache.reset();
     797           0 :     }
     798             : 
     799           0 :     static void HandleEviction(GrDrawOpAtlas::AtlasID id, void* pr) {
     800           0 :         PathTestStruct* dfpr = (PathTestStruct*)pr;
     801             :         // remove any paths that use this plot
     802           0 :         ShapeDataList::Iter iter;
     803           0 :         iter.init(dfpr->fShapeList, ShapeDataList::Iter::kHead_IterStart);
     804             :         ShapeData* shapeData;
     805           0 :         while ((shapeData = iter.get())) {
     806           0 :             iter.next();
     807           0 :             if (id == shapeData->fID) {
     808           0 :                 dfpr->fShapeCache.remove(shapeData->fKey);
     809           0 :                 dfpr->fShapeList.remove(shapeData);
     810           0 :                 delete shapeData;
     811             :             }
     812             :         }
     813           0 :     }
     814             : 
     815             :     uint32_t fContextID;
     816             :     std::unique_ptr<GrDrawOpAtlas> fAtlas;
     817             :     ShapeCache fShapeCache;
     818             :     ShapeDataList fShapeList;
     819             : };
     820             : 
     821           0 : DRAW_OP_TEST_DEFINE(SmallPathOp) {
     822           0 :     static PathTestStruct gTestStruct;
     823             : 
     824           0 :     if (context->uniqueID() != gTestStruct.fContextID) {
     825           0 :         gTestStruct.fContextID = context->uniqueID();
     826           0 :         gTestStruct.reset();
     827           0 :         gTestStruct.fAtlas = GrDrawOpAtlas::Make(context, kAlpha_8_GrPixelConfig,
     828             :                                                  ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
     829             :                                                  NUM_PLOTS_X, NUM_PLOTS_Y,
     830             :                                                  &PathTestStruct::HandleEviction,
     831           0 :                                                  (void*)&gTestStruct);
     832             :     }
     833             : 
     834           0 :     SkMatrix viewMatrix = GrTest::TestMatrix(random);
     835           0 :     GrColor color = GrRandomColor(random);
     836           0 :     bool gammaCorrect = random->nextBool();
     837             : 
     838             :     // This path renderer only allows fill styles.
     839           0 :     GrShape shape(GrTest::TestPath(random), GrStyle::SimpleFill());
     840             : 
     841             :     return SmallPathOp::Make(color, shape, viewMatrix, gTestStruct.fAtlas.get(),
     842           0 :                              &gTestStruct.fShapeCache, &gTestStruct.fShapeList, gammaCorrect);
     843             : }
     844             : 
     845             : #endif

Generated by: LCOV version 1.13