blob: ea8bc2e46e1aa1ce0149d95094efb4cc4a08cf05 [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;
joshualitt11edad92015-09-22 10:32:28 -070056 bool fDegenerate;
joshualitt3566d442015-09-18 07:12:55 -070057 };
58
joshualittaa37a962015-09-18 13:03:25 -070059 static AAStrokeRectBatch* Create(const SkMatrix& viewMatrix, bool miterStroke) {
60 return new AAStrokeRectBatch(viewMatrix, miterStroke);
joshualitt3566d442015-09-18 07:12:55 -070061 }
62
63 const char* name() const override { return "AAStrokeRect"; }
64
65 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
66 // When this is called on a batch, there is only one geometry bundle
67 out->setKnownFourComponents(fGeoData[0].fColor);
68 }
69
70 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
71 out->setUnknownSingleComponent();
72 }
73
74 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
75
joshualittaa37a962015-09-18 13:03:25 -070076 bool canAppend(const SkMatrix& viewMatrix, bool miterStroke) {
77 return fViewMatrix.cheapEqualTo(viewMatrix) && fMiterStroke == miterStroke;
78 }
joshualitt3566d442015-09-18 07:12:55 -070079
joshualittaa37a962015-09-18 13:03:25 -070080 void append(GrColor color, const SkRect& devOutside, const SkRect& devOutsideAssist,
joshualitt11edad92015-09-22 10:32:28 -070081 const SkRect& devInside, bool degenerate) {
joshualitt3566d442015-09-18 07:12:55 -070082 Geometry& geometry = fGeoData.push_back();
83 geometry.fColor = color;
84 geometry.fDevOutside = devOutside;
85 geometry.fDevOutsideAssist = devOutsideAssist;
86 geometry.fDevInside = devInside;
joshualitt11edad92015-09-22 10:32:28 -070087 geometry.fDegenerate = degenerate;
joshualitt3566d442015-09-18 07:12:55 -070088 }
89
joshualittaa37a962015-09-18 13:03:25 -070090 void appendAndUpdateBounds(GrColor color, const SkRect& devOutside,
joshualitt11edad92015-09-22 10:32:28 -070091 const SkRect& devOutsideAssist, const SkRect& devInside,
92 bool degenerate) {
93 this->append(color, devOutside, devOutsideAssist, devInside, degenerate);
joshualittaa37a962015-09-18 13:03:25 -070094
95 SkRect bounds;
96 this->updateBounds(&bounds, fGeoData.back());
97 this->joinBounds(bounds);
98 }
99
100 void init() { this->updateBounds(&fBounds, fGeoData[0]); }
101
102private:
103 void updateBounds(SkRect* bounds, const Geometry& geo) {
104 // If we have miterstroke then we inset devOutside and outset devOutsideAssist, so we need
105 // the join for proper bounds
106 *bounds = geo.fDevOutside;
107 bounds->join(geo.fDevOutsideAssist);
108 }
109
110 void onPrepareDraws(Target*) override;
111 void initBatchTracker(const GrPipelineOptimizations&) override;
112
113 AAStrokeRectBatch(const SkMatrix& viewMatrix,bool miterStroke)
114 : INHERITED(ClassID()) {
115 fViewMatrix = viewMatrix;
116 fMiterStroke = miterStroke;
117 }
joshualitt3566d442015-09-18 07:12:55 -0700118
119 static const int kMiterIndexCnt = 3 * 24;
120 static const int kMiterVertexCnt = 16;
121 static const int kNumMiterRectsInIndexBuffer = 256;
122
123 static const int kBevelIndexCnt = 48 + 36 + 24;
124 static const int kBevelVertexCnt = 24;
125 static const int kNumBevelRectsInIndexBuffer = 256;
126
127 static const GrIndexBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider,
128 bool miterStroke);
129
130 GrColor color() const { return fBatch.fColor; }
131 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
132 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
133 bool colorIgnored() const { return fBatch.fColorIgnored; }
joshualitt3566d442015-09-18 07:12:55 -0700134 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
joshualittaa37a962015-09-18 13:03:25 -0700135 const Geometry& geometry() const { return fGeoData[0]; }
136 const SkMatrix& viewMatrix() const { return fViewMatrix; }
137 bool miterStroke() const { return fMiterStroke; }
joshualitt3566d442015-09-18 07:12:55 -0700138
139 bool onCombineIfPossible(GrBatch* t, const GrCaps&) override;
140
141 void generateAAStrokeRectGeometry(void* vertices,
142 size_t offset,
143 size_t vertexStride,
144 int outerVertexNum,
145 int innerVertexNum,
146 GrColor color,
147 const SkRect& devOutside,
148 const SkRect& devOutsideAssist,
149 const SkRect& devInside,
150 bool miterStroke,
joshualitt11edad92015-09-22 10:32:28 -0700151 bool degenerate,
joshualitt3566d442015-09-18 07:12:55 -0700152 bool tweakAlphaForCoverage) const;
153
154 struct BatchTracker {
joshualitt3566d442015-09-18 07:12:55 -0700155 GrColor fColor;
156 bool fUsesLocalCoords;
157 bool fColorIgnored;
158 bool fCoverageIgnored;
joshualitt3566d442015-09-18 07:12:55 -0700159 bool fCanTweakAlphaForCoverage;
160 };
161
162 BatchTracker fBatch;
163 SkSTArray<1, Geometry, true> fGeoData;
joshualittaa37a962015-09-18 13:03:25 -0700164 SkMatrix fViewMatrix;
165 bool fMiterStroke;
joshualitt3566d442015-09-18 07:12:55 -0700166
167 typedef GrVertexBatch INHERITED;
168};
169
170void AAStrokeRectBatch::initBatchTracker(const GrPipelineOptimizations& opt) {
joshualitt9ff64252015-08-10 09:03:51 -0700171 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -0700172 if (!opt.readsColor()) {
joshualitt9ff64252015-08-10 09:03:51 -0700173 fGeoData[0].fColor = GrColor_ILLEGAL;
174 }
bsalomon91d844d2015-08-10 10:47:29 -0700175 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt9ff64252015-08-10 09:03:51 -0700176
177 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -0700178 fBatch.fColorIgnored = !opt.readsColor();
joshualitt9ff64252015-08-10 09:03:51 -0700179 fBatch.fColor = fGeoData[0].fColor;
bsalomon91d844d2015-08-10 10:47:29 -0700180 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
181 fBatch.fCoverageIgnored = !opt.readsCoverage();
bsalomon91d844d2015-08-10 10:47:29 -0700182 fBatch.fCanTweakAlphaForCoverage = opt.canTweakAlphaForCoverage();
joshualitt9ff64252015-08-10 09:03:51 -0700183}
184
joshualitt3566d442015-09-18 07:12:55 -0700185void AAStrokeRectBatch::onPrepareDraws(Target* target) {
joshualitt9ff64252015-08-10 09:03:51 -0700186 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
187
188 SkAutoTUnref<const GrGeometryProcessor> gp(create_stroke_rect_gp(canTweakAlphaForCoverage,
189 this->viewMatrix(),
190 this->usesLocalCoords(),
191 this->coverageIgnored()));
192 if (!gp) {
193 SkDebugf("Couldn't create GrGeometryProcessor\n");
194 return;
195 }
196
bsalomon75398562015-08-17 12:55:38 -0700197 target->initDraw(gp, this->pipeline());
joshualitt9ff64252015-08-10 09:03:51 -0700198
199 size_t vertexStride = gp->getVertexStride();
200
201 SkASSERT(canTweakAlphaForCoverage ?
202 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
203 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
204 int innerVertexNum = 4;
205 int outerVertexNum = this->miterStroke() ? 4 : 8;
206 int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
207 int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
208 int instanceCount = fGeoData.count();
209
210 const SkAutoTUnref<const GrIndexBuffer> indexBuffer(
bsalomon75398562015-08-17 12:55:38 -0700211 GetIndexBuffer(target->resourceProvider(), this->miterStroke()));
joshualitt9ff64252015-08-10 09:03:51 -0700212 InstancedHelper helper;
bsalomon75398562015-08-17 12:55:38 -0700213 void* vertices = helper.init(target, kTriangles_GrPrimitiveType, vertexStride,
joshualitt9ff64252015-08-10 09:03:51 -0700214 indexBuffer, verticesPerInstance, indicesPerInstance,
215 instanceCount);
216 if (!vertices || !indexBuffer) {
217 SkDebugf("Could not allocate vertices\n");
218 return;
219 }
220
221 for (int i = 0; i < instanceCount; i++) {
222 const Geometry& args = fGeoData[i];
223 this->generateAAStrokeRectGeometry(vertices,
224 i * verticesPerInstance * vertexStride,
225 vertexStride,
226 outerVertexNum,
227 innerVertexNum,
228 args.fColor,
229 args.fDevOutside,
230 args.fDevOutsideAssist,
231 args.fDevInside,
joshualittaa37a962015-09-18 13:03:25 -0700232 fMiterStroke,
joshualitt11edad92015-09-22 10:32:28 -0700233 args.fDegenerate,
joshualitt9ff64252015-08-10 09:03:51 -0700234 canTweakAlphaForCoverage);
235 }
bsalomon75398562015-08-17 12:55:38 -0700236 helper.recordDraw(target);
joshualitt9ff64252015-08-10 09:03:51 -0700237}
238
joshualitt3566d442015-09-18 07:12:55 -0700239const GrIndexBuffer* AAStrokeRectBatch::GetIndexBuffer(GrResourceProvider* resourceProvider,
240 bool miterStroke) {
joshualitt9ff64252015-08-10 09:03:51 -0700241
242 if (miterStroke) {
243 static const uint16_t gMiterIndices[] = {
244 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
245 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
246 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
247 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
248
249 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
250 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
251 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
252 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
253
254 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
255 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
256 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
257 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
258 };
259 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
260 GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
261 return resourceProvider->findOrCreateInstancedIndexBuffer(gMiterIndices,
262 kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
263 gMiterIndexBufferKey);
264 } else {
265 /**
266 * As in miter-stroke, index = a + b, and a is the current index, b is the shift
267 * from the first index. The index layout:
268 * outer AA line: 0~3, 4~7
269 * outer edge: 8~11, 12~15
270 * inner edge: 16~19
271 * inner AA line: 20~23
272 * Following comes a bevel-stroke rect and its indices:
273 *
274 * 4 7
275 * *********************************
276 * * ______________________________ *
277 * * / 12 15 \ *
278 * * / \ *
279 * 0 * |8 16_____________________19 11 | * 3
280 * * | | | | *
281 * * | | **************** | | *
282 * * | | * 20 23 * | | *
283 * * | | * * | | *
284 * * | | * 21 22 * | | *
285 * * | | **************** | | *
286 * * | |____________________| | *
287 * 1 * |9 17 18 10| * 2
288 * * \ / *
289 * * \13 __________________________14/ *
290 * * *
291 * **********************************
292 * 5 6
293 */
294 static const uint16_t gBevelIndices[] = {
295 // Draw outer AA, from outer AA line to outer edge, shift is 0.
296 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
297 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
298 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
299 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
300 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
301 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
302 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
303 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
304
305 // Draw the stroke, from outer edge to inner edge, shift is 8.
306 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
307 1 + 8, 5 + 8, 9 + 8,
308 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
309 6 + 8, 2 + 8, 10 + 8,
310 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
311 3 + 8, 7 + 8, 11 + 8,
312 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
313 4 + 8, 0 + 8, 8 + 8,
314
315 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
316 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
317 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
318 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
319 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
320 };
321 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
322
323 GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
324 return resourceProvider->findOrCreateInstancedIndexBuffer(gBevelIndices,
325 kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
326 gBevelIndexBufferKey);
327 }
328}
329
joshualitt3566d442015-09-18 07:12:55 -0700330bool AAStrokeRectBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) {
331 AAStrokeRectBatch* that = t->cast<AAStrokeRectBatch>();
bsalomonabd30f52015-08-13 13:34:48 -0700332
333 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
334 that->bounds(), caps)) {
joshualitt9ff64252015-08-10 09:03:51 -0700335 return false;
336 }
337
joshualitt9ff64252015-08-10 09:03:51 -0700338 // TODO batch across miterstroke changes
339 if (this->miterStroke() != that->miterStroke()) {
340 return false;
341 }
342
343 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
344 // local coords then we won't be able to batch. We could actually upload the viewmatrix
345 // using vertex attributes in these cases, but haven't investigated that
346 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
347 return false;
348 }
349
350 // In the event of two batches, one who can tweak, one who cannot, we just fall back to
351 // not tweaking
352 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
353 fBatch.fCanTweakAlphaForCoverage = false;
354 }
355
356 if (this->color() != that->color()) {
357 fBatch.fColor = GrColor_ILLEGAL;
358 }
359 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
360 this->joinBounds(that->bounds());
361 return true;
362}
363
joshualitt11edad92015-09-22 10:32:28 -0700364static void setup_scale(int* scale, SkScalar inset) {
365 if (inset < SK_ScalarHalf) {
366 *scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
367 SkASSERT(*scale >= 0 && *scale <= 255);
368 } else {
369 *scale = 0xff;
370 }
371}
372
joshualitt3566d442015-09-18 07:12:55 -0700373void AAStrokeRectBatch::generateAAStrokeRectGeometry(void* vertices,
374 size_t offset,
375 size_t vertexStride,
376 int outerVertexNum,
377 int innerVertexNum,
378 GrColor color,
379 const SkRect& devOutside,
380 const SkRect& devOutsideAssist,
381 const SkRect& devInside,
382 bool miterStroke,
joshualitt11edad92015-09-22 10:32:28 -0700383 bool degenerate,
joshualitt3566d442015-09-18 07:12:55 -0700384 bool tweakAlphaForCoverage) const {
joshualitt9ff64252015-08-10 09:03:51 -0700385 intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
386
387 // We create vertices for four nested rectangles. There are two ramps from 0 to full
388 // coverage, one on the exterior of the stroke and the other on the interior.
389 // The following pointers refer to the four rects, from outermost to innermost.
390 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
391 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
392 SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
393 SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(verts +
394 (2 * outerVertexNum + innerVertexNum) *
395 vertexStride);
396
397#ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
398 // TODO: this only really works if the X & Y margins are the same all around
399 // the rect (or if they are all >= 1.0).
joshualitt11edad92015-09-22 10:32:28 -0700400 SkScalar inset;
401 if (!degenerate) {
402 inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
403 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
404 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
405 if (miterStroke) {
406 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
407 } else {
408 inset = SK_ScalarHalf * SkMinScalar(inset, devOutsideAssist.fBottom -
409 devInside.fBottom);
410 }
411 SkASSERT(inset >= 0);
joshualitt9ff64252015-08-10 09:03:51 -0700412 } else {
joshualitt11edad92015-09-22 10:32:28 -0700413 // TODO use real devRect here
414 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
415 inset = SK_ScalarHalf * SkMinScalar(inset, SkTMax(devOutside.height(),
416 devOutsideAssist.height()));
joshualitt9ff64252015-08-10 09:03:51 -0700417 }
joshualitt9ff64252015-08-10 09:03:51 -0700418#else
joshualitt11edad92015-09-22 10:32:28 -0700419 SkScalar inset;
420 if (!degenerate) {
421 inset = SK_ScalarHalf;
422 } else {
423 // TODO use real devRect here
424 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
425 inset = SK_ScalarHalf * SkMinScalar(inset, SkTMax(devOutside.height(),
426 devOutsideAssist.height()));
427 }
joshualitt9ff64252015-08-10 09:03:51 -0700428#endif
429
430 if (miterStroke) {
431 // outermost
432 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
433 // inner two
434 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
joshualitt11edad92015-09-22 10:32:28 -0700435 if (!degenerate) {
436 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
437 // innermost
438 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
439 } else {
440 // When the interior rect has become degenerate we smoosh to a single point
441 SkASSERT(devInside.fLeft == devInside.fRight &&
442 devInside.fTop == devInside.fBottom);
443 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop,
444 devInside.fRight, devInside.fBottom, vertexStride);
445 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop,
446 devInside.fRight, devInside.fBottom, vertexStride);
447 }
joshualitt9ff64252015-08-10 09:03:51 -0700448 } else {
449 SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
450 SkPoint* fan1AssistPos = reinterpret_cast<SkPoint*>(verts +
451 (outerVertexNum + 4) *
452 vertexStride);
453 // outermost
454 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
455 set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
456 -SK_ScalarHalf);
457 // outer one of the inner two
458 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
459 set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist, inset, inset);
joshualitt11edad92015-09-22 10:32:28 -0700460 if (!degenerate) {
461 // inner one of the inner two
462 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
463 // innermost
464 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
465 } else {
466 // When the interior rect has become degenerate we smoosh to a single point
467 SkASSERT(devInside.fLeft == devInside.fRight &&
468 devInside.fTop == devInside.fBottom);
469 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop,
470 devInside.fRight, devInside.fBottom, vertexStride);
471 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop,
472 devInside.fRight, devInside.fBottom, vertexStride);
473 }
joshualitt9ff64252015-08-10 09:03:51 -0700474 }
475
476 // Make verts point to vertex color and then set all the color and coverage vertex attrs
477 // values. The outermost rect has 0 coverage
478 verts += sizeof(SkPoint);
479 for (int i = 0; i < outerVertexNum; ++i) {
480 if (tweakAlphaForCoverage) {
481 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
482 } else {
483 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
484 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
485 }
486 }
487
488 // scale is the coverage for the the inner two rects.
489 int scale;
joshualitt11edad92015-09-22 10:32:28 -0700490 setup_scale(&scale, inset);
joshualitt9ff64252015-08-10 09:03:51 -0700491
492 float innerCoverage = GrNormalizeByteToFloat(scale);
493 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
494
495 verts += outerVertexNum * vertexStride;
496 for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
497 if (tweakAlphaForCoverage) {
498 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
499 } else {
500 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
joshualitt11edad92015-09-22 10:32:28 -0700501 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
joshualitt9ff64252015-08-10 09:03:51 -0700502 }
503 }
504
joshualitt11edad92015-09-22 10:32:28 -0700505 // The innermost rect has 0 coverage, unless we are degenerate, in which case we must apply the
506 // scaled coverage
joshualitt9ff64252015-08-10 09:03:51 -0700507 verts += (outerVertexNum + innerVertexNum) * vertexStride;
joshualitt11edad92015-09-22 10:32:28 -0700508 if (!degenerate) {
509 innerCoverage = 0;
510 scaledColor = 0;
511 }
512
joshualitt9ff64252015-08-10 09:03:51 -0700513 for (int i = 0; i < innerVertexNum; ++i) {
514 if (tweakAlphaForCoverage) {
joshualitt11edad92015-09-22 10:32:28 -0700515 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
joshualitt9ff64252015-08-10 09:03:51 -0700516 } else {
517 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
joshualitt11edad92015-09-22 10:32:28 -0700518 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
joshualitt9ff64252015-08-10 09:03:51 -0700519 }
520 }
521}
522
joshualitt3566d442015-09-18 07:12:55 -0700523namespace GrAAStrokeRectBatch {
524
525GrDrawBatch* Create(GrColor color,
526 const SkMatrix& viewMatrix,
527 const SkRect& devOutside,
528 const SkRect& devOutsideAssist,
529 const SkRect& devInside,
joshualitt11edad92015-09-22 10:32:28 -0700530 bool miterStroke,
531 bool degenerate) {
joshualittaa37a962015-09-18 13:03:25 -0700532 AAStrokeRectBatch* batch = AAStrokeRectBatch::Create(viewMatrix, miterStroke);
joshualitt11edad92015-09-22 10:32:28 -0700533 batch->append(color, devOutside, devOutsideAssist, devInside, degenerate);
joshualittaa37a962015-09-18 13:03:25 -0700534 batch->init();
535 return batch;
536}
537
538bool Append(GrBatch* origBatch,
539 GrColor color,
540 const SkMatrix& viewMatrix,
541 const SkRect& devOutside,
542 const SkRect& devOutsideAssist,
543 const SkRect& devInside,
joshualitt11edad92015-09-22 10:32:28 -0700544 bool miterStroke,
545 bool degenerate) {
joshualittaa37a962015-09-18 13:03:25 -0700546 AAStrokeRectBatch* batch = origBatch->cast<AAStrokeRectBatch>();
547
548 // we can't batch across vm changes
549 if (!batch->canAppend(viewMatrix, miterStroke)) {
550 return false;
551 }
552
joshualitt11edad92015-09-22 10:32:28 -0700553 batch->appendAndUpdateBounds(color, devOutside, devOutsideAssist, devInside, degenerate);
joshualittaa37a962015-09-18 13:03:25 -0700554 return true;
joshualitt3566d442015-09-18 07:12:55 -0700555}
556
557};
558
joshualitt9ff64252015-08-10 09:03:51 -0700559///////////////////////////////////////////////////////////////////////////////////////////////////
560
561#ifdef GR_TEST_UTILS
562
563#include "GrBatchTest.h"
564
bsalomonabd30f52015-08-13 13:34:48 -0700565DRAW_BATCH_TEST_DEFINE(AAStrokeRectBatch) {
joshualitt9ff64252015-08-10 09:03:51 -0700566 bool miterStroke = random->nextBool();
567
568 // Create mock stroke rect
569 SkRect outside = GrTest::TestRect(random);
570 SkScalar minDim = SkMinScalar(outside.width(), outside.height());
571 SkScalar strokeWidth = minDim * 0.1f;
572 SkRect outsideAssist = outside;
573 outsideAssist.outset(strokeWidth, strokeWidth);
574 SkRect inside = outside;
575 inside.inset(strokeWidth, strokeWidth);
576
joshualitt3566d442015-09-18 07:12:55 -0700577 GrColor color = GrRandomColor(random);
joshualitt9ff64252015-08-10 09:03:51 -0700578
joshualitt3566d442015-09-18 07:12:55 -0700579 return GrAAStrokeRectBatch::Create(color, GrTest::TestMatrix(random), outside, outsideAssist,
joshualitt11edad92015-09-22 10:32:28 -0700580 inside, miterStroke, inside.isFinite() && inside.isEmpty());
joshualitt9ff64252015-08-10 09:03:51 -0700581}
582
583#endif