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