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