blob: 420c9e7c5a409f2de6f9a82962080d052afa864f [file] [log] [blame]
robertphillips@google.comf6747b02012-06-12 00:32:28 +00001/*
2 * Copyright 2012 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 "GrAARectRenderer.h"
joshualitt4d8da812015-01-28 12:53:54 -08009#include "GrBatch.h"
10#include "GrBatchTarget.h"
11#include "GrBufferAllocPool.h"
joshualitt5478d422014-11-14 16:00:38 -080012#include "GrDefaultGeoProcFactory.h"
13#include "GrGeometryProcessor.h"
robertphillips@google.comf6747b02012-06-12 00:32:28 +000014#include "GrGpu.h"
egdaniel605dd0f2014-11-12 08:35:25 -080015#include "GrInvariantOutput.h"
robertphillips@google.com908aed82013-05-28 13:16:20 +000016#include "SkColorPriv.h"
joshualitt5478d422014-11-14 16:00:38 -080017#include "gl/GrGLProcessor.h"
18#include "gl/GrGLGeometryProcessor.h"
19#include "gl/builders/GrGLProgramBuilder.h"
robertphillips@google.comf6747b02012-06-12 00:32:28 +000020
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +000021///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comdf3695e2013-04-09 14:01:44 +000022
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000023static void set_inset_fan(SkPoint* pts, size_t stride,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +000024 const SkRect& r, SkScalar dx, SkScalar dy) {
robertphillips@google.comf6747b02012-06-12 00:32:28 +000025 pts->setRectFan(r.fLeft + dx, r.fTop + dy,
26 r.fRight - dx, r.fBottom - dy, stride);
27}
28
robertphillips@google.com6d067302012-12-18 21:47:47 +000029static const uint16_t gFillAARectIdx[] = {
robertphillips@google.comf6747b02012-06-12 00:32:28 +000030 0, 1, 5, 5, 4, 0,
31 1, 2, 6, 6, 5, 1,
32 2, 3, 7, 7, 6, 2,
33 3, 0, 4, 4, 7, 3,
34 4, 5, 6, 6, 7, 4,
35};
36
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000037static const int kIndicesPerAAFillRect = SK_ARRAY_COUNT(gFillAARectIdx);
robertphillips@google.com6d067302012-12-18 21:47:47 +000038static const int kVertsPerAAFillRect = 8;
39static const int kNumAAFillRectsInIndexBuffer = 256;
robertphillips@google.comf6747b02012-06-12 00:32:28 +000040
joshualitt4d8da812015-01-28 12:53:54 -080041static const GrGeometryProcessor* create_fill_rect_gp(bool tweakAlphaForCoverage,
42 const SkMatrix& localMatrix) {
43 uint32_t flags = GrDefaultGeoProcFactory::kColor_GPType;
44 const GrGeometryProcessor* gp;
45 if (tweakAlphaForCoverage) {
46 gp = GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, SkMatrix::I(), localMatrix,
47 false, 0xff);
48 } else {
49 flags |= GrDefaultGeoProcFactory::kCoverage_GPType;
50 gp = GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, SkMatrix::I(), localMatrix,
51 false, 0xff);
52 }
53 return gp;
54}
55
56class AAFillRectBatch : public GrBatch {
57public:
58 struct Geometry {
59 GrColor fColor;
60 SkMatrix fViewMatrix;
61 SkRect fRect;
62 SkRect fDevRect;
63 };
64
65 static GrBatch* Create(const Geometry& geometry, const GrIndexBuffer* indexBuffer) {
66 return SkNEW_ARGS(AAFillRectBatch, (geometry, indexBuffer));
67 }
68
69 const char* name() const SK_OVERRIDE { return "AAFillRectBatch"; }
70
71 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
72 // When this is called on a batch, there is only one geometry bundle
73 if (!this->canTweakAlphaForCoverage() && GrColorIsOpaque(fGeoData[0].fColor)) {
74 out->setUnknownOpaqueFourComponents();
75 } else {
76 out->setUnknownFourComponents();
77 }
78 }
79
80 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
81 if (this->canTweakAlphaForCoverage()) {
82 // uniform coverage
83 out->setKnownSingleComponent(0xff);
84 } else {
85 out->setUnknownSingleComponent();
86 }
87 }
88
89 void initBatchOpt(const GrBatchOpt& batchOpt) {
90 fBatchOpt = batchOpt;
91 }
92
93 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
94 // Handle any color overrides
95 if (init.fColorIgnored) {
96 fGeoData[0].fColor = GrColor_ILLEGAL;
97 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
98 fGeoData[0].fColor = init.fOverrideColor;
99 }
100
101 // setup batch properties
102 fBatch.fColorIgnored = init.fColorIgnored;
103 fBatch.fColor = fGeoData[0].fColor;
104 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
105 fBatch.fCoverageIgnored = init.fCoverageIgnored;
106 }
107
108 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
109 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
110
111 SkMatrix localMatrix;
joshualitt84b13df2015-02-12 15:03:49 -0800112 if (!this->viewMatrix().invert(&localMatrix)) {
joshualitt4d8da812015-01-28 12:53:54 -0800113 SkDebugf("Cannot invert\n");
114 return;
115 }
116
joshualitt84b13df2015-02-12 15:03:49 -0800117 const GrGeometryProcessor* gp = create_fill_rect_gp(canTweakAlphaForCoverage,
118 localMatrix);
joshualitt4d8da812015-01-28 12:53:54 -0800119
120 batchTarget->initDraw(gp, pipeline);
joshualitt84b13df2015-02-12 15:03:49 -0800121 gp->unref();
joshualitt4d8da812015-01-28 12:53:54 -0800122
123 // TODO this is hacky, but the only way we have to initialize the GP is to use the
124 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
125 // everywhere we can remove this nastiness
126 GrPipelineInfo init;
127 init.fColorIgnored = fBatch.fColorIgnored;
128 init.fOverrideColor = GrColor_ILLEGAL;
129 init.fCoverageIgnored = fBatch.fCoverageIgnored;
130 init.fUsesLocalCoords = this->usesLocalCoords();
131 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
132
133 size_t vertexStride = gp->getVertexStride();
134
135 SkASSERT(canTweakAlphaForCoverage ?
136 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
137 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
138
139 int instanceCount = fGeoData.count();
140 int vertexCount = kVertsPerAAFillRect * instanceCount;
141
142 const GrVertexBuffer* vertexBuffer;
143 int firstVertex;
144
joshualitt84b13df2015-02-12 15:03:49 -0800145 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
joshualitt4d8da812015-01-28 12:53:54 -0800146 vertexCount,
147 &vertexBuffer,
148 &firstVertex);
149
150 for (int i = 0; i < instanceCount; i++) {
151 const Geometry& args = fGeoData[i];
152 this->generateAAFillRectGeometry(vertices,
joshualitt84b13df2015-02-12 15:03:49 -0800153 i * kVertsPerAAFillRect * vertexStride,
154 vertexStride,
155 args.fColor,
156 args.fViewMatrix,
157 args.fRect,
158 args.fDevRect,
159 canTweakAlphaForCoverage);
joshualitt4d8da812015-01-28 12:53:54 -0800160 }
161
162 GrDrawTarget::DrawInfo drawInfo;
163 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
164 drawInfo.setStartVertex(0);
165 drawInfo.setStartIndex(0);
166 drawInfo.setVerticesPerInstance(kVertsPerAAFillRect);
167 drawInfo.setIndicesPerInstance(kIndicesPerAAFillRect);
168 drawInfo.adjustStartVertex(firstVertex);
169 drawInfo.setVertexBuffer(vertexBuffer);
170 drawInfo.setIndexBuffer(fIndexBuffer);
171
172 int maxInstancesPerDraw = kNumAAFillRectsInIndexBuffer;
173
174 while (instanceCount) {
175 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
176 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
177 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
178
179 batchTarget->draw(drawInfo);
180
181 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
182 instanceCount -= drawInfo.instanceCount();
183 }
184 }
185
186 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
187
188private:
189 AAFillRectBatch(const Geometry& geometry, const GrIndexBuffer* indexBuffer)
190 : fIndexBuffer(indexBuffer) {
191 this->initClassID<AAFillRectBatch>();
192 fGeoData.push_back(geometry);
193 }
194
195 GrColor color() const { return fBatch.fColor; }
196 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
197 bool canTweakAlphaForCoverage() const { return fBatchOpt.fCanTweakAlphaForCoverage; }
198 bool colorIgnored() const { return fBatch.fColorIgnored; }
199 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
200
201 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
202 AAFillRectBatch* that = t->cast<AAFillRectBatch>();
joshualitt84b13df2015-02-12 15:03:49 -0800203 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
204 return false;
205 }
joshualitt4d8da812015-01-28 12:53:54 -0800206
joshualitt84b13df2015-02-12 15:03:49 -0800207 if (this->colorIgnored() != that->colorIgnored()) {
208 return false;
209 }
210
211 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
joshualitt4d8da812015-01-28 12:53:54 -0800212 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
213 // local coords then we won't be able to batch. We could actually upload the viewmatrix
214 // using vertex attributes in these cases, but haven't investigated that
215 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
216 return false;
217 }
218
219 if (this->color() != that->color()) {
220 fBatch.fColor = GrColor_ILLEGAL;
221 }
222 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
223 return true;
224 }
225
226 void generateAAFillRectGeometry(void* vertices,
bsalomon81aca542015-01-29 07:13:20 -0800227 size_t offset,
228 size_t vertexStride,
joshualitt4d8da812015-01-28 12:53:54 -0800229 GrColor color,
230 const SkMatrix& viewMatrix,
231 const SkRect& rect,
232 const SkRect& devRect,
233 bool tweakAlphaForCoverage) const {
234 intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
235
236 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
237 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
238
239 SkScalar inset = SkMinScalar(devRect.width(), SK_Scalar1);
240 inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height());
241
242 if (viewMatrix.rectStaysRect()) {
243 set_inset_fan(fan0Pos, vertexStride, devRect, -SK_ScalarHalf, -SK_ScalarHalf);
244 set_inset_fan(fan1Pos, vertexStride, devRect, inset, inset);
245 } else {
246 // compute transformed (1, 0) and (0, 1) vectors
247 SkVector vec[2] = {
248 { viewMatrix[SkMatrix::kMScaleX], viewMatrix[SkMatrix::kMSkewY] },
249 { viewMatrix[SkMatrix::kMSkewX], viewMatrix[SkMatrix::kMScaleY] }
250 };
251
252 vec[0].normalize();
253 vec[0].scale(SK_ScalarHalf);
254 vec[1].normalize();
255 vec[1].scale(SK_ScalarHalf);
256
257 // create the rotated rect
258 fan0Pos->setRectFan(rect.fLeft, rect.fTop,
259 rect.fRight, rect.fBottom, vertexStride);
260 viewMatrix.mapPointsWithStride(fan0Pos, vertexStride, 4);
261
262 // Now create the inset points and then outset the original
263 // rotated points
264
265 // TL
266 *((SkPoint*)((intptr_t)fan1Pos + 0 * vertexStride)) =
267 *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) + vec[0] + vec[1];
268 *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) -= vec[0] + vec[1];
269 // BL
270 *((SkPoint*)((intptr_t)fan1Pos + 1 * vertexStride)) =
271 *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) + vec[0] - vec[1];
272 *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) -= vec[0] - vec[1];
273 // BR
274 *((SkPoint*)((intptr_t)fan1Pos + 2 * vertexStride)) =
275 *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) - vec[0] - vec[1];
276 *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) += vec[0] + vec[1];
277 // TR
278 *((SkPoint*)((intptr_t)fan1Pos + 3 * vertexStride)) =
279 *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) - vec[0] + vec[1];
280 *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) += vec[0] - vec[1];
281 }
282
283 // Make verts point to vertex color and then set all the color and coverage vertex attrs
284 // values.
285 verts += sizeof(SkPoint);
286 for (int i = 0; i < 4; ++i) {
287 if (tweakAlphaForCoverage) {
288 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
289 } else {
290 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
291 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
292 }
293 }
294
295 int scale;
296 if (inset < SK_ScalarHalf) {
297 scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
298 SkASSERT(scale >= 0 && scale <= 255);
299 } else {
300 scale = 0xff;
301 }
302
303 verts += 4 * vertexStride;
304
305 float innerCoverage = GrNormalizeByteToFloat(scale);
306 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
307
308 for (int i = 0; i < 4; ++i) {
309 if (tweakAlphaForCoverage) {
310 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
311 } else {
312 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
313 *reinterpret_cast<float*>(verts + i * vertexStride +
314 sizeof(GrColor)) = innerCoverage;
315 }
316 }
317 }
318
319 struct BatchTracker {
320 GrColor fColor;
321 bool fUsesLocalCoords;
322 bool fColorIgnored;
323 bool fCoverageIgnored;
324 };
325
326 GrBatchOpt fBatchOpt;
327 BatchTracker fBatch;
328 const GrIndexBuffer* fIndexBuffer;
329 SkSTArray<1, Geometry, true> fGeoData;
330};
331
332namespace {
333// Should the coverage be multiplied into the color attrib or use a separate attrib.
334enum CoverageAttribType {
335 kUseColor_CoverageAttribType,
336 kUseCoverage_CoverageAttribType,
337};
338}
339
340void GrAARectRenderer::reset() {
341 SkSafeSetNull(fAAFillRectIndexBuffer);
342 SkSafeSetNull(fAAMiterStrokeRectIndexBuffer);
343 SkSafeSetNull(fAABevelStrokeRectIndexBuffer);
344}
345
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000346static const uint16_t gMiterStrokeAARectIdx[] = {
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000347 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
348 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
349 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
350 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
351
352 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
353 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
354 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
355 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
356
357 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
358 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
359 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
360 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
361};
362
joshualitt5ead6da2014-10-22 16:00:29 -0700363static const int kIndicesPerMiterStrokeRect = SK_ARRAY_COUNT(gMiterStrokeAARectIdx);
364static const int kVertsPerMiterStrokeRect = 16;
365static const int kNumMiterStrokeRectsInIndexBuffer = 256;
366
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000367/**
368 * As in miter-stroke, index = a + b, and a is the current index, b is the shift
369 * from the first index. The index layout:
370 * outer AA line: 0~3, 4~7
371 * outer edge: 8~11, 12~15
372 * inner edge: 16~19
373 * inner AA line: 20~23
374 * Following comes a bevel-stroke rect and its indices:
375 *
376 * 4 7
skia.committer@gmail.com26144182013-11-07 07:02:19 +0000377 * *********************************
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000378 * * ______________________________ *
379 * * / 12 15 \ *
380 * * / \ *
381 * 0 * |8 16_____________________19 11 | * 3
382 * * | | | | *
383 * * | | **************** | | *
384 * * | | * 20 23 * | | *
385 * * | | * * | | *
386 * * | | * 21 22 * | | *
387 * * | | **************** | | *
388 * * | |____________________| | *
389 * 1 * |9 17 18 10| * 2
skia.committer@gmail.com26144182013-11-07 07:02:19 +0000390 * * \ / *
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000391 * * \13 __________________________14/ *
392 * * *
skia.committer@gmail.com26144182013-11-07 07:02:19 +0000393 * **********************************
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000394 * 5 6
395 */
396static const uint16_t gBevelStrokeAARectIdx[] = {
397 // Draw outer AA, from outer AA line to outer edge, shift is 0.
398 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
399 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
400 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
401 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
402 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
403 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
404 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
405 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
406
407 // Draw the stroke, from outer edge to inner edge, shift is 8.
408 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
409 1 + 8, 5 + 8, 9 + 8,
410 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
411 6 + 8, 2 + 8, 10 + 8,
412 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
413 3 + 8, 7 + 8, 11 + 8,
414 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
415 4 + 8, 0 + 8, 8 + 8,
416
417 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
418 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
419 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
420 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
421 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
422};
423
joshualitt5ead6da2014-10-22 16:00:29 -0700424static const int kIndicesPerBevelStrokeRect = SK_ARRAY_COUNT(gBevelStrokeAARectIdx);
425static const int kVertsPerBevelStrokeRect = 24;
426static const int kNumBevelStrokeRectsInIndexBuffer = 256;
427
joshualittb44293e2014-10-28 08:12:18 -0700428static int aa_stroke_rect_index_count(bool miterStroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000429 return miterStroke ? SK_ARRAY_COUNT(gMiterStrokeAARectIdx) :
430 SK_ARRAY_COUNT(gBevelStrokeAARectIdx);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000431}
432
joshualittb44293e2014-10-28 08:12:18 -0700433GrIndexBuffer* GrAARectRenderer::aaStrokeRectIndexBuffer(bool miterStroke) {
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000434 if (miterStroke) {
435 if (NULL == fAAMiterStrokeRectIndexBuffer) {
436 fAAMiterStrokeRectIndexBuffer =
joshualittb44293e2014-10-28 08:12:18 -0700437 fGpu->createInstancedIndexBuffer(gMiterStrokeAARectIdx,
438 kIndicesPerMiterStrokeRect,
439 kNumMiterStrokeRectsInIndexBuffer,
440 kVertsPerMiterStrokeRect);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000441 }
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000442 return fAAMiterStrokeRectIndexBuffer;
443 } else {
444 if (NULL == fAABevelStrokeRectIndexBuffer) {
445 fAABevelStrokeRectIndexBuffer =
joshualittb44293e2014-10-28 08:12:18 -0700446 fGpu->createInstancedIndexBuffer(gBevelStrokeAARectIdx,
447 kIndicesPerBevelStrokeRect,
448 kNumBevelStrokeRectsInIndexBuffer,
449 kVertsPerBevelStrokeRect);
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000450 }
451 return fAABevelStrokeRectIndexBuffer;
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000452 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000453}
454
joshualittb44293e2014-10-28 08:12:18 -0700455void GrAARectRenderer::geometryFillAARect(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800456 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800457 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800458 const SkMatrix& viewMatrix,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000459 const SkRect& rect,
bsalomon9c0822a2014-08-11 11:07:48 -0700460 const SkRect& devRect) {
joshualitt5ead6da2014-10-22 16:00:29 -0700461 if (NULL == fAAFillRectIndexBuffer) {
joshualittb44293e2014-10-28 08:12:18 -0700462 fAAFillRectIndexBuffer = fGpu->createInstancedIndexBuffer(gFillAARectIdx,
463 kIndicesPerAAFillRect,
464 kNumAAFillRectsInIndexBuffer,
465 kVertsPerAAFillRect);
joshualitt5ead6da2014-10-22 16:00:29 -0700466 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000467
joshualitt4d8da812015-01-28 12:53:54 -0800468 AAFillRectBatch::Geometry geometry;
469 geometry.fRect = rect;
470 geometry.fViewMatrix = viewMatrix;
471 geometry.fDevRect = devRect;
472 geometry.fColor = color;
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000473
joshualitt4d8da812015-01-28 12:53:54 -0800474 SkAutoTUnref<GrBatch> batch(AAFillRectBatch::Create(geometry, fAAFillRectIndexBuffer));
475 target->drawBatch(pipelineBuilder, batch, &devRect);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000476}
477
joshualittb44293e2014-10-28 08:12:18 -0700478void GrAARectRenderer::strokeAARect(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800479 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800480 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800481 const SkMatrix& viewMatrix,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000482 const SkRect& rect,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000483 const SkRect& devRect,
bsalomon9c0822a2014-08-11 11:07:48 -0700484 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000485 SkVector devStrokeSize;
egdanield58a0ba2014-06-11 10:30:05 -0700486 SkScalar width = stroke.getWidth();
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000487 if (width > 0) {
488 devStrokeSize.set(width, width);
joshualitt8059eb92014-12-29 15:10:07 -0800489 viewMatrix.mapVectors(&devStrokeSize, 1);
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000490 devStrokeSize.setAbs(devStrokeSize);
491 } else {
492 devStrokeSize.set(SK_Scalar1, SK_Scalar1);
493 }
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000494
robertphillips@google.com18136d12013-05-10 11:05:58 +0000495 const SkScalar dx = devStrokeSize.fX;
496 const SkScalar dy = devStrokeSize.fY;
bsalomon@google.com81712882012-11-01 17:12:34 +0000497 const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf);
498 const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000499
robertphillips@google.comafd1cba2013-05-14 19:47:47 +0000500 // Temporarily #if'ed out. We don't want to pass in the devRect but
501 // right now it is computed in GrContext::apply_aa_to_rect and we don't
502 // want to throw away the work
503#if 0
robertphillips@google.com18136d12013-05-10 11:05:58 +0000504 SkRect devRect;
505 combinedMatrix.mapRect(&devRect, rect);
robertphillips@google.comafd1cba2013-05-14 19:47:47 +0000506#endif
robertphillips@google.com18136d12013-05-10 11:05:58 +0000507
bsalomon@google.com81712882012-11-01 17:12:34 +0000508 SkScalar spare;
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000509 {
bsalomon@google.com81712882012-11-01 17:12:34 +0000510 SkScalar w = devRect.width() - dx;
511 SkScalar h = devRect.height() - dy;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000512 spare = SkTMin(w, h);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000513 }
514
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000515 SkRect devOutside(devRect);
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000516 devOutside.outset(rx, ry);
517
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000518 bool miterStroke = true;
yunchao.he2bff2302014-07-28 19:18:49 -0700519 // For hairlines, make bevel and round joins appear the same as mitered ones.
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000520 // small miter limit means right angles show bevel...
yunchao.he2bff2302014-07-28 19:18:49 -0700521 if ((width > 0) && (stroke.getJoin() != SkPaint::kMiter_Join ||
522 stroke.getMiter() < SK_ScalarSqrt2)) {
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000523 miterStroke = false;
524 }
525
526 if (spare <= 0 && miterStroke) {
egdaniel8dd688b2015-01-22 10:16:09 -0800527 this->fillAARect(target, pipelineBuilder, color, viewMatrix, devOutside,
joshualittd27f73e2014-12-29 07:43:36 -0800528 devOutside);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000529 return;
530 }
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000531
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000532 SkRect devInside(devRect);
533 devInside.inset(rx, ry);
534
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000535 SkRect devOutsideAssist(devRect);
536
537 // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
538 // to draw the outer of the rect. Because there are 8 vertices on the outer
skia.committer@gmail.com26144182013-11-07 07:02:19 +0000539 // edge, while vertex number of inner edge is 4, the same as miter-stroke.
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000540 if (!miterStroke) {
541 devOutside.inset(0, ry);
542 devOutsideAssist.outset(0, ry);
543 }
544
joshualitt4d8da812015-01-28 12:53:54 -0800545 this->geometryStrokeAARect(target, pipelineBuilder, color, viewMatrix, devOutside,
546 devOutsideAssist, devInside, miterStroke);
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000547}
548
joshualitt84b13df2015-02-12 15:03:49 -0800549static const GrGeometryProcessor* create_rect_gp(const GrPipelineBuilder& pipelneBuilder,
550 GrColor color,
551 CoverageAttribType* type,
552 const SkMatrix& localMatrix) {
553 uint32_t flags = GrDefaultGeoProcFactory::kColor_GPType;
554 const GrGeometryProcessor* gp;
555 if (pipelneBuilder.canTweakAlphaForCoverage()) {
556 gp = GrDefaultGeoProcFactory::Create(flags, color, SkMatrix::I(), localMatrix);
557 SkASSERT(gp->getVertexStride() == sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
558 *type = kUseColor_CoverageAttribType;
559 } else {
560 flags |= GrDefaultGeoProcFactory::kCoverage_GPType;
561 gp = GrDefaultGeoProcFactory::Create(flags, color, SkMatrix::I(), localMatrix,
562 GrColorIsOpaque(color));
563 SkASSERT(gp->getVertexStride()==sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
564 *type = kUseCoverage_CoverageAttribType;
joshualitt4d8da812015-01-28 12:53:54 -0800565 }
joshualitt84b13df2015-02-12 15:03:49 -0800566 return gp;
567}
joshualitt4d8da812015-01-28 12:53:54 -0800568
569
joshualittb44293e2014-10-28 08:12:18 -0700570void GrAARectRenderer::geometryStrokeAARect(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800571 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800572 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800573 const SkMatrix& viewMatrix,
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000574 const SkRect& devOutside,
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000575 const SkRect& devOutsideAssist,
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000576 const SkRect& devInside,
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000577 bool miterStroke) {
joshualitt84b13df2015-02-12 15:03:49 -0800578 SkMatrix localMatrix;
579 if (!viewMatrix.invert(&localMatrix)) {
580 SkDebugf("Cannot invert\n");
581 return;
582 }
583
584 CoverageAttribType type;
585 SkAutoTUnref<const GrGeometryProcessor> gp(create_rect_gp(*pipelineBuilder, color, &type,
586 localMatrix));
587
588 int innerVertexNum = 4;
589 int outerVertexNum = miterStroke ? 4 : 8;
590 int totalVertexNum = (outerVertexNum + innerVertexNum) * 2;
591
592 size_t vstride = gp->getVertexStride();
593 GrDrawTarget::AutoReleaseGeometry geo(target, totalVertexNum, vstride, 0);
594 if (!geo.succeeded()) {
595 SkDebugf("Failed to get space for vertices!\n");
596 return;
597 }
joshualittb44293e2014-10-28 08:12:18 -0700598 GrIndexBuffer* indexBuffer = this->aaStrokeRectIndexBuffer(miterStroke);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000599 if (NULL == indexBuffer) {
tfarina38406c82014-10-31 07:11:12 -0700600 SkDebugf("Failed to create index buffer!\n");
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000601 return;
602 }
603
joshualitt84b13df2015-02-12 15:03:49 -0800604 intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000605
joshualitt84b13df2015-02-12 15:03:49 -0800606 // We create vertices for four nested rectangles. There are two ramps from 0 to full
607 // coverage, one on the exterior of the stroke and the other on the interior.
608 // The following pointers refer to the four rects, from outermost to innermost.
609 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
610 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vstride);
611 SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vstride);
612 SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(verts + (2 * outerVertexNum + innerVertexNum) * vstride);
613
614#ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
615 // TODO: this only really works if the X & Y margins are the same all around
616 // the rect (or if they are all >= 1.0).
617 SkScalar inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
618 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
619 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
620 if (miterStroke) {
621 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
622 } else {
623 inset = SK_ScalarHalf * SkMinScalar(inset, devOutsideAssist.fBottom - devInside.fBottom);
624 }
625 SkASSERT(inset >= 0);
626#else
627 SkScalar inset = SK_ScalarHalf;
628#endif
629
630 if (miterStroke) {
631 // outermost
632 set_inset_fan(fan0Pos, vstride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
633 // inner two
634 set_inset_fan(fan1Pos, vstride, devOutside, inset, inset);
635 set_inset_fan(fan2Pos, vstride, devInside, -inset, -inset);
636 // innermost
637 set_inset_fan(fan3Pos, vstride, devInside, SK_ScalarHalf, SK_ScalarHalf);
638 } else {
639 SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vstride);
640 SkPoint* fan1AssistPos = reinterpret_cast<SkPoint*>(verts + (outerVertexNum + 4) * vstride);
641 // outermost
642 set_inset_fan(fan0Pos, vstride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
643 set_inset_fan(fan0AssistPos, vstride, devOutsideAssist, -SK_ScalarHalf, -SK_ScalarHalf);
644 // outer one of the inner two
645 set_inset_fan(fan1Pos, vstride, devOutside, inset, inset);
646 set_inset_fan(fan1AssistPos, vstride, devOutsideAssist, inset, inset);
647 // inner one of the inner two
648 set_inset_fan(fan2Pos, vstride, devInside, -inset, -inset);
649 // innermost
650 set_inset_fan(fan3Pos, vstride, devInside, SK_ScalarHalf, SK_ScalarHalf);
651 }
652
653 // Make verts point to vertex color and then set all the color and coverage vertex attrs values.
654 // The outermost rect has 0 coverage
655 verts += sizeof(SkPoint);
656 for (int i = 0; i < outerVertexNum; ++i) {
657 if (kUseCoverage_CoverageAttribType == type) {
658 *reinterpret_cast<GrColor*>(verts + i * vstride) = color;
659 *reinterpret_cast<float*>(verts + i * vstride + sizeof(GrColor)) = 0;
660 } else {
661 *reinterpret_cast<GrColor*>(verts + i * vstride) = 0;
662 }
663 }
664
665 // scale is the coverage for the the inner two rects.
666 int scale;
667 if (inset < SK_ScalarHalf) {
668 scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
669 SkASSERT(scale >= 0 && scale <= 255);
670 } else {
671 scale = 0xff;
672 }
673
674 float innerCoverage = GrNormalizeByteToFloat(scale);
675 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
676
677 verts += outerVertexNum * vstride;
678 for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
679 if (kUseCoverage_CoverageAttribType == type) {
680 *reinterpret_cast<GrColor*>(verts + i * vstride) = color;
681 *reinterpret_cast<float*>(verts + i * vstride + sizeof(GrColor)) = innerCoverage;
682 } else {
683 *reinterpret_cast<GrColor*>(verts + i * vstride) = scaledColor;
684 }
685 }
686
687 // The innermost rect has 0 coverage
688 verts += (outerVertexNum + innerVertexNum) * vstride;
689 for (int i = 0; i < innerVertexNum; ++i) {
690 if (kUseCoverage_CoverageAttribType == type) {
691 *reinterpret_cast<GrColor*>(verts + i * vstride) = color;
692 *reinterpret_cast<GrColor*>(verts + i * vstride + sizeof(GrColor)) = 0;
693 } else {
694 *reinterpret_cast<GrColor*>(verts + i * vstride) = 0;
695 }
696 }
697
698 target->setIndexSourceToBuffer(indexBuffer);
699 target->drawIndexedInstances(pipelineBuilder,
700 gp,
701 kTriangles_GrPrimitiveType,
702 1,
703 totalVertexNum,
704 aa_stroke_rect_index_count(miterStroke));
705 target->resetIndexSource();
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000706}
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000707
joshualittb44293e2014-10-28 08:12:18 -0700708void GrAARectRenderer::fillAANestedRects(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800709 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800710 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800711 const SkMatrix& viewMatrix,
712 const SkRect rects[2]) {
713 SkASSERT(viewMatrix.rectStaysRect());
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000714 SkASSERT(!rects[1].isEmpty());
715
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000716 SkRect devOutside, devOutsideAssist, devInside;
joshualitt8059eb92014-12-29 15:10:07 -0800717 viewMatrix.mapRect(&devOutside, rects[0]);
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000718 // can't call mapRect for devInside since it calls sort
joshualitt8059eb92014-12-29 15:10:07 -0800719 viewMatrix.mapPoints((SkPoint*)&devInside, (const SkPoint*)&rects[1], 2);
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000720
721 if (devInside.isEmpty()) {
egdaniel8dd688b2015-01-22 10:16:09 -0800722 this->fillAARect(target, pipelineBuilder, color, viewMatrix, devOutside,
joshualittd27f73e2014-12-29 07:43:36 -0800723 devOutside);
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000724 return;
725 }
726
egdaniel8dd688b2015-01-22 10:16:09 -0800727 this->geometryStrokeAARect(target, pipelineBuilder, color, viewMatrix, devOutside,
728 devOutsideAssist, devInside, true);
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000729}