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
27static 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.
31static const SkScalar kMaxStrokeWidth = 20.0;
32
33GrAALinearizingConvexPathRenderer::GrAALinearizingConvexPathRenderer() {
34}
35
36///////////////////////////////////////////////////////////////////////////////
37
38bool 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
70static 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
103static 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
120class AAFlatteningConvexPathOp final : public GrMeshDrawOp {
121public:
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
149private:
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
324bool 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
355DRAW_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