blob: 69f9d1cca10f41a55462c2f12f0b26932db9f6d8 [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
bsalomon0aff2fa2015-07-31 06:48:27 -070042bool GrAALinearizingConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
43 if (!args.fAntiAlias) {
fmalitabd5d7e72015-06-26 07:18:24 -070044 return false;
45 }
bsalomon0aff2fa2015-07-31 06:48:27 -070046 if (args.fPath->isInverseFillType()) {
fmalitabd5d7e72015-06-26 07:18:24 -070047 return false;
48 }
bsalomon0aff2fa2015-07-31 06:48:27 -070049 if (!args.fPath->isConvex()) {
fmalitabd5d7e72015-06-26 07:18:24 -070050 return false;
51 }
bsalomon0aff2fa2015-07-31 06:48:27 -070052 if (args.fStroke->getStyle() == SkStrokeRec::kStroke_Style) {
53 return args.fViewMatrix->isSimilarity() && args.fStroke->getWidth() >= 1.0f &&
54 args.fStroke->getWidth() <= kMaxStrokeWidth && !args.fStroke->isDashed() &&
55 SkPathPriv::LastVerbIsClose(*args.fPath) &&
56 args.fStroke->getJoin() != SkPaint::Join::kRound_Join;
fmalitabd5d7e72015-06-26 07:18:24 -070057 }
bsalomon0aff2fa2015-07-31 06:48:27 -070058 return args.fStroke->getStyle() == SkStrokeRec::kFill_Style;
ethannicholas1a1b3ac2015-06-10 12:11:17 -070059}
60
61// extract the result vertices and indices from the GrAAConvexTessellator
62static void extract_verts(const GrAAConvexTessellator& tess,
63 void* vertices,
64 size_t vertexStride,
65 GrColor color,
66 uint16_t firstIndex,
67 uint16_t* idxs,
68 bool tweakAlphaForCoverage) {
69 intptr_t verts = reinterpret_cast<intptr_t>(vertices);
70
71 for (int i = 0; i < tess.numPts(); ++i) {
72 *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i);
73 }
74
75 // Make 'verts' point to the colors
76 verts += sizeof(SkPoint);
77 for (int i = 0; i < tess.numPts(); ++i) {
ethannicholas1a1b3ac2015-06-10 12:11:17 -070078 if (tweakAlphaForCoverage) {
fmalitabd5d7e72015-06-26 07:18:24 -070079 SkASSERT(SkScalarRoundToInt(255.0f * tess.coverage(i)) <= 255);
80 unsigned scale = SkScalarRoundToInt(255.0f * tess.coverage(i));
ethannicholas1a1b3ac2015-06-10 12:11:17 -070081 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
82 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
83 } else {
84 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
85 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
fmalitabd5d7e72015-06-26 07:18:24 -070086 tess.coverage(i);
ethannicholas1a1b3ac2015-06-10 12:11:17 -070087 }
88 }
89
90 for (int i = 0; i < tess.numIndices(); ++i) {
91 idxs[i] = tess.index(i) + firstIndex;
92 }
93}
94
95static const GrGeometryProcessor* create_fill_gp(bool tweakAlphaForCoverage,
96 const SkMatrix& localMatrix,
97 bool usesLocalCoords,
98 bool coverageIgnored) {
99 uint32_t flags = GrDefaultGeoProcFactory::kColor_GPType;
100 if (!tweakAlphaForCoverage) {
101 flags |= GrDefaultGeoProcFactory::kCoverage_GPType;
102 }
103
104 return GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, usesLocalCoords, coverageIgnored,
105 SkMatrix::I(), localMatrix);
106}
107
108class AAFlatteningConvexPathBatch : public GrBatch {
109public:
110 struct Geometry {
111 GrColor fColor;
112 SkMatrix fViewMatrix;
113 SkPath fPath;
fmalitabd5d7e72015-06-26 07:18:24 -0700114 SkScalar fStrokeWidth;
115 SkPaint::Join fJoin;
116 SkScalar fMiterLimit;
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700117 };
118
119 static GrBatch* Create(const Geometry& geometry) {
120 return SkNEW_ARGS(AAFlatteningConvexPathBatch, (geometry));
121 }
122
123 const char* name() const override { return "AAConvexBatch"; }
124
125 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
126 // When this is called on a batch, there is only one geometry bundle
127 out->setKnownFourComponents(fGeoData[0].fColor);
128 }
129 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
130 out->setUnknownSingleComponent();
131 }
132
133 void initBatchTracker(const GrPipelineInfo& init) override {
134 // Handle any color overrides
bsalomon7765a472015-07-08 11:26:37 -0700135 if (!init.readsColor()) {
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700136 fGeoData[0].fColor = GrColor_ILLEGAL;
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700137 }
bsalomon7765a472015-07-08 11:26:37 -0700138 init.getOverrideColorIfSet(&fGeoData[0].fColor);
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700139
140 // setup batch properties
bsalomon7765a472015-07-08 11:26:37 -0700141 fBatch.fColorIgnored = !init.readsColor();
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700142 fBatch.fColor = fGeoData[0].fColor;
bsalomon7765a472015-07-08 11:26:37 -0700143 fBatch.fUsesLocalCoords = init.readsLocalCoords();
144 fBatch.fCoverageIgnored = !init.readsCoverage();
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700145 fBatch.fLinesOnly = SkPath::kLine_SegmentMask == fGeoData[0].fPath.getSegmentMasks();
bsalomon7765a472015-07-08 11:26:37 -0700146 fBatch.fCanTweakAlphaForCoverage = init.canTweakAlphaForCoverage();
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700147 }
148
149 void draw(GrBatchTarget* batchTarget, const GrPipeline* pipeline, int vertexCount,
150 size_t vertexStride, void* vertices, int indexCount, uint16_t* indices) {
151 if (vertexCount == 0 || indexCount == 0) {
152 return;
153 }
154 const GrVertexBuffer* vertexBuffer;
155 GrVertices info;
156 int firstVertex;
157 void* verts = batchTarget->makeVertSpace(vertexStride, vertexCount, &vertexBuffer,
158 &firstVertex);
159 if (!verts) {
160 SkDebugf("Could not allocate vertices\n");
161 return;
162 }
163 memcpy(verts, vertices, vertexCount * vertexStride);
164
165 const GrIndexBuffer* indexBuffer;
166 int firstIndex;
167 uint16_t* idxs = batchTarget->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
168 if (!idxs) {
169 SkDebugf("Could not allocate indices\n");
170 return;
171 }
172 memcpy(idxs, indices, indexCount * sizeof(uint16_t));
173 info.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
174 firstIndex, vertexCount, indexCount);
175 batchTarget->draw(info);
176 }
fmalitabd5d7e72015-06-26 07:18:24 -0700177
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700178 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
179 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
180
181 SkMatrix invert;
182 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
183 SkDebugf("Could not invert viewmatrix\n");
184 return;
185 }
186
187 // Setup GrGeometryProcessor
188 SkAutoTUnref<const GrGeometryProcessor> gp(
189 create_fill_gp(canTweakAlphaForCoverage, invert,
190 this->usesLocalCoords(),
191 this->coverageIgnored()));
192
193 batchTarget->initDraw(gp, pipeline);
194
195 size_t vertexStride = gp->getVertexStride();
196
197 SkASSERT(canTweakAlphaForCoverage ?
198 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
199 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
200
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700201 int instanceCount = fGeoData.count();
202
203 int vertexCount = 0;
204 int indexCount = 0;
205 int maxVertices = DEFAULT_BUFFER_SIZE;
206 int maxIndices = DEFAULT_BUFFER_SIZE;
207 uint8_t* vertices = (uint8_t*) malloc(maxVertices * vertexStride);
208 uint16_t* indices = (uint16_t*) malloc(maxIndices * sizeof(uint16_t));
209 for (int i = 0; i < instanceCount; i++) {
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700210 Geometry& args = fGeoData[i];
fmalitabd5d7e72015-06-26 07:18:24 -0700211 GrAAConvexTessellator tess(args.fStrokeWidth, args.fJoin, args.fMiterLimit);
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700212
213 if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
214 continue;
215 }
216
217 int currentIndices = tess.numIndices();
218 SkASSERT(currentIndices <= UINT16_MAX);
219 if (indexCount + currentIndices > UINT16_MAX) {
220 // if we added the current instance, we would overflow the indices we can store in a
221 // uint16_t. Draw what we've got so far and reset.
222 draw(batchTarget, pipeline, vertexCount, vertexStride, vertices, indexCount,
223 indices);
224 vertexCount = 0;
225 indexCount = 0;
226 }
227 int currentVertices = tess.numPts();
228 if (vertexCount + currentVertices > maxVertices) {
229 maxVertices = SkTMax(vertexCount + currentVertices, maxVertices * 2);
230 vertices = (uint8_t*) realloc(vertices, maxVertices * vertexStride);
231 }
232 if (indexCount + currentIndices > maxIndices) {
233 maxIndices = SkTMax(indexCount + currentIndices, maxIndices * 2);
234 indices = (uint16_t*) realloc(indices, maxIndices * sizeof(uint16_t));
235 }
236
237 extract_verts(tess, vertices + vertexStride * vertexCount, vertexStride, args.fColor,
238 vertexCount, indices + indexCount, canTweakAlphaForCoverage);
239 vertexCount += currentVertices;
240 indexCount += currentIndices;
241 }
242 draw(batchTarget, pipeline, vertexCount, vertexStride, vertices, indexCount, indices);
243 free(vertices);
244 free(indices);
245 }
246
247 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
248
249private:
250 AAFlatteningConvexPathBatch(const Geometry& geometry) {
251 this->initClassID<AAFlatteningConvexPathBatch>();
252 fGeoData.push_back(geometry);
253
254 // compute bounds
255 fBounds = geometry.fPath.getBounds();
256 geometry.fViewMatrix.mapRect(&fBounds);
257 }
258
259 bool onCombineIfPossible(GrBatch* t) override {
joshualitt8cab9a72015-07-16 09:13:50 -0700260 if (!this->pipeline()->isEqual(*t->pipeline())) {
261 return false;
262 }
263
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700264 AAFlatteningConvexPathBatch* that = t->cast<AAFlatteningConvexPathBatch>();
265
266 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
267 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
268 return false;
269 }
270
271 // In the event of two batches, one who can tweak, one who cannot, we just fall back to
272 // not tweaking
273 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
274 fBatch.fCanTweakAlphaForCoverage = false;
275 }
276
277 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
278 this->joinBounds(that->bounds());
279 return true;
280 }
281
282 GrColor color() const { return fBatch.fColor; }
283 bool linesOnly() const { return fBatch.fLinesOnly; }
284 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
285 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
286 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
287 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
288
289 struct BatchTracker {
290 GrColor fColor;
291 bool fUsesLocalCoords;
292 bool fColorIgnored;
293 bool fCoverageIgnored;
294 bool fLinesOnly;
295 bool fCanTweakAlphaForCoverage;
296 };
297
298 BatchTracker fBatch;
299 SkSTArray<1, Geometry, true> fGeoData;
300};
301
bsalomon0aff2fa2015-07-31 06:48:27 -0700302bool GrAALinearizingConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
303 if (args.fPath->isEmpty()) {
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700304 return true;
305 }
306 AAFlatteningConvexPathBatch::Geometry geometry;
bsalomon0aff2fa2015-07-31 06:48:27 -0700307 geometry.fColor = args.fColor;
308 geometry.fViewMatrix = *args.fViewMatrix;
309 geometry.fPath = *args.fPath;
310 geometry.fStrokeWidth = args.fStroke->isFillStyle() ? -1.0f : args.fStroke->getWidth();
311 geometry.fJoin = args.fStroke->isFillStyle() ? SkPaint::Join::kMiter_Join :
312 args.fStroke->getJoin();
313 geometry.fMiterLimit = args.fStroke->getMiter();
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700314
315 SkAutoTUnref<GrBatch> batch(AAFlatteningConvexPathBatch::Create(geometry));
bsalomon0aff2fa2015-07-31 06:48:27 -0700316 args.fTarget->drawBatch(*args.fPipelineBuilder, batch);
ethannicholas1a1b3ac2015-06-10 12:11:17 -0700317
318 return true;
319}
320
321///////////////////////////////////////////////////////////////////////////////////////////////////
322
323#ifdef GR_TEST_UTILS
324
325BATCH_TEST_DEFINE(AAFlatteningConvexPathBatch) {
326 AAFlatteningConvexPathBatch::Geometry geometry;
327 geometry.fColor = GrRandomColor(random);
328 geometry.fViewMatrix = GrTest::TestMatrixInvertible(random);
329 geometry.fPath = GrTest::TestPathConvex(random);
330
331 return AAFlatteningConvexPathBatch::Create(geometry);
332}
333
334#endif