1 /*
2  * Copyright 2012 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "GrAARectRenderer.h"
9 #include "GrBatch.h"
10 #include "GrBatchTarget.h"
11 #include "GrBatchTest.h"
12 #include "GrContext.h"
13 #include "GrDefaultGeoProcFactory.h"
14 #include "GrGeometryProcessor.h"
15 #include "GrInvariantOutput.h"
16 #include "GrResourceKey.h"
17 #include "GrResourceProvider.h"
18 #include "GrTestUtils.h"
19 #include "GrVertexBuffer.h"
20 #include "SkColorPriv.h"
21 #include "gl/GrGLProcessor.h"
22 #include "gl/GrGLGeometryProcessor.h"
23 #include "gl/builders/GrGLProgramBuilder.h"
24 
25 ///////////////////////////////////////////////////////////////////////////////
26 
set_inset_fan(SkPoint * pts,size_t stride,const SkRect & r,SkScalar dx,SkScalar dy)27 static void set_inset_fan(SkPoint* pts, size_t stride,
28                           const SkRect& r, SkScalar dx, SkScalar dy) {
29     pts->setRectFan(r.fLeft + dx, r.fTop + dy,
30                     r.fRight - dx, r.fBottom - dy, stride);
31 }
32 
create_fill_rect_gp(bool tweakAlphaForCoverage,const SkMatrix & localMatrix)33 static const GrGeometryProcessor* create_fill_rect_gp(bool tweakAlphaForCoverage,
34                                                       const SkMatrix& localMatrix) {
35     uint32_t flags = GrDefaultGeoProcFactory::kColor_GPType;
36     const GrGeometryProcessor* gp;
37     if (tweakAlphaForCoverage) {
38         gp = GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, SkMatrix::I(), localMatrix);
39     } else {
40         flags |= GrDefaultGeoProcFactory::kCoverage_GPType;
41         gp = GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, SkMatrix::I(), localMatrix);
42     }
43     return gp;
44 }
45 
46 GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
47 
48 class AAFillRectBatch : public GrBatch {
49 public:
50     struct Geometry {
51         GrColor fColor;
52         SkMatrix fViewMatrix;
53         SkRect fRect;
54         SkRect fDevRect;
55     };
56 
Create(const Geometry & geometry)57     static GrBatch* Create(const Geometry& geometry) {
58         return SkNEW_ARGS(AAFillRectBatch, (geometry));
59     }
60 
name() const61     const char* name() const override { return "AAFillRectBatch"; }
62 
getInvariantOutputColor(GrInitInvariantOutput * out) const63     void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
64         // When this is called on a batch, there is only one geometry bundle
65         out->setKnownFourComponents(fGeoData[0].fColor);
66     }
67 
getInvariantOutputCoverage(GrInitInvariantOutput * out) const68     void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
69         out->setUnknownSingleComponent();
70     }
71 
initBatchTracker(const GrPipelineInfo & init)72     void initBatchTracker(const GrPipelineInfo& init) override {
73         // Handle any color overrides
74         if (init.fColorIgnored) {
75             fGeoData[0].fColor = GrColor_ILLEGAL;
76         } else if (GrColor_ILLEGAL != init.fOverrideColor) {
77             fGeoData[0].fColor = init.fOverrideColor;
78         }
79 
80         // setup batch properties
81         fBatch.fColorIgnored = init.fColorIgnored;
82         fBatch.fColor = fGeoData[0].fColor;
83         fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
84         fBatch.fCoverageIgnored = init.fCoverageIgnored;
85         fBatch.fCanTweakAlphaForCoverage = init.fCanTweakAlphaForCoverage;
86     }
87 
generateGeometry(GrBatchTarget * batchTarget,const GrPipeline * pipeline)88     void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
89         bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
90 
91         SkMatrix localMatrix;
92         if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
93             SkDebugf("Cannot invert\n");
94             return;
95         }
96 
97         SkAutoTUnref<const GrGeometryProcessor> gp(create_fill_rect_gp(canTweakAlphaForCoverage,
98                                                                        localMatrix));
99 
100         batchTarget->initDraw(gp, pipeline);
101 
102         // TODO this is hacky, but the only way we have to initialize the GP is to use the
103         // GrPipelineInfo struct so we can generate the correct shader.  Once we have GrBatch
104         // everywhere we can remove this nastiness
105         GrPipelineInfo init;
106         init.fColorIgnored = fBatch.fColorIgnored;
107         init.fOverrideColor = GrColor_ILLEGAL;
108         init.fCoverageIgnored = fBatch.fCoverageIgnored;
109         init.fUsesLocalCoords = this->usesLocalCoords();
110         gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
111 
112         size_t vertexStride = gp->getVertexStride();
113         SkASSERT(canTweakAlphaForCoverage ?
114                  vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
115                  vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
116         int instanceCount = fGeoData.count();
117 
118         SkAutoTUnref<const GrIndexBuffer> indexBuffer(this->getIndexBuffer(
119             batchTarget->resourceProvider()));
120         InstancedHelper helper;
121         void* vertices = helper.init(batchTarget, kTriangles_GrPrimitiveType, vertexStride,
122                                      indexBuffer, kVertsPerAAFillRect, kIndicesPerAAFillRect,
123                                      instanceCount);
124         if (!vertices || !indexBuffer) {
125             SkDebugf("Could not allocate vertices\n");
126             return;
127         }
128 
129         for (int i = 0; i < instanceCount; i++) {
130             const Geometry& args = fGeoData[i];
131             this->generateAAFillRectGeometry(vertices,
132                                              i * kVertsPerAAFillRect * vertexStride,
133                                              vertexStride,
134                                              args.fColor,
135                                              args.fViewMatrix,
136                                              args.fRect,
137                                              args.fDevRect,
138                                              canTweakAlphaForCoverage);
139         }
140 
141         helper.issueDraw(batchTarget);
142     }
143 
geoData()144     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
145 
146 private:
AAFillRectBatch(const Geometry & geometry)147     AAFillRectBatch(const Geometry& geometry) {
148         this->initClassID<AAFillRectBatch>();
149         fGeoData.push_back(geometry);
150 
151         this->setBounds(geometry.fDevRect);
152     }
153 
154     static const int kNumAAFillRectsInIndexBuffer = 256;
155     static const int kVertsPerAAFillRect = 8;
156     static const int kIndicesPerAAFillRect = 30;
157 
getIndexBuffer(GrResourceProvider * resourceProvider)158     const GrIndexBuffer* getIndexBuffer(GrResourceProvider* resourceProvider) {
159         GR_DEFINE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
160 
161         static const uint16_t gFillAARectIdx[] = {
162             0, 1, 5, 5, 4, 0,
163             1, 2, 6, 6, 5, 1,
164             2, 3, 7, 7, 6, 2,
165             3, 0, 4, 4, 7, 3,
166             4, 5, 6, 6, 7, 4,
167         };
168         GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect);
169         return resourceProvider->refOrCreateInstancedIndexBuffer(gFillAARectIdx,
170             kIndicesPerAAFillRect, kNumAAFillRectsInIndexBuffer, kVertsPerAAFillRect,
171             gAAFillRectIndexBufferKey);
172     }
173 
color() const174     GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const175     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
canTweakAlphaForCoverage() const176     bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
colorIgnored() const177     bool colorIgnored() const { return fBatch.fColorIgnored; }
viewMatrix() const178     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
179 
onCombineIfPossible(GrBatch * t)180     bool onCombineIfPossible(GrBatch* t) override {
181         AAFillRectBatch* that = t->cast<AAFillRectBatch>();
182 
183         SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
184         // We apply the viewmatrix to the rect points on the cpu.  However, if the pipeline uses
185         // local coords then we won't be able to batch.  We could actually upload the viewmatrix
186         // using vertex attributes in these cases, but haven't investigated that
187         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
188             return false;
189         }
190 
191         if (this->color() != that->color()) {
192             fBatch.fColor = GrColor_ILLEGAL;
193         }
194 
195         // In the event of two batches, one who can tweak, one who cannot, we just fall back to
196         // not tweaking
197         if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
198             fBatch.fCanTweakAlphaForCoverage = false;
199         }
200 
201         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
202         this->joinBounds(that->bounds());
203         return true;
204     }
205 
generateAAFillRectGeometry(void * vertices,size_t offset,size_t vertexStride,GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect & devRect,bool tweakAlphaForCoverage) const206     void generateAAFillRectGeometry(void* vertices,
207                                     size_t offset,
208                                     size_t vertexStride,
209                                     GrColor color,
210                                     const SkMatrix& viewMatrix,
211                                     const SkRect& rect,
212                                     const SkRect& devRect,
213                                     bool tweakAlphaForCoverage) const {
214         intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
215 
216         SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
217         SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
218 
219         SkScalar inset = SkMinScalar(devRect.width(), SK_Scalar1);
220         inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height());
221 
222         if (viewMatrix.rectStaysRect()) {
223             set_inset_fan(fan0Pos, vertexStride, devRect, -SK_ScalarHalf, -SK_ScalarHalf);
224             set_inset_fan(fan1Pos, vertexStride, devRect, inset,  inset);
225         } else {
226             // compute transformed (1, 0) and (0, 1) vectors
227             SkVector vec[2] = {
228               { viewMatrix[SkMatrix::kMScaleX], viewMatrix[SkMatrix::kMSkewY] },
229               { viewMatrix[SkMatrix::kMSkewX],  viewMatrix[SkMatrix::kMScaleY] }
230             };
231 
232             vec[0].normalize();
233             vec[0].scale(SK_ScalarHalf);
234             vec[1].normalize();
235             vec[1].scale(SK_ScalarHalf);
236 
237             // create the rotated rect
238             fan0Pos->setRectFan(rect.fLeft, rect.fTop,
239                                 rect.fRight, rect.fBottom, vertexStride);
240             viewMatrix.mapPointsWithStride(fan0Pos, vertexStride, 4);
241 
242             // Now create the inset points and then outset the original
243             // rotated points
244 
245             // TL
246             *((SkPoint*)((intptr_t)fan1Pos + 0 * vertexStride)) =
247                 *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) + vec[0] + vec[1];
248             *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) -= vec[0] + vec[1];
249             // BL
250             *((SkPoint*)((intptr_t)fan1Pos + 1 * vertexStride)) =
251                 *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) + vec[0] - vec[1];
252             *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) -= vec[0] - vec[1];
253             // BR
254             *((SkPoint*)((intptr_t)fan1Pos + 2 * vertexStride)) =
255                 *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) - vec[0] - vec[1];
256             *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) += vec[0] + vec[1];
257             // TR
258             *((SkPoint*)((intptr_t)fan1Pos + 3 * vertexStride)) =
259                 *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) - vec[0] + vec[1];
260             *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) += vec[0] - vec[1];
261         }
262 
263         // Make verts point to vertex color and then set all the color and coverage vertex attrs
264         // values.
265         verts += sizeof(SkPoint);
266         for (int i = 0; i < 4; ++i) {
267             if (tweakAlphaForCoverage) {
268                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
269             } else {
270                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
271                 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
272             }
273         }
274 
275         int scale;
276         if (inset < SK_ScalarHalf) {
277             scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
278             SkASSERT(scale >= 0 && scale <= 255);
279         } else {
280             scale = 0xff;
281         }
282 
283         verts += 4 * vertexStride;
284 
285         float innerCoverage = GrNormalizeByteToFloat(scale);
286         GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
287 
288         for (int i = 0; i < 4; ++i) {
289             if (tweakAlphaForCoverage) {
290                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
291             } else {
292                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
293                 *reinterpret_cast<float*>(verts + i * vertexStride +
294                                           sizeof(GrColor)) = innerCoverage;
295             }
296         }
297     }
298 
299     struct BatchTracker {
300         GrColor fColor;
301         bool fUsesLocalCoords;
302         bool fColorIgnored;
303         bool fCoverageIgnored;
304         bool fCanTweakAlphaForCoverage;
305     };
306 
307     BatchTracker fBatch;
308     SkSTArray<1, Geometry, true> fGeoData;
309 };
310 
311 namespace {
312 // Should the coverage be multiplied into the color attrib or use a separate attrib.
313 enum CoverageAttribType {
314     kUseColor_CoverageAttribType,
315     kUseCoverage_CoverageAttribType,
316 };
317 }
318 
geometryFillAARect(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect & devRect)319 void GrAARectRenderer::geometryFillAARect(GrDrawTarget* target,
320                                           GrPipelineBuilder* pipelineBuilder,
321                                           GrColor color,
322                                           const SkMatrix& viewMatrix,
323                                           const SkRect& rect,
324                                           const SkRect& devRect) {
325     AAFillRectBatch::Geometry geometry;
326     geometry.fRect = rect;
327     geometry.fViewMatrix = viewMatrix;
328     geometry.fDevRect = devRect;
329     geometry.fColor = color;
330 
331 
332     SkAutoTUnref<GrBatch> batch(AAFillRectBatch::Create(geometry));
333     target->drawBatch(pipelineBuilder, batch);
334 }
335 
strokeAARect(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect & devRect,const SkStrokeRec & stroke)336 void GrAARectRenderer::strokeAARect(GrDrawTarget* target,
337                                     GrPipelineBuilder* pipelineBuilder,
338                                     GrColor color,
339                                     const SkMatrix& viewMatrix,
340                                     const SkRect& rect,
341                                     const SkRect& devRect,
342                                     const SkStrokeRec& stroke) {
343     SkVector devStrokeSize;
344     SkScalar width = stroke.getWidth();
345     if (width > 0) {
346         devStrokeSize.set(width, width);
347         viewMatrix.mapVectors(&devStrokeSize, 1);
348         devStrokeSize.setAbs(devStrokeSize);
349     } else {
350         devStrokeSize.set(SK_Scalar1, SK_Scalar1);
351     }
352 
353     const SkScalar dx = devStrokeSize.fX;
354     const SkScalar dy = devStrokeSize.fY;
355     const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf);
356     const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf);
357 
358     SkScalar spare;
359     {
360         SkScalar w = devRect.width() - dx;
361         SkScalar h = devRect.height() - dy;
362         spare = SkTMin(w, h);
363     }
364 
365     SkRect devOutside(devRect);
366     devOutside.outset(rx, ry);
367 
368     bool miterStroke = true;
369     // For hairlines, make bevel and round joins appear the same as mitered ones.
370     // small miter limit means right angles show bevel...
371     if ((width > 0) && (stroke.getJoin() != SkPaint::kMiter_Join ||
372                         stroke.getMiter() < SK_ScalarSqrt2)) {
373         miterStroke = false;
374     }
375 
376     if (spare <= 0 && miterStroke) {
377         this->fillAARect(target, pipelineBuilder, color, viewMatrix, devOutside, devOutside);
378         return;
379     }
380 
381     SkRect devInside(devRect);
382     devInside.inset(rx, ry);
383 
384     SkRect devOutsideAssist(devRect);
385 
386     // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
387     // to draw the outer of the rect. Because there are 8 vertices on the outer
388     // edge, while vertex number of inner edge is 4, the same as miter-stroke.
389     if (!miterStroke) {
390         devOutside.inset(0, ry);
391         devOutsideAssist.outset(0, ry);
392     }
393 
394     this->geometryStrokeAARect(target, pipelineBuilder, color, viewMatrix, devOutside,
395                                devOutsideAssist, devInside, miterStroke);
396 }
397 
398 GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
399 GR_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
400 
401 class AAStrokeRectBatch : public GrBatch {
402 public:
403     // TODO support AA rotated stroke rects by copying around view matrices
404     struct Geometry {
405         GrColor fColor;
406         SkRect fDevOutside;
407         SkRect fDevOutsideAssist;
408         SkRect fDevInside;
409         bool fMiterStroke;
410     };
411 
Create(const Geometry & geometry,const SkMatrix & viewMatrix)412     static GrBatch* Create(const Geometry& geometry, const SkMatrix& viewMatrix) {
413         return SkNEW_ARGS(AAStrokeRectBatch, (geometry, viewMatrix));
414     }
415 
name() const416     const char* name() const override { return "AAStrokeRect"; }
417 
getInvariantOutputColor(GrInitInvariantOutput * out) const418     void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
419         // When this is called on a batch, there is only one geometry bundle
420         out->setKnownFourComponents(fGeoData[0].fColor);
421     }
422 
getInvariantOutputCoverage(GrInitInvariantOutput * out) const423     void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
424         out->setUnknownSingleComponent();
425     }
426 
initBatchTracker(const GrPipelineInfo & init)427     void initBatchTracker(const GrPipelineInfo& init) override {
428         // Handle any color overrides
429         if (init.fColorIgnored) {
430             fGeoData[0].fColor = GrColor_ILLEGAL;
431         } else if (GrColor_ILLEGAL != init.fOverrideColor) {
432             fGeoData[0].fColor = init.fOverrideColor;
433         }
434 
435         // setup batch properties
436         fBatch.fColorIgnored = init.fColorIgnored;
437         fBatch.fColor = fGeoData[0].fColor;
438         fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
439         fBatch.fCoverageIgnored = init.fCoverageIgnored;
440         fBatch.fMiterStroke = fGeoData[0].fMiterStroke;
441         fBatch.fCanTweakAlphaForCoverage = init.fCanTweakAlphaForCoverage;
442     }
443 
generateGeometry(GrBatchTarget * batchTarget,const GrPipeline * pipeline)444     void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
445         bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
446 
447         // Local matrix is ignored if we don't have local coords.  If we have localcoords we only
448         // batch with identical view matrices
449         SkMatrix localMatrix;
450         if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
451             SkDebugf("Cannot invert\n");
452             return;
453         }
454 
455         SkAutoTUnref<const GrGeometryProcessor> gp(create_fill_rect_gp(canTweakAlphaForCoverage,
456                                                                        localMatrix));
457 
458         batchTarget->initDraw(gp, pipeline);
459 
460         // TODO this is hacky, but the only way we have to initialize the GP is to use the
461         // GrPipelineInfo struct so we can generate the correct shader.  Once we have GrBatch
462         // everywhere we can remove this nastiness
463         GrPipelineInfo init;
464         init.fColorIgnored = fBatch.fColorIgnored;
465         init.fOverrideColor = GrColor_ILLEGAL;
466         init.fCoverageIgnored = fBatch.fCoverageIgnored;
467         init.fUsesLocalCoords = this->usesLocalCoords();
468         gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
469 
470         size_t vertexStride = gp->getVertexStride();
471 
472         SkASSERT(canTweakAlphaForCoverage ?
473                  vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
474                  vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
475         int innerVertexNum = 4;
476         int outerVertexNum = this->miterStroke() ? 4 : 8;
477         int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
478         int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
479         int instanceCount = fGeoData.count();
480 
481         const SkAutoTUnref<const GrIndexBuffer> indexBuffer(
482             GetIndexBuffer(batchTarget->resourceProvider(), this->miterStroke()));
483         InstancedHelper helper;
484         void* vertices = helper.init(batchTarget, kTriangles_GrPrimitiveType, vertexStride,
485                                      indexBuffer, verticesPerInstance,  indicesPerInstance,
486                                      instanceCount);
487         if (!vertices || !indexBuffer) {
488              SkDebugf("Could not allocate vertices\n");
489              return;
490          }
491 
492         for (int i = 0; i < instanceCount; i++) {
493             const Geometry& args = fGeoData[i];
494             this->generateAAStrokeRectGeometry(vertices,
495                                                i * verticesPerInstance * vertexStride,
496                                                vertexStride,
497                                                outerVertexNum,
498                                                innerVertexNum,
499                                                args.fColor,
500                                                args.fDevOutside,
501                                                args.fDevOutsideAssist,
502                                                args.fDevInside,
503                                                args.fMiterStroke,
504                                                canTweakAlphaForCoverage);
505         }
506         helper.issueDraw(batchTarget);
507     }
508 
geoData()509     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
510 
511 private:
AAStrokeRectBatch(const Geometry & geometry,const SkMatrix & viewMatrix)512     AAStrokeRectBatch(const Geometry& geometry, const SkMatrix& viewMatrix)  {
513         this->initClassID<AAStrokeRectBatch>();
514         fBatch.fViewMatrix = viewMatrix;
515         fGeoData.push_back(geometry);
516 
517         // If we have miterstroke then we inset devOutside and outset devOutsideAssist, so we need
518         // the join for proper bounds
519         fBounds = geometry.fDevOutside;
520         fBounds.join(geometry.fDevOutsideAssist);
521     }
522 
523 
524     static const int kMiterIndexCnt = 3 * 24;
525     static const int kMiterVertexCnt = 16;
526     static const int kNumMiterRectsInIndexBuffer = 256;
527 
528     static const int kBevelIndexCnt = 48 + 36 + 24;
529     static const int kBevelVertexCnt = 24;
530     static const int kNumBevelRectsInIndexBuffer = 256;
531 
GetIndexBuffer(GrResourceProvider * resourceProvider,bool miterStroke)532     static const GrIndexBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider,
533                                                bool miterStroke) {
534 
535         if (miterStroke) {
536             static const uint16_t gMiterIndices[] = {
537                 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
538                 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
539                 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
540                 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
541 
542                 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
543                 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
544                 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
545                 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
546 
547                 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
548                 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
549                 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
550                 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
551             };
552             GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
553             GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
554             return resourceProvider->refOrCreateInstancedIndexBuffer(gMiterIndices,
555                 kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
556                 gMiterIndexBufferKey);
557         } else {
558             /**
559              * As in miter-stroke, index = a + b, and a is the current index, b is the shift
560              * from the first index. The index layout:
561              * outer AA line: 0~3, 4~7
562              * outer edge:    8~11, 12~15
563              * inner edge:    16~19
564              * inner AA line: 20~23
565              * Following comes a bevel-stroke rect and its indices:
566              *
567              *           4                                 7
568              *            *********************************
569              *          *   ______________________________  *
570              *         *  / 12                          15 \  *
571              *        *  /                                  \  *
572              *     0 *  |8     16_____________________19  11 |  * 3
573              *       *  |       |                    |       |  *
574              *       *  |       |  ****************  |       |  *
575              *       *  |       |  * 20        23 *  |       |  *
576              *       *  |       |  *              *  |       |  *
577              *       *  |       |  * 21        22 *  |       |  *
578              *       *  |       |  ****************  |       |  *
579              *       *  |       |____________________|       |  *
580              *     1 *  |9    17                      18   10|  * 2
581              *        *  \                                  /  *
582              *         *  \13 __________________________14/  *
583              *          *                                   *
584              *           **********************************
585              *          5                                  6
586              */
587             static const uint16_t gBevelIndices[] = {
588                 // Draw outer AA, from outer AA line to outer edge, shift is 0.
589                 0 + 0, 1 + 0,  9 + 0,  9 + 0,  8 + 0, 0 + 0,
590                 1 + 0, 5 + 0, 13 + 0, 13 + 0,  9 + 0, 1 + 0,
591                 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
592                 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
593                 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
594                 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
595                 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
596                 4 + 0, 0 + 0,  8 + 0,  8 + 0, 12 + 0, 4 + 0,
597 
598                 // Draw the stroke, from outer edge to inner edge, shift is 8.
599                 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
600                 1 + 8, 5 + 8, 9 + 8,
601                 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
602                 6 + 8, 2 + 8, 10 + 8,
603                 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
604                 3 + 8, 7 + 8, 11 + 8,
605                 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
606                 4 + 8, 0 + 8, 8 + 8,
607 
608                 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
609                 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
610                 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
611                 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
612                 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
613             };
614             GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
615 
616             GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
617             return resourceProvider->refOrCreateInstancedIndexBuffer(gBevelIndices,
618                 kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
619                 gBevelIndexBufferKey);
620         }
621     }
622 
color() const623     GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const624     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
canTweakAlphaForCoverage() const625     bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
colorIgnored() const626     bool colorIgnored() const { return fBatch.fColorIgnored; }
viewMatrix() const627     const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
miterStroke() const628     bool miterStroke() const { return fBatch.fMiterStroke; }
629 
onCombineIfPossible(GrBatch * t)630     bool onCombineIfPossible(GrBatch* t) override {
631         AAStrokeRectBatch* that = t->cast<AAStrokeRectBatch>();
632 
633         // TODO batch across miterstroke changes
634         if (this->miterStroke() != that->miterStroke()) {
635             return false;
636         }
637 
638         // We apply the viewmatrix to the rect points on the cpu.  However, if the pipeline uses
639         // local coords then we won't be able to batch.  We could actually upload the viewmatrix
640         // using vertex attributes in these cases, but haven't investigated that
641         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
642             return false;
643         }
644 
645         // In the event of two batches, one who can tweak, one who cannot, we just fall back to
646         // not tweaking
647         if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
648             fBatch.fCanTweakAlphaForCoverage = false;
649         }
650 
651         if (this->color() != that->color()) {
652             fBatch.fColor = GrColor_ILLEGAL;
653         }
654         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
655         this->joinBounds(that->bounds());
656         return true;
657     }
658 
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 tweakAlphaForCoverage) const659     void generateAAStrokeRectGeometry(void* vertices,
660                                       size_t offset,
661                                       size_t vertexStride,
662                                       int outerVertexNum,
663                                       int innerVertexNum,
664                                       GrColor color,
665                                       const SkRect& devOutside,
666                                       const SkRect& devOutsideAssist,
667                                       const SkRect& devInside,
668                                       bool miterStroke,
669                                       bool tweakAlphaForCoverage) const {
670         intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
671 
672         // We create vertices for four nested rectangles. There are two ramps from 0 to full
673         // coverage, one on the exterior of the stroke and the other on the interior.
674         // The following pointers refer to the four rects, from outermost to innermost.
675         SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
676         SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
677         SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
678         SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(verts +
679                                                       (2 * outerVertexNum + innerVertexNum) *
680                                                       vertexStride);
681 
682     #ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
683         // TODO: this only really works if the X & Y margins are the same all around
684         // the rect (or if they are all >= 1.0).
685         SkScalar inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
686         inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
687         inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
688         if (miterStroke) {
689             inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
690         } else {
691             inset = SK_ScalarHalf * SkMinScalar(inset, devOutsideAssist.fBottom -
692                                                        devInside.fBottom);
693         }
694         SkASSERT(inset >= 0);
695     #else
696         SkScalar inset = SK_ScalarHalf;
697     #endif
698 
699         if (miterStroke) {
700             // outermost
701             set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
702             // inner two
703             set_inset_fan(fan1Pos, vertexStride, devOutside,  inset,  inset);
704             set_inset_fan(fan2Pos, vertexStride, devInside,  -inset, -inset);
705             // innermost
706             set_inset_fan(fan3Pos, vertexStride, devInside,   SK_ScalarHalf,  SK_ScalarHalf);
707         } else {
708             SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
709             SkPoint* fan1AssistPos = reinterpret_cast<SkPoint*>(verts +
710                                                                 (outerVertexNum + 4) *
711                                                                 vertexStride);
712             // outermost
713             set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
714             set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
715                           -SK_ScalarHalf);
716             // outer one of the inner two
717             set_inset_fan(fan1Pos, vertexStride, devOutside,  inset,  inset);
718             set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist,  inset,  inset);
719             // inner one of the inner two
720             set_inset_fan(fan2Pos, vertexStride, devInside,  -inset, -inset);
721             // innermost
722             set_inset_fan(fan3Pos, vertexStride, devInside,   SK_ScalarHalf,  SK_ScalarHalf);
723         }
724 
725         // Make verts point to vertex color and then set all the color and coverage vertex attrs
726         // values. The outermost rect has 0 coverage
727         verts += sizeof(SkPoint);
728         for (int i = 0; i < outerVertexNum; ++i) {
729             if (tweakAlphaForCoverage) {
730                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
731             } else {
732                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
733                 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
734             }
735         }
736 
737         // scale is the coverage for the the inner two rects.
738         int scale;
739         if (inset < SK_ScalarHalf) {
740             scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
741             SkASSERT(scale >= 0 && scale <= 255);
742         } else {
743             scale = 0xff;
744         }
745 
746         float innerCoverage = GrNormalizeByteToFloat(scale);
747         GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
748 
749         verts += outerVertexNum * vertexStride;
750         for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
751             if (tweakAlphaForCoverage) {
752                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
753             } else {
754                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
755                 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
756                         innerCoverage;
757             }
758         }
759 
760         // The innermost rect has 0 coverage
761         verts += (outerVertexNum + innerVertexNum) * vertexStride;
762         for (int i = 0; i < innerVertexNum; ++i) {
763             if (tweakAlphaForCoverage) {
764                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
765             } else {
766                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
767                 *reinterpret_cast<GrColor*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
768             }
769         }
770     }
771 
772     struct BatchTracker {
773         SkMatrix fViewMatrix;
774         GrColor fColor;
775         bool fUsesLocalCoords;
776         bool fColorIgnored;
777         bool fCoverageIgnored;
778         bool fMiterStroke;
779         bool fCanTweakAlphaForCoverage;
780     };
781 
782     BatchTracker fBatch;
783     SkSTArray<1, Geometry, true> fGeoData;
784 };
785 
geometryStrokeAARect(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,const SkRect & devOutside,const SkRect & devOutsideAssist,const SkRect & devInside,bool miterStroke)786 void GrAARectRenderer::geometryStrokeAARect(GrDrawTarget* target,
787                                             GrPipelineBuilder* pipelineBuilder,
788                                             GrColor color,
789                                             const SkMatrix& viewMatrix,
790                                             const SkRect& devOutside,
791                                             const SkRect& devOutsideAssist,
792                                             const SkRect& devInside,
793                                             bool miterStroke) {
794     AAStrokeRectBatch::Geometry geometry;
795     geometry.fColor = color;
796     geometry.fDevOutside = devOutside;
797     geometry.fDevOutsideAssist = devOutsideAssist;
798     geometry.fDevInside = devInside;
799     geometry.fMiterStroke = miterStroke;
800 
801     SkAutoTUnref<GrBatch> batch(AAStrokeRectBatch::Create(geometry, viewMatrix));
802     target->drawBatch(pipelineBuilder, batch);
803 }
804 
fillAANestedRects(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,const SkRect rects[2])805 void GrAARectRenderer::fillAANestedRects(GrDrawTarget* target,
806                                          GrPipelineBuilder* pipelineBuilder,
807                                          GrColor color,
808                                          const SkMatrix& viewMatrix,
809                                          const SkRect rects[2]) {
810     SkASSERT(viewMatrix.rectStaysRect());
811     SkASSERT(!rects[1].isEmpty());
812 
813     SkRect devOutside, devInside;
814     viewMatrix.mapRect(&devOutside, rects[0]);
815     // can't call mapRect for devInside since it calls sort
816     viewMatrix.mapPoints((SkPoint*)&devInside, (const SkPoint*)&rects[1], 2);
817 
818     if (devInside.isEmpty()) {
819         this->fillAARect(target, pipelineBuilder, color, viewMatrix, devOutside, devOutside);
820         return;
821     }
822 
823     this->geometryStrokeAARect(target, pipelineBuilder, color, viewMatrix, devOutside,
824                                devOutside, devInside, true);
825 }
826 
827 ///////////////////////////////////////////////////////////////////////////////////////////////////
828 
829 #ifdef GR_TEST_UTILS
830 
BATCH_TEST_DEFINE(AAFillRectBatch)831 BATCH_TEST_DEFINE(AAFillRectBatch) {
832     AAFillRectBatch::Geometry geo;
833     geo.fColor = GrRandomColor(random);
834     geo.fViewMatrix = GrTest::TestMatrix(random);
835     geo.fRect = GrTest::TestRect(random);
836     geo.fDevRect = GrTest::TestRect(random);
837     return AAFillRectBatch::Create(geo);
838 }
839 
BATCH_TEST_DEFINE(AAStrokeRectBatch)840 BATCH_TEST_DEFINE(AAStrokeRectBatch) {
841     bool miterStroke = random->nextBool();
842 
843     // Create mock stroke rect
844     SkRect outside = GrTest::TestRect(random);
845     SkScalar minDim = SkMinScalar(outside.width(), outside.height());
846     SkScalar strokeWidth = minDim * 0.1f;
847     SkRect outsideAssist = outside;
848     outsideAssist.outset(strokeWidth, strokeWidth);
849     SkRect inside = outside;
850     inside.inset(strokeWidth, strokeWidth);
851 
852     AAStrokeRectBatch::Geometry geo;
853     geo.fColor = GrRandomColor(random);
854     geo.fDevOutside = outside;
855     geo.fDevOutsideAssist = outsideAssist;
856     geo.fDevInside = inside;
857     geo.fMiterStroke = miterStroke;
858 
859     return AAStrokeRectBatch::Create(geo, GrTest::TestMatrix(random));
860 }
861 
862 #endif
863