blob: 0a9601f3102f87f27bdd10eb4057e47f2e0cf055 [file] [log] [blame]
joshualitt9ff64252015-08-10 09:03:51 -07001/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrAAStrokeRectBatch.h"
9
bsalomon75398562015-08-17 12:55:38 -070010#include "GrBatchFlushState.h"
joshualitt9ff64252015-08-10 09:03:51 -070011#include "GrDefaultGeoProcFactory.h"
12#include "GrResourceKey.h"
13#include "GrResourceProvider.h"
14
15GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
16GR_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
17
18static void set_inset_fan(SkPoint* pts, size_t stride,
19 const SkRect& r, SkScalar dx, SkScalar dy) {
20 pts->setRectFan(r.fLeft + dx, r.fTop + dy,
21 r.fRight - dx, r.fBottom - dy, stride);
22}
23
24static const GrGeometryProcessor* create_stroke_rect_gp(bool tweakAlphaForCoverage,
25 const SkMatrix& viewMatrix,
26 bool usesLocalCoords,
27 bool coverageIgnored) {
28 using namespace GrDefaultGeoProcFactory;
29
30 Color color(Color::kAttribute_Type);
31 Coverage::Type coverageType;
32 // TODO remove coverage if coverage is ignored
33 /*if (coverageIgnored) {
34 coverageType = Coverage::kNone_Type;
35 } else*/ if (tweakAlphaForCoverage) {
36 coverageType = Coverage::kSolid_Type;
37 } else {
38 coverageType = Coverage::kAttribute_Type;
39 }
40 Coverage coverage(coverageType);
41 LocalCoords localCoords(usesLocalCoords ? LocalCoords::kUsePosition_Type :
42 LocalCoords::kUnused_Type);
43 return CreateForDeviceSpace(color, coverage, localCoords, viewMatrix);
44}
45
joshualitt3566d442015-09-18 07:12:55 -070046class AAStrokeRectBatch : public GrVertexBatch {
47public:
48 DEFINE_BATCH_CLASS_ID
49
50 // TODO support AA rotated stroke rects by copying around view matrices
51 struct Geometry {
52 SkRect fDevOutside;
53 SkRect fDevOutsideAssist;
54 SkRect fDevInside;
55 GrColor fColor;
joshualitt3566d442015-09-18 07:12:55 -070056 };
57
joshualittaa37a962015-09-18 13:03:25 -070058 static AAStrokeRectBatch* Create(const SkMatrix& viewMatrix, bool miterStroke) {
59 return new AAStrokeRectBatch(viewMatrix, miterStroke);
joshualitt3566d442015-09-18 07:12:55 -070060 }
61
62 const char* name() const override { return "AAStrokeRect"; }
63
64 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
65 // When this is called on a batch, there is only one geometry bundle
66 out->setKnownFourComponents(fGeoData[0].fColor);
67 }
68
69 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
70 out->setUnknownSingleComponent();
71 }
72
73 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
74
joshualittaa37a962015-09-18 13:03:25 -070075 bool canAppend(const SkMatrix& viewMatrix, bool miterStroke) {
76 return fViewMatrix.cheapEqualTo(viewMatrix) && fMiterStroke == miterStroke;
77 }
joshualitt3566d442015-09-18 07:12:55 -070078
joshualittaa37a962015-09-18 13:03:25 -070079 void append(GrColor color, const SkRect& devOutside, const SkRect& devOutsideAssist,
80 const SkRect& devInside) {
joshualitt3566d442015-09-18 07:12:55 -070081 Geometry& geometry = fGeoData.push_back();
82 geometry.fColor = color;
83 geometry.fDevOutside = devOutside;
84 geometry.fDevOutsideAssist = devOutsideAssist;
85 geometry.fDevInside = devInside;
joshualitt3566d442015-09-18 07:12:55 -070086 }
87
joshualittaa37a962015-09-18 13:03:25 -070088 void appendAndUpdateBounds(GrColor color, const SkRect& devOutside,
89 const SkRect& devOutsideAssist, const SkRect& devInside) {
90 this->append(color, devOutside, devOutsideAssist, devInside);
91
92 SkRect bounds;
93 this->updateBounds(&bounds, fGeoData.back());
94 this->joinBounds(bounds);
95 }
96
97 void init() { this->updateBounds(&fBounds, fGeoData[0]); }
98
99private:
100 void updateBounds(SkRect* bounds, const Geometry& geo) {
101 // If we have miterstroke then we inset devOutside and outset devOutsideAssist, so we need
102 // the join for proper bounds
103 *bounds = geo.fDevOutside;
104 bounds->join(geo.fDevOutsideAssist);
105 }
106
107 void onPrepareDraws(Target*) override;
108 void initBatchTracker(const GrPipelineOptimizations&) override;
109
110 AAStrokeRectBatch(const SkMatrix& viewMatrix,bool miterStroke)
111 : INHERITED(ClassID()) {
112 fViewMatrix = viewMatrix;
113 fMiterStroke = miterStroke;
114 }
joshualitt3566d442015-09-18 07:12:55 -0700115
116 static const int kMiterIndexCnt = 3 * 24;
117 static const int kMiterVertexCnt = 16;
118 static const int kNumMiterRectsInIndexBuffer = 256;
119
120 static const int kBevelIndexCnt = 48 + 36 + 24;
121 static const int kBevelVertexCnt = 24;
122 static const int kNumBevelRectsInIndexBuffer = 256;
123
124 static const GrIndexBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider,
125 bool miterStroke);
126
127 GrColor color() const { return fBatch.fColor; }
128 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
129 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
130 bool colorIgnored() const { return fBatch.fColorIgnored; }
joshualitt3566d442015-09-18 07:12:55 -0700131 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
joshualittaa37a962015-09-18 13:03:25 -0700132 const Geometry& geometry() const { return fGeoData[0]; }
133 const SkMatrix& viewMatrix() const { return fViewMatrix; }
134 bool miterStroke() const { return fMiterStroke; }
joshualitt3566d442015-09-18 07:12:55 -0700135
136 bool onCombineIfPossible(GrBatch* t, const GrCaps&) override;
137
138 void generateAAStrokeRectGeometry(void* vertices,
139 size_t offset,
140 size_t vertexStride,
141 int outerVertexNum,
142 int innerVertexNum,
143 GrColor color,
144 const SkRect& devOutside,
145 const SkRect& devOutsideAssist,
146 const SkRect& devInside,
147 bool miterStroke,
148 bool tweakAlphaForCoverage) const;
149
150 struct BatchTracker {
joshualitt3566d442015-09-18 07:12:55 -0700151 GrColor fColor;
152 bool fUsesLocalCoords;
153 bool fColorIgnored;
154 bool fCoverageIgnored;
joshualitt3566d442015-09-18 07:12:55 -0700155 bool fCanTweakAlphaForCoverage;
156 };
157
158 BatchTracker fBatch;
159 SkSTArray<1, Geometry, true> fGeoData;
joshualittaa37a962015-09-18 13:03:25 -0700160 SkMatrix fViewMatrix;
161 bool fMiterStroke;
joshualitt3566d442015-09-18 07:12:55 -0700162
163 typedef GrVertexBatch INHERITED;
164};
165
166void AAStrokeRectBatch::initBatchTracker(const GrPipelineOptimizations& opt) {
joshualitt9ff64252015-08-10 09:03:51 -0700167 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -0700168 if (!opt.readsColor()) {
joshualitt9ff64252015-08-10 09:03:51 -0700169 fGeoData[0].fColor = GrColor_ILLEGAL;
170 }
bsalomon91d844d2015-08-10 10:47:29 -0700171 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt9ff64252015-08-10 09:03:51 -0700172
173 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -0700174 fBatch.fColorIgnored = !opt.readsColor();
joshualitt9ff64252015-08-10 09:03:51 -0700175 fBatch.fColor = fGeoData[0].fColor;
bsalomon91d844d2015-08-10 10:47:29 -0700176 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
177 fBatch.fCoverageIgnored = !opt.readsCoverage();
bsalomon91d844d2015-08-10 10:47:29 -0700178 fBatch.fCanTweakAlphaForCoverage = opt.canTweakAlphaForCoverage();
joshualitt9ff64252015-08-10 09:03:51 -0700179}
180
joshualitt3566d442015-09-18 07:12:55 -0700181void AAStrokeRectBatch::onPrepareDraws(Target* target) {
joshualitt9ff64252015-08-10 09:03:51 -0700182 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
183
184 SkAutoTUnref<const GrGeometryProcessor> gp(create_stroke_rect_gp(canTweakAlphaForCoverage,
185 this->viewMatrix(),
186 this->usesLocalCoords(),
187 this->coverageIgnored()));
188 if (!gp) {
189 SkDebugf("Couldn't create GrGeometryProcessor\n");
190 return;
191 }
192
bsalomon75398562015-08-17 12:55:38 -0700193 target->initDraw(gp, this->pipeline());
joshualitt9ff64252015-08-10 09:03:51 -0700194
195 size_t vertexStride = gp->getVertexStride();
196
197 SkASSERT(canTweakAlphaForCoverage ?
198 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
199 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
200 int innerVertexNum = 4;
201 int outerVertexNum = this->miterStroke() ? 4 : 8;
202 int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
203 int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
204 int instanceCount = fGeoData.count();
205
206 const SkAutoTUnref<const GrIndexBuffer> indexBuffer(
bsalomon75398562015-08-17 12:55:38 -0700207 GetIndexBuffer(target->resourceProvider(), this->miterStroke()));
joshualitt9ff64252015-08-10 09:03:51 -0700208 InstancedHelper helper;
bsalomon75398562015-08-17 12:55:38 -0700209 void* vertices = helper.init(target, kTriangles_GrPrimitiveType, vertexStride,
joshualitt9ff64252015-08-10 09:03:51 -0700210 indexBuffer, verticesPerInstance, indicesPerInstance,
211 instanceCount);
212 if (!vertices || !indexBuffer) {
213 SkDebugf("Could not allocate vertices\n");
214 return;
215 }
216
217 for (int i = 0; i < instanceCount; i++) {
218 const Geometry& args = fGeoData[i];
219 this->generateAAStrokeRectGeometry(vertices,
220 i * verticesPerInstance * vertexStride,
221 vertexStride,
222 outerVertexNum,
223 innerVertexNum,
224 args.fColor,
225 args.fDevOutside,
226 args.fDevOutsideAssist,
227 args.fDevInside,
joshualittaa37a962015-09-18 13:03:25 -0700228 fMiterStroke,
joshualitt9ff64252015-08-10 09:03:51 -0700229 canTweakAlphaForCoverage);
230 }
bsalomon75398562015-08-17 12:55:38 -0700231 helper.recordDraw(target);
joshualitt9ff64252015-08-10 09:03:51 -0700232}
233
joshualitt3566d442015-09-18 07:12:55 -0700234const GrIndexBuffer* AAStrokeRectBatch::GetIndexBuffer(GrResourceProvider* resourceProvider,
235 bool miterStroke) {
joshualitt9ff64252015-08-10 09:03:51 -0700236
237 if (miterStroke) {
238 static const uint16_t gMiterIndices[] = {
239 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
240 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
241 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
242 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
243
244 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
245 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
246 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
247 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
248
249 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
250 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
251 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
252 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
253 };
254 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
255 GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
256 return resourceProvider->findOrCreateInstancedIndexBuffer(gMiterIndices,
257 kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
258 gMiterIndexBufferKey);
259 } else {
260 /**
261 * As in miter-stroke, index = a + b, and a is the current index, b is the shift
262 * from the first index. The index layout:
263 * outer AA line: 0~3, 4~7
264 * outer edge: 8~11, 12~15
265 * inner edge: 16~19
266 * inner AA line: 20~23
267 * Following comes a bevel-stroke rect and its indices:
268 *
269 * 4 7
270 * *********************************
271 * * ______________________________ *
272 * * / 12 15 \ *
273 * * / \ *
274 * 0 * |8 16_____________________19 11 | * 3
275 * * | | | | *
276 * * | | **************** | | *
277 * * | | * 20 23 * | | *
278 * * | | * * | | *
279 * * | | * 21 22 * | | *
280 * * | | **************** | | *
281 * * | |____________________| | *
282 * 1 * |9 17 18 10| * 2
283 * * \ / *
284 * * \13 __________________________14/ *
285 * * *
286 * **********************************
287 * 5 6
288 */
289 static const uint16_t gBevelIndices[] = {
290 // Draw outer AA, from outer AA line to outer edge, shift is 0.
291 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
292 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
293 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
294 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
295 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
296 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
297 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
298 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
299
300 // Draw the stroke, from outer edge to inner edge, shift is 8.
301 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
302 1 + 8, 5 + 8, 9 + 8,
303 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
304 6 + 8, 2 + 8, 10 + 8,
305 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
306 3 + 8, 7 + 8, 11 + 8,
307 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
308 4 + 8, 0 + 8, 8 + 8,
309
310 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
311 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
312 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
313 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
314 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
315 };
316 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
317
318 GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
319 return resourceProvider->findOrCreateInstancedIndexBuffer(gBevelIndices,
320 kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
321 gBevelIndexBufferKey);
322 }
323}
324
joshualitt3566d442015-09-18 07:12:55 -0700325bool AAStrokeRectBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) {
326 AAStrokeRectBatch* that = t->cast<AAStrokeRectBatch>();
bsalomonabd30f52015-08-13 13:34:48 -0700327
328 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
329 that->bounds(), caps)) {
joshualitt9ff64252015-08-10 09:03:51 -0700330 return false;
331 }
332
joshualitt9ff64252015-08-10 09:03:51 -0700333 // TODO batch across miterstroke changes
334 if (this->miterStroke() != that->miterStroke()) {
335 return false;
336 }
337
338 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
339 // local coords then we won't be able to batch. We could actually upload the viewmatrix
340 // using vertex attributes in these cases, but haven't investigated that
341 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
342 return false;
343 }
344
345 // In the event of two batches, one who can tweak, one who cannot, we just fall back to
346 // not tweaking
347 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
348 fBatch.fCanTweakAlphaForCoverage = false;
349 }
350
351 if (this->color() != that->color()) {
352 fBatch.fColor = GrColor_ILLEGAL;
353 }
354 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
355 this->joinBounds(that->bounds());
356 return true;
357}
358
joshualitt3566d442015-09-18 07:12:55 -0700359void AAStrokeRectBatch::generateAAStrokeRectGeometry(void* vertices,
360 size_t offset,
361 size_t vertexStride,
362 int outerVertexNum,
363 int innerVertexNum,
364 GrColor color,
365 const SkRect& devOutside,
366 const SkRect& devOutsideAssist,
367 const SkRect& devInside,
368 bool miterStroke,
369 bool tweakAlphaForCoverage) const {
joshualitt9ff64252015-08-10 09:03:51 -0700370 intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
371
372 // We create vertices for four nested rectangles. There are two ramps from 0 to full
373 // coverage, one on the exterior of the stroke and the other on the interior.
374 // The following pointers refer to the four rects, from outermost to innermost.
375 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
376 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
377 SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
378 SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(verts +
379 (2 * outerVertexNum + innerVertexNum) *
380 vertexStride);
381
382#ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
383 // TODO: this only really works if the X & Y margins are the same all around
384 // the rect (or if they are all >= 1.0).
385 SkScalar inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
386 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
387 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
388 if (miterStroke) {
389 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
390 } else {
391 inset = SK_ScalarHalf * SkMinScalar(inset, devOutsideAssist.fBottom -
392 devInside.fBottom);
393 }
394 SkASSERT(inset >= 0);
395#else
396 SkScalar inset = SK_ScalarHalf;
397#endif
398
399 if (miterStroke) {
400 // outermost
401 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
402 // inner two
403 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
404 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
405 // innermost
406 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
407 } else {
408 SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
409 SkPoint* fan1AssistPos = reinterpret_cast<SkPoint*>(verts +
410 (outerVertexNum + 4) *
411 vertexStride);
412 // outermost
413 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
414 set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
415 -SK_ScalarHalf);
416 // outer one of the inner two
417 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
418 set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist, inset, inset);
419 // inner one of the inner two
420 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
421 // innermost
422 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
423 }
424
425 // Make verts point to vertex color and then set all the color and coverage vertex attrs
426 // values. The outermost rect has 0 coverage
427 verts += sizeof(SkPoint);
428 for (int i = 0; i < outerVertexNum; ++i) {
429 if (tweakAlphaForCoverage) {
430 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
431 } else {
432 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
433 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
434 }
435 }
436
437 // scale is the coverage for the the inner two rects.
438 int scale;
439 if (inset < SK_ScalarHalf) {
440 scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
441 SkASSERT(scale >= 0 && scale <= 255);
442 } else {
443 scale = 0xff;
444 }
445
446 float innerCoverage = GrNormalizeByteToFloat(scale);
447 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
448
449 verts += outerVertexNum * vertexStride;
450 for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
451 if (tweakAlphaForCoverage) {
452 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
453 } else {
454 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
455 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
456 innerCoverage;
457 }
458 }
459
460 // The innermost rect has 0 coverage
461 verts += (outerVertexNum + innerVertexNum) * vertexStride;
462 for (int i = 0; i < innerVertexNum; ++i) {
463 if (tweakAlphaForCoverage) {
464 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
465 } else {
466 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
467 *reinterpret_cast<GrColor*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
468 }
469 }
470}
471
joshualitt3566d442015-09-18 07:12:55 -0700472namespace GrAAStrokeRectBatch {
473
474GrDrawBatch* Create(GrColor color,
475 const SkMatrix& viewMatrix,
476 const SkRect& devOutside,
477 const SkRect& devOutsideAssist,
478 const SkRect& devInside,
479 bool miterStroke) {
joshualittaa37a962015-09-18 13:03:25 -0700480 AAStrokeRectBatch* batch = AAStrokeRectBatch::Create(viewMatrix, miterStroke);
481 batch->append(color, devOutside, devOutsideAssist, devInside);
482 batch->init();
483 return batch;
484}
485
486bool Append(GrBatch* origBatch,
487 GrColor color,
488 const SkMatrix& viewMatrix,
489 const SkRect& devOutside,
490 const SkRect& devOutsideAssist,
491 const SkRect& devInside,
492 bool miterStroke) {
493 AAStrokeRectBatch* batch = origBatch->cast<AAStrokeRectBatch>();
494
495 // we can't batch across vm changes
496 if (!batch->canAppend(viewMatrix, miterStroke)) {
497 return false;
498 }
499
500 batch->appendAndUpdateBounds(color, devOutside, devOutsideAssist, devInside);
501 return true;
joshualitt3566d442015-09-18 07:12:55 -0700502}
503
504};
505
joshualitt9ff64252015-08-10 09:03:51 -0700506///////////////////////////////////////////////////////////////////////////////////////////////////
507
508#ifdef GR_TEST_UTILS
509
510#include "GrBatchTest.h"
511
bsalomonabd30f52015-08-13 13:34:48 -0700512DRAW_BATCH_TEST_DEFINE(AAStrokeRectBatch) {
joshualitt9ff64252015-08-10 09:03:51 -0700513 bool miterStroke = random->nextBool();
514
515 // Create mock stroke rect
516 SkRect outside = GrTest::TestRect(random);
517 SkScalar minDim = SkMinScalar(outside.width(), outside.height());
518 SkScalar strokeWidth = minDim * 0.1f;
519 SkRect outsideAssist = outside;
520 outsideAssist.outset(strokeWidth, strokeWidth);
521 SkRect inside = outside;
522 inside.inset(strokeWidth, strokeWidth);
523
joshualitt3566d442015-09-18 07:12:55 -0700524 GrColor color = GrRandomColor(random);
joshualitt9ff64252015-08-10 09:03:51 -0700525
joshualitt3566d442015-09-18 07:12:55 -0700526 return GrAAStrokeRectBatch::Create(color, GrTest::TestMatrix(random), outside, outsideAssist,
527 inside, miterStroke);
joshualitt9ff64252015-08-10 09:03:51 -0700528}
529
530#endif