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