blob: d899e7d0841425dd598ebe0f4c48b0aa47551bf7 [file] [log] [blame]
ethannicholas1a1b3ac2015-06-10 12:11:17 -07001
2/*
3 * Copyright 2015 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include "GrAALinearizingConvexPathRenderer.h"
10
11#include "GrAAConvexTessellator.h"
12#include "GrBatch.h"
13#include "GrBatchTarget.h"
14#include "GrBatchTest.h"
15#include "GrContext.h"
16#include "GrDefaultGeoProcFactory.h"
17#include "GrGeometryProcessor.h"
18#include "GrInvariantOutput.h"
19#include "GrPathUtils.h"
20#include "GrProcessor.h"
21#include "GrPipelineBuilder.h"
22#include "GrStrokeInfo.h"
23#include "SkGeometry.h"
24#include "SkString.h"
25#include "SkTraceEvent.h"
fmalitabd5d7e72015-06-26 07:18:24 -070026#include "SkPathPriv.h"
ethannicholas1a1b3ac2015-06-10 12:11:17 -070027#include "gl/GrGLProcessor.h"
ethannicholas1a1b3ac2015-06-10 12:11:17 -070028#include "gl/GrGLGeometryProcessor.h"
29#include "gl/builders/GrGLProgramBuilder.h"
30
fmalitabd5d7e72015-06-26 07:18:24 -070031static const int DEFAULT_BUFFER_SIZE = 100;
32
33// The thicker the stroke, the harder it is to produce high-quality results using tessellation. For
34// the time being, we simply drop back to software rendering above this stroke width.
35static const SkScalar kMaxStrokeWidth = 20.0;
ethannicholas1a1b3ac2015-06-10 12:11:17 -070036
37GrAALinearizingConvexPathRenderer::GrAALinearizingConvexPathRenderer() {
38}
39
40///////////////////////////////////////////////////////////////////////////////
41
42bool GrAALinearizingConvexPathRenderer::canDrawPath(const GrDrawTarget* target,
43 const GrPipelineBuilder*,
44 const SkMatrix& viewMatrix,
45 const SkPath& path,
46 const GrStrokeInfo& stroke,
47 bool antiAlias) const {
fmalitabd5d7e72015-06-26 07:18:24 -070048 if (!antiAlias) {
49 return false;
50 }
51 if (path.isInverseFillType()) {
52 return false;
53 }
54 if (!path.isConvex()) {
55 return false;
56 }
57 if (stroke.getStyle() == SkStrokeRec::kStroke_Style) {
58 return viewMatrix.isSimilarity() && stroke.getWidth() >= 1.0f &&
59 stroke.getWidth() <= kMaxStrokeWidth && !stroke.isDashed() &&
60 SkPathPriv::LastVerbIsClose(path) && stroke.getJoin() != SkPaint::Join::kRound_Join;
61 }
62 return stroke.getStyle() == SkStrokeRec::kFill_Style;
ethannicholas1a1b3ac2015-06-10 12:11:17 -070063}
64
65// extract the result vertices and indices from the GrAAConvexTessellator
66static void extract_verts(const GrAAConvexTessellator& tess,
67 void* vertices,
68 size_t vertexStride,
69 GrColor color,
70 uint16_t firstIndex,
71 uint16_t* idxs,
72 bool tweakAlphaForCoverage) {
73 intptr_t verts = reinterpret_cast<intptr_t>(vertices);
74
75 for (int i = 0; i < tess.numPts(); ++i) {
76 *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i);
77 }
78
79 // Make 'verts' point to the colors
80 verts += sizeof(SkPoint);
81 for (int i = 0; i < tess.numPts(); ++i) {
ethannicholas1a1b3ac2015-06-10 12:11:17 -070082 if (tweakAlphaForCoverage) {
fmalitabd5d7e72015-06-26 07:18:24 -070083 SkASSERT(SkScalarRoundToInt(255.0f * tess.coverage(i)) <= 255);
84 unsigned scale = SkScalarRoundToInt(255.0f * tess.coverage(i));
ethannicholas1a1b3ac2015-06-10 12:11:17 -070085 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
86 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
87 } else {
88 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
89 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
fmalitabd5d7e72015-06-26 07:18:24 -070090 tess.coverage(i);
ethannicholas1a1b3ac2015-06-10 12:11:17 -070091 }
92 }
93
94 for (int i = 0; i < tess.numIndices(); ++i) {
95 idxs[i] = tess.index(i) + firstIndex;
96 }
97}
98
99static const GrGeometryProcessor* create_fill_gp(bool tweakAlphaForCoverage,
100 const SkMatrix& localMatrix,
101 bool usesLocalCoords,
102 bool coverageIgnored) {
103 uint32_t flags = GrDefaultGeoProcFactory::kColor_GPType;
104 if (!tweakAlphaForCoverage) {
105 flags |= GrDefaultGeoProcFactory::kCoverage_GPType;
106 }
107
108 return GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, usesLocalCoords, coverageIgnored,
109 SkMatrix::I(), localMatrix);
110}
111
112class AAFlatteningConvexPathBatch : public GrBatch {
113public:
114 struct Geometry {
115 GrColor fColor;
116 SkMatrix fViewMatrix;
117 SkPath fPath;
fmalitabd5d7e72015-06-26 07:18:24 -0700118 SkScalar fStrokeWidth;
119 SkPaint::Join fJoin;
120 SkScalar fMiterLimit;
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700121 };
122
123 static GrBatch* Create(const Geometry& geometry) {
124 return SkNEW_ARGS(AAFlatteningConvexPathBatch, (geometry));
125 }
126
127 const char* name() const override { return "AAConvexBatch"; }
128
129 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
130 // When this is called on a batch, there is only one geometry bundle
131 out->setKnownFourComponents(fGeoData[0].fColor);
132 }
133 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
134 out->setUnknownSingleComponent();
135 }
136
137 void initBatchTracker(const GrPipelineInfo& init) override {
138 // Handle any color overrides
bsalomon7765a472015-07-08 11:26:37 -0700139 if (!init.readsColor()) {
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700140 fGeoData[0].fColor = GrColor_ILLEGAL;
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700141 }
bsalomon7765a472015-07-08 11:26:37 -0700142 init.getOverrideColorIfSet(&fGeoData[0].fColor);
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700143
144 // setup batch properties
bsalomon7765a472015-07-08 11:26:37 -0700145 fBatch.fColorIgnored = !init.readsColor();
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700146 fBatch.fColor = fGeoData[0].fColor;
bsalomon7765a472015-07-08 11:26:37 -0700147 fBatch.fUsesLocalCoords = init.readsLocalCoords();
148 fBatch.fCoverageIgnored = !init.readsCoverage();
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700149 fBatch.fLinesOnly = SkPath::kLine_SegmentMask == fGeoData[0].fPath.getSegmentMasks();
bsalomon7765a472015-07-08 11:26:37 -0700150 fBatch.fCanTweakAlphaForCoverage = init.canTweakAlphaForCoverage();
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700151 }
152
153 void draw(GrBatchTarget* batchTarget, const GrPipeline* pipeline, int vertexCount,
154 size_t vertexStride, void* vertices, int indexCount, uint16_t* indices) {
155 if (vertexCount == 0 || indexCount == 0) {
156 return;
157 }
158 const GrVertexBuffer* vertexBuffer;
159 GrVertices info;
160 int firstVertex;
161 void* verts = batchTarget->makeVertSpace(vertexStride, vertexCount, &vertexBuffer,
162 &firstVertex);
163 if (!verts) {
164 SkDebugf("Could not allocate vertices\n");
165 return;
166 }
167 memcpy(verts, vertices, vertexCount * vertexStride);
168
169 const GrIndexBuffer* indexBuffer;
170 int firstIndex;
171 uint16_t* idxs = batchTarget->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
172 if (!idxs) {
173 SkDebugf("Could not allocate indices\n");
174 return;
175 }
176 memcpy(idxs, indices, indexCount * sizeof(uint16_t));
177 info.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
178 firstIndex, vertexCount, indexCount);
179 batchTarget->draw(info);
180 }
fmalitabd5d7e72015-06-26 07:18:24 -0700181
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700182 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
183 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
184
185 SkMatrix invert;
186 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
187 SkDebugf("Could not invert viewmatrix\n");
188 return;
189 }
190
191 // Setup GrGeometryProcessor
192 SkAutoTUnref<const GrGeometryProcessor> gp(
193 create_fill_gp(canTweakAlphaForCoverage, invert,
194 this->usesLocalCoords(),
195 this->coverageIgnored()));
196
197 batchTarget->initDraw(gp, pipeline);
198
199 size_t vertexStride = gp->getVertexStride();
200
201 SkASSERT(canTweakAlphaForCoverage ?
202 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
203 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
204
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700205 int instanceCount = fGeoData.count();
206
207 int vertexCount = 0;
208 int indexCount = 0;
209 int maxVertices = DEFAULT_BUFFER_SIZE;
210 int maxIndices = DEFAULT_BUFFER_SIZE;
211 uint8_t* vertices = (uint8_t*) malloc(maxVertices * vertexStride);
212 uint16_t* indices = (uint16_t*) malloc(maxIndices * sizeof(uint16_t));
213 for (int i = 0; i < instanceCount; i++) {
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700214 Geometry& args = fGeoData[i];
fmalitabd5d7e72015-06-26 07:18:24 -0700215 GrAAConvexTessellator tess(args.fStrokeWidth, args.fJoin, args.fMiterLimit);
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700216
217 if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
218 continue;
219 }
220
221 int currentIndices = tess.numIndices();
222 SkASSERT(currentIndices <= UINT16_MAX);
223 if (indexCount + currentIndices > UINT16_MAX) {
224 // if we added the current instance, we would overflow the indices we can store in a
225 // uint16_t. Draw what we've got so far and reset.
226 draw(batchTarget, pipeline, vertexCount, vertexStride, vertices, indexCount,
227 indices);
228 vertexCount = 0;
229 indexCount = 0;
230 }
231 int currentVertices = tess.numPts();
232 if (vertexCount + currentVertices > maxVertices) {
233 maxVertices = SkTMax(vertexCount + currentVertices, maxVertices * 2);
234 vertices = (uint8_t*) realloc(vertices, maxVertices * vertexStride);
235 }
236 if (indexCount + currentIndices > maxIndices) {
237 maxIndices = SkTMax(indexCount + currentIndices, maxIndices * 2);
238 indices = (uint16_t*) realloc(indices, maxIndices * sizeof(uint16_t));
239 }
240
241 extract_verts(tess, vertices + vertexStride * vertexCount, vertexStride, args.fColor,
242 vertexCount, indices + indexCount, canTweakAlphaForCoverage);
243 vertexCount += currentVertices;
244 indexCount += currentIndices;
245 }
246 draw(batchTarget, pipeline, vertexCount, vertexStride, vertices, indexCount, indices);
247 free(vertices);
248 free(indices);
249 }
250
251 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
252
253private:
254 AAFlatteningConvexPathBatch(const Geometry& geometry) {
255 this->initClassID<AAFlatteningConvexPathBatch>();
256 fGeoData.push_back(geometry);
257
258 // compute bounds
259 fBounds = geometry.fPath.getBounds();
260 geometry.fViewMatrix.mapRect(&fBounds);
261 }
262
263 bool onCombineIfPossible(GrBatch* t) override {
joshualitt8cab9a72015-07-16 09:13:50 -0700264 if (!this->pipeline()->isEqual(*t->pipeline())) {
265 return false;
266 }
267
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700268 AAFlatteningConvexPathBatch* that = t->cast<AAFlatteningConvexPathBatch>();
269
270 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
271 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
272 return false;
273 }
274
275 // In the event of two batches, one who can tweak, one who cannot, we just fall back to
276 // not tweaking
277 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
278 fBatch.fCanTweakAlphaForCoverage = false;
279 }
280
281 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
282 this->joinBounds(that->bounds());
283 return true;
284 }
285
286 GrColor color() const { return fBatch.fColor; }
287 bool linesOnly() const { return fBatch.fLinesOnly; }
288 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
289 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
290 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
291 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
292
293 struct BatchTracker {
294 GrColor fColor;
295 bool fUsesLocalCoords;
296 bool fColorIgnored;
297 bool fCoverageIgnored;
298 bool fLinesOnly;
299 bool fCanTweakAlphaForCoverage;
300 };
301
302 BatchTracker fBatch;
303 SkSTArray<1, Geometry, true> fGeoData;
304};
305
306bool GrAALinearizingConvexPathRenderer::onDrawPath(GrDrawTarget* target,
307 GrPipelineBuilder* pipelineBuilder,
308 GrColor color,
309 const SkMatrix& vm,
310 const SkPath& path,
fmalitabd5d7e72015-06-26 07:18:24 -0700311 const GrStrokeInfo& stroke,
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700312 bool antiAlias) {
313 if (path.isEmpty()) {
314 return true;
315 }
316 AAFlatteningConvexPathBatch::Geometry geometry;
317 geometry.fColor = color;
318 geometry.fViewMatrix = vm;
319 geometry.fPath = path;
fmalitabd5d7e72015-06-26 07:18:24 -0700320 geometry.fStrokeWidth = stroke.isFillStyle() ? -1.0f : stroke.getWidth();
321 geometry.fJoin = stroke.isFillStyle() ? SkPaint::Join::kMiter_Join : stroke.getJoin();
322 geometry.fMiterLimit = stroke.getMiter();
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700323
324 SkAutoTUnref<GrBatch> batch(AAFlatteningConvexPathBatch::Create(geometry));
joshualitt1c735482015-07-13 08:08:25 -0700325 target->drawBatch(*pipelineBuilder, batch);
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700326
327 return true;
328}
329
330///////////////////////////////////////////////////////////////////////////////////////////////////
331
332#ifdef GR_TEST_UTILS
333
334BATCH_TEST_DEFINE(AAFlatteningConvexPathBatch) {
335 AAFlatteningConvexPathBatch::Geometry geometry;
336 geometry.fColor = GrRandomColor(random);
337 geometry.fViewMatrix = GrTest::TestMatrixInvertible(random);
338 geometry.fPath = GrTest::TestPathConvex(random);
339
340 return AAFlatteningConvexPathBatch::Create(geometry);
341}
342
343#endif