1 /*
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
10 #include "GrBatchFlushState.h"
11 #include "GrDefaultGeoProcFactory.h"
12 #include "GrResourceKey.h"
13 #include "GrResourceProvider.h"
14
15 GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
16 GR_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
17
set_inset_fan(SkPoint * pts,size_t stride,const SkRect & r,SkScalar dx,SkScalar dy)18 static 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
create_stroke_rect_gp(bool tweakAlphaForCoverage,const SkMatrix & viewMatrix,bool usesLocalCoords,bool coverageIgnored)24 static 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
46 class AAStrokeRectBatch : public GrVertexBatch {
47 public:
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;
56 bool fDegenerate;
57 };
58
Create(const SkMatrix & viewMatrix,bool miterStroke)59 static AAStrokeRectBatch* Create(const SkMatrix& viewMatrix, bool miterStroke) {
60 return new AAStrokeRectBatch(viewMatrix, miterStroke);
61 }
62
name() const63 const char* name() const override { return "AAStrokeRect"; }
64
computePipelineOptimizations(GrInitInvariantOutput * color,GrInitInvariantOutput * coverage,GrBatchToXPOverrides * overrides) const65 void computePipelineOptimizations(GrInitInvariantOutput* color,
66 GrInitInvariantOutput* coverage,
67 GrBatchToXPOverrides* overrides) const override {
68 // When this is called on a batch, there is only one geometry bundle
69 color->setKnownFourComponents(fGeoData[0].fColor);
70 coverage->setUnknownSingleComponent();
71 }
72
geoData()73 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
74
canAppend(const SkMatrix & viewMatrix,bool miterStroke)75 bool canAppend(const SkMatrix& viewMatrix, bool miterStroke) {
76 return fViewMatrix.cheapEqualTo(viewMatrix) && fMiterStroke == miterStroke;
77 }
78
append(GrColor color,const SkRect & devOutside,const SkRect & devOutsideAssist,const SkRect & devInside,bool degenerate)79 void append(GrColor color, const SkRect& devOutside, const SkRect& devOutsideAssist,
80 const SkRect& devInside, bool degenerate) {
81 Geometry& geometry = fGeoData.push_back();
82 geometry.fColor = color;
83 geometry.fDevOutside = devOutside;
84 geometry.fDevOutsideAssist = devOutsideAssist;
85 geometry.fDevInside = devInside;
86 geometry.fDegenerate = degenerate;
87 }
88
appendAndUpdateBounds(GrColor color,const SkRect & devOutside,const SkRect & devOutsideAssist,const SkRect & devInside,bool degenerate)89 void appendAndUpdateBounds(GrColor color, const SkRect& devOutside,
90 const SkRect& devOutsideAssist, const SkRect& devInside,
91 bool degenerate) {
92 this->append(color, devOutside, devOutsideAssist, devInside, degenerate);
93
94 SkRect bounds;
95 this->updateBounds(&bounds, fGeoData.back());
96 this->joinBounds(bounds);
97 }
98
init()99 void init() { this->updateBounds(&fBounds, fGeoData[0]); }
100
101 private:
updateBounds(SkRect * bounds,const Geometry & geo)102 void updateBounds(SkRect* bounds, const Geometry& geo) {
103 // If we have miterstroke then we inset devOutside and outset devOutsideAssist, so we need
104 // the join for proper bounds
105 *bounds = geo.fDevOutside;
106 bounds->join(geo.fDevOutsideAssist);
107 }
108
109 void onPrepareDraws(Target*) const override;
110 void initBatchTracker(const GrXPOverridesForBatch&) override;
111
AAStrokeRectBatch(const SkMatrix & viewMatrix,bool miterStroke)112 AAStrokeRectBatch(const SkMatrix& viewMatrix,bool miterStroke)
113 : INHERITED(ClassID()) {
114 fViewMatrix = viewMatrix;
115 fMiterStroke = miterStroke;
116 }
117
118 static const int kMiterIndexCnt = 3 * 24;
119 static const int kMiterVertexCnt = 16;
120 static const int kNumMiterRectsInIndexBuffer = 256;
121
122 static const int kBevelIndexCnt = 48 + 36 + 24;
123 static const int kBevelVertexCnt = 24;
124 static const int kNumBevelRectsInIndexBuffer = 256;
125
126 static const GrIndexBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider,
127 bool miterStroke);
128
color() const129 GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const130 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
canTweakAlphaForCoverage() const131 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
colorIgnored() const132 bool colorIgnored() const { return fBatch.fColorIgnored; }
coverageIgnored() const133 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
geometry() const134 const Geometry& geometry() const { return fGeoData[0]; }
viewMatrix() const135 const SkMatrix& viewMatrix() const { return fViewMatrix; }
miterStroke() const136 bool miterStroke() const { return fMiterStroke; }
137
138 bool onCombineIfPossible(GrBatch* t, const GrCaps&) override;
139
140 void generateAAStrokeRectGeometry(void* vertices,
141 size_t offset,
142 size_t vertexStride,
143 int outerVertexNum,
144 int innerVertexNum,
145 GrColor color,
146 const SkRect& devOutside,
147 const SkRect& devOutsideAssist,
148 const SkRect& devInside,
149 bool miterStroke,
150 bool degenerate,
151 bool tweakAlphaForCoverage) const;
152
153 struct BatchTracker {
154 GrColor fColor;
155 bool fUsesLocalCoords;
156 bool fColorIgnored;
157 bool fCoverageIgnored;
158 bool fCanTweakAlphaForCoverage;
159 };
160
161 BatchTracker fBatch;
162 SkSTArray<1, Geometry, true> fGeoData;
163 SkMatrix fViewMatrix;
164 bool fMiterStroke;
165
166 typedef GrVertexBatch INHERITED;
167 };
168
initBatchTracker(const GrXPOverridesForBatch & overrides)169 void AAStrokeRectBatch::initBatchTracker(const GrXPOverridesForBatch& overrides) {
170 // Handle any color overrides
171 if (!overrides.readsColor()) {
172 fGeoData[0].fColor = GrColor_ILLEGAL;
173 }
174 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
175
176 // setup batch properties
177 fBatch.fColorIgnored = !overrides.readsColor();
178 fBatch.fColor = fGeoData[0].fColor;
179 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
180 fBatch.fCoverageIgnored = !overrides.readsCoverage();
181 fBatch.fCanTweakAlphaForCoverage = overrides.canTweakAlphaForCoverage();
182 }
183
onPrepareDraws(Target * target) const184 void AAStrokeRectBatch::onPrepareDraws(Target* target) const {
185 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
186
187 SkAutoTUnref<const GrGeometryProcessor> gp(create_stroke_rect_gp(canTweakAlphaForCoverage,
188 this->viewMatrix(),
189 this->usesLocalCoords(),
190 this->coverageIgnored()));
191 if (!gp) {
192 SkDebugf("Couldn't create GrGeometryProcessor\n");
193 return;
194 }
195
196 target->initDraw(gp, this->pipeline());
197
198 size_t vertexStride = gp->getVertexStride();
199
200 SkASSERT(canTweakAlphaForCoverage ?
201 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
202 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
203 int innerVertexNum = 4;
204 int outerVertexNum = this->miterStroke() ? 4 : 8;
205 int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
206 int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
207 int instanceCount = fGeoData.count();
208
209 const SkAutoTUnref<const GrIndexBuffer> indexBuffer(
210 GetIndexBuffer(target->resourceProvider(), this->miterStroke()));
211 InstancedHelper helper;
212 void* vertices = helper.init(target, kTriangles_GrPrimitiveType, vertexStride,
213 indexBuffer, verticesPerInstance, indicesPerInstance,
214 instanceCount);
215 if (!vertices || !indexBuffer) {
216 SkDebugf("Could not allocate vertices\n");
217 return;
218 }
219
220 for (int i = 0; i < instanceCount; i++) {
221 const Geometry& args = fGeoData[i];
222 this->generateAAStrokeRectGeometry(vertices,
223 i * verticesPerInstance * vertexStride,
224 vertexStride,
225 outerVertexNum,
226 innerVertexNum,
227 args.fColor,
228 args.fDevOutside,
229 args.fDevOutsideAssist,
230 args.fDevInside,
231 fMiterStroke,
232 args.fDegenerate,
233 canTweakAlphaForCoverage);
234 }
235 helper.recordDraw(target);
236 }
237
GetIndexBuffer(GrResourceProvider * resourceProvider,bool miterStroke)238 const GrIndexBuffer* AAStrokeRectBatch::GetIndexBuffer(GrResourceProvider* resourceProvider,
239 bool miterStroke) {
240
241 if (miterStroke) {
242 static const uint16_t gMiterIndices[] = {
243 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
244 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
245 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
246 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
247
248 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
249 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
250 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
251 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
252
253 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
254 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
255 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
256 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
257 };
258 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
259 GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
260 return resourceProvider->findOrCreateInstancedIndexBuffer(gMiterIndices,
261 kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
262 gMiterIndexBufferKey);
263 } else {
264 /**
265 * As in miter-stroke, index = a + b, and a is the current index, b is the shift
266 * from the first index. The index layout:
267 * outer AA line: 0~3, 4~7
268 * outer edge: 8~11, 12~15
269 * inner edge: 16~19
270 * inner AA line: 20~23
271 * Following comes a bevel-stroke rect and its indices:
272 *
273 * 4 7
274 * *********************************
275 * * ______________________________ *
276 * * / 12 15 \ *
277 * * / \ *
278 * 0 * |8 16_____________________19 11 | * 3
279 * * | | | | *
280 * * | | **************** | | *
281 * * | | * 20 23 * | | *
282 * * | | * * | | *
283 * * | | * 21 22 * | | *
284 * * | | **************** | | *
285 * * | |____________________| | *
286 * 1 * |9 17 18 10| * 2
287 * * \ / *
288 * * \13 __________________________14/ *
289 * * *
290 * **********************************
291 * 5 6
292 */
293 static const uint16_t gBevelIndices[] = {
294 // Draw outer AA, from outer AA line to outer edge, shift is 0.
295 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
296 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
297 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
298 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
299 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
300 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
301 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
302 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
303
304 // Draw the stroke, from outer edge to inner edge, shift is 8.
305 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
306 1 + 8, 5 + 8, 9 + 8,
307 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
308 6 + 8, 2 + 8, 10 + 8,
309 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
310 3 + 8, 7 + 8, 11 + 8,
311 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
312 4 + 8, 0 + 8, 8 + 8,
313
314 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
315 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
316 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
317 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
318 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
319 };
320 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
321
322 GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
323 return resourceProvider->findOrCreateInstancedIndexBuffer(gBevelIndices,
324 kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
325 gBevelIndexBufferKey);
326 }
327 }
328
onCombineIfPossible(GrBatch * t,const GrCaps & caps)329 bool AAStrokeRectBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) {
330 AAStrokeRectBatch* that = t->cast<AAStrokeRectBatch>();
331
332 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
333 that->bounds(), caps)) {
334 return false;
335 }
336
337 // TODO batch across miterstroke changes
338 if (this->miterStroke() != that->miterStroke()) {
339 return false;
340 }
341
342 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
343 // local coords then we won't be able to batch. We could actually upload the viewmatrix
344 // using vertex attributes in these cases, but haven't investigated that
345 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
346 return false;
347 }
348
349 // In the event of two batches, one who can tweak, one who cannot, we just fall back to
350 // not tweaking
351 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
352 fBatch.fCanTweakAlphaForCoverage = false;
353 }
354
355 if (this->color() != that->color()) {
356 fBatch.fColor = GrColor_ILLEGAL;
357 }
358 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
359 this->joinBounds(that->bounds());
360 return true;
361 }
362
setup_scale(int * scale,SkScalar inset)363 static void setup_scale(int* scale, SkScalar inset) {
364 if (inset < SK_ScalarHalf) {
365 *scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
366 SkASSERT(*scale >= 0 && *scale <= 255);
367 } else {
368 *scale = 0xff;
369 }
370 }
371
generateAAStrokeRectGeometry(void * vertices,size_t offset,size_t vertexStride,int outerVertexNum,int innerVertexNum,GrColor color,const SkRect & devOutside,const SkRect & devOutsideAssist,const SkRect & devInside,bool miterStroke,bool degenerate,bool tweakAlphaForCoverage) const372 void AAStrokeRectBatch::generateAAStrokeRectGeometry(void* vertices,
373 size_t offset,
374 size_t vertexStride,
375 int outerVertexNum,
376 int innerVertexNum,
377 GrColor color,
378 const SkRect& devOutside,
379 const SkRect& devOutsideAssist,
380 const SkRect& devInside,
381 bool miterStroke,
382 bool degenerate,
383 bool tweakAlphaForCoverage) const {
384 intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
385
386 // We create vertices for four nested rectangles. There are two ramps from 0 to full
387 // coverage, one on the exterior of the stroke and the other on the interior.
388 // The following pointers refer to the four rects, from outermost to innermost.
389 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
390 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
391 SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
392 SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(verts +
393 (2 * outerVertexNum + innerVertexNum) *
394 vertexStride);
395
396 #ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
397 // TODO: this only really works if the X & Y margins are the same all around
398 // the rect (or if they are all >= 1.0).
399 SkScalar inset;
400 if (!degenerate) {
401 inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
402 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
403 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
404 if (miterStroke) {
405 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
406 } else {
407 inset = SK_ScalarHalf * SkMinScalar(inset, devOutsideAssist.fBottom -
408 devInside.fBottom);
409 }
410 SkASSERT(inset >= 0);
411 } else {
412 // TODO use real devRect here
413 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
414 inset = SK_ScalarHalf * SkMinScalar(inset, SkTMax(devOutside.height(),
415 devOutsideAssist.height()));
416 }
417 #else
418 SkScalar inset;
419 if (!degenerate) {
420 inset = SK_ScalarHalf;
421 } else {
422 // TODO use real devRect here
423 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
424 inset = SK_ScalarHalf * SkMinScalar(inset, SkTMax(devOutside.height(),
425 devOutsideAssist.height()));
426 }
427 #endif
428
429 if (miterStroke) {
430 // outermost
431 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
432 // inner two
433 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
434 if (!degenerate) {
435 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
436 // innermost
437 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
438 } else {
439 // When the interior rect has become degenerate we smoosh to a single point
440 SkASSERT(devInside.fLeft == devInside.fRight &&
441 devInside.fTop == devInside.fBottom);
442 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop,
443 devInside.fRight, devInside.fBottom, vertexStride);
444 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop,
445 devInside.fRight, devInside.fBottom, vertexStride);
446 }
447 } else {
448 SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
449 SkPoint* fan1AssistPos = reinterpret_cast<SkPoint*>(verts +
450 (outerVertexNum + 4) *
451 vertexStride);
452 // outermost
453 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
454 set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
455 -SK_ScalarHalf);
456 // outer one of the inner two
457 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
458 set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist, inset, inset);
459 if (!degenerate) {
460 // inner one of the inner two
461 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
462 // innermost
463 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
464 } else {
465 // When the interior rect has become degenerate we smoosh to a single point
466 SkASSERT(devInside.fLeft == devInside.fRight &&
467 devInside.fTop == devInside.fBottom);
468 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop,
469 devInside.fRight, devInside.fBottom, vertexStride);
470 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop,
471 devInside.fRight, devInside.fBottom, vertexStride);
472 }
473 }
474
475 // Make verts point to vertex color and then set all the color and coverage vertex attrs
476 // values. The outermost rect has 0 coverage
477 verts += sizeof(SkPoint);
478 for (int i = 0; i < outerVertexNum; ++i) {
479 if (tweakAlphaForCoverage) {
480 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
481 } else {
482 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
483 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
484 }
485 }
486
487 // scale is the coverage for the the inner two rects.
488 int scale;
489 setup_scale(&scale, inset);
490
491 float innerCoverage = GrNormalizeByteToFloat(scale);
492 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
493
494 verts += outerVertexNum * vertexStride;
495 for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
496 if (tweakAlphaForCoverage) {
497 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
498 } else {
499 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
500 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
501 }
502 }
503
504 // The innermost rect has 0 coverage, unless we are degenerate, in which case we must apply the
505 // scaled coverage
506 verts += (outerVertexNum + innerVertexNum) * vertexStride;
507 if (!degenerate) {
508 innerCoverage = 0;
509 scaledColor = 0;
510 }
511
512 for (int i = 0; i < innerVertexNum; ++i) {
513 if (tweakAlphaForCoverage) {
514 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
515 } else {
516 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
517 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
518 }
519 }
520 }
521
is_miter(const SkStrokeRec & stroke)522 inline static bool is_miter(const SkStrokeRec& stroke) {
523 // For hairlines, make bevel and round joins appear the same as mitered ones.
524 // small miter limit means right angles show bevel...
525 if ((stroke.getWidth() > 0) && (stroke.getJoin() != SkPaint::kMiter_Join ||
526 stroke.getMiter() < SK_ScalarSqrt2)) {
527 return false;
528 }
529 return true;
530 }
531
compute_rects(SkRect * devOutside,SkRect * devOutsideAssist,SkRect * devInside,bool * isDegenerate,const SkMatrix & viewMatrix,const SkRect & rect,SkScalar strokeWidth,bool miterStroke)532 static void compute_rects(SkRect* devOutside, SkRect* devOutsideAssist, SkRect* devInside,
533 bool* isDegenerate, const SkMatrix& viewMatrix, const SkRect& rect,
534 SkScalar strokeWidth, bool miterStroke) {
535 SkRect devRect;
536 viewMatrix.mapRect(&devRect, rect);
537
538 SkVector devStrokeSize;
539 if (strokeWidth > 0) {
540 devStrokeSize.set(strokeWidth, strokeWidth);
541 viewMatrix.mapVectors(&devStrokeSize, 1);
542 devStrokeSize.setAbs(devStrokeSize);
543 } else {
544 devStrokeSize.set(SK_Scalar1, SK_Scalar1);
545 }
546
547 const SkScalar dx = devStrokeSize.fX;
548 const SkScalar dy = devStrokeSize.fY;
549 const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf);
550 const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf);
551
552 *devOutside = devRect;
553 *devOutsideAssist = devRect;
554 *devInside = devRect;
555
556 devOutside->outset(rx, ry);
557 devInside->inset(rx, ry);
558
559 // If we have a degenerate stroking rect(ie the stroke is larger than inner rect) then we
560 // make a degenerate inside rect to avoid double hitting. We will also jam all of the points
561 // together when we render these rects.
562 SkScalar spare;
563 {
564 SkScalar w = devRect.width() - dx;
565 SkScalar h = devRect.height() - dy;
566 spare = SkTMin(w, h);
567 }
568
569 *isDegenerate = spare <= 0;
570 if (*isDegenerate) {
571 devInside->fLeft = devInside->fRight = devRect.centerX();
572 devInside->fTop = devInside->fBottom = devRect.centerY();
573 }
574
575 // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
576 // to draw the outside of the octagon. Because there are 8 vertices on the outer
577 // edge, while vertex number of inner edge is 4, the same as miter-stroke.
578 if (!miterStroke) {
579 devOutside->inset(0, ry);
580 devOutsideAssist->outset(0, ry);
581 }
582 }
583
584 namespace GrAAStrokeRectBatch {
585
Create(GrColor color,const SkMatrix & viewMatrix,const SkRect & devOutside,const SkRect & devOutsideAssist,const SkRect & devInside,bool miterStroke,bool degenerate)586 GrDrawBatch* Create(GrColor color,
587 const SkMatrix& viewMatrix,
588 const SkRect& devOutside,
589 const SkRect& devOutsideAssist,
590 const SkRect& devInside,
591 bool miterStroke,
592 bool degenerate) {
593 AAStrokeRectBatch* batch = AAStrokeRectBatch::Create(viewMatrix, miterStroke);
594 batch->append(color, devOutside, devOutsideAssist, devInside, degenerate);
595 batch->init();
596 return batch;
597 }
598
Create(GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkStrokeRec & stroke)599 GrDrawBatch* Create(GrColor color,
600 const SkMatrix& viewMatrix,
601 const SkRect& rect,
602 const SkStrokeRec& stroke) {
603 bool isMiterStroke = is_miter(stroke);
604 AAStrokeRectBatch* batch = AAStrokeRectBatch::Create(viewMatrix, isMiterStroke);
605
606 SkRect devOutside, devOutsideAssist, devInside;
607 bool isDegenerate;
608 compute_rects(&devOutside, &devOutsideAssist, &devInside, &isDegenerate, viewMatrix,
609 rect, stroke.getWidth(), isMiterStroke);
610
611 batch->append(color, devOutside, devOutsideAssist, devInside, isDegenerate);
612 batch->init();
613 return batch;
614 }
615
Append(GrBatch * origBatch,GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkStrokeRec & stroke)616 bool Append(GrBatch* origBatch,
617 GrColor color,
618 const SkMatrix& viewMatrix,
619 const SkRect& rect,
620 const SkStrokeRec& stroke) {
621 AAStrokeRectBatch* batch = origBatch->cast<AAStrokeRectBatch>();
622
623 // we can't batch across vm changes
624 bool isMiterStroke = is_miter(stroke);
625 if (!batch->canAppend(viewMatrix, isMiterStroke)) {
626 return false;
627 }
628
629 SkRect devOutside, devOutsideAssist, devInside;
630 bool isDegenerate;
631 compute_rects(&devOutside, &devOutsideAssist, &devInside, &isDegenerate, viewMatrix,
632 rect, stroke.getWidth(), isMiterStroke);
633
634 batch->appendAndUpdateBounds(color, devOutside, devOutsideAssist, devInside, isDegenerate);
635 return true;
636 }
637
638 };
639
640 ///////////////////////////////////////////////////////////////////////////////////////////////////
641
642 #ifdef GR_TEST_UTILS
643
644 #include "GrBatchTest.h"
645
DRAW_BATCH_TEST_DEFINE(AAStrokeRectBatch)646 DRAW_BATCH_TEST_DEFINE(AAStrokeRectBatch) {
647 bool miterStroke = random->nextBool();
648
649 // Create mock stroke rect
650 SkRect outside = GrTest::TestRect(random);
651 SkScalar minDim = SkMinScalar(outside.width(), outside.height());
652 SkScalar strokeWidth = minDim * 0.1f;
653 SkRect outsideAssist = outside;
654 outsideAssist.outset(strokeWidth, strokeWidth);
655 SkRect inside = outside;
656 inside.inset(strokeWidth, strokeWidth);
657
658 GrColor color = GrRandomColor(random);
659
660 return GrAAStrokeRectBatch::Create(color, GrTest::TestMatrix(random), outside, outsideAssist,
661 inside, miterStroke, inside.isFinite() && inside.isEmpty());
662 }
663
664 #endif
665