• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GrAALinearizingConvexPathRenderer()33 GrAALinearizingConvexPathRenderer::GrAALinearizingConvexPathRenderer() {
34 }
35 
36 ///////////////////////////////////////////////////////////////////////////////
37 
onCanDrawPath(const CanDrawPathArgs & args) const38 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
extract_verts(const GrAAConvexTessellator & tess,void * vertices,size_t vertexStride,GrColor color,uint16_t firstIndex,uint16_t * idxs,bool tweakAlphaForCoverage)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 
create_fill_gp(bool tweakAlphaForCoverage,const SkMatrix & viewMatrix,bool usesLocalCoords)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
Make(GrColor color,const SkMatrix & viewMatrix,const SkPath & path,SkScalar strokeWidth,SkStrokeRec::Style style,SkPaint::Join join,SkScalar miterLimit)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 
name() const134     const char* name() const override { return "AAFlatteningConvexPathOp"; }
135 
dumpInfo() const136     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:
AAFlatteningConvexPathOp(GrColor color,const SkMatrix & viewMatrix,const SkPath & path,SkScalar strokeWidth,SkStrokeRec::Style style,SkPaint::Join join,SkScalar miterLimit)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 
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const175     void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
176                                             GrPipelineAnalysisCoverage* coverage) const override {
177         color->setToConstant(fPaths[0].fColor);
178         *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
179     }
180 
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)181     void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
182         optimizations.getOverrideColorIfSet(&fPaths[0].fColor);
183         fUsesLocalCoords = optimizations.readsLocalCoords();
184         fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
185     }
186 
draw(GrMeshDrawOp::Target * target,const GrGeometryProcessor * gp,int vertexCount,size_t vertexStride,void * vertices,int indexCount,uint16_t * indices) const187     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 
onPrepareDraws(Target * target) const216     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 
onCombineIfPossible(GrOp * t,const GrCaps & caps)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 
usesLocalCoords() const303     bool usesLocalCoords() const { return fUsesLocalCoords; }
canTweakAlphaForCoverage() const304     bool canTweakAlphaForCoverage() const { return fCanTweakAlphaForCoverage; }
viewMatrix() const305     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 
onDrawPath(const DrawPathArgs & args)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 
DRAW_OP_TEST_DEFINE(AAFlatteningConvexPathOp)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