Line data Source code
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 :
33 0 : GrAALinearizingConvexPathRenderer::GrAALinearizingConvexPathRenderer() {
34 0 : }
35 :
36 : ///////////////////////////////////////////////////////////////////////////////
37 :
38 0 : bool GrAALinearizingConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
39 0 : if (GrAAType::kCoverage != args.fAAType) {
40 0 : return false;
41 : }
42 0 : if (!args.fShape->knownToBeConvex()) {
43 0 : return false;
44 : }
45 0 : if (args.fShape->style().pathEffect()) {
46 0 : return false;
47 : }
48 0 : if (args.fShape->inverseFilled()) {
49 0 : return false;
50 : }
51 0 : const SkStrokeRec& stroke = args.fShape->style().strokeRec();
52 :
53 0 : if (stroke.getStyle() == SkStrokeRec::kStroke_Style ||
54 0 : stroke.getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
55 0 : if (!args.fViewMatrix->isSimilarity()) {
56 0 : return false;
57 : }
58 0 : SkScalar strokeWidth = args.fViewMatrix->getMaxScale() * stroke.getWidth();
59 0 : if (strokeWidth < 1.0f && stroke.getStyle() == SkStrokeRec::kStroke_Style) {
60 0 : return false;
61 : }
62 0 : return strokeWidth <= kMaxStrokeWidth &&
63 0 : args.fShape->knownToBeClosed() &&
64 0 : stroke.getJoin() != SkPaint::Join::kRound_Join;
65 : }
66 0 : return stroke.getStyle() == SkStrokeRec::kFill_Style;
67 : }
68 :
69 : // extract the result vertices and indices from the GrAAConvexTessellator
70 0 : 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 0 : intptr_t verts = reinterpret_cast<intptr_t>(vertices);
78 :
79 0 : for (int i = 0; i < tess.numPts(); ++i) {
80 0 : *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i);
81 : }
82 :
83 : // Make 'verts' point to the colors
84 0 : verts += sizeof(SkPoint);
85 0 : for (int i = 0; i < tess.numPts(); ++i) {
86 0 : if (tweakAlphaForCoverage) {
87 0 : SkASSERT(SkScalarRoundToInt(255.0f * tess.coverage(i)) <= 255);
88 0 : unsigned scale = SkScalarRoundToInt(255.0f * tess.coverage(i));
89 0 : GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
90 0 : *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
91 : } else {
92 0 : *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
93 0 : *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
94 0 : tess.coverage(i);
95 : }
96 : }
97 :
98 0 : for (int i = 0; i < tess.numIndices(); ++i) {
99 0 : idxs[i] = tess.index(i) + firstIndex;
100 : }
101 0 : }
102 :
103 0 : 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 0 : if (tweakAlphaForCoverage) {
110 0 : coverageType = Coverage::kSolid_Type;
111 : } else {
112 0 : coverageType = Coverage::kAttribute_Type;
113 : }
114 : LocalCoords::Type localCoordsType =
115 0 : usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
116 : return MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type, coverageType, localCoordsType,
117 0 : viewMatrix);
118 : }
119 :
120 0 : class AAFlatteningConvexPathOp final : public GrLegacyMeshDrawOp {
121 : public:
122 0 : DEFINE_OP_CLASS_ID
123 0 : static std::unique_ptr<GrLegacyMeshDrawOp> 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<GrLegacyMeshDrawOp>(new AAFlatteningConvexPathOp(
131 0 : color, viewMatrix, path, strokeWidth, style, join, miterLimit));
132 : }
133 :
134 0 : const char* name() const override { return "AAFlatteningConvexPathOp"; }
135 :
136 0 : SkString dumpInfo() const override {
137 0 : SkString string;
138 0 : for (const auto& path : fPaths) {
139 0 : string.appendf(
140 : "Color: 0x%08x, StrokeWidth: %.2f, Style: %d, Join: %d, "
141 : "MiterLimit: %.2f\n",
142 0 : path.fColor, path.fStrokeWidth, path.fStyle, path.fJoin, path.fMiterLimit);
143 : }
144 0 : string.append(DumpPipelineInfo(*this->pipeline()));
145 0 : string.append(INHERITED::dumpInfo());
146 0 : return string;
147 : }
148 :
149 : private:
150 0 : 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 0 : : INHERITED(ClassID()) {
158 : fPaths.emplace_back(
159 0 : PathData{color, viewMatrix, path, strokeWidth, style, join, miterLimit});
160 :
161 : // compute bounds
162 0 : SkRect bounds = path.getBounds();
163 0 : SkScalar w = strokeWidth;
164 0 : if (w > 0) {
165 0 : w /= 2;
166 : // If the half stroke width is < 1 then we effectively fallback to bevel joins.
167 0 : if (SkPaint::kMiter_Join == join && w > 1.f) {
168 0 : w *= miterLimit;
169 : }
170 0 : bounds.outset(w, w);
171 : }
172 0 : this->setTransformedBounds(bounds, viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
173 0 : }
174 :
175 0 : void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
176 : GrProcessorAnalysisCoverage* coverage) const override {
177 0 : color->setToConstant(fPaths[0].fColor);
178 0 : *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
179 0 : }
180 :
181 0 : void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
182 0 : optimizations.getOverrideColorIfSet(&fPaths[0].fColor);
183 0 : fUsesLocalCoords = optimizations.readsLocalCoords();
184 0 : fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
185 0 : }
186 :
187 0 : void draw(GrLegacyMeshDrawOp::Target* target, const GrGeometryProcessor* gp, int vertexCount,
188 : size_t vertexStride, void* vertices, int indexCount, uint16_t* indices) const {
189 0 : if (vertexCount == 0 || indexCount == 0) {
190 0 : return;
191 : }
192 : const GrBuffer* vertexBuffer;
193 0 : GrMesh mesh;
194 : int firstVertex;
195 : void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer,
196 0 : &firstVertex);
197 0 : if (!verts) {
198 0 : SkDebugf("Could not allocate vertices\n");
199 0 : return;
200 : }
201 0 : memcpy(verts, vertices, vertexCount * vertexStride);
202 :
203 : const GrBuffer* indexBuffer;
204 : int firstIndex;
205 0 : uint16_t* idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
206 0 : if (!idxs) {
207 0 : SkDebugf("Could not allocate indices\n");
208 0 : return;
209 : }
210 0 : memcpy(idxs, indices, indexCount * sizeof(uint16_t));
211 : mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
212 0 : firstIndex, vertexCount, indexCount);
213 0 : target->draw(gp, this->pipeline(), mesh);
214 : }
215 :
216 0 : void onPrepareDraws(Target* target) const override {
217 0 : bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
218 :
219 : // Setup GrGeometryProcessor
220 : sk_sp<GrGeometryProcessor> gp(create_fill_gp(
221 0 : canTweakAlphaForCoverage, this->viewMatrix(), this->usesLocalCoords()));
222 0 : if (!gp) {
223 0 : SkDebugf("Couldn't create a GrGeometryProcessor\n");
224 0 : return;
225 : }
226 :
227 0 : size_t vertexStride = gp->getVertexStride();
228 :
229 0 : SkASSERT(canTweakAlphaForCoverage ?
230 : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
231 : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
232 :
233 0 : int instanceCount = fPaths.count();
234 :
235 0 : int vertexCount = 0;
236 0 : int indexCount = 0;
237 0 : int maxVertices = DEFAULT_BUFFER_SIZE;
238 0 : int maxIndices = DEFAULT_BUFFER_SIZE;
239 0 : uint8_t* vertices = (uint8_t*) sk_malloc_throw(maxVertices * vertexStride);
240 0 : uint16_t* indices = (uint16_t*) sk_malloc_throw(maxIndices * sizeof(uint16_t));
241 0 : for (int i = 0; i < instanceCount; i++) {
242 0 : const PathData& args = fPaths[i];
243 0 : GrAAConvexTessellator tess(args.fStyle, args.fStrokeWidth,
244 0 : args.fJoin, args.fMiterLimit);
245 :
246 0 : if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
247 0 : continue;
248 : }
249 :
250 0 : int currentIndices = tess.numIndices();
251 0 : SkASSERT(currentIndices <= UINT16_MAX);
252 0 : 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 0 : this->draw(target, gp.get(),
256 0 : vertexCount, vertexStride, vertices, indexCount, indices);
257 0 : vertexCount = 0;
258 0 : indexCount = 0;
259 : }
260 0 : int currentVertices = tess.numPts();
261 0 : if (vertexCount + currentVertices > maxVertices) {
262 0 : maxVertices = SkTMax(vertexCount + currentVertices, maxVertices * 2);
263 0 : vertices = (uint8_t*) sk_realloc_throw(vertices, maxVertices * vertexStride);
264 : }
265 0 : if (indexCount + currentIndices > maxIndices) {
266 0 : maxIndices = SkTMax(indexCount + currentIndices, maxIndices * 2);
267 0 : indices = (uint16_t*) sk_realloc_throw(indices, maxIndices * sizeof(uint16_t));
268 : }
269 :
270 0 : extract_verts(tess, vertices + vertexStride * vertexCount, vertexStride, args.fColor,
271 0 : vertexCount, indices + indexCount, canTweakAlphaForCoverage);
272 0 : vertexCount += currentVertices;
273 0 : indexCount += currentIndices;
274 : }
275 0 : this->draw(target, gp.get(), vertexCount, vertexStride, vertices, indexCount, indices);
276 0 : sk_free(vertices);
277 0 : sk_free(indices);
278 : }
279 :
280 0 : bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
281 0 : AAFlatteningConvexPathOp* that = t->cast<AAFlatteningConvexPathOp>();
282 0 : if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
283 : that->bounds(), caps)) {
284 0 : return false;
285 : }
286 :
287 0 : SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
288 0 : if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
289 0 : 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 0 : if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
295 0 : fCanTweakAlphaForCoverage = false;
296 : }
297 :
298 0 : fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
299 0 : this->joinBounds(*that);
300 0 : return true;
301 : }
302 :
303 0 : bool usesLocalCoords() const { return fUsesLocalCoords; }
304 0 : bool canTweakAlphaForCoverage() const { return fCanTweakAlphaForCoverage; }
305 0 : const SkMatrix& viewMatrix() const { return fPaths[0].fViewMatrix; }
306 :
307 0 : 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 GrLegacyMeshDrawOp INHERITED;
322 : };
323 :
324 0 : bool GrAALinearizingConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
325 0 : GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
326 : "GrAALinearizingConvexPathRenderer::onDrawPath");
327 0 : SkASSERT(!args.fRenderTargetContext->isUnifiedMultisampled());
328 0 : SkASSERT(!args.fShape->isEmpty());
329 0 : SkASSERT(!args.fShape->style().pathEffect());
330 :
331 0 : SkPath path;
332 0 : args.fShape->asPath(&path);
333 0 : bool fill = args.fShape->style().isSimpleFill();
334 0 : const SkStrokeRec& stroke = args.fShape->style().strokeRec();
335 0 : SkScalar strokeWidth = fill ? -1.0f : stroke.getWidth();
336 0 : SkPaint::Join join = fill ? SkPaint::Join::kMiter_Join : stroke.getJoin();
337 0 : SkScalar miterLimit = stroke.getMiter();
338 :
339 : std::unique_ptr<GrLegacyMeshDrawOp> op =
340 0 : AAFlatteningConvexPathOp::Make(args.fPaint.getColor(), *args.fViewMatrix, path,
341 0 : strokeWidth, stroke.getStyle(), join, miterLimit);
342 :
343 0 : GrPipelineBuilder pipelineBuilder(std::move(args.fPaint), args.fAAType);
344 0 : pipelineBuilder.setUserStencil(args.fUserStencilSettings);
345 :
346 0 : args.fRenderTargetContext->addLegacyMeshDrawOp(std::move(pipelineBuilder), *args.fClip,
347 0 : std::move(op));
348 :
349 0 : return true;
350 : }
351 :
352 : ///////////////////////////////////////////////////////////////////////////////////////////////////
353 :
354 : #if GR_TEST_UTILS
355 :
356 0 : DRAW_OP_TEST_DEFINE(AAFlatteningConvexPathOp) {
357 0 : GrColor color = GrRandomColor(random);
358 0 : SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
359 0 : SkPath path = GrTest::TestPathConvex(random);
360 :
361 : SkStrokeRec::Style styles[3] = { SkStrokeRec::kFill_Style,
362 : SkStrokeRec::kStroke_Style,
363 0 : SkStrokeRec::kStrokeAndFill_Style };
364 :
365 0 : SkStrokeRec::Style style = styles[random->nextU() % 3];
366 :
367 0 : SkScalar strokeWidth = -1.f;
368 0 : SkPaint::Join join = SkPaint::kMiter_Join;
369 0 : SkScalar miterLimit = 0.5f;
370 :
371 0 : if (SkStrokeRec::kFill_Style != style) {
372 0 : strokeWidth = random->nextRangeF(1.0f, 10.0f);
373 0 : if (random->nextBool()) {
374 0 : join = SkPaint::kMiter_Join;
375 : } else {
376 0 : join = SkPaint::kBevel_Join;
377 : }
378 0 : miterLimit = random->nextRangeF(0.5f, 2.0f);
379 : }
380 :
381 : return AAFlatteningConvexPathOp::Make(color, viewMatrix, path, strokeWidth, style, join,
382 0 : miterLimit);
383 : }
384 :
385 : #endif
|