Home | History | Annotate | Download | only in ops
      1 /*
      2  * Copyright 2015 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "GrAALinearizingConvexPathRenderer.h"
      9 
     10 #include "GrAAConvexTessellator.h"
     11 #include "GrContext.h"
     12 #include "GrDefaultGeoProcFactory.h"
     13 #include "GrDrawOpTest.h"
     14 #include "GrGeometryProcessor.h"
     15 #include "GrOpFlushState.h"
     16 #include "GrPathUtils.h"
     17 #include "GrPipelineBuilder.h"
     18 #include "GrProcessor.h"
     19 #include "GrStyle.h"
     20 #include "SkGeometry.h"
     21 #include "SkPathPriv.h"
     22 #include "SkString.h"
     23 #include "SkTraceEvent.h"
     24 #include "glsl/GrGLSLGeometryProcessor.h"
     25 #include "ops/GrMeshDrawOp.h"
     26 
     27 static const int DEFAULT_BUFFER_SIZE = 100;
     28 
     29 // The thicker the stroke, the harder it is to produce high-quality results using tessellation. For
     30 // the time being, we simply drop back to software rendering above this stroke width.
     31 static const SkScalar kMaxStrokeWidth = 20.0;
     32 
     33 GrAALinearizingConvexPathRenderer::GrAALinearizingConvexPathRenderer() {
     34 }
     35 
     36 ///////////////////////////////////////////////////////////////////////////////
     37 
     38 bool GrAALinearizingConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
     39     if (GrAAType::kCoverage != args.fAAType) {
     40         return false;
     41     }
     42     if (!args.fShape->knownToBeConvex()) {
     43         return false;
     44     }
     45     if (args.fShape->style().pathEffect()) {
     46         return false;
     47     }
     48     if (args.fShape->inverseFilled()) {
     49         return false;
     50     }
     51     const SkStrokeRec& stroke = args.fShape->style().strokeRec();
     52 
     53     if (stroke.getStyle() == SkStrokeRec::kStroke_Style ||
     54         stroke.getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
     55         if (!args.fViewMatrix->isSimilarity()) {
     56             return false;
     57         }
     58         SkScalar strokeWidth = args.fViewMatrix->getMaxScale() * stroke.getWidth();
     59         if (strokeWidth < 1.0f && stroke.getStyle() == SkStrokeRec::kStroke_Style) {
     60             return false;
     61         }
     62         return strokeWidth <= kMaxStrokeWidth &&
     63                args.fShape->knownToBeClosed() &&
     64                stroke.getJoin() != SkPaint::Join::kRound_Join;
     65     }
     66     return stroke.getStyle() == SkStrokeRec::kFill_Style;
     67 }
     68 
     69 // extract the result vertices and indices from the GrAAConvexTessellator
     70 static void extract_verts(const GrAAConvexTessellator& tess,
     71                           void* vertices,
     72                           size_t vertexStride,
     73                           GrColor color,
     74                           uint16_t firstIndex,
     75                           uint16_t* idxs,
     76                           bool tweakAlphaForCoverage) {
     77     intptr_t verts = reinterpret_cast<intptr_t>(vertices);
     78 
     79     for (int i = 0; i < tess.numPts(); ++i) {
     80         *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i);
     81     }
     82 
     83     // Make 'verts' point to the colors
     84     verts += sizeof(SkPoint);
     85     for (int i = 0; i < tess.numPts(); ++i) {
     86         if (tweakAlphaForCoverage) {
     87             SkASSERT(SkScalarRoundToInt(255.0f * tess.coverage(i)) <= 255);
     88             unsigned scale = SkScalarRoundToInt(255.0f * tess.coverage(i));
     89             GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
     90             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
     91         } else {
     92             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
     93             *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
     94                     tess.coverage(i);
     95         }
     96     }
     97 
     98     for (int i = 0; i < tess.numIndices(); ++i) {
     99         idxs[i] = tess.index(i) + firstIndex;
    100     }
    101 }
    102 
    103 static sk_sp<GrGeometryProcessor> create_fill_gp(bool tweakAlphaForCoverage,
    104                                                  const SkMatrix& viewMatrix,
    105                                                  bool usesLocalCoords) {
    106     using namespace GrDefaultGeoProcFactory;
    107 
    108     Coverage::Type coverageType;
    109     if (tweakAlphaForCoverage) {
    110         coverageType = Coverage::kSolid_Type;
    111     } else {
    112         coverageType = Coverage::kAttribute_Type;
    113     }
    114     LocalCoords::Type localCoordsType =
    115             usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
    116     return MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type, coverageType, localCoordsType,
    117                               viewMatrix);
    118 }
    119 
    120 class AAFlatteningConvexPathOp final : public GrMeshDrawOp {
    121 public:
    122     DEFINE_OP_CLASS_ID
    123     static std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
    124                                               const SkMatrix& viewMatrix,
    125                                               const SkPath& path,
    126                                               SkScalar strokeWidth,
    127                                               SkStrokeRec::Style style,
    128                                               SkPaint::Join join,
    129                                               SkScalar miterLimit) {
    130         return std::unique_ptr<GrMeshDrawOp>(new AAFlatteningConvexPathOp(
    131                 color, viewMatrix, path, strokeWidth, style, join, miterLimit));
    132     }
    133 
    134     const char* name() const override { return "AAFlatteningConvexPathOp"; }
    135 
    136     SkString dumpInfo() const override {
    137         SkString string;
    138         for (const auto& path : fPaths) {
    139             string.appendf(
    140                     "Color: 0x%08x, StrokeWidth: %.2f, Style: %d, Join: %d, "
    141                     "MiterLimit: %.2f\n",
    142                     path.fColor, path.fStrokeWidth, path.fStyle, path.fJoin, path.fMiterLimit);
    143         }
    144         string.append(DumpPipelineInfo(*this->pipeline()));
    145         string.append(INHERITED::dumpInfo());
    146         return string;
    147     }
    148 
    149 private:
    150     AAFlatteningConvexPathOp(GrColor color,
    151                              const SkMatrix& viewMatrix,
    152                              const SkPath& path,
    153                              SkScalar strokeWidth,
    154                              SkStrokeRec::Style style,
    155                              SkPaint::Join join,
    156                              SkScalar miterLimit)
    157             : INHERITED(ClassID()) {
    158         fPaths.emplace_back(
    159                 PathData{color, viewMatrix, path, strokeWidth, style, join, miterLimit});
    160 
    161         // compute bounds
    162         SkRect bounds = path.getBounds();
    163         SkScalar w = strokeWidth;
    164         if (w > 0) {
    165             w /= 2;
    166             // If the half stroke width is < 1 then we effectively fallback to bevel joins.
    167             if (SkPaint::kMiter_Join == join && w > 1.f) {
    168                 w *= miterLimit;
    169             }
    170             bounds.outset(w, w);
    171         }
    172         this->setTransformedBounds(bounds, viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
    173     }
    174 
    175     void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
    176                                             GrPipelineAnalysisCoverage* coverage) const override {
    177         color->setToConstant(fPaths[0].fColor);
    178         *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
    179     }
    180 
    181     void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
    182         optimizations.getOverrideColorIfSet(&fPaths[0].fColor);
    183         fUsesLocalCoords = optimizations.readsLocalCoords();
    184         fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
    185     }
    186 
    187     void draw(GrMeshDrawOp::Target* target, const GrGeometryProcessor* gp, int vertexCount,
    188               size_t vertexStride, void* vertices, int indexCount, uint16_t* indices) const {
    189         if (vertexCount == 0 || indexCount == 0) {
    190             return;
    191         }
    192         const GrBuffer* vertexBuffer;
    193         GrMesh mesh;
    194         int firstVertex;
    195         void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer,
    196                                               &firstVertex);
    197         if (!verts) {
    198             SkDebugf("Could not allocate vertices\n");
    199             return;
    200         }
    201         memcpy(verts, vertices, vertexCount * vertexStride);
    202 
    203         const GrBuffer* indexBuffer;
    204         int firstIndex;
    205         uint16_t* idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
    206         if (!idxs) {
    207             SkDebugf("Could not allocate indices\n");
    208             return;
    209         }
    210         memcpy(idxs, indices, indexCount * sizeof(uint16_t));
    211         mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
    212                          firstIndex, vertexCount, indexCount);
    213         target->draw(gp, mesh);
    214     }
    215 
    216     void onPrepareDraws(Target* target) const override {
    217         bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
    218 
    219         // Setup GrGeometryProcessor
    220         sk_sp<GrGeometryProcessor> gp(create_fill_gp(
    221                 canTweakAlphaForCoverage, this->viewMatrix(), this->usesLocalCoords()));
    222         if (!gp) {
    223             SkDebugf("Couldn't create a GrGeometryProcessor\n");
    224             return;
    225         }
    226 
    227         size_t vertexStride = gp->getVertexStride();
    228 
    229         SkASSERT(canTweakAlphaForCoverage ?
    230                  vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
    231                  vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
    232 
    233         int instanceCount = fPaths.count();
    234 
    235         int vertexCount = 0;
    236         int indexCount = 0;
    237         int maxVertices = DEFAULT_BUFFER_SIZE;
    238         int maxIndices = DEFAULT_BUFFER_SIZE;
    239         uint8_t* vertices = (uint8_t*) sk_malloc_throw(maxVertices * vertexStride);
    240         uint16_t* indices = (uint16_t*) sk_malloc_throw(maxIndices * sizeof(uint16_t));
    241         for (int i = 0; i < instanceCount; i++) {
    242             const PathData& args = fPaths[i];
    243             GrAAConvexTessellator tess(args.fStyle, args.fStrokeWidth,
    244                                        args.fJoin, args.fMiterLimit);
    245 
    246             if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
    247                 continue;
    248             }
    249 
    250             int currentIndices = tess.numIndices();
    251             SkASSERT(currentIndices <= UINT16_MAX);
    252             if (indexCount + currentIndices > UINT16_MAX) {
    253                 // if we added the current instance, we would overflow the indices we can store in a
    254                 // uint16_t. Draw what we've got so far and reset.
    255                 this->draw(target, gp.get(),
    256                            vertexCount, vertexStride, vertices, indexCount, indices);
    257                 vertexCount = 0;
    258                 indexCount = 0;
    259             }
    260             int currentVertices = tess.numPts();
    261             if (vertexCount + currentVertices > maxVertices) {
    262                 maxVertices = SkTMax(vertexCount + currentVertices, maxVertices * 2);
    263                 vertices = (uint8_t*) sk_realloc_throw(vertices, maxVertices * vertexStride);
    264             }
    265             if (indexCount + currentIndices > maxIndices) {
    266                 maxIndices = SkTMax(indexCount + currentIndices, maxIndices * 2);
    267                 indices = (uint16_t*) sk_realloc_throw(indices, maxIndices * sizeof(uint16_t));
    268             }
    269 
    270             extract_verts(tess, vertices + vertexStride * vertexCount, vertexStride, args.fColor,
    271                     vertexCount, indices + indexCount, canTweakAlphaForCoverage);
    272             vertexCount += currentVertices;
    273             indexCount += currentIndices;
    274         }
    275         this->draw(target, gp.get(), vertexCount, vertexStride, vertices, indexCount, indices);
    276         sk_free(vertices);
    277         sk_free(indices);
    278     }
    279 
    280     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
    281         AAFlatteningConvexPathOp* that = t->cast<AAFlatteningConvexPathOp>();
    282         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
    283                                     that->bounds(), caps)) {
    284             return false;
    285         }
    286 
    287         SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
    288         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
    289             return false;
    290         }
    291 
    292         // In the event of two ops, one who can tweak, one who cannot, we just fall back to not
    293         // tweaking
    294         if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
    295             fCanTweakAlphaForCoverage = false;
    296         }
    297 
    298         fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
    299         this->joinBounds(*that);
    300         return true;
    301     }
    302 
    303     bool usesLocalCoords() const { return fUsesLocalCoords; }
    304     bool canTweakAlphaForCoverage() const { return fCanTweakAlphaForCoverage; }
    305     const SkMatrix& viewMatrix() const { return fPaths[0].fViewMatrix; }
    306 
    307     struct PathData {
    308         GrColor fColor;
    309         SkMatrix fViewMatrix;
    310         SkPath fPath;
    311         SkScalar fStrokeWidth;
    312         SkStrokeRec::Style fStyle;
    313         SkPaint::Join fJoin;
    314         SkScalar fMiterLimit;
    315     };
    316 
    317     bool fUsesLocalCoords;
    318     bool fCanTweakAlphaForCoverage;
    319     SkSTArray<1, PathData, true> fPaths;
    320 
    321     typedef GrMeshDrawOp INHERITED;
    322 };
    323 
    324 bool GrAALinearizingConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
    325     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
    326                               "GrAALinearizingConvexPathRenderer::onDrawPath");
    327     SkASSERT(!args.fRenderTargetContext->isUnifiedMultisampled());
    328     SkASSERT(!args.fShape->isEmpty());
    329     SkASSERT(!args.fShape->style().pathEffect());
    330 
    331     SkPath path;
    332     args.fShape->asPath(&path);
    333     bool fill = args.fShape->style().isSimpleFill();
    334     const SkStrokeRec& stroke = args.fShape->style().strokeRec();
    335     SkScalar strokeWidth = fill ? -1.0f : stroke.getWidth();
    336     SkPaint::Join join = fill ? SkPaint::Join::kMiter_Join : stroke.getJoin();
    337     SkScalar miterLimit = stroke.getMiter();
    338 
    339     std::unique_ptr<GrMeshDrawOp> op =
    340             AAFlatteningConvexPathOp::Make(args.fPaint.getColor(), *args.fViewMatrix, path,
    341                                            strokeWidth, stroke.getStyle(), join, miterLimit);
    342 
    343     GrPipelineBuilder pipelineBuilder(std::move(args.fPaint), args.fAAType);
    344     pipelineBuilder.setUserStencil(args.fUserStencilSettings);
    345 
    346     args.fRenderTargetContext->addMeshDrawOp(pipelineBuilder, *args.fClip, std::move(op));
    347 
    348     return true;
    349 }
    350 
    351 ///////////////////////////////////////////////////////////////////////////////////////////////////
    352 
    353 #if GR_TEST_UTILS
    354 
    355 DRAW_OP_TEST_DEFINE(AAFlatteningConvexPathOp) {
    356     GrColor color = GrRandomColor(random);
    357     SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
    358     SkPath path = GrTest::TestPathConvex(random);
    359 
    360     SkStrokeRec::Style styles[3] = { SkStrokeRec::kFill_Style,
    361                                      SkStrokeRec::kStroke_Style,
    362                                      SkStrokeRec::kStrokeAndFill_Style };
    363 
    364     SkStrokeRec::Style style = styles[random->nextU() % 3];
    365 
    366     SkScalar strokeWidth = -1.f;
    367     SkPaint::Join join = SkPaint::kMiter_Join;
    368     SkScalar miterLimit = 0.5f;
    369 
    370     if (SkStrokeRec::kFill_Style != style) {
    371         strokeWidth = random->nextRangeF(1.0f, 10.0f);
    372         if (random->nextBool()) {
    373             join = SkPaint::kMiter_Join;
    374         } else {
    375             join = SkPaint::kBevel_Join;
    376         }
    377         miterLimit = random->nextRangeF(0.5f, 2.0f);
    378     }
    379 
    380     return AAFlatteningConvexPathOp::Make(color, viewMatrix, path, strokeWidth, style, join,
    381                                           miterLimit);
    382 }
    383 
    384 #endif
    385