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