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;
112 if (!this->viewMatrix().invert(&localMatrix)) {
113 SkDebugf("Cannot invert\n");
114 return;
115 }
116
117 const GrGeometryProcessor* gp = create_fill_rect_gp(canTweakAlphaForCoverage,
118 localMatrix);
119
120 batchTarget->initDraw(gp, pipeline);
121 gp->unref();
122
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
145 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
146 vertexCount,
147 &vertexBuffer,
148 &firstVertex);
149
150 for (int i = 0; i < instanceCount; i++) {
151 const Geometry& args = fGeoData[i];
152 this->generateAAFillRectGeometry(vertices,
153 i * kVertsPerAAFillRect * vertexStride,
154 vertexStride,
155 args.fColor,
156 args.fViewMatrix,
157 args.fRect,
158 args.fDevRect,
159 canTweakAlphaForCoverage);
160 }
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>();
203 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
204 return false;
205 }
206
207 if (this->colorIgnored() != that->colorIgnored()) {
208 return false;
209 }
210
joshualitt7bc18b72015-02-03 16:41:41 -0800211 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
joshualitt4d8da812015-01-28 12:53:54 -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;
565 }
566 return gp;
567}
568
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) {
joshualitt8059eb92014-12-29 15:10:07 -0800578 SkMatrix localMatrix;
579 if (!viewMatrix.invert(&localMatrix)) {
580 SkDebugf("Cannot invert\n");
581 return;
582 }
583
joshualitt56995b52014-12-11 15:44:02 -0800584 CoverageAttribType type;
egdaniel8dd688b2015-01-22 10:16:09 -0800585 SkAutoTUnref<const GrGeometryProcessor> gp(create_rect_gp(*pipelineBuilder, color, &type,
joshualittd27f73e2014-12-29 07:43:36 -0800586 localMatrix));
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000587
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000588 int innerVertexNum = 4;
589 int outerVertexNum = miterStroke ? 4 : 8;
590 int totalVertexNum = (outerVertexNum + innerVertexNum) * 2;
591
joshualitt56995b52014-12-11 15:44:02 -0800592 size_t vstride = gp->getVertexStride();
joshualitt2dd1ae02014-12-03 06:24:10 -0800593 GrDrawTarget::AutoReleaseGeometry geo(target, totalVertexNum, vstride, 0);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000594 if (!geo.succeeded()) {
tfarina38406c82014-10-31 07:11:12 -0700595 SkDebugf("Failed to get space for vertices!\n");
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000596 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
604 intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());
605
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000606 // 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.
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000609 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
egdaniel7b3d5ee2014-08-28 05:41:14 -0700610 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);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000613
robertphillips@google.comc111ce22013-07-01 13:10:10 +0000614#ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
robertphillips@google.com353f0972013-06-28 17:57:06 +0000615 // TODO: this only really works if the X & Y margins are the same all around
robertphillips183e9852014-10-21 11:25:37 -0700616 // the rect (or if they are all >= 1.0).
robertphillips@google.com353f0972013-06-28 17:57:06 +0000617 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);
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000620 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 }
robertphillips@google.com353f0972013-06-28 17:57:06 +0000625 SkASSERT(inset >= 0);
robertphillips@google.comc111ce22013-07-01 13:10:10 +0000626#else
627 SkScalar inset = SK_ScalarHalf;
628#endif
robertphillips@google.com353f0972013-06-28 17:57:06 +0000629
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000630 if (miterStroke) {
631 // outermost
egdaniel7b3d5ee2014-08-28 05:41:14 -0700632 set_inset_fan(fan0Pos, vstride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000633 // inner two
egdaniel7b3d5ee2014-08-28 05:41:14 -0700634 set_inset_fan(fan1Pos, vstride, devOutside, inset, inset);
635 set_inset_fan(fan2Pos, vstride, devInside, -inset, -inset);
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000636 // innermost
egdaniel7b3d5ee2014-08-28 05:41:14 -0700637 set_inset_fan(fan3Pos, vstride, devInside, SK_ScalarHalf, SK_ScalarHalf);
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000638 } else {
egdaniel7b3d5ee2014-08-28 05:41:14 -0700639 SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vstride);
640 SkPoint* fan1AssistPos = reinterpret_cast<SkPoint*>(verts + (outerVertexNum + 4) * vstride);
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000641 // outermost
egdaniel7b3d5ee2014-08-28 05:41:14 -0700642 set_inset_fan(fan0Pos, vstride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
643 set_inset_fan(fan0AssistPos, vstride, devOutsideAssist, -SK_ScalarHalf, -SK_ScalarHalf);
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000644 // outer one of the inner two
egdaniel7b3d5ee2014-08-28 05:41:14 -0700645 set_inset_fan(fan1Pos, vstride, devOutside, inset, inset);
646 set_inset_fan(fan1AssistPos, vstride, devOutsideAssist, inset, inset);
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000647 // inner one of the inner two
egdaniel7b3d5ee2014-08-28 05:41:14 -0700648 set_inset_fan(fan2Pos, vstride, devInside, -inset, -inset);
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000649 // innermost
egdaniel7b3d5ee2014-08-28 05:41:14 -0700650 set_inset_fan(fan3Pos, vstride, devInside, SK_ScalarHalf, SK_ScalarHalf);
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000651 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000652
bsalomon9c0822a2014-08-11 11:07:48 -0700653 // Make verts point to vertex color and then set all the color and coverage vertex attrs values.
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000654 // The outermost rect has 0 coverage
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000655 verts += sizeof(SkPoint);
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000656 for (int i = 0; i < outerVertexNum; ++i) {
joshualitt56995b52014-12-11 15:44:02 -0800657 if (kUseCoverage_CoverageAttribType == type) {
egdaniel7b3d5ee2014-08-28 05:41:14 -0700658 *reinterpret_cast<GrColor*>(verts + i * vstride) = color;
egdaniele27065a2014-11-06 08:00:48 -0800659 *reinterpret_cast<float*>(verts + i * vstride + sizeof(GrColor)) = 0;
bsalomonc30aaa02014-08-13 07:15:29 -0700660 } else {
egdaniel7b3d5ee2014-08-28 05:41:14 -0700661 *reinterpret_cast<GrColor*>(verts + i * vstride) = 0;
bsalomonc30aaa02014-08-13 07:15:29 -0700662 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000663 }
664
bsalomon9c0822a2014-08-11 11:07:48 -0700665 // scale is the coverage for the the inner two rects.
robertphillips@google.com353f0972013-06-28 17:57:06 +0000666 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
egdaniele27065a2014-11-06 08:00:48 -0800674 float innerCoverage = GrNormalizeByteToFloat(scale);
675 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
bsalomonc30aaa02014-08-13 07:15:29 -0700676
egdaniele27065a2014-11-06 08:00:48 -0800677 verts += outerVertexNum * vstride;
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000678 for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
joshualitt56995b52014-12-11 15:44:02 -0800679 if (kUseCoverage_CoverageAttribType == type) {
egdaniel7b3d5ee2014-08-28 05:41:14 -0700680 *reinterpret_cast<GrColor*>(verts + i * vstride) = color;
egdaniele27065a2014-11-06 08:00:48 -0800681 *reinterpret_cast<float*>(verts + i * vstride + sizeof(GrColor)) = innerCoverage;
bsalomonc30aaa02014-08-13 07:15:29 -0700682 } else {
egdaniele27065a2014-11-06 08:00:48 -0800683 *reinterpret_cast<GrColor*>(verts + i * vstride) = scaledColor;
bsalomonc30aaa02014-08-13 07:15:29 -0700684 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000685 }
686
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000687 // The innermost rect has 0 coverage
egdaniel7b3d5ee2014-08-28 05:41:14 -0700688 verts += (outerVertexNum + innerVertexNum) * vstride;
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000689 for (int i = 0; i < innerVertexNum; ++i) {
joshualitt56995b52014-12-11 15:44:02 -0800690 if (kUseCoverage_CoverageAttribType == type) {
egdaniel7b3d5ee2014-08-28 05:41:14 -0700691 *reinterpret_cast<GrColor*>(verts + i * vstride) = color;
692 *reinterpret_cast<GrColor*>(verts + i * vstride + sizeof(GrColor)) = 0;
bsalomonc30aaa02014-08-13 07:15:29 -0700693 } else {
egdaniel7b3d5ee2014-08-28 05:41:14 -0700694 *reinterpret_cast<GrColor*>(verts + i * vstride) = 0;
bsalomonc30aaa02014-08-13 07:15:29 -0700695 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000696 }
697
698 target->setIndexSourceToBuffer(indexBuffer);
egdaniel8dd688b2015-01-22 10:16:09 -0800699 target->drawIndexedInstances(pipelineBuilder,
joshualitt56995b52014-12-11 15:44:02 -0800700 gp,
joshualitt9853cce2014-11-17 14:22:48 -0800701 kTriangles_GrPrimitiveType,
702 1,
703 totalVertexNum,
704 aa_stroke_rect_index_count(miterStroke));
joshualitt5ead6da2014-10-22 16:00:29 -0700705 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}